Merge branch 'master' into resolve-107208
This commit is contained in:
commit
b24e3ecfed
|
@ -1,122 +0,0 @@
|
|||
#-------------------------------------------------------------------------------------------------------------
|
||||
# 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 \
|
||||
libgbm1 \
|
||||
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"]
|
|
@ -1,8 +1,8 @@
|
|||
# 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).
|
||||
This repository includes configuration for a development container for working with Code - OSS in an isolated local container or using [GitHub Codespaces](https://github.com/features/codespaces).
|
||||
|
||||
> **Tip:** The default VNC password is `vscode`. The VNC server runs on port `5901` with a web client at `6080`. For better performance, we recommend using a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/). Applications like the macOS Screen Sharing app will not perform as well. [Chicken](https://sourceforge.net/projects/chicken/) is a good macOS alternative.
|
||||
> **Tip:** The default VNC password is `vscode`. The VNC server runs on port `5901` with a web client at `6080`. For better performance, we recommend using a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/). Applications like the macOS Screen Sharing app will not perform as well.
|
||||
|
||||
## Quick start - local
|
||||
|
||||
|
@ -30,25 +30,41 @@ Anything you start in VS Code or the integrated terminal will appear here.
|
|||
|
||||
Next: **[Try it out!](#try-it)**
|
||||
|
||||
## Quick start - Codespaces
|
||||
## Quick start - GitHub 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.
|
||||
> **IMPORTANT:** The current user beta for GitHub Codespaces uses a "Basic" sized codespace which is too small to run a full build of VS Code. You'll soon be able to use a "Standard" sized codespace (4-core, 8GB) that will be better suited for this purpose.
|
||||
|
||||
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.
|
||||
1. From the [microsoft/vscode GitHub repository](https://github.com/microsoft/vscode), click on the **Code** dropdown, select **Open with Codespaces**, and the **New codespace**
|
||||
|
||||
![Image of VS Codespaces extension](https://microsoft.github.io/vscode-remote-release/images/codespaces-extn.png)
|
||||
> Note that you will not see these options if you are not in the beta yet.
|
||||
|
||||
> Note that the Visual Studio Codespaces extension requires the Visual Studio Code distribution of Code - OSS.
|
||||
2. After the codespace is up and running in your browser, press <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> and select **View: Show Remote Explorer**.
|
||||
|
||||
2. Sign in by pressing <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> 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. You should see port `6080` under **Forwarded Ports**. Select the line and click on the globe icon to open it in a browser tab.
|
||||
|
||||
3. Press <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> and select **Codespaces: Create New Codespace**.
|
||||
> If you do not see port `6080`, press <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd>, select **Forward a Port** and enter port `6080`.
|
||||
|
||||
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.
|
||||
4. In the new tab, you should see noVNC. Click **Connect** and enter `vscode` as the password.
|
||||
|
||||
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.
|
||||
Anything you start in VS Code or the integrated terminal will appear here.
|
||||
|
||||
6. Anything you start in VS Code or the integrated terminal will appear here.
|
||||
Next: **[Try it out!](#try-it)**
|
||||
|
||||
### Using VS Code with GitHub Codespaces
|
||||
|
||||
You will likely see better performance when accessing the codespace you created from VS Code since you can use a[VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/). Here's how to do it.
|
||||
|
||||
1. [Create a codespace](#quick-start---github-codespaces) if you have not already.
|
||||
|
||||
2. Set up [VS Code for use with GitHub Codespaces](https://docs.github.com/github/developing-online-with-codespaces/using-codespaces-in-visual-studio-code)
|
||||
|
||||
3. After the VS Code is up and running, press <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd>, choose **Codespaces: Connect to Codespace**, and select the codespace you created.
|
||||
|
||||
4. After you've connected to the codespace, use a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to connect to `localhost:5901` and enter `vscode` as the password.
|
||||
|
||||
5. Anything you start in VS Code or the integrated terminal will appear here.
|
||||
|
||||
Next: **[Try it out!](#try-it)**
|
||||
|
||||
## Try it!
|
||||
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
#!/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 **"
|
|
@ -1,25 +0,0 @@
|
|||
#!/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
|
|
@ -1,42 +1,26 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
},
|
||||
|
||||
// Image contents: https://github.com/microsoft/vscode-dev-containers/blob/master/repository-containers/images/github.com/microsoft/vscode/.devcontainer/base.Dockerfile
|
||||
"image": "mcr.microsoft.com/vscode/devcontainers/repos/microsoft/vscode:dev",
|
||||
|
||||
"workspaceMount": "source=${localWorkspaceFolder},target=/home/node/workspace/vscode,type=bind,consistency=cached",
|
||||
"workspaceFolder": "/home/node/workspace/vscode",
|
||||
"overrideCommand": false,
|
||||
"runArgs": [
|
||||
"--init",
|
||||
// seccomp=unconfined is required for Chrome sandboxing
|
||||
"--security-opt", "seccomp=unconfined"
|
||||
],
|
||||
"runArgs": [ "--init", "--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
|
||||
"resmon.show.cpufreq": false
|
||||
},
|
||||
|
||||
// noVNC, VNC ports
|
||||
"forwardPorts": [6080, 5901],
|
||||
// noVNC, VNC ports, debug
|
||||
"forwardPorts": [6080, 5901, 9222],
|
||||
|
||||
"extensions": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"EditorConfig.EditorConfig",
|
||||
"msjsdiag.debugger-for-chrome",
|
||||
"mutantdino.resourcemonitor",
|
||||
"GitHub.vscode-pull-request-github"
|
||||
],
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
[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]
|
|
@ -1,9 +0,0 @@
|
|||
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
|
|
@ -1,16 +0,0 @@
|
|||
[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]
|
4
.github/classifier.json
vendored
4
.github/classifier.json
vendored
|
@ -20,8 +20,8 @@
|
|||
"context-keys": {"assign": []},
|
||||
"css-less-scss": {"assign": ["aeschli"]},
|
||||
"custom-editors": {"assign": ["mjbvz"]},
|
||||
"debug": {"assign": ["isidorn"]},
|
||||
"debug-console": {"assign": ["isidorn"]},
|
||||
"debug": {"assign": ["connor4312 "]},
|
||||
"debug-console": {"assign": ["connor4312 "]},
|
||||
"dialogs": {"assign": ["sbatten"]},
|
||||
"diff-editor": {"assign": []},
|
||||
"dropdown": {"assign": []},
|
||||
|
|
7
.github/commands.json
vendored
7
.github/commands.json
vendored
|
@ -133,6 +133,13 @@
|
|||
"action": "updateLabels",
|
||||
"addLabel": "~needs more info"
|
||||
},
|
||||
{
|
||||
"type": "comment",
|
||||
"name": "jsDebugLogs",
|
||||
"action": "updateLabels",
|
||||
"addLabel": "needs more info",
|
||||
"comment": "Please collect trace logs using the following instructions:\n\n> If you're able to, add `\"trace\": true` to your `launch.json` and reproduce the issue. The location of the log file on your disk will be written to the Debug Console. Share that with us.\n>\n> ⚠️ This log file will not contain source code, but will contain file paths. You can drop it into https://microsoft.github.io/vscode-pwa-analyzer/index.html to see what it contains. If you'd rather not share the log publicly, you can email it to connor@xbox.com"
|
||||
},
|
||||
{
|
||||
"type": "comment",
|
||||
"name": "closedWith",
|
||||
|
|
2
.github/workflows/author-verified.yml
vendored
2
.github/workflows/author-verified.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
|||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'microsoft/vscode-github-triage-actions'
|
||||
ref: v35
|
||||
ref: v36
|
||||
path: ./actions
|
||||
- name: Install Actions
|
||||
if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested')
|
||||
|
|
2
.github/workflows/commands.yml
vendored
2
.github/workflows/commands.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
|||
with:
|
||||
repository: 'microsoft/vscode-github-triage-actions'
|
||||
path: ./actions
|
||||
ref: v35
|
||||
ref: v36
|
||||
- name: Install Actions
|
||||
run: npm install --production --prefix ./actions
|
||||
- name: Run Commands
|
||||
|
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'microsoft/vscode-github-triage-actions'
|
||||
ref: v35
|
||||
ref: v36
|
||||
path: ./actions
|
||||
- name: Install Actions
|
||||
run: npm install --production --prefix ./actions
|
||||
|
|
2
.github/workflows/deep-classifier-runner.yml
vendored
2
.github/workflows/deep-classifier-runner.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
|||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'microsoft/vscode-github-triage-actions'
|
||||
ref: v35
|
||||
ref: v36
|
||||
path: ./actions
|
||||
- name: Install Actions
|
||||
run: npm install --production --prefix ./actions
|
||||
|
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'microsoft/vscode-github-triage-actions'
|
||||
ref: v35
|
||||
ref: v36
|
||||
path: ./actions
|
||||
- name: Install Actions
|
||||
run: npm install --production --prefix ./actions
|
||||
|
|
2
.github/workflows/english-please.yml
vendored
2
.github/workflows/english-please.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
|||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'microsoft/vscode-github-triage-actions'
|
||||
ref: v35
|
||||
ref: v36
|
||||
path: ./actions
|
||||
- name: Install Actions
|
||||
if: contains(github.event.issue.labels.*.name, '*english-please')
|
||||
|
|
2
.github/workflows/feature-request.yml
vendored
2
.github/workflows/feature-request.yml
vendored
|
@ -18,7 +18,7 @@ jobs:
|
|||
with:
|
||||
repository: 'microsoft/vscode-github-triage-actions'
|
||||
path: ./actions
|
||||
ref: v35
|
||||
ref: v36
|
||||
- name: Install Actions
|
||||
if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request')
|
||||
run: npm install --production --prefix ./actions
|
||||
|
|
2
.github/workflows/latest-release-monitor.yml
vendored
2
.github/workflows/latest-release-monitor.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
|||
with:
|
||||
repository: 'microsoft/vscode-github-triage-actions'
|
||||
path: ./actions
|
||||
ref: v35
|
||||
ref: v36
|
||||
- name: Install Actions
|
||||
run: npm install --production --prefix ./actions
|
||||
- name: Install Storage Module
|
||||
|
|
2
.github/workflows/locker.yml
vendored
2
.github/workflows/locker.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
|||
with:
|
||||
repository: 'microsoft/vscode-github-triage-actions'
|
||||
path: ./actions
|
||||
ref: v35
|
||||
ref: v36
|
||||
- name: Install Actions
|
||||
run: npm install --production --prefix ./actions
|
||||
- name: Run Locker
|
||||
|
|
2
.github/workflows/needs-more-info-closer.yml
vendored
2
.github/workflows/needs-more-info-closer.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
|||
with:
|
||||
repository: 'microsoft/vscode-github-triage-actions'
|
||||
path: ./actions
|
||||
ref: v35
|
||||
ref: v36
|
||||
- name: Install Actions
|
||||
run: npm install --production --prefix ./actions
|
||||
- name: Run Needs More Info Closer
|
||||
|
|
2
.github/workflows/on-label.yml
vendored
2
.github/workflows/on-label.yml
vendored
|
@ -11,7 +11,7 @@ jobs:
|
|||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'microsoft/vscode-github-triage-actions'
|
||||
ref: v35
|
||||
ref: v36
|
||||
path: ./actions
|
||||
- name: Install Actions
|
||||
run: npm install --production --prefix ./actions
|
||||
|
|
2
.github/workflows/on-open.yml
vendored
2
.github/workflows/on-open.yml
vendored
|
@ -11,7 +11,7 @@ jobs:
|
|||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'microsoft/vscode-github-triage-actions'
|
||||
ref: v35
|
||||
ref: v36
|
||||
path: ./actions
|
||||
- name: Install Actions
|
||||
run: npm install --production --prefix ./actions
|
||||
|
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'microsoft/vscode-github-triage-actions'
|
||||
ref: v35
|
||||
ref: v36
|
||||
path: ./actions
|
||||
- name: Checkout Repo
|
||||
if: github.event_name != 'issues'
|
||||
|
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
with:
|
||||
repository: 'microsoft/vscode-github-triage-actions'
|
||||
path: ./actions
|
||||
ref: v35
|
||||
ref: v36
|
||||
- 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
|
||||
|
|
182
.vscode/notebooks/endgame.github-issues
vendored
Normal file
182
.vscode/notebooks/endgame.github-issues
vendored
Normal file
|
@ -0,0 +1,182 @@
|
|||
[
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "# Endgame",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "## Macros",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github\n\n$MILESTONE=milestone:\"September 2020\"\n\n$MINE=assignee:@me",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "## Endgame Champion",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "### Test Plan Items",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$REPOS $MILESTONE label:testplan-item is:open",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "### Feature Requests Missing Labels",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$REPOS $MILESTONE is:issue is:closed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "### Open Issues on the Milestone",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$REPOS $MILESTONE -label:testplan-item -label:iteration-plan -label:endgame-plan is:open",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "## Testing",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "### My Assigned Test Plan Items",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$REPOS $MILESTONE $MINE label:testplan-item is:open",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "### My Created Test Plan Items",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$REPOS $MILESTONE author:@me label:testplan-item",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "## Verification",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "### Issues to Verify",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "#### Issues filed by me",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$REPOS $MILESTONE -$MINE author:@me sort:updated-asc is:closed is:issue label:bug -label:verified -label:on-testplan -label:duplicate -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "#### Issues filed by others",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$REPOS $MILESTONE -$MINE -author:@me sort:updated-asc is:closed is:issue label:bug -label:verified -label:on-testplan -label:duplicate -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "### Verification Needed",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$REPOS $MILESTONE is:issue is:closed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate ",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "### My Issues Needing Verification",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$REPOS $MILESTONE $MINE sort:updated-desc is:closed is:issue -label:verified -label:on-testplan -label:duplicate -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found\n",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "### My Open Issues",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$REPOS $MILESTONE $MINE is:open is:issue",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "## Documentation",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "### Needing Release Notes",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "repo:microsoft/vscode $MILESTONE $MINE is:closed label:feature-request -label:on-release-notes",
|
||||
"editable": true
|
||||
}
|
||||
]
|
4
.vscode/notebooks/inbox.github-issues
vendored
4
.vscode/notebooks/inbox.github-issues
vendored
|
@ -8,7 +8,7 @@
|
|||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$inbox -label:\"needs more info\" -label:emmet",
|
||||
"value": "$inbox -label:\"needs more info\"",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
|
@ -44,7 +44,7 @@
|
|||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$inbox -label:emmet",
|
||||
"value": "$inbox",
|
||||
"editable": true
|
||||
}
|
||||
]
|
116
.vscode/searches/TrustedTypes.code-search
vendored
116
.vscode/searches/TrustedTypes.code-search
vendored
|
@ -33,22 +33,22 @@ src/vs/base/browser/ui/contextview/contextview.ts:
|
|||
163 </style>
|
||||
|
||||
src/vs/code/electron-sandbox/issue/issueReporterMain.ts:
|
||||
57 const platformClass = platform.isWindows ? 'windows' : platform.isLinux ? 'linux' : 'mac';
|
||||
58 addClass(document.body, platformClass); // used by our fonts
|
||||
59
|
||||
60: document.body.innerHTML = BaseHtml();
|
||||
61 const issueReporter = new IssueReporter(configuration);
|
||||
62 issueReporter.render();
|
||||
63 document.body.style.display = 'block';
|
||||
58 const platformClass = platform.isWindows ? 'windows' : platform.isLinux ? 'linux' : 'mac';
|
||||
59 document.body.classList.add(platformClass); // used by our fonts
|
||||
60
|
||||
61: document.body.innerHTML = BaseHtml();
|
||||
62 const issueReporter = new IssueReporter(configuration);
|
||||
63 issueReporter.render();
|
||||
64 document.body.style.display = 'block';
|
||||
|
||||
src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts:
|
||||
320 content.push(`.highest { color: ${styles.highlightForeground}; }`);
|
||||
321 }
|
||||
322
|
||||
323: styleTag.innerHTML = content.join('\n');
|
||||
324 if (document.head) {
|
||||
325 document.head.appendChild(styleTag);
|
||||
326 }
|
||||
321 content.push(`.highest { color: ${styles.highlightForeground}; }`);
|
||||
322 }
|
||||
323
|
||||
324: styleTag.innerHTML = content.join('\n');
|
||||
325 if (document.head) {
|
||||
326 document.head.appendChild(styleTag);
|
||||
327 }
|
||||
|
||||
src/vs/editor/browser/view/domLineBreaksComputer.ts:
|
||||
107 allCharOffsets[i] = tmp[0];
|
||||
|
@ -100,29 +100,29 @@ src/vs/editor/standalone/browser/colorizer.ts:
|
|||
46 }
|
||||
|
||||
src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts:
|
||||
212 if (!this._globalStyleElement) {
|
||||
213 this._globalStyleElement = dom.createStyleSheet();
|
||||
214 this._globalStyleElement.className = 'monaco-colors';
|
||||
215: this._globalStyleElement.innerHTML = this._css;
|
||||
216 this._styleElements.push(this._globalStyleElement);
|
||||
217 }
|
||||
218 return Disposable.None;
|
||||
232 if (!this._globalStyleElement) {
|
||||
233 this._globalStyleElement = dom.createStyleSheet();
|
||||
234 this._globalStyleElement.className = 'monaco-colors';
|
||||
235: this._globalStyleElement.innerHTML = this._allCSS;
|
||||
236 this._styleElements.push(this._globalStyleElement);
|
||||
237 }
|
||||
238 return Disposable.None;
|
||||
|
||||
221 private _registerShadowDomContainer(domNode: HTMLElement): IDisposable {
|
||||
222 const styleElement = dom.createStyleSheet(domNode);
|
||||
223 styleElement.className = 'monaco-colors';
|
||||
224: styleElement.innerHTML = this._css;
|
||||
225 this._styleElements.push(styleElement);
|
||||
226 return {
|
||||
227 dispose: () => {
|
||||
241 private _registerShadowDomContainer(domNode: HTMLElement): IDisposable {
|
||||
242 const styleElement = dom.createStyleSheet(domNode);
|
||||
243 styleElement.className = 'monaco-colors';
|
||||
244: styleElement.innerHTML = this._allCSS;
|
||||
245 this._styleElements.push(styleElement);
|
||||
246 return {
|
||||
247 dispose: () => {
|
||||
|
||||
291 ruleCollector.addRule(generateTokensCSSForColorMap(colorMap));
|
||||
292
|
||||
293 this._css = cssRules.join('\n');
|
||||
294: this._styleElements.forEach(styleElement => styleElement.innerHTML = this._css);
|
||||
295
|
||||
296 TokenizationRegistry.setColorMap(colorMap);
|
||||
297 this._onColorThemeChange.fire(theme);
|
||||
321
|
||||
322 private _updateCSS(): void {
|
||||
323 this._allCSS = `${this._codiconCSS}\n${this._themeCSS}`;
|
||||
324: this._styleElements.forEach(styleElement => styleElement.innerHTML = this._allCSS);
|
||||
325 }
|
||||
326
|
||||
327 public getFileIconTheme(): IFileIconTheme {
|
||||
|
||||
src/vs/editor/test/browser/controller/imeTester.ts:
|
||||
55 let content = this._model.getModelLineContent(i);
|
||||
|
@ -142,21 +142,21 @@ src/vs/editor/test/browser/controller/imeTester.ts:
|
|||
75 let startBtn = document.createElement('button');
|
||||
|
||||
src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts:
|
||||
454
|
||||
455 private getMarkdownDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement {
|
||||
456 const dragImageContainer = DOM.$('.cell-drag-image.monaco-list-row.focused.markdown-cell-row');
|
||||
457: dragImageContainer.innerHTML = templateData.container.outerHTML;
|
||||
458
|
||||
459 // Remove all rendered content nodes after the
|
||||
460 const markdownContent = dragImageContainer.querySelector('.cell.markdown')!;
|
||||
455
|
||||
456 private getMarkdownDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement {
|
||||
457 const dragImageContainer = DOM.$('.cell-drag-image.monaco-list-row.focused.markdown-cell-row');
|
||||
458: dragImageContainer.innerHTML = templateData.container.outerHTML;
|
||||
459
|
||||
460 // Remove all rendered content nodes after the
|
||||
461 const markdownContent = dragImageContainer.querySelector('.cell.markdown')!;
|
||||
|
||||
611 return null;
|
||||
612 }
|
||||
613
|
||||
614: editorContainer.innerHTML = richEditorText;
|
||||
615
|
||||
616 return dragImageContainer;
|
||||
617 }
|
||||
641 return null;
|
||||
642 }
|
||||
643
|
||||
644: editorContainer.innerHTML = richEditorText;
|
||||
645
|
||||
646 return dragImageContainer;
|
||||
647 }
|
||||
|
||||
src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts:
|
||||
375 addMouseoverListeners(outputNode, outputId);
|
||||
|
@ -165,21 +165,21 @@ src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts:
|
|||
378: outputNode.innerHTML = content.htmlContent;
|
||||
379 cellOutputContainer.appendChild(outputNode);
|
||||
380 domEval(outputNode);
|
||||
381 } else {
|
||||
381 } else if (preloadErrs.some(e => !!e)) {
|
||||
|
||||
src/vs/workbench/contrib/webview/browser/pre/main.js:
|
||||
386 // apply default styles
|
||||
387 const defaultStyles = newDocument.createElement('style');
|
||||
388 defaultStyles.id = '_defaultStyles';
|
||||
389: defaultStyles.innerHTML = defaultCssRules;
|
||||
390 newDocument.head.prepend(defaultStyles);
|
||||
391
|
||||
392 applyStyles(newDocument, newDocument.body);
|
||||
393 // apply default styles
|
||||
394 const defaultStyles = newDocument.createElement('style');
|
||||
395 defaultStyles.id = '_defaultStyles';
|
||||
396: defaultStyles.innerHTML = defaultCssRules;
|
||||
397 newDocument.head.prepend(defaultStyles);
|
||||
398
|
||||
399 applyStyles(newDocument, newDocument.body);
|
||||
|
||||
src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts:
|
||||
281
|
||||
282 const content = model.main.textEditorModel.getValue(EndOfLinePreference.LF);
|
||||
283 if (!strings.endsWith(input.resource.path, '.md')) {
|
||||
283 if (!input.resource.path.endsWith('.md')) {
|
||||
284: this.content.innerHTML = content;
|
||||
285 this.updateSizeClasses();
|
||||
286 this.decorateContent();
|
||||
|
|
30
.vscode/searches/es6.code-search
vendored
30
.vscode/searches/es6.code-search
vendored
|
@ -2,7 +2,7 @@
|
|||
# Flags: CaseSensitive WordMatch
|
||||
# ContextLines: 2
|
||||
|
||||
10 results - 2 files
|
||||
7 results - 2 files
|
||||
|
||||
src/vs/base/browser/dom.ts:
|
||||
74 };
|
||||
|
@ -22,26 +22,8 @@ src/vs/base/browser/dom.ts:
|
|||
88
|
||||
|
||||
src/vs/base/common/strings.ts:
|
||||
15
|
||||
16 /**
|
||||
17: * @deprecated ES6: use `String.padStart`
|
||||
18 */
|
||||
19 export function pad(n: number, l: number, char: string = '0'): string {
|
||||
|
||||
146
|
||||
147 /**
|
||||
148: * @deprecated ES6: use `String.startsWith`
|
||||
149 */
|
||||
150 export function startsWith(haystack: string, needle: string): boolean {
|
||||
|
||||
167
|
||||
168 /**
|
||||
169: * @deprecated ES6: use `String.endsWith`
|
||||
170 */
|
||||
171 export function endsWith(haystack: string, needle: string): boolean {
|
||||
|
||||
857
|
||||
858 /**
|
||||
859: * @deprecated ES6
|
||||
860 */
|
||||
861 export function repeat(s: string, count: number): string {
|
||||
15
|
||||
16 /**
|
||||
17: * @deprecated ES6: use `String.padStart`
|
||||
18 */
|
||||
19 export function pad(n: number, l: number, char: string = '0'): string {
|
||||
|
|
2
build/.gitattributes
vendored
2
build/.gitattributes
vendored
|
@ -1 +1,3 @@
|
|||
* text eol=lf
|
||||
*.exe binary
|
||||
*.dll binary
|
||||
|
|
|
@ -67,7 +67,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
|||
}
|
||||
}
|
||||
|
||||
requestService = getRequestService(params.initializationOptions.handledSchemas || ['file'], connection, runtime);
|
||||
requestService = getRequestService(params.initializationOptions?.handledSchemas || ['file'], connection, runtime);
|
||||
|
||||
function getClientCapability<T>(name: string, def: T) {
|
||||
const keys = name.split('.');
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { promises as fs } from 'fs';
|
||||
import { createServer, Server } from 'net';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
|
@ -203,7 +204,27 @@ async function createAttachServer(context: vscode.ExtensionContext) {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
server = new Promise<Server>((resolve, reject) => {
|
||||
server = createServerInner(ipcAddress).catch(err => {
|
||||
console.error(err);
|
||||
return undefined;
|
||||
});
|
||||
|
||||
return await server;
|
||||
}
|
||||
|
||||
const createServerInner = async (ipcAddress: string) => {
|
||||
try {
|
||||
return await createServerInstance(ipcAddress);
|
||||
} catch (e) {
|
||||
// On unix/linux, the file can 'leak' if the process exits unexpectedly.
|
||||
// If we see this, try to delete the file and then listen again.
|
||||
await fs.unlink(ipcAddress).catch(() => undefined);
|
||||
return await createServerInstance(ipcAddress);
|
||||
}
|
||||
};
|
||||
|
||||
const createServerInstance = (ipcAddress: string) =>
|
||||
new Promise<Server>((resolve, reject) => {
|
||||
const s = createServer(socket => {
|
||||
let data: Buffer[] = [];
|
||||
socket.on('data', async chunk => {
|
||||
|
@ -229,14 +250,8 @@ async function createAttachServer(context: vscode.ExtensionContext) {
|
|||
})
|
||||
.on('error', reject)
|
||||
.listen(ipcAddress, () => resolve(s));
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
return undefined;
|
||||
});
|
||||
|
||||
return await server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the auto-attach server, if it's running.
|
||||
*/
|
||||
|
|
|
@ -112,16 +112,16 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
|||
}
|
||||
}
|
||||
|
||||
requestService = getRequestService(params.initializationOptions.handledSchemas || ['file'], connection, runtime);
|
||||
requestService = getRequestService(initializationOptions?.handledSchemas || ['file'], connection, runtime);
|
||||
|
||||
const workspace = {
|
||||
get settings() { return globalSettings; },
|
||||
get folders() { return workspaceFolders; }
|
||||
};
|
||||
|
||||
languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true }, workspace, params.capabilities, requestService);
|
||||
languageModes = getLanguageModes(initializationOptions?.embeddedLanguages || { css: true, javascript: true }, workspace, params.capabilities, requestService);
|
||||
|
||||
const dataPaths: string[] = params.initializationOptions.dataPaths || [];
|
||||
const dataPaths: string[] = initializationOptions?.dataPaths || [];
|
||||
fetchHTMLDataProviders(dataPaths, requestService).then(dataProviders => {
|
||||
languageModes.updateDataProviders(dataProviders);
|
||||
});
|
||||
|
@ -146,7 +146,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
|||
}
|
||||
|
||||
clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false);
|
||||
dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof params.initializationOptions.provideFormatter !== 'boolean');
|
||||
dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof initializationOptions?.provideFormatter !== 'boolean');
|
||||
scopedSettingsSupport = getClientCapability('workspace.configuration', false);
|
||||
workspaceFoldersSupport = getClientCapability('workspace.workspaceFolders', false);
|
||||
foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE);
|
||||
|
@ -155,7 +155,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
|||
completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['.', ':', '<', '"', '=', '/'] } : undefined,
|
||||
hoverProvider: true,
|
||||
documentHighlightProvider: true,
|
||||
documentRangeFormattingProvider: params.initializationOptions.provideFormatter === true,
|
||||
documentRangeFormattingProvider: initializationOptions?.provideFormatter === true,
|
||||
documentLinkProvider: { resolveProvider: false },
|
||||
documentSymbolProvider: true,
|
||||
definitionProvider: true,
|
||||
|
|
|
@ -167,7 +167,12 @@ interface ISchemaAssociation {
|
|||
* A match succeeds when there is at least one pattern matching and last matching pattern does not start with '!'.
|
||||
*/
|
||||
fileMatch: string[];
|
||||
|
||||
|
||||
/*
|
||||
* The schema for the given URI.
|
||||
* If no schema is provided, the schema will be fetched with the schema request service (if available).
|
||||
*/
|
||||
schema?: JSONSchema;
|
||||
}
|
||||
|
||||
```
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "vscode-json-languageserver",
|
||||
"description": "JSON language server",
|
||||
"version": "1.2.3",
|
||||
"version": "1.3.0",
|
||||
"author": "Microsoft Corporation",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
|
@ -137,7 +137,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
|||
} : undefined,
|
||||
hoverProvider: true,
|
||||
documentSymbolProvider: true,
|
||||
documentRangeFormattingProvider: params.initializationOptions.provideFormatter === true,
|
||||
documentRangeFormattingProvider: params.initializationOptions?.provideFormatter === true,
|
||||
colorProvider: {},
|
||||
foldingRangeProvider: true,
|
||||
selectionRangeProvider: true,
|
||||
|
|
|
@ -68,6 +68,7 @@ export interface ITokenResponse {
|
|||
refresh_token: string;
|
||||
scope: string;
|
||||
token_type: string;
|
||||
id_token?: string;
|
||||
}
|
||||
|
||||
function parseQuery(uri: vscode.Uri) {
|
||||
|
@ -449,7 +450,19 @@ export class AzureActiveDirectoryService {
|
|||
}
|
||||
|
||||
private getTokenFromResponse(json: ITokenResponse, scope: string, existingId?: string): IToken {
|
||||
const claims = this.getTokenClaims(json.access_token);
|
||||
let claims = undefined;
|
||||
|
||||
try {
|
||||
claims = this.getTokenClaims(json.access_token);
|
||||
} catch (e) {
|
||||
if (json.id_token) {
|
||||
Logger.info('Failed to fetch token claims from access_token. Attempting to parse id_token instead');
|
||||
claims = this.getTokenClaims(json.id_token);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
expiresIn: json.expires_in,
|
||||
expiresAt: json.expires_in ? Date.now() + json.expires_in * 1000 : undefined,
|
||||
|
|
|
@ -282,8 +282,8 @@ export class PackageJSONContribution implements IJSONContribution {
|
|||
|
||||
private npmView(pack: string): Promise<ViewPackageInfo | undefined> {
|
||||
return new Promise((resolve, _reject) => {
|
||||
const command = 'npm view --json ' + pack + ' description dist-tags.latest homepage version';
|
||||
cp.exec(command, (error, stdout) => {
|
||||
const args = ['view', '--json', pack, 'description', 'dist-tags.latest', 'homepage', 'version'];
|
||||
cp.execFile('npm', args, (error, stdout) => {
|
||||
if (!error) {
|
||||
try {
|
||||
const content = JSON.parse(stdout);
|
||||
|
|
|
@ -137,7 +137,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> {
|
|||
}
|
||||
|
||||
private async debugScript(script: NpmScript) {
|
||||
startDebugging(script.task.name, script.getFolder());
|
||||
startDebugging(script.task.definition.script, path.dirname(script.package.resourceUri!.fsPath), script.getFolder());
|
||||
}
|
||||
|
||||
private findScript(document: TextDocument, script?: NpmScript): number {
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
createTask, startDebugging, findAllScriptRanges
|
||||
} from './tasks';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { dirname } from 'path';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
@ -107,12 +108,12 @@ export class NpmScriptHoverProvider implements HoverProvider {
|
|||
}
|
||||
}
|
||||
|
||||
public debugScriptFromHover(args: any) {
|
||||
public debugScriptFromHover(args: { script: string; documentUri: Uri }) {
|
||||
let script = args.script;
|
||||
let documentUri = args.documentUri;
|
||||
let folder = workspace.getWorkspaceFolder(documentUri);
|
||||
if (folder) {
|
||||
startDebugging(script, folder);
|
||||
startDebugging(script, dirname(documentUri.fsPath), folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -357,11 +357,12 @@ export function runScript(script: string, document: TextDocument) {
|
|||
}
|
||||
}
|
||||
|
||||
export function startDebugging(scriptName: string, folder: WorkspaceFolder) {
|
||||
export function startDebugging(scriptName: string, cwd: string, folder: WorkspaceFolder) {
|
||||
const config: DebugConfiguration = {
|
||||
type: 'pwa-node',
|
||||
request: 'launch',
|
||||
name: `Debug ${scriptName}`,
|
||||
cwd,
|
||||
runtimeExecutable: getPackageManager(folder),
|
||||
runtimeArgs: [
|
||||
'run',
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.
|
||||
|
||||
This extension provides Syntax Highlighting, Symbol Infomation, Result Highlighting, and Go to Definition capabilities for the Search Results Editor.
|
||||
This extension provides Syntax Highlighting, Symbol Information, Result Highlighting, and Go to Definition capabilities for the Search Results Editor.
|
||||
|
|
|
@ -134,7 +134,7 @@ function relativePathToUri(path: string, resultsUri: vscode.Uri): vscode.Uri | u
|
|||
}
|
||||
|
||||
const uriFromFolderWithPath = (folder: vscode.WorkspaceFolder, path: string): vscode.Uri =>
|
||||
folder.uri.with({ path: pathUtils.join(folder.uri.fsPath, path) });
|
||||
vscode.Uri.joinPath(folder.uri, path);
|
||||
|
||||
if (vscode.workspace.workspaceFolders) {
|
||||
const multiRootFormattedPath = /^(.*) • (.*)$/.exec(path);
|
||||
|
|
|
@ -19,7 +19,13 @@ export class WorkerServerProcess implements TsServerProcess {
|
|||
_configuration: TypeScriptServiceConfiguration,
|
||||
) {
|
||||
const worker = new Worker(tsServerPath);
|
||||
return new WorkerServerProcess(worker, args);
|
||||
return new WorkerServerProcess(worker, [
|
||||
...args,
|
||||
|
||||
// Explicitly give TS Server its path so it can
|
||||
// load local resources
|
||||
'--executingFilePath', tsServerPath,
|
||||
]);
|
||||
}
|
||||
|
||||
private _onDataHandlers = new Set<(data: Proto.Response) => void>();
|
||||
|
|
|
@ -10,6 +10,7 @@ import { ClientCapabilities, ClientCapability, ServerType } from '../typescriptS
|
|||
import API from '../utils/api';
|
||||
import { SeparateSyntaxServerConfiguration, TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration';
|
||||
import { Logger } from '../utils/logger';
|
||||
import { isWeb } from '../utils/platform';
|
||||
import { TypeScriptPluginPathsProvider } from '../utils/pluginPathsProvider';
|
||||
import { PluginManager } from '../utils/plugins';
|
||||
import { TelemetryReporter } from '../utils/telemetry';
|
||||
|
@ -203,29 +204,35 @@ export class TypeScriptServerSpawner {
|
|||
}
|
||||
|
||||
if (TypeScriptServerSpawner.isLoggingEnabled(configuration)) {
|
||||
const logDir = this._logDirectoryProvider.getNewLogDirectory();
|
||||
if (logDir) {
|
||||
tsServerLogFile = path.join(logDir, `tsserver.log`);
|
||||
if (isWeb()) {
|
||||
args.push('--logVerbosity', TsServerLogLevel.toString(configuration.tsServerLogLevel));
|
||||
args.push('--logFile', tsServerLogFile);
|
||||
}
|
||||
}
|
||||
|
||||
const pluginPaths = this._pluginPathsProvider.getPluginPaths();
|
||||
|
||||
if (pluginManager.plugins.length) {
|
||||
args.push('--globalPlugins', pluginManager.plugins.map(x => x.name).join(','));
|
||||
|
||||
const isUsingBundledTypeScriptVersion = currentVersion.path === this._versionProvider.defaultVersion.path;
|
||||
for (const plugin of pluginManager.plugins) {
|
||||
if (isUsingBundledTypeScriptVersion || plugin.enableForWorkspaceTypeScriptVersions) {
|
||||
pluginPaths.push(plugin.path);
|
||||
} else {
|
||||
const logDir = this._logDirectoryProvider.getNewLogDirectory();
|
||||
if (logDir) {
|
||||
tsServerLogFile = path.join(logDir, `tsserver.log`);
|
||||
args.push('--logVerbosity', TsServerLogLevel.toString(configuration.tsServerLogLevel));
|
||||
args.push('--logFile', tsServerLogFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pluginPaths.length !== 0) {
|
||||
args.push('--pluginProbeLocations', pluginPaths.join(','));
|
||||
if (!isWeb()) {
|
||||
const pluginPaths = this._pluginPathsProvider.getPluginPaths();
|
||||
|
||||
if (pluginManager.plugins.length) {
|
||||
args.push('--globalPlugins', pluginManager.plugins.map(x => x.name).join(','));
|
||||
|
||||
const isUsingBundledTypeScriptVersion = currentVersion.path === this._versionProvider.defaultVersion.path;
|
||||
for (const plugin of pluginManager.plugins) {
|
||||
if (isUsingBundledTypeScriptVersion || plugin.enableForWorkspaceTypeScriptVersions) {
|
||||
pluginPaths.push(plugin.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pluginPaths.length !== 0) {
|
||||
args.push('--pluginProbeLocations', pluginPaths.join(','));
|
||||
}
|
||||
}
|
||||
|
||||
if (configuration.npmLocation) {
|
||||
|
|
|
@ -926,7 +926,10 @@ suite('vscode API - workspace', () => {
|
|||
assert.ok(await vscode.workspace.applyEdit(we));
|
||||
|
||||
const document = await vscode.workspace.openTextDocument(newUri);
|
||||
assert.equal(document.isDirty, true);
|
||||
// See https://github.com/microsoft/vscode/issues/107739
|
||||
// RenameOperation currently saves the file before applying the rename
|
||||
// so that is why the document is not dirty here
|
||||
assert.equal(document.isDirty, false);
|
||||
|
||||
await document.save();
|
||||
assert.equal(document.isDirty, false);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
"extensionKind": [ "ui" ],
|
||||
"scripts": {
|
||||
"compile": "node ./node_modules/vscode/bin/compile -watch -p ./",
|
||||
"vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-test-resolver ./tsconfig.json"
|
||||
"vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-test-resolver"
|
||||
},
|
||||
"activationEvents": [
|
||||
"onResolveRemoteAuthority:test",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.50.0",
|
||||
"distro": "8b294911aabcfe39d612f32d1d2aa3cd6e9732b3",
|
||||
"version": "1.51.0",
|
||||
"distro": "e42a5273a865424266dd22838961d286c5c45ddc",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
@ -91,6 +91,7 @@
|
|||
"@types/mocha": "2.2.39",
|
||||
"@types/node": "^12.11.7",
|
||||
"@types/sinon": "^1.16.36",
|
||||
"@types/trusted-types": "^1.0.6",
|
||||
"@types/vscode-windows-registry": "^1.0.0",
|
||||
"@types/webpack": "^4.4.10",
|
||||
"@types/windows-foreground-love": "^0.3.0",
|
||||
|
@ -193,4 +194,4 @@
|
|||
"windows-mutex": "0.3.0",
|
||||
"windows-process-tree": "0.2.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
},
|
||||
{
|
||||
"name": "ms-vscode.references-view",
|
||||
"version": "0.0.67",
|
||||
"version": "0.0.68",
|
||||
"repo": "https://github.com/microsoft/vscode-reference-view",
|
||||
"metadata": {
|
||||
"id": "dc489f46-520d-4556-ae85-1f9eab3c412d",
|
||||
|
@ -91,7 +91,7 @@
|
|||
},
|
||||
{
|
||||
"name": "ms-vscode.js-debug",
|
||||
"version": "1.50.1",
|
||||
"version": "1.50.2",
|
||||
"repo": "https://github.com/microsoft/vscode-js-debug",
|
||||
"metadata": {
|
||||
"id": "25629058-ddac-4e17-abba-74678e126c5d",
|
||||
|
@ -106,7 +106,7 @@
|
|||
},
|
||||
{
|
||||
"name": "ms-vscode.vscode-js-profile-table",
|
||||
"version": "0.0.10",
|
||||
"version": "0.0.11",
|
||||
"repo": "https://github.com/microsoft/vscode-js-debug",
|
||||
"metadata": {
|
||||
"id": "7e52b41b-71ad-457b-ab7e-0620f1fc4feb",
|
||||
|
@ -123,7 +123,7 @@
|
|||
"webBuiltInExtensions": [
|
||||
{
|
||||
"name": "ms-vscode.github-browser",
|
||||
"version": "0.0.12",
|
||||
"version": "0.0.13",
|
||||
"repo": "https://github.com/microsoft/vscode-github-browser",
|
||||
"metadata": {
|
||||
"id": "c1bcff4b-4ecb-466e-b8f6-b02788b5fb5a",
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
"node-pty": "0.10.0-beta17",
|
||||
"semver-umd": "^5.5.7",
|
||||
"spdlog": "^0.11.1",
|
||||
"tas-client-umd": "0.1.2",
|
||||
"vscode-nsfw": "1.2.8",
|
||||
"vscode-oniguruma": "1.3.1",
|
||||
"vscode-proxy-agent": "^0.5.2",
|
||||
|
|
|
@ -373,6 +373,11 @@ spdlog@^0.11.1:
|
|||
mkdirp "^0.5.1"
|
||||
nan "^2.14.0"
|
||||
|
||||
tas-client-umd@0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.2.tgz#fe93ae085f65424292ac79feff4f1add3e50e624"
|
||||
integrity sha512-rT9BdDCejckqOTQL2ShX67QtTiAUGbmPm5ZTC8giXobBvZC6JuvBVy5G32hoGZ3Q0dpTvMfgpf3iVFNN2F7wzg==
|
||||
|
||||
to-regex-range@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
||||
|
|
|
@ -276,12 +276,13 @@ async function handleStatic(req, res, parsedUrl) {
|
|||
if (/^\/static\/extensions\//.test(parsedUrl.pathname)) {
|
||||
const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/static/extensions/'.length));
|
||||
const filePath = getExtensionFilePath(relativePath, (await builtInExtensionsPromise).locations);
|
||||
if (!filePath) {
|
||||
return serveError(req, res, 400, `Bad request.`);
|
||||
}
|
||||
return serveFile(req, res, filePath, {
|
||||
const responseHeaders = {
|
||||
'Access-Control-Allow-Origin': '*'
|
||||
});
|
||||
};
|
||||
if (!filePath) {
|
||||
return serveError(req, res, 400, `Bad request.`, responseHeaders);
|
||||
}
|
||||
return serveFile(req, res, filePath, responseHeaders);
|
||||
}
|
||||
|
||||
// Strip `/static/` from the path
|
||||
|
@ -299,12 +300,13 @@ async function handleExtension(req, res, parsedUrl) {
|
|||
// Strip `/extension/` from the path
|
||||
const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/extension/'.length));
|
||||
const filePath = getExtensionFilePath(relativePath, (await commandlineProvidedExtensionsPromise).locations);
|
||||
if (!filePath) {
|
||||
return serveError(req, res, 400, `Bad request.`);
|
||||
}
|
||||
return serveFile(req, res, filePath, {
|
||||
const responseHeaders = {
|
||||
'Access-Control-Allow-Origin': '*'
|
||||
});
|
||||
};
|
||||
if (!filePath) {
|
||||
return serveError(req, res, 400, `Bad request.`, responseHeaders);
|
||||
}
|
||||
return serveFile(req, res, filePath, responseHeaders);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -517,8 +519,9 @@ function getExtensionFilePath(relativePath, locations) {
|
|||
* @param {import('http').ServerResponse} res
|
||||
* @param {string} errorMessage
|
||||
*/
|
||||
function serveError(req, res, errorCode, errorMessage) {
|
||||
res.writeHead(errorCode, { 'Content-Type': 'text/plain' });
|
||||
function serveError(req, res, errorCode, errorMessage, responseHeaders = Object.create(null)) {
|
||||
responseHeaders['Content-Type'] = 'text/plain';
|
||||
res.writeHead(errorCode, responseHeaders);
|
||||
res.end(errorMessage);
|
||||
}
|
||||
|
||||
|
@ -583,7 +586,8 @@ async function serveFile(req, res, filePath, responseHeaders = Object.create(nul
|
|||
fs.createReadStream(filePath).pipe(res);
|
||||
} catch (error) {
|
||||
console.error(error.toString());
|
||||
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||
responseHeaders['Content-Type'] = 'text/plain';
|
||||
res.writeHead(404, responseHeaders);
|
||||
return res.end('Not found');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
"mocha",
|
||||
"semver",
|
||||
"sinon",
|
||||
"winreg"
|
||||
"winreg",
|
||||
"trusted-types"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
"extends": "./tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"types": [],
|
||||
"types": [
|
||||
"trusted-types"
|
||||
],
|
||||
"paths": {},
|
||||
"module": "amd",
|
||||
"moduleResolution": "classic",
|
||||
|
|
36
src/typings/trustedTypes.d.ts
vendored
36
src/typings/trustedTypes.d.ts
vendored
|
@ -1,36 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// see https://w3c.github.io/webappsec-trusted-types/dist/spec/
|
||||
// this isn't complete nor 100% correct
|
||||
|
||||
type TrustedHTML = string & object;
|
||||
type TrustedScript = string;
|
||||
type TrustedScriptURL = string;
|
||||
|
||||
interface TrustedTypePolicyOptions {
|
||||
createHTML?: (value: string) => string
|
||||
createScript?: (value: string) => string
|
||||
createScriptURL?: (value: string) => string
|
||||
}
|
||||
|
||||
interface TrustedTypePolicy {
|
||||
readonly name: string;
|
||||
createHTML(input: string, ...more: any[]): TrustedHTML
|
||||
createScript(input: string, ...more: any[]): TrustedScript
|
||||
createScriptURL(input: string, ...more: any[]): TrustedScriptURL
|
||||
}
|
||||
|
||||
interface TrustedTypePolicyFactory {
|
||||
createPolicy(policyName: string, object: TrustedTypePolicyOptions): TrustedTypePolicy;
|
||||
}
|
||||
|
||||
interface Window {
|
||||
trustedTypes: TrustedTypePolicyFactory | undefined;
|
||||
}
|
||||
|
||||
interface WorkerGlobalScope {
|
||||
trustedTypes: TrustedTypePolicyFactory | undefined;
|
||||
}
|
|
@ -22,6 +22,13 @@ export function clearNode(node: HTMLElement): void {
|
|||
}
|
||||
}
|
||||
|
||||
export function trustedInnerHTML(node: Element, value: TrustedHTML): void {
|
||||
// this is a workaround for innerHTML not allowing for "asymetric" accessors
|
||||
// see https://github.com/microsoft/vscode/issues/106396#issuecomment-692625393
|
||||
// and https://github.com/microsoft/TypeScript/issues/30024
|
||||
node.innerHTML = value as unknown as string;
|
||||
}
|
||||
|
||||
export function isInDOM(node: Node | null): boolean {
|
||||
while (node) {
|
||||
if (node === document.body) {
|
||||
|
@ -1229,7 +1236,6 @@ export function asCSSUrl(uri: URI): string {
|
|||
return `url('${FileAccess.asBrowserUri(uri).toString(true).replace(/'/g, '%27')}')`;
|
||||
}
|
||||
|
||||
|
||||
export function triggerDownload(dataOrUri: Uint8Array | URI, name: string): void {
|
||||
|
||||
// If the data is provided as Buffer, we create a
|
||||
|
|
|
@ -64,8 +64,9 @@ export class ActionBar extends Disposable implements IActionRunner {
|
|||
private _onDidBlur = this._register(new Emitter<void>());
|
||||
readonly onDidBlur = this._onDidBlur.event;
|
||||
|
||||
private _onDidCancel = this._register(new Emitter<void>());
|
||||
private _onDidCancel = this._register(new Emitter<void>({ onFirstListenerAdd: () => this.cancelHasListener = true }));
|
||||
readonly onDidCancel = this._onDidCancel.event;
|
||||
private cancelHasListener = false;
|
||||
|
||||
private _onDidRun = this._register(new Emitter<IRunEvent>());
|
||||
readonly onDidRun = this._onDidRun.event;
|
||||
|
@ -138,7 +139,7 @@ export class ActionBar extends Disposable implements IActionRunner {
|
|||
eventHandled = this.focusPrevious();
|
||||
} else if (nextKeys && (event.equals(nextKeys[0]) || event.equals(nextKeys[1]))) {
|
||||
eventHandled = this.focusNext();
|
||||
} else if (event.equals(KeyCode.Escape)) {
|
||||
} else if (event.equals(KeyCode.Escape) && this.cancelHasListener) {
|
||||
this._onDidCancel.fire();
|
||||
} else if (this.isTriggerKeyEvent(event)) {
|
||||
// Staying out of the else branch even if not triggered
|
||||
|
|
|
@ -133,7 +133,7 @@
|
|||
}
|
||||
|
||||
/** Spans in markdown hovers need a margin-bottom to avoid looking cramped: https://github.com/microsoft/vscode/issues/101496 **/
|
||||
.monaco-hover .markdown-hover .hover-contents span {
|
||||
.monaco-hover .markdown-hover .hover-contents:not(.code-hover-contents) span {
|
||||
margin-bottom: 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ const SELECT_OPTION_ENTRY_TEMPLATE_ID = 'selectOption.entry.template';
|
|||
interface ISelectListTemplateData {
|
||||
root: HTMLElement;
|
||||
text: HTMLElement;
|
||||
itemDescription: HTMLElement;
|
||||
decoratorRight: HTMLElement;
|
||||
disposables: IDisposable[];
|
||||
}
|
||||
|
@ -44,8 +43,6 @@ class SelectListRenderer implements IListRenderer<ISelectOptionItem, ISelectList
|
|||
data.root = container;
|
||||
data.text = dom.append(container, $('.option-text'));
|
||||
data.decoratorRight = dom.append(container, $('.option-decorator-right'));
|
||||
data.itemDescription = dom.append(container, $('.option-text-description'));
|
||||
data.itemDescription.classList.add('visually-hidden');
|
||||
|
||||
return data;
|
||||
}
|
||||
|
@ -59,13 +56,6 @@ class SelectListRenderer implements IListRenderer<ISelectOptionItem, ISelectList
|
|||
data.text.textContent = text;
|
||||
data.decoratorRight.innerText = (!!decoratorRight ? decoratorRight : '');
|
||||
|
||||
if (typeof element.description === 'string') {
|
||||
const itemDescriptionId = (text.replace(/ /g, '_').toLowerCase() + '_description_' + data.root.id);
|
||||
data.text.setAttribute('aria-describedby', itemDescriptionId);
|
||||
data.itemDescription.id = itemDescriptionId;
|
||||
data.itemDescription.innerText = element.description;
|
||||
}
|
||||
|
||||
// pseudo-select disabled option
|
||||
if (isDisabled) {
|
||||
data.root.classList.add('option-disabled');
|
||||
|
@ -705,7 +695,18 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
|
|||
keyboardSupport: false,
|
||||
mouseSupport: false,
|
||||
accessibilityProvider: {
|
||||
getAriaLabel: (element) => element.text,
|
||||
getAriaLabel: element => {
|
||||
let label = element.text;
|
||||
if (element.decoratorRight) {
|
||||
label += `. ${element.decoratorRight}`;
|
||||
}
|
||||
|
||||
if (element.description) {
|
||||
label += `. ${element.description}`;
|
||||
}
|
||||
|
||||
return label;
|
||||
},
|
||||
getWidgetAriaLabel: () => localize({ key: 'selectBox', comment: ['Behave like native select dropdown element.'] }, "Select Box"),
|
||||
getRole: () => 'option',
|
||||
getWidgetRole: () => 'listbox'
|
||||
|
|
|
@ -234,15 +234,13 @@ export class Separator extends Action {
|
|||
}
|
||||
}
|
||||
|
||||
export type SubmenuActions = IAction[] | (() => IAction[]);
|
||||
|
||||
export class SubmenuAction extends Action {
|
||||
|
||||
get actions(): IAction[] {
|
||||
return Array.isArray(this._actions) ? this._actions : this._actions();
|
||||
return this._actions;
|
||||
}
|
||||
|
||||
constructor(id: string, label: string, private _actions: SubmenuActions, cssClass?: string) {
|
||||
constructor(id: string, label: string, private _actions: IAction[], cssClass?: string) {
|
||||
super(id, label, cssClass, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,21 @@ export function raceCancellation<T>(promise: Promise<T>, token: CancellationToke
|
|||
return Promise.race([promise, new Promise<T | undefined>(resolve => token.onCancellationRequested(() => resolve(defaultValue)))]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns as soon as one of the promises is resolved and cancels remaining promises
|
||||
*/
|
||||
export async function raceCancellablePromises<T>(cancellablePromises: CancelablePromise<T>[]): Promise<T> {
|
||||
let resolvedPromiseIndex = -1;
|
||||
const promises = cancellablePromises.map((promise, index) => promise.then(result => { resolvedPromiseIndex = index; return result; }));
|
||||
const result = await Promise.race(promises);
|
||||
cancellablePromises.forEach((cancellablePromise, index) => {
|
||||
if (index !== resolvedPromiseIndex) {
|
||||
cancellablePromise.cancel();
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
export function raceTimeout<T>(promise: Promise<T>, timeout: number, onTimeout?: () => void): Promise<T | undefined> {
|
||||
let promiseResolve: ((value: T | undefined) => void) | undefined = undefined;
|
||||
|
||||
|
@ -149,13 +164,13 @@ export class Throttler {
|
|||
|
||||
this.activePromise = promiseFactory();
|
||||
|
||||
return new Promise((c, e) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.activePromise!.then((result: any) => {
|
||||
this.activePromise = null;
|
||||
c(result);
|
||||
resolve(result);
|
||||
}, (err: any) => {
|
||||
this.activePromise = null;
|
||||
e(err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -924,3 +939,38 @@ export class TaskSequentializer {
|
|||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region
|
||||
|
||||
/**
|
||||
* The `IntervalCounter` allows to count the number
|
||||
* of calls to `increment()` over a duration of
|
||||
* `interval`. This utility can be used to conditionally
|
||||
* throttle a frequent task when a certain threshold
|
||||
* is reached.
|
||||
*/
|
||||
export class IntervalCounter {
|
||||
|
||||
private lastIncrementTime = 0;
|
||||
|
||||
private value = 0;
|
||||
|
||||
constructor(private readonly interval: number) { }
|
||||
|
||||
increment(): number {
|
||||
const now = Date.now();
|
||||
|
||||
// We are outside of the range of `interval` and as such
|
||||
// start counting from 0 and remember the time
|
||||
if (now - this.lastIncrementTime > this.interval) {
|
||||
this.lastIncrementTime = now;
|
||||
this.value = 0;
|
||||
}
|
||||
|
||||
this.value++;
|
||||
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
|
|
@ -5,9 +5,7 @@
|
|||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { compareSubstringIgnoreCase, compare, compareSubstring } from 'vs/base/common/strings';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { isLinux } from 'vs/base/common/platform';
|
||||
import { compareSubstringIgnoreCase, compare, compareSubstring, compareIgnoreCase } from 'vs/base/common/strings';
|
||||
|
||||
export function getOrSet<K, V>(map: Map<K, V>, key: K, value: V): V {
|
||||
let result = map.get(key);
|
||||
|
@ -140,6 +138,8 @@ export class UriIterator implements IKeyIterator<URI> {
|
|||
private _states: UriIteratorState[] = [];
|
||||
private _stateIdx: number = 0;
|
||||
|
||||
constructor(private readonly _ignorePathCasing: boolean) { }
|
||||
|
||||
reset(key: URI): this {
|
||||
this._value = key;
|
||||
this._states = [];
|
||||
|
@ -150,10 +150,7 @@ export class UriIterator implements IKeyIterator<URI> {
|
|||
this._states.push(UriIteratorState.Authority);
|
||||
}
|
||||
if (this._value.path) {
|
||||
//todo@jrieken the case-sensitive logic is copied form `resources.ts#hasToIgnoreCase`
|
||||
// which cannot be used because it depends on this
|
||||
const caseSensitive = key.scheme === Schemas.file && isLinux;
|
||||
this._pathIterator = new PathIterator(false, caseSensitive);
|
||||
this._pathIterator = new PathIterator(false, !this._ignorePathCasing);
|
||||
this._pathIterator.reset(key.path);
|
||||
if (this._pathIterator.value()) {
|
||||
this._states.push(UriIteratorState.Path);
|
||||
|
@ -185,9 +182,9 @@ export class UriIterator implements IKeyIterator<URI> {
|
|||
|
||||
cmp(a: string): number {
|
||||
if (this._states[this._stateIdx] === UriIteratorState.Scheme) {
|
||||
return compare(a, this._value.scheme);
|
||||
return compareIgnoreCase(a, this._value.scheme);
|
||||
} else if (this._states[this._stateIdx] === UriIteratorState.Authority) {
|
||||
return compareSubstringIgnoreCase(a, this._value.authority);
|
||||
return compareIgnoreCase(a, this._value.authority);
|
||||
} else if (this._states[this._stateIdx] === UriIteratorState.Path) {
|
||||
return this._pathIterator.cmp(a);
|
||||
} else if (this._states[this._stateIdx] === UriIteratorState.Query) {
|
||||
|
@ -229,8 +226,8 @@ class TernarySearchTreeNode<K, V> {
|
|||
|
||||
export class TernarySearchTree<K, V> {
|
||||
|
||||
static forUris<E>(): TernarySearchTree<URI, E> {
|
||||
return new TernarySearchTree<URI, E>(new UriIterator());
|
||||
static forUris<E>(ignorePathCasing: boolean = false): TernarySearchTree<URI, E> {
|
||||
return new TernarySearchTree<URI, E>(new UriIterator(ignorePathCasing));
|
||||
}
|
||||
|
||||
static forPaths<E>(): TernarySearchTree<string, E> {
|
||||
|
@ -405,57 +402,44 @@ export class TernarySearchTree<K, V> {
|
|||
if (!node.mid) {
|
||||
return undefined;
|
||||
} else {
|
||||
return this._nodeIterator(node.mid);
|
||||
return this._values(node.mid);
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _nodeIterator(node: TernarySearchTreeNode<K, V>): Iterator<V> {
|
||||
let res: { done: false; value: V; };
|
||||
let idx: number;
|
||||
let data: V[];
|
||||
const next = (): IteratorResult<V> => {
|
||||
if (!data) {
|
||||
// lazy till first invocation
|
||||
data = [];
|
||||
idx = 0;
|
||||
this._forEach(node, value => data.push(value));
|
||||
}
|
||||
if (idx >= data.length) {
|
||||
return { done: true, value: undefined };
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
res = { done: false, value: data[idx++] };
|
||||
} else {
|
||||
res.value = data[idx++];
|
||||
}
|
||||
return res;
|
||||
};
|
||||
return { next };
|
||||
forEach(callback: (value: V, index: K) => any): void {
|
||||
for (const [key, value] of this) {
|
||||
callback(value, key);
|
||||
}
|
||||
}
|
||||
|
||||
forEach(callback: (value: V, index: K) => any) {
|
||||
this._forEach(this._root, callback);
|
||||
*[Symbol.iterator](): IterableIterator<[K, V]> {
|
||||
yield* this._entries(this._root);
|
||||
}
|
||||
|
||||
private _forEach(node: TernarySearchTreeNode<K, V> | undefined, callback: (value: V, index: K) => any) {
|
||||
private *_values(node: TernarySearchTreeNode<K, V>): IterableIterator<V> {
|
||||
for (const [, value] of this._entries(node)) {
|
||||
yield value;
|
||||
}
|
||||
}
|
||||
|
||||
private *_entries(node: TernarySearchTreeNode<K, V> | undefined): IterableIterator<[K, V]> {
|
||||
if (node) {
|
||||
// left
|
||||
this._forEach(node.left, callback);
|
||||
yield* this._entries(node.left);
|
||||
|
||||
// node
|
||||
if (node.value) {
|
||||
// callback(node.value, this._iter.join(parts));
|
||||
callback(node.value, node.key);
|
||||
yield [node.key, node.value];
|
||||
}
|
||||
// mid
|
||||
this._forEach(node.mid, callback);
|
||||
yield* this._entries(node.mid);
|
||||
|
||||
// right
|
||||
this._forEach(node.right, callback);
|
||||
yield* this._entries(node.right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,6 +135,8 @@ class FileAccessImpl {
|
|||
/**
|
||||
* Returns a URI to use in contexts where the browser is responsible
|
||||
* for loading (e.g. fetch()) or when used within the DOM.
|
||||
*
|
||||
* **Note:** use `dom.ts#asCSSUrl` whenever the URL is to be used in CSS context.
|
||||
*/
|
||||
asBrowserUri(uri: URI): URI;
|
||||
asBrowserUri(moduleId: string, moduleIdToUrl: { toUrl(moduleId: string): string }): URI;
|
||||
|
|
|
@ -318,16 +318,16 @@ export abstract class AbstractProcess<TProgressData> {
|
|||
}
|
||||
|
||||
private useExec(): Promise<boolean> {
|
||||
return new Promise<boolean>((c, e) => {
|
||||
return new Promise<boolean>(resolve => {
|
||||
if (!this.shell || !Platform.isWindows) {
|
||||
return c(false);
|
||||
return resolve(false);
|
||||
}
|
||||
const cmdShell = cp.spawn(getWindowsShell(), ['/s', '/c']);
|
||||
cmdShell.on('error', (error: Error) => {
|
||||
return c(true);
|
||||
return resolve(true);
|
||||
});
|
||||
cmdShell.on('exit', (data: any) => {
|
||||
return c(false);
|
||||
return resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -706,4 +706,17 @@ suite('Async', () => {
|
|||
const r3 = await s.queue('key2', () => Promise.resolve('hello'));
|
||||
assert.equal(r3, 'hello');
|
||||
});
|
||||
|
||||
test('IntervalCounter', async () => {
|
||||
const counter = new async.IntervalCounter(10);
|
||||
assert.equal(counter.increment(), 1);
|
||||
assert.equal(counter.increment(), 2);
|
||||
assert.equal(counter.increment(), 3);
|
||||
|
||||
await async.timeout(20);
|
||||
|
||||
assert.equal(counter.increment(), 1);
|
||||
assert.equal(counter.increment(), 2);
|
||||
assert.equal(counter.increment(), 3);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -368,7 +368,7 @@ suite('Map', () => {
|
|||
});
|
||||
|
||||
test('URIIterator', function () {
|
||||
const iter = new UriIterator();
|
||||
const iter = new UriIterator(false);
|
||||
iter.reset(URI.parse('file:///usr/bin/file.txt'));
|
||||
|
||||
assert.equal(iter.value(), 'file');
|
||||
|
@ -434,11 +434,22 @@ suite('Map', () => {
|
|||
map.forEach((value, key) => {
|
||||
assert.equal(trie.get(key), value);
|
||||
});
|
||||
|
||||
// forEach
|
||||
let forEachCount = 0;
|
||||
trie.forEach((element, key) => {
|
||||
assert.equal(element, map.get(key));
|
||||
map.delete(key);
|
||||
forEachCount++;
|
||||
});
|
||||
assert.equal(map.size, 0);
|
||||
assert.equal(map.size, forEachCount);
|
||||
|
||||
// iterator
|
||||
let iterCount = 0;
|
||||
for (let [key, value] of trie) {
|
||||
assert.equal(value, map.get(key));
|
||||
iterCount++;
|
||||
}
|
||||
assert.equal(map.size, iterCount);
|
||||
}
|
||||
|
||||
test('TernarySearchTree - set', function () {
|
||||
|
@ -609,7 +620,7 @@ suite('Map', () => {
|
|||
|
||||
|
||||
test('TernarySearchTree (URI) - basics', function () {
|
||||
let trie = new TernarySearchTree<URI, number>(new UriIterator());
|
||||
let trie = new TernarySearchTree<URI, number>(new UriIterator(false));
|
||||
|
||||
trie.set(URI.file('/user/foo/bar'), 1);
|
||||
trie.set(URI.file('/user/foo'), 2);
|
||||
|
@ -629,7 +640,7 @@ suite('Map', () => {
|
|||
|
||||
test('TernarySearchTree (URI) - lookup', function () {
|
||||
|
||||
const map = new TernarySearchTree<URI, number>(new UriIterator());
|
||||
const map = new TernarySearchTree<URI, number>(new UriIterator(false));
|
||||
map.set(URI.parse('http://foo.bar/user/foo/bar'), 1);
|
||||
map.set(URI.parse('http://foo.bar/user/foo?query'), 2);
|
||||
map.set(URI.parse('http://foo.bar/user/foo?QUERY'), 3);
|
||||
|
@ -646,7 +657,7 @@ suite('Map', () => {
|
|||
|
||||
test('TernarySearchTree (PathSegments) - superstr', function () {
|
||||
|
||||
const map = new TernarySearchTree<URI, number>(new UriIterator());
|
||||
const map = new TernarySearchTree<URI, number>(new UriIterator(false));
|
||||
map.set(URI.file('/user/foo/bar'), 1);
|
||||
map.set(URI.file('/user/foo'), 2);
|
||||
map.set(URI.file('/user/foo/flip/flop'), 3);
|
||||
|
|
|
@ -19,7 +19,7 @@ function getWorker(workerId: string, label: string): Worker | Promise<Worker> {
|
|||
// ESM-comment-begin
|
||||
if (typeof require === 'function') {
|
||||
// check if the JS lives on a different origin
|
||||
const workerMain = require.toUrl('./' + workerId);
|
||||
const workerMain = require.toUrl('./' + workerId); // explicitly using require.toUrl(), see https://github.com/microsoft/vscode/issues/107440#issuecomment-698982321
|
||||
const workerUrl = getWorkerBootstrapUrl(workerMain, label);
|
||||
return new Worker(workerUrl, { name: label });
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ export function getWorkerBootstrapUrl(scriptPath: string, label: string, forceDa
|
|||
// this is the cross-origin case
|
||||
// i.e. the webpage is running at a different origin than where the scripts are loaded from
|
||||
const myPath = 'vs/base/worker/defaultWorkerFactory.js';
|
||||
const workerBaseUrl = require.toUrl(myPath).slice(0, -myPath.length);
|
||||
const workerBaseUrl = require.toUrl(myPath).slice(0, -myPath.length); // explicitly using require.toUrl(), see https://github.com/microsoft/vscode/issues/107440#issuecomment-698982321
|
||||
const js = `/*${label}*/self.MonacoEnvironment={baseUrl: '${workerBaseUrl}'};importScripts('${scriptPath}');/*${label}*/`;
|
||||
if (forceDataUri) {
|
||||
const url = `data:text/javascript;charset=utf-8,${encodeURIComponent(js)}`;
|
||||
|
|
|
@ -35,7 +35,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';
|
||||
import { FileAccess, Schemas } from 'vs/base/common/network';
|
||||
import { ColorScheme } from 'vs/platform/theme/common/theme';
|
||||
|
||||
export interface IWindowCreationOptions {
|
||||
state: IWindowState;
|
||||
|
@ -784,7 +783,10 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
|||
windowConfiguration.fullscreen = this.isFullScreen;
|
||||
|
||||
// Set Accessibility Config
|
||||
windowConfiguration.colorScheme = (nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors) ? ColorScheme.HIGH_CONTRAST : nativeTheme.shouldUseDarkColors ? ColorScheme.DARK : ColorScheme.LIGHT;
|
||||
windowConfiguration.colorScheme = {
|
||||
dark: nativeTheme.shouldUseDarkColors,
|
||||
highContrast: nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors
|
||||
};
|
||||
windowConfiguration.autoDetectHighContrast = windowConfig?.autoDetectHighContrast ?? true;
|
||||
windowConfiguration.accessibilitySupport = app.accessibilitySupportEnabled;
|
||||
|
||||
|
|
|
@ -138,7 +138,7 @@ export async function main(argv: string[]): Promise<any> {
|
|||
child.stdout!.on('data', (data: Buffer) => console.log(data.toString('utf8').trim()));
|
||||
child.stderr!.on('data', (data: Buffer) => console.log(data.toString('utf8').trim()));
|
||||
|
||||
await new Promise<void>(c => child.once('exit', () => c()));
|
||||
await new Promise<void>(resolve => child.once('exit', () => resolve()));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -332,13 +332,13 @@ export async function main(argv: string[]): Promise<any> {
|
|||
const child = spawn(process.execPath, argv.slice(2), options);
|
||||
|
||||
if (args.wait && waitMarkerFilePath) {
|
||||
return new Promise<void>(c => {
|
||||
return new Promise<void>(resolve => {
|
||||
|
||||
// Complete when process exits
|
||||
child.once('exit', () => c(undefined));
|
||||
child.once('exit', () => resolve(undefined));
|
||||
|
||||
// Complete when wait marker file is deleted
|
||||
whenDeleted(waitMarkerFilePath!).then(c, c);
|
||||
whenDeleted(waitMarkerFilePath!).then(resolve, resolve);
|
||||
}).then(() => {
|
||||
|
||||
// Make sure to delete the tmp stdin file if we have any
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { isFirefox } from 'vs/base/browser/browser';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
|
@ -1640,6 +1641,11 @@ export namespace CoreNavigationCommands {
|
|||
super(SelectAllCommand);
|
||||
}
|
||||
public runDOMCommand(): void {
|
||||
if (isFirefox) {
|
||||
(<HTMLInputElement>document.activeElement).focus();
|
||||
(<HTMLInputElement>document.activeElement).select();
|
||||
}
|
||||
|
||||
document.execCommand('selectAll');
|
||||
}
|
||||
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
|
||||
|
|
|
@ -20,6 +20,11 @@
|
|||
color: var(--outline-element-color);
|
||||
}
|
||||
|
||||
.monaco-list .outline-element .monaco-icon-label-container .monaco-highlighted-label,
|
||||
.monaco-list .outline-element .monaco-icon-label-container .label-description {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.monaco-list .outline-element .outline-element-decoration {
|
||||
opacity: 0.75;
|
||||
font-size: 90%;
|
||||
|
|
|
@ -55,8 +55,7 @@ export class CompletionModel {
|
|||
private _lineContext: LineContext;
|
||||
private _refilterKind: Refilter;
|
||||
private _filteredItems?: StrictCompletionItem[];
|
||||
private _isIncomplete?: Set<CompletionItemProvider>;
|
||||
private _allProvider?: Set<CompletionItemProvider>; // TODO@jrieken merge incomplete and all provider info
|
||||
private _providerInfo?: Map<CompletionItemProvider, boolean>;
|
||||
private _stats?: ICompletionStats;
|
||||
|
||||
constructor(
|
||||
|
@ -100,14 +99,20 @@ export class CompletionModel {
|
|||
return this._filteredItems!;
|
||||
}
|
||||
|
||||
get allProvider(): Set<CompletionItemProvider> {
|
||||
get allProvider(): IterableIterator<CompletionItemProvider> {
|
||||
this._ensureCachedState();
|
||||
return this._allProvider!;
|
||||
return this._providerInfo!.keys();
|
||||
}
|
||||
|
||||
get incomplete(): Set<CompletionItemProvider> {
|
||||
this._ensureCachedState();
|
||||
return this._isIncomplete!;
|
||||
const result = new Set<CompletionItemProvider>();
|
||||
for (let [provider, incomplete] of this._providerInfo!) {
|
||||
if (incomplete) {
|
||||
result.add(provider);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
adopt(except: Set<CompletionItemProvider>): CompletionItem[] {
|
||||
|
@ -141,8 +146,7 @@ export class CompletionModel {
|
|||
|
||||
private _createCachedState(): void {
|
||||
|
||||
this._isIncomplete = new Set();
|
||||
this._allProvider = new Set();
|
||||
this._providerInfo = new Map();
|
||||
this._stats = { suggestionCount: 0, snippetCount: 0, textCount: 0 };
|
||||
|
||||
const { leadingLineContent, characterCountDelta } = this._lineContext;
|
||||
|
@ -166,12 +170,8 @@ export class CompletionModel {
|
|||
continue; // SKIP invalid items
|
||||
}
|
||||
|
||||
// collect those supports that signaled having
|
||||
// an incomplete result
|
||||
if (item.container.incomplete) {
|
||||
this._isIncomplete.add(item.provider);
|
||||
}
|
||||
this._allProvider.add(item.provider);
|
||||
// collect all support, know if their result is incomplete
|
||||
this._providerInfo.set(item.provider, Boolean(item.container.incomplete));
|
||||
|
||||
// 'word' is that remainder of the current line that we
|
||||
// filter and score against. In theory each suggestion uses a
|
||||
|
|
|
@ -4,66 +4,147 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Suggest widget*/
|
||||
|
||||
.monaco-editor .suggest-widget {
|
||||
width: 430px; /** Initial widths **/
|
||||
z-index: 40;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
/** Initial widths **/
|
||||
|
||||
.monaco-editor .suggest-widget {
|
||||
width: 430px;
|
||||
.monaco-editor .suggest-widget.docs-side {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget > .message,
|
||||
.monaco-editor .suggest-widget > .tree,
|
||||
.monaco-editor .suggest-widget > .details {
|
||||
.monaco-editor .suggest-widget.docs-side.reverse {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.docs-below {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.docs-below.reverse {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
/* --- fiddle with details margin so that borders merge/overlap */
|
||||
.monaco-editor .suggest-widget.docs-side>.details {
|
||||
margin: 0 0 0 -1px;
|
||||
}
|
||||
.monaco-editor .suggest-widget.docs-side.reverse>.details {
|
||||
margin: 0 -1px 0 0;
|
||||
}
|
||||
.monaco-editor.hc-black .suggest-widget.docs-side>.details {
|
||||
margin: 0 0 0 -2px;
|
||||
}
|
||||
.monaco-editor.hc-black .suggest-widget.docs-side.reverse>.details {
|
||||
margin: 0 -2px 0 0;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget>.message,
|
||||
.monaco-editor .suggest-widget>.tree,
|
||||
.monaco-editor .suggest-widget>.details {
|
||||
flex: 0 1 auto;
|
||||
width: 100%;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.monaco-editor.hc-black .suggest-widget > .message,
|
||||
.monaco-editor.hc-black .suggest-widget > .tree,
|
||||
.monaco-editor.hc-black .suggest-widget > .details {
|
||||
.monaco-editor .suggest-widget>.tree.docs-higher {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.monaco-editor.hc-black .suggest-widget>.message,
|
||||
.monaco-editor.hc-black .suggest-widget>.tree,
|
||||
.monaco-editor.hc-black .suggest-widget>.details {
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
/** Adjust width when docs are expanded to the side **/
|
||||
|
||||
.monaco-editor .suggest-widget.docs-side {
|
||||
width: 660px;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.docs-side > .tree,
|
||||
.monaco-editor .suggest-widget.docs-side > .details {
|
||||
.monaco-editor .suggest-widget.docs-side>.tree,
|
||||
.monaco-editor .suggest-widget.docs-side>.details {
|
||||
width: 50%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.docs-side.list-right > .tree,
|
||||
.monaco-editor .suggest-widget.docs-side.list-right > .details {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* MarkupContent Layout */
|
||||
.monaco-editor .suggest-widget > .details ul {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.monaco-editor .suggest-widget > .details ol {
|
||||
|
||||
.monaco-editor .suggest-widget>.details ul {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget > .details p code {
|
||||
.monaco-editor .suggest-widget>.details ol {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget>.details p code {
|
||||
font-family: var(--monaco-monospace-font);
|
||||
}
|
||||
|
||||
|
||||
/* Styles for status bar part */
|
||||
|
||||
|
||||
.monaco-editor .suggest-widget .suggest-status-bar {
|
||||
visibility: hidden;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
font-size: 80%;
|
||||
padding: 0 8px 0 4px;
|
||||
border-top: 1px solid transparent;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.list-right.docs-side .suggest-status-bar {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar .action-label {
|
||||
min-height: 18px;
|
||||
opacity: 0.5;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar .action-item:not(:last-of-type) .action-label {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar .action-item:not(:last-of-type) .action-label::after {
|
||||
content: ', ';
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar .monaco-list .monaco-list-row>.contents>.main>.right>.readMore,
|
||||
.monaco-editor .suggest-widget.with-status-bar .monaco-list .monaco-list-row.focused.string-label>.contents>.main>.right>.readMore {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar:not(.docs-side) .monaco-list .monaco-list-row:hover>.contents>.main>.right.can-expand-details>.details-label {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Styles for Message element for when widget is loading or is empty */
|
||||
.monaco-editor .suggest-widget > .message {
|
||||
|
||||
.monaco-editor .suggest-widget>.message {
|
||||
padding-left: 22px;
|
||||
}
|
||||
|
||||
/** Styles for the list element **/
|
||||
.monaco-editor .suggest-widget > .tree {
|
||||
|
||||
.monaco-editor .suggest-widget>.tree {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
@ -87,14 +168,14 @@
|
|||
touch-action: none;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
@ -102,8 +183,7 @@
|
|||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left,
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.left, .monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
|
@ -111,143 +191,108 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
/** Status Bar **/
|
||||
|
||||
.monaco-editor .suggest-widget > .suggest-status-bar {
|
||||
visibility: hidden;
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
|
||||
width: 100%;
|
||||
|
||||
font-size: 80%;
|
||||
|
||||
border-left-width: 1px;
|
||||
border-left-style: solid;
|
||||
border-right-width: 1px;
|
||||
border-right-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
|
||||
padding: 0 8px 0 4px;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.list-right.docs-side > .suggest-status-bar {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
.monaco-editor .suggest-widget.docs-side > .suggest-status-bar {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
/** ReadMore Icon styles **/
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .codicon-close,
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .readMore::before {
|
||||
.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.header>.codicon-close,
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right>.readMore::before {
|
||||
color: inherit;
|
||||
opacity: 1;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .codicon-close {
|
||||
.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.header>.codicon-close {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
top: 6px;
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .codicon-close:hover,
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .readMore:hover {
|
||||
.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.header>.codicon-close:hover, .monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right>.readMore:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/** signature, qualifier, type/details opacity **/
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .signature-label,
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .qualifier-label,
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label {
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.left>.signature-label,
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.left>.qualifier-label,
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right>.details-label {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .signature-label {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.left>.signature-label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .qualifier-label {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.left>.qualifier-label {
|
||||
margin-left: 4px;
|
||||
opacity: 0.4;
|
||||
font-size: 90%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
line-height: 17px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
/** Type Info and icon next to the label in the focused completion item **/
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label {
|
||||
margin-left: 0.8em;
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right>.details-label {
|
||||
margin-left: 1.1em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label > .monaco-tokenized-source {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right>.details-label>.monaco-tokenized-source {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/** Details: if using CompletionItem#details, show on focus **/
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label,
|
||||
.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .right > .details-label {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right>.details-label, .monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused>.contents>.main>.right>.details-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .right > .details-label {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused>.contents>.main>.right>.details-label {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/** Details: if using CompletionItemLabel#details, always show **/
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.string-label) > .contents > .main > .right > .details-label,
|
||||
.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused:not(.string-label) > .contents > .main > .right > .details-label {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.string-label)>.contents>.main>.right>.details-label, .monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused:not(.string-label)>.contents>.main>.right>.details-label {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/** Ellipsis on hover **/
|
||||
.monaco-editor .suggest-widget:not(.docs-side) .monaco-list .monaco-list-row:hover > .contents > .main > .right.can-expand-details > .details-label {
|
||||
|
||||
.monaco-editor .suggest-widget:not(.docs-side) .monaco-list .monaco-list-row:hover>.contents>.main>.right.can-expand-details>.details-label {
|
||||
width: calc(100% - 26px);
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.left {
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .monaco-icon-label {
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.left>.monaco-icon-label {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.string-label) > .contents > .main > .left > .monaco-icon-label {
|
||||
max-width: 100%;
|
||||
}
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.string-label > .contents > .main > .left > .monaco-icon-label {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right {
|
||||
overflow: hidden;
|
||||
margin-left: 16px;
|
||||
flex-shrink: 0;
|
||||
max-width: 45%;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .readMore {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.string-label)>.contents>.main>.left>.monaco-icon-label {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.string-label>.contents>.main>.left>.monaco-icon-label {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right {
|
||||
overflow: hidden;
|
||||
flex-shrink: 2;
|
||||
max-width: 77%;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.right>.readMore {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
|
@ -257,26 +302,28 @@
|
|||
}
|
||||
|
||||
/** Do NOT display ReadMore when docs is side/below **/
|
||||
.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row > .contents > .main > .right > .readMore,
|
||||
.monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row > .contents > .main > .right > .readMore {
|
||||
|
||||
.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row>.contents>.main>.right>.readMore, .monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row>.contents>.main>.right>.readMore {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/** Do NOT display ReadMore when using plain CompletionItemLabel (details/documentation might not be resolved) **/
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.string-label > .contents > .main > .right > .readMore {
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.string-label>.contents>.main>.right>.readMore {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/** Focused item can show ReadMore, but can't when docs is side/below **/
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused.string-label > .contents > .main > .right > .readMore {
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused.string-label>.contents>.main>.right>.readMore {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row > .contents > .main > .right > .readMore,
|
||||
.monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row > .contents > .main > .right > .readMore {
|
||||
.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row>.contents>.main>.right>.readMore, .monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row>.contents>.main>.right>.readMore {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row:hover > .contents > .main > .right > .readMore {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row:hover>.contents>.main>.right>.readMore {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
|
@ -286,7 +333,8 @@
|
|||
opacity: 0.66;
|
||||
text-decoration: unset;
|
||||
}
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.deprecated > .monaco-icon-label-container > .monaco-icon-name-container {
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.deprecated>.monaco-icon-label-container>.monaco-icon-name-container {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
|
@ -314,8 +362,7 @@
|
|||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.no-icons .monaco-list .monaco-list-row .icon,
|
||||
.monaco-editor .suggest-widget.no-icons .monaco-list .monaco-list-row .suggest-icon::before {
|
||||
.monaco-editor .suggest-widget.no-icons .monaco-list .monaco-list-row .icon, .monaco-editor .suggest-widget.no-icons .monaco-list .monaco-list-row .suggest-icon::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -328,6 +375,7 @@
|
|||
}
|
||||
|
||||
/** Styles for the docs of the completion item in focus **/
|
||||
|
||||
.monaco-editor .suggest-widget .details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -340,13 +388,19 @@
|
|||
|
||||
.monaco-editor .suggest-widget.docs-below .details {
|
||||
border-top-width: 0;
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element {
|
||||
.monaco-editor .suggest-widget.docs-below.reverse .details {
|
||||
border-bottom-width: 0;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details>.monaco-scrollable-element {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body {
|
||||
.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
|
@ -354,51 +408,50 @@
|
|||
padding-right: 22px;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .type {
|
||||
.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.header>.type {
|
||||
flex: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
opacity: 0.7;
|
||||
word-break: break-all;
|
||||
white-space: nowrap;
|
||||
margin: 0 24px 0 0;
|
||||
padding: 4px 0 12px 5px;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs {
|
||||
.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.docs {
|
||||
margin: 0;
|
||||
padding: 4px 5px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs {
|
||||
.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.docs.markdown-docs {
|
||||
padding: 0;
|
||||
white-space: initial;
|
||||
min-height: calc(1rem + 8px);
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs > div,
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs > span:not(:empty) {
|
||||
.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.docs.markdown-docs>div, .monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.docs.markdown-docs>span:not(:empty) {
|
||||
padding: 4px 5px;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs > div > p:first-child {
|
||||
.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.docs.markdown-docs>div>p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs > div > p:last-child {
|
||||
.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.docs.markdown-docs>div>p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs .code {
|
||||
.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.docs .code {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs .codicon {
|
||||
.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.docs.markdown-docs .codicon {
|
||||
vertical-align: sub;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > p:empty {
|
||||
.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>p:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -406,11 +459,3 @@
|
|||
border-radius: 3px;
|
||||
padding: 0 0.4em;
|
||||
}
|
||||
|
||||
|
||||
/* replace/insert decorations */
|
||||
|
||||
.monaco-editor .suggest-insert-unexpected {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar {
|
||||
visibility: visible;
|
||||
}
|
||||
.monaco-editor .suggest-widget.with-status-bar > .tree {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar .action-label {
|
||||
min-height: 18px;
|
||||
opacity: 0.5;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar .action-item:not(:last-of-type) .action-label {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar .action-item:not(:last-of-type) .action-label::after {
|
||||
content: ', ';
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar .monaco-list .monaco-list-row > .contents > .main > .right > .readMore,
|
||||
.monaco-editor .suggest-widget.with-status-bar .monaco-list .monaco-list-row.focused.string-label > .contents > .main > .right > .readMore {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar:not(.docs-side) .monaco-list .monaco-list-row:hover > .contents > .main > .right.can-expand-details > .details-label {
|
||||
width: 100%;
|
||||
}
|
|
@ -559,7 +559,9 @@ export class SuggestModel implements IDisposable {
|
|||
// Select those providers have not contributed to this completion model and re-trigger completions for
|
||||
// them. Also adopt the existing items and merge them into the new completion model
|
||||
const inactiveProvider = new Set(CompletionProviderRegistry.all(this._editor.getModel()!));
|
||||
this._completionModel.allProvider.forEach(provider => inactiveProvider.delete(provider));
|
||||
for (let provider of this._completionModel.allProvider) {
|
||||
inactiveProvider.delete(provider);
|
||||
}
|
||||
const items = this._completionModel.adopt(new Set());
|
||||
this.trigger({ auto: this._context.auto, shy: false }, true, inactiveProvider, items);
|
||||
return;
|
||||
|
|
|
@ -4,24 +4,21 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/suggest';
|
||||
import 'vs/css!./media/suggestStatusBar';
|
||||
import 'vs/base/browser/ui/codicons/codiconStyles'; // The codicon symbol styles are defined here and must be loaded
|
||||
import 'vs/editor/contrib/documentSymbols/outlineTree'; // The codicon symbol colors are defined here and must be loaded
|
||||
import * as nls from 'vs/nls';
|
||||
import { createMatches } from 'vs/base/common/filters';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { IDisposable, dispose, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { append, $, hide, show, getDomNodePagePosition, addDisposableListener, addStandardDisposableListener } from 'vs/base/browser/dom';
|
||||
import { IListVirtualDelegate, IListEvent, IListRenderer, IListMouseEvent, IListGestureEvent } from 'vs/base/browser/ui/list/list';
|
||||
import { IListVirtualDelegate, IListEvent, IListMouseEvent, IListGestureEvent } from 'vs/base/browser/ui/list/list';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser';
|
||||
import { Context as SuggestContext, CompletionItem, suggestWidgetStatusbarMenu } from './suggest';
|
||||
import { Context as SuggestContext, CompletionItem } from './suggest';
|
||||
import { CompletionModel } from './completionModel';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
|
@ -32,50 +29,15 @@ import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
|
|||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { TimeoutTimer, CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async';
|
||||
import { CompletionItemKind, completionKindToCssClass, CompletionItemTag } from 'vs/editor/common/modes';
|
||||
import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { flatten, isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { IMenuService } from 'vs/platform/actions/common/actions';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IAction, IActionViewItemProvider } from 'vs/base/common/actions';
|
||||
import { Codicon, registerIcon } from 'vs/base/common/codicons';
|
||||
import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
|
||||
import { SuggestionDetails, canExpandCompletionItem } from './suggestWidgetDetails';
|
||||
import { SuggestWidgetStatus } from 'vs/editor/contrib/suggest/suggestWidgetStatus';
|
||||
import { getAriaId, ItemRenderer } from './suggestWidgetRenderer';
|
||||
|
||||
const expandSuggestionDocsByDefault = false;
|
||||
|
||||
const suggestMoreInfoIcon = registerIcon('suggest-more-info', Codicon.chevronRight);
|
||||
|
||||
interface ISuggestionTemplateData {
|
||||
root: HTMLElement;
|
||||
|
||||
/**
|
||||
* Flexbox
|
||||
* < ------------- left ------------ > < --- right -- >
|
||||
* <icon><label><signature><qualifier> <type><readmore>
|
||||
*/
|
||||
left: HTMLElement;
|
||||
right: HTMLElement;
|
||||
|
||||
icon: HTMLElement;
|
||||
colorspan: HTMLElement;
|
||||
iconLabel: IconLabel;
|
||||
iconContainer: HTMLElement;
|
||||
parametersLabel: HTMLElement;
|
||||
qualifierLabel: HTMLElement;
|
||||
/**
|
||||
* Showing either `CompletionItem#details` or `CompletionItemLabel#type`
|
||||
*/
|
||||
detailsLabel: HTMLElement;
|
||||
readMore: HTMLElement;
|
||||
disposables: DisposableStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Suggest widget colors
|
||||
|
@ -86,198 +48,8 @@ export const editorSuggestWidgetForeground = registerColor('editorSuggestWidget.
|
|||
export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: listFocusBackground, light: listFocusBackground, hc: listFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.'));
|
||||
export const editorSuggestWidgetHighlightForeground = registerColor('editorSuggestWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hc: listHighlightForeground }, nls.localize('editorSuggestWidgetHighlightForeground', 'Color of the match highlights in the suggest widget.'));
|
||||
|
||||
const colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i;
|
||||
function extractColor(item: CompletionItem, out: string[]): boolean {
|
||||
const label = typeof item.completion.label === 'string'
|
||||
? item.completion.label
|
||||
: item.completion.label.name;
|
||||
|
||||
if (label.match(colorRegExp)) {
|
||||
out[0] = label;
|
||||
return true;
|
||||
}
|
||||
if (typeof item.completion.documentation === 'string' && item.completion.documentation.match(colorRegExp)) {
|
||||
out[0] = item.completion.documentation;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function canExpandCompletionItem(item: CompletionItem | null) {
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
const suggestion = item.completion;
|
||||
if (suggestion.documentation) {
|
||||
return true;
|
||||
}
|
||||
return (suggestion.detail && suggestion.detail !== suggestion.label);
|
||||
}
|
||||
|
||||
function getAriaId(index: number): string {
|
||||
return `suggest-aria-id:${index}`;
|
||||
}
|
||||
|
||||
class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTemplateData> {
|
||||
|
||||
constructor(
|
||||
private widget: SuggestWidget,
|
||||
private editor: ICodeEditor,
|
||||
private triggerKeybindingLabel: string,
|
||||
@IModelService private readonly _modelService: IModelService,
|
||||
@IModeService private readonly _modeService: IModeService,
|
||||
@IThemeService private readonly _themeService: IThemeService,
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
get templateId(): string {
|
||||
return 'suggestion';
|
||||
}
|
||||
|
||||
renderTemplate(container: HTMLElement): ISuggestionTemplateData {
|
||||
const data = <ISuggestionTemplateData>Object.create(null);
|
||||
data.disposables = new DisposableStore();
|
||||
|
||||
data.root = container;
|
||||
data.root.classList.add('show-file-icons');
|
||||
|
||||
data.icon = append(container, $('.icon'));
|
||||
data.colorspan = append(data.icon, $('span.colorspan'));
|
||||
|
||||
const text = append(container, $('.contents'));
|
||||
const main = append(text, $('.main'));
|
||||
|
||||
data.iconContainer = append(main, $('.icon-label.codicon'));
|
||||
data.left = append(main, $('span.left'));
|
||||
data.right = append(main, $('span.right'));
|
||||
|
||||
data.iconLabel = new IconLabel(data.left, { supportHighlights: true, supportCodicons: true });
|
||||
data.disposables.add(data.iconLabel);
|
||||
|
||||
data.parametersLabel = append(data.left, $('span.signature-label'));
|
||||
data.qualifierLabel = append(data.left, $('span.qualifier-label'));
|
||||
data.detailsLabel = append(data.right, $('span.details-label'));
|
||||
|
||||
data.readMore = append(data.right, $('span.readMore' + suggestMoreInfoIcon.cssSelector));
|
||||
data.readMore.title = nls.localize('readMore', "Read More ({0})", this.triggerKeybindingLabel);
|
||||
|
||||
const configureFont = () => {
|
||||
const options = this.editor.getOptions();
|
||||
const fontInfo = options.get(EditorOption.fontInfo);
|
||||
const fontFamily = fontInfo.fontFamily;
|
||||
const fontFeatureSettings = fontInfo.fontFeatureSettings;
|
||||
const fontSize = options.get(EditorOption.suggestFontSize) || fontInfo.fontSize;
|
||||
const lineHeight = options.get(EditorOption.suggestLineHeight) || fontInfo.lineHeight;
|
||||
const fontWeight = fontInfo.fontWeight;
|
||||
const fontSizePx = `${fontSize}px`;
|
||||
const lineHeightPx = `${lineHeight}px`;
|
||||
|
||||
data.root.style.fontSize = fontSizePx;
|
||||
data.root.style.fontWeight = fontWeight;
|
||||
main.style.fontFamily = fontFamily;
|
||||
main.style.fontFeatureSettings = fontFeatureSettings;
|
||||
main.style.lineHeight = lineHeightPx;
|
||||
data.icon.style.height = lineHeightPx;
|
||||
data.icon.style.width = lineHeightPx;
|
||||
data.readMore.style.height = lineHeightPx;
|
||||
data.readMore.style.width = lineHeightPx;
|
||||
};
|
||||
|
||||
configureFont();
|
||||
|
||||
data.disposables.add(Event.chain<ConfigurationChangedEvent>(this.editor.onDidChangeConfiguration.bind(this.editor))
|
||||
.filter(e => e.hasChanged(EditorOption.fontInfo) || e.hasChanged(EditorOption.suggestFontSize) || e.hasChanged(EditorOption.suggestLineHeight))
|
||||
.on(configureFont, null));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
renderElement(element: CompletionItem, index: number, templateData: ISuggestionTemplateData): void {
|
||||
const data = <ISuggestionTemplateData>templateData;
|
||||
const suggestion = (<CompletionItem>element).completion;
|
||||
const textLabel = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name;
|
||||
|
||||
data.root.id = getAriaId(index);
|
||||
data.colorspan.style.backgroundColor = '';
|
||||
|
||||
const labelOptions: IIconLabelValueOptions = {
|
||||
labelEscapeNewLines: true,
|
||||
matches: createMatches(element.score)
|
||||
};
|
||||
|
||||
let color: string[] = [];
|
||||
if (suggestion.kind === CompletionItemKind.Color && extractColor(element, color)) {
|
||||
// special logic for 'color' completion items
|
||||
data.icon.className = 'icon customcolor';
|
||||
data.iconContainer.className = 'icon hide';
|
||||
data.colorspan.style.backgroundColor = color[0];
|
||||
|
||||
} else if (suggestion.kind === CompletionItemKind.File && this._themeService.getFileIconTheme().hasFileIcons) {
|
||||
// special logic for 'file' completion items
|
||||
data.icon.className = 'icon hide';
|
||||
data.iconContainer.className = 'icon hide';
|
||||
const labelClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: textLabel }), FileKind.FILE);
|
||||
const detailClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FILE);
|
||||
labelOptions.extraClasses = labelClasses.length > detailClasses.length ? labelClasses : detailClasses;
|
||||
|
||||
} else if (suggestion.kind === CompletionItemKind.Folder && this._themeService.getFileIconTheme().hasFolderIcons) {
|
||||
// special logic for 'folder' completion items
|
||||
data.icon.className = 'icon hide';
|
||||
data.iconContainer.className = 'icon hide';
|
||||
labelOptions.extraClasses = flatten([
|
||||
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: textLabel }), FileKind.FOLDER),
|
||||
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FOLDER)
|
||||
]);
|
||||
} else {
|
||||
// normal icon
|
||||
data.icon.className = 'icon hide';
|
||||
data.iconContainer.className = '';
|
||||
data.iconContainer.classList.add('suggest-icon', ...completionKindToCssClass(suggestion.kind).split(' '));
|
||||
}
|
||||
|
||||
if (suggestion.tags && suggestion.tags.indexOf(CompletionItemTag.Deprecated) >= 0) {
|
||||
labelOptions.extraClasses = (labelOptions.extraClasses || []).concat(['deprecated']);
|
||||
labelOptions.matches = [];
|
||||
}
|
||||
|
||||
data.iconLabel.setLabel(textLabel, undefined, labelOptions);
|
||||
if (typeof suggestion.label === 'string') {
|
||||
data.parametersLabel.textContent = '';
|
||||
data.qualifierLabel.textContent = '';
|
||||
data.detailsLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, '');
|
||||
data.root.classList.add('string-label');
|
||||
} else {
|
||||
data.parametersLabel.textContent = (suggestion.label.parameters || '').replace(/\n.*$/m, '');
|
||||
data.qualifierLabel.textContent = (suggestion.label.qualifier || '').replace(/\n.*$/m, '');
|
||||
data.detailsLabel.textContent = (suggestion.label.type || '').replace(/\n.*$/m, '');
|
||||
data.root.classList.remove('string-label');
|
||||
}
|
||||
|
||||
if (canExpandCompletionItem(element)) {
|
||||
data.right.classList.add('can-expand-details');
|
||||
show(data.readMore);
|
||||
data.readMore.onmousedown = e => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
data.readMore.onclick = e => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.widget.toggleDetails();
|
||||
};
|
||||
} else {
|
||||
data.right.classList.remove('can-expand-details');
|
||||
hide(data.readMore);
|
||||
data.readMore.onmousedown = null;
|
||||
data.readMore.onclick = null;
|
||||
}
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: ISuggestionTemplateData): void {
|
||||
templateData.disposables.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
const enum State {
|
||||
Hidden,
|
||||
|
@ -289,176 +61,6 @@ const enum State {
|
|||
}
|
||||
|
||||
|
||||
class SuggestionDetails {
|
||||
|
||||
private el: HTMLElement;
|
||||
private close: HTMLElement;
|
||||
private scrollbar: DomScrollableElement;
|
||||
private body: HTMLElement;
|
||||
private header: HTMLElement;
|
||||
private type: HTMLElement;
|
||||
private docs: HTMLElement;
|
||||
private readonly disposables: DisposableStore;
|
||||
private renderDisposeable?: IDisposable;
|
||||
private borderWidth: number = 1;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
private readonly widget: SuggestWidget,
|
||||
private readonly editor: ICodeEditor,
|
||||
private readonly markdownRenderer: MarkdownRenderer,
|
||||
private readonly kbToggleDetails: string,
|
||||
) {
|
||||
this.disposables = new DisposableStore();
|
||||
|
||||
this.el = append(container, $('.details'));
|
||||
this.disposables.add(toDisposable(() => container.removeChild(this.el)));
|
||||
|
||||
this.body = $('.body');
|
||||
|
||||
this.scrollbar = new DomScrollableElement(this.body, {});
|
||||
append(this.el, this.scrollbar.getDomNode());
|
||||
this.disposables.add(this.scrollbar);
|
||||
|
||||
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.type = append(this.header, $('p.type'));
|
||||
|
||||
this.docs = append(this.body, $('p.docs'));
|
||||
|
||||
this.configureFont();
|
||||
|
||||
Event.chain<ConfigurationChangedEvent>(this.editor.onDidChangeConfiguration.bind(this.editor))
|
||||
.filter(e => e.hasChanged(EditorOption.fontInfo))
|
||||
.on(this.configureFont, this, this.disposables);
|
||||
|
||||
markdownRenderer.onDidRenderCodeBlock(() => this.scrollbar.scanDomNode(), this, this.disposables);
|
||||
}
|
||||
|
||||
get element() {
|
||||
return this.el;
|
||||
}
|
||||
|
||||
renderLoading(): void {
|
||||
this.type.textContent = nls.localize('loading', "Loading...");
|
||||
this.docs.textContent = '';
|
||||
}
|
||||
|
||||
renderItem(item: CompletionItem, explainMode: boolean): void {
|
||||
dispose(this.renderDisposeable);
|
||||
this.renderDisposeable = undefined;
|
||||
|
||||
let { documentation, detail } = item.completion;
|
||||
// --- documentation
|
||||
|
||||
if (explainMode) {
|
||||
let md = '';
|
||||
md += `score: ${item.score[0]}${item.word ? `, compared '${item.completion.filterText && (item.completion.filterText + ' (filterText)') || item.completion.label}' with '${item.word}'` : ' (no prefix)'}\n`;
|
||||
md += `distance: ${item.distance}, see localityBonus-setting\n`;
|
||||
md += `index: ${item.idx}, based on ${item.completion.sortText && `sortText: "${item.completion.sortText}"` || 'label'}\n`;
|
||||
documentation = new MarkdownString().appendCodeblock('empty', md);
|
||||
detail = `Provider: ${item.provider._debugDisplayName}`;
|
||||
}
|
||||
|
||||
if (!explainMode && !canExpandCompletionItem(item)) {
|
||||
this.type.textContent = '';
|
||||
this.docs.textContent = '';
|
||||
this.el.classList.add('no-docs');
|
||||
return;
|
||||
}
|
||||
this.el.classList.remove('no-docs');
|
||||
if (typeof documentation === 'string') {
|
||||
this.docs.classList.remove('markdown-docs');
|
||||
this.docs.textContent = documentation;
|
||||
} else {
|
||||
this.docs.classList.add('markdown-docs');
|
||||
this.docs.innerText = '';
|
||||
const renderedContents = this.markdownRenderer.render(documentation);
|
||||
this.renderDisposeable = renderedContents;
|
||||
this.docs.appendChild(renderedContents.element);
|
||||
}
|
||||
|
||||
// --- details
|
||||
if (detail) {
|
||||
this.type.innerText = detail.length > 100_000 ? `${detail.substr(0, 100_000)}…` : detail;
|
||||
show(this.type);
|
||||
} else {
|
||||
this.type.innerText = '';
|
||||
hide(this.type);
|
||||
}
|
||||
|
||||
this.el.style.height = this.header.offsetHeight + this.docs.offsetHeight + (this.borderWidth * 2) + 'px';
|
||||
this.el.style.userSelect = 'text';
|
||||
this.el.tabIndex = -1;
|
||||
|
||||
this.close.onmousedown = e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
this.close.onclick = e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.widget.toggleDetails();
|
||||
};
|
||||
|
||||
this.body.scrollTop = 0;
|
||||
this.scrollbar.scanDomNode();
|
||||
}
|
||||
|
||||
scrollDown(much = 8): void {
|
||||
this.body.scrollTop += much;
|
||||
}
|
||||
|
||||
scrollUp(much = 8): void {
|
||||
this.body.scrollTop -= much;
|
||||
}
|
||||
|
||||
scrollTop(): void {
|
||||
this.body.scrollTop = 0;
|
||||
}
|
||||
|
||||
scrollBottom(): void {
|
||||
this.body.scrollTop = this.body.scrollHeight;
|
||||
}
|
||||
|
||||
pageDown(): void {
|
||||
this.scrollDown(80);
|
||||
}
|
||||
|
||||
pageUp(): void {
|
||||
this.scrollUp(80);
|
||||
}
|
||||
|
||||
setBorderWidth(width: number): void {
|
||||
this.borderWidth = width;
|
||||
}
|
||||
|
||||
private configureFont() {
|
||||
const options = this.editor.getOptions();
|
||||
const fontInfo = options.get(EditorOption.fontInfo);
|
||||
const fontFamily = fontInfo.fontFamily;
|
||||
const fontSize = options.get(EditorOption.suggestFontSize) || fontInfo.fontSize;
|
||||
const lineHeight = options.get(EditorOption.suggestLineHeight) || fontInfo.lineHeight;
|
||||
const fontWeight = fontInfo.fontWeight;
|
||||
const fontSizePx = `${fontSize}px`;
|
||||
const lineHeightPx = `${lineHeight}px`;
|
||||
|
||||
this.el.style.fontSize = fontSizePx;
|
||||
this.el.style.fontWeight = fontWeight;
|
||||
this.el.style.fontFeatureSettings = fontInfo.fontFeatureSettings;
|
||||
this.type.style.fontFamily = fontFamily;
|
||||
this.close.style.height = lineHeightPx;
|
||||
this.close.style.width = lineHeightPx;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables.dispose();
|
||||
dispose(this.renderDisposeable);
|
||||
this.renderDisposeable = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ISelectedSuggestion {
|
||||
item: CompletionItem;
|
||||
index: number;
|
||||
|
@ -480,17 +82,18 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
private isAddedAsContentWidget: boolean = false;
|
||||
private isAuto: boolean = false;
|
||||
private loadingTimeout: IDisposable = Disposable.None;
|
||||
private currentSuggestionDetails: CancelablePromise<void> | null = null;
|
||||
private focusedItem: CompletionItem | null;
|
||||
private currentSuggestionDetails?: CancelablePromise<void>;
|
||||
private focusedItem?: CompletionItem;
|
||||
private ignoreFocusEvents: boolean = false;
|
||||
private completionModel: CompletionModel | null = null;
|
||||
private completionModel?: CompletionModel;
|
||||
|
||||
private element: HTMLElement;
|
||||
private messageElement: HTMLElement;
|
||||
private listElement: HTMLElement;
|
||||
private statusBarElement: HTMLElement;
|
||||
private details: SuggestionDetails;
|
||||
private mainElement: HTMLElement;
|
||||
private listContainer: HTMLElement;
|
||||
private list: List<CompletionItem>;
|
||||
private status: SuggestWidgetStatus;
|
||||
private details: SuggestionDetails;
|
||||
private listHeight?: number;
|
||||
|
||||
private readonly ctxSuggestWidgetVisible: IContextKey<boolean>;
|
||||
|
@ -498,7 +101,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
private readonly ctxSuggestWidgetMultipleSuggestions: IContextKey<boolean>;
|
||||
|
||||
private readonly showTimeout = new TimeoutTimer();
|
||||
private readonly toDispose = new DisposableStore();
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
private readonly onDidSelectEmitter = new Emitter<ISelectedSuggestion>();
|
||||
private readonly onDidFocusEmitter = new Emitter<ISelectedSuggestion>();
|
||||
|
@ -512,14 +115,13 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
|
||||
private readonly maxWidgetWidth = 660;
|
||||
private readonly listWidth = 330;
|
||||
private readonly storageService: IStorageService;
|
||||
private detailsFocusBorderColor?: string;
|
||||
private detailsBorderColor?: string;
|
||||
|
||||
private firstFocusInCurrentList: boolean = false;
|
||||
|
||||
private preferDocPositionTop: boolean = false;
|
||||
private docsPositionPreviousWidgetY: number | null = null;
|
||||
private docsPositionPreviousWidgetY?: number;
|
||||
private explainMode: boolean = false;
|
||||
|
||||
private readonly _onDetailsKeydown = new Emitter<IKeyboardEvent>();
|
||||
|
@ -528,90 +130,48 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
constructor(
|
||||
private readonly editor: ICodeEditor,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IModeService modeService: IModeService,
|
||||
@IOpenerService openerService: IOpenerService,
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
) {
|
||||
const markdownRenderer = this.toDispose.add(new MarkdownRenderer(editor, modeService, openerService));
|
||||
|
||||
const markdownRenderer = this._disposables.add(new MarkdownRenderer(editor, modeService, openerService));
|
||||
const kbToggleDetails = keybindingService.lookupKeybinding('toggleSuggestionDetails')?.getLabel() ?? '';
|
||||
|
||||
this.isAuto = false;
|
||||
this.focusedItem = null;
|
||||
this.storageService = storageService;
|
||||
|
||||
this.element = $('.editor-widget.suggest-widget');
|
||||
this.toDispose.add(addDisposableListener(this.element, 'click', e => {
|
||||
this._disposables.add(addDisposableListener(this.element, 'click', e => {
|
||||
if (e.target === this.element) {
|
||||
this.hideWidget();
|
||||
}
|
||||
}));
|
||||
|
||||
this.messageElement = append(this.element, $('.message'));
|
||||
this.listElement = append(this.element, $('.tree'));
|
||||
this.mainElement = append(this.element, $('.tree'));
|
||||
|
||||
const applyStatusBarStyle = () => this.element.classList.toggle('with-status-bar', this.editor.getOption(EditorOption.suggest).statusBar.visible);
|
||||
applyStatusBarStyle();
|
||||
|
||||
this.statusBarElement = append(this.element, $('.suggest-status-bar'));
|
||||
const actionViewItemProvider = <IActionViewItemProvider>(action => {
|
||||
const kb = keybindingService.lookupKeybindings(action.id);
|
||||
return new class extends ActionViewItem {
|
||||
constructor() {
|
||||
super(undefined, action, { label: true, icon: false });
|
||||
}
|
||||
updateLabel() {
|
||||
if (isFalsyOrEmpty(kb) || !this.label) {
|
||||
return super.updateLabel();
|
||||
}
|
||||
const { label } = this.getAction();
|
||||
this.label.textContent = /{\d}/.test(label)
|
||||
? strings.format(this.getAction().label, kb[0].getLabel())
|
||||
: `${this.getAction().label} (${kb[0].getLabel()})`;
|
||||
}
|
||||
};
|
||||
});
|
||||
const leftActions = new ActionBar(this.statusBarElement, { actionViewItemProvider });
|
||||
const rightActions = new ActionBar(this.statusBarElement, { actionViewItemProvider });
|
||||
const menu = menuService.createMenu(suggestWidgetStatusbarMenu, contextKeyService);
|
||||
const renderMenu = () => {
|
||||
const left: IAction[] = [];
|
||||
const right: IAction[] = [];
|
||||
for (let [group, actions] of menu.getActions()) {
|
||||
if (group === 'left') {
|
||||
left.push(...actions);
|
||||
} else {
|
||||
right.push(...actions);
|
||||
}
|
||||
}
|
||||
leftActions.clear();
|
||||
leftActions.push(left);
|
||||
rightActions.clear();
|
||||
rightActions.push(right);
|
||||
};
|
||||
this.toDispose.add(menu.onDidChange(() => renderMenu()));
|
||||
this.toDispose.add(menu);
|
||||
|
||||
this.details = instantiationService.createInstance(SuggestionDetails, this.element, this, this.editor, markdownRenderer, kbToggleDetails);
|
||||
this.details = instantiationService.createInstance(SuggestionDetails, this.element, this.editor, markdownRenderer, kbToggleDetails);
|
||||
this.details.onDidClose(this.toggleDetails, this, this._disposables);
|
||||
hide(this.details.element);
|
||||
|
||||
const applyIconStyle = () => this.element.classList.toggle('no-icons', !this.editor.getOption(EditorOption.suggest).showIcons);
|
||||
applyIconStyle();
|
||||
|
||||
let renderer = instantiationService.createInstance(ItemRenderer, this, this.editor, kbToggleDetails);
|
||||
this.listContainer = append(this.mainElement, $('.list-container'));
|
||||
|
||||
this.list = new List('SuggestWidget', this.listElement, this, [renderer], {
|
||||
const renderer = instantiationService.createInstance(ItemRenderer, this.editor, kbToggleDetails);
|
||||
this._disposables.add(renderer);
|
||||
this._disposables.add(renderer.onDidToggleDetails(() => this.toggleDetails()));
|
||||
|
||||
this.list = new List('SuggestWidget', this.listContainer, this, [renderer], {
|
||||
useShadows: false,
|
||||
mouseSupport: false,
|
||||
accessibilityProvider: {
|
||||
getRole: () => 'option',
|
||||
getAriaLabel: (item: CompletionItem) => {
|
||||
const textLabel = typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.name;
|
||||
if (item.isResolved && this.expandDocsSettingFromStorage()) {
|
||||
if (item.isResolved && this._isDetailsVisible()) {
|
||||
const { documentation, detail } = item.completion;
|
||||
const docs = strings.format(
|
||||
'{0}{1}',
|
||||
|
@ -628,18 +188,22 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
}
|
||||
});
|
||||
|
||||
this.toDispose.add(attachListStyler(this.list, themeService, {
|
||||
this.status = instantiationService.createInstance(SuggestWidgetStatus, this.mainElement);
|
||||
const applyStatusBarStyle = () => this.element.classList.toggle('with-status-bar', this.editor.getOption(EditorOption.suggest).statusBar.visible);
|
||||
applyStatusBarStyle();
|
||||
|
||||
this._disposables.add(attachListStyler(this.list, themeService, {
|
||||
listInactiveFocusBackground: editorSuggestWidgetSelectedBackground,
|
||||
listInactiveFocusOutline: activeContrastBorder
|
||||
}));
|
||||
this.toDispose.add(themeService.onDidColorThemeChange(t => this.onThemeChange(t)));
|
||||
this.toDispose.add(editor.onDidLayoutChange(() => this.onEditorLayoutChange()));
|
||||
this.toDispose.add(this.list.onMouseDown(e => this.onListMouseDownOrTap(e)));
|
||||
this.toDispose.add(this.list.onTap(e => this.onListMouseDownOrTap(e)));
|
||||
this.toDispose.add(this.list.onDidChangeSelection(e => this.onListSelection(e)));
|
||||
this.toDispose.add(this.list.onDidChangeFocus(e => this.onListFocus(e)));
|
||||
this.toDispose.add(this.editor.onDidChangeCursorSelection(() => this.onCursorSelectionChanged()));
|
||||
this.toDispose.add(this.editor.onDidChangeConfiguration(e => {
|
||||
this._disposables.add(themeService.onDidColorThemeChange(t => this.onThemeChange(t)));
|
||||
this._disposables.add(editor.onDidLayoutChange(() => this.onEditorLayoutChange()));
|
||||
this._disposables.add(this.list.onMouseDown(e => this.onListMouseDownOrTap(e)));
|
||||
this._disposables.add(this.list.onTap(e => this.onListMouseDownOrTap(e)));
|
||||
this._disposables.add(this.list.onDidChangeSelection(e => this.onListSelection(e)));
|
||||
this._disposables.add(this.list.onDidChangeFocus(e => this.onListFocus(e)));
|
||||
this._disposables.add(this.editor.onDidChangeCursorSelection(() => this.onCursorSelectionChanged()));
|
||||
this._disposables.add(this.editor.onDidChangeConfiguration(e => {
|
||||
if (e.hasChanged(EditorOption.suggest)) {
|
||||
applyStatusBarStyle();
|
||||
applyIconStyle();
|
||||
|
@ -652,11 +216,11 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
|
||||
this.onThemeChange(themeService.getColorTheme());
|
||||
|
||||
this.toDispose.add(addStandardDisposableListener(this.details.element, 'keydown', e => {
|
||||
this._disposables.add(addStandardDisposableListener(this.details.element, 'keydown', e => {
|
||||
this._onDetailsKeydown.fire(e);
|
||||
}));
|
||||
|
||||
this.toDispose.add(this.editor.onMouseDown((e: IEditorMouseEvent) => this.onEditorMouseDown(e)));
|
||||
this._disposables.add(this.editor.onMouseDown((e: IEditorMouseEvent) => this.onEditorMouseDown(e)));
|
||||
}
|
||||
|
||||
private onEditorMouseDown(mouseEvent: IEditorMouseEvent): void {
|
||||
|
@ -673,15 +237,13 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
}
|
||||
|
||||
private onCursorSelectionChanged(): void {
|
||||
if (this.state === State.Hidden) {
|
||||
return;
|
||||
if (this.state !== State.Hidden) {
|
||||
this.editor.layoutContentWidget(this);
|
||||
}
|
||||
|
||||
this.editor.layoutContentWidget(this);
|
||||
}
|
||||
|
||||
private onEditorLayoutChange(): void {
|
||||
if ((this.state === State.Open || this.state === State.Details) && this.expandDocsSettingFromStorage()) {
|
||||
if ((this.state === State.Open || this.state === State.Details) && this._isDetailsVisible()) {
|
||||
this.expandSideOrBelow();
|
||||
}
|
||||
}
|
||||
|
@ -720,15 +282,14 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
private onThemeChange(theme: IColorTheme) {
|
||||
const backgroundColor = theme.getColor(editorSuggestWidgetBackground);
|
||||
if (backgroundColor) {
|
||||
this.listElement.style.backgroundColor = backgroundColor.toString();
|
||||
this.statusBarElement.style.backgroundColor = backgroundColor.toString();
|
||||
this.mainElement.style.backgroundColor = backgroundColor.toString();
|
||||
this.details.element.style.backgroundColor = backgroundColor.toString();
|
||||
this.messageElement.style.backgroundColor = backgroundColor.toString();
|
||||
}
|
||||
const borderColor = theme.getColor(editorSuggestWidgetBorder);
|
||||
if (borderColor) {
|
||||
this.listElement.style.borderColor = borderColor.toString();
|
||||
this.statusBarElement.style.borderColor = borderColor.toString();
|
||||
this.mainElement.style.borderColor = borderColor.toString();
|
||||
this.status.element.style.borderTopColor = borderColor.toString();
|
||||
this.details.element.style.borderColor = borderColor.toString();
|
||||
this.messageElement.style.borderColor = borderColor.toString();
|
||||
this.detailsBorderColor = borderColor.toString();
|
||||
|
@ -748,8 +309,8 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
if (!e.elements.length) {
|
||||
if (this.currentSuggestionDetails) {
|
||||
this.currentSuggestionDetails.cancel();
|
||||
this.currentSuggestionDetails = null;
|
||||
this.focusedItem = null;
|
||||
this.currentSuggestionDetails = undefined;
|
||||
this.focusedItem = undefined;
|
||||
}
|
||||
|
||||
this.editor.setAriaOptions({ activeDescendant: undefined });
|
||||
|
@ -766,10 +327,8 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
this.firstFocusInCurrentList = !this.focusedItem;
|
||||
if (item !== this.focusedItem) {
|
||||
|
||||
if (this.currentSuggestionDetails) {
|
||||
this.currentSuggestionDetails.cancel();
|
||||
this.currentSuggestionDetails = null;
|
||||
}
|
||||
this.currentSuggestionDetails?.cancel();
|
||||
this.currentSuggestionDetails = undefined;
|
||||
|
||||
this.focusedItem = item;
|
||||
|
||||
|
@ -794,7 +353,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
this.list.setFocus([index]);
|
||||
this.ignoreFocusEvents = false;
|
||||
|
||||
if (this.expandDocsSettingFromStorage()) {
|
||||
if (this._isDetailsVisible()) {
|
||||
this.showDetails(false);
|
||||
} else {
|
||||
this.element.classList.remove('docs-side');
|
||||
|
@ -825,43 +384,43 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
|
||||
switch (state) {
|
||||
case State.Hidden:
|
||||
hide(this.messageElement, this.details.element, this.listElement, this.statusBarElement);
|
||||
hide(this.messageElement, this.details.element, this.mainElement);
|
||||
this.hide();
|
||||
this.listHeight = 0;
|
||||
if (stateChanged) {
|
||||
this.list.splice(0, this.list.length);
|
||||
}
|
||||
this.focusedItem = null;
|
||||
this.focusedItem = undefined;
|
||||
break;
|
||||
case State.Loading:
|
||||
this.messageElement.textContent = SuggestWidget.LOADING_MESSAGE;
|
||||
hide(this.listElement, this.details.element, this.statusBarElement);
|
||||
hide(this.mainElement, this.details.element);
|
||||
show(this.messageElement);
|
||||
this.element.classList.remove('docs-side');
|
||||
this.show();
|
||||
this.focusedItem = null;
|
||||
this.focusedItem = undefined;
|
||||
break;
|
||||
case State.Empty:
|
||||
this.messageElement.textContent = SuggestWidget.NO_SUGGESTIONS_MESSAGE;
|
||||
hide(this.listElement, this.details.element, this.statusBarElement);
|
||||
hide(this.mainElement, this.details.element);
|
||||
show(this.messageElement);
|
||||
this.element.classList.remove('docs-side');
|
||||
this.show();
|
||||
this.focusedItem = null;
|
||||
this.focusedItem = undefined;
|
||||
break;
|
||||
case State.Open:
|
||||
hide(this.messageElement);
|
||||
show(this.listElement, this.statusBarElement);
|
||||
show(this.mainElement);
|
||||
this.show();
|
||||
break;
|
||||
case State.Frozen:
|
||||
hide(this.messageElement);
|
||||
show(this.listElement);
|
||||
show(this.mainElement);
|
||||
this.show();
|
||||
break;
|
||||
case State.Details:
|
||||
hide(this.messageElement);
|
||||
show(this.details.element, this.listElement, this.statusBarElement);
|
||||
show(this.details.element, this.mainElement);
|
||||
this.show();
|
||||
break;
|
||||
}
|
||||
|
@ -881,14 +440,12 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
|
||||
showSuggestions(completionModel: CompletionModel, selectionIndex: number, isFrozen: boolean, isAuto: boolean): void {
|
||||
this.preferDocPositionTop = false;
|
||||
this.docsPositionPreviousWidgetY = null;
|
||||
this.docsPositionPreviousWidgetY = undefined;
|
||||
|
||||
this.loadingTimeout.dispose();
|
||||
|
||||
if (this.currentSuggestionDetails) {
|
||||
this.currentSuggestionDetails.cancel();
|
||||
this.currentSuggestionDetails = null;
|
||||
}
|
||||
this.currentSuggestionDetails?.cancel();
|
||||
this.currentSuggestionDetails = undefined;
|
||||
|
||||
if (this.completionModel !== completionModel) {
|
||||
this.completionModel = completionModel;
|
||||
|
@ -911,7 +468,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
this.setState(State.Empty);
|
||||
}
|
||||
|
||||
this.completionModel = null;
|
||||
this.completionModel = undefined;
|
||||
|
||||
} else {
|
||||
|
||||
|
@ -929,7 +486,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
this.telemetryService.publicLog('suggestWidget', { ...stats });
|
||||
}
|
||||
|
||||
this.focusedItem = null;
|
||||
this.focusedItem = undefined;
|
||||
this.list.splice(0, this.list.length, this.completionModel.items);
|
||||
|
||||
if (isFrozen) {
|
||||
|
@ -1054,7 +611,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
if (this.detailsBorderColor) {
|
||||
this.details.element.style.borderColor = this.detailsBorderColor;
|
||||
}
|
||||
} else if (this.state === State.Open && this.expandDocsSettingFromStorage()) {
|
||||
} else if (this.state === State.Open && this._isDetailsVisible()) {
|
||||
this.setState(State.Details);
|
||||
if (this.detailsFocusBorderColor) {
|
||||
this.details.element.style.borderColor = this.detailsFocusBorderColor;
|
||||
|
@ -1064,24 +621,19 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
}
|
||||
|
||||
toggleDetails(): void {
|
||||
if (!canExpandCompletionItem(this.list.getFocusedElements()[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.expandDocsSettingFromStorage()) {
|
||||
if (this._isDetailsVisible()) {
|
||||
// hide details widget
|
||||
this.ctxSuggestWidgetDetailsVisible.set(false);
|
||||
this.updateExpandDocsSetting(false);
|
||||
this._setDetailsVisible(false);
|
||||
hide(this.details.element);
|
||||
this.element.classList.remove('docs-side', 'doc-below');
|
||||
this.editor.layoutContentWidget(this);
|
||||
this.telemetryService.publicLog2('suggestWidget:collapseDetails');
|
||||
} else {
|
||||
if (this.state !== State.Open && this.state !== State.Details && this.state !== State.Frozen) {
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (canExpandCompletionItem(this.list.getFocusedElements()[0]) && (this.state === State.Open || this.state === State.Details || this.state === State.Frozen)) {
|
||||
// show details widget (iff possible)
|
||||
this.ctxSuggestWidgetDetailsVisible.set(true);
|
||||
this.updateExpandDocsSetting(true);
|
||||
this._setDetailsVisible(true);
|
||||
this.showDetails(false);
|
||||
this.telemetryService.publicLog2('suggestWidget:expandDetails');
|
||||
}
|
||||
|
@ -1103,8 +655,6 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
this.details.renderItem(this.list.getFocusedElements()[0], this.explainMode);
|
||||
}
|
||||
|
||||
// Reset margin-top that was set as Fix for #26416
|
||||
this.listElement.style.marginTop = '0px';
|
||||
|
||||
// with docs showing up widget width/height may change, so reposition the widget
|
||||
this.editor.layoutContentWidget(this);
|
||||
|
@ -1115,7 +665,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
}
|
||||
|
||||
toggleExplainMode(): void {
|
||||
if (this.list.getFocusedElements()[0] && this.expandDocsSettingFromStorage()) {
|
||||
if (this.list.getFocusedElements()[0] && this._isDetailsVisible()) {
|
||||
this.explainMode = !this.explainMode;
|
||||
this.showDetails(false);
|
||||
}
|
||||
|
@ -1179,19 +729,17 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
}
|
||||
|
||||
private updateListHeight(): number {
|
||||
let height = 0;
|
||||
let height = this.unfocusedHeight;
|
||||
|
||||
if (this.state === State.Empty || this.state === State.Loading) {
|
||||
height = this.unfocusedHeight;
|
||||
} else {
|
||||
if (this.state !== State.Empty && this.state !== State.Loading) {
|
||||
const suggestionCount = this.list.contentHeight / this.unfocusedHeight;
|
||||
const { maxVisibleSuggestions } = this.editor.getOption(EditorOption.suggest);
|
||||
height = Math.min(suggestionCount, maxVisibleSuggestions) * this.unfocusedHeight;
|
||||
}
|
||||
|
||||
this.element.style.lineHeight = `${this.unfocusedHeight}px`;
|
||||
this.listElement.style.height = `${height}px`;
|
||||
this.statusBarElement.style.top = `${height}px`;
|
||||
this.listContainer.style.height = `${height}px`;
|
||||
this.mainElement.style.height = `${height + (this.editor.getOption(EditorOption.suggest).statusBar.visible ? this.unfocusedHeight : 0)}px`;
|
||||
this.list.layout(height);
|
||||
return height;
|
||||
}
|
||||
|
@ -1215,33 +763,32 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
|
||||
// Fixes #27649
|
||||
// Check if the Y changed to the top of the cursor and keep the widget flagged to prefer top
|
||||
if (this.docsPositionPreviousWidgetY &&
|
||||
if (this.docsPositionPreviousWidgetY !== undefined &&
|
||||
this.docsPositionPreviousWidgetY < widgetY &&
|
||||
!this.preferDocPositionTop) {
|
||||
!this.preferDocPositionTop
|
||||
) {
|
||||
this.preferDocPositionTop = true;
|
||||
this.adjustDocsPosition();
|
||||
return;
|
||||
}
|
||||
this.docsPositionPreviousWidgetY = widgetY;
|
||||
|
||||
if (widgetX < cursorX - this.listWidth) {
|
||||
// Widget is too far to the left of cursor, swap list and docs
|
||||
this.element.classList.add('list-right');
|
||||
} else {
|
||||
this.element.classList.remove('list-right');
|
||||
}
|
||||
const aboveCursor = cursorY - lineHeight > widgetY;
|
||||
const rowMode = this.element.classList.contains('docs-side');
|
||||
|
||||
// Compare top of the cursor (cursorY - lineheight) with widgetTop to determine if
|
||||
// margin-top needs to be applied on list to make it appear right above the cursor
|
||||
// Cannot compare cursorY directly as it may be a few decimals off due to zoooming
|
||||
if (this.element.classList.contains('docs-side')
|
||||
&& cursorY - lineHeight > widgetY
|
||||
&& this.details.element.offsetHeight > this.listElement.offsetHeight) {
|
||||
// row mode: reverse doc/list when being too far right
|
||||
// column mode: reverse doc/list when being too far down
|
||||
this.element.classList.toggle(
|
||||
'reverse',
|
||||
(rowMode && widgetX < cursorX - this.listWidth) || (!rowMode && aboveCursor)
|
||||
);
|
||||
|
||||
// Fix for #26416
|
||||
// Docs is bigger than list and widget is above cursor, apply margin-top so that list appears right above cursor
|
||||
this.listElement.style.marginTop = `${this.details.element.offsetHeight - this.listElement.offsetHeight}px`;
|
||||
}
|
||||
// row mode: when detail is higher and when showing above the cursor then align
|
||||
// the list at the bottom
|
||||
this.mainElement.classList.toggle(
|
||||
'docs-higher',
|
||||
rowMode && aboveCursor && this.details.element.offsetHeight > this.mainElement.offsetHeight
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1253,7 +800,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
return;
|
||||
}
|
||||
|
||||
let matches = this.element.style.maxWidth!.match(/(\d+)px/);
|
||||
let matches = this.element.style.maxWidth.match(/(\d+)px/);
|
||||
if (!matches || Number(matches[1]) < this.maxWidgetWidth) {
|
||||
this.element.classList.add('docs-below');
|
||||
this.element.classList.remove('docs-side');
|
||||
|
@ -1276,26 +823,27 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
|||
|
||||
// IDelegate
|
||||
|
||||
getHeight(element: CompletionItem): number {
|
||||
getHeight(_element: CompletionItem): number {
|
||||
return this.unfocusedHeight;
|
||||
}
|
||||
|
||||
getTemplateId(element: CompletionItem): string {
|
||||
getTemplateId(_element: CompletionItem): string {
|
||||
return 'suggestion';
|
||||
}
|
||||
|
||||
private expandDocsSettingFromStorage(): boolean {
|
||||
private _isDetailsVisible(): boolean {
|
||||
return this.storageService.getBoolean('expandSuggestionDocs', StorageScope.GLOBAL, expandSuggestionDocsByDefault);
|
||||
}
|
||||
|
||||
private updateExpandDocsSetting(value: boolean) {
|
||||
private _setDetailsVisible(value: boolean) {
|
||||
this.storageService.store('expandSuggestionDocs', value, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.details.dispose();
|
||||
this.list.dispose();
|
||||
this.toDispose.dispose();
|
||||
this.status.dispose();
|
||||
this._disposables.dispose();
|
||||
this.loadingTimeout.dispose();
|
||||
this.showTimeout.dispose();
|
||||
this.editor.removeContentWidget(this);
|
||||
|
|
189
src/vs/editor/contrib/suggest/suggestWidgetDetails.ts
Normal file
189
src/vs/editor/contrib/suggest/suggestWidgetDetails.ts
Normal file
|
@ -0,0 +1,189 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { CompletionItem } from './suggest';
|
||||
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
|
||||
export function canExpandCompletionItem(item: CompletionItem | undefined): boolean {
|
||||
return !!item && Boolean(item.completion.documentation || item.completion.detail && item.completion.detail !== item.completion.label);
|
||||
}
|
||||
|
||||
export class SuggestionDetails {
|
||||
|
||||
readonly element: HTMLElement;
|
||||
|
||||
private readonly _onDidClose = new Emitter<void>();
|
||||
readonly onDidClose: Event<void> = this._onDidClose.event;
|
||||
|
||||
private readonly _close: HTMLElement;
|
||||
private readonly _scrollbar: DomScrollableElement;
|
||||
private readonly _body: HTMLElement;
|
||||
private readonly _header: HTMLElement;
|
||||
private readonly _type: HTMLElement;
|
||||
private readonly _docs: HTMLElement;
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
private _renderDisposeable?: IDisposable;
|
||||
private _borderWidth: number = 1;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
private readonly _editor: ICodeEditor,
|
||||
private readonly _markdownRenderer: MarkdownRenderer,
|
||||
private readonly _kbToggleDetails: string
|
||||
) {
|
||||
this.element = dom.append(container, dom.$('.details'));
|
||||
this._disposables.add(toDisposable(() => this.element.remove()));
|
||||
|
||||
this._body = dom.$('.body');
|
||||
|
||||
this._scrollbar = new DomScrollableElement(this._body, {});
|
||||
dom.append(this.element, this._scrollbar.getDomNode());
|
||||
this._disposables.add(this._scrollbar);
|
||||
|
||||
this._header = dom.append(this._body, dom.$('.header'));
|
||||
this._close = dom.append(this._header, dom.$('span' + Codicon.close.cssSelector));
|
||||
this._close.title = nls.localize('readLess', "Read Less ({0})", this._kbToggleDetails);
|
||||
this._type = dom.append(this._header, dom.$('p.type'));
|
||||
|
||||
this._docs = dom.append(this._body, dom.$('p.docs'));
|
||||
|
||||
this._configureFont();
|
||||
|
||||
this._disposables.add(this._editor.onDidChangeConfiguration(e => {
|
||||
if (e.hasChanged(EditorOption.fontInfo)) {
|
||||
this._configureFont();
|
||||
}
|
||||
}));
|
||||
|
||||
_markdownRenderer.onDidRenderCodeBlock(() => this._scrollbar.scanDomNode(), this, this._disposables);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
this._renderDisposeable?.dispose();
|
||||
this._renderDisposeable = undefined;
|
||||
}
|
||||
|
||||
private _configureFont() {
|
||||
const options = this._editor.getOptions();
|
||||
const fontInfo = options.get(EditorOption.fontInfo);
|
||||
const fontFamily = fontInfo.fontFamily;
|
||||
const fontSize = options.get(EditorOption.suggestFontSize) || fontInfo.fontSize;
|
||||
const lineHeight = options.get(EditorOption.suggestLineHeight) || fontInfo.lineHeight;
|
||||
const fontWeight = fontInfo.fontWeight;
|
||||
const fontSizePx = `${fontSize}px`;
|
||||
const lineHeightPx = `${lineHeight}px`;
|
||||
|
||||
this.element.style.fontSize = fontSizePx;
|
||||
this.element.style.fontWeight = fontWeight;
|
||||
this.element.style.fontFeatureSettings = fontInfo.fontFeatureSettings;
|
||||
this._type.style.fontFamily = fontFamily;
|
||||
this._close.style.height = lineHeightPx;
|
||||
this._close.style.width = lineHeightPx;
|
||||
}
|
||||
|
||||
renderLoading(): void {
|
||||
this._type.textContent = nls.localize('loading', "Loading...");
|
||||
this._docs.textContent = '';
|
||||
}
|
||||
|
||||
renderItem(item: CompletionItem, explainMode: boolean): void {
|
||||
this._renderDisposeable?.dispose();
|
||||
this._renderDisposeable = undefined;
|
||||
|
||||
let { documentation, detail } = item.completion;
|
||||
// --- documentation
|
||||
if (explainMode) {
|
||||
let md = '';
|
||||
md += `score: ${item.score[0]}${item.word ? `, compared '${item.completion.filterText && (item.completion.filterText + ' (filterText)') || item.completion.label}' with '${item.word}'` : ' (no prefix)'}\n`;
|
||||
md += `distance: ${item.distance}, see localityBonus-setting\n`;
|
||||
md += `index: ${item.idx}, based on ${item.completion.sortText && `sortText: "${item.completion.sortText}"` || 'label'}\n`;
|
||||
documentation = new MarkdownString().appendCodeblock('empty', md);
|
||||
detail = `Provider: ${item.provider._debugDisplayName}`;
|
||||
}
|
||||
|
||||
if (!explainMode && !canExpandCompletionItem(item)) {
|
||||
this._type.textContent = '';
|
||||
this._docs.textContent = '';
|
||||
this.element.classList.add('no-docs');
|
||||
return;
|
||||
}
|
||||
this.element.classList.remove('no-docs');
|
||||
if (typeof documentation === 'string') {
|
||||
this._docs.classList.remove('markdown-docs');
|
||||
this._docs.textContent = documentation;
|
||||
} else {
|
||||
this._docs.classList.add('markdown-docs');
|
||||
this._docs.innerText = '';
|
||||
const renderedContents = this._markdownRenderer.render(documentation);
|
||||
this._renderDisposeable = renderedContents;
|
||||
this._docs.appendChild(renderedContents.element);
|
||||
}
|
||||
|
||||
// --- details
|
||||
if (detail) {
|
||||
this._type.textContent = detail.length > 100000 ? `${detail.substr(0, 100000)}…` : detail;
|
||||
dom.show(this._type);
|
||||
} else {
|
||||
dom.clearNode(this._type);
|
||||
dom.hide(this._type);
|
||||
}
|
||||
|
||||
this.element.style.height = this._header.offsetHeight + this._docs.offsetHeight + (this._borderWidth * 2) + 'px';
|
||||
this.element.style.userSelect = 'text';
|
||||
this.element.tabIndex = -1;
|
||||
|
||||
this._close.onmousedown = e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
this._close.onclick = e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this._onDidClose.fire();
|
||||
};
|
||||
|
||||
this._body.scrollTop = 0;
|
||||
this._scrollbar.scanDomNode();
|
||||
}
|
||||
|
||||
scrollDown(much = 8): void {
|
||||
this._body.scrollTop += much;
|
||||
}
|
||||
|
||||
scrollUp(much = 8): void {
|
||||
this._body.scrollTop -= much;
|
||||
}
|
||||
|
||||
scrollTop(): void {
|
||||
this._body.scrollTop = 0;
|
||||
}
|
||||
|
||||
scrollBottom(): void {
|
||||
this._body.scrollTop = this._body.scrollHeight;
|
||||
}
|
||||
|
||||
pageDown(): void {
|
||||
this.scrollDown(80);
|
||||
}
|
||||
|
||||
pageUp(): void {
|
||||
this.scrollUp(80);
|
||||
}
|
||||
|
||||
setBorderWidth(width: number): void {
|
||||
this._borderWidth = width;
|
||||
}
|
||||
}
|
241
src/vs/editor/contrib/suggest/suggestWidgetRenderer.ts
Normal file
241
src/vs/editor/contrib/suggest/suggestWidgetRenderer.ts
Normal file
|
@ -0,0 +1,241 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { createMatches } from 'vs/base/common/filters';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { append, $, hide, show } from 'vs/base/browser/dom';
|
||||
import { IListRenderer } from 'vs/base/browser/ui/list/list';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { CompletionItem } from './suggest';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { CompletionItemKind, completionKindToCssClass, CompletionItemTag } from 'vs/editor/common/modes';
|
||||
import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
import { canExpandCompletionItem } from './suggestWidgetDetails';
|
||||
import { Codicon, registerIcon } from 'vs/base/common/codicons';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
|
||||
export function getAriaId(index: number): string {
|
||||
return `suggest-aria-id:${index}`;
|
||||
}
|
||||
|
||||
export const suggestMoreInfoIcon = registerIcon('suggest-more-info', Codicon.chevronRight);
|
||||
|
||||
const colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i;
|
||||
|
||||
function extractColor(item: CompletionItem, out: string[]): boolean {
|
||||
const label = typeof item.completion.label === 'string'
|
||||
? item.completion.label
|
||||
: item.completion.label.name;
|
||||
|
||||
if (label.match(colorRegExp)) {
|
||||
out[0] = label;
|
||||
return true;
|
||||
}
|
||||
if (typeof item.completion.documentation === 'string' && item.completion.documentation.match(colorRegExp)) {
|
||||
out[0] = item.completion.documentation;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
export interface ISuggestionTemplateData {
|
||||
root: HTMLElement;
|
||||
|
||||
/**
|
||||
* Flexbox
|
||||
* < ------------- left ------------ > < --- right -- >
|
||||
* <icon><label><signature><qualifier> <type><readmore>
|
||||
*/
|
||||
left: HTMLElement;
|
||||
right: HTMLElement;
|
||||
|
||||
icon: HTMLElement;
|
||||
colorspan: HTMLElement;
|
||||
iconLabel: IconLabel;
|
||||
iconContainer: HTMLElement;
|
||||
parametersLabel: HTMLElement;
|
||||
qualifierLabel: HTMLElement;
|
||||
/**
|
||||
* Showing either `CompletionItem#details` or `CompletionItemLabel#type`
|
||||
*/
|
||||
detailsLabel: HTMLElement;
|
||||
readMore: HTMLElement;
|
||||
disposables: DisposableStore;
|
||||
}
|
||||
|
||||
export class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTemplateData> {
|
||||
|
||||
private readonly _onDidToggleDetails = new Emitter<void>();
|
||||
readonly onDidToggleDetails: Event<void> = this._onDidToggleDetails.event;
|
||||
|
||||
readonly templateId = 'suggestion';
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
private readonly _triggerKeybindingLabel: string,
|
||||
@IModelService private readonly _modelService: IModelService,
|
||||
@IModeService private readonly _modeService: IModeService,
|
||||
@IThemeService private readonly _themeService: IThemeService
|
||||
) { }
|
||||
|
||||
dispose(): void {
|
||||
this._onDidToggleDetails.dispose();
|
||||
}
|
||||
|
||||
renderTemplate(container: HTMLElement): ISuggestionTemplateData {
|
||||
const data = <ISuggestionTemplateData>Object.create(null);
|
||||
data.disposables = new DisposableStore();
|
||||
|
||||
data.root = container;
|
||||
data.root.classList.add('show-file-icons');
|
||||
|
||||
data.icon = append(container, $('.icon'));
|
||||
data.colorspan = append(data.icon, $('span.colorspan'));
|
||||
|
||||
const text = append(container, $('.contents'));
|
||||
const main = append(text, $('.main'));
|
||||
|
||||
data.iconContainer = append(main, $('.icon-label.codicon'));
|
||||
data.left = append(main, $('span.left'));
|
||||
data.right = append(main, $('span.right'));
|
||||
|
||||
data.iconLabel = new IconLabel(data.left, { supportHighlights: true, supportCodicons: true });
|
||||
data.disposables.add(data.iconLabel);
|
||||
|
||||
data.parametersLabel = append(data.left, $('span.signature-label'));
|
||||
data.qualifierLabel = append(data.left, $('span.qualifier-label'));
|
||||
data.detailsLabel = append(data.right, $('span.details-label'));
|
||||
|
||||
data.readMore = append(data.right, $('span.readMore' + suggestMoreInfoIcon.cssSelector));
|
||||
data.readMore.title = nls.localize('readMore', "Read More ({0})", this._triggerKeybindingLabel);
|
||||
|
||||
const configureFont = () => {
|
||||
const options = this._editor.getOptions();
|
||||
const fontInfo = options.get(EditorOption.fontInfo);
|
||||
const fontFamily = fontInfo.fontFamily;
|
||||
const fontFeatureSettings = fontInfo.fontFeatureSettings;
|
||||
const fontSize = options.get(EditorOption.suggestFontSize) || fontInfo.fontSize;
|
||||
const lineHeight = options.get(EditorOption.suggestLineHeight) || fontInfo.lineHeight;
|
||||
const fontWeight = fontInfo.fontWeight;
|
||||
const fontSizePx = `${fontSize}px`;
|
||||
const lineHeightPx = `${lineHeight}px`;
|
||||
|
||||
data.root.style.fontSize = fontSizePx;
|
||||
data.root.style.fontWeight = fontWeight;
|
||||
main.style.fontFamily = fontFamily;
|
||||
main.style.fontFeatureSettings = fontFeatureSettings;
|
||||
main.style.lineHeight = lineHeightPx;
|
||||
data.icon.style.height = lineHeightPx;
|
||||
data.icon.style.width = lineHeightPx;
|
||||
data.readMore.style.height = lineHeightPx;
|
||||
data.readMore.style.width = lineHeightPx;
|
||||
};
|
||||
|
||||
configureFont();
|
||||
|
||||
data.disposables.add(this._editor.onDidChangeConfiguration(e => {
|
||||
if (e.hasChanged(EditorOption.fontInfo) || e.hasChanged(EditorOption.suggestFontSize) || e.hasChanged(EditorOption.suggestLineHeight)) {
|
||||
configureFont();
|
||||
}
|
||||
}));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
renderElement(element: CompletionItem, index: number, data: ISuggestionTemplateData): void {
|
||||
const { completion } = element;
|
||||
const textLabel = typeof completion.label === 'string' ? completion.label : completion.label.name;
|
||||
|
||||
data.root.id = getAriaId(index);
|
||||
data.colorspan.style.backgroundColor = '';
|
||||
|
||||
const labelOptions: IIconLabelValueOptions = {
|
||||
labelEscapeNewLines: true,
|
||||
matches: createMatches(element.score)
|
||||
};
|
||||
|
||||
let color: string[] = [];
|
||||
if (completion.kind === CompletionItemKind.Color && extractColor(element, color)) {
|
||||
// special logic for 'color' completion items
|
||||
data.icon.className = 'icon customcolor';
|
||||
data.iconContainer.className = 'icon hide';
|
||||
data.colorspan.style.backgroundColor = color[0];
|
||||
|
||||
} else if (completion.kind === CompletionItemKind.File && this._themeService.getFileIconTheme().hasFileIcons) {
|
||||
// special logic for 'file' completion items
|
||||
data.icon.className = 'icon hide';
|
||||
data.iconContainer.className = 'icon hide';
|
||||
const labelClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: textLabel }), FileKind.FILE);
|
||||
const detailClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: completion.detail }), FileKind.FILE);
|
||||
labelOptions.extraClasses = labelClasses.length > detailClasses.length ? labelClasses : detailClasses;
|
||||
|
||||
} else if (completion.kind === CompletionItemKind.Folder && this._themeService.getFileIconTheme().hasFolderIcons) {
|
||||
// special logic for 'folder' completion items
|
||||
data.icon.className = 'icon hide';
|
||||
data.iconContainer.className = 'icon hide';
|
||||
labelOptions.extraClasses = flatten([
|
||||
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: textLabel }), FileKind.FOLDER),
|
||||
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: completion.detail }), FileKind.FOLDER)
|
||||
]);
|
||||
} else {
|
||||
// normal icon
|
||||
data.icon.className = 'icon hide';
|
||||
data.iconContainer.className = '';
|
||||
data.iconContainer.classList.add('suggest-icon', ...completionKindToCssClass(completion.kind).split(' '));
|
||||
}
|
||||
|
||||
if (completion.tags && completion.tags.indexOf(CompletionItemTag.Deprecated) >= 0) {
|
||||
labelOptions.extraClasses = (labelOptions.extraClasses || []).concat(['deprecated']);
|
||||
labelOptions.matches = [];
|
||||
}
|
||||
|
||||
data.iconLabel.setLabel(textLabel, undefined, labelOptions);
|
||||
if (typeof completion.label === 'string') {
|
||||
data.parametersLabel.textContent = '';
|
||||
data.qualifierLabel.textContent = '';
|
||||
data.detailsLabel.textContent = (completion.detail || '').replace(/\n.*$/m, '');
|
||||
data.root.classList.add('string-label');
|
||||
data.root.title = '';
|
||||
} else {
|
||||
data.parametersLabel.textContent = (completion.label.parameters || '').replace(/\n.*$/m, '');
|
||||
data.qualifierLabel.textContent = (completion.label.qualifier || '').replace(/\n.*$/m, '');
|
||||
data.detailsLabel.textContent = (completion.label.type || '').replace(/\n.*$/m, '');
|
||||
data.root.classList.remove('string-label');
|
||||
data.root.title = `${textLabel}${completion.label.parameters ?? ''} ${completion.label.qualifier ?? ''} ${completion.label.type ?? ''}`;
|
||||
}
|
||||
|
||||
if (canExpandCompletionItem(element)) {
|
||||
data.right.classList.add('can-expand-details');
|
||||
show(data.readMore);
|
||||
data.readMore.onmousedown = e => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
data.readMore.onclick = e => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this._onDidToggleDetails.fire();
|
||||
};
|
||||
} else {
|
||||
data.right.classList.remove('can-expand-details');
|
||||
hide(data.readMore);
|
||||
data.readMore.onmousedown = null;
|
||||
data.readMore.onclick = null;
|
||||
}
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: ISuggestionTemplateData): void {
|
||||
templateData.disposables.dispose();
|
||||
}
|
||||
}
|
76
src/vs/editor/contrib/suggest/suggestWidgetStatus.ts
Normal file
76
src/vs/editor/contrib/suggest/suggestWidgetStatus.ts
Normal file
|
@ -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 dom from 'vs/base/browser/dom';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
|
||||
import { IActionViewItemProvider, IAction } from 'vs/base/common/actions';
|
||||
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { format } from 'vs/base/common/strings';
|
||||
import { suggestWidgetStatusbarMenu } from 'vs/editor/contrib/suggest/suggest';
|
||||
import { IMenuService } from 'vs/platform/actions/common/actions';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
|
||||
export class SuggestWidgetStatus {
|
||||
|
||||
readonly element: HTMLElement;
|
||||
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
) {
|
||||
this.element = dom.append(container, dom.$('.suggest-status-bar'));
|
||||
|
||||
|
||||
const actionViewItemProvider = <IActionViewItemProvider>(action => {
|
||||
const kb = keybindingService.lookupKeybindings(action.id);
|
||||
return new class extends ActionViewItem {
|
||||
constructor() {
|
||||
super(undefined, action, { label: true, icon: false });
|
||||
}
|
||||
updateLabel() {
|
||||
if (isFalsyOrEmpty(kb) || !this.label) {
|
||||
return super.updateLabel();
|
||||
}
|
||||
const { label } = this.getAction();
|
||||
this.label.textContent = /{\d}/.test(label)
|
||||
? format(this.getAction().label, kb[0].getLabel())
|
||||
: `${this.getAction().label} (${kb[0].getLabel()})`;
|
||||
}
|
||||
};
|
||||
});
|
||||
const leftActions = new ActionBar(this.element, { actionViewItemProvider });
|
||||
const rightActions = new ActionBar(this.element, { actionViewItemProvider });
|
||||
const menu = menuService.createMenu(suggestWidgetStatusbarMenu, contextKeyService);
|
||||
const renderMenu = () => {
|
||||
const left: IAction[] = [];
|
||||
const right: IAction[] = [];
|
||||
for (let [group, actions] of menu.getActions()) {
|
||||
if (group === 'left') {
|
||||
left.push(...actions);
|
||||
} else {
|
||||
right.push(...actions);
|
||||
}
|
||||
}
|
||||
leftActions.clear();
|
||||
leftActions.push(left);
|
||||
rightActions.clear();
|
||||
rightActions.push(right);
|
||||
};
|
||||
this._disposables.add(menu.onDidChange(() => renderMenu()));
|
||||
this._disposables.add(menu);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
this.element.remove();
|
||||
}
|
||||
}
|
|
@ -302,33 +302,34 @@ export class ExecuteCommandAction extends Action {
|
|||
|
||||
export class SubmenuItemAction extends SubmenuAction {
|
||||
|
||||
readonly item: ISubmenuItem;
|
||||
|
||||
constructor(
|
||||
readonly item: ISubmenuItem,
|
||||
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();
|
||||
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;
|
||||
for (let group of groups) {
|
||||
const [, actions] = group;
|
||||
|
||||
if (actions.length > 0) {
|
||||
result.push(...actions);
|
||||
result.push(new Separator());
|
||||
}
|
||||
if (actions.length > 0) {
|
||||
result.push(...actions);
|
||||
result.push(new Separator());
|
||||
}
|
||||
}
|
||||
|
||||
if (result.length) {
|
||||
result.pop(); // remove last separator
|
||||
}
|
||||
if (result.length) {
|
||||
result.pop(); // remove last separator
|
||||
}
|
||||
|
||||
return result;
|
||||
}, 'submenu');
|
||||
super(`submenuitem.${item.submenu.id}`, typeof item.title === 'string' ? item.title : item.title.value, result, 'submenu');
|
||||
this.item = item;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -328,7 +328,7 @@ export abstract class AbstractContextKeyService implements IContextKeyService {
|
|||
public abstract getContextValuesContainer(contextId: number): Context;
|
||||
public abstract createChildContext(parentContextId?: number): number;
|
||||
public abstract disposeContext(contextId: number): void;
|
||||
public abstract updateParent(parentContextKeyService: IContextKeyService): void;
|
||||
public abstract updateParent(parentContextKeyService?: IContextKeyService): void;
|
||||
}
|
||||
|
||||
export class ContextKeyService extends AbstractContextKeyService implements IContextKeyService {
|
||||
|
@ -423,6 +423,7 @@ class ScopedContextKeyService extends AbstractContextKeyService {
|
|||
public dispose(): void {
|
||||
this._isDisposed = true;
|
||||
this._parent.disposeContext(this._myContextId);
|
||||
this._parentChangeListener?.dispose();
|
||||
if (this._domNode) {
|
||||
this._domNode.removeAttribute(KEYBINDING_CONTEXT_ATTR);
|
||||
this._domNode = undefined;
|
||||
|
|
|
@ -1139,7 +1139,7 @@ export interface IContextKeyService {
|
|||
createScoped(target?: IContextKeyServiceTarget): IContextKeyService;
|
||||
getContext(target: IContextKeyServiceTarget | null): IContext;
|
||||
|
||||
updateParent(parentContextKeyService: IContextKeyService): void;
|
||||
updateParent(parentContextKeyService?: IContextKeyService): void;
|
||||
}
|
||||
|
||||
export const SET_CONTEXT_COMMAND_ID = 'setContext';
|
||||
|
|
|
@ -56,7 +56,6 @@ export interface NativeParsedArgs {
|
|||
'enable-proposed-api'?: string[]; // undefined or array of 1 or more
|
||||
'open-url'?: boolean;
|
||||
'skip-release-notes'?: boolean;
|
||||
'disable-restore-windows'?: boolean;
|
||||
'disable-telemetry'?: boolean;
|
||||
'export-default-configuration'?: string;
|
||||
'install-source'?: string;
|
||||
|
|
|
@ -90,7 +90,6 @@ export const OPTIONS: OptionDescriptions<Required<NativeParsedArgs>> = {
|
|||
'driver': { type: 'string' },
|
||||
'logExtensionHostCommunication': { type: 'boolean' },
|
||||
'skip-release-notes': { type: 'boolean' },
|
||||
'disable-restore-windows': { type: 'boolean' },
|
||||
'disable-telemetry': { type: 'boolean' },
|
||||
'disable-updates': { type: 'boolean' },
|
||||
'disable-crash-reporter': { type: 'boolean' },
|
||||
|
|
|
@ -20,14 +20,14 @@ export function hasStdinWithoutTty() {
|
|||
}
|
||||
|
||||
export function stdinDataListener(durationinMs: number): Promise<boolean> {
|
||||
return new Promise(c => {
|
||||
const dataListener = () => c(true);
|
||||
return new Promise(resolve => {
|
||||
const dataListener = () => resolve(true);
|
||||
|
||||
// wait for 1s maximum...
|
||||
setTimeout(() => {
|
||||
process.stdin.removeListener('data', dataListener);
|
||||
|
||||
c(false);
|
||||
resolve(false);
|
||||
}, durationinMs);
|
||||
|
||||
// ...but finish early if we detect data
|
||||
|
|
|
@ -505,19 +505,20 @@ export class FileChangesEvent {
|
|||
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
|
||||
* type DELETED, this method will also return true if a folder got deleted that is the parent of the
|
||||
* provided file path.
|
||||
* Returns true if this change event contains the provided file
|
||||
* with the given change type (if provided). In case of type
|
||||
* DELETED, this method will also return true if a folder got
|
||||
* deleted that is the parent of the provided file path.
|
||||
*/
|
||||
contains(resource: URI, type?: FileChangeType): boolean {
|
||||
contains(resource: URI, ...types: FileChangeType[]): boolean {
|
||||
if (!resource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const checkForChangeType = !isUndefinedOrNull(type);
|
||||
const hasTypesFilter = types.length > 0;
|
||||
|
||||
return this.changes.some(change => {
|
||||
if (checkForChangeType && change.type !== type) {
|
||||
if (hasTypesFilter && !types.includes(change.type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -577,9 +578,7 @@ export class FileChangesEvent {
|
|||
}
|
||||
|
||||
private hasType(type: FileChangeType): boolean {
|
||||
return this.changes.some(change => {
|
||||
return change.type === type;
|
||||
});
|
||||
return this.changes.some(change => change.type === type);
|
||||
}
|
||||
|
||||
filter(filterFn: (change: IFileChange) => boolean): FileChangesEvent {
|
||||
|
@ -862,11 +861,11 @@ export function whenProviderRegistered(file: URI, fileService: IFileService): Pr
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise((c, e) => {
|
||||
return new Promise(resolve => {
|
||||
const disposable = fileService.onDidChangeFileSystemProviderRegistrations(e => {
|
||||
if (e.scheme === file.scheme && e.added) {
|
||||
disposable.dispose();
|
||||
c();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,6 +26,9 @@ suite('Files', () => {
|
|||
|
||||
assert(!r1.contains(toResource.call(this, '/foo'), FileChangeType.UPDATED));
|
||||
assert(r1.contains(toResource.call(this, '/foo/updated.txt'), FileChangeType.UPDATED));
|
||||
assert(r1.contains(toResource.call(this, '/foo/updated.txt'), FileChangeType.UPDATED, FileChangeType.ADDED));
|
||||
assert(r1.contains(toResource.call(this, '/foo/updated.txt'), FileChangeType.UPDATED, FileChangeType.ADDED, FileChangeType.DELETED));
|
||||
assert(!r1.contains(toResource.call(this, '/foo/updated.txt'), FileChangeType.ADDED, FileChangeType.DELETED));
|
||||
assert(!r1.contains(toResource.call(this, '/foo/updated.txt'), FileChangeType.ADDED));
|
||||
assert(!r1.contains(toResource.call(this, '/foo/updated.txt'), FileChangeType.DELETED));
|
||||
|
||||
|
|
|
@ -379,7 +379,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
|
|||
|
||||
// Always allow to unload a window that is not yet ready
|
||||
if (!window.isReady) {
|
||||
return Promise.resolve(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.logService.trace(`Lifecycle#unload() - window ID ${window.id}`);
|
||||
|
@ -432,17 +432,17 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
|
|||
}
|
||||
|
||||
private onBeforeUnloadWindowInRenderer(window: ICodeWindow, reason: UnloadReason): Promise<boolean /* veto */> {
|
||||
return new Promise<boolean>(c => {
|
||||
return new Promise<boolean>(resolve => {
|
||||
const oneTimeEventToken = this.oneTimeListenerTokenGenerator++;
|
||||
const okChannel = `vscode:ok${oneTimeEventToken}`;
|
||||
const cancelChannel = `vscode:cancel${oneTimeEventToken}`;
|
||||
|
||||
ipc.once(okChannel, () => {
|
||||
c(false); // no veto
|
||||
resolve(false); // no veto
|
||||
});
|
||||
|
||||
ipc.once(cancelChannel, () => {
|
||||
c(true); // veto
|
||||
resolve(true); // veto
|
||||
});
|
||||
|
||||
window.send('vscode:onBeforeUnload', { okChannel, cancelChannel, reason });
|
||||
|
|
|
@ -5,10 +5,9 @@
|
|||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogReturnValue, MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes';
|
||||
import { IOpenedWindow, IWindowOpenable, IOpenEmptyWindowOptions, IOpenWindowOptions } from 'vs/platform/windows/common/windows';
|
||||
import { IOpenedWindow, IWindowOpenable, IOpenEmptyWindowOptions, IOpenWindowOptions, IColorScheme } from 'vs/platform/windows/common/windows';
|
||||
import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { ColorScheme } from 'vs/platform/theme/common/theme';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export interface ICPUProperties {
|
||||
|
@ -48,7 +47,7 @@ export interface ICommonNativeHostService {
|
|||
|
||||
readonly onOSResume: Event<unknown>;
|
||||
|
||||
readonly onColorSchemeChange: Event<ColorScheme>;
|
||||
readonly onColorSchemeChange: Event<IColorScheme>;
|
||||
|
||||
// Window
|
||||
getWindows(): Promise<IOpenedWindow[]>;
|
||||
|
|
|
@ -8,7 +8,7 @@ import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-m
|
|||
import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, Menu, BrowserWindow, app, clipboard, powerMonitor, nativeTheme } from 'electron';
|
||||
import { OpenContext } from 'vs/platform/windows/node/window';
|
||||
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||
import { IOpenedWindow, IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
|
||||
import { IOpenedWindow, IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions, IColorScheme } from 'vs/platform/windows/common/windows';
|
||||
import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { isMacintosh, isWindows, isRootUser, isLinux } from 'vs/base/common/platform';
|
||||
import { ICommonNativeHostService, IOSProperties, IOSStatistics } from 'vs/platform/native/common/native';
|
||||
|
@ -22,7 +22,6 @@ import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/
|
|||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes';
|
||||
import { arch, totalmem, release, platform, type, loadavg, freemem, cpus } from 'os';
|
||||
import { ColorScheme } from 'vs/platform/theme/common/theme';
|
||||
import { virtualMachineHint } from 'vs/base/node/id';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { dirname, join } from 'vs/base/common/path';
|
||||
|
@ -52,16 +51,10 @@ export class NativeHostMainService implements INativeHostMainService {
|
|||
|
||||
// Color Scheme changes
|
||||
nativeTheme.on('updated', () => {
|
||||
let colorScheme: ColorScheme;
|
||||
if (nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors) {
|
||||
colorScheme = ColorScheme.HIGH_CONTRAST;
|
||||
} else if (nativeTheme.shouldUseDarkColors) {
|
||||
colorScheme = ColorScheme.DARK;
|
||||
} else {
|
||||
colorScheme = ColorScheme.LIGHT;
|
||||
}
|
||||
|
||||
this._onColorSchemeChange.fire(colorScheme);
|
||||
this._onColorSchemeChange.fire({
|
||||
highContrast: nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors,
|
||||
dark: nativeTheme.shouldUseDarkColors
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -87,7 +80,7 @@ export class NativeHostMainService implements INativeHostMainService {
|
|||
|
||||
readonly onOSResume = Event.fromNodeEventEmitter(powerMonitor, 'resume');
|
||||
|
||||
private readonly _onColorSchemeChange = new Emitter<ColorScheme>();
|
||||
private readonly _onColorSchemeChange = new Emitter<IColorScheme>();
|
||||
readonly onColorSchemeChange = this._onColorSchemeChange.event;
|
||||
|
||||
//#endregion
|
||||
|
|
|
@ -20,7 +20,7 @@ if (isWeb || typeof require === 'undefined' || typeof require.__$__nodeRequire !
|
|||
// Running out of sources
|
||||
if (Object.keys(product).length === 0) {
|
||||
Object.assign(product, {
|
||||
version: '1.50.0-dev',
|
||||
version: '1.51.0-dev',
|
||||
nameShort: isWeb ? 'Code Web - OSS Dev' : 'Code - OSS Dev',
|
||||
nameLong: isWeb ? 'Code Web - OSS Dev' : 'Code - OSS Dev',
|
||||
applicationName: 'code-oss',
|
||||
|
|
|
@ -107,10 +107,17 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel {
|
|||
this._socketsDispose.delete(localSocket.localAddress);
|
||||
remoteSocket.end();
|
||||
});
|
||||
|
||||
localSocket.on('close', () => remoteSocket.end());
|
||||
localSocket.on('error', () => {
|
||||
this._socketsDispose.delete(localSocket.localAddress);
|
||||
remoteSocket.destroy();
|
||||
});
|
||||
|
||||
remoteSocket.on('end', () => localSocket.end());
|
||||
remoteSocket.on('close', () => localSocket.end());
|
||||
remoteSocket.on('error', () => {
|
||||
localSocket.destroy();
|
||||
});
|
||||
|
||||
localSocket.pipe(remoteSocket);
|
||||
remoteSocket.pipe(localSocket);
|
||||
|
|
|
@ -16,7 +16,12 @@ 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', () => {
|
||||
suite('StorageService', function () {
|
||||
|
||||
// Given issues such as https://github.com/microsoft/vscode/issues/108113
|
||||
// we see random test failures when accessing the native file system.
|
||||
this.retries(3);
|
||||
this.timeout(1000 * 10);
|
||||
|
||||
test('Remove Data (global, in-memory)', () => {
|
||||
removeData(StorageScope.GLOBAL);
|
||||
|
|
|
@ -53,7 +53,7 @@ class ResourceStackElement {
|
|||
}
|
||||
|
||||
public toString(): string {
|
||||
return `[id:${this.id}] [group:${this.groupId}] [${this.isValid ? 'VALID' : 'INVALID'}] ${this.actual}`;
|
||||
return `[id:${this.id}] [group:${this.groupId}] [${this.isValid ? ' VALID' : 'INVALID'}] ${this.actual.constructor.name} - ${this.actual}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,7 +176,7 @@ class WorkspaceStackElement {
|
|||
}
|
||||
|
||||
public toString(): string {
|
||||
return `[id:${this.id}] [group:${this.groupId}] [${this.invalidatedResources ? 'INVALID' : 'VALID'}] ${this.actual}`;
|
||||
return `[id:${this.id}] [group:${this.groupId}] [${this.invalidatedResources ? 'INVALID' : ' VALID'}] ${this.actual.constructor.name} - ${this.actual}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,13 +277,6 @@ class ResourceEditStack {
|
|||
}
|
||||
}
|
||||
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++;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IFileService, IFileContent, FileChangesEvent, FileOperationResult, FileOperationError } from 'vs/platform/files/common/files';
|
||||
import { IFileService, IFileContent, FileChangesEvent, FileOperationResult, FileOperationError, FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import {
|
||||
|
@ -13,7 +13,7 @@ import {
|
|||
IUserDataManifest, ISyncData, IRemoteUserData, PREVIEW_DIR_NAME, IResourcePreview as IBaseResourcePreview, Change, MergeState, IUserDataInitializer
|
||||
} 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 { IExtUri, extUri, extUriIgnorePathCase } from 'vs/base/common/resources';
|
||||
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';
|
||||
|
@ -53,8 +53,8 @@ function isSyncData(thing: any): thing is ISyncData {
|
|||
return false;
|
||||
}
|
||||
|
||||
function getLastSyncResourceUri(syncResource: SyncResource, environmentService: IEnvironmentService): URI {
|
||||
return joinPath(environmentService.userDataSyncHome, syncResource, `lastSync${syncResource}.json`);
|
||||
function getLastSyncResourceUri(syncResource: SyncResource, environmentService: IEnvironmentService, extUri: IExtUri): URI {
|
||||
return extUri.joinPath(environmentService.userDataSyncHome, syncResource, `lastSync${syncResource}.json`);
|
||||
}
|
||||
|
||||
export interface IResourcePreview {
|
||||
|
@ -100,6 +100,7 @@ export abstract class AbstractSynchroniser extends Disposable {
|
|||
|
||||
protected readonly syncFolder: URI;
|
||||
protected readonly syncPreviewFolder: URI;
|
||||
protected readonly extUri: IExtUri;
|
||||
private readonly currentMachineIdPromise: Promise<string>;
|
||||
|
||||
private _status: SyncStatus = SyncStatus.Idle;
|
||||
|
@ -135,9 +136,10 @@ export abstract class AbstractSynchroniser extends Disposable {
|
|||
) {
|
||||
super();
|
||||
this.syncResourceLogLabel = uppercaseFirstLetter(this.resource);
|
||||
this.syncFolder = joinPath(environmentService.userDataSyncHome, resource);
|
||||
this.syncPreviewFolder = joinPath(this.syncFolder, PREVIEW_DIR_NAME);
|
||||
this.lastSyncResource = getLastSyncResourceUri(resource, environmentService);
|
||||
this.extUri = this.fileService.hasCapability(environmentService.userDataSyncHome, FileSystemProviderCapabilities.PathCaseSensitive) ? extUri : extUriIgnorePathCase;
|
||||
this.syncFolder = this.extUri.joinPath(environmentService.userDataSyncHome, resource);
|
||||
this.syncPreviewFolder = this.extUri.joinPath(this.syncFolder, PREVIEW_DIR_NAME);
|
||||
this.lastSyncResource = getLastSyncResourceUri(resource, environmentService, this.extUri);
|
||||
this.currentMachineIdPromise = getServiceMachineId(environmentService, fileService, storageService);
|
||||
}
|
||||
|
||||
|
@ -423,7 +425,7 @@ export abstract class AbstractSynchroniser extends Disposable {
|
|||
|
||||
let preview = await this.syncPreviewPromise;
|
||||
const index = preview.resourcePreviews.findIndex(({ localResource, remoteResource, previewResource }) =>
|
||||
isEqual(localResource, resource) || isEqual(remoteResource, resource) || isEqual(previewResource, resource));
|
||||
this.extUri.isEqual(localResource, resource) || this.extUri.isEqual(remoteResource, resource) || this.extUri.isEqual(previewResource, resource));
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
@ -483,7 +485,7 @@ export abstract class AbstractSynchroniser extends Disposable {
|
|||
|
||||
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))) {
|
||||
if (!equals(this._conflicts, conflicts, (a, b) => this.extUri.isEqual(a.previewResource, b.previewResource))) {
|
||||
this._conflicts = conflicts;
|
||||
this._onDidChangeConflicts.fire(conflicts);
|
||||
}
|
||||
|
@ -513,8 +515,8 @@ export abstract class AbstractSynchroniser extends Disposable {
|
|||
}
|
||||
|
||||
async getMachineId({ uri }: ISyncResourceHandle): Promise<string | undefined> {
|
||||
const ref = basename(uri);
|
||||
if (isEqual(uri, this.toRemoteBackupResource(ref))) {
|
||||
const ref = this.extUri.basename(uri);
|
||||
if (this.extUri.isEqual(uri, this.toRemoteBackupResource(ref))) {
|
||||
const { content } = await this.getUserData(ref);
|
||||
if (content) {
|
||||
const syncData = this.parseSyncData(content);
|
||||
|
@ -525,12 +527,12 @@ export abstract class AbstractSynchroniser extends Disposable {
|
|||
}
|
||||
|
||||
async resolveContent(uri: URI): Promise<string | null> {
|
||||
const ref = basename(uri);
|
||||
if (isEqual(uri, this.toRemoteBackupResource(ref))) {
|
||||
const ref = this.extUri.basename(uri);
|
||||
if (this.extUri.isEqual(uri, this.toRemoteBackupResource(ref))) {
|
||||
const { content } = await this.getUserData(ref);
|
||||
return content;
|
||||
}
|
||||
if (isEqual(uri, this.toLocalBackupResource(ref))) {
|
||||
if (this.extUri.isEqual(uri, this.toLocalBackupResource(ref))) {
|
||||
return this.userDataSyncBackupStoreService.resolveContent(this.resource, ref);
|
||||
}
|
||||
return null;
|
||||
|
@ -540,13 +542,13 @@ export abstract class AbstractSynchroniser extends Disposable {
|
|||
const syncPreview = this.syncPreviewPromise ? await this.syncPreviewPromise : null;
|
||||
if (syncPreview) {
|
||||
for (const resourcePreview of syncPreview.resourcePreviews) {
|
||||
if (isEqual(resourcePreview.acceptedResource, uri)) {
|
||||
if (this.extUri.isEqual(resourcePreview.acceptedResource, uri)) {
|
||||
return resourcePreview.acceptResult ? resourcePreview.acceptResult.content : null;
|
||||
}
|
||||
if (isEqual(resourcePreview.remoteResource, uri)) {
|
||||
if (this.extUri.isEqual(resourcePreview.remoteResource, uri)) {
|
||||
return resourcePreview.remoteContent;
|
||||
}
|
||||
if (isEqual(resourcePreview.localResource, uri)) {
|
||||
if (this.extUri.isEqual(resourcePreview.localResource, uri)) {
|
||||
return resourcePreview.localContent;
|
||||
}
|
||||
}
|
||||
|
@ -727,7 +729,7 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser {
|
|||
@IConfigurationService configurationService: IConfigurationService,
|
||||
) {
|
||||
super(resource, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, configurationService);
|
||||
this._register(this.fileService.watch(dirname(file)));
|
||||
this._register(this.fileService.watch(this.extUri.dirname(file)));
|
||||
this._register(this.fileService.onDidFilesChange(e => this.onFileChanges(e)));
|
||||
}
|
||||
|
||||
|
@ -804,6 +806,7 @@ export abstract class AbstractJsonFileSynchroniser extends AbstractFileSynchroni
|
|||
|
||||
export abstract class AbstractInitializer implements IUserDataInitializer {
|
||||
|
||||
protected readonly extUri: IExtUri;
|
||||
private readonly lastSyncResource: URI;
|
||||
|
||||
constructor(
|
||||
|
@ -812,7 +815,8 @@ export abstract class AbstractInitializer implements IUserDataInitializer {
|
|||
@IUserDataSyncLogService protected readonly logService: IUserDataSyncLogService,
|
||||
@IFileService protected readonly fileService: IFileService,
|
||||
) {
|
||||
this.lastSyncResource = getLastSyncResourceUri(this.resource, environmentService);
|
||||
this.extUri = this.fileService.hasCapability(environmentService.userDataSyncHome, FileSystemProviderCapabilities.PathCaseSensitive) ? extUri : extUriIgnorePathCase;
|
||||
this.lastSyncResource = getLastSyncResourceUri(this.resource, environmentService, extUri);
|
||||
}
|
||||
|
||||
async initialize({ ref, content }: IUserData): Promise<void> {
|
||||
|
|
|
@ -18,7 +18,6 @@ import { merge, getIgnoredExtensions } from 'vs/platform/userDataSync/common/ext
|
|||
import { AbstractInitializer, 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';
|
||||
|
@ -82,7 +81,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||
protected readonly version: number = 4;
|
||||
|
||||
protected isEnabled(): boolean { return super.isEnabled() && this.extensionGalleryService.isEnabled(); }
|
||||
private readonly previewResource: URI = joinPath(this.syncPreviewFolder, 'extensions.json');
|
||||
private readonly previewResource: URI = this.extUri.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' });
|
||||
|
@ -183,17 +182,17 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||
protected async getAcceptResult(resourcePreview: IExtensionResourcePreview, resource: URI, content: string | null | undefined, token: CancellationToken): Promise<IExtensionResourceMergeResult> {
|
||||
|
||||
/* Accept local resource */
|
||||
if (isEqual(resource, this.localResource)) {
|
||||
if (this.extUri.isEqual(resource, this.localResource)) {
|
||||
return this.acceptLocal(resourcePreview);
|
||||
}
|
||||
|
||||
/* Accept remote resource */
|
||||
if (isEqual(resource, this.remoteResource)) {
|
||||
if (this.extUri.isEqual(resource, this.remoteResource)) {
|
||||
return this.acceptRemote(resourcePreview);
|
||||
}
|
||||
|
||||
/* Accept preview resource */
|
||||
if (isEqual(resource, this.previewResource)) {
|
||||
if (this.extUri.isEqual(resource, this.previewResource)) {
|
||||
return resourcePreview.previewResult;
|
||||
}
|
||||
|
||||
|
@ -272,18 +271,18 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||
}
|
||||
|
||||
async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource: URI }[]> {
|
||||
return [{ resource: joinPath(uri, 'extensions.json'), comparableResource: ExtensionsSynchroniser.EXTENSIONS_DATA_URI }];
|
||||
return [{ resource: this.extUri.joinPath(uri, 'extensions.json'), comparableResource: ExtensionsSynchroniser.EXTENSIONS_DATA_URI }];
|
||||
}
|
||||
|
||||
async resolveContent(uri: URI): Promise<string | null> {
|
||||
if (isEqual(uri, ExtensionsSynchroniser.EXTENSIONS_DATA_URI)) {
|
||||
if (this.extUri.isEqual(uri, ExtensionsSynchroniser.EXTENSIONS_DATA_URI)) {
|
||||
const installedExtensions = await this.extensionManagementService.getInstalled();
|
||||
const ignoredExtensions = getIgnoredExtensions(installedExtensions, this.configurationService);
|
||||
const localExtensions = this.getLocalExtensions(installedExtensions).filter(e => !ignoredExtensions.some(id => areSameExtensions({ id }, e.identifier)));
|
||||
return this.format(localExtensions);
|
||||
}
|
||||
|
||||
if (isEqual(this.remoteResource, uri) || isEqual(this.localResource, uri) || isEqual(this.acceptedResource, uri)) {
|
||||
if (this.extUri.isEqual(this.remoteResource, uri) || this.extUri.isEqual(this.localResource, uri) || this.extUri.isEqual(this.acceptedResource, uri)) {
|
||||
return this.resolvePreviewContent(uri);
|
||||
}
|
||||
|
||||
|
@ -292,11 +291,11 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||
return content;
|
||||
}
|
||||
|
||||
content = await super.resolveContent(dirname(uri));
|
||||
content = await super.resolveContent(this.extUri.dirname(uri));
|
||||
if (content) {
|
||||
const syncData = this.parseSyncData(content);
|
||||
if (syncData) {
|
||||
switch (basename(uri)) {
|
||||
switch (this.extUri.basename(uri)) {
|
||||
case 'extensions.json':
|
||||
return this.format(this.parseExtensions(syncData));
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { dirname, joinPath, basename, isEqual } from 'vs/base/common/resources';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { edit } from 'vs/platform/userDataSync/common/content';
|
||||
|
@ -49,7 +48,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
|
|||
|
||||
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 previewResource: URI = this.extUri.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' });
|
||||
|
@ -67,7 +66,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
|
|||
@IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
|
||||
) {
|
||||
super(SyncResource.GlobalState, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, configurationService);
|
||||
this._register(this.fileService.watch(dirname(this.environmentService.argvResource)));
|
||||
this._register(this.fileService.watch(this.extUri.dirname(this.environmentService.argvResource)));
|
||||
this._register(
|
||||
Event.any(
|
||||
/* Locale change */
|
||||
|
@ -123,17 +122,17 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
|
|||
protected async getAcceptResult(resourcePreview: IGlobalStateResourcePreview, resource: URI, content: string | null | undefined, token: CancellationToken): Promise<IGlobalStateResourceMergeResult> {
|
||||
|
||||
/* Accept local resource */
|
||||
if (isEqual(resource, this.localResource)) {
|
||||
if (this.extUri.isEqual(resource, this.localResource)) {
|
||||
return this.acceptLocal(resourcePreview);
|
||||
}
|
||||
|
||||
/* Accept remote resource */
|
||||
if (isEqual(resource, this.remoteResource)) {
|
||||
if (this.extUri.isEqual(resource, this.remoteResource)) {
|
||||
return this.acceptRemote(resourcePreview);
|
||||
}
|
||||
|
||||
/* Accept preview resource */
|
||||
if (isEqual(resource, this.previewResource)) {
|
||||
if (this.extUri.isEqual(resource, this.previewResource)) {
|
||||
return resourcePreview.previewResult;
|
||||
}
|
||||
|
||||
|
@ -205,16 +204,16 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
|
|||
}
|
||||
|
||||
async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource: URI }[]> {
|
||||
return [{ resource: joinPath(uri, 'globalState.json'), comparableResource: GlobalStateSynchroniser.GLOBAL_STATE_DATA_URI }];
|
||||
return [{ resource: this.extUri.joinPath(uri, 'globalState.json'), comparableResource: GlobalStateSynchroniser.GLOBAL_STATE_DATA_URI }];
|
||||
}
|
||||
|
||||
async resolveContent(uri: URI): Promise<string | null> {
|
||||
if (isEqual(uri, GlobalStateSynchroniser.GLOBAL_STATE_DATA_URI)) {
|
||||
if (this.extUri.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)) {
|
||||
if (this.extUri.isEqual(this.remoteResource, uri) || this.extUri.isEqual(this.localResource, uri) || this.extUri.isEqual(this.acceptedResource, uri)) {
|
||||
return this.resolvePreviewContent(uri);
|
||||
}
|
||||
|
||||
|
@ -223,11 +222,11 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
|
|||
return content;
|
||||
}
|
||||
|
||||
content = await super.resolveContent(dirname(uri));
|
||||
content = await super.resolveContent(this.extUri.dirname(uri));
|
||||
if (content) {
|
||||
const syncData = this.parseSyncData(content);
|
||||
if (syncData) {
|
||||
switch (basename(uri)) {
|
||||
switch (this.extUri.basename(uri)) {
|
||||
case 'globalState.json':
|
||||
return this.format(JSON.parse(syncData.content));
|
||||
}
|
||||
|
@ -238,7 +237,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
|
|||
}
|
||||
|
||||
private format(globalState: IGlobalState): string {
|
||||
const storageKeys = Object.keys(globalState.storage).sort();
|
||||
const storageKeys = globalState.storage ? Object.keys(globalState.storage).sort() : [];
|
||||
const storage: IStringDictionary<IStorageValue> = {};
|
||||
storageKeys.forEach(key => storage[key] = globalState.storage[key]);
|
||||
globalState.storage = storage;
|
||||
|
|
|
@ -21,7 +21,6 @@ import { isNonEmptyArray } from 'vs/base/common/arrays';
|
|||
import { AbstractInitializer, 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';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
|
||||
|
@ -55,7 +54,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
|
|||
|
||||
/* Version 2: Change settings from `sync.${setting}` to `settingsSync.{setting}` */
|
||||
protected readonly version: number = 2;
|
||||
private readonly previewResource: URI = joinPath(this.syncPreviewFolder, 'keybindings.json');
|
||||
private readonly previewResource: URI = this.extUri.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' });
|
||||
|
@ -149,7 +148,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
|
|||
protected async getAcceptResult(resourcePreview: IKeybindingsResourcePreview, resource: URI, content: string | null | undefined, token: CancellationToken): Promise<IAcceptResult> {
|
||||
|
||||
/* Accept local resource */
|
||||
if (isEqual(resource, this.localResource)) {
|
||||
if (this.extUri.isEqual(resource, this.localResource)) {
|
||||
return {
|
||||
content: resourcePreview.fileContent ? resourcePreview.fileContent.value.toString() : null,
|
||||
localChange: Change.None,
|
||||
|
@ -158,7 +157,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
|
|||
}
|
||||
|
||||
/* Accept remote resource */
|
||||
if (isEqual(resource, this.remoteResource)) {
|
||||
if (this.extUri.isEqual(resource, this.remoteResource)) {
|
||||
return {
|
||||
content: resourcePreview.remoteContent,
|
||||
localChange: Change.Modified,
|
||||
|
@ -167,7 +166,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
|
|||
}
|
||||
|
||||
/* Accept preview resource */
|
||||
if (isEqual(resource, this.previewResource)) {
|
||||
if (this.extUri.isEqual(resource, this.previewResource)) {
|
||||
if (content === undefined) {
|
||||
return {
|
||||
content: resourcePreview.previewResult.content,
|
||||
|
@ -258,22 +257,22 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
|
|||
|
||||
async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource: URI }[]> {
|
||||
const comparableResource = (await this.fileService.exists(this.file)) ? this.file : this.localResource;
|
||||
return [{ resource: joinPath(uri, 'keybindings.json'), comparableResource }];
|
||||
return [{ resource: this.extUri.joinPath(uri, 'keybindings.json'), comparableResource }];
|
||||
}
|
||||
|
||||
async resolveContent(uri: URI): Promise<string | null> {
|
||||
if (isEqual(this.remoteResource, uri) || isEqual(this.localResource, uri) || isEqual(this.acceptedResource, uri)) {
|
||||
if (this.extUri.isEqual(this.remoteResource, uri) || this.extUri.isEqual(this.localResource, uri) || this.extUri.isEqual(this.acceptedResource, uri)) {
|
||||
return this.resolvePreviewContent(uri);
|
||||
}
|
||||
let content = await super.resolveContent(uri);
|
||||
if (content) {
|
||||
return content;
|
||||
}
|
||||
content = await super.resolveContent(dirname(uri));
|
||||
content = await super.resolveContent(this.extUri.dirname(uri));
|
||||
if (content) {
|
||||
const syncData = this.parseSyncData(content);
|
||||
if (syncData) {
|
||||
switch (basename(uri)) {
|
||||
switch (this.extUri.basename(uri)) {
|
||||
case 'keybindings.json':
|
||||
return this.getKeybindingsContentFromSyncContent(syncData.content);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import { AbstractInitializer, AbstractJsonFileSynchroniser, IAcceptResult, IFile
|
|||
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';
|
||||
|
@ -49,7 +48,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
|
|||
|
||||
/* Version 2: Change settings from `sync.${setting}` to `settingsSync.{setting}` */
|
||||
protected readonly version: number = 2;
|
||||
readonly previewResource: URI = joinPath(this.syncPreviewFolder, 'settings.json');
|
||||
readonly previewResource: URI = this.extUri.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' });
|
||||
|
@ -141,7 +140,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
|
|||
const ignoredSettings = await this.getIgnoredSettings();
|
||||
|
||||
/* Accept local resource */
|
||||
if (isEqual(resource, this.localResource)) {
|
||||
if (this.extUri.isEqual(resource, this.localResource)) {
|
||||
return {
|
||||
/* Remove ignored settings */
|
||||
content: resourcePreview.fileContent ? updateIgnoredSettings(resourcePreview.fileContent.value.toString(), '{}', ignoredSettings, formattingOptions) : null,
|
||||
|
@ -151,7 +150,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
|
|||
}
|
||||
|
||||
/* Accept remote resource */
|
||||
if (isEqual(resource, this.remoteResource)) {
|
||||
if (this.extUri.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,
|
||||
|
@ -161,7 +160,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
|
|||
}
|
||||
|
||||
/* Accept preview resource */
|
||||
if (isEqual(resource, this.previewResource)) {
|
||||
if (this.extUri.isEqual(resource, this.previewResource)) {
|
||||
if (content === undefined) {
|
||||
return {
|
||||
content: resourcePreview.previewResult.content,
|
||||
|
@ -244,24 +243,24 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
|
|||
|
||||
async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource: URI }[]> {
|
||||
const comparableResource = (await this.fileService.exists(this.file)) ? this.file : this.localResource;
|
||||
return [{ resource: joinPath(uri, 'settings.json'), comparableResource }];
|
||||
return [{ resource: this.extUri.joinPath(uri, 'settings.json'), comparableResource }];
|
||||
}
|
||||
|
||||
async resolveContent(uri: URI): Promise<string | null> {
|
||||
if (isEqual(this.remoteResource, uri) || isEqual(this.localResource, uri) || isEqual(this.acceptedResource, uri)) {
|
||||
if (this.extUri.isEqual(this.remoteResource, uri) || this.extUri.isEqual(this.localResource, uri) || this.extUri.isEqual(this.acceptedResource, uri)) {
|
||||
return this.resolvePreviewContent(uri);
|
||||
}
|
||||
let content = await super.resolveContent(uri);
|
||||
if (content) {
|
||||
return content;
|
||||
}
|
||||
content = await super.resolveContent(dirname(uri));
|
||||
content = await super.resolveContent(this.extUri.dirname(uri));
|
||||
if (content) {
|
||||
const syncData = this.parseSyncData(content);
|
||||
if (syncData) {
|
||||
const settingsSyncContent = this.parseSettingsSyncContent(syncData.content);
|
||||
if (settingsSyncContent) {
|
||||
switch (basename(uri)) {
|
||||
switch (this.extUri.basename(uri)) {
|
||||
case 'settings.json':
|
||||
return settingsSyncContent.settings;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import { AbstractInitializer, AbstractSynchroniser, IAcceptResult, IFileResource
|
|||
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, basename, dirname } from 'vs/base/common/resources';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { merge, IMergeResult as ISnippetsMergeResult, areSame } from 'vs/platform/userDataSync/common/snippetsMerge';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
@ -53,7 +52,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
}
|
||||
|
||||
private onFileChanges(e: FileChangesEvent): void {
|
||||
if (!e.changes.some(change => isEqualOrParent(change.resource, this.snippetsFolder))) {
|
||||
if (!e.changes.some(change => this.extUri.isEqualOrParent(change.resource, this.snippetsFolder))) {
|
||||
return;
|
||||
}
|
||||
this.triggerLocalChange();
|
||||
|
@ -82,7 +81,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
protected async getAcceptResult(resourcePreview: ISnippetsResourcePreview, resource: URI, content: string | null | undefined, token: CancellationToken): Promise<IAcceptResult> {
|
||||
|
||||
/* Accept local resource */
|
||||
if (isEqualOrParent(resource, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }))) {
|
||||
if (this.extUri.isEqualOrParent(resource, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }))) {
|
||||
return {
|
||||
content: resourcePreview.fileContent ? resourcePreview.fileContent.value.toString() : null,
|
||||
localChange: Change.None,
|
||||
|
@ -93,7 +92,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
}
|
||||
|
||||
/* Accept remote resource */
|
||||
if (isEqualOrParent(resource, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }))) {
|
||||
if (this.extUri.isEqualOrParent(resource, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }))) {
|
||||
return {
|
||||
content: resourcePreview.remoteContent,
|
||||
localChange: resourcePreview.remoteContent !== null
|
||||
|
@ -104,7 +103,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
}
|
||||
|
||||
/* Accept preview resource */
|
||||
if (isEqualOrParent(resource, this.syncPreviewFolder)) {
|
||||
if (this.extUri.isEqualOrParent(resource, this.syncPreviewFolder)) {
|
||||
if (content === undefined) {
|
||||
return {
|
||||
content: resourcePreview.previewResult.content,
|
||||
|
@ -172,15 +171,15 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
};
|
||||
resourcePreviews.set(key, {
|
||||
fileContent: null,
|
||||
localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }),
|
||||
localResource: this.extUri.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' }),
|
||||
remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
|
||||
remoteContent: remoteSnippets[key],
|
||||
previewResource: joinPath(this.syncPreviewFolder, key),
|
||||
previewResource: this.extUri.joinPath(this.syncPreviewFolder, key),
|
||||
previewResult,
|
||||
localChange: previewResult.localChange,
|
||||
remoteChange: previewResult.remoteChange,
|
||||
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -193,16 +192,16 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
remoteChange: Change.None,
|
||||
};
|
||||
resourcePreviews.set(key, {
|
||||
localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }),
|
||||
localResource: this.extUri.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' }),
|
||||
remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
|
||||
remoteContent: remoteSnippets[key],
|
||||
previewResource: joinPath(this.syncPreviewFolder, key),
|
||||
previewResource: this.extUri.joinPath(this.syncPreviewFolder, key),
|
||||
previewResult,
|
||||
localChange: previewResult.localChange,
|
||||
remoteChange: previewResult.remoteChange,
|
||||
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -215,16 +214,16 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
remoteChange: Change.None,
|
||||
};
|
||||
resourcePreviews.set(key, {
|
||||
localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }),
|
||||
localResource: this.extUri.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' }),
|
||||
remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
|
||||
remoteContent: null,
|
||||
previewResource: joinPath(this.syncPreviewFolder, key),
|
||||
previewResource: this.extUri.joinPath(this.syncPreviewFolder, key),
|
||||
previewResult,
|
||||
localChange: previewResult.localChange,
|
||||
remoteChange: previewResult.remoteChange,
|
||||
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -237,16 +236,16 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
remoteChange: Change.Added,
|
||||
};
|
||||
resourcePreviews.set(key, {
|
||||
localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }),
|
||||
localResource: this.extUri.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' }),
|
||||
remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
|
||||
remoteContent: null,
|
||||
previewResource: joinPath(this.syncPreviewFolder, key),
|
||||
previewResource: this.extUri.joinPath(this.syncPreviewFolder, key),
|
||||
previewResult,
|
||||
localChange: previewResult.localChange,
|
||||
remoteChange: previewResult.remoteChange,
|
||||
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -259,16 +258,16 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
remoteChange: Change.Modified,
|
||||
};
|
||||
resourcePreviews.set(key, {
|
||||
localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }),
|
||||
localResource: this.extUri.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' }),
|
||||
remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
|
||||
remoteContent: remoteSnippets[key],
|
||||
previewResource: joinPath(this.syncPreviewFolder, key),
|
||||
previewResource: this.extUri.joinPath(this.syncPreviewFolder, key),
|
||||
previewResult,
|
||||
localChange: previewResult.localChange,
|
||||
remoteChange: previewResult.remoteChange,
|
||||
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -281,16 +280,16 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
remoteChange: Change.Deleted,
|
||||
};
|
||||
resourcePreviews.set(key, {
|
||||
localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }),
|
||||
localResource: this.extUri.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' }),
|
||||
remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
|
||||
remoteContent: remoteSnippets[key],
|
||||
previewResource: joinPath(this.syncPreviewFolder, key),
|
||||
previewResource: this.extUri.joinPath(this.syncPreviewFolder, key),
|
||||
previewResult,
|
||||
localChange: previewResult.localChange,
|
||||
remoteChange: previewResult.remoteChange,
|
||||
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -303,16 +302,16 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
remoteChange: remoteSnippets[key] ? Change.Modified : Change.Added
|
||||
};
|
||||
resourcePreviews.set(key, {
|
||||
localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }),
|
||||
localResource: this.extUri.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' }),
|
||||
remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
|
||||
remoteContent: remoteSnippets[key] || null,
|
||||
previewResource: joinPath(this.syncPreviewFolder, key),
|
||||
previewResource: this.extUri.joinPath(this.syncPreviewFolder, key),
|
||||
previewResult,
|
||||
localChange: previewResult.localChange,
|
||||
remoteChange: previewResult.remoteChange,
|
||||
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -326,16 +325,16 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
remoteChange: Change.None
|
||||
};
|
||||
resourcePreviews.set(key, {
|
||||
localResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }),
|
||||
localResource: this.extUri.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' }),
|
||||
remoteResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
|
||||
remoteContent: remoteSnippets[key] || null,
|
||||
previewResource: joinPath(this.syncPreviewFolder, key),
|
||||
previewResource: this.extUri.joinPath(this.syncPreviewFolder, key),
|
||||
previewResult,
|
||||
localChange: previewResult.localChange,
|
||||
remoteChange: previewResult.remoteChange,
|
||||
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
acceptedResource: this.extUri.joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -351,10 +350,10 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
const snippets = this.parseSnippets(syncData);
|
||||
const result = [];
|
||||
for (const snippet of Object.keys(snippets)) {
|
||||
const resource = joinPath(uri, snippet);
|
||||
const comparableResource = joinPath(this.snippetsFolder, snippet);
|
||||
const resource = this.extUri.joinPath(uri, snippet);
|
||||
const comparableResource = this.extUri.joinPath(this.snippetsFolder, snippet);
|
||||
const exists = await this.fileService.exists(comparableResource);
|
||||
result.push({ resource, comparableResource: exists ? comparableResource : joinPath(this.syncPreviewFolder, snippet).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }) });
|
||||
result.push({ resource, comparableResource: exists ? comparableResource : this.extUri.joinPath(this.syncPreviewFolder, snippet).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }) });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -363,9 +362,9 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
}
|
||||
|
||||
async resolveContent(uri: URI): Promise<string | null> {
|
||||
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' }))) {
|
||||
if (this.extUri.isEqualOrParent(uri, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }))
|
||||
|| this.extUri.isEqualOrParent(uri, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' }))
|
||||
|| this.extUri.isEqualOrParent(uri, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }))) {
|
||||
return this.resolvePreviewContent(uri);
|
||||
}
|
||||
|
||||
|
@ -374,12 +373,12 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
return content;
|
||||
}
|
||||
|
||||
content = await super.resolveContent(dirname(uri));
|
||||
content = await super.resolveContent(this.extUri.dirname(uri));
|
||||
if (content) {
|
||||
const syncData = this.parseSyncData(content);
|
||||
if (syncData) {
|
||||
const snippets = this.parseSnippets(syncData);
|
||||
return snippets[basename(uri)] || null;
|
||||
return snippets[this.extUri.basename(uri)] || null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -402,7 +401,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
const local: IStringDictionary<IFileContent> = {};
|
||||
for (const resourcePreview of resourcePreviews) {
|
||||
if (resourcePreview.fileContent) {
|
||||
local[basename(resourcePreview.localResource!)] = resourcePreview.fileContent;
|
||||
local[this.extUri.basename(resourcePreview.localResource!)] = resourcePreview.fileContent;
|
||||
}
|
||||
}
|
||||
await this.backupLocal(JSON.stringify(this.toSnippetsContents(local)));
|
||||
|
@ -411,28 +410,28 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
private async updateLocalSnippets(resourcePreviews: ISnippetsAcceptedResourcePreview[], force: boolean): Promise<void> {
|
||||
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);
|
||||
const key = remoteResource ? this.extUri.basename(remoteResource) : this.extUri.basename(localResource!);
|
||||
const resource = this.extUri.joinPath(this.snippetsFolder, key);
|
||||
|
||||
// Removed
|
||||
if (localChange === Change.Deleted) {
|
||||
this.logService.trace(`${this.syncResourceLogLabel}: Deleting snippet...`, basename(resource));
|
||||
this.logService.trace(`${this.syncResourceLogLabel}: Deleting snippet...`, this.extUri.basename(resource));
|
||||
await this.fileService.del(resource);
|
||||
this.logService.info(`${this.syncResourceLogLabel}: Deleted snippet`, basename(resource));
|
||||
this.logService.info(`${this.syncResourceLogLabel}: Deleted snippet`, this.extUri.basename(resource));
|
||||
}
|
||||
|
||||
// Added
|
||||
else if (localChange === Change.Added) {
|
||||
this.logService.trace(`${this.syncResourceLogLabel}: Creating snippet...`, basename(resource));
|
||||
this.logService.trace(`${this.syncResourceLogLabel}: Creating snippet...`, this.extUri.basename(resource));
|
||||
await this.fileService.createFile(resource, VSBuffer.fromString(acceptResult.content!), { overwrite: force });
|
||||
this.logService.info(`${this.syncResourceLogLabel}: Created snippet`, basename(resource));
|
||||
this.logService.info(`${this.syncResourceLogLabel}: Created snippet`, this.extUri.basename(resource));
|
||||
}
|
||||
|
||||
// Updated
|
||||
else {
|
||||
this.logService.trace(`${this.syncResourceLogLabel}: Updating snippet...`, basename(resource));
|
||||
this.logService.trace(`${this.syncResourceLogLabel}: Updating snippet...`, this.extUri.basename(resource));
|
||||
await this.fileService.writeFile(resource, VSBuffer.fromString(acceptResult.content!), force ? undefined : fileContent!);
|
||||
this.logService.info(`${this.syncResourceLogLabel}: Updated snippet`, basename(resource));
|
||||
this.logService.info(`${this.syncResourceLogLabel}: Updated snippet`, this.extUri.basename(resource));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -444,7 +443,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
|
||||
for (const { acceptResult, localResource, remoteResource, remoteChange } of resourcePreviews) {
|
||||
if (remoteChange !== Change.None) {
|
||||
const key = localResource ? basename(localResource) : basename(remoteResource!);
|
||||
const key = localResource ? this.extUri.basename(localResource) : this.extUri.basename(remoteResource!);
|
||||
if (remoteChange === Change.Deleted) {
|
||||
delete newSnippets[key];
|
||||
} else {
|
||||
|
@ -489,9 +488,9 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
|
|||
}
|
||||
for (const entry of stat.children || []) {
|
||||
const resource = entry.resource;
|
||||
const extension = extname(resource);
|
||||
const extension = this.extUri.extname(resource);
|
||||
if (extension === '.json' || extension === '.code-snippets') {
|
||||
const key = relativePath(this.snippetsFolder, resource)!;
|
||||
const key = this.extUri.relativePath(this.snippetsFolder, resource)!;
|
||||
const content = await this.fileService.readFile(resource);
|
||||
snippets[key] = content;
|
||||
}
|
||||
|
@ -526,9 +525,9 @@ export class SnippetsInitializer extends AbstractInitializer {
|
|||
for (const key of Object.keys(remoteSnippets)) {
|
||||
const content = remoteSnippets[key];
|
||||
if (content) {
|
||||
const resource = joinPath(this.environmentService.snippetsHome, key);
|
||||
const resource = this.extUri.joinPath(this.environmentService.snippetsHome, key);
|
||||
await this.fileService.createFile(resource, VSBuffer.fromString(content));
|
||||
this.logService.info('Created snippet', basename(resource));
|
||||
this.logService.info('Created snippet', this.extUri.basename(resource));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue