mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 13:29:44 +00:00
Merge branch 'main' into joh/disposeSnapshot
This commit is contained in:
commit
1735c9913d
|
@ -449,6 +449,7 @@
|
|||
"vs/workbench/~",
|
||||
"vs/workbench/services/*/~",
|
||||
"vs/workbench/contrib/*/~",
|
||||
"vscode-notebook-renderer", // Type of import
|
||||
{
|
||||
"when": "hasBrowser",
|
||||
"pattern": "xterm"
|
||||
|
|
|
@ -1 +1 @@
|
|||
2022-02-09T15:22:23.902Z
|
||||
2022-02-10T01:18:08.504Z
|
||||
|
|
|
@ -87,42 +87,27 @@ steps:
|
|||
set -e
|
||||
export npm_config_arch=$(NPM_ARCH)
|
||||
|
||||
# Download clang based on chromium revision used by vscode
|
||||
curl -s https://raw.githubusercontent.com/chromium/chromium/96.0.4664.110/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux
|
||||
# Download libcxx headers and objects from upstream electron releases
|
||||
DEBUG=libcxx-fetcher \
|
||||
VSCODE_LIBCXX_OBJECTS_DIR=$PWD/.build/libcxx-objects \
|
||||
VSCODE_LIBCXX_HEADERS_DIR=$PWD/.build/libcxx_headers \
|
||||
VSCODE_LIBCXXABI_HEADERS_DIR=$PWD/.build/libcxxabi_headers \
|
||||
VSCODE_ARCH="$(NPM_ARCH)" \
|
||||
node build/linux/libcxx-fetcher.js
|
||||
# Set compiler toolchain
|
||||
# Flags for the client build are based on
|
||||
# https://source.chromium.org/chromium/chromium/src/+/refs/tags/96.0.4664.110:build/config/arm.gni
|
||||
# https://source.chromium.org/chromium/chromium/src/+/refs/tags/96.0.4664.110:build/config/compiler/BUILD.gn
|
||||
# https://source.chromium.org/chromium/chromium/src/+/refs/tags/96.0.4664.110:build/config/c++/BUILD.gn
|
||||
export CC=$PWD/.build/CR_Clang/bin/clang
|
||||
export CXX=$PWD/.build/CR_Clang/bin/clang++
|
||||
export CXXFLAGS="-nostdinc++ -D__NO_INLINE__ -isystem$PWD/.build/libcxx_headers -isystem$PWD/.build/libcxx_headers/include -isystem$PWD/.build/libcxxabi_headers/include -fPIC -flto=thin -fsplit-lto-unit"
|
||||
export CFLAGS="-nostdinc++ -D__NO_INLINE__ -isystem$PWD/.build/libcxx_headers -isystem$PWD/.build/libcxx_headers/include -isystem$PWD/.build/libcxxabi_headers/include -fPIC -flto=thin -fsplit-lto-unit"
|
||||
export LDFLAGS="-stdlib=libc++ -fuse-ld=lld -flto=thin -L$PWD/.build/libcxx-objects -lc++abi -Wl,--lto-O0"
|
||||
export VSCODE_REMOTE_CC=$(which gcc)
|
||||
export VSCODE_REMOTE_CXX=$(which g++)
|
||||
|
||||
if [ "$VSCODE_ARCH" == "arm64" ]; then
|
||||
export CXXFLAGS="${CXXFLAGS} --target=aarch64-linux-gnu -fwhole-program-vtables"
|
||||
export CFLAGS="${CFLAGS} --target=aarch64-linux-gnu -fwhole-program-vtables"
|
||||
export LDFLAGS="${LDFLAGS} --target=aarch64-linux-gnu -march=arm64 -fwhole-program-vtables"
|
||||
export VSCODE_REMOTE_CC=/usr/bin/aarch64-linux-gnu-gcc
|
||||
export VSCODE_REMOTE_CXX=/usr/bin/aarch64-linux-gnu-g++
|
||||
fi
|
||||
|
||||
if [ "$VSCODE_ARCH" == "armhf" ]; then
|
||||
export CXXFLAGS="${CXXFLAGS} --target=arm-linux-gnueabihf -march=armv7-a -mthumb -mfloat-abi=hard -mfpu=neon -mtune=generic-armv7-a"
|
||||
export CFLAGS="${CFLAGS} --target=arm-linux-gnueabihf -march=armv7-a -mthumb -mfloat-abi=hard -mfpu=neon -mtune=generic-armv7-a"
|
||||
export LDFLAGS="${LDFLAGS} --target=arm-linux-gnueabihf -march=armv7-a"
|
||||
export VSCODE_REMOTE_CC=/usr/bin/arm-linux-gnueabihf-gcc
|
||||
export VSCODE_REMOTE_CXX=/usr/bin/arm-linux-gnueabihf-g++
|
||||
if [ -z "$CC" ] || [ -z "$CXX" ]; then
|
||||
# Download clang based on chromium revision used by vscode
|
||||
curl -s https://raw.githubusercontent.com/chromium/chromium/96.0.4664.110/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux
|
||||
# Download libcxx headers and objects from upstream electron releases
|
||||
DEBUG=libcxx-fetcher \
|
||||
VSCODE_LIBCXX_OBJECTS_DIR=$PWD/.build/libcxx-objects \
|
||||
VSCODE_LIBCXX_HEADERS_DIR=$PWD/.build/libcxx_headers \
|
||||
VSCODE_LIBCXXABI_HEADERS_DIR=$PWD/.build/libcxxabi_headers \
|
||||
VSCODE_ARCH="$(NPM_ARCH)" \
|
||||
node build/linux/libcxx-fetcher.js
|
||||
# Set compiler toolchain
|
||||
# Flags for the client build are based on
|
||||
# https://source.chromium.org/chromium/chromium/src/+/refs/tags/96.0.4664.110:build/config/arm.gni
|
||||
# https://source.chromium.org/chromium/chromium/src/+/refs/tags/96.0.4664.110:build/config/compiler/BUILD.gn
|
||||
# https://source.chromium.org/chromium/chromium/src/+/refs/tags/96.0.4664.110:build/config/c++/BUILD.gn
|
||||
export CC=$PWD/.build/CR_Clang/bin/clang
|
||||
export CXX=$PWD/.build/CR_Clang/bin/clang++
|
||||
export CXXFLAGS="-nostdinc++ -D__NO_INLINE__ -isystem$PWD/.build/libcxx_headers -isystem$PWD/.build/libcxx_headers/include -isystem$PWD/.build/libcxxabi_headers/include -fPIC -flto=thin -fsplit-lto-unit"
|
||||
export LDFLAGS="-stdlib=libc++ -fuse-ld=lld -flto=thin -L$PWD/.build/libcxx-objects -lc++abi -Wl,--lto-O0"
|
||||
export VSCODE_REMOTE_CC=$(which gcc)
|
||||
export VSCODE_REMOTE_CXX=$(which g++)
|
||||
fi
|
||||
|
||||
for i in {1..3}; do # try 3 times, for Terrapin
|
||||
|
|
|
@ -53,7 +53,7 @@ function hasSupportedVisualStudioVersion() {
|
|||
const path = require('path');
|
||||
// Translated over from
|
||||
// https://source.chromium.org/chromium/chromium/src/+/master:build/vs_toolchain.py;l=140-175
|
||||
const supportedVersions = ['2019', '2017'];
|
||||
const supportedVersions = ['2022', '2019', '2017'];
|
||||
|
||||
const availableVersions = [];
|
||||
for (const version of supportedVersions) {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
"main": "./out/node/cssServerMain",
|
||||
"browser": "./dist/browser/cssServerMain",
|
||||
"dependencies": {
|
||||
"vscode-css-languageservice": "^5.1.12",
|
||||
"vscode-css-languageservice": "^5.1.13",
|
||||
"vscode-languageserver": "^7.0.0",
|
||||
"vscode-uri": "^3.0.3"
|
||||
},
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae"
|
||||
integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
|
||||
|
||||
vscode-css-languageservice@^5.1.12:
|
||||
version "5.1.12"
|
||||
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-5.1.12.tgz#eed3a6a1bb8a7a70636ef07cd742a034c71635a1"
|
||||
integrity sha512-293C5C2732Rbhh3opTs+nQBpC5Dd+oYrEA8lc0OWdyt40oYmJ331FV7NMF1SLFSIcOFB5XveLiWUZak2oyc49Q==
|
||||
vscode-css-languageservice@^5.1.13:
|
||||
version "5.1.13"
|
||||
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-5.1.13.tgz#debc7c8368223b211a734cb7eb7789c586d3e2d9"
|
||||
integrity sha512-FA0foqMzMmEoO0WJP+MjoD4dRERhKS+Ag+yBrtmWQDmw2OuZ1R/5FkvI/XdTkCpHmTD9VMczugpHRejQyTXCNQ==
|
||||
dependencies:
|
||||
vscode-languageserver-textdocument "^1.0.1"
|
||||
vscode-languageserver-types "^3.16.0"
|
||||
|
|
|
@ -1273,13 +1273,14 @@ export class Repository implements Disposable {
|
|||
const diffEditorTabsToClose: Tab[] = [];
|
||||
|
||||
// Index
|
||||
diffEditorTabsToClose.push(...window.tabs
|
||||
const tabs = window.tabGroups.all.map(g => g.tabs).flat(1);
|
||||
diffEditorTabsToClose.push(...tabs
|
||||
.filter(t =>
|
||||
t.resource && t.resource.scheme === 'git' && t.viewId === 'diff' &&
|
||||
indexResources.some(r => pathEquals(r, t.resource!.fsPath))));
|
||||
|
||||
// Working Tree
|
||||
diffEditorTabsToClose.push(...window.tabs
|
||||
diffEditorTabsToClose.push(...tabs
|
||||
.filter(t =>
|
||||
t.resource && t.resource.scheme === 'file' && t.viewId === 'diff' &&
|
||||
workingTreeResources.some(r => pathEquals(r, t.resource!.fsPath)) &&
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
},
|
||||
"main": "./out/node/htmlServerMain",
|
||||
"dependencies": {
|
||||
"vscode-css-languageservice": "^5.1.12",
|
||||
"vscode-css-languageservice": "^5.1.13",
|
||||
"vscode-html-languageservice": "^4.2.1",
|
||||
"vscode-languageserver": "^7.0.0",
|
||||
"vscode-languageserver-textdocument": "^1.0.3",
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae"
|
||||
integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
|
||||
|
||||
vscode-css-languageservice@^5.1.12:
|
||||
version "5.1.12"
|
||||
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-5.1.12.tgz#eed3a6a1bb8a7a70636ef07cd742a034c71635a1"
|
||||
integrity sha512-293C5C2732Rbhh3opTs+nQBpC5Dd+oYrEA8lc0OWdyt40oYmJ331FV7NMF1SLFSIcOFB5XveLiWUZak2oyc49Q==
|
||||
vscode-css-languageservice@^5.1.13:
|
||||
version "5.1.13"
|
||||
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-5.1.13.tgz#debc7c8368223b211a734cb7eb7789c586d3e2d9"
|
||||
integrity sha512-FA0foqMzMmEoO0WJP+MjoD4dRERhKS+Ag+yBrtmWQDmw2OuZ1R/5FkvI/XdTkCpHmTD9VMczugpHRejQyTXCNQ==
|
||||
dependencies:
|
||||
vscode-languageserver-textdocument "^1.0.1"
|
||||
vscode-languageserver-types "^3.16.0"
|
||||
|
|
|
@ -25,10 +25,17 @@
|
|||
"image/gif",
|
||||
"image/png",
|
||||
"image/jpeg",
|
||||
"image/git",
|
||||
"image/svg+xml",
|
||||
"text/html",
|
||||
"application/javascript",
|
||||
"application/vnd.code.notebook.error"
|
||||
"application/vnd.code.notebook.error",
|
||||
"application/vnd.code.notebook.stdout",
|
||||
"application/x.notebook.stdout",
|
||||
"application/x.notebook.stream",
|
||||
"application/vnd.code.notebook.stderr",
|
||||
"application/x.notebook.stderr",
|
||||
"text/plain"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -3,13 +3,16 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { ActivationFunction, OutputItem } from 'vscode-notebook-renderer';
|
||||
import type { ActivationFunction, OutputItem, RendererContext } from 'vscode-notebook-renderer';
|
||||
import { handleANSIOutput } from './ansi';
|
||||
import { truncatedArrayOfString } from './textHelper';
|
||||
|
||||
interface IDisposable {
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function renderImage(outputInfo: OutputItem, element: HTMLElement): IDisposable {
|
||||
const blob = new Blob([outputInfo.data()], { type: outputInfo.mime });
|
||||
const src = URL.createObjectURL(blob);
|
||||
|
@ -76,21 +79,19 @@ function renderJavascript(outputInfo: OutputItem, container: HTMLElement): void
|
|||
domEval(element);
|
||||
}
|
||||
|
||||
function renderError(outputIfo: OutputItem, container: HTMLElement): void {
|
||||
function renderError(outputInfo: OutputItem, container: HTMLElement): void {
|
||||
const element = document.createElement('div');
|
||||
container.appendChild(element);
|
||||
type ErrorLike = Partial<Error>;
|
||||
|
||||
let err: ErrorLike;
|
||||
try {
|
||||
err = <ErrorLike>JSON.parse(outputIfo.text());
|
||||
err = <ErrorLike>JSON.parse(outputInfo.text());
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(err);
|
||||
|
||||
if (err.stack) {
|
||||
const stack = document.createElement('pre');
|
||||
stack.classList.add('traceback');
|
||||
|
@ -109,9 +110,70 @@ function renderError(outputIfo: OutputItem, container: HTMLElement): void {
|
|||
container.classList.add('error');
|
||||
}
|
||||
|
||||
function renderStream(outputInfo: OutputItem, container: HTMLElement, error: boolean, ctx: RendererContext<void> & { readonly settings: { readonly lineLimit: number } }): void {
|
||||
const outputContainer = container.parentElement;
|
||||
if (!outputContainer) {
|
||||
// should never happen
|
||||
return;
|
||||
}
|
||||
|
||||
const prev = outputContainer.previousSibling;
|
||||
if (prev) {
|
||||
// OutputItem in the same cell
|
||||
// check if the previous item is a stream
|
||||
const outputElement = (prev.firstChild as HTMLElement | null);
|
||||
if (outputElement && outputElement.getAttribute('output-mime-type') === outputInfo.mime) {
|
||||
// same stream
|
||||
const text = outputInfo.text();
|
||||
|
||||
const element = document.createElement('span');
|
||||
truncatedArrayOfString(outputInfo.id, [text], 30, element);
|
||||
outputElement.appendChild(element);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const element = document.createElement('span');
|
||||
element.classList.add('output-stream');
|
||||
|
||||
const text = outputInfo.text();
|
||||
truncatedArrayOfString(outputInfo.id, [text], ctx.settings.lineLimit, element);
|
||||
container.appendChild(element);
|
||||
container.setAttribute('output-mime-type', outputInfo.mime);
|
||||
if (error) {
|
||||
container.classList.add('error');
|
||||
}
|
||||
}
|
||||
|
||||
function renderText(outputInfo: OutputItem, container: HTMLElement, ctx: RendererContext<void> & { readonly settings: { readonly lineLimit: number } }): void {
|
||||
const contentNode = document.createElement('div');
|
||||
contentNode.classList.add('.output-plaintext');
|
||||
const text = outputInfo.text();
|
||||
truncatedArrayOfString(outputInfo.id, [text], ctx.settings.lineLimit, contentNode);
|
||||
container.appendChild(contentNode);
|
||||
|
||||
}
|
||||
|
||||
export const activate: ActivationFunction<void> = (ctx) => {
|
||||
const disposables = new Map<string, IDisposable>();
|
||||
const latestContext = ctx as (RendererContext<void> & { readonly settings: { readonly lineLimit: number } });
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.output-stream {
|
||||
line-height: 22px;
|
||||
font-family: var(--notebook-cell-output-font-family);
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
|
||||
font-size: var(--notebook-cell-output-font-size);
|
||||
user-select: text;
|
||||
-webkit-user-select: text;
|
||||
-ms-user-select: text;
|
||||
cursor: auto;
|
||||
}
|
||||
`;
|
||||
document.body.appendChild(style);
|
||||
return {
|
||||
renderOutputItem: (outputInfo, element) => {
|
||||
switch (outputInfo.mime) {
|
||||
|
@ -137,6 +199,7 @@ export const activate: ActivationFunction<void> = (ctx) => {
|
|||
case 'image/gif':
|
||||
case 'image/png':
|
||||
case 'image/jpeg':
|
||||
case 'image/git':
|
||||
{
|
||||
const disposable = renderImage(outputInfo, element);
|
||||
disposables.set(outputInfo.id, disposable);
|
||||
|
@ -146,6 +209,25 @@ export const activate: ActivationFunction<void> = (ctx) => {
|
|||
{
|
||||
renderError(outputInfo, element);
|
||||
}
|
||||
break;
|
||||
case 'application/vnd.code.notebook.stdout':
|
||||
case 'application/x.notebook.stdout':
|
||||
case 'application/x.notebook.stream':
|
||||
{
|
||||
renderStream(outputInfo, element, false, latestContext);
|
||||
}
|
||||
break;
|
||||
case 'application/vnd.code.notebook.stderr':
|
||||
case 'application/x.notebook.stderr':
|
||||
{
|
||||
renderStream(outputInfo, element, true, latestContext);
|
||||
}
|
||||
break;
|
||||
case 'text/plain':
|
||||
{
|
||||
renderText(outputInfo, element, latestContext);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
51
extensions/notebook-renderers/src/textHelper.ts
Normal file
51
extensions/notebook-renderers/src/textHelper.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { handleANSIOutput } from './ansi';
|
||||
|
||||
function generateViewMoreElement(outputId: string) {
|
||||
const container = document.createElement('span');
|
||||
const first = document.createElement('span');
|
||||
first.textContent = 'Output exceeds the ';
|
||||
const second = document.createElement('a');
|
||||
second.textContent = 'size limit';
|
||||
second.href = `command:workbench.action.openSettings?["notebook.output.textLineLimit"]`;
|
||||
const third = document.createElement('span');
|
||||
third.textContent = '. Open the full output data';
|
||||
const forth = document.createElement('a');
|
||||
forth.textContent = ' in a text editor';
|
||||
forth.href = `command:workbench.action.openLargeOutput?${outputId}`;
|
||||
container.appendChild(first);
|
||||
container.appendChild(second);
|
||||
container.appendChild(third);
|
||||
container.appendChild(forth);
|
||||
return container;
|
||||
}
|
||||
|
||||
export function truncatedArrayOfString(id: string, outputs: string[], linesLimit: number, container: HTMLElement) {
|
||||
let buffer = outputs.join('\n').split(/\r|\n|\r\n/g);
|
||||
let lineCount = buffer.length;
|
||||
|
||||
if (lineCount < linesLimit) {
|
||||
const spanElement = handleANSIOutput(buffer.slice(0, linesLimit).join('\n'));
|
||||
container.appendChild(spanElement);
|
||||
return;
|
||||
}
|
||||
|
||||
container.appendChild(generateViewMoreElement(id));
|
||||
|
||||
const div = document.createElement('div');
|
||||
container.appendChild(div);
|
||||
div.appendChild(handleANSIOutput(buffer.slice(0, linesLimit - 5).join('\n')));
|
||||
|
||||
// view more ...
|
||||
const viewMoreSpan = document.createElement('span');
|
||||
viewMoreSpan.innerText = '...';
|
||||
container.appendChild(viewMoreSpan);
|
||||
|
||||
const div2 = document.createElement('div');
|
||||
container.appendChild(div2);
|
||||
div2.appendChild(handleANSIOutput(buffer.slice(lineCount - 5).join('\n')));
|
||||
}
|
|
@ -16,7 +16,8 @@
|
|||
{
|
||||
"id": "razor",
|
||||
"extensions": [
|
||||
".cshtml"
|
||||
".cshtml",
|
||||
".razor"
|
||||
],
|
||||
"aliases": [
|
||||
"Razor",
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
{
|
||||
"name": "Types declaration and references",
|
||||
"scope": [
|
||||
"meta.return-type",
|
||||
"support.class",
|
||||
"support.type",
|
||||
"entity.name.type",
|
||||
|
|
|
@ -353,7 +353,6 @@
|
|||
{
|
||||
"name": "Types declaration and references",
|
||||
"scope": [
|
||||
"meta.return-type",
|
||||
"support.class",
|
||||
"support.type",
|
||||
"entity.name.type",
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
{
|
||||
"name": "Types declaration and references",
|
||||
"scope": [
|
||||
"meta.return-type",
|
||||
"support.class",
|
||||
"support.type",
|
||||
"entity.name.type",
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"git": {
|
||||
"name": "seti-ui",
|
||||
"repositoryUrl": "https://github.com/jesseweed/seti-ui",
|
||||
"commitHash": "bc194faed12b10692807f47b97f0ff963e4c9f24"
|
||||
"commitHash": "529789dc4eee3e2fdc5985f082ee58adfb97696c"
|
||||
}
|
||||
},
|
||||
"version": "0.1.0"
|
||||
|
|
Binary file not shown.
|
@ -1683,8 +1683,6 @@
|
|||
"tf": "_terraform",
|
||||
"tf.json": "_terraform",
|
||||
"tfvars": "_terraform",
|
||||
"tex": "_tex",
|
||||
"sty": "_tex_1",
|
||||
"dtx": "_tex_2",
|
||||
"ins": "_tex_3",
|
||||
"toml": "_config",
|
||||
|
@ -1709,6 +1707,7 @@
|
|||
"ttf": "_font",
|
||||
"woff": "_font",
|
||||
"woff2": "_font",
|
||||
"avif": "_image",
|
||||
"gif": "_image",
|
||||
"jpg": "_image",
|
||||
"jpeg": "_image",
|
||||
|
@ -1865,6 +1864,8 @@
|
|||
"javascript": "_javascript",
|
||||
"json": "_json",
|
||||
"julia": "_julia",
|
||||
"tex": "_tex_1",
|
||||
"latex": "_tex",
|
||||
"less": "_less",
|
||||
"lua": "_lua",
|
||||
"makefile": "_makefile",
|
||||
|
@ -2062,8 +2063,6 @@
|
|||
"tf": "_terraform_light",
|
||||
"tf.json": "_terraform_light",
|
||||
"tfvars": "_terraform_light",
|
||||
"tex": "_tex_light",
|
||||
"sty": "_tex_1_light",
|
||||
"dtx": "_tex_2_light",
|
||||
"ins": "_tex_3_light",
|
||||
"toml": "_config_light",
|
||||
|
@ -2088,6 +2087,7 @@
|
|||
"ttf": "_font_light",
|
||||
"woff": "_font_light",
|
||||
"woff2": "_font_light",
|
||||
"avif": "_image_light",
|
||||
"gif": "_image_light",
|
||||
"jpg": "_image_light",
|
||||
"jpeg": "_image_light",
|
||||
|
@ -2178,6 +2178,8 @@
|
|||
"javascript": "_javascript_light",
|
||||
"json": "_json_light",
|
||||
"julia": "_julia_light",
|
||||
"tex": "_tex_1_light",
|
||||
"latex": "_tex_light",
|
||||
"less": "_less_light",
|
||||
"lua": "_lua_light",
|
||||
"makefile": "_makefile_light",
|
||||
|
@ -2294,5 +2296,5 @@
|
|||
"npm-debug.log": "_npm_ignored_light"
|
||||
}
|
||||
},
|
||||
"version": "https://github.com/jesseweed/seti-ui/commit/bc194faed12b10692807f47b97f0ff963e4c9f24"
|
||||
"version": "https://github.com/jesseweed/seti-ui/commit/529789dc4eee3e2fdc5985f082ee58adfb97696c"
|
||||
}
|
|
@ -3,13 +3,13 @@
|
|||
"tokenColors": [
|
||||
{
|
||||
"settings": {
|
||||
"foreground": "#93A1A1"
|
||||
"foreground": "#839496"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["meta.embedded", "source.groovy.embedded"],
|
||||
"settings": {
|
||||
"foreground": "#93A1A1"
|
||||
"foreground": "#839496"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -17,7 +17,7 @@
|
|||
"scope": "comment",
|
||||
"settings": {
|
||||
"fontStyle": "italic",
|
||||
"foreground": "#657B83"
|
||||
"foreground": "#586E75"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -156,7 +156,7 @@
|
|||
"name": "Tag start/end",
|
||||
"scope": "punctuation.definition.tag",
|
||||
"settings": {
|
||||
"foreground": "#657B83"
|
||||
"foreground": "#586E75"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -369,7 +369,7 @@
|
|||
|
||||
// Editor
|
||||
"editor.background": "#002B36",
|
||||
// "editor.foreground": "#6688cc",
|
||||
"editor.foreground": "#839496",
|
||||
"editorWidget.background": "#00212B",
|
||||
"editorCursor.foreground": "#D30102",
|
||||
"editorWhitespace.foreground": "#93A1A180",
|
||||
|
@ -498,7 +498,7 @@
|
|||
"terminal.ansiMagenta": "#d33682",
|
||||
"terminal.ansiCyan": "#2aa198",
|
||||
"terminal.ansiWhite": "#eee8d5",
|
||||
"terminal.ansiBrightBlack": "#586e75",
|
||||
"terminal.ansiBrightBlack": "#002b36",
|
||||
"terminal.ansiBrightRed": "#cb4b16",
|
||||
"terminal.ansiBrightGreen": "#586e75",
|
||||
"terminal.ansiBrightYellow": "#657b83",
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
"scope": "storage",
|
||||
"settings": {
|
||||
"fontStyle": "bold",
|
||||
"foreground": "#073642"
|
||||
"foreground": "#586E75"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -361,8 +361,8 @@
|
|||
// "scrollbarSlider.hoverBackground": "",
|
||||
// Editor
|
||||
"editor.background": "#FDF6E3",
|
||||
"editor.foreground": "#657B83",
|
||||
"notebook.cellEditorBackground": "#F7F0E0",
|
||||
// "editor.foreground": "#6688cc",
|
||||
"editorWidget.background": "#EEE8D5",
|
||||
"editorCursor.foreground": "#657B83",
|
||||
"editorWhitespace.foreground": "#586E7580",
|
||||
|
@ -480,14 +480,14 @@
|
|||
"terminal.ansiMagenta": "#d33682",
|
||||
"terminal.ansiCyan": "#2aa198",
|
||||
"terminal.ansiWhite": "#eee8d5",
|
||||
"terminal.ansiBrightBlack": "#586e75",
|
||||
"terminal.ansiBrightBlack": "#002b36",
|
||||
"terminal.ansiBrightRed": "#cb4b16",
|
||||
"terminal.ansiBrightGreen": "#586e75",
|
||||
"terminal.ansiBrightYellow": "#657b83",
|
||||
"terminal.ansiBrightBlue": "#839496",
|
||||
"terminal.ansiBrightMagenta": "#6c71c4",
|
||||
"terminal.ansiBrightCyan": "#93a1a1",
|
||||
"terminal.ansiBrightWhite": "#eee8d5",
|
||||
"terminal.ansiBrightWhite": "#fdf6e3",
|
||||
// Interactive Playground
|
||||
"walkThrough.embeddedEditorBackground": "#00000014"
|
||||
},
|
||||
|
|
|
@ -140,70 +140,68 @@ export class IntellisenseStatus extends Disposable {
|
|||
this._state = newState;
|
||||
|
||||
switch (this._state.type) {
|
||||
case IntellisenseState.Type.None:
|
||||
case IntellisenseState.Type.None: {
|
||||
this._statusItem?.dispose();
|
||||
this._statusItem = undefined;
|
||||
break;
|
||||
|
||||
case IntellisenseState.Type.Pending:
|
||||
{
|
||||
const statusItem = this.ensureStatusItem();
|
||||
statusItem.severity = vscode.LanguageStatusSeverity.Information;
|
||||
statusItem.text = '$(loading~spin)';
|
||||
statusItem.detail = localize('pending.detail', 'Loading IntelliSense status');
|
||||
statusItem.command = undefined;
|
||||
break;
|
||||
}
|
||||
case IntellisenseState.Type.Pending: {
|
||||
const statusItem = this.ensureStatusItem();
|
||||
statusItem.severity = vscode.LanguageStatusSeverity.Information;
|
||||
statusItem.text = localize('pending.detail', 'Loading IntelliSense status');
|
||||
statusItem.detail = undefined;
|
||||
statusItem.command = undefined;
|
||||
statusItem.busy = true;
|
||||
break;
|
||||
}
|
||||
case IntellisenseState.Type.Resolved: {
|
||||
const rootPath = this._client.getWorkspaceRootForResource(this._state.resource);
|
||||
if (!rootPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
case IntellisenseState.Type.Resolved:
|
||||
{
|
||||
const rootPath = this._client.getWorkspaceRootForResource(this._state.resource);
|
||||
if (!rootPath) {
|
||||
return;
|
||||
}
|
||||
const statusItem = this.ensureStatusItem();
|
||||
statusItem.busy = false;
|
||||
statusItem.detail = undefined;
|
||||
|
||||
const statusItem = this.ensureStatusItem();
|
||||
statusItem.severity = vscode.LanguageStatusSeverity.Information;
|
||||
if (isImplicitProjectConfigFile(this._state.configFile)) {
|
||||
statusItem.text = this._state.projectType === ProjectType.TypeScript
|
||||
? localize('resolved.detail.noTsConfig', "No tsconfig")
|
||||
: localize('resolved.detail.noJsConfig', "No jsconfig");
|
||||
statusItem.severity = vscode.LanguageStatusSeverity.Information;
|
||||
if (isImplicitProjectConfigFile(this._state.configFile)) {
|
||||
statusItem.text = this._state.projectType === ProjectType.TypeScript
|
||||
? localize('resolved.detail.noTsConfig', "No tsconfig")
|
||||
: localize('resolved.detail.noJsConfig', "No jsconfig");
|
||||
|
||||
statusItem.detail = undefined;
|
||||
statusItem.command = {
|
||||
command: this.createConfigCommandId,
|
||||
title: this._state.projectType === ProjectType.TypeScript
|
||||
? localize('resolved.command.title.createTsconfig', "Create tsconfig")
|
||||
: localize('resolved.command.title.createJsconfig', "Create jsconfig"),
|
||||
arguments: [rootPath],
|
||||
};
|
||||
} else {
|
||||
statusItem.text = vscode.workspace.asRelativePath(this._state.configFile);
|
||||
statusItem.detail = undefined;
|
||||
statusItem.command = {
|
||||
command: this.openOpenConfigCommandId,
|
||||
title: localize('resolved.command.title.open', "Open config file"),
|
||||
arguments: [rootPath],
|
||||
};
|
||||
}
|
||||
statusItem.command = {
|
||||
command: this.createConfigCommandId,
|
||||
title: this._state.projectType === ProjectType.TypeScript
|
||||
? localize('resolved.command.title.createTsconfig', "Create tsconfig")
|
||||
: localize('resolved.command.title.createJsconfig', "Create jsconfig"),
|
||||
arguments: [rootPath],
|
||||
};
|
||||
} else {
|
||||
statusItem.text = vscode.workspace.asRelativePath(this._state.configFile);
|
||||
statusItem.command = {
|
||||
command: this.openOpenConfigCommandId,
|
||||
title: localize('resolved.command.title.open', "Open config file"),
|
||||
arguments: [rootPath],
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
case IntellisenseState.Type.SyntaxOnly:
|
||||
{
|
||||
const statusItem = this.ensureStatusItem();
|
||||
statusItem.severity = vscode.LanguageStatusSeverity.Warning;
|
||||
statusItem.text = localize('syntaxOnly.text', 'Partial Mode');
|
||||
statusItem.detail = localize('syntaxOnly.detail', 'Project Wide IntelliSense not available');
|
||||
statusItem.command = {
|
||||
title: localize('syntaxOnly.command.title.learnMore', "Learn More"),
|
||||
command: 'vscode.open',
|
||||
arguments: [
|
||||
vscode.Uri.parse('https://aka.ms/vscode/jsts/partial-mode'),
|
||||
]
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
case IntellisenseState.Type.SyntaxOnly: {
|
||||
const statusItem = this.ensureStatusItem();
|
||||
statusItem.severity = vscode.LanguageStatusSeverity.Warning;
|
||||
statusItem.text = localize('syntaxOnly.text', 'Partial Mode');
|
||||
statusItem.detail = localize('syntaxOnly.detail', 'Project Wide IntelliSense not available');
|
||||
statusItem.busy = false;
|
||||
statusItem.command = {
|
||||
title: localize('syntaxOnly.command.title.learnMore', "Learn More"),
|
||||
command: 'vscode.open',
|
||||
arguments: [
|
||||
vscode.Uri.parse('https://aka.ms/vscode/jsts/partial-mode'),
|
||||
]
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -371,6 +371,31 @@ suite('vscode API - window', () => {
|
|||
});
|
||||
|
||||
//#region Tabs API tests
|
||||
// eslint-disable-next-line code-no-test-only
|
||||
test.only('Tabs - move tab', async function () {
|
||||
const [docA, docB, docC] = await Promise.all([
|
||||
workspace.openTextDocument(await createRandomFile()),
|
||||
workspace.openTextDocument(await createRandomFile()),
|
||||
workspace.openTextDocument(await createRandomFile())
|
||||
]);
|
||||
|
||||
await window.showTextDocument(docA, { viewColumn: ViewColumn.One, preview: false });
|
||||
await window.showTextDocument(docB, { viewColumn: ViewColumn.One, preview: false });
|
||||
await window.showTextDocument(docC, { viewColumn: ViewColumn.Two, preview: false });
|
||||
|
||||
const tabGroups = window.tabGroups;
|
||||
assert.strictEqual(tabGroups.all.length, 2);
|
||||
|
||||
const group1Tabs = tabGroups.all[0].tabs;
|
||||
assert.strictEqual(group1Tabs.length, 2);
|
||||
|
||||
const group2Tabs = tabGroups.all[1].tabs;
|
||||
assert.strictEqual(group2Tabs.length, 1);
|
||||
|
||||
await group1Tabs[0].move(1, ViewColumn.One);
|
||||
console.log('Tab moved - Integration test');
|
||||
});
|
||||
/*
|
||||
test('Tabs - Ensure tabs getter is correct', async function () {
|
||||
// Reduce test timeout as this test should be quick, so even with 3 retries it will be under 60s.
|
||||
this.timeout(10000);
|
||||
|
@ -419,23 +444,29 @@ suite('vscode API - window', () => {
|
|||
workspace.openTextDocument(await createRandomFile()),
|
||||
]);
|
||||
|
||||
// Function to acquire the active tab within the active group
|
||||
const getActiveTabInActiveGroup = () => {
|
||||
const activeGroup = window.tabGroups.all.filter(group => group.isActive)[0];
|
||||
return activeGroup.activeTab;
|
||||
};
|
||||
|
||||
await window.showTextDocument(docA, { viewColumn: ViewColumn.One, preview: false });
|
||||
assert.ok(window.activeTab);
|
||||
assert.strictEqual(window.activeTab.resource?.toString(), docA.uri.toString());
|
||||
assert.ok(getActiveTabInActiveGroup());
|
||||
assert.strictEqual(getActiveTabInActiveGroup()?.resource?.toString(), docA.uri.toString());
|
||||
|
||||
await window.showTextDocument(docB, { viewColumn: ViewColumn.Two, preview: false });
|
||||
assert.ok(window.activeTab);
|
||||
assert.strictEqual(window.activeTab.resource?.toString(), docB.uri.toString());
|
||||
assert.ok(getActiveTabInActiveGroup());
|
||||
assert.strictEqual(getActiveTabInActiveGroup()?.resource?.toString(), docB.uri.toString());
|
||||
|
||||
await window.showTextDocument(docC, { viewColumn: ViewColumn.Three, preview: false });
|
||||
assert.ok(window.activeTab);
|
||||
assert.strictEqual(window.activeTab.resource?.toString(), docC.uri.toString());
|
||||
assert.ok(getActiveTabInActiveGroup());
|
||||
assert.strictEqual(getActiveTabInActiveGroup()?.resource?.toString(), docC.uri.toString());
|
||||
|
||||
await commands.executeCommand('workbench.action.closeActiveEditor');
|
||||
await commands.executeCommand('workbench.action.closeActiveEditor');
|
||||
await commands.executeCommand('workbench.action.closeActiveEditor');
|
||||
|
||||
assert.ok(!window.activeTab);
|
||||
assert.ok(!getActiveTabInActiveGroup());
|
||||
});
|
||||
|
||||
test('Tabs - Move Tab', async () => {
|
||||
|
@ -448,6 +479,9 @@ suite('vscode API - window', () => {
|
|||
await window.showTextDocument(docB, { viewColumn: ViewColumn.One, preview: false });
|
||||
await window.showTextDocument(docC, { viewColumn: ViewColumn.Two, preview: false });
|
||||
|
||||
const getAllTabs = () => {
|
||||
|
||||
};
|
||||
let tabs = window.tabs;
|
||||
assert.strictEqual(tabs.length, 3);
|
||||
|
||||
|
@ -512,7 +546,7 @@ suite('vscode API - window', () => {
|
|||
assert.strictEqual(tabs.length, 0);
|
||||
assert.ok(!window.activeTab);
|
||||
});
|
||||
|
||||
*/
|
||||
//#endregion
|
||||
|
||||
test('#7013 - input without options', function () {
|
||||
|
|
|
@ -289,11 +289,11 @@
|
|||
"c": "(",
|
||||
"t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.function.objc meta.return-type.objc punctuation.definition.type.begin.objc",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -311,22 +311,22 @@
|
|||
"c": ")",
|
||||
"t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.function.objc meta.return-type.objc punctuation.definition.type.end.objc",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.function.objc meta.return-type.objc",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -454,11 +454,11 @@
|
|||
"c": "(",
|
||||
"t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.function.objc meta.return-type.objc punctuation.definition.type.begin.objc",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -476,11 +476,11 @@
|
|||
"c": ")",
|
||||
"t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.function.objc meta.return-type.objc punctuation.definition.type.end.objc",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -2159,11 +2159,11 @@
|
|||
"c": "(",
|
||||
"t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.function.objc meta.return-type.objc punctuation.definition.type.begin.objc",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -2181,22 +2181,22 @@
|
|||
"c": ")",
|
||||
"t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.function.objc meta.return-type.objc punctuation.definition.type.end.objc",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.function.objc meta.return-type.objc",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -289,11 +289,11 @@
|
|||
"c": "(",
|
||||
"t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp punctuation.definition.type.begin.objcpp",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -311,22 +311,22 @@
|
|||
"c": ")",
|
||||
"t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp punctuation.definition.type.end.objcpp",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -454,11 +454,11 @@
|
|||
"c": "(",
|
||||
"t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp punctuation.definition.type.begin.objcpp",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -476,11 +476,11 @@
|
|||
"c": ")",
|
||||
"t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp punctuation.definition.type.end.objcpp",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -2093,11 +2093,11 @@
|
|||
"c": "(",
|
||||
"t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp punctuation.definition.type.begin.objcpp",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -2115,22 +2115,22 @@
|
|||
"c": ")",
|
||||
"t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp punctuation.definition.type.end.objcpp",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp",
|
||||
"r": {
|
||||
"dark_plus": "meta.return-type: #4EC9B0",
|
||||
"light_plus": "meta.return-type: #267F99",
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "meta.return-type: #4EC9B0"
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
11
package.json
11
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.65.0",
|
||||
"distro": "c3b60faa31dfdab1c61aabfd39185ea5e35ce017",
|
||||
"distro": "50d4ff495dbc12afb02dd4e536b49a8e937bb3d7",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
@ -61,7 +61,7 @@
|
|||
"dependencies": {
|
||||
"@microsoft/applicationinsights-web": "^2.6.4",
|
||||
"@parcel/watcher": "2.0.5",
|
||||
"@vscode/debugprotocol": "1.51.0",
|
||||
"@vscode/debugprotocol": "1.54.0-pre.1",
|
||||
"@vscode/iconv-lite-umd": "0.7.0",
|
||||
"@vscode/ripgrep": "^1.14.1",
|
||||
"@vscode/sqlite3": "5.0.7",
|
||||
|
@ -85,12 +85,12 @@
|
|||
"vscode-proxy-agent": "^0.12.0",
|
||||
"vscode-regexpp": "^3.1.0",
|
||||
"vscode-textmate": "6.0.0",
|
||||
"xterm": "4.18.0-beta.2",
|
||||
"xterm": "4.18.0-beta.4",
|
||||
"xterm-addon-search": "0.9.0-beta.10",
|
||||
"xterm-addon-serialize": "0.7.0-beta.8",
|
||||
"xterm-addon-unicode11": "0.4.0-beta.3",
|
||||
"xterm-addon-webgl": "0.12.0-beta.24",
|
||||
"xterm-headless": "4.18.0-beta.2",
|
||||
"xterm-headless": "4.18.0-beta.4",
|
||||
"yauzl": "^2.9.2",
|
||||
"yazl": "^2.4.3"
|
||||
},
|
||||
|
@ -113,6 +113,7 @@
|
|||
"@types/sinon": "^10.0.2",
|
||||
"@types/sinon-test": "^2.4.2",
|
||||
"@types/trusted-types": "^1.0.6",
|
||||
"@types/vscode-notebook-renderer": "^1.60.0",
|
||||
"@types/webpack": "^4.41.25",
|
||||
"@types/wicg-file-system-access": "^2020.9.5",
|
||||
"@types/windows-foreground-love": "^0.3.0",
|
||||
|
@ -201,7 +202,7 @@
|
|||
"style-loader": "^1.0.0",
|
||||
"ts-loader": "^9.2.3",
|
||||
"tsec": "0.1.4",
|
||||
"typescript": "^4.6.0-dev.20220110",
|
||||
"typescript": "^4.6.0-dev.20220209",
|
||||
"typescript-formatter": "7.1.0",
|
||||
"underscore": "^1.12.1",
|
||||
"util": "^0.12.4",
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
"builtInExtensions": [
|
||||
{
|
||||
"name": "ms-vscode.references-view",
|
||||
"version": "0.0.81",
|
||||
"version": "0.0.85",
|
||||
"repo": "https://github.com/microsoft/vscode-references-view",
|
||||
"metadata": {
|
||||
"id": "dc489f46-520d-4556-ae85-1f9eab3c412d",
|
||||
|
|
|
@ -24,12 +24,12 @@
|
|||
"vscode-proxy-agent": "^0.12.0",
|
||||
"vscode-regexpp": "^3.1.0",
|
||||
"vscode-textmate": "6.0.0",
|
||||
"xterm": "4.18.0-beta.2",
|
||||
"xterm": "4.18.0-beta.4",
|
||||
"xterm-addon-search": "0.9.0-beta.10",
|
||||
"xterm-addon-serialize": "0.7.0-beta.8",
|
||||
"xterm-addon-unicode11": "0.4.0-beta.3",
|
||||
"xterm-addon-webgl": "0.12.0-beta.24",
|
||||
"xterm-headless": "4.18.0-beta.2",
|
||||
"xterm-headless": "4.18.0-beta.4",
|
||||
"yauzl": "^2.9.2",
|
||||
"yazl": "^2.4.3"
|
||||
},
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
"tas-client-umd": "0.1.4",
|
||||
"vscode-oniguruma": "1.6.1",
|
||||
"vscode-textmate": "6.0.0",
|
||||
"xterm": "4.18.0-beta.2",
|
||||
"xterm": "4.18.0-beta.4",
|
||||
"xterm-addon-search": "0.9.0-beta.10",
|
||||
"xterm-addon-unicode11": "0.4.0-beta.3",
|
||||
"xterm-addon-webgl": "0.12.0-beta.24"
|
||||
|
|
|
@ -128,7 +128,7 @@ xterm-addon-webgl@0.12.0-beta.24:
|
|||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.24.tgz#5c17256933991856554c95c9bd1eaab42e9727a0"
|
||||
integrity sha512-+wZxKReEOlfN9JRHyikoffA6Do61/THR7QY35ajkQo0lLutKr6hTd/TLTuZh0PhFVelgTgudpXqlP++Lc0WFIA==
|
||||
|
||||
xterm@4.18.0-beta.2:
|
||||
version "4.18.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.18.0-beta.2.tgz#50e0de2ebe22fd1600e6d7fbc78136a843bb4c1d"
|
||||
integrity sha512-fzsSI0xL3HuWBuw5o3xPcR8/R8tTpSd60hHtf0Z2ZrIXDRdniJMw+StOv/xZNp2CUY6LcSiF9MmM0ylgiLHbdw==
|
||||
xterm@4.18.0-beta.4:
|
||||
version "4.18.0-beta.4"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.18.0-beta.4.tgz#547faa85d8388f6f344d9589dca951177e9ecb23"
|
||||
integrity sha512-+8uuHuyLTFKVIkRGELJnooR+VkEGBHL+04LXLqXtf8b52W2acUCFq2uL2gTGAIPKbrP14WDiF+dG1m7YctDKTQ==
|
||||
|
|
|
@ -928,15 +928,15 @@ xterm-addon-webgl@0.12.0-beta.24:
|
|||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.24.tgz#5c17256933991856554c95c9bd1eaab42e9727a0"
|
||||
integrity sha512-+wZxKReEOlfN9JRHyikoffA6Do61/THR7QY35ajkQo0lLutKr6hTd/TLTuZh0PhFVelgTgudpXqlP++Lc0WFIA==
|
||||
|
||||
xterm-headless@4.18.0-beta.2:
|
||||
version "4.18.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.18.0-beta.2.tgz#b930ac8a79626ef4443cd3ab1a86d1a96039fef8"
|
||||
integrity sha512-V4CSLfjqE1/pV+3HEs8l0T5W3FORoVX2f17AIUTSsuXUunNXcpib+g6ExxnR9Nm5ztbYnmZIX8AA3mClpqO1OQ==
|
||||
xterm-headless@4.18.0-beta.4:
|
||||
version "4.18.0-beta.4"
|
||||
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.18.0-beta.4.tgz#6083fdbcf6357756be0f21336cb99dae6fc81ed1"
|
||||
integrity sha512-0wOzTHltRBZxYjvAGTsFthNS08hEZBdxDPnFMRSP3RgPEwUB/6nlK2q4/RKyWkLYm4XPuKKk8lrSE43awm/7WA==
|
||||
|
||||
xterm@4.18.0-beta.2:
|
||||
version "4.18.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.18.0-beta.2.tgz#50e0de2ebe22fd1600e6d7fbc78136a843bb4c1d"
|
||||
integrity sha512-fzsSI0xL3HuWBuw5o3xPcR8/R8tTpSd60hHtf0Z2ZrIXDRdniJMw+StOv/xZNp2CUY6LcSiF9MmM0ylgiLHbdw==
|
||||
xterm@4.18.0-beta.4:
|
||||
version "4.18.0-beta.4"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.18.0-beta.4.tgz#547faa85d8388f6f344d9589dca951177e9ecb23"
|
||||
integrity sha512-+8uuHuyLTFKVIkRGELJnooR+VkEGBHL+04LXLqXtf8b52W2acUCFq2uL2gTGAIPKbrP14WDiF+dG1m7YctDKTQ==
|
||||
|
||||
yauzl@^2.9.2:
|
||||
version "2.10.0"
|
||||
|
|
|
@ -30,7 +30,7 @@ if [ "$(id -u)" = "0" ]; then
|
|||
esac
|
||||
done
|
||||
if [ -z $CAN_LAUNCH_AS_ROOT ]; then
|
||||
echo "You are trying to start @@PRODNAME@@ as a super user which isn't recommended. If this was intended, please specify an alternate user data directory using the \`--user-data-dir\` argument." 1>&2
|
||||
echo "You are trying to start @@PRODNAME@@ as a super user which isn't recommended. If this was intended, please add the argument \`--no-sandbox\` and specify an alternate user data directory using the \`--user-data-dir\` argument." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -11,6 +11,7 @@ export namespace Mimes {
|
|||
export const unknown = 'application/unknown';
|
||||
export const markdown = 'text/markdown';
|
||||
export const latex = 'text/latex';
|
||||
export const uriList = 'text/uri-list';
|
||||
}
|
||||
|
||||
interface MapExtToMediaMimes {
|
||||
|
|
|
@ -152,7 +152,7 @@ export interface IProductConfiguration {
|
|||
readonly darwinUniversalAssetId?: string;
|
||||
}
|
||||
|
||||
export type ImportantExtensionTip = { name: string; languages?: string[]; pattern?: string; isExtensionPack?: boolean };
|
||||
export type ImportantExtensionTip = { name: string; languages?: string[]; pattern?: string; isExtensionPack?: boolean; whenNotInstalled?: string[] };
|
||||
|
||||
export interface IAppCenterConfiguration {
|
||||
readonly 'win32-ia32': string;
|
||||
|
@ -165,14 +165,14 @@ export interface IConfigBasedExtensionTip {
|
|||
configPath: string;
|
||||
configName: string;
|
||||
configScheme?: string;
|
||||
recommendations: IStringDictionary<{ name: string; remotes?: string[]; important?: boolean; isExtensionPack?: boolean }>;
|
||||
recommendations: IStringDictionary<{ name: string; remotes?: string[]; important?: boolean; isExtensionPack?: boolean; whenNotInstalled?: string[] }>;
|
||||
}
|
||||
|
||||
export interface IExeBasedExtensionTip {
|
||||
friendlyName: string;
|
||||
windowsPath?: string;
|
||||
important?: boolean;
|
||||
recommendations: IStringDictionary<{ name: string; important?: boolean; isExtensionPack?: boolean }>;
|
||||
recommendations: IStringDictionary<{ name: string; important?: boolean; isExtensionPack?: boolean; whenNotInstalled?: string[] }>;
|
||||
}
|
||||
|
||||
export interface IRemoteExtensionTip {
|
||||
|
|
|
@ -479,7 +479,6 @@ function ensureWriteOptions(options?: IWriteFileOptions): IEnsuredWriteFileOptio
|
|||
|
||||
/**
|
||||
* A drop-in replacement for `fs.rename` that:
|
||||
* - updates the `mtime` of the `source` after the operation
|
||||
* - allows to move across multiple disks
|
||||
*/
|
||||
async function move(source: string, target: string): Promise<void> {
|
||||
|
@ -487,30 +486,8 @@ async function move(source: string, target: string): Promise<void> {
|
|||
return; // simulate node.js behaviour here and do a no-op if paths match
|
||||
}
|
||||
|
||||
// We have been updating `mtime` for move operations for files since the
|
||||
// beginning for reasons that are no longer quite clear, but changing
|
||||
// this could be risky as well. As such, trying to reason about it:
|
||||
// It is very common as developer to have file watchers enabled that watch
|
||||
// the current workspace for changes. Updating the `mtime` might make it
|
||||
// easier for these watchers to recognize an actual change. Since changing
|
||||
// a source code file also updates the `mtime`, moving a file should do so
|
||||
// as well because conceptually it is a change of a similar category.
|
||||
async function updateMtime(path: string): Promise<void> {
|
||||
try {
|
||||
const stat = await Promises.lstat(path);
|
||||
if (stat.isDirectory() || stat.isSymbolicLink()) {
|
||||
return; // only for files
|
||||
}
|
||||
|
||||
await Promises.utimes(path, stat.atime, new Date());
|
||||
} catch (error) {
|
||||
// Ignore any error
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await Promises.rename(source, target);
|
||||
await updateMtime(target);
|
||||
} catch (error) {
|
||||
|
||||
// In two cases we fallback to classic copy and delete:
|
||||
|
@ -524,7 +501,6 @@ async function move(source: string, target: string): Promise<void> {
|
|||
if (source.toLowerCase() !== target.toLowerCase() && error.code === 'EXDEV' || source.endsWith('.')) {
|
||||
await copy(source, target, { preserveSymlinks: false /* copying to another device */ });
|
||||
await rimraf(source, RimRafMode.MOVE);
|
||||
await updateMtime(target);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
|
|
|
@ -801,8 +801,11 @@ export class CodeApplication extends Disposable {
|
|||
const app = this;
|
||||
const environmentService = this.environmentMainService;
|
||||
const productService = this.productService;
|
||||
const logService = this.logService;
|
||||
urlService.registerHandler({
|
||||
async handleURL(uri: URI, options?: IOpenURLOptions): Promise<boolean> {
|
||||
logService.trace('app#handleURL: ', uri.toString(true), options);
|
||||
|
||||
if (uri.scheme === productService.urlProtocol && uri.path === 'workspace') {
|
||||
uri = uri.with({
|
||||
authority: 'file',
|
||||
|
@ -818,6 +821,7 @@ export class CodeApplication extends Disposable {
|
|||
|
||||
// Check for URIs to open in window
|
||||
const windowOpenableFromProtocolLink = app.getWindowOpenableFromProtocolLink(uri);
|
||||
logService.trace('app#handleURL: windowOpenableFromProtocolLink = ', windowOpenableFromProtocolLink);
|
||||
if (windowOpenableFromProtocolLink) {
|
||||
const [window] = windowsMainService.open({
|
||||
context: OpenContext.API,
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ITextModel, shouldSynchronizeModel } from 'vs/editor/common/model';
|
||||
import { LanguageFilter, LanguageSelector, score } from 'vs/editor/common/languageSelector';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
interface Entry<T> {
|
||||
selector: LanguageSelector;
|
||||
|
@ -25,15 +26,19 @@ function isExclusive(selector: LanguageSelector): boolean {
|
|||
}
|
||||
}
|
||||
|
||||
export interface NotebooTypeResolver {
|
||||
(uri: URI): string | undefined;
|
||||
}
|
||||
|
||||
export class LanguageFeatureRegistry<T> {
|
||||
|
||||
private _clock: number = 0;
|
||||
private readonly _entries: Entry<T>[] = [];
|
||||
private readonly _onDidChange = new Emitter<number>();
|
||||
|
||||
get onDidChange(): Event<number> {
|
||||
return this._onDidChange.event;
|
||||
}
|
||||
private readonly _onDidChange = new Emitter<number>();
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
constructor(private readonly _notebookTypeResolver?: NotebooTypeResolver) { }
|
||||
|
||||
register(selector: LanguageSelector, provider: T): IDisposable {
|
||||
|
||||
|
@ -122,18 +127,23 @@ export class LanguageFeatureRegistry<T> {
|
|||
}
|
||||
}
|
||||
|
||||
private _lastCandidate: { uri: string; language: string } | undefined;
|
||||
private _lastCandidate: { uri: string; language: string; notebookType?: string } | undefined;
|
||||
|
||||
private _updateScores(model: ITextModel): void {
|
||||
|
||||
const notebookType = this._notebookTypeResolver?.(model.uri);
|
||||
|
||||
const candidate = {
|
||||
uri: model.uri.toString(),
|
||||
language: model.getLanguageId()
|
||||
language: model.getLanguageId(),
|
||||
notebookType
|
||||
};
|
||||
|
||||
if (this._lastCandidate
|
||||
&& this._lastCandidate.language === candidate.language
|
||||
&& this._lastCandidate.uri === candidate.uri) {
|
||||
&& this._lastCandidate.uri === candidate.uri
|
||||
&& this._lastCandidate.notebookType === candidate.notebookType
|
||||
) {
|
||||
|
||||
// nothing has changed
|
||||
return;
|
||||
|
@ -142,7 +152,7 @@ export class LanguageFeatureRegistry<T> {
|
|||
this._lastCandidate = candidate;
|
||||
|
||||
for (let entry of this._entries) {
|
||||
entry._score = score(entry.selector, model.uri, model.getLanguageId(), shouldSynchronizeModel(model));
|
||||
entry._score = score(entry.selector, model.uri, model.getLanguageId(), shouldSynchronizeModel(model), notebookType);
|
||||
|
||||
if (isExclusive(entry.selector) && entry._score > 0) {
|
||||
// support for one exclusive selector that overwrites
|
||||
|
|
|
@ -11,6 +11,7 @@ export interface LanguageFilter {
|
|||
readonly language?: string;
|
||||
readonly scheme?: string;
|
||||
readonly pattern?: string | IRelativePattern;
|
||||
readonly notebookType?: string;
|
||||
/**
|
||||
* This provider is implemented in the UI thread.
|
||||
*/
|
||||
|
@ -20,13 +21,13 @@ export interface LanguageFilter {
|
|||
|
||||
export type LanguageSelector = string | LanguageFilter | ReadonlyArray<string | LanguageFilter>;
|
||||
|
||||
export function score(selector: LanguageSelector | undefined, candidateUri: URI, candidateLanguage: string, candidateIsSynchronized: boolean): number {
|
||||
export function score(selector: LanguageSelector | undefined, candidateUri: URI, candidateLanguage: string, candidateIsSynchronized: boolean, candidateNotebookType: string | undefined): number {
|
||||
|
||||
if (Array.isArray(selector)) {
|
||||
// array -> take max individual value
|
||||
let ret = 0;
|
||||
for (const filter of selector) {
|
||||
const value = score(filter, candidateUri, candidateLanguage, candidateIsSynchronized);
|
||||
const value = score(filter, candidateUri, candidateLanguage, candidateIsSynchronized, candidateNotebookType);
|
||||
if (value === 10) {
|
||||
return value; // already at the highest
|
||||
}
|
||||
|
@ -55,7 +56,7 @@ export function score(selector: LanguageSelector | undefined, candidateUri: URI,
|
|||
|
||||
} else if (selector) {
|
||||
// filter -> select accordingly, use defaults for scheme
|
||||
const { language, pattern, scheme, hasAccessToAllModels } = selector as LanguageFilter; // TODO: microsoft/TypeScript#42768
|
||||
const { language, pattern, scheme, hasAccessToAllModels, notebookType } = selector as LanguageFilter; // TODO: microsoft/TypeScript#42768
|
||||
|
||||
if (!candidateIsSynchronized && !hasAccessToAllModels) {
|
||||
return 0;
|
||||
|
@ -83,6 +84,16 @@ export function score(selector: LanguageSelector | undefined, candidateUri: URI,
|
|||
}
|
||||
}
|
||||
|
||||
if (notebookType) {
|
||||
if (notebookType === candidateNotebookType) {
|
||||
ret = 10;
|
||||
} else if (notebookType === '*') {
|
||||
ret = Math.max(ret, 5);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pattern) {
|
||||
let normalizedPattern: string | IRelativePattern;
|
||||
if (typeof pattern === 'string') {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry';
|
||||
import { LanguageFeatureRegistry, NotebooTypeResolver } from 'vs/editor/common/languageFeatureRegistry';
|
||||
import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
|
@ -13,8 +13,6 @@ export interface ILanguageFeaturesService {
|
|||
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
// --- navigation
|
||||
|
||||
readonly referenceProvider: LanguageFeatureRegistry<ReferenceProvider>;
|
||||
|
||||
readonly definitionProvider: LanguageFeatureRegistry<DefinitionProvider>;
|
||||
|
@ -25,8 +23,6 @@ export interface ILanguageFeaturesService {
|
|||
|
||||
readonly implementationProvider: LanguageFeatureRegistry<ImplementationProvider>;
|
||||
|
||||
// --- code actions
|
||||
|
||||
readonly codeActionProvider: LanguageFeatureRegistry<CodeActionProvider>;
|
||||
|
||||
readonly renameProvider: LanguageFeatureRegistry<RenameProvider>;
|
||||
|
@ -37,8 +33,6 @@ export interface ILanguageFeaturesService {
|
|||
|
||||
readonly onTypeFormattingEditProvider: LanguageFeatureRegistry<OnTypeFormattingEditProvider>;
|
||||
|
||||
// --- insights
|
||||
|
||||
readonly documentSymbolProvider: LanguageFeatureRegistry<DocumentSymbolProvider>;
|
||||
|
||||
readonly inlayHintsProvider: LanguageFeatureRegistry<InlayHintsProvider>;
|
||||
|
@ -53,8 +47,6 @@ export interface ILanguageFeaturesService {
|
|||
|
||||
readonly documentHighlightProvider: LanguageFeatureRegistry<DocumentHighlightProvider>;
|
||||
|
||||
// ---
|
||||
|
||||
readonly documentRangeSemanticTokensProvider: LanguageFeatureRegistry<DocumentRangeSemanticTokensProvider>;
|
||||
|
||||
readonly documentSemanticTokensProvider: LanguageFeatureRegistry<DocumentSemanticTokensProvider>;
|
||||
|
@ -65,17 +57,17 @@ export interface ILanguageFeaturesService {
|
|||
|
||||
readonly linkProvider: LanguageFeatureRegistry<LinkProvider>;
|
||||
|
||||
// --- completions
|
||||
|
||||
readonly inlineCompletionsProvider: LanguageFeatureRegistry<InlineCompletionsProvider>;
|
||||
|
||||
readonly completionProvider: LanguageFeatureRegistry<CompletionItemProvider>;
|
||||
|
||||
readonly linkedEditingRangeProvider: LanguageFeatureRegistry<LinkedEditingRangeProvider>;
|
||||
|
||||
// --- debug
|
||||
|
||||
readonly inlineValuesProvider: LanguageFeatureRegistry<InlineValuesProvider>;
|
||||
|
||||
readonly evaluatableExpressionProvider: LanguageFeatureRegistry<EvaluatableExpressionProvider>;
|
||||
|
||||
// --
|
||||
|
||||
setNotebookTypeResolver(resolver: NotebooTypeResolver | undefined): void;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { LanguageFeatureRegistry, NotebooTypeResolver } from 'vs/editor/common/languageFeatureRegistry';
|
||||
import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages';
|
||||
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
@ -12,59 +13,45 @@ export class LanguageFeaturesService implements ILanguageFeaturesService {
|
|||
|
||||
declare _serviceBrand: undefined;
|
||||
|
||||
readonly referenceProvider = new LanguageFeatureRegistry<ReferenceProvider>();
|
||||
readonly referenceProvider = new LanguageFeatureRegistry<ReferenceProvider>(this._score.bind(this));
|
||||
readonly renameProvider = new LanguageFeatureRegistry<RenameProvider>(this._score.bind(this));
|
||||
readonly codeActionProvider = new LanguageFeatureRegistry<CodeActionProvider>(this._score.bind(this));
|
||||
readonly definitionProvider = new LanguageFeatureRegistry<DefinitionProvider>(this._score.bind(this));
|
||||
readonly typeDefinitionProvider = new LanguageFeatureRegistry<TypeDefinitionProvider>(this._score.bind(this));
|
||||
readonly declarationProvider = new LanguageFeatureRegistry<DeclarationProvider>(this._score.bind(this));
|
||||
readonly implementationProvider = new LanguageFeatureRegistry<ImplementationProvider>(this._score.bind(this));
|
||||
readonly documentSymbolProvider = new LanguageFeatureRegistry<DocumentSymbolProvider>(this._score.bind(this));
|
||||
readonly inlayHintsProvider = new LanguageFeatureRegistry<InlayHintsProvider>(this._score.bind(this));
|
||||
readonly colorProvider = new LanguageFeatureRegistry<DocumentColorProvider>(this._score.bind(this));
|
||||
readonly codeLensProvider = new LanguageFeatureRegistry<CodeLensProvider>(this._score.bind(this));
|
||||
readonly documentFormattingEditProvider = new LanguageFeatureRegistry<DocumentFormattingEditProvider>(this._score.bind(this));
|
||||
readonly documentRangeFormattingEditProvider = new LanguageFeatureRegistry<DocumentRangeFormattingEditProvider>(this._score.bind(this));
|
||||
readonly onTypeFormattingEditProvider = new LanguageFeatureRegistry<OnTypeFormattingEditProvider>(this._score.bind(this));
|
||||
readonly signatureHelpProvider = new LanguageFeatureRegistry<SignatureHelpProvider>(this._score.bind(this));
|
||||
readonly hoverProvider = new LanguageFeatureRegistry<HoverProvider>(this._score.bind(this));
|
||||
readonly documentHighlightProvider = new LanguageFeatureRegistry<DocumentHighlightProvider>(this._score.bind(this));
|
||||
readonly selectionRangeProvider = new LanguageFeatureRegistry<SelectionRangeProvider>(this._score.bind(this));
|
||||
readonly foldingRangeProvider = new LanguageFeatureRegistry<FoldingRangeProvider>(this._score.bind(this));
|
||||
readonly linkProvider = new LanguageFeatureRegistry<LinkProvider>(this._score.bind(this));
|
||||
readonly inlineCompletionsProvider = new LanguageFeatureRegistry<InlineCompletionsProvider>(this._score.bind(this));
|
||||
readonly completionProvider = new LanguageFeatureRegistry<CompletionItemProvider>(this._score.bind(this));
|
||||
readonly linkedEditingRangeProvider = new LanguageFeatureRegistry<LinkedEditingRangeProvider>(this._score.bind(this));
|
||||
readonly inlineValuesProvider = new LanguageFeatureRegistry<InlineValuesProvider>(this._score.bind(this));
|
||||
readonly evaluatableExpressionProvider = new LanguageFeatureRegistry<EvaluatableExpressionProvider>(this._score.bind(this));
|
||||
readonly documentRangeSemanticTokensProvider = new LanguageFeatureRegistry<DocumentRangeSemanticTokensProvider>(this._score.bind(this));
|
||||
readonly documentSemanticTokensProvider = new LanguageFeatureRegistry<DocumentSemanticTokensProvider>(this._score.bind(this));
|
||||
|
||||
readonly renameProvider = new LanguageFeatureRegistry<RenameProvider>();
|
||||
|
||||
readonly codeActionProvider = new LanguageFeatureRegistry<CodeActionProvider>();
|
||||
private _notebookTypeResolver?: NotebooTypeResolver;
|
||||
|
||||
readonly definitionProvider = new LanguageFeatureRegistry<DefinitionProvider>();
|
||||
setNotebookTypeResolver(resolver: NotebooTypeResolver | undefined) {
|
||||
this._notebookTypeResolver = resolver;
|
||||
}
|
||||
|
||||
readonly typeDefinitionProvider = new LanguageFeatureRegistry<TypeDefinitionProvider>();
|
||||
private _score(uri: URI): string | undefined {
|
||||
return this._notebookTypeResolver?.(uri);
|
||||
}
|
||||
|
||||
readonly declarationProvider = new LanguageFeatureRegistry<DeclarationProvider>();
|
||||
|
||||
readonly implementationProvider = new LanguageFeatureRegistry<ImplementationProvider>();
|
||||
|
||||
readonly documentSymbolProvider = new LanguageFeatureRegistry<DocumentSymbolProvider>();
|
||||
|
||||
readonly inlayHintsProvider = new LanguageFeatureRegistry<InlayHintsProvider>();
|
||||
|
||||
readonly colorProvider = new LanguageFeatureRegistry<DocumentColorProvider>();
|
||||
|
||||
readonly codeLensProvider = new LanguageFeatureRegistry<CodeLensProvider>();
|
||||
|
||||
readonly documentFormattingEditProvider = new LanguageFeatureRegistry<DocumentFormattingEditProvider>();
|
||||
|
||||
readonly documentRangeFormattingEditProvider = new LanguageFeatureRegistry<DocumentRangeFormattingEditProvider>();
|
||||
|
||||
readonly onTypeFormattingEditProvider = new LanguageFeatureRegistry<OnTypeFormattingEditProvider>();
|
||||
|
||||
readonly signatureHelpProvider = new LanguageFeatureRegistry<SignatureHelpProvider>();
|
||||
|
||||
readonly hoverProvider = new LanguageFeatureRegistry<HoverProvider>();
|
||||
|
||||
readonly documentHighlightProvider = new LanguageFeatureRegistry<DocumentHighlightProvider>();
|
||||
|
||||
readonly selectionRangeProvider = new LanguageFeatureRegistry<SelectionRangeProvider>();
|
||||
|
||||
readonly foldingRangeProvider = new LanguageFeatureRegistry<FoldingRangeProvider>();
|
||||
|
||||
readonly linkProvider = new LanguageFeatureRegistry<LinkProvider>();
|
||||
|
||||
readonly inlineCompletionsProvider = new LanguageFeatureRegistry<InlineCompletionsProvider>();
|
||||
|
||||
readonly completionProvider = new LanguageFeatureRegistry<CompletionItemProvider>();
|
||||
|
||||
readonly linkedEditingRangeProvider = new LanguageFeatureRegistry<LinkedEditingRangeProvider>();
|
||||
|
||||
readonly inlineValuesProvider = new LanguageFeatureRegistry<InlineValuesProvider>();
|
||||
|
||||
readonly evaluatableExpressionProvider = new LanguageFeatureRegistry<EvaluatableExpressionProvider>();
|
||||
|
||||
readonly documentRangeSemanticTokensProvider = new LanguageFeatureRegistry<DocumentRangeSemanticTokensProvider>();
|
||||
|
||||
readonly documentSemanticTokensProvider = new LanguageFeatureRegistry<DocumentSemanticTokensProvider>();
|
||||
}
|
||||
|
||||
registerSingleton(ILanguageFeaturesService, LanguageFeaturesService, true);
|
||||
|
|
|
@ -32,7 +32,7 @@ import * as nls from 'vs/nls';
|
|||
import { ISubmenuItem, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
|
||||
import { TextEditorSelectionRevealType, TextEditorSelectionSource } from 'vs/platform/editor/common/editor';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
@ -202,7 +202,8 @@ export abstract class SymbolNavigationAction extends EditorAction {
|
|||
resource: reference.uri,
|
||||
options: {
|
||||
selection: Range.collapseToStart(range),
|
||||
selectionRevealType: TextEditorSelectionRevealType.NearTopIfOutsideViewport
|
||||
selectionRevealType: TextEditorSelectionRevealType.NearTopIfOutsideViewport,
|
||||
selectionSource: TextEditorSelectionSource.JUMP
|
||||
}
|
||||
}, editor, sideBySide);
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import * as nls from 'vs/nls';
|
|||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { TextEditorSelectionSource } from 'vs/platform/editor/common/editor';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IListService, WorkbenchListFocusContextKey, WorkbenchTreeElementCanCollapse, WorkbenchTreeElementCanExpand } from 'vs/platform/list/browser/listService';
|
||||
|
@ -246,7 +247,7 @@ export abstract class ReferencesController implements IEditorContribution {
|
|||
|
||||
return this._editorService.openCodeEditor({
|
||||
resource: ref.uri,
|
||||
options: { selection: range }
|
||||
options: { selection: range, selectionSource: TextEditorSelectionSource.JUMP }
|
||||
}, this._editor).then(openedEditor => {
|
||||
this._ignoreModelChangeEvent = false;
|
||||
|
||||
|
@ -292,7 +293,7 @@ export abstract class ReferencesController implements IEditorContribution {
|
|||
const { uri, range } = ref;
|
||||
this._editorService.openCodeEditor({
|
||||
resource: uri,
|
||||
options: { selection: range, pinned }
|
||||
options: { selection: range, selectionSource: TextEditorSelectionSource.JUMP, pinned }
|
||||
}, this._editor, sideBySide);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Command } from 'vs/editor/common/languages';
|
||||
import { InlayHintItem } from 'vs/editor/contrib/inlayHints/browser/inlayHints';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Link } from 'vs/platform/opener/browser/link';
|
||||
|
||||
|
||||
export class InlayHintsAccessibility {
|
||||
|
||||
static readonly IsReading = new RawContextKey<boolean>('isReadingLineWithInlayHints', false, { type: 'boolean', description: localize('isReadingLineWithInlayHints', "Whether the current line and its inlay hints are currently focused") });
|
||||
|
||||
private readonly _ariaElement: HTMLSpanElement;
|
||||
private readonly _ctxIsReading: IContextKey<boolean>;
|
||||
|
||||
|
||||
private _sessionDispoosables = new DisposableStore();
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IInstantiationService private readonly _instaService: IInstantiationService,
|
||||
) {
|
||||
this._ariaElement = document.createElement('span');
|
||||
this._ariaElement.style.position = 'fixed';
|
||||
this._ariaElement.className = 'inlayhint-accessibility-element';
|
||||
this._ariaElement.tabIndex = 0;
|
||||
this._ariaElement.setAttribute('aria-description', localize('description', "Code with Inlay Hint Information"));
|
||||
|
||||
this._ctxIsReading = InlayHintsAccessibility.IsReading.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._sessionDispoosables.dispose();
|
||||
this._ctxIsReading.reset();
|
||||
this._ariaElement.remove();
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
dom.clearNode(this._ariaElement);
|
||||
this._sessionDispoosables.clear();
|
||||
this._ctxIsReading.reset();
|
||||
}
|
||||
|
||||
async read(line: number, hints: InlayHintItem[]) {
|
||||
|
||||
this._sessionDispoosables.clear();
|
||||
|
||||
if (!this._ariaElement.isConnected) {
|
||||
this._editor.getDomNode()?.appendChild(this._ariaElement);
|
||||
}
|
||||
|
||||
if (!this._editor.hasModel() || !this._ariaElement.isConnected) {
|
||||
this._ctxIsReading.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const cts = new CancellationTokenSource();
|
||||
this._sessionDispoosables.add(cts);
|
||||
|
||||
for (let hint of hints) {
|
||||
await hint.resolve(cts.token);
|
||||
}
|
||||
|
||||
if (cts.token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
const model = this._editor.getModel();
|
||||
// const text = this._editor.getModel().getLineContent(line);
|
||||
const newChildren: (string | HTMLElement)[] = [];
|
||||
|
||||
let start = 0;
|
||||
for (const item of hints) {
|
||||
|
||||
// text
|
||||
const part = model.getValueInRange({ startLineNumber: line, startColumn: start + 1, endLineNumber: line, endColumn: item.hint.position.column });
|
||||
if (part.length > 0) {
|
||||
newChildren.push(part);
|
||||
start = item.hint.position.column - 1;
|
||||
}
|
||||
|
||||
// hint
|
||||
const em = document.createElement('em');
|
||||
const { label } = item.hint;
|
||||
if (typeof label === 'string') {
|
||||
em.innerText = label;
|
||||
} else {
|
||||
for (let part of label) {
|
||||
if (part.command) {
|
||||
const link = this._instaService.createInstance(Link, em,
|
||||
{ href: InlayHintsAccessibility._asCommandLink(part.command), label: part.label, title: part.command.title },
|
||||
undefined
|
||||
);
|
||||
this._sessionDispoosables.add(link);
|
||||
|
||||
} else {
|
||||
em.innerText += part.label;
|
||||
}
|
||||
}
|
||||
}
|
||||
newChildren.push(em);
|
||||
}
|
||||
|
||||
// trailing text
|
||||
newChildren.push(model.getValueInRange({ startLineNumber: line, startColumn: start + 1, endLineNumber: line, endColumn: Number.MAX_SAFE_INTEGER }));
|
||||
dom.reset(this._ariaElement, ...newChildren);
|
||||
|
||||
this._ariaElement.focus();
|
||||
this._ctxIsReading.set(true);
|
||||
|
||||
// reset on blur
|
||||
this._sessionDispoosables.add(dom.addDisposableListener(this._ariaElement, 'focusout', () => {
|
||||
this.reset();
|
||||
}));
|
||||
}
|
||||
|
||||
private static _asCommandLink(command: Command): string {
|
||||
return URI.from({
|
||||
scheme: Schemas.command,
|
||||
path: command.id,
|
||||
query: encodeURIComponent(JSON.stringify(command.arguments))
|
||||
}).toString();
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { IRange } from 'vs/base/common/range';
|
||||
|
@ -13,9 +14,11 @@ import { assertType } from 'vs/base/common/types';
|
|||
import { URI } from 'vs/base/common/uri';
|
||||
import { IActiveCodeEditor, ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
|
||||
import { ClassNameReference, CssProperties, DynamicCssRules } from 'vs/editor/browser/editorDom';
|
||||
import { EditorAction2 } from 'vs/editor/browser/editorExtensions';
|
||||
import { EditorOption, EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import * as languages from 'vs/editor/common/languages';
|
||||
import { IModelDeltaDecoration, InjectedTextCursorStops, ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import { ModelDecorationInjectedTextOptions } from 'vs/editor/common/model/textModel';
|
||||
|
@ -24,10 +27,14 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat
|
|||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { ClickLinkGesture, ClickLinkMouseEvent } from 'vs/editor/contrib/gotoSymbol/browser/link/clickLinkGesture';
|
||||
import { InlayHintAnchor, InlayHintItem, InlayHintsFragments } from 'vs/editor/contrib/inlayHints/browser/inlayHints';
|
||||
import { InlayHintsAccessibility } from 'vs/editor/contrib/inlayHints/browser/inlayHintsAccessibility';
|
||||
import { goToDefinitionWithLocation, showGoToContextMenu } from 'vs/editor/contrib/inlayHints/browser/inlayHintsLocations';
|
||||
import { localize } from 'vs/nls';
|
||||
import { registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { createDecorator, IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
|
||||
|
@ -82,8 +89,8 @@ export class InlayHintsController implements IEditorContribution {
|
|||
|
||||
private static readonly _MAX_DECORATORS = 1500;
|
||||
|
||||
static get(editor: ICodeEditor) {
|
||||
return editor.getContribution(InlayHintsController.ID) ?? undefined;
|
||||
static get(editor: ICodeEditor): InlayHintsController | undefined {
|
||||
return editor.getContribution<InlayHintsController>(InlayHintsController.ID) ?? undefined;
|
||||
}
|
||||
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
@ -91,6 +98,7 @@ export class InlayHintsController implements IEditorContribution {
|
|||
private readonly _debounceInfo: IFeatureDebounceInformation;
|
||||
private readonly _decorationsMetadata = new Map<string, { item: InlayHintItem; classNameRef: IDisposable }>();
|
||||
private readonly _ruleFactory = new DynamicCssRules(this._editor);
|
||||
private readonly _accessibility: InlayHintsAccessibility;
|
||||
|
||||
private _activeInlayHintPart?: RenderedInlayHintLabelPart;
|
||||
|
||||
|
@ -103,6 +111,7 @@ export class InlayHintsController implements IEditorContribution {
|
|||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@IInstantiationService private readonly _instaService: IInstantiationService,
|
||||
) {
|
||||
this._accessibility = _instaService.createInstance(InlayHintsAccessibility, _editor);
|
||||
this._debounceInfo = _featureDebounce.for(_languageFeaturesService.inlayHintsProvider, 'InlayHint', { min: 25 });
|
||||
this._disposables.add(_languageFeaturesService.inlayHintsProvider.onDidChange(() => this._update()));
|
||||
this._disposables.add(_editor.onDidChangeModel(() => this._update()));
|
||||
|
@ -523,9 +532,35 @@ export class InlayHintsController implements IEditorContribution {
|
|||
}
|
||||
this._decorationsMetadata.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- accessibility
|
||||
|
||||
startInlayHintsReading(): void {
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const line = this._editor.getPosition().lineNumber;
|
||||
const set = new Set<languages.InlayHint>();
|
||||
const items: InlayHintItem[] = [];
|
||||
for (let deco of this._editor.getLineDecorations(line)) {
|
||||
const data = this._decorationsMetadata.get(deco.id);
|
||||
if (data && !set.has(data.item.hint)) {
|
||||
set.add(data.item.hint);
|
||||
items.push(data.item);
|
||||
}
|
||||
}
|
||||
if (set.size > 0) {
|
||||
this._accessibility.read(line, items);
|
||||
}
|
||||
}
|
||||
|
||||
stopInlayHintsReading(): void {
|
||||
this._accessibility.reset();
|
||||
this._editor.focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Prevents the view from potentially visible whitespace
|
||||
function fixSpace(str: string): string {
|
||||
|
@ -533,6 +568,49 @@ function fixSpace(str: string): string {
|
|||
return str.replace(/[ \t]/g, noBreakWhitespace);
|
||||
}
|
||||
|
||||
registerAction2(class StartReadHints extends EditorAction2 {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'inlayHints.startReadingLineWithHint',
|
||||
title: localize('read.title', 'Read Line With Inline Hints'),
|
||||
precondition: EditorContextKeys.hasInlayHintsProvider,
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
|
||||
runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor) {
|
||||
const ctrl = InlayHintsController.get(editor);
|
||||
if (ctrl) {
|
||||
ctrl.startInlayHintsReading();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class StopReadHints extends EditorAction2 {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'inlayHints.stopReadingLineWithHint',
|
||||
title: localize('stop.title', 'Stop Inlay Hints Reading'),
|
||||
precondition: InlayHintsAccessibility.IsReading,
|
||||
f1: true,
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.EditorContrib,
|
||||
primary: KeyCode.Escape
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor) {
|
||||
const ctrl = InlayHintsController.get(editor);
|
||||
if (ctrl) {
|
||||
ctrl.stopInlayHintsReading();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
CommandsRegistry.registerCommand('_executeInlayHintProvider', async (accessor, ...args: [URI, IRange]): Promise<languages.InlayHint[]> => {
|
||||
|
||||
const [uri, range] = args;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { Disposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { firstNonWhitespaceIndex } from 'vs/base/common/strings';
|
||||
|
@ -37,6 +38,9 @@ export class GhostTextController extends Disposable {
|
|||
return this.activeController.value?.model;
|
||||
}
|
||||
|
||||
private readonly activeModelDidChangeEmitter = this._register(new Emitter<void>());
|
||||
public readonly onActiveModelDidChange = this.activeModelDidChangeEmitter.event;
|
||||
|
||||
constructor(
|
||||
public readonly editor: ICodeEditor,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
|
@ -71,6 +75,7 @@ export class GhostTextController extends Disposable {
|
|||
this.editor
|
||||
)
|
||||
: undefined;
|
||||
this.activeModelDidChangeEmitter.fire();
|
||||
}
|
||||
|
||||
public shouldShowHoverAt(hoverRange: Range): boolean {
|
||||
|
|
|
@ -137,7 +137,7 @@
|
|||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.focused) .monaco-icon-label {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.focused)>.contents>.main .monaco-icon-label {
|
||||
color: var(--vscode-editorSuggestWidget-foreground);
|
||||
}
|
||||
|
||||
|
@ -145,11 +145,11 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-highlighted-label .highlight {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main .monaco-highlighted-label .highlight {
|
||||
color: var(--vscode-editorSuggestWidget-highlightForeground);
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused .monaco-highlighted-label .highlight {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused>.contents>.main .monaco-highlighted-label .highlight {
|
||||
color: var(--vscode-editorSuggestWidget-focusHighlightForeground);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,18 +15,18 @@ suite('LanguageSelector', function () {
|
|||
};
|
||||
|
||||
test('score, invalid selector', function () {
|
||||
assert.strictEqual(score({}, model.uri, model.language, true), 0);
|
||||
assert.strictEqual(score(undefined!, model.uri, model.language, true), 0);
|
||||
assert.strictEqual(score(null!, model.uri, model.language, true), 0);
|
||||
assert.strictEqual(score('', model.uri, model.language, true), 0);
|
||||
assert.strictEqual(score({}, model.uri, model.language, true, undefined), 0);
|
||||
assert.strictEqual(score(undefined!, model.uri, model.language, true, undefined), 0);
|
||||
assert.strictEqual(score(null!, model.uri, model.language, true, undefined), 0);
|
||||
assert.strictEqual(score('', model.uri, model.language, true, undefined), 0);
|
||||
});
|
||||
|
||||
test('score, any language', function () {
|
||||
assert.strictEqual(score({ language: '*' }, model.uri, model.language, true), 5);
|
||||
assert.strictEqual(score('*', model.uri, model.language, true), 5);
|
||||
assert.strictEqual(score({ language: '*' }, model.uri, model.language, true, undefined), 5);
|
||||
assert.strictEqual(score('*', model.uri, model.language, true, undefined), 5);
|
||||
|
||||
assert.strictEqual(score('*', URI.parse('foo:bar'), model.language, true), 5);
|
||||
assert.strictEqual(score('farboo', URI.parse('foo:bar'), model.language, true), 10);
|
||||
assert.strictEqual(score('*', URI.parse('foo:bar'), model.language, true, undefined), 5);
|
||||
assert.strictEqual(score('farboo', URI.parse('foo:bar'), model.language, true, undefined), 10);
|
||||
});
|
||||
|
||||
test('score, default schemes', function () {
|
||||
|
@ -34,50 +34,50 @@ suite('LanguageSelector', function () {
|
|||
const uri = URI.parse('git:foo/file.txt');
|
||||
const language = 'farboo';
|
||||
|
||||
assert.strictEqual(score('*', uri, language, true), 5);
|
||||
assert.strictEqual(score('farboo', uri, language, true), 10);
|
||||
assert.strictEqual(score({ language: 'farboo', scheme: '' }, uri, language, true), 10);
|
||||
assert.strictEqual(score({ language: 'farboo', scheme: 'git' }, uri, language, true), 10);
|
||||
assert.strictEqual(score({ language: 'farboo', scheme: '*' }, uri, language, true), 10);
|
||||
assert.strictEqual(score({ language: 'farboo' }, uri, language, true), 10);
|
||||
assert.strictEqual(score({ language: '*' }, uri, language, true), 5);
|
||||
assert.strictEqual(score('*', uri, language, true, undefined), 5);
|
||||
assert.strictEqual(score('farboo', uri, language, true, undefined), 10);
|
||||
assert.strictEqual(score({ language: 'farboo', scheme: '' }, uri, language, true, undefined), 10);
|
||||
assert.strictEqual(score({ language: 'farboo', scheme: 'git' }, uri, language, true, undefined), 10);
|
||||
assert.strictEqual(score({ language: 'farboo', scheme: '*' }, uri, language, true, undefined), 10);
|
||||
assert.strictEqual(score({ language: 'farboo' }, uri, language, true, undefined), 10);
|
||||
assert.strictEqual(score({ language: '*' }, uri, language, true, undefined), 5);
|
||||
|
||||
assert.strictEqual(score({ scheme: '*' }, uri, language, true), 5);
|
||||
assert.strictEqual(score({ scheme: 'git' }, uri, language, true), 10);
|
||||
assert.strictEqual(score({ scheme: '*' }, uri, language, true, undefined), 5);
|
||||
assert.strictEqual(score({ scheme: 'git' }, uri, language, true, undefined), 10);
|
||||
});
|
||||
|
||||
test('score, filter', function () {
|
||||
assert.strictEqual(score('farboo', model.uri, model.language, true), 10);
|
||||
assert.strictEqual(score({ language: 'farboo' }, model.uri, model.language, true), 10);
|
||||
assert.strictEqual(score({ language: 'farboo', scheme: 'file' }, model.uri, model.language, true), 10);
|
||||
assert.strictEqual(score({ language: 'farboo', scheme: 'http' }, model.uri, model.language, true), 0);
|
||||
assert.strictEqual(score('farboo', model.uri, model.language, true, undefined), 10);
|
||||
assert.strictEqual(score({ language: 'farboo' }, model.uri, model.language, true, undefined), 10);
|
||||
assert.strictEqual(score({ language: 'farboo', scheme: 'file' }, model.uri, model.language, true, undefined), 10);
|
||||
assert.strictEqual(score({ language: 'farboo', scheme: 'http' }, model.uri, model.language, true, undefined), 0);
|
||||
|
||||
assert.strictEqual(score({ pattern: '**/*.fb' }, model.uri, model.language, true), 10);
|
||||
assert.strictEqual(score({ pattern: '**/*.fb', scheme: 'file' }, model.uri, model.language, true), 10);
|
||||
assert.strictEqual(score({ pattern: '**/*.fb' }, URI.parse('foo:bar'), model.language, true), 0);
|
||||
assert.strictEqual(score({ pattern: '**/*.fb', scheme: 'foo' }, URI.parse('foo:bar'), model.language, true), 0);
|
||||
assert.strictEqual(score({ pattern: '**/*.fb' }, model.uri, model.language, true, undefined), 10);
|
||||
assert.strictEqual(score({ pattern: '**/*.fb', scheme: 'file' }, model.uri, model.language, true, undefined), 10);
|
||||
assert.strictEqual(score({ pattern: '**/*.fb' }, URI.parse('foo:bar'), model.language, true, undefined), 0);
|
||||
assert.strictEqual(score({ pattern: '**/*.fb', scheme: 'foo' }, URI.parse('foo:bar'), model.language, true, undefined), 0);
|
||||
|
||||
let doc = {
|
||||
uri: URI.parse('git:/my/file.js'),
|
||||
langId: 'javascript'
|
||||
};
|
||||
assert.strictEqual(score('javascript', doc.uri, doc.langId, true), 10); // 0;
|
||||
assert.strictEqual(score({ language: 'javascript', scheme: 'git' }, doc.uri, doc.langId, true), 10); // 10;
|
||||
assert.strictEqual(score('*', doc.uri, doc.langId, true), 5); // 5
|
||||
assert.strictEqual(score('fooLang', doc.uri, doc.langId, true), 0); // 0
|
||||
assert.strictEqual(score(['fooLang', '*'], doc.uri, doc.langId, true), 5); // 5
|
||||
assert.strictEqual(score('javascript', doc.uri, doc.langId, true, undefined), 10); // 0;
|
||||
assert.strictEqual(score({ language: 'javascript', scheme: 'git' }, doc.uri, doc.langId, true, undefined), 10); // 10;
|
||||
assert.strictEqual(score('*', doc.uri, doc.langId, true, undefined), 5); // 5
|
||||
assert.strictEqual(score('fooLang', doc.uri, doc.langId, true, undefined), 0); // 0
|
||||
assert.strictEqual(score(['fooLang', '*'], doc.uri, doc.langId, true, undefined), 5); // 5
|
||||
});
|
||||
|
||||
test('score, max(filters)', function () {
|
||||
let match = { language: 'farboo', scheme: 'file' };
|
||||
let fail = { language: 'farboo', scheme: 'http' };
|
||||
|
||||
assert.strictEqual(score(match, model.uri, model.language, true), 10);
|
||||
assert.strictEqual(score(fail, model.uri, model.language, true), 0);
|
||||
assert.strictEqual(score([match, fail], model.uri, model.language, true), 10);
|
||||
assert.strictEqual(score([fail, fail], model.uri, model.language, true), 0);
|
||||
assert.strictEqual(score(['farboo', '*'], model.uri, model.language, true), 10);
|
||||
assert.strictEqual(score(['*', 'farboo'], model.uri, model.language, true), 10);
|
||||
assert.strictEqual(score(match, model.uri, model.language, true, undefined), 10);
|
||||
assert.strictEqual(score(fail, model.uri, model.language, true, undefined), 0);
|
||||
assert.strictEqual(score([match, fail], model.uri, model.language, true, undefined), 10);
|
||||
assert.strictEqual(score([fail, fail], model.uri, model.language, true, undefined), 0);
|
||||
assert.strictEqual(score(['farboo', '*'], model.uri, model.language, true, undefined), 10);
|
||||
assert.strictEqual(score(['*', 'farboo'], model.uri, model.language, true, undefined), 10);
|
||||
});
|
||||
|
||||
test('score hasAccessToAllModels', function () {
|
||||
|
@ -85,14 +85,29 @@ suite('LanguageSelector', function () {
|
|||
uri: URI.parse('file:/my/file.js'),
|
||||
langId: 'javascript'
|
||||
};
|
||||
assert.strictEqual(score('javascript', doc.uri, doc.langId, false), 0);
|
||||
assert.strictEqual(score({ language: 'javascript', scheme: 'file' }, doc.uri, doc.langId, false), 0);
|
||||
assert.strictEqual(score('*', doc.uri, doc.langId, false), 0);
|
||||
assert.strictEqual(score('fooLang', doc.uri, doc.langId, false), 0);
|
||||
assert.strictEqual(score(['fooLang', '*'], doc.uri, doc.langId, false), 0);
|
||||
assert.strictEqual(score('javascript', doc.uri, doc.langId, false, undefined), 0);
|
||||
assert.strictEqual(score({ language: 'javascript', scheme: 'file' }, doc.uri, doc.langId, false, undefined), 0);
|
||||
assert.strictEqual(score('*', doc.uri, doc.langId, false, undefined), 0);
|
||||
assert.strictEqual(score('fooLang', doc.uri, doc.langId, false, undefined), 0);
|
||||
assert.strictEqual(score(['fooLang', '*'], doc.uri, doc.langId, false, undefined), 0);
|
||||
|
||||
assert.strictEqual(score({ language: 'javascript', scheme: 'file', hasAccessToAllModels: true }, doc.uri, doc.langId, false), 10);
|
||||
assert.strictEqual(score(['fooLang', '*', { language: '*', hasAccessToAllModels: true }], doc.uri, doc.langId, false), 5);
|
||||
assert.strictEqual(score({ language: 'javascript', scheme: 'file', hasAccessToAllModels: true }, doc.uri, doc.langId, false, undefined), 10);
|
||||
assert.strictEqual(score(['fooLang', '*', { language: '*', hasAccessToAllModels: true }], doc.uri, doc.langId, false, undefined), 5);
|
||||
});
|
||||
|
||||
test('score, notebookType', function () {
|
||||
let obj = {
|
||||
uri: URI.parse('file:/my/file.js'),
|
||||
langId: 'javascript',
|
||||
notebookType: 'fooBook'
|
||||
};
|
||||
|
||||
assert.strictEqual(score('javascript', obj.uri, obj.langId, true, undefined), 10);
|
||||
assert.strictEqual(score('javascript', obj.uri, obj.langId, true, obj.notebookType), 10);
|
||||
assert.strictEqual(score({ notebookType: 'fooBook' }, obj.uri, obj.langId, true, obj.notebookType), 10);
|
||||
assert.strictEqual(score({ notebookType: 'fooBook', language: '*' }, obj.uri, obj.langId, true, obj.notebookType), 10);
|
||||
assert.strictEqual(score({ notebookType: '*', language: '*' }, obj.uri, obj.langId, true, obj.notebookType), 5);
|
||||
assert.strictEqual(score({ notebookType: '*', language: 'javascript' }, obj.uri, obj.langId, true, obj.notebookType), 10);
|
||||
});
|
||||
|
||||
test('Document selector match - unexpected result value #60232', function () {
|
||||
|
@ -101,7 +116,7 @@ suite('LanguageSelector', function () {
|
|||
scheme: 'file',
|
||||
pattern: '**/*.interface.json'
|
||||
};
|
||||
let value = score(selector, URI.parse('file:///C:/Users/zlhe/Desktop/test.interface.json'), 'json', true);
|
||||
let value = score(selector, URI.parse('file:///C:/Users/zlhe/Desktop/test.interface.json'), 'json', true, undefined);
|
||||
assert.strictEqual(value, 10);
|
||||
});
|
||||
|
||||
|
@ -112,7 +127,7 @@ suite('LanguageSelector', function () {
|
|||
pattern: '*.json'
|
||||
}
|
||||
};
|
||||
let value = score(selector, URI.file('/home/user/Desktop/test.json'), 'json', true);
|
||||
let value = score(selector, URI.file('/home/user/Desktop/test.json'), 'json', true, undefined);
|
||||
assert.strictEqual(value, 10);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -328,16 +328,25 @@ export const enum TextEditorSelectionSource {
|
|||
|
||||
/**
|
||||
* Programmatic source indicates a selection change that
|
||||
* was not triggered by the user via keyboard or mouse.
|
||||
* was not triggered by the user via keyboard or mouse
|
||||
* but through text editor APIs.
|
||||
*/
|
||||
PROGRAMMATIC = 'api',
|
||||
|
||||
/**
|
||||
* Navigation source indicates a change that was caused
|
||||
* by navigating in the text editor from commands such
|
||||
* as "Go to definition"
|
||||
* Navigation source indicates a selection change that
|
||||
* was caused via some command or UI component such as
|
||||
* an outline tree.
|
||||
*/
|
||||
NAVIGATION = 'code.navigation'
|
||||
NAVIGATION = 'code.navigation',
|
||||
|
||||
/**
|
||||
* Jump source indicates a selection change that
|
||||
* was caused from within the text editor to another
|
||||
* location in the same or different text editor such
|
||||
* as "Go to definition".
|
||||
*/
|
||||
JUMP = 'code.jump'
|
||||
}
|
||||
|
||||
export interface ITextEditorOptions extends IEditorOptions {
|
||||
|
@ -352,4 +361,9 @@ export interface ITextEditorOptions extends IEditorOptions {
|
|||
* Defaults to TextEditorSelectionRevealType.Center
|
||||
*/
|
||||
selectionRevealType?: TextEditorSelectionRevealType;
|
||||
|
||||
/**
|
||||
* Source of the call that caused the selection.
|
||||
*/
|
||||
selectionSource?: TextEditorSelectionSource | string;
|
||||
}
|
||||
|
|
|
@ -465,6 +465,7 @@ export type IConfigBasedExtensionTip = {
|
|||
readonly isExtensionPack: boolean;
|
||||
readonly configName: string;
|
||||
readonly important: boolean;
|
||||
readonly whenNotInstalled?: string[];
|
||||
};
|
||||
|
||||
export type IExecutableBasedExtensionTip = {
|
||||
|
@ -474,6 +475,7 @@ export type IExecutableBasedExtensionTip = {
|
|||
readonly exeName: string;
|
||||
readonly exeFriendlyName: string;
|
||||
readonly windowsPath?: string;
|
||||
readonly whenNotInstalled?: string[];
|
||||
};
|
||||
|
||||
export type IWorkspaceTips = { readonly remoteSet: string[]; readonly recommendations: string[] };
|
||||
|
|
|
@ -68,7 +68,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
|||
extensionName: value.name,
|
||||
configName: tip.configName,
|
||||
important: !!value.important,
|
||||
isExtensionPack: !!value.isExtensionPack
|
||||
isExtensionPack: !!value.isExtensionPack,
|
||||
whenNotInstalled: value.whenNotInstalled
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
@ -77,7 +78,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
|||
extensionName: value.name,
|
||||
configName: tip.configName,
|
||||
important: !!value.important,
|
||||
isExtensionPack: !!value.isExtensionPack
|
||||
isExtensionPack: !!value.isExtensionPack,
|
||||
whenNotInstalled: value.whenNotInstalled
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -14,8 +14,10 @@ import { URI } from 'vs/base/common/uri';
|
|||
import { localize } from 'vs/nls';
|
||||
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IExecutableBasedExtensionTip, IExtensionManagementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { ExtensionTipsService as BaseExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionTipsService';
|
||||
import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations';
|
||||
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
|
||||
|
@ -32,7 +34,7 @@ type ExeExtensionRecommendationsClassification = {
|
|||
type IExeBasedExtensionTips = {
|
||||
readonly exeFriendlyName: string;
|
||||
readonly windowsPath?: string;
|
||||
readonly recommendations: { extensionId: string; extensionName: string; isExtensionPack: boolean }[];
|
||||
readonly recommendations: { extensionId: string; extensionName: string; isExtensionPack: boolean; whenNotInstalled?: string[] }[];
|
||||
};
|
||||
|
||||
const promptedExecutableTipsStorageKey = 'extensionTips/promptedExecutableTips';
|
||||
|
@ -242,8 +244,11 @@ export class ExtensionTipsService extends BaseExtensionTipsService {
|
|||
});
|
||||
}
|
||||
|
||||
private promptExeRecommendations(tips: IExecutableBasedExtensionTip[]): Promise<RecommendationsNotificationResult> {
|
||||
const extensionIds = tips.map(({ extensionId }) => extensionId.toLowerCase());
|
||||
private async promptExeRecommendations(tips: IExecutableBasedExtensionTip[]): Promise<RecommendationsNotificationResult> {
|
||||
const installed = await this.extensionManagementService.getInstalled(ExtensionType.User);
|
||||
const extensionIds = tips
|
||||
.filter(tip => !tip.whenNotInstalled || tip.whenNotInstalled.every(id => installed.every(local => !areSameExtensions(local.identifier, { id }))))
|
||||
.map(({ extensionId }) => extensionId.toLowerCase());
|
||||
const message = localize({ key: 'exeRecommended', comment: ['Placeholder string is the name of the software that is installed.'] }, "You have {0} installed on your system. Do you want to install the recommended extensions for it?", tips[0].exeFriendlyName);
|
||||
return this.extensionRecommendationNotificationService.promptImportantExtensionsInstallNotification(extensionIds, message, `@exe:"${tips[0].exeName}"`, RecommendationSource.EXE);
|
||||
}
|
||||
|
@ -316,7 +321,7 @@ export class ExtensionTipsService extends BaseExtensionTipsService {
|
|||
checkedExecutables.set(exePath, exists);
|
||||
}
|
||||
if (exists) {
|
||||
for (const { extensionId, extensionName, isExtensionPack } of extensionTip.recommendations) {
|
||||
for (const { extensionId, extensionName, isExtensionPack, whenNotInstalled } of extensionTip.recommendations) {
|
||||
result.push({
|
||||
extensionId,
|
||||
extensionName,
|
||||
|
@ -324,6 +329,7 @@ export class ExtensionTipsService extends BaseExtensionTipsService {
|
|||
exeName,
|
||||
exeFriendlyName: extensionTip.exeFriendlyName,
|
||||
windowsPath: extensionTip.windowsPath,
|
||||
whenNotInstalled: whenNotInstalled
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import { isLinux, isMacintosh, platform } from 'vs/base/common/platform';
|
|||
import { arch } from 'vs/base/common/process';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import * as semver from 'vs/base/common/semver/semver';
|
||||
import { isBoolean } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
|
@ -292,7 +293,10 @@ class InstallGalleryExtensionTask extends AbstractInstallExtensionTask {
|
|||
installableExtension.metadata.isMachineScoped = this.options.isMachineScoped || existingExtension?.isMachineScoped;
|
||||
installableExtension.metadata.isBuiltin = this.options.isBuiltin || existingExtension?.isBuiltin;
|
||||
installableExtension.metadata.isPreReleaseVersion = this.gallery.properties.isPreReleaseVersion;
|
||||
installableExtension.metadata.preRelease = this.gallery.hasPreReleaseVersion ? this.gallery.properties.isPreReleaseVersion : (existingExtension?.preRelease || this.options.installPreReleaseVersion);
|
||||
installableExtension.metadata.preRelease = this.gallery.properties.isPreReleaseVersion ||
|
||||
(isBoolean(this.options.installPreReleaseVersion)
|
||||
? this.options.installPreReleaseVersion /* Respect the passed flag */
|
||||
: existingExtension?.preRelease /* Respect the existing pre-release flag if it was set */);
|
||||
|
||||
try {
|
||||
const local = await this.installExtension(installableExtension, token);
|
||||
|
|
|
@ -286,37 +286,37 @@ export class ConsoleLogger extends AbstractLogger implements ILogger {
|
|||
|
||||
trace(message: string, ...args: any[]): void {
|
||||
if (this.getLevel() <= LogLevel.Trace) {
|
||||
console.log(`%cTRACE %c${message}`, 'color: #888', 'color: #000', ...args);
|
||||
console.log(`%cTRACE%c ${message}`, 'color: #888', 'color: #444', ...args);
|
||||
}
|
||||
}
|
||||
|
||||
debug(message: string, ...args: any[]): void {
|
||||
if (this.getLevel() <= LogLevel.Debug) {
|
||||
console.log(`%cDEBUG %c${message}`, 'background: #eee; color: #888', 'color: #000', ...args);
|
||||
console.log(`%cDEBUG%c ${message}`, 'background: #eee; color: #888', 'color: #444', ...args);
|
||||
}
|
||||
}
|
||||
|
||||
info(message: string, ...args: any[]): void {
|
||||
if (this.getLevel() <= LogLevel.Info) {
|
||||
console.log(`%c INFO %c${message}`, 'color: #33f', 'color: #000', ...args);
|
||||
console.log(`%c INFO%c ${message}`, 'color: #33f', 'color: #444', ...args);
|
||||
}
|
||||
}
|
||||
|
||||
warn(message: string | Error, ...args: any[]): void {
|
||||
if (this.getLevel() <= LogLevel.Warning) {
|
||||
console.log(`%c WARN %c${message}`, 'color: #993', 'color: #000', ...args);
|
||||
console.log(`%c WARN%c ${message}`, 'color: #993', 'color: #444', ...args);
|
||||
}
|
||||
}
|
||||
|
||||
error(message: string, ...args: any[]): void {
|
||||
if (this.getLevel() <= LogLevel.Error) {
|
||||
console.log(`%c ERR %c${message}`, 'color: #f33', 'color: #000', ...args);
|
||||
console.log(`%c ERR%c ${message}`, 'color: #f33', 'color: #444', ...args);
|
||||
}
|
||||
}
|
||||
|
||||
critical(message: string, ...args: any[]): void {
|
||||
if (this.getLevel() <= LogLevel.Critical) {
|
||||
console.log(`%cCRITI %c${message}`, 'background: #f33; color: white', 'color: #000', ...args);
|
||||
console.log(`%cCRITI%c ${message}`, 'background: #f33; color: white', 'color: #444', ...args);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ export interface ColorContribution {
|
|||
* @sample `editorSuggestWidget.background` is `--vscode-editorSuggestWidget-background`.
|
||||
*/
|
||||
export function asCssVariableName(colorIdent: ColorIdentifier): string {
|
||||
return `--vscode-${colorIdent.replace('.', '-')}`;
|
||||
return `--vscode-${colorIdent.replace(/\./g, '-')}`;
|
||||
}
|
||||
|
||||
export const enum ColorTransformType {
|
||||
|
|
|
@ -382,6 +382,13 @@ export class WorkspaceFolder implements IWorkspaceFolder {
|
|||
|
||||
constructor(
|
||||
data: IWorkspaceFolderData,
|
||||
/**
|
||||
* Provides access to the original metadata for this workspace
|
||||
* folder. This can be different from the metadata provided in
|
||||
* this class:
|
||||
* - raw paths can be relative
|
||||
* - raw paths are not normalized
|
||||
*/
|
||||
readonly raw?: IRawFileWorkspaceFolder | IRawUriWorkspaceFolder
|
||||
) {
|
||||
this.uri = data.uri;
|
||||
|
|
|
@ -5,36 +5,34 @@
|
|||
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ExtHostContext, IExtHostEditorTabsShape, MainContext, IEditorTabDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostContext, IExtHostEditorTabsShape, MainContext, IEditorTabDto, IEditorTabGroupDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
|
||||
import { EditorResourceAccessor, IUntypedEditorInput, SideBySideEditor, GroupModelChangeKind, DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor';
|
||||
import { EditorResourceAccessor, IUntypedEditorInput, SideBySideEditor, DEFAULT_EDITOR_ASSOCIATION, GroupModelChangeKind } from 'vs/workbench/common/editor';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { isGroupEditorCloseEvent, isGroupEditorMoveEvent, isGroupEditorOpenEvent } from 'vs/workbench/common/editor/editorGroupModel';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput';
|
||||
import { columnToEditorGroup, EditorGroupColumn, editorGroupToColumn } from 'vs/workbench/services/editor/common/editorGroupColumn';
|
||||
import { GroupDirection, IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorsChangeEvent, IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadEditorTabs)
|
||||
export class MainThreadEditorTabs {
|
||||
|
||||
private readonly _dispoables = new DisposableStore();
|
||||
private readonly _proxy: IExtHostEditorTabsShape;
|
||||
private readonly _tabModel: Map<number, IEditorTabDto[]> = new Map<number, IEditorTabDto[]>();
|
||||
private _currentlyActiveTab: { groupId: number; tab: IEditorTabDto } | undefined = undefined;
|
||||
private _tabGroupModel: IEditorTabGroupDto[] = [];
|
||||
private readonly _tabModel: Map<number, IEditorTabDto[]> = new Map();
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IEditorGroupsService private readonly _editorGroupsService: IEditorGroupsService,
|
||||
@IEditorService editorService: IEditorService
|
||||
@IEditorService editorService: IEditorService,
|
||||
) {
|
||||
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostEditorTabs);
|
||||
|
||||
// Queue all events that arrive on the same event loop and then send them as a batch
|
||||
this._dispoables.add(editorService.onDidEditorsChange((events) => this._updateTabsModel(events)));
|
||||
this._dispoables.add(editorService.onDidEditorsChange((event) => this._updateTabsModel(event)));
|
||||
this._editorGroupsService.whenReady.then(() => this._createTabsModel());
|
||||
}
|
||||
|
||||
|
@ -57,7 +55,7 @@ export class MainThreadEditorTabs {
|
|||
resource: editor instanceof SideBySideEditorInput ? EditorResourceAccessor.getCanonicalUri(editor, { supportSideBySide: SideBySideEditor.PRIMARY }) : EditorResourceAccessor.getCanonicalUri(editor),
|
||||
editorId,
|
||||
additionalResourcesAndViewIds: [],
|
||||
isActive: (this._editorGroupsService.activeGroup === group) && group.isActive(editor)
|
||||
isActive: group.isActive(editor)
|
||||
};
|
||||
tab.additionalResourcesAndViewIds.push({ resource: tab.resource, viewId: tab.editorId });
|
||||
if (editor instanceof SideBySideEditorInput) {
|
||||
|
@ -85,162 +83,166 @@ export class MainThreadEditorTabs {
|
|||
}
|
||||
|
||||
/**
|
||||
* Builds the model from scratch based on the current state of the editor service.
|
||||
* Called whenever a group activates, updates the model by marking the group as active an notifies the extension host
|
||||
*/
|
||||
private _createTabsModel(): void {
|
||||
this._tabModel.clear();
|
||||
let tabs: IEditorTabDto[] = [];
|
||||
for (const group of this._editorGroupsService.groups) {
|
||||
for (const editor of group.editors) {
|
||||
if (editor.isDisposed()) {
|
||||
continue;
|
||||
}
|
||||
const tab = this._buildTabObject(editor, group);
|
||||
if (tab.isActive) {
|
||||
this._currentlyActiveTab = { groupId: group.id, tab };
|
||||
}
|
||||
tabs.push(tab);
|
||||
}
|
||||
this._tabModel.set(group.id, tabs);
|
||||
private _onDidGroupActivate() {
|
||||
const activeGroupId = this._editorGroupsService.activeGroup.id;
|
||||
for (const group of this._tabGroupModel) {
|
||||
group.isActive = group.groupId === activeGroupId;
|
||||
}
|
||||
this._proxy.$acceptEditorTabs(tabs);
|
||||
}
|
||||
|
||||
private _onDidTabOpen(event: IEditorsChangeEvent): void {
|
||||
if (!isGroupEditorOpenEvent(event)) {
|
||||
return;
|
||||
}
|
||||
if (!this._tabModel.has(event.groupId)) {
|
||||
this._tabModel.set(event.groupId, []);
|
||||
}
|
||||
const editor = event.editor;
|
||||
const tab = this._buildTabObject(editor, this._editorGroupsService.getGroup(event.groupId) ?? this._editorGroupsService.activeGroup);
|
||||
this._tabModel.get(event.groupId)?.splice(event.editorIndex, 0, tab);
|
||||
// Update the currently active tab which may or may not be the opened one
|
||||
if (tab.isActive) {
|
||||
if (this._currentlyActiveTab) {
|
||||
this._currentlyActiveTab.tab.isActive = (this._editorGroupsService.activeGroup.id === this._currentlyActiveTab.groupId) && this._editorGroupsService.activeGroup.isActive(this._tabToUntypedEditorInput(this._currentlyActiveTab.tab));
|
||||
}
|
||||
this._currentlyActiveTab = { groupId: event.groupId, tab };
|
||||
}
|
||||
}
|
||||
|
||||
private _onDidTabClose(event: IEditorsChangeEvent): void {
|
||||
if (!isGroupEditorCloseEvent(event)) {
|
||||
return;
|
||||
}
|
||||
this._tabModel.get(event.groupId)?.splice(event.editorIndex, 1);
|
||||
this._findAndUpdateActiveTab();
|
||||
|
||||
// Remove any empty groups
|
||||
if (this._tabModel.get(event.groupId)?.length === 0) {
|
||||
this._tabModel.delete(event.groupId);
|
||||
}
|
||||
}
|
||||
|
||||
private _onDidTabMove(event: IEditorsChangeEvent): void {
|
||||
if (!isGroupEditorMoveEvent(event)) {
|
||||
return;
|
||||
}
|
||||
const movedTab = this._tabModel.get(event.groupId)?.splice(event.oldEditorIndex, 1);
|
||||
if (movedTab === undefined) {
|
||||
return;
|
||||
}
|
||||
this._tabModel.get(event.groupId)?.splice(event.editorIndex, 0, movedTab[0]);
|
||||
movedTab[0].isActive = (this._editorGroupsService.activeGroup.id === event.groupId) && this._editorGroupsService.activeGroup.isActive(this._tabToUntypedEditorInput(movedTab[0]));
|
||||
// Update the currently active tab
|
||||
if (movedTab[0].isActive) {
|
||||
if (this._currentlyActiveTab) {
|
||||
this._currentlyActiveTab.tab.isActive = (this._editorGroupsService.activeGroup.id === this._currentlyActiveTab.groupId) && this._editorGroupsService.activeGroup.isActive(this._tabToUntypedEditorInput(this._currentlyActiveTab.tab));
|
||||
}
|
||||
this._currentlyActiveTab = { groupId: event.groupId, tab: movedTab[0] };
|
||||
}
|
||||
}
|
||||
|
||||
private _onDidGroupActivate(event: IEditorsChangeEvent): void {
|
||||
if (event.kind !== GroupModelChangeKind.GROUP_INDEX && event.kind !== GroupModelChangeKind.EDITOR_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
this._findAndUpdateActiveTab();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the currently active tab so that `this._currentlyActiveTab` is up to date.
|
||||
* Called when the tab label changes
|
||||
* @param groupId The id of the group the tab exists in
|
||||
* @param editorInput The editor input represented by the tab
|
||||
* @param editorIndex The index of the editor within that group
|
||||
*/
|
||||
private _findAndUpdateActiveTab() {
|
||||
// Go to the active group and update the active tab
|
||||
const activeGroupId = this._editorGroupsService.activeGroup.id;
|
||||
this._tabModel.get(activeGroupId)?.forEach(t => {
|
||||
if (t.resource) {
|
||||
t.isActive = this._editorGroupsService.activeGroup.isActive(this._tabToUntypedEditorInput(t));
|
||||
private _onDidTabLabelChange(groupId: number, editorInput: EditorInput, editorIndex: number) {
|
||||
this._tabGroupModel[groupId].tabs[editorIndex].label = editorInput.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a new tab is opened
|
||||
* @param groupId The id of the group the tab is being created in
|
||||
* @param editorInput The editor input being opened
|
||||
* @param editorIndex The index of the editor within that group
|
||||
*/
|
||||
private _onDidTabOpen(groupId: number, editorInput: EditorInput, editorIndex: number) {
|
||||
const group = this._editorGroupsService.getGroup(groupId);
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
// Splice tab into group at index editorIndex
|
||||
this._tabGroupModel[groupId].tabs.splice(editorIndex, 0, this._buildTabObject(editorInput, group));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a tab is closed
|
||||
* @param groupId The id of the group the tab is being removed from
|
||||
* @param editorIndex The index of the editor within that group
|
||||
*/
|
||||
private _onDidTabClose(groupId: number, editorIndex: number) {
|
||||
const group = this._editorGroupsService.getGroup(groupId);
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
// Splice tab into group at index editorIndex
|
||||
this._tabGroupModel[groupId].tabs.splice(editorIndex, 1);
|
||||
// If no tabs it's an empty group and gets deleted from the model
|
||||
// In the future we may want to support empty groups
|
||||
if (this._tabGroupModel[groupId].tabs.length === 0) {
|
||||
this._tabGroupModel.splice(groupId, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the active tab changes
|
||||
* @param groupId The id of the group the tab is contained in
|
||||
* @param editorIndex The index of the tab
|
||||
*/
|
||||
private _onDidTabActiveChange(groupId: number, editorIndex: number) {
|
||||
const tabs = this._tabGroupModel[groupId].tabs;
|
||||
let activeTab: IEditorTabDto | undefined;
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
if (i === editorIndex) {
|
||||
tabs[i].isActive = true;
|
||||
activeTab = tabs[i];
|
||||
} else {
|
||||
tabs[i].isActive = false;
|
||||
}
|
||||
if (t.isActive) {
|
||||
if (this._currentlyActiveTab) {
|
||||
this._currentlyActiveTab.tab.isActive = (this._editorGroupsService.activeGroup.id === this._currentlyActiveTab.groupId) && this._editorGroupsService.activeGroup.isActive(this._tabToUntypedEditorInput(this._currentlyActiveTab.tab));
|
||||
}
|
||||
this._tabGroupModel[groupId].activeTab = activeTab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the model from scratch based on the current state of the editor service.
|
||||
*/
|
||||
private _createTabsModel(): void {
|
||||
this._tabGroupModel = [];
|
||||
this._tabModel.clear();
|
||||
let tabs: IEditorTabDto[] = [];
|
||||
for (const group of this._editorGroupsService.groups) {
|
||||
const currentTabGroupModel: IEditorTabGroupDto = {
|
||||
groupId: group.id,
|
||||
isActive: group.id === this._editorGroupsService.activeGroup.id,
|
||||
viewColumn: editorGroupToColumn(this._editorGroupsService, group),
|
||||
activeTab: undefined,
|
||||
tabs: []
|
||||
};
|
||||
for (const editor of group.editors) {
|
||||
const tab = this._buildTabObject(editor, group);
|
||||
// Mark the tab active within the group
|
||||
if (tab.isActive) {
|
||||
currentTabGroupModel.activeTab = tab;
|
||||
}
|
||||
this._currentlyActiveTab = { groupId: activeGroupId, tab: t };
|
||||
return;
|
||||
tabs.push(tab);
|
||||
}
|
||||
}, this);
|
||||
currentTabGroupModel.tabs = tabs;
|
||||
this._tabGroupModel.push(currentTabGroupModel);
|
||||
this._tabModel.set(group.id, tabs);
|
||||
tabs = [];
|
||||
}
|
||||
}
|
||||
|
||||
// TODOD @lramos15 Remove this after done finishing the tab model code
|
||||
// private _eventArrayToString(events: IEditorsChangeEvent[]): void {
|
||||
// let eventString = '[';
|
||||
// events.forEach(event => {
|
||||
// switch (event.kind) {
|
||||
// case GroupModelChangeKind.GROUP_INDEX: eventString += 'GROUP_INDEX, '; break;
|
||||
// case GroupModelChangeKind.EDITOR_ACTIVE: eventString += 'EDITOR_ACTIVE, '; break;
|
||||
// case GroupModelChangeKind.EDITOR_PIN: eventString += 'EDITOR_PIN, '; break;
|
||||
// case GroupModelChangeKind.EDITOR_OPEN: eventString += 'EDITOR_OPEN, '; break;
|
||||
// case GroupModelChangeKind.EDITOR_CLOSE: eventString += 'EDITOR_CLOSE, '; break;
|
||||
// case GroupModelChangeKind.EDITOR_MOVE: eventString += 'EDITOR_MOVE, '; break;
|
||||
// case GroupModelChangeKind.EDITOR_LABEL: eventString += 'EDITOR_LABEL, '; break;
|
||||
// case GroupModelChangeKind.GROUP_ACTIVE: eventString += 'GROUP_ACTIVE, '; break;
|
||||
// case GroupModelChangeKind.GROUP_LOCKED: eventString += 'GROUP_LOCKED, '; break;
|
||||
// default: eventString += 'UNKNOWN, '; break;
|
||||
// }
|
||||
// });
|
||||
// eventString += ']';
|
||||
// console.log(eventString);
|
||||
// }
|
||||
private _eventToString(event: IEditorsChangeEvent): string {
|
||||
let eventString = '';
|
||||
switch (event.kind) {
|
||||
case GroupModelChangeKind.GROUP_INDEX: eventString += 'GROUP_INDEX'; break;
|
||||
case GroupModelChangeKind.EDITOR_ACTIVE: eventString += 'EDITOR_ACTIVE'; break;
|
||||
case GroupModelChangeKind.EDITOR_PIN: eventString += 'EDITOR_PIN'; break;
|
||||
case GroupModelChangeKind.EDITOR_OPEN: eventString += 'EDITOR_OPEN'; break;
|
||||
case GroupModelChangeKind.EDITOR_CLOSE: eventString += 'EDITOR_CLOSE'; break;
|
||||
case GroupModelChangeKind.EDITOR_MOVE: eventString += 'EDITOR_MOVE'; break;
|
||||
case GroupModelChangeKind.EDITOR_LABEL: eventString += 'EDITOR_LABEL'; break;
|
||||
case GroupModelChangeKind.GROUP_ACTIVE: eventString += 'GROUP_ACTIVE'; break;
|
||||
case GroupModelChangeKind.GROUP_LOCKED: eventString += 'GROUP_LOCKED'; break;
|
||||
default: eventString += 'UNKNOWN'; break;
|
||||
}
|
||||
return eventString;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main handler for the tab events
|
||||
* @param events The list of events to process
|
||||
*/
|
||||
private _updateTabsModel(events: IEditorsChangeEvent[]): void {
|
||||
events.forEach(event => {
|
||||
// Call the correct function for the change type
|
||||
switch (event.kind) {
|
||||
case GroupModelChangeKind.EDITOR_OPEN:
|
||||
this._onDidTabOpen(event);
|
||||
private _updateTabsModel(event: IEditorsChangeEvent): void {
|
||||
console.log(this._eventToString(event));
|
||||
switch (event.kind) {
|
||||
case GroupModelChangeKind.GROUP_ACTIVE:
|
||||
if (event.groupId === this._editorGroupsService.activeGroup.id) {
|
||||
this._onDidGroupActivate();
|
||||
break;
|
||||
case GroupModelChangeKind.EDITOR_CLOSE:
|
||||
this._onDidTabClose(event);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
case GroupModelChangeKind.EDITOR_LABEL:
|
||||
if (event.editor && event.editorIndex) {
|
||||
this._onDidTabLabelChange(event.groupId, event.editor, event.editorIndex);
|
||||
break;
|
||||
case GroupModelChangeKind.EDITOR_ACTIVE:
|
||||
case GroupModelChangeKind.GROUP_ACTIVE:
|
||||
if (this._editorGroupsService.activeGroup.id !== event.groupId) {
|
||||
return;
|
||||
}
|
||||
this._onDidGroupActivate(event);
|
||||
}
|
||||
case GroupModelChangeKind.EDITOR_OPEN:
|
||||
if (event.editor && event.editorIndex) {
|
||||
this._onDidTabOpen(event.groupId, event.editor, event.editorIndex);
|
||||
break;
|
||||
case GroupModelChangeKind.GROUP_INDEX:
|
||||
this._createTabsModel();
|
||||
// Here we stop the loop as no need to process other events
|
||||
}
|
||||
case GroupModelChangeKind.EDITOR_CLOSE:
|
||||
if (event.editorIndex) {
|
||||
this._onDidTabClose(event.groupId, event.editorIndex);
|
||||
break;
|
||||
case GroupModelChangeKind.EDITOR_MOVE:
|
||||
this._onDidTabMove(event);
|
||||
}
|
||||
case GroupModelChangeKind.EDITOR_ACTIVE:
|
||||
if (event.editorIndex) {
|
||||
this._onDidTabActiveChange(event.groupId, event.editorIndex);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
// Flatten the map into a singular array to send the ext host
|
||||
let allTabs: IEditorTabDto[] = [];
|
||||
this._tabModel.forEach((tabs) => allTabs = allTabs.concat(tabs));
|
||||
this._proxy.$acceptEditorTabs(allTabs);
|
||||
}
|
||||
default:
|
||||
// If it's not an optimized case we rebuild the tabs model from scratch
|
||||
this._createTabsModel();
|
||||
}
|
||||
// notify the ext host of the new model
|
||||
this._proxy.$acceptEditorTabModel(this._tabGroupModel);
|
||||
}
|
||||
//#region Messages received from Ext Host
|
||||
$moveTab(tab: IEditorTabDto, index: number, viewColumn: EditorGroupColumn): void {
|
||||
|
@ -271,6 +273,7 @@ export class MainThreadEditorTabs {
|
|||
}
|
||||
// Move the editor to the target group
|
||||
sourceGroup.moveEditor(editorInput, targetGroup, { index, preserveFocus: true });
|
||||
return;
|
||||
}
|
||||
|
||||
async $closeTab(tab: IEditorTabDto): Promise<void> {
|
||||
|
|
|
@ -33,14 +33,14 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
|
|||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews);
|
||||
}
|
||||
|
||||
async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: string[] | undefined; dragMimeTypes: string[] | undefined; hasHandleDrag: boolean }): Promise<void> {
|
||||
async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: string[]; dragMimeTypes: string[]; hasHandleDrag: boolean }): Promise<void> {
|
||||
this.logService.trace('MainThreadTreeViews#$registerTreeViewDataProvider', treeViewId, options);
|
||||
|
||||
this.extensionService.whenInstalledExtensionsRegistered().then(() => {
|
||||
const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService);
|
||||
this._dataProviders.set(treeViewId, dataProvider);
|
||||
const dndController = options.dropMimeTypes
|
||||
? new TreeViewDragAndDropController(treeViewId, options.dropMimeTypes, options.dragMimeTypes ?? [], options.hasHandleDrag, this._proxy) : undefined;
|
||||
? new TreeViewDragAndDropController(treeViewId, options.dropMimeTypes, options.dragMimeTypes, options.hasHandleDrag, this._proxy) : undefined;
|
||||
const viewer = this.getTreeView(treeViewId);
|
||||
if (viewer) {
|
||||
// Order is important here. The internal tree isn't created until the dataProvider is set.
|
||||
|
|
|
@ -223,6 +223,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
if (typeof filter.exclusive === 'boolean') {
|
||||
checkProposedApiEnabled(extension, 'documentFiltersExclusive');
|
||||
}
|
||||
if (typeof filter.notebookType === 'string') {
|
||||
checkProposedApiEnabled(extension, 'notebookDocumentSelector');
|
||||
}
|
||||
}
|
||||
return selector;
|
||||
};
|
||||
|
@ -420,7 +423,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
return extHostLanguages.changeLanguage(document.uri, languageId);
|
||||
},
|
||||
match(selector: vscode.DocumentSelector, document: vscode.TextDocument): number {
|
||||
return score(typeConverters.LanguageSelector.from(selector), document.uri, document.languageId, true);
|
||||
return score(typeConverters.LanguageSelector.from(selector), document.uri, document.languageId, true, document.notebook?.notebookType);
|
||||
},
|
||||
registerCodeActionsProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable {
|
||||
return extHostLanguageFeatures.registerCodeActionProvider(extension, checkSelector(selector), provider, metadata);
|
||||
|
@ -745,21 +748,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
checkProposedApiEnabled(extension, 'externalUriOpener');
|
||||
return extHostUriOpeners.registerExternalUriOpener(extension.identifier, id, opener, metadata);
|
||||
},
|
||||
get tabs() {
|
||||
get tabGroups(): vscode.TabGroups {
|
||||
checkProposedApiEnabled(extension, 'tabs');
|
||||
return extHostEditorTabs.tabs;
|
||||
},
|
||||
get activeTab() {
|
||||
checkProposedApiEnabled(extension, 'tabs');
|
||||
return extHostEditorTabs.activeTab;
|
||||
},
|
||||
get onDidChangeTabs() {
|
||||
checkProposedApiEnabled(extension, 'tabs');
|
||||
return extHostEditorTabs.onDidChangeTabs;
|
||||
},
|
||||
get onDidChangeActiveTab() {
|
||||
checkProposedApiEnabled(extension, 'tabs');
|
||||
return extHostEditorTabs.onDidChangeActiveTab;
|
||||
return extHostEditorTabs.tabGroups;
|
||||
},
|
||||
getInlineCompletionItemController<T extends vscode.InlineCompletionItem>(provider: vscode.InlineCompletionItemProvider<T>): vscode.InlineCompletionController<T> {
|
||||
checkProposedApiEnabled(extension, 'inlineCompletions');
|
||||
|
|
|
@ -257,7 +257,7 @@ export interface MainThreadTextEditorsShape extends IDisposable {
|
|||
}
|
||||
|
||||
export interface MainThreadTreeViewsShape extends IDisposable {
|
||||
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: string[] | undefined; dragMimeTypes: string[] | undefined; hasHandleDrag: boolean }): Promise<void>;
|
||||
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: string[]; dragMimeTypes: string[]; hasHandleDrag: boolean }): Promise<void>;
|
||||
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Promise<void>;
|
||||
$reveal(treeViewId: string, itemInfo: { item: ITreeItem; parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise<void>;
|
||||
$setMessage(treeViewId: string, message: string): void;
|
||||
|
@ -336,6 +336,7 @@ export interface IDocumentFilterDto {
|
|||
scheme?: string;
|
||||
pattern?: string | IRelativePattern;
|
||||
exclusive?: boolean;
|
||||
notebookType?: string;
|
||||
}
|
||||
|
||||
export interface ISignatureHelpProviderMetadataDto {
|
||||
|
@ -614,6 +615,16 @@ export interface MainThreadEditorTabsShape extends IDisposable {
|
|||
$closeTab(tab: IEditorTabDto): Promise<void>;
|
||||
}
|
||||
|
||||
export interface IEditorTabGroupDto {
|
||||
isActive: boolean;
|
||||
viewColumn: EditorGroupColumn;
|
||||
// Decided not to go with simple index here due to opening and closing causing index shifts
|
||||
// This allows us to patch the model without having to do full rebuilds
|
||||
activeTab: IEditorTabDto | undefined;
|
||||
tabs: IEditorTabDto[];
|
||||
groupId: number;
|
||||
}
|
||||
|
||||
export interface IEditorTabDto {
|
||||
viewColumn: EditorGroupColumn;
|
||||
label: string;
|
||||
|
@ -624,7 +635,7 @@ export interface IEditorTabDto {
|
|||
}
|
||||
|
||||
export interface IExtHostEditorTabsShape {
|
||||
$acceptEditorTabs(tabs: IEditorTabDto[]): void;
|
||||
$acceptEditorTabModel(tabGroups: IEditorTabGroupDto[]): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
|
|
@ -953,14 +953,14 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
|
|||
if (activeEditor) {
|
||||
return activeEditor.document.uri;
|
||||
}
|
||||
const tabs = editorTabs.tabs.filter(tab => tab.isActive);
|
||||
if (tabs.length > 0) {
|
||||
const activeTab = editorTabs.tabGroups.all.find(group => group.isActive)?.activeTab;
|
||||
if (activeTab !== undefined) {
|
||||
// Resolve a resource from the tab
|
||||
const asSideBySideResource = tabs[0].resource as { primary?: URI; secondary?: URI } | undefined;
|
||||
const asSideBySideResource = activeTab.resource as { primary?: URI; secondary?: URI } | undefined;
|
||||
if (asSideBySideResource && (asSideBySideResource.primary || asSideBySideResource.secondary)) {
|
||||
return asSideBySideResource.primary ?? asSideBySideResource.secondary;
|
||||
} else {
|
||||
return tabs[0].resource as URI | undefined;
|
||||
return activeTab.resource as URI | undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,18 +5,15 @@
|
|||
|
||||
import type * as vscode from 'vscode';
|
||||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { IEditorTabDto, IExtHostEditorTabsShape, MainContext, MainThreadEditorTabsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IEditorTabDto, IEditorTabGroupDto, IExtHostEditorTabsShape, MainContext, MainThreadEditorTabsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ViewColumn } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { raceTimeout } from 'vs/base/common/async';
|
||||
|
||||
export interface IEditorTab {
|
||||
label: string;
|
||||
viewColumn: ViewColumn;
|
||||
index: number;
|
||||
resource: vscode.Uri | undefined;
|
||||
viewId: string | undefined;
|
||||
isActive: boolean;
|
||||
|
@ -25,12 +22,21 @@ export interface IEditorTab {
|
|||
close(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface IEditorTabGroup {
|
||||
isActive: boolean;
|
||||
viewColumn: ViewColumn;
|
||||
activeTab: IEditorTab | undefined;
|
||||
tabs: IEditorTab[];
|
||||
}
|
||||
|
||||
export interface IEditorTabGroups {
|
||||
all: IEditorTabGroup[];
|
||||
onDidChangeTabGroup: Event<void>;
|
||||
}
|
||||
|
||||
export interface IExtHostEditorTabs extends IExtHostEditorTabsShape {
|
||||
readonly _serviceBrand: undefined;
|
||||
tabs: readonly IEditorTab[];
|
||||
activeTab: IEditorTab | undefined;
|
||||
onDidChangeActiveTab: Event<IEditorTab | undefined>;
|
||||
onDidChangeTabs: Event<IEditorTab[]>;
|
||||
tabGroups: IEditorTabGroups;
|
||||
}
|
||||
|
||||
export const IExtHostEditorTabs = createDecorator<IExtHostEditorTabs>('IExtHostEditorTabs');
|
||||
|
@ -39,61 +45,64 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs {
|
|||
readonly _serviceBrand: undefined;
|
||||
private readonly _proxy: MainThreadEditorTabsShape;
|
||||
|
||||
private readonly _onDidChangeTabs = new Emitter<IEditorTab[]>();
|
||||
readonly onDidChangeTabs: Event<IEditorTab[]> = this._onDidChangeTabs.event;
|
||||
private readonly _onDidChangeTabGroup = new Emitter<void>();
|
||||
readonly onDidChangeTabGroup: Event<void> = this._onDidChangeTabGroup.event;
|
||||
|
||||
private readonly _onDidChangeActiveTab = new Emitter<IEditorTab | undefined>();
|
||||
readonly onDidChangeActiveTab: Event<IEditorTab | undefined> = this._onDidChangeActiveTab.event;
|
||||
|
||||
private _tabs: IEditorTab[] = [];
|
||||
private _activeTab: IEditorTab | undefined;
|
||||
private _tabGroups: IEditorTabGroups = {
|
||||
all: [],
|
||||
onDidChangeTabGroup: this._onDidChangeTabGroup.event
|
||||
};
|
||||
|
||||
constructor(@IExtHostRpcService extHostRpc: IExtHostRpcService) {
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadEditorTabs);
|
||||
}
|
||||
|
||||
get tabs(): readonly IEditorTab[] {
|
||||
return this._tabs;
|
||||
get tabGroups(): IEditorTabGroups {
|
||||
return this._tabGroups;
|
||||
}
|
||||
|
||||
get activeTab(): IEditorTab | undefined {
|
||||
return this._activeTab;
|
||||
}
|
||||
|
||||
$acceptEditorTabs(tabs: IEditorTabDto[]): void {
|
||||
let activeIndex = -1;
|
||||
this._tabs = tabs.map((dto, index) => {
|
||||
if (dto.isActive) {
|
||||
activeIndex = index;
|
||||
}
|
||||
return Object.freeze({
|
||||
label: dto.label,
|
||||
viewColumn: typeConverters.ViewColumn.to(dto.viewColumn),
|
||||
index,
|
||||
resource: URI.revive(dto.resource),
|
||||
additionalResourcesAndViewIds: dto.additionalResourcesAndViewIds.map(({ resource, viewId }) => ({ resource: URI.revive(resource), viewId })),
|
||||
viewId: dto.editorId,
|
||||
isActive: dto.isActive,
|
||||
move: async (index: number, viewColumn: ViewColumn) => {
|
||||
this._proxy.$moveTab(dto, index, typeConverters.ViewColumn.from(viewColumn));
|
||||
await raceTimeout(Event.toPromise(this._onDidChangeTabs.event), 1000);
|
||||
return;
|
||||
},
|
||||
close: async () => {
|
||||
await this._proxy.$closeTab(dto);
|
||||
await raceTimeout(Event.toPromise(this._onDidChangeTabs.event), 1000);
|
||||
return;
|
||||
$acceptEditorTabModel(tabGroups: IEditorTabGroupDto[]): void {
|
||||
// Clears the tab groups array
|
||||
this._tabGroups.all.length = 0;
|
||||
for (const group of tabGroups) {
|
||||
let activeTab: IEditorTab | undefined;
|
||||
const tabs = group.tabs.map(tab => {
|
||||
const extHostTab = this.createExtHostTabObject(tab);
|
||||
if (tab.isActive) {
|
||||
activeTab = extHostTab;
|
||||
}
|
||||
return extHostTab;
|
||||
});
|
||||
});
|
||||
this._tabs = this._tabs.sort((t1, t2) => {
|
||||
return t1.viewColumn === t2.viewColumn ? t1.index - t2.index : t1.viewColumn - t2.viewColumn;
|
||||
});
|
||||
const oldActiveTab = this._activeTab;
|
||||
this._activeTab = activeIndex === -1 ? undefined : this._tabs[activeIndex];
|
||||
if (this._activeTab !== oldActiveTab) {
|
||||
this._onDidChangeActiveTab.fire(this._activeTab);
|
||||
this._tabGroups.all.push(Object.freeze({
|
||||
isActive: group.isActive,
|
||||
viewColumn: typeConverters.ViewColumn.to(group.viewColumn),
|
||||
activeTab,
|
||||
tabs
|
||||
}));
|
||||
}
|
||||
this._onDidChangeTabs.fire(this._tabs);
|
||||
this._onDidChangeTabGroup.fire();
|
||||
}
|
||||
|
||||
private createExtHostTabObject(tabDto: IEditorTabDto) {
|
||||
return Object.freeze({
|
||||
label: tabDto.label,
|
||||
viewColumn: typeConverters.ViewColumn.to(tabDto.viewColumn),
|
||||
resource: URI.revive(tabDto.resource),
|
||||
additionalResourcesAndViewIds: tabDto.additionalResourcesAndViewIds.map(({ resource, viewId }) => ({ resource: URI.revive(resource), viewId })),
|
||||
viewId: tabDto.editorId,
|
||||
isActive: tabDto.isActive,
|
||||
move: async (index: number, viewColumn: ViewColumn) => {
|
||||
this._proxy.$moveTab(tabDto, index, typeConverters.ViewColumn.from(viewColumn));
|
||||
// TODO: Need an on did change tab event at the group level
|
||||
// await raceTimeout(Event.toPromise(this._onDidChangeTabs.event), 1000);
|
||||
return;
|
||||
},
|
||||
close: async () => {
|
||||
await this._proxy.$closeTab(tabDto);
|
||||
// TODO: Need an on did change tab event at the group level
|
||||
// await raceTimeout(Event.toPromise(this._onDidChangeTabs.event), 1000);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
|||
const cellLengths: number[] = [];
|
||||
const cellLineCounts: number[] = [];
|
||||
for (const cell of this._notebook.getCells()) {
|
||||
if (cell.kind === types.NotebookCellKind.Code && (!this._selector || score(this._selector, cell.document.uri, cell.document.languageId, true))) {
|
||||
if (cell.kind === types.NotebookCellKind.Code && (!this._selector || score(this._selector, cell.document.uri, cell.document.languageId, true, undefined))) {
|
||||
this._cellUris.set(cell.document.uri, this._cells.length);
|
||||
this._cells.push(cell);
|
||||
cellLengths.push(cell.document.getText().length + 1);
|
||||
|
|
|
@ -87,8 +87,8 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
|||
if (!options || !options.treeDataProvider) {
|
||||
throw new Error('Options with treeDataProvider is mandatory');
|
||||
}
|
||||
const dropMimeTypes = options.dragAndDropController?.dropMimeTypes;
|
||||
const dragMimeTypes = options.dragAndDropController?.dragMimeTypes;
|
||||
const dropMimeTypes = options.dragAndDropController?.dropMimeTypes ?? [];
|
||||
const dragMimeTypes = options.dragAndDropController?.dragMimeTypes ?? [];
|
||||
const hasHandleDrag = !!options.dragAndDropController?.handleDrag;
|
||||
const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, dropMimeTypes, dragMimeTypes, hasHandleDrag: hasHandleDrag });
|
||||
const treeView = this.createExtHostTreeView(viewId, options, extension);
|
||||
|
@ -457,7 +457,7 @@ class ExtHostTreeView<T> extends Disposable {
|
|||
if (!target) {
|
||||
return;
|
||||
}
|
||||
return asPromise(() => this.dndController?.handleDrop(treeDataTransfer, target, token));
|
||||
return asPromise(() => this.dndController?.handleDrop(target, treeDataTransfer, token));
|
||||
}
|
||||
|
||||
get hasResolve(): boolean {
|
||||
|
|
|
@ -148,7 +148,8 @@ export namespace DocumentSelector {
|
|||
language: selector.language,
|
||||
scheme: _transformScheme(selector.scheme, uriTransformer),
|
||||
pattern: GlobPattern.from(selector.pattern) ?? undefined,
|
||||
exclusive: selector.exclusive
|
||||
exclusive: selector.exclusive,
|
||||
notebookType: selector.notebookType
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -104,6 +104,15 @@ export class Position {
|
|||
return false;
|
||||
}
|
||||
|
||||
static of(obj: vscode.Position): Position {
|
||||
if (obj instanceof Position) {
|
||||
return obj;
|
||||
} else if (this.isPosition(obj)) {
|
||||
return new Position(obj.line, obj.character);
|
||||
}
|
||||
throw new Error('Invalid argument, is NOT a position-like object');
|
||||
}
|
||||
|
||||
private _line: number;
|
||||
private _character: number;
|
||||
|
||||
|
@ -245,6 +254,16 @@ export class Range {
|
|||
&& Position.isPosition((<Range>thing.end));
|
||||
}
|
||||
|
||||
static of(obj: vscode.Range): Range {
|
||||
if (obj instanceof Range) {
|
||||
return obj;
|
||||
}
|
||||
if (this.isRange(obj)) {
|
||||
return new Range(obj.start, obj.end);
|
||||
}
|
||||
throw new Error('Invalid argument, is NOT a range-like object');
|
||||
}
|
||||
|
||||
protected _start: Position;
|
||||
protected _end: Position;
|
||||
|
||||
|
@ -256,18 +275,19 @@ export class Range {
|
|||
return this._end;
|
||||
}
|
||||
|
||||
constructor(start: vscode.Position, end: vscode.Position);
|
||||
constructor(start: Position, end: Position);
|
||||
constructor(startLine: number, startColumn: number, endLine: number, endColumn: number);
|
||||
constructor(startLineOrStart: number | Position, startColumnOrEnd: number | Position, endLine?: number, endColumn?: number) {
|
||||
constructor(startLineOrStart: number | Position | vscode.Position, startColumnOrEnd: number | Position | vscode.Position, endLine?: number, endColumn?: number) {
|
||||
let start: Position | undefined;
|
||||
let end: Position | undefined;
|
||||
|
||||
if (typeof startLineOrStart === 'number' && typeof startColumnOrEnd === 'number' && typeof endLine === 'number' && typeof endColumn === 'number') {
|
||||
start = new Position(startLineOrStart, startColumnOrEnd);
|
||||
end = new Position(endLine, endColumn);
|
||||
} else if (startLineOrStart instanceof Position && startColumnOrEnd instanceof Position) {
|
||||
start = startLineOrStart;
|
||||
end = startColumnOrEnd;
|
||||
} else if (Position.isPosition(startLineOrStart) && Position.isPosition(startColumnOrEnd)) {
|
||||
start = Position.of(startLineOrStart);
|
||||
end = Position.of(startColumnOrEnd);
|
||||
}
|
||||
|
||||
if (!start || !end) {
|
||||
|
@ -284,12 +304,12 @@ export class Range {
|
|||
}
|
||||
|
||||
contains(positionOrRange: Position | Range): boolean {
|
||||
if (positionOrRange instanceof Range) {
|
||||
return this.contains(positionOrRange._start)
|
||||
&& this.contains(positionOrRange._end);
|
||||
if (Range.isRange(positionOrRange)) {
|
||||
return this.contains(positionOrRange.start)
|
||||
&& this.contains(positionOrRange.end);
|
||||
|
||||
} else if (positionOrRange instanceof Position) {
|
||||
if (positionOrRange.isBefore(this._start)) {
|
||||
} else if (Position.isPosition(positionOrRange)) {
|
||||
if (Position.of(positionOrRange).isBefore(this._start)) {
|
||||
return false;
|
||||
}
|
||||
if (this._end.isBefore(positionOrRange)) {
|
||||
|
@ -403,9 +423,9 @@ export class Selection extends Range {
|
|||
if (typeof anchorLineOrAnchor === 'number' && typeof anchorColumnOrActive === 'number' && typeof activeLine === 'number' && typeof activeColumn === 'number') {
|
||||
anchor = new Position(anchorLineOrAnchor, anchorColumnOrActive);
|
||||
active = new Position(activeLine, activeColumn);
|
||||
} else if (anchorLineOrAnchor instanceof Position && anchorColumnOrActive instanceof Position) {
|
||||
anchor = anchorLineOrAnchor;
|
||||
active = anchorColumnOrActive;
|
||||
} else if (Position.isPosition(anchorLineOrAnchor) && Position.isPosition(anchorColumnOrActive)) {
|
||||
anchor = Position.of(anchorLineOrAnchor);
|
||||
active = Position.of(anchorColumnOrActive);
|
||||
}
|
||||
|
||||
if (!anchor || !active) {
|
||||
|
@ -893,9 +913,9 @@ export class Location {
|
|||
|
||||
if (!rangeOrPosition) {
|
||||
//that's OK
|
||||
} else if (rangeOrPosition instanceof Range) {
|
||||
this.range = rangeOrPosition;
|
||||
} else if (rangeOrPosition instanceof Position) {
|
||||
} else if (Range.isRange(rangeOrPosition)) {
|
||||
this.range = Range.of(rangeOrPosition);
|
||||
} else if (Position.isPosition(rangeOrPosition)) {
|
||||
this.range = new Range(rangeOrPosition, rangeOrPosition);
|
||||
} else {
|
||||
throw new Error('Illegal argument');
|
||||
|
@ -2331,7 +2351,7 @@ export enum TreeItemCollapsibleState {
|
|||
@es5ClassCompat
|
||||
export class TreeDataTransferItem {
|
||||
async asString(): Promise<string> {
|
||||
return JSON.stringify(this.value);
|
||||
return typeof this.value === 'string' ? this.value : JSON.stringify(this.value);
|
||||
}
|
||||
|
||||
constructor(public readonly value: any) { }
|
||||
|
|
|
@ -92,7 +92,7 @@ export function deserializeWebviewMessage(jsonMessage: string, buffers: VSBuffer
|
|||
});
|
||||
|
||||
const reviver = !buffers.length ? undefined : (_key: string, value: any) => {
|
||||
if (typeof value === 'object' && (value as extHostProtocol.WebviewMessageArrayBufferReference).$$vscode_array_buffer_reference$$) {
|
||||
if (value && typeof value === 'object' && (value as extHostProtocol.WebviewMessageArrayBufferReference).$$vscode_array_buffer_reference$$) {
|
||||
const ref = value as extHostProtocol.WebviewMessageArrayBufferReference;
|
||||
const { index } = ref;
|
||||
const arrayBuffer = arrayBuffers[index];
|
||||
|
|
|
@ -251,6 +251,18 @@ suite('ExtHostTypes', function () {
|
|||
assert.ok(!range.contains(new types.Range(1, 1, 3, 11)));
|
||||
});
|
||||
|
||||
test('Range, contains (no instanceof)', function () {
|
||||
let range = new types.Range(1, 1, 2, 11);
|
||||
|
||||
let startLike = { line: range.start.line, character: range.start.character };
|
||||
let endLike = { line: range.end.line, character: range.end.character };
|
||||
let rangeLike = { start: startLike, end: endLike };
|
||||
|
||||
assert.ok(range.contains((<types.Position>startLike)));
|
||||
assert.ok(range.contains((<types.Position>endLike)));
|
||||
assert.ok(range.contains((<types.Range>rangeLike)));
|
||||
});
|
||||
|
||||
test('Range, intersection', function () {
|
||||
let range = new types.Range(1, 1, 2, 11);
|
||||
let res: types.Range;
|
||||
|
|
|
@ -157,12 +157,13 @@ function createDraggedEditorInputFromRawResourcesData(rawResourcesData: string |
|
|||
|
||||
export async function extractTreeDropData(dataTransfer: ITreeDataTransfer): Promise<Array<IDraggedResourceEditorInput>> {
|
||||
const editors: IDraggedResourceEditorInput[] = [];
|
||||
const resourcesKey = DataTransfers.RESOURCES.toLowerCase();
|
||||
const resourcesKey = Mimes.uriList.toLowerCase();
|
||||
|
||||
// Data Transfer: Resources
|
||||
if (dataTransfer.has(resourcesKey)) {
|
||||
try {
|
||||
const rawResourcesData = await dataTransfer.get(resourcesKey)?.asString();
|
||||
const asString = await dataTransfer.get(resourcesKey)?.asString();
|
||||
const rawResourcesData = JSON.stringify(asString?.split('\\n').filter(value => !value.startsWith('#')));
|
||||
editors.push(...createDraggedEditorInputFromRawResourcesData(rawResourcesData));
|
||||
} catch (error) {
|
||||
// Invalid transfer
|
||||
|
|
|
@ -352,6 +352,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
|||
const newPositionValue = (position === Position.LEFT) ? 'left' : 'right';
|
||||
const oldPositionValue = (position === Position.RIGHT) ? 'left' : 'right';
|
||||
const panelAlignment = this.getPanelAlignment();
|
||||
const panelPosition = this.getPanelPosition();
|
||||
|
||||
this.stateModel.setRuntimeValue(LayoutStateKeys.SIDEBAR_POSITON, position);
|
||||
|
||||
|
@ -374,7 +375,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
|||
auxiliaryBar.updateStyles();
|
||||
|
||||
// Move activity bar, side bar, and side panel
|
||||
this.adjustPartPositions(position, panelAlignment);
|
||||
this.adjustPartPositions(position, panelAlignment, panelPosition);
|
||||
}
|
||||
|
||||
private updateWindowBorder(skipLayout: boolean = false) {
|
||||
|
@ -1464,31 +1465,43 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
|||
return viewContainerModel.activeViewDescriptors.length >= 1;
|
||||
}
|
||||
|
||||
private adjustPartPositions(sideBarPosition: Position, panelAlignment: PanelAlignment): void {
|
||||
private adjustPartPositions(sideBarPosition: Position, panelAlignment: PanelAlignment, panelPosition: Position): void {
|
||||
// Move activity bar, side bar, and side panel
|
||||
const sideBarNextToEditor = !(panelAlignment === 'center' || (sideBarPosition === Position.LEFT && panelAlignment === 'right') || (sideBarPosition === Position.RIGHT && panelAlignment === 'left'));
|
||||
const auxiliaryBarNextToEditor = !(panelAlignment === 'center' || (sideBarPosition === Position.RIGHT && panelAlignment === 'right') || (sideBarPosition === Position.LEFT && panelAlignment === 'left'));
|
||||
const sideBarSiblingToEditor = panelPosition !== Position.BOTTOM || !(panelAlignment === 'center' || (sideBarPosition === Position.LEFT && panelAlignment === 'right') || (sideBarPosition === Position.RIGHT && panelAlignment === 'left'));
|
||||
const auxiliaryBarSiblingToEditor = panelPosition !== Position.BOTTOM || !(panelAlignment === 'center' || (sideBarPosition === Position.RIGHT && panelAlignment === 'right') || (sideBarPosition === Position.LEFT && panelAlignment === 'left'));
|
||||
const preMovePanelWidth = !this.isVisible(Parts.PANEL_PART) ? Sizing.Invisible(this.workbenchGrid.getViewCachedVisibleSize(this.panelPartView) ?? this.panelPartView.minimumWidth) : this.workbenchGrid.getViewSize(this.panelPartView).width;
|
||||
const preMovePanelHeight = !this.isVisible(Parts.PANEL_PART) ? Sizing.Invisible(this.workbenchGrid.getViewCachedVisibleSize(this.panelPartView) ?? this.panelPartView.minimumHeight) : this.workbenchGrid.getViewSize(this.panelPartView).height;
|
||||
const preMoveSideBarSize = !this.isVisible(Parts.SIDEBAR_PART) ? Sizing.Invisible(this.workbenchGrid.getViewCachedVisibleSize(this.sideBarPartView) ?? this.sideBarPartView.minimumWidth) : this.workbenchGrid.getViewSize(this.sideBarPartView).width;
|
||||
const preMoveAuxiliaryBarSize = !this.isVisible(Parts.AUXILIARYBAR_PART) ? Sizing.Invisible(this.workbenchGrid.getViewCachedVisibleSize(this.auxiliaryBarPartView) ?? this.auxiliaryBarPartView.minimumWidth) : this.workbenchGrid.getViewSize(this.auxiliaryBarPartView).width;
|
||||
|
||||
if (sideBarPosition === Position.LEFT) {
|
||||
this.workbenchGrid.moveViewTo(this.activityBarPartView, [2, 0]);
|
||||
this.workbenchGrid.moveView(this.sideBarPartView, preMoveSideBarSize, sideBarNextToEditor ? this.editorPartView : this.activityBarPartView, sideBarNextToEditor ? Direction.Left : Direction.Right);
|
||||
if (auxiliaryBarNextToEditor) {
|
||||
this.workbenchGrid.moveView(this.sideBarPartView, preMoveSideBarSize, sideBarSiblingToEditor ? this.editorPartView : this.activityBarPartView, sideBarSiblingToEditor ? Direction.Left : Direction.Right);
|
||||
if (auxiliaryBarSiblingToEditor) {
|
||||
this.workbenchGrid.moveView(this.auxiliaryBarPartView, preMoveAuxiliaryBarSize, this.editorPartView, Direction.Right);
|
||||
} else {
|
||||
this.workbenchGrid.moveViewTo(this.auxiliaryBarPartView, [2, -1]);
|
||||
}
|
||||
} else {
|
||||
this.workbenchGrid.moveViewTo(this.activityBarPartView, [2, -1]);
|
||||
this.workbenchGrid.moveView(this.sideBarPartView, preMoveSideBarSize, sideBarNextToEditor ? this.editorPartView : this.activityBarPartView, sideBarNextToEditor ? Direction.Right : Direction.Left);
|
||||
if (auxiliaryBarNextToEditor) {
|
||||
this.workbenchGrid.moveView(this.sideBarPartView, preMoveSideBarSize, sideBarSiblingToEditor ? this.editorPartView : this.activityBarPartView, sideBarSiblingToEditor ? Direction.Right : Direction.Left);
|
||||
if (auxiliaryBarSiblingToEditor) {
|
||||
this.workbenchGrid.moveView(this.auxiliaryBarPartView, preMoveAuxiliaryBarSize, this.editorPartView, Direction.Left);
|
||||
} else {
|
||||
this.workbenchGrid.moveViewTo(this.auxiliaryBarPartView, [2, 0]);
|
||||
}
|
||||
}
|
||||
|
||||
// We moved all the side parts based on the editor and ignored the panel
|
||||
// Now, we need to put the panel back in the right position when it is next to the editor
|
||||
if (panelPosition !== Position.BOTTOM) {
|
||||
this.workbenchGrid.moveView(this.panelPartView, preMovePanelWidth, this.editorPartView, panelPosition === Position.LEFT ? Direction.Left : Direction.Right);
|
||||
this.workbenchGrid.resizeView(this.panelPartView, {
|
||||
height: preMovePanelHeight as number,
|
||||
width: preMovePanelWidth as number
|
||||
});
|
||||
}
|
||||
|
||||
// Moving views in the grid can cause them to re-distribute sizing unnecessarily
|
||||
// Resize visible parts to the width they were before the operation
|
||||
if (this.isVisible(Parts.SIDEBAR_PART)) {
|
||||
|
@ -1519,7 +1532,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
|||
|
||||
this.stateModel.setRuntimeValue(LayoutStateKeys.PANEL_ALIGNMENT, alignment);
|
||||
|
||||
this.adjustPartPositions(this.getSideBarPosition(), alignment);
|
||||
this.adjustPartPositions(this.getSideBarPosition(), alignment, this.getPanelPosition());
|
||||
|
||||
this._onDidChangePanelAlignment.fire(alignment);
|
||||
}
|
||||
|
@ -1627,7 +1640,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
|||
*/
|
||||
private panelOpensMaximized(): boolean {
|
||||
// the workbench grid currently prevents us from supporting panel maximization with non-center panel alignment
|
||||
if (this.getPanelAlignment() !== 'center') {
|
||||
if (this.getPanelAlignment() !== 'center' && this.getPanelPosition() === Position.BOTTOM) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1760,8 +1773,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
|||
const oldPositionValue = positionToString(this.getPanelPosition());
|
||||
const newPositionValue = positionToString(position);
|
||||
|
||||
this.stateModel.setRuntimeValue(LayoutStateKeys.PANEL_POSITION, position);
|
||||
|
||||
// Adjust CSS
|
||||
const panelContainer = assertIsDefined(panelPart.getContainer());
|
||||
panelContainer.classList.remove(oldPositionValue);
|
||||
|
@ -1775,7 +1786,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
|||
const sideBarSize = this.workbenchGrid.getViewSize(this.sideBarPartView);
|
||||
const auxiliaryBarSize = this.workbenchGrid.getViewSize(this.auxiliaryBarPartView);
|
||||
|
||||
const editorHidden = !this.isVisible(Parts.EDITOR_PART);
|
||||
let editorHidden = !this.isVisible(Parts.EDITOR_PART);
|
||||
|
||||
// Save last non-maximized size for panel before move
|
||||
if (newPositionValue !== oldPositionValue && !editorHidden) {
|
||||
|
@ -1790,6 +1801,13 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
|||
}
|
||||
}
|
||||
|
||||
if (position === Position.BOTTOM && this.getPanelAlignment() !== 'center' && editorHidden) {
|
||||
this.toggleMaximizedPanel();
|
||||
editorHidden = false;
|
||||
}
|
||||
|
||||
this.stateModel.setRuntimeValue(LayoutStateKeys.PANEL_POSITION, position);
|
||||
|
||||
const sideBarVisible = this.isVisible(Parts.SIDEBAR_PART);
|
||||
const auxiliaryBarVisible = this.isVisible(Parts.AUXILIARYBAR_PART);
|
||||
|
||||
|
@ -1812,6 +1830,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
|||
this.setAuxiliaryBarHidden(true);
|
||||
}
|
||||
|
||||
if (position === Position.BOTTOM) {
|
||||
this.adjustPartPositions(this.getSideBarPosition(), this.getPanelAlignment(), position);
|
||||
}
|
||||
|
||||
this._onDidChangePanelPosition.fire(newPositionValue);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import { IAction, Separator, toAction } from 'vs/base/common/actions';
|
|||
import { ToggleAuxiliaryBarAction } from 'vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { MoveSidePanelToPanelAction } from 'vs/workbench/browser/parts/panel/panelActions';
|
||||
import { LayoutPriority } from 'vs/base/browser/ui/splitview/splitview';
|
||||
|
||||
export class AuxiliaryBarPart extends BasePanelPart {
|
||||
static readonly activePanelSettingsKey = 'workbench.auxiliarybar.activepanelid';
|
||||
|
@ -39,6 +40,8 @@ export class AuxiliaryBarPart extends BasePanelPart {
|
|||
override readonly minimumHeight: number = 0;
|
||||
override readonly maximumHeight: number = Number.POSITIVE_INFINITY;
|
||||
|
||||
readonly priority: LayoutPriority = LayoutPriority.Low;
|
||||
|
||||
constructor(
|
||||
@INotificationService notificationService: INotificationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
|
|
|
@ -153,6 +153,7 @@ export abstract class BaseTextEditor<T extends IEditorViewState> extends Abstrac
|
|||
switch (e.source) {
|
||||
case TextEditorSelectionSource.PROGRAMMATIC: return EditorPaneSelectionChangeReason.PROGRAMMATIC;
|
||||
case TextEditorSelectionSource.NAVIGATION: return EditorPaneSelectionChangeReason.NAVIGATION;
|
||||
case TextEditorSelectionSource.JUMP: return EditorPaneSelectionChangeReason.JUMP;
|
||||
default: return EditorPaneSelectionChangeReason.USER;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar';
|
||||
import { isStatusbarEntryLocation, IStatusbarEntryLocation, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar';
|
||||
import { hide, show, isAncestor } from 'vs/base/browser/dom';
|
||||
import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
@ -32,34 +32,6 @@ export interface IStatusbarEntryPriority {
|
|||
readonly secondary: number;
|
||||
}
|
||||
|
||||
export interface IStatusbarEntryLocation {
|
||||
|
||||
/**
|
||||
* The identifier of another status bar entry to
|
||||
* position relative to.
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The alignment of the status bar entry relative
|
||||
* to the referenced entry.
|
||||
*/
|
||||
alignment: StatusbarAlignment;
|
||||
|
||||
/**
|
||||
* Whether to move the entry close to the location
|
||||
* so that it appears as if both this entry and
|
||||
* the location belong to each other.
|
||||
*/
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
export function isStatusbarEntryLocation(thing: unknown): thing is IStatusbarEntryLocation {
|
||||
const candidate = thing as IStatusbarEntryLocation | undefined;
|
||||
|
||||
return typeof candidate?.id === 'string' && typeof candidate.alignment === 'number';
|
||||
}
|
||||
|
||||
export interface IStatusbarViewModelEntry {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
|
|
|
@ -9,7 +9,7 @@ import { DisposableStore, dispose, IDisposable, MutableDisposable, toDisposable
|
|||
import { Part } from 'vs/workbench/browser/part';
|
||||
import { EventType as TouchEventType, Gesture, GestureEvent } from 'vs/base/browser/touch';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { StatusbarAlignment, IStatusbarService, IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarStyleOverride } from 'vs/workbench/services/statusbar/browser/statusbar';
|
||||
import { StatusbarAlignment, IStatusbarService, IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarStyleOverride, isStatusbarEntryLocation, IStatusbarEntryLocation } from 'vs/workbench/services/statusbar/browser/statusbar';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IAction, Separator, toAction } from 'vs/base/common/actions';
|
||||
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
|
@ -31,7 +31,7 @@ import { IHoverService } from 'vs/workbench/services/hover/browser/hover';
|
|||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IHoverDelegate, IHoverDelegateOptions, IHoverWidget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
|
||||
import { HideStatusbarEntryAction, ToggleStatusbarEntryVisibilityAction } from 'vs/workbench/browser/parts/statusbar/statusbarActions';
|
||||
import { IStatusbarEntryPriority, IStatusbarEntryLocation, IStatusbarViewModelEntry, StatusbarViewModel, isStatusbarEntryLocation } from 'vs/workbench/browser/parts/statusbar/statusbarModel';
|
||||
import { IStatusbarEntryPriority, IStatusbarViewModelEntry, StatusbarViewModel } from 'vs/workbench/browser/parts/statusbar/statusbarModel';
|
||||
import { StatusbarEntryItem } from 'vs/workbench/browser/parts/statusbar/statusbarItem';
|
||||
import { StatusBarFocused } from 'vs/workbench/common/contextkeys';
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ import { isString } from 'vs/base/common/types';
|
|||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list';
|
||||
import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeDragOverReaction, TreeDragOverBubble } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IDragAndDropData } from 'vs/base/browser/dnd';
|
||||
import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd';
|
||||
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
|
||||
import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults';
|
||||
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
|
||||
|
@ -62,6 +62,7 @@ import { Schemas } from 'vs/base/common/network';
|
|||
import { ITreeViewsDragAndDropService } from 'vs/workbench/services/views/common/treeViewsDragAndDropService';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Mimes } from 'vs/base/common/mime';
|
||||
|
||||
export class TreeViewPane extends ViewPane {
|
||||
|
||||
|
@ -1266,7 +1267,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
|
|||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ITreeViewsDragAndDropService private readonly treeViewsDragAndDropService: ITreeViewsDragAndDropService<ITreeDataTransfer>,
|
||||
@ILogService private readonly logService: ILogService) {
|
||||
this.treeMimeType = `tree/${treeId.toLowerCase()}`;
|
||||
this.treeMimeType = `application/vnd.code.tree.${treeId.toLowerCase()}`;
|
||||
}
|
||||
|
||||
private dndController: ITreeViewDragAndDropController | undefined;
|
||||
|
@ -1283,7 +1284,11 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
|
|||
this.dragCancellationToken = new CancellationTokenSource();
|
||||
this.treeViewsDragAndDropService.addDragOperationTransfer(uuid, this.dndController.handleDrag(itemHandles, uuid, this.dragCancellationToken.token));
|
||||
originalEvent.dataTransfer.setData(TREE_DRAG_UUID_MIME, uuid);
|
||||
this.treeItemsTransfer.setData([new DraggedTreeItemsIdentifier(uuid)], DraggedTreeItemsIdentifier.prototype);
|
||||
if (this.dndController.dragMimeTypes.find((element) => element === Mimes.uriList)) {
|
||||
this.treeItemsTransfer.setData([new DraggedTreeItemsIdentifier(uuid)], DraggedTreeItemsIdentifier.prototype);
|
||||
// Add the type that the editor knows
|
||||
originalEvent.dataTransfer?.setData(DataTransfers.RESOURCES, '');
|
||||
}
|
||||
this.dndController.dragMimeTypes.forEach(supportedType => {
|
||||
originalEvent.dataTransfer?.setData(supportedType, '');
|
||||
});
|
||||
|
|
|
@ -223,10 +223,19 @@ export const enum EditorPaneSelectionChangeReason {
|
|||
* The selection was changed as a result of a navigation
|
||||
* action.
|
||||
*
|
||||
* For a text editor pane, this for example can be invoking
|
||||
* "Go to definition" on a symbol.
|
||||
* For a text editor pane, this for example can be a result
|
||||
* of selecting an entry from a text outline view.
|
||||
*/
|
||||
NAVIGATION
|
||||
NAVIGATION,
|
||||
|
||||
/**
|
||||
* The selection was changed as a result of a jump action
|
||||
* from within the editor pane.
|
||||
*
|
||||
* For a text editor pane, this for example can be a result
|
||||
* of invoking "Go to definition" from a symbol.
|
||||
*/
|
||||
JUMP
|
||||
}
|
||||
|
||||
export interface IEditorPaneSelection {
|
||||
|
@ -898,34 +907,8 @@ export interface IEditorWillMoveEvent extends IEditorIdentifier {
|
|||
readonly target: GroupIdentifier;
|
||||
}
|
||||
|
||||
export interface IEditorMoveEvent extends IEditorIdentifier {
|
||||
|
||||
/**
|
||||
* The target group of the move operation.
|
||||
*/
|
||||
readonly target: GroupIdentifier;
|
||||
|
||||
/**
|
||||
* The index of the editor before moving.
|
||||
*/
|
||||
readonly index: number;
|
||||
|
||||
/**
|
||||
* The index of the editor after moving.
|
||||
*/
|
||||
readonly newIndex: number;
|
||||
}
|
||||
|
||||
export interface IEditorWillOpenEvent extends IEditorIdentifier { }
|
||||
|
||||
export interface IEditorOpenEvent extends IEditorIdentifier {
|
||||
|
||||
/**
|
||||
* The index the editor opens in.
|
||||
*/
|
||||
readonly index: number;
|
||||
}
|
||||
|
||||
export type GroupIdentifier = number;
|
||||
|
||||
export const enum GroupModelChangeKind {
|
||||
|
|
|
@ -82,20 +82,29 @@ export interface IGroupModelChangeEvent {
|
|||
* access to the editor the event is about.
|
||||
*/
|
||||
readonly editor?: EditorInput;
|
||||
|
||||
/**
|
||||
* Only applies when editors change providing
|
||||
* access to the index of the editor the event
|
||||
* is about.
|
||||
*/
|
||||
readonly editorIndex?: number;
|
||||
}
|
||||
|
||||
export interface IGroupEditorChangeEvent extends IGroupModelChangeEvent {
|
||||
readonly editor: EditorInput;
|
||||
readonly editorIndex: number;
|
||||
}
|
||||
|
||||
export function isGroupEditorChangeEvent(e: IGroupModelChangeEvent): e is IGroupEditorChangeEvent {
|
||||
const candidate = e as IGroupEditorOpenEvent;
|
||||
|
||||
return candidate.editor && candidate.editorIndex !== undefined;
|
||||
}
|
||||
|
||||
export interface IGroupEditorOpenEvent extends IGroupEditorChangeEvent {
|
||||
|
||||
readonly kind: GroupModelChangeKind.EDITOR_OPEN;
|
||||
|
||||
/**
|
||||
* Identifies the index of the editor in the group.
|
||||
*/
|
||||
readonly editorIndex: number;
|
||||
}
|
||||
|
||||
export function isGroupEditorOpenEvent(e: IGroupModelChangeEvent): e is IGroupEditorOpenEvent {
|
||||
|
@ -108,11 +117,6 @@ export interface IGroupEditorMoveEvent extends IGroupEditorChangeEvent {
|
|||
|
||||
readonly kind: GroupModelChangeKind.EDITOR_MOVE;
|
||||
|
||||
/**
|
||||
* Identifies the index of the editor in the group.
|
||||
*/
|
||||
readonly editorIndex: number;
|
||||
|
||||
/**
|
||||
* Signifies the index the editor is moving from.
|
||||
* `editorIndex` will contain the index the editor
|
||||
|
@ -131,11 +135,6 @@ export interface IGroupEditorCloseEvent extends IGroupEditorChangeEvent {
|
|||
|
||||
readonly kind: GroupModelChangeKind.EDITOR_CLOSE;
|
||||
|
||||
/**
|
||||
* Identifies the index of the editor in the group.
|
||||
*/
|
||||
readonly editorIndex: number;
|
||||
|
||||
/**
|
||||
* Signifies the context in which the editor
|
||||
* is being closed. This allows for understanding
|
||||
|
@ -359,7 +358,7 @@ export class EditorGroupModel extends Disposable {
|
|||
|
||||
// Handle active
|
||||
if (makeActive) {
|
||||
this.doSetActive(newEditor);
|
||||
this.doSetActive(newEditor, targetIndex);
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -370,16 +369,16 @@ export class EditorGroupModel extends Disposable {
|
|||
|
||||
// Existing editor
|
||||
else {
|
||||
const [existingEditor] = existingEditorAndIndex;
|
||||
const [existingEditor, existingEditorIndex] = existingEditorAndIndex;
|
||||
|
||||
// Pin it
|
||||
if (makePinned) {
|
||||
this.doPin(existingEditor);
|
||||
this.doPin(existingEditor, existingEditorIndex);
|
||||
}
|
||||
|
||||
// Activate it
|
||||
if (makeActive) {
|
||||
this.doSetActive(existingEditor);
|
||||
this.doSetActive(existingEditor, existingEditorIndex);
|
||||
}
|
||||
|
||||
// Respect index
|
||||
|
@ -405,36 +404,45 @@ export class EditorGroupModel extends Disposable {
|
|||
|
||||
// Re-emit disposal of editor input as our own event
|
||||
listeners.add(Event.once(editor.onWillDispose)(() => {
|
||||
if (this.indexOf(editor) >= 0) {
|
||||
this._onDidModelChange.fire({
|
||||
const editorIndex = this.editors.indexOf(editor);
|
||||
if (editorIndex >= 0) {
|
||||
const event: IGroupEditorChangeEvent = {
|
||||
kind: GroupModelChangeKind.EDITOR_WILL_DISPOSE,
|
||||
editor
|
||||
});
|
||||
editor,
|
||||
editorIndex
|
||||
};
|
||||
this._onDidModelChange.fire(event);
|
||||
}
|
||||
}));
|
||||
|
||||
// Re-Emit dirty state changes
|
||||
listeners.add(editor.onDidChangeDirty(() => {
|
||||
this._onDidModelChange.fire({
|
||||
const event: IGroupEditorChangeEvent = {
|
||||
kind: GroupModelChangeKind.EDITOR_DIRTY,
|
||||
editor
|
||||
});
|
||||
editor,
|
||||
editorIndex: this.editors.indexOf(editor)
|
||||
};
|
||||
this._onDidModelChange.fire(event);
|
||||
}));
|
||||
|
||||
// Re-Emit label changes
|
||||
listeners.add(editor.onDidChangeLabel(() => {
|
||||
this._onDidModelChange.fire({
|
||||
const event: IGroupEditorChangeEvent = {
|
||||
kind: GroupModelChangeKind.EDITOR_LABEL,
|
||||
editor
|
||||
});
|
||||
editor,
|
||||
editorIndex: this.editors.indexOf(editor)
|
||||
};
|
||||
this._onDidModelChange.fire(event);
|
||||
}));
|
||||
|
||||
// Re-Emit capability changes
|
||||
listeners.add(editor.onDidChangeCapabilities(() => {
|
||||
this._onDidModelChange.fire({
|
||||
const event: IGroupEditorChangeEvent = {
|
||||
kind: GroupModelChangeKind.EDITOR_CAPABILITIES,
|
||||
editor
|
||||
});
|
||||
editor,
|
||||
editorIndex: this.editors.indexOf(editor)
|
||||
};
|
||||
this._onDidModelChange.fire(event);
|
||||
}));
|
||||
|
||||
// Clean up dispose listeners once the editor gets closed
|
||||
|
@ -503,7 +511,7 @@ export class EditorGroupModel extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
this.doSetActive(newActive);
|
||||
this.doSetActive(newActive, this.editors.indexOf(newActive));
|
||||
}
|
||||
|
||||
// One Editor
|
||||
|
@ -592,14 +600,14 @@ export class EditorGroupModel extends Disposable {
|
|||
return; // not found
|
||||
}
|
||||
|
||||
const [editor] = res;
|
||||
const [editor, editorIndex] = res;
|
||||
|
||||
this.doSetActive(editor);
|
||||
this.doSetActive(editor, editorIndex);
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
private doSetActive(editor: EditorInput): void {
|
||||
private doSetActive(editor: EditorInput, editorIndex: number): void {
|
||||
if (this.matches(this.active, editor)) {
|
||||
return; // already active
|
||||
}
|
||||
|
@ -612,10 +620,12 @@ export class EditorGroupModel extends Disposable {
|
|||
this.mru.unshift(editor);
|
||||
|
||||
// Event
|
||||
this._onDidModelChange.fire({
|
||||
const event: IGroupEditorChangeEvent = {
|
||||
kind: GroupModelChangeKind.EDITOR_ACTIVE,
|
||||
editor
|
||||
});
|
||||
editor,
|
||||
editorIndex
|
||||
};
|
||||
this._onDidModelChange.fire(event);
|
||||
}
|
||||
|
||||
setIndex(index: number) {
|
||||
|
@ -632,14 +642,14 @@ export class EditorGroupModel extends Disposable {
|
|||
return; // not found
|
||||
}
|
||||
|
||||
const [editor] = res;
|
||||
const [editor, editorIndex] = res;
|
||||
|
||||
this.doPin(editor);
|
||||
this.doPin(editor, editorIndex);
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
private doPin(editor: EditorInput): void {
|
||||
private doPin(editor: EditorInput, editorIndex: number): void {
|
||||
if (this.isPinned(editor)) {
|
||||
return; // can only pin a preview editor
|
||||
}
|
||||
|
@ -648,10 +658,12 @@ export class EditorGroupModel extends Disposable {
|
|||
this.preview = null;
|
||||
|
||||
// Event
|
||||
this._onDidModelChange.fire({
|
||||
const event: IGroupEditorChangeEvent = {
|
||||
kind: GroupModelChangeKind.EDITOR_PIN,
|
||||
editor
|
||||
});
|
||||
editor,
|
||||
editorIndex
|
||||
};
|
||||
this._onDidModelChange.fire(event);
|
||||
}
|
||||
|
||||
unpin(candidate: EditorInput): EditorInput | undefined {
|
||||
|
@ -660,14 +672,14 @@ export class EditorGroupModel extends Disposable {
|
|||
return; // not found
|
||||
}
|
||||
|
||||
const [editor] = res;
|
||||
const [editor, editorIndex] = res;
|
||||
|
||||
this.doUnpin(editor);
|
||||
this.doUnpin(editor, editorIndex);
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
private doUnpin(editor: EditorInput): void {
|
||||
private doUnpin(editor: EditorInput, editorIndex: number): void {
|
||||
if (!this.isPinned(editor)) {
|
||||
return; // can only unpin a pinned editor
|
||||
}
|
||||
|
@ -677,10 +689,12 @@ export class EditorGroupModel extends Disposable {
|
|||
this.preview = editor;
|
||||
|
||||
// Event
|
||||
this._onDidModelChange.fire({
|
||||
const event: IGroupEditorChangeEvent = {
|
||||
kind: GroupModelChangeKind.EDITOR_PIN,
|
||||
editor
|
||||
});
|
||||
editor,
|
||||
editorIndex
|
||||
};
|
||||
this._onDidModelChange.fire(event);
|
||||
|
||||
// Close old preview editor if any
|
||||
if (oldPreview) {
|
||||
|
@ -705,15 +719,15 @@ export class EditorGroupModel extends Disposable {
|
|||
return; // not found
|
||||
}
|
||||
|
||||
const [editor, index] = res;
|
||||
const [editor, editorIndex] = res;
|
||||
|
||||
this.doStick(editor, index);
|
||||
this.doStick(editor, editorIndex);
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
private doStick(editor: EditorInput, index: number): void {
|
||||
if (this.isSticky(index)) {
|
||||
private doStick(editor: EditorInput, editorIndex: number): void {
|
||||
if (this.isSticky(editorIndex)) {
|
||||
return; // can only stick a non-sticky editor
|
||||
}
|
||||
|
||||
|
@ -727,10 +741,12 @@ export class EditorGroupModel extends Disposable {
|
|||
this.sticky++;
|
||||
|
||||
// Event
|
||||
this._onDidModelChange.fire({
|
||||
const event: IGroupEditorChangeEvent = {
|
||||
kind: GroupModelChangeKind.EDITOR_STICKY,
|
||||
editor
|
||||
});
|
||||
editor,
|
||||
editorIndex
|
||||
};
|
||||
this._onDidModelChange.fire(event);
|
||||
}
|
||||
|
||||
unstick(candidate: EditorInput): EditorInput | undefined {
|
||||
|
@ -739,15 +755,15 @@ export class EditorGroupModel extends Disposable {
|
|||
return; // not found
|
||||
}
|
||||
|
||||
const [editor, index] = res;
|
||||
const [editor, editorIndex] = res;
|
||||
|
||||
this.doUnstick(editor, index);
|
||||
this.doUnstick(editor, editorIndex);
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
private doUnstick(editor: EditorInput, index: number): void {
|
||||
if (!this.isSticky(index)) {
|
||||
private doUnstick(editor: EditorInput, editorIndex: number): void {
|
||||
if (!this.isSticky(editorIndex)) {
|
||||
return; // can only unstick a sticky editor
|
||||
}
|
||||
|
||||
|
@ -758,10 +774,12 @@ export class EditorGroupModel extends Disposable {
|
|||
this.sticky--;
|
||||
|
||||
// Event
|
||||
this._onDidModelChange.fire({
|
||||
const event: IGroupEditorChangeEvent = {
|
||||
kind: GroupModelChangeKind.EDITOR_STICKY,
|
||||
editor
|
||||
});
|
||||
editor,
|
||||
editorIndex
|
||||
};
|
||||
this._onDidModelChange.fire(event);
|
||||
}
|
||||
|
||||
isSticky(candidateOrIndex: EditorInput | number): boolean {
|
||||
|
@ -836,21 +854,22 @@ export class EditorGroupModel extends Disposable {
|
|||
|
||||
indexOf(candidate: EditorInput | null, editors = this.editors, options?: IMatchEditorOptions): number {
|
||||
let index = -1;
|
||||
if (!candidate) {
|
||||
return index;
|
||||
}
|
||||
|
||||
if (candidate) {
|
||||
for (let i = 0; i < editors.length; i++) {
|
||||
const editor = editors[i];
|
||||
for (let i = 0; i < editors.length; i++) {
|
||||
const editor = editors[i];
|
||||
|
||||
if (this.matches(editor, candidate, options)) {
|
||||
// If we are to support side by side matching, it is possible that
|
||||
// a better direct match is found later. As such, we continue finding
|
||||
// a matching editor and prefer that match over the side by side one.
|
||||
if (options?.supportSideBySide && editor instanceof SideBySideEditorInput && !(candidate instanceof SideBySideEditorInput)) {
|
||||
index = i;
|
||||
} else {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
if (this.matches(editor, candidate, options)) {
|
||||
// If we are to support side by side matching, it is possible that
|
||||
// a better direct match is found later. As such, we continue finding
|
||||
// a matching editor and prefer that match over the side by side one.
|
||||
if (options?.supportSideBySide && editor instanceof SideBySideEditorInput && !(candidate instanceof SideBySideEditorInput)) {
|
||||
index = i;
|
||||
} else {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,10 +25,11 @@ export function applyTextEditorOptions(options: ITextEditorOptions, editor: IEdi
|
|||
endColumn: options.selection.endColumn ?? options.selection.startColumn
|
||||
};
|
||||
|
||||
// Apply selection and give it a `TextEditorSelectionSource`
|
||||
// so that listeners can distinguish this selection change
|
||||
// from others.
|
||||
editor.setSelection(range, TextEditorSelectionSource.NAVIGATION);
|
||||
// Apply selection with a source so that listeners can
|
||||
// distinguish this selection change from others.
|
||||
// If no source is provided, set a default source to
|
||||
// signal this navigation.
|
||||
editor.setSelection(range, options.selectionSource ?? TextEditorSelectionSource.NAVIGATION);
|
||||
|
||||
// Reveal selection
|
||||
if (options.selectionRevealType === TextEditorSelectionRevealType.NearTop) {
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IDebugService } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { raceTimeout } from 'vs/base/common/async';
|
||||
|
@ -15,17 +15,18 @@ import { FileAccess } from 'vs/base/common/network';
|
|||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
import { FoldingController } from 'vs/editor/contrib/folding/browser/folding';
|
||||
import { FoldingModel } from 'vs/editor/contrib/folding/browser/foldingModel';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { autorunDelta, constObservable, debouncedObservable, fromEvent, fromPromise, IObservable, LazyDerived, wasEventTriggeredRecently } from 'vs/workbench/contrib/audioCues/browser/observable';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { GhostTextController } from 'vs/editor/contrib/inlineCompletions/browser/ghostTextController';
|
||||
|
||||
export class AudioCueContribution extends DisposableStore implements IWorkbenchContribution {
|
||||
export class AudioCueContribution extends Disposable implements IWorkbenchContribution {
|
||||
private audioCuesEnabled = false;
|
||||
private readonly store = this.add(new DisposableStore());
|
||||
private readonly store = this._register(new DisposableStore());
|
||||
|
||||
constructor(
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
) {
|
||||
|
@ -35,8 +36,8 @@ export class AudioCueContribution extends DisposableStore implements IWorkbenchC
|
|||
this.updateAudioCuesEnabled();
|
||||
});
|
||||
|
||||
this.add(
|
||||
_configurationService.onDidChangeConfiguration((e) => {
|
||||
this._register(
|
||||
configurationService.onDidChangeConfiguration((e) => {
|
||||
if (e.affectsConfiguration('audioCues.enabled')) {
|
||||
this.updateAudioCuesEnabled();
|
||||
}
|
||||
|
@ -47,7 +48,7 @@ export class AudioCueContribution extends DisposableStore implements IWorkbenchC
|
|||
}
|
||||
|
||||
private getAudioCuesEnabled(): boolean {
|
||||
const value = this._configurationService.getValue<'auto' | 'on' | 'off'>('audioCues.enabled');
|
||||
const value = this.configurationService.getValue<'auto' | 'on' | 'off'>('audioCues.enabled');
|
||||
if (value === 'on') {
|
||||
return true;
|
||||
} else if (value === 'auto') {
|
||||
|
@ -81,96 +82,90 @@ export class AudioCueContribution extends DisposableStore implements IWorkbenchC
|
|||
? activeTextEditorControl
|
||||
: undefined;
|
||||
|
||||
if (editor) {
|
||||
this.handleCurrentEditor(editor, store);
|
||||
if (editor && editor.hasModel()) {
|
||||
this.handleCurrentEditor(editor, editor.getModel(), store);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private handleCurrentEditor(editor: ICodeEditor, store: DisposableStore): void {
|
||||
private handleCurrentEditor(editor: ICodeEditor, editorModel: ITextModel, store: DisposableStore): void {
|
||||
const features: Feature[] = [
|
||||
this.instantiationService.createInstance(ErrorFeature),
|
||||
this.instantiationService.createInstance(FoldedAreaFeature),
|
||||
this.instantiationService.createInstance(BreakpointFeature),
|
||||
this.instantiationService.createInstance(InlineCompletionFeature),
|
||||
];
|
||||
|
||||
const featuresPerEditor = new Map(
|
||||
features.map((feature) => [
|
||||
feature,
|
||||
feature.createForEditor(editor, editor.getModel()!.uri),
|
||||
])
|
||||
const observableFeatureStates = features.map((feature) =>
|
||||
feature.getObservableState(editor, editorModel)
|
||||
);
|
||||
|
||||
interface State {
|
||||
lineNumber: number;
|
||||
featureStates: Map<Feature, boolean>;
|
||||
}
|
||||
const curLineNumber = fromEvent(
|
||||
editor.onDidChangeCursorPosition,
|
||||
() => editor.getPosition()?.lineNumber
|
||||
);
|
||||
const debouncedLineNumber = debouncedObservable(curLineNumber, 100, store);
|
||||
|
||||
const computeNewState = (): State | undefined => {
|
||||
if (!editor.hasModel()) {
|
||||
const lineNumberWithObservableFeatures = debouncedLineNumber.map(
|
||||
(lineNumber) => lineNumber === undefined ? undefined : {
|
||||
lineNumber,
|
||||
featureStatesForLine: observableFeatureStates.map(
|
||||
(featureResult) =>
|
||||
// This caches the feature state for the active line
|
||||
new LazyDerived(
|
||||
(reader) => featureResult.read(reader).isActive(lineNumber),
|
||||
'isActiveForLine'
|
||||
)
|
||||
),
|
||||
}
|
||||
);
|
||||
|
||||
const isTyping = wasEventTriggeredRecently(
|
||||
editorModel.onDidChangeContent.bind(editorModel),
|
||||
1000,
|
||||
store
|
||||
);
|
||||
const featureStatesBeforeTyping = isTyping.map((isTyping) =>
|
||||
!isTyping
|
||||
? undefined
|
||||
: lineNumberWithObservableFeatures
|
||||
.get()
|
||||
?.featureStatesForLine?.map((featureState, idx) =>
|
||||
features[idx].debounceWhileTyping ? featureState.get() : undefined
|
||||
)
|
||||
);
|
||||
|
||||
const state = new LazyDerived(reader => {
|
||||
const lineInfo = lineNumberWithObservableFeatures.read(reader);
|
||||
if (lineInfo === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const position = editor.getPosition();
|
||||
|
||||
const lineNumber = position.lineNumber;
|
||||
const featureStates = new Map(
|
||||
features.map((feature) => [
|
||||
feature,
|
||||
featuresPerEditor.get(feature)!.isActive(lineNumber),
|
||||
])
|
||||
);
|
||||
return {
|
||||
lineNumber,
|
||||
featureStates
|
||||
lineNumber: lineInfo.lineNumber,
|
||||
featureStates: new Map(
|
||||
lineInfo.featureStatesForLine.map((featureState, idx) => [
|
||||
features[idx],
|
||||
featureStatesBeforeTyping.read(reader)?.at(idx) ??
|
||||
featureState.read(reader),
|
||||
])
|
||||
),
|
||||
};
|
||||
};
|
||||
}, 'state');
|
||||
|
||||
let lastState: State | undefined;
|
||||
const updateState = () => {
|
||||
const newState = computeNewState();
|
||||
|
||||
for (const feature of features) {
|
||||
if (
|
||||
newState &&
|
||||
newState.featureStates.get(feature) &&
|
||||
(!lastState?.featureStates?.get(feature) ||
|
||||
newState.lineNumber !== lastState.lineNumber)
|
||||
) {
|
||||
this.playSound(feature.audioCueFilename);
|
||||
store.add(
|
||||
autorunDelta(state, ({ lastValue, newValue }) => {
|
||||
for (const feature of features) {
|
||||
if (
|
||||
newValue?.featureStates.get(feature) &&
|
||||
(!lastValue?.featureStates?.get(feature) ||
|
||||
newValue.lineNumber !== lastValue.lineNumber)
|
||||
) {
|
||||
this.playSound(feature.audioCueFilename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastState = newState;
|
||||
};
|
||||
|
||||
for (const feature of featuresPerEditor.values()) {
|
||||
if (feature.onChange) {
|
||||
store.add(feature.onChange(updateState));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let lastLineNumber = -1;
|
||||
store.add(
|
||||
editor.onDidChangeCursorPosition(() => {
|
||||
const position = editor.getPosition();
|
||||
if (!position) {
|
||||
return;
|
||||
}
|
||||
const lineNumber = position.lineNumber;
|
||||
if (lineNumber === lastLineNumber) {
|
||||
return;
|
||||
}
|
||||
lastLineNumber = lineNumber;
|
||||
|
||||
updateState();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
updateState();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private async playSound(fileName: string) {
|
||||
|
@ -197,82 +192,69 @@ export class AudioCueContribution extends DisposableStore implements IWorkbenchC
|
|||
|
||||
interface Feature {
|
||||
audioCueFilename: string;
|
||||
createForEditor(
|
||||
debounceWhileTyping?: boolean;
|
||||
getObservableState(
|
||||
editor: ICodeEditor,
|
||||
uri: URI
|
||||
): FeatureResult;
|
||||
model: ITextModel
|
||||
): IObservable<FeatureState>;
|
||||
}
|
||||
|
||||
interface FeatureResult {
|
||||
interface FeatureState {
|
||||
isActive(lineNumber: number): boolean;
|
||||
onChange?: Event<void>;
|
||||
}
|
||||
|
||||
class ErrorFeature implements Feature {
|
||||
public readonly audioCueFilename = 'error';
|
||||
public readonly debounceWhileTyping = true;
|
||||
|
||||
constructor(@IMarkerService private readonly markerService: IMarkerService) { }
|
||||
|
||||
createForEditor(
|
||||
editor: ICodeEditor,
|
||||
uri: URI
|
||||
): FeatureResult {
|
||||
return {
|
||||
isActive: (lineNumber) => {
|
||||
const hasMarker = this.markerService
|
||||
.read({ resource: uri })
|
||||
.some(
|
||||
(m) =>
|
||||
m.severity === MarkerSeverity.Error &&
|
||||
m.startLineNumber <= lineNumber &&
|
||||
lineNumber <= m.endLineNumber
|
||||
);
|
||||
return hasMarker;
|
||||
},
|
||||
onChange: Event.map(
|
||||
Event.filter(
|
||||
this.markerService.onMarkerChanged,
|
||||
(changedUris) => {
|
||||
const curUri = editor.getModel()?.uri?.toString();
|
||||
return (
|
||||
!!curUri && changedUris.some((u) => u.toString() === curUri)
|
||||
);
|
||||
}
|
||||
),
|
||||
(x) => undefined
|
||||
getObservableState(editor: ICodeEditor, model: ITextModel): IObservable<FeatureState> {
|
||||
return fromEvent(
|
||||
Event.filter(this.markerService.onMarkerChanged, (changedUris) =>
|
||||
changedUris.some((u) => u.toString() === model.uri.toString())
|
||||
),
|
||||
};
|
||||
() => ({
|
||||
isActive: (lineNumber) => {
|
||||
const hasMarker = this.markerService
|
||||
.read({ resource: model.uri })
|
||||
.some(
|
||||
(m) =>
|
||||
m.severity === MarkerSeverity.Error &&
|
||||
m.startLineNumber <= lineNumber &&
|
||||
lineNumber <= m.endLineNumber
|
||||
);
|
||||
return hasMarker;
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FoldedAreaFeature implements Feature {
|
||||
public readonly audioCueFilename = 'foldedAreas';
|
||||
|
||||
createForEditor(
|
||||
editor: ICodeEditor,
|
||||
uri: URI
|
||||
): FeatureResult {
|
||||
const emitter = new Emitter<void>();
|
||||
let foldingModel: FoldingModel | null = null;
|
||||
editor
|
||||
.getContribution<FoldingController>(FoldingController.ID)
|
||||
?.getFoldingModel()
|
||||
?.then((newFoldingModel) => {
|
||||
foldingModel = newFoldingModel;
|
||||
emitter.fire();
|
||||
getObservableState(editor: ICodeEditor, model: ITextModel): IObservable<FeatureState> {
|
||||
const foldingController = FoldingController.get(editor);
|
||||
if (!foldingController) {
|
||||
return constObservable({
|
||||
isActive: () => false,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
isActive: lineNumber => {
|
||||
const regionAtLine = foldingModel?.getRegionAtLine(lineNumber);
|
||||
const foldingModel = fromPromise(
|
||||
foldingController.getFoldingModel() ?? Promise.resolve(undefined)
|
||||
);
|
||||
return foldingModel.map((v) => ({
|
||||
isActive: (lineNumber) => {
|
||||
const regionAtLine = v.value?.getRegionAtLine(lineNumber);
|
||||
const hasFolding = !regionAtLine
|
||||
? false
|
||||
: regionAtLine.isCollapsed &&
|
||||
regionAtLine.startLineNumber === lineNumber;
|
||||
return hasFolding;
|
||||
},
|
||||
onChange: emitter.event,
|
||||
};
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,22 +263,52 @@ class BreakpointFeature implements Feature {
|
|||
|
||||
constructor(@IDebugService private readonly debugService: IDebugService) { }
|
||||
|
||||
createForEditor(
|
||||
editor: ICodeEditor,
|
||||
uri: URI
|
||||
): FeatureResult {
|
||||
return {
|
||||
isActive: (lineNumber) => {
|
||||
const breakpoints = this.debugService
|
||||
.getModel()
|
||||
.getBreakpoints({ uri, lineNumber });
|
||||
const hasBreakpoints = breakpoints.length > 0;
|
||||
return hasBreakpoints;
|
||||
},
|
||||
onChange: Event.map(
|
||||
this.debugService.getModel().onDidChangeBreakpoints,
|
||||
() => undefined
|
||||
),
|
||||
};
|
||||
getObservableState(editor: ICodeEditor, model: ITextModel): IObservable<FeatureState> {
|
||||
return fromEvent(
|
||||
this.debugService.getModel().onDidChangeBreakpoints,
|
||||
() => ({
|
||||
isActive: (lineNumber) => {
|
||||
const breakpoints = this.debugService
|
||||
.getModel()
|
||||
.getBreakpoints({ uri: model.uri, lineNumber });
|
||||
const hasBreakpoints = breakpoints.length > 0;
|
||||
return hasBreakpoints;
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class InlineCompletionFeature implements Feature {
|
||||
public readonly audioCueFilename = 'break';
|
||||
|
||||
getObservableState(editor: ICodeEditor, model: ITextModel): IObservable<FeatureState> {
|
||||
const ghostTextController = GhostTextController.get(editor);
|
||||
if (!ghostTextController) {
|
||||
return constObservable({
|
||||
isActive: () => false,
|
||||
});
|
||||
}
|
||||
|
||||
const activeGhostText = fromEvent(
|
||||
ghostTextController.onActiveModelDidChange,
|
||||
() => ghostTextController.activeModel
|
||||
).map((activeModel) => (
|
||||
activeModel
|
||||
? fromEvent(
|
||||
activeModel.inlineCompletionsModel.onDidChange,
|
||||
() => activeModel.inlineCompletionsModel.ghostText
|
||||
)
|
||||
: undefined
|
||||
));
|
||||
|
||||
return new LazyDerived(reader => {
|
||||
const ghostText = activeGhostText.read(reader)?.read(reader);
|
||||
return {
|
||||
isActive(lineNumber) {
|
||||
return ghostText?.lineNumber === lineNumber;
|
||||
}
|
||||
};
|
||||
}, 'ghostText');
|
||||
}
|
||||
}
|
||||
|
|
575
src/vs/workbench/contrib/audioCues/browser/observable.ts
Normal file
575
src/vs/workbench/contrib/audioCues/browser/observable.ts
Normal file
|
@ -0,0 +1,575 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface IObservable<T> {
|
||||
/**
|
||||
* Reads the current value.
|
||||
*
|
||||
* This causes a recomputation if needed.
|
||||
* Calling this method forces changes to propagate to observers during update operations.
|
||||
* Must not be called from {@link IObserver.handleChange}.
|
||||
*/
|
||||
get(): T;
|
||||
|
||||
/**
|
||||
* Registers an observer.
|
||||
*
|
||||
* Calls {@link IObserver.handleChange} immediately after a change is noticed.
|
||||
* Might happen while someone calls {@link IObservable.get} or {@link IObservable.read}.
|
||||
*/
|
||||
subscribe(observer: IObserver): void;
|
||||
unsubscribe(observer: IObserver): void;
|
||||
|
||||
/**
|
||||
* Calls {@link IObservable.get} and then {@link IReader.handleBeforeReadObservable}.
|
||||
*/
|
||||
read(reader: IReader): T;
|
||||
|
||||
map<TNew>(fn: (value: T) => TNew): IObservable<TNew>;
|
||||
}
|
||||
|
||||
export interface IReader {
|
||||
/**
|
||||
* Reports an observable that was read.
|
||||
*
|
||||
* Is called by `Observable.read`.
|
||||
*/
|
||||
handleBeforeReadObservable<T>(observable: IObservable<T>): void;
|
||||
}
|
||||
|
||||
export interface IObserver {
|
||||
/**
|
||||
* Indicates that an update operation is about to begin.
|
||||
*
|
||||
* During an update, invariants might not hold for subscribed observables and
|
||||
* change events might be delayed.
|
||||
* However, all changes must be reported before all update operations are over.
|
||||
*/
|
||||
beginUpdate<T>(observable: IObservable<T>): void;
|
||||
|
||||
/**
|
||||
* Is called by a subscribed observable immediately after it notices a change.
|
||||
*
|
||||
* When {@link IObservable.get} returns and no change has been reported,
|
||||
* there has been no change for that observable.
|
||||
*
|
||||
* Implementations must not call into other observables!
|
||||
* The change should be processed when {@link IObserver.endUpdate} is called.
|
||||
*/
|
||||
handleChange<T>(observable: IObservable<T>): void;
|
||||
|
||||
/**
|
||||
* Indicates that an update operation has completed.
|
||||
*/
|
||||
endUpdate<T>(observable: IObservable<T>): void;
|
||||
}
|
||||
|
||||
export interface ISettable<T> {
|
||||
set(value: T, transaction: ITransaction | undefined): void;
|
||||
}
|
||||
|
||||
export interface ITransaction {
|
||||
/**
|
||||
* Calls `Observer.beginUpdate` immediately
|
||||
* and `Observer.endUpdate` when the transaction is complete.
|
||||
*/
|
||||
updateObserver(
|
||||
observer: IObserver,
|
||||
observable: IObservable<any>
|
||||
): void;
|
||||
}
|
||||
|
||||
// === Base ===
|
||||
export abstract class ConvenientObservable<T> implements IObservable<T> {
|
||||
public abstract get(): T;
|
||||
public abstract subscribe(observer: IObserver): void;
|
||||
public abstract unsubscribe(observer: IObserver): void;
|
||||
|
||||
public read(reader: IReader): T {
|
||||
reader.handleBeforeReadObservable(this);
|
||||
return this.get();
|
||||
}
|
||||
|
||||
public map<TNew>(fn: (value: T) => TNew): IObservable<TNew> {
|
||||
return new LazyDerived((reader) => fn(this.read(reader)), '(mapped)');
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class BaseObservable<T> extends ConvenientObservable<T> {
|
||||
protected readonly observers = new Set<IObserver>();
|
||||
|
||||
public subscribe(observer: IObserver): void {
|
||||
const len = this.observers.size;
|
||||
this.observers.add(observer);
|
||||
if (len === 0) {
|
||||
this.onFirstObserverSubscribed();
|
||||
}
|
||||
}
|
||||
|
||||
public unsubscribe(observer: IObserver): void {
|
||||
const deleted = this.observers.delete(observer);
|
||||
if (deleted && this.observers.size === 0) {
|
||||
this.onLastObserverUnsubscribed();
|
||||
}
|
||||
}
|
||||
|
||||
protected onFirstObserverSubscribed(): void { }
|
||||
protected onLastObserverUnsubscribed(): void { }
|
||||
}
|
||||
|
||||
export function transaction(fn: (tx: ITransaction) => void) {
|
||||
const tx = new TransactionImpl();
|
||||
try {
|
||||
fn(tx);
|
||||
} finally {
|
||||
tx.finish();
|
||||
}
|
||||
}
|
||||
|
||||
class TransactionImpl implements ITransaction {
|
||||
private readonly finishActions = new Array<() => void>();
|
||||
|
||||
public updateObserver(
|
||||
observer: IObserver,
|
||||
observable: IObservable<any>
|
||||
): void {
|
||||
this.finishActions.push(function () {
|
||||
observer.endUpdate(observable);
|
||||
});
|
||||
observer.beginUpdate(observable);
|
||||
}
|
||||
|
||||
public finish(): void {
|
||||
for (const action of this.finishActions) {
|
||||
action();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ObservableValue<T>
|
||||
extends BaseObservable<T>
|
||||
implements ISettable<T>
|
||||
{
|
||||
private value: T;
|
||||
|
||||
constructor(initialValue: T, public readonly name: string) {
|
||||
super();
|
||||
this.value = initialValue;
|
||||
}
|
||||
|
||||
public get(): T {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public set(value: T, tx: ITransaction | undefined): void {
|
||||
if (this.value === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tx) {
|
||||
transaction((tx) => {
|
||||
this.set(value, tx);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
|
||||
for (const observer of this.observers) {
|
||||
tx.updateObserver(observer, this);
|
||||
observer.handleChange(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function constObservable<T>(value: T): IObservable<T> {
|
||||
return new ConstObservable(value);
|
||||
}
|
||||
|
||||
class ConstObservable<T> extends ConvenientObservable<T> {
|
||||
constructor(private readonly value: T) {
|
||||
super();
|
||||
}
|
||||
|
||||
public get(): T {
|
||||
return this.value;
|
||||
}
|
||||
public subscribe(observer: IObserver): void {
|
||||
// NO OP
|
||||
}
|
||||
public unsubscribe(observer: IObserver): void {
|
||||
// NO OP
|
||||
}
|
||||
}
|
||||
|
||||
// == autorun ==
|
||||
export function autorun(
|
||||
fn: (reader: IReader) => void,
|
||||
name: string
|
||||
): IDisposable {
|
||||
return new AutorunObserver(fn, name);
|
||||
}
|
||||
|
||||
export class AutorunObserver implements IObserver, IReader, IDisposable {
|
||||
public needsToRun = true;
|
||||
private updateCount = 0;
|
||||
|
||||
/**
|
||||
* The actual dependencies.
|
||||
*/
|
||||
private _dependencies = new Set<IObservable<any>>();
|
||||
public get dependencies() {
|
||||
return this._dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dependencies that have to be removed when {@link runFn} ran through.
|
||||
*/
|
||||
private staleDependencies = new Set<IObservable<any>>();
|
||||
|
||||
constructor(
|
||||
private readonly runFn: (reader: IReader) => void,
|
||||
public readonly name: string
|
||||
) {
|
||||
this.runIfNeeded();
|
||||
}
|
||||
|
||||
public handleBeforeReadObservable<T>(observable: IObservable<T>) {
|
||||
this._dependencies.add(observable);
|
||||
if (!this.staleDependencies.delete(observable)) {
|
||||
observable.subscribe(this);
|
||||
}
|
||||
}
|
||||
|
||||
public handleChange() {
|
||||
this.needsToRun = true;
|
||||
|
||||
if (this.updateCount === 0) {
|
||||
this.runIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
public beginUpdate() {
|
||||
this.updateCount++;
|
||||
}
|
||||
|
||||
public endUpdate() {
|
||||
this.updateCount--;
|
||||
if (this.updateCount === 0) {
|
||||
this.runIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
private runIfNeeded(): void {
|
||||
if (!this.needsToRun) {
|
||||
return;
|
||||
}
|
||||
// Assert: this.staleDependencies is an empty set.
|
||||
const emptySet = this.staleDependencies;
|
||||
this.staleDependencies = this._dependencies;
|
||||
this._dependencies = emptySet;
|
||||
|
||||
this.needsToRun = false;
|
||||
|
||||
try {
|
||||
this.runFn(this);
|
||||
} finally {
|
||||
// We don't want our observed observables to think that they are (not even temporarily) not being observed.
|
||||
// Thus, we only unsubscribe from observables that are definitely not read anymore.
|
||||
for (const o of this.staleDependencies) {
|
||||
o.unsubscribe(this);
|
||||
}
|
||||
this.staleDependencies.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
for (const o of this._dependencies) {
|
||||
o.unsubscribe(this);
|
||||
}
|
||||
this._dependencies.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export function autorunDelta<T>(
|
||||
observable: IObservable<T>,
|
||||
handler: (args: { lastValue: T | undefined; newValue: T }) => void
|
||||
): IDisposable {
|
||||
let _lastValue: T | undefined;
|
||||
return autorun((reader) => {
|
||||
const newValue = observable.read(reader);
|
||||
const lastValue = _lastValue;
|
||||
_lastValue = newValue;
|
||||
handler({ lastValue, newValue });
|
||||
}, '');
|
||||
}
|
||||
|
||||
|
||||
// == Lazy Derived ==
|
||||
|
||||
export class LazyDerived<T> extends ConvenientObservable<T> {
|
||||
private readonly observer: LazyDerivedObserver<T>;
|
||||
|
||||
constructor(computeFn: (reader: IReader) => T, name: string) {
|
||||
super();
|
||||
this.observer = new LazyDerivedObserver(computeFn, name);
|
||||
}
|
||||
|
||||
public subscribe(observer: IObserver): void {
|
||||
this.observer.subscribe(observer);
|
||||
}
|
||||
|
||||
public unsubscribe(observer: IObserver): void {
|
||||
this.observer.unsubscribe(observer);
|
||||
}
|
||||
|
||||
public get(): T {
|
||||
return this.observer.get();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class LazyDerivedObserver<T>
|
||||
extends BaseObservable<T>
|
||||
implements IReader, IObserver {
|
||||
private hadValue = false;
|
||||
private hasValue = false;
|
||||
private value: T | undefined = undefined;
|
||||
private updateCount = 0;
|
||||
|
||||
private _dependencies = new Set<IObservable<any>>();
|
||||
public get dependencies(): ReadonlySet<IObservable<any>> {
|
||||
return this._dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dependencies that have to be removed when {@link runFn} ran through.
|
||||
*/
|
||||
private staleDependencies = new Set<IObservable<any>>();
|
||||
|
||||
constructor(
|
||||
private readonly computeFn: (reader: IReader) => T,
|
||||
public readonly name: string
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected override onLastObserverUnsubscribed(): void {
|
||||
/**
|
||||
* We are not tracking changes anymore, thus we have to assume
|
||||
* that our cache is invalid.
|
||||
*/
|
||||
this.hasValue = false;
|
||||
this.hadValue = false;
|
||||
this.value = undefined;
|
||||
for (const d of this._dependencies) {
|
||||
d.unsubscribe(this);
|
||||
}
|
||||
this._dependencies.clear();
|
||||
}
|
||||
|
||||
public handleBeforeReadObservable<T>(observable: IObservable<T>) {
|
||||
this._dependencies.add(observable);
|
||||
if (!this.staleDependencies.delete(observable)) {
|
||||
observable.subscribe(this);
|
||||
}
|
||||
}
|
||||
|
||||
public handleChange() {
|
||||
if (this.hasValue) {
|
||||
this.hadValue = true;
|
||||
this.hasValue = false;
|
||||
}
|
||||
|
||||
// Not in transaction: Recompute & inform observers immediately
|
||||
if (this.updateCount === 0 && this.observers.size > 0) {
|
||||
this.get();
|
||||
}
|
||||
|
||||
// Otherwise, recompute in `endUpdate` or on demand.
|
||||
}
|
||||
|
||||
public beginUpdate() {
|
||||
if (this.updateCount === 0) {
|
||||
for (const r of this.observers) {
|
||||
r.beginUpdate(this);
|
||||
}
|
||||
}
|
||||
this.updateCount++;
|
||||
}
|
||||
|
||||
public endUpdate() {
|
||||
this.updateCount--;
|
||||
if (this.updateCount === 0) {
|
||||
if (this.observers.size > 0) {
|
||||
// Propagate invalidation
|
||||
this.get();
|
||||
}
|
||||
|
||||
for (const r of this.observers) {
|
||||
r.endUpdate(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get(): T {
|
||||
if (this.observers.size === 0) {
|
||||
// Cache is not valid and don't refresh the cache.
|
||||
// Observables should not be read in non-reactive contexts.
|
||||
return this.computeFn(this);
|
||||
}
|
||||
|
||||
if (this.updateCount > 0 && this.hasValue) {
|
||||
// Refresh dependencies
|
||||
for (const d of this._dependencies) {
|
||||
// Maybe `.get()` triggers `handleChange`?
|
||||
d.get();
|
||||
if (!this.hasValue) {
|
||||
// The other dependencies will refresh on demand
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.hasValue) {
|
||||
const emptySet = this.staleDependencies;
|
||||
this.staleDependencies = this._dependencies;
|
||||
this._dependencies = emptySet;
|
||||
|
||||
const oldValue = this.value;
|
||||
try {
|
||||
this.value = this.computeFn(this);
|
||||
} finally {
|
||||
// We don't want our observed observables to think that they are (not even temporarily) not being observed.
|
||||
// Thus, we only unsubscribe from observables that are definitely not read anymore.
|
||||
for (const o of this.staleDependencies) {
|
||||
o.unsubscribe(this);
|
||||
}
|
||||
this.staleDependencies.clear();
|
||||
}
|
||||
|
||||
this.hasValue = true;
|
||||
if (this.hadValue && oldValue !== this.value) {
|
||||
//
|
||||
for (const r of this.observers) {
|
||||
r.handleChange(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.value!;
|
||||
}
|
||||
}
|
||||
|
||||
export function fromPromise<T>(promise: Promise<T>): IObservable<{ value?: T }> {
|
||||
const observable = new ObservableValue<{ value?: T }>({}, 'promiseValue');
|
||||
promise.then((value) => {
|
||||
observable.set({ value }, undefined);
|
||||
});
|
||||
return observable;
|
||||
}
|
||||
|
||||
export function fromEvent<TArgs, T>(
|
||||
event: Event<TArgs>,
|
||||
getValue: (args: TArgs | undefined) => T
|
||||
): IObservable<T> {
|
||||
return new FromEventObservable(event, getValue);
|
||||
}
|
||||
|
||||
class FromEventObservable<TArgs, T> extends BaseObservable<T> {
|
||||
private value: T | undefined;
|
||||
private hasValue = false;
|
||||
private subscription: IDisposable | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly event: Event<TArgs>,
|
||||
private readonly getValue: (args: TArgs | undefined) => T
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected override onFirstObserverSubscribed(): void {
|
||||
this.subscription = this.event(this.handleEvent);
|
||||
}
|
||||
|
||||
private readonly handleEvent = (args: TArgs | undefined) => {
|
||||
const newValue = this.getValue(args);
|
||||
if (this.value !== newValue) {
|
||||
this.value = newValue;
|
||||
|
||||
if (this.hasValue) {
|
||||
transaction(tx => {
|
||||
for (const o of this.observers) {
|
||||
tx.updateObserver(o, this);
|
||||
o.handleChange(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.hasValue = true;
|
||||
}
|
||||
};
|
||||
|
||||
protected override onLastObserverUnsubscribed(): void {
|
||||
this.subscription!.dispose();
|
||||
this.subscription = undefined;
|
||||
this.hasValue = false;
|
||||
this.value = undefined;
|
||||
}
|
||||
|
||||
public get(): T {
|
||||
if (this.subscription) {
|
||||
if (!this.hasValue) {
|
||||
this.handleEvent(undefined);
|
||||
}
|
||||
return this.value!;
|
||||
} else {
|
||||
// no cache, as there are no subscribers to clean it up
|
||||
return this.getValue(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function debouncedObservable<T>(observable: IObservable<T>, debounceMs: number, disposableStore: DisposableStore): IObservable<T> {
|
||||
const debouncedObservable = new ObservableValue(observable.get(), 'debounced');
|
||||
|
||||
let timeout: any = undefined;
|
||||
|
||||
disposableStore.add(autorun(reader => {
|
||||
const value = observable.read(reader);
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
timeout = setTimeout(() => {
|
||||
transaction(tx => {
|
||||
debouncedObservable.set(value, tx);
|
||||
});
|
||||
}, debounceMs);
|
||||
}, 'debounce'));
|
||||
|
||||
return debouncedObservable;
|
||||
}
|
||||
|
||||
export function wasEventTriggeredRecently(event: Event<any>, timeoutMs: number, disposableStore: DisposableStore): IObservable<boolean> {
|
||||
const observable = new ObservableValue(false, 'triggeredRecently');
|
||||
|
||||
let timeout: any = undefined;
|
||||
|
||||
disposableStore.add(event(() => {
|
||||
observable.set(true, undefined);
|
||||
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
timeout = setTimeout(() => {
|
||||
observable.set(false, undefined);
|
||||
}, timeoutMs);
|
||||
}));
|
||||
|
||||
return observable;
|
||||
}
|
|
@ -1937,7 +1937,11 @@ declare module DebugProtocol {
|
|||
export interface Variable {
|
||||
/** The variable's name. */
|
||||
name: string;
|
||||
/** The variable's value. This can be a multi-line text, e.g. for a function the body of a function. */
|
||||
/** The variable's value.
|
||||
This can be a multi-line text, e.g. for a function the body of a function.
|
||||
For structured variables (which do not have a simple value), it is recommended to provide a one line representation of the structured object. This helps to identify the structured object in the collapsed state when its children are not yet visible.
|
||||
An empty string can be used if no value should be shown in the UI.
|
||||
*/
|
||||
value: string;
|
||||
/** The type of the variable's value. Typically shown in the UI when hovering over the value.
|
||||
This attribute should only be returned by a debug adapter if the client has passed the value true for the 'supportsVariableType' capability of the 'initialize' request.
|
||||
|
@ -1999,6 +2003,8 @@ declare module DebugProtocol {
|
|||
Values: 'public', 'private', 'protected', 'internal', 'final', etc.
|
||||
*/
|
||||
visibility?: 'public' | 'private' | 'protected' | 'internal' | 'final' | string;
|
||||
/** If true clients can present the variable with a UI that supports a specific gesture to trigger its evaluation. */
|
||||
lazy?: boolean;
|
||||
}
|
||||
|
||||
/** Properties of a breakpoint location returned from the 'breakpointLocations' request. */
|
||||
|
@ -2163,6 +2169,8 @@ declare module DebugProtocol {
|
|||
text?: string;
|
||||
/** A string that should be used when comparing this item with other items. When `falsy` the label is used. */
|
||||
sortText?: string;
|
||||
/** A human-readable string with additional information about this item, like type or symbol information. */
|
||||
detail?: string;
|
||||
/** The item's type. Typically the client uses this information to render the item in the UI with an icon. */
|
||||
type?: CompletionItemType;
|
||||
/** This value determines the location (in the CompletionsRequest's 'text' attribute) where the completion text is added.
|
||||
|
|
|
@ -10,6 +10,8 @@ import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRe
|
|||
import { IWorkspaceContextService, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
export type ConfigBasedExtensionRecommendation = ExtensionRecommendation & { whenNotInstalled: string[] | undefined };
|
||||
|
||||
export class ConfigBasedRecommendations extends ExtensionRecommendations {
|
||||
|
||||
private importantTips: IConfigBasedExtensionTip[] = [];
|
||||
|
@ -18,13 +20,13 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations {
|
|||
private _onDidChangeRecommendations = this._register(new Emitter<void>());
|
||||
readonly onDidChangeRecommendations = this._onDidChangeRecommendations.event;
|
||||
|
||||
private _otherRecommendations: ExtensionRecommendation[] = [];
|
||||
get otherRecommendations(): ReadonlyArray<ExtensionRecommendation> { return this._otherRecommendations; }
|
||||
private _otherRecommendations: ConfigBasedExtensionRecommendation[] = [];
|
||||
get otherRecommendations(): ReadonlyArray<ConfigBasedExtensionRecommendation> { return this._otherRecommendations; }
|
||||
|
||||
private _importantRecommendations: ExtensionRecommendation[] = [];
|
||||
get importantRecommendations(): ReadonlyArray<ExtensionRecommendation> { return this._importantRecommendations; }
|
||||
private _importantRecommendations: ConfigBasedExtensionRecommendation[] = [];
|
||||
get importantRecommendations(): ReadonlyArray<ConfigBasedExtensionRecommendation> { return this._importantRecommendations; }
|
||||
|
||||
get recommendations(): ReadonlyArray<ExtensionRecommendation> { return [...this.importantRecommendations, ...this.otherRecommendations]; }
|
||||
get recommendations(): ReadonlyArray<ConfigBasedExtensionRecommendation> { return [...this.importantRecommendations, ...this.otherRecommendations]; }
|
||||
|
||||
constructor(
|
||||
@IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService,
|
||||
|
@ -69,13 +71,14 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations {
|
|||
}
|
||||
}
|
||||
|
||||
private toExtensionRecommendation(tip: IConfigBasedExtensionTip): ExtensionRecommendation {
|
||||
private toExtensionRecommendation(tip: IConfigBasedExtensionTip): ConfigBasedExtensionRecommendation {
|
||||
return {
|
||||
extensionId: tip.extensionId,
|
||||
reason: {
|
||||
reasonId: ExtensionRecommendationReason.WorkspaceConfig,
|
||||
reasonText: localize('exeBasedRecommendation', "This extension is recommended because of the current workspace configuration")
|
||||
}
|
||||
},
|
||||
whenNotInstalled: tip.whenNotInstalled
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -210,7 +210,7 @@ class PreReleaseTextWidget extends ExtensionWithDifferentGalleryVersionWidget {
|
|||
if (this.gallery) {
|
||||
return this.gallery.properties.isPreReleaseVersion;
|
||||
}
|
||||
return !!(this.extension.local?.isPreReleaseVersion || this.extension.gallery?.properties.isPreReleaseVersion);
|
||||
return !!(this.extension.state === ExtensionState.Installed ? this.extension.local?.isPreReleaseVersion : this.extension.gallery?.properties.isPreReleaseVersion);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ import { IExtensionRecommendationNotificationService } from 'vs/platform/extensi
|
|||
import { timeout } from 'vs/base/common/async';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { WebRecommendations } from 'vs/workbench/contrib/extensions/browser/webRecommendations';
|
||||
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
|
||||
type IgnoreRecommendationClassification = {
|
||||
recommendationReason: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true };
|
||||
|
@ -61,6 +63,7 @@ export class ExtensionRecommendationsService extends Disposable implements IExte
|
|||
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
|
||||
@IExtensionIgnoredRecommendationsService private readonly extensionRecommendationsManagementService: IExtensionIgnoredRecommendationsService,
|
||||
@IExtensionRecommendationNotificationService private readonly extensionRecommendationNotificationService: IExtensionRecommendationNotificationService,
|
||||
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
) {
|
||||
super();
|
||||
|
||||
|
@ -260,7 +263,12 @@ export class ExtensionRecommendationsService extends Disposable implements IExte
|
|||
}
|
||||
|
||||
private async promptWorkspaceRecommendations(): Promise<void> {
|
||||
const allowedRecommendations = [...this.workspaceRecommendations.recommendations, ...this.configBasedRecommendations.importantRecommendations]
|
||||
const installed = await this.extensionsWorkbenchService.queryLocal();
|
||||
const allowedRecommendations = [
|
||||
...this.workspaceRecommendations.recommendations,
|
||||
...this.configBasedRecommendations.importantRecommendations.filter(
|
||||
recommendation => !recommendation.whenNotInstalled || recommendation.whenNotInstalled.every(id => installed.every(local => !areSameExtensions(local.identifier, { id }))))
|
||||
]
|
||||
.map(({ extensionId }) => extensionId)
|
||||
.filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId));
|
||||
|
||||
|
|
|
@ -2261,7 +2261,7 @@ export class ExtensionStatusAction extends ExtensionAction {
|
|||
if (this.extensionManagementServerService.localExtensionManagementServer) {
|
||||
message = new MarkdownString(`${localize('Install in local server to enable', "This extension is disabled in this workspace because it is defined to run in the Local Extension Host. Please install the extension locally to enable.", this.extensionManagementServerService.remoteExtensionManagementServer.label)} [${localize('learn more', "Learn More")}](https://aka.ms/vscode-remote/developing-extensions/architecture)`);
|
||||
} else if (isWeb) {
|
||||
message = new MarkdownString(`${localize('Cannot be enabled', "This extension is disabled because it is not supported in {0} for the Web.", this.productService.nameLong)} [${localize('learn more', "Learn More")}](https://aka.ms/vscode-remote/developing-extensions/architecture)`);
|
||||
message = new MarkdownString(`${localize('Defined to run in desktop', "This extension is disabled because it is defined to run only in {0} for the Desktop.", this.productService.nameLong)} [${localize('learn more', "Learn More")}](https://aka.ms/vscode-remote/developing-extensions/architecture)`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import { Event } from 'vs/base/common/event';
|
|||
import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { UpdateAction, ManageExtensionAction, ReloadAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget, SyncIgnoredWidget, ExtensionHoverWidget, ExtensionActivationStatusWidget, PreReleaseBookmarkWidget, PreReleaseIndicatorWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets';
|
||||
import { RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget, SyncIgnoredWidget, ExtensionHoverWidget, ExtensionActivationStatusWidget, PreReleaseBookmarkWidget, RunningPreReleaseVersionIndicatorWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets';
|
||||
import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
@ -138,7 +138,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
|
|||
extensionPackBadgeWidget,
|
||||
headerRemoteBadgeWidget,
|
||||
extensionHoverWidget,
|
||||
this.instantiationService.createInstance(PreReleaseIndicatorWidget, preRelease),
|
||||
this.instantiationService.createInstance(RunningPreReleaseVersionIndicatorWidget, preRelease),
|
||||
this.instantiationService.createInstance(SyncIgnoredWidget, syncIgnore),
|
||||
this.instantiationService.createInstance(ExtensionActivationStatusWidget, activationStatus, true),
|
||||
this.instantiationService.createInstance(InstallCountWidget, installCount, true),
|
||||
|
|
|
@ -158,7 +158,7 @@ export class RatingsWidget extends ExtensionWidget {
|
|||
}
|
||||
}
|
||||
|
||||
export class PreReleaseIndicatorWidget extends ExtensionWidget {
|
||||
export class RunningPreReleaseVersionIndicatorWidget extends ExtensionWidget {
|
||||
|
||||
constructor(
|
||||
private readonly container: HTMLElement,
|
||||
|
@ -171,15 +171,9 @@ export class PreReleaseIndicatorWidget extends ExtensionWidget {
|
|||
render(): void {
|
||||
this.container.innerText = '';
|
||||
|
||||
if (!this.extension) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.extension.local?.isPreReleaseVersion && !this.extension.gallery?.properties.isPreReleaseVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.extension.state !== ExtensionState.Installed) {
|
||||
if (!this.extension
|
||||
|| this.extension.state !== ExtensionState.Installed
|
||||
|| !this.extension.local?.isPreReleaseVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -487,9 +481,9 @@ export class ExtensionHoverWidget extends ExtensionWidget {
|
|||
const markdown = new MarkdownString('', { isTrusted: true, supportThemeIcons: true });
|
||||
|
||||
markdown.appendMarkdown(`**${this.extension.displayName}** <span style="background-color:#8080802B;">** _v${this.extension.version}_** </span>`);
|
||||
if (this.extension.local?.isPreReleaseVersion || this.extension.gallery?.properties.isPreReleaseVersion) {
|
||||
if (this.extension.state === ExtensionState.Installed ? this.extension.local?.isPreReleaseVersion : this.extension.gallery?.properties.isPreReleaseVersion) {
|
||||
const extensionPreReleaseIcon = this.themeService.getColorTheme().getColor(extensionPreReleaseIconColor);
|
||||
markdown.appendMarkdown(`** ** <span style="background-color:${extensionPreReleaseIcon ? Color.Format.CSS.formatHex(extensionPreReleaseIcon) : '#ffffff'};"> $(${preReleaseIcon.id}) ${localize('pre-release-label', "Pre-Release")} </span>`);
|
||||
markdown.appendMarkdown(`** ** <span style="color:#ffffff;background-color:${extensionPreReleaseIcon ? Color.Format.CSS.formatHex(extensionPreReleaseIcon) : '#ffffff'};"> $(${preReleaseIcon.id}) ${localize('pre-release-label', "Pre-Release")} </span>`);
|
||||
}
|
||||
markdown.appendText(`\n`);
|
||||
|
||||
|
|
|
@ -695,6 +695,10 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
|
|||
this.queryLocal().then(() => {
|
||||
this.resetIgnoreAutoUpdateExtensions();
|
||||
this.eventuallyCheckForUpdates(true);
|
||||
// Always auto update builtin extensions
|
||||
if (!this.isAutoUpdateEnabled()) {
|
||||
this.autoUpdateBuiltinExtensions();
|
||||
}
|
||||
});
|
||||
|
||||
this._register(this.onChange(() => {
|
||||
|
@ -994,7 +998,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
|
|||
return ExtensionState.Uninstalled;
|
||||
}
|
||||
|
||||
async checkForUpdates(): Promise<void> {
|
||||
async checkForUpdates(onlyBuiltin?: boolean): Promise<void> {
|
||||
const extensions: Extensions[] = [];
|
||||
if (this.localExtensions) {
|
||||
extensions.push(this.localExtensions);
|
||||
|
@ -1010,7 +1014,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
|
|||
}
|
||||
const infos: IExtensionInfo[] = [];
|
||||
for (const installed of this.local) {
|
||||
if (installed.type === ExtensionType.User) {
|
||||
if (installed.type === ExtensionType.User && (!onlyBuiltin || installed.isBuiltin)) {
|
||||
infos.push({ ...installed.identifier, preRelease: !!installed.local?.preRelease });
|
||||
}
|
||||
}
|
||||
|
@ -1070,6 +1074,12 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
|
|||
.then(undefined, err => null);
|
||||
}
|
||||
|
||||
private async autoUpdateBuiltinExtensions(): Promise<void> {
|
||||
await this.checkForUpdates(true);
|
||||
const toUpdate = this.outdated.filter(e => e.isBuiltin);
|
||||
await Promises.settled(toUpdate.map(e => this.install(e, e.local?.preRelease ? { installPreReleaseVersion: true } : undefined)));
|
||||
}
|
||||
|
||||
private autoUpdateExtensions(): Promise<any> {
|
||||
if (!this.isAutoUpdateEnabled()) {
|
||||
return Promise.resolve();
|
||||
|
|
|
@ -34,6 +34,7 @@ import { disposableTimeout } from 'vs/base/common/async';
|
|||
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
|
||||
import { ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
|
||||
type FileExtensionSuggestionClassification = {
|
||||
userReaction: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' };
|
||||
|
@ -197,7 +198,15 @@ export class FileBasedRecommendations extends ExtensionRecommendations {
|
|||
}
|
||||
|
||||
private async promptRecommendations(uri: URI, language: string, fileExtension: string): Promise<void> {
|
||||
const importantRecommendations: string[] = (this.fileBasedRecommendationsByLanguage.get(language) || []).filter(extensionId => this.importantExtensionTips.has(extensionId));
|
||||
const installed = await this.extensionsWorkbenchService.queryLocal();
|
||||
const importantRecommendations: string[] = (this.fileBasedRecommendationsByLanguage.get(language) || [])
|
||||
.filter(extensionId => {
|
||||
const importantTip = this.importantExtensionTips.get(extensionId);
|
||||
if (importantTip) {
|
||||
return !importantTip.whenNotInstalled || importantTip.whenNotInstalled.every(id => installed.every(local => !areSameExtensions(local.identifier, { id })));
|
||||
}
|
||||
return false;
|
||||
});
|
||||
let languageName: string | null = importantRecommendations.length ? this.languageService.getLanguageName(language) : null;
|
||||
|
||||
const fileBasedRecommendations: string[] = [...importantRecommendations];
|
||||
|
@ -231,7 +240,6 @@ export class FileBasedRecommendations extends ExtensionRecommendations {
|
|||
return;
|
||||
}
|
||||
|
||||
const installed = await this.extensionsWorkbenchService.queryLocal();
|
||||
if (importantRecommendations.length &&
|
||||
await this.promptRecommendedExtensionForFileType(languageName || basename(uri), language, importantRecommendations, installed)) {
|
||||
return;
|
||||
|
|
|
@ -470,7 +470,7 @@ configurationRegistry.registerConfiguration({
|
|||
},
|
||||
'explorer.experimental.fileNesting.patterns': {
|
||||
'type': 'object',
|
||||
'markdownDescription': nls.localize('fileNestingPatterns', "Experimental. Controls nesting of files in the explorer. `#explorer.experimental.fileNesting.enabled#` must be set for this to take effect. Each key describes a parent file pattern and each value should be a comma separated list of children file patterns that will be nested under the parent.\n\nA single `*` in a parent pattern may be used to capture any substring, which can then be matched against using `$\u200b(capture)` in a child pattern. Child patterns may also contain one `*` to match any substring."),
|
||||
'markdownDescription': nls.localize('fileNestingPatterns', "Experimental. Controls nesting of files in the explorer. `#explorer.experimental.fileNesting.enabled#` must be set for this to take effect. Each key describes a parent file pattern and each value should be a comma separated list of children file patterns that will be nested under the parent.\n\nA single `*` in a parent pattern may be used to capture any substring, which can then be matched against using `$\u200b(capture)` in a child pattern. Child patterns may also contain one `*` to match any substring.\n\nFor example, given the configuration `*.ts => $(capture).js, $(capture).*.ts`, and a directory containing `a.ts, a.js, a.d.ts`, and `b.js`, nesting would apply as follows: \n- `*.ts` matches `a.ts`, capturing `a`. This causes any sibilings matching `a.js` or `a.*.ts` to be nested under `a.ts`\n - `a.js` matches `a.js` exactly, so is nested under `a.ts`\n - `a.d.ts` matches `a.*.ts`, so is also nested under `a.ts`\n\nThe final directory will be rendered with `a.ts` containg `a.js` and `a.d.ts` as nested children, and `b.js` as normal file."),
|
||||
patternProperties: {
|
||||
'^[^*]*\\*?[^*]*$': {
|
||||
markdownDescription: nls.localize('fileNesting.description', "Key patterns may contain a single `*` capture group which matches any string. Each value pattern may contain one `$\u200b(capture)` token to be substituted with the parent capture group and one `*` token to match any string"),
|
||||
|
@ -480,7 +480,7 @@ configurationRegistry.registerConfiguration({
|
|||
},
|
||||
additionalProperties: false,
|
||||
'default': {
|
||||
'*.ts': '$(capture).js, $(capture).d.ts',
|
||||
'*.ts': '$(capture).js, $(capture).*.ts',
|
||||
'*.js': '$(capture).js.map, $(capture).min.js, $(capture).d.ts',
|
||||
'*.jsx': '$(capture).js',
|
||||
'*.tsx': '$(capture).ts',
|
||||
|
|
|
@ -386,7 +386,7 @@ registerAction2(class extends Action2 {
|
|||
const allKernels = kernelService.getMatchingKernel({ uri: notebookUri, viewType: 'interactive' }).all;
|
||||
const preferredKernel = allKernels.find(kernel => kernel.id === id);
|
||||
if (preferredKernel) {
|
||||
kernelService.selectKernelForNotebook(preferredKernel, { uri: notebookUri, viewType: 'interactive' });
|
||||
kernelService.preselectKernelForNotebook(preferredKernel, { uri: notebookUri, viewType: 'interactive' });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,150 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { ILanguageService } from 'vs/editor/common/languages/language';
|
||||
import { IModelService } from 'vs/editor/common/services/model';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import { ICellOutputViewModel, IOutputTransformContribution, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { INotebookDelegateForOutput } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon';
|
||||
import { OutputRendererRegistry } from 'vs/workbench/contrib/notebook/browser/view/output/rendererRegistry';
|
||||
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
|
||||
import { IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
|
||||
abstract class CodeRendererContrib extends Disposable implements IOutputTransformContribution {
|
||||
getType() {
|
||||
return RenderOutputType.Mainframe;
|
||||
}
|
||||
|
||||
abstract getMimetypes(): string[];
|
||||
|
||||
constructor(
|
||||
public notebookEditor: INotebookDelegateForOutput,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
@ILanguageService private readonly languageService: ILanguageService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
abstract render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement): IRenderOutput;
|
||||
|
||||
protected _render(output: ICellOutputViewModel, container: HTMLElement, value: string, languageId: string): IRenderOutput {
|
||||
const disposable = new DisposableStore();
|
||||
const editor = this.instantiationService.createInstance(CodeEditorWidget, container, getOutputSimpleEditorOptions(), { isSimpleWidget: true, contributions: this.notebookEditor.creationOptions.cellEditorContributions });
|
||||
|
||||
if (output.cellViewModel instanceof CodeCellViewModel) {
|
||||
disposable.add(output.cellViewModel.viewContext.eventDispatcher.onDidChangeLayout(() => {
|
||||
const outputWidth = this.notebookEditor.getCellOutputLayoutInfo(output.cellViewModel).width;
|
||||
const fontInfo = this.notebookEditor.getCellOutputLayoutInfo(output.cellViewModel).fontInfo;
|
||||
const editorHeight = Math.min(16 * (fontInfo.lineHeight || 18), editor.getLayoutInfo().height);
|
||||
|
||||
editor.layout({ height: editorHeight, width: outputWidth });
|
||||
container.style.height = `${editorHeight + 8}px`;
|
||||
}));
|
||||
}
|
||||
|
||||
disposable.add(editor.onDidContentSizeChange(e => {
|
||||
const outputWidth = this.notebookEditor.getCellOutputLayoutInfo(output.cellViewModel).width;
|
||||
const fontInfo = this.notebookEditor.getCellOutputLayoutInfo(output.cellViewModel).fontInfo;
|
||||
const editorHeight = Math.min(16 * (fontInfo.lineHeight || 18), e.contentHeight);
|
||||
|
||||
editor.layout({ height: editorHeight, width: outputWidth });
|
||||
container.style.height = `${editorHeight + 8}px`;
|
||||
}));
|
||||
|
||||
const mode = this.languageService.createById(languageId);
|
||||
const textModel = this.modelService.createModel(value, mode, undefined, false);
|
||||
editor.setModel(textModel);
|
||||
|
||||
const width = this.notebookEditor.getCellOutputLayoutInfo(output.cellViewModel).width;
|
||||
const fontInfo = this.notebookEditor.getCellOutputLayoutInfo(output.cellViewModel).fontInfo;
|
||||
const height = Math.min(textModel.getLineCount(), 16) * (fontInfo.lineHeight || 18);
|
||||
|
||||
editor.layout({ height, width });
|
||||
|
||||
disposable.add(editor);
|
||||
disposable.add(textModel);
|
||||
|
||||
container.style.height = `${height + 8}px`;
|
||||
|
||||
return { type: RenderOutputType.Mainframe, initHeight: height, disposable };
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookCodeRendererContribution extends Disposable {
|
||||
|
||||
constructor(@ILanguageService _languageService: ILanguageService) {
|
||||
super();
|
||||
|
||||
const registeredMimeTypes = new Map();
|
||||
const registerCodeRendererContrib = (mimeType: string, languageId: string) => {
|
||||
if (registeredMimeTypes.has(mimeType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
OutputRendererRegistry.registerOutputTransform(class extends CodeRendererContrib {
|
||||
getMimetypes() {
|
||||
return [mimeType];
|
||||
}
|
||||
|
||||
render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement): IRenderOutput {
|
||||
const str = item.data.toString();
|
||||
return this._render(output, container, str, languageId);
|
||||
}
|
||||
});
|
||||
|
||||
registeredMimeTypes.set(mimeType, true);
|
||||
};
|
||||
|
||||
_languageService.getRegisteredLanguageIds().forEach(id => {
|
||||
registerCodeRendererContrib(`text/x-${id}`, id);
|
||||
});
|
||||
|
||||
this._register(_languageService.onDidChange(() => {
|
||||
_languageService.getRegisteredLanguageIds().forEach(id => {
|
||||
registerCodeRendererContrib(`text/x-${id}`, id);
|
||||
});
|
||||
}));
|
||||
|
||||
registerCodeRendererContrib('application/json', 'json');
|
||||
}
|
||||
}
|
||||
|
||||
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(NotebookCodeRendererContribution, LifecyclePhase.Restored);
|
||||
|
||||
|
||||
// --- utils ---
|
||||
|
||||
function getOutputSimpleEditorOptions(): IEditorConstructionOptions {
|
||||
return {
|
||||
dimension: { height: 0, width: 0 },
|
||||
readOnly: true,
|
||||
wordWrap: 'on',
|
||||
overviewRulerLanes: 0,
|
||||
glyphMargin: false,
|
||||
selectOnLineNumbers: false,
|
||||
hideCursorInOverviewRuler: true,
|
||||
selectionHighlight: false,
|
||||
lineDecorationsWidth: 0,
|
||||
overviewRulerBorder: false,
|
||||
scrollBeyondLastLine: false,
|
||||
renderLineHighlight: 'none',
|
||||
minimap: {
|
||||
enabled: false
|
||||
},
|
||||
lineNumbers: 'off',
|
||||
scrollbar: {
|
||||
alwaysConsumeMouseWheel: false
|
||||
},
|
||||
automaticLayout: true,
|
||||
};
|
||||
}
|
|
@ -3,14 +3,13 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { CellEditState, IInsetRenderOutput, INotebookEditor, INotebookEditorContribution, INotebookEditorDelegate, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions';
|
||||
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
|
||||
import { BUILTIN_RENDERER_ID, CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { cellRangesToIndexes } from 'vs/workbench/contrib/notebook/common/notebookRange';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
|
||||
|
@ -105,14 +104,6 @@ class NotebookViewportContribution extends Disposable implements INotebookEditor
|
|||
return;
|
||||
}
|
||||
|
||||
if (pickedMimeTypeRenderer.rendererId === BUILTIN_RENDERER_ID) {
|
||||
const renderer = this._notebookEditor.getOutputRenderer().getContribution(pickedMimeTypeRenderer.mimeType);
|
||||
if (renderer?.getType() === RenderOutputType.Html) {
|
||||
const renderResult = renderer.render(output, output.model.outputs.filter(op => op.mime === pickedMimeTypeRenderer.mimeType)[0], DOM.$(''), this._notebookEditor.textModel.uri) as IInsetRenderOutput;
|
||||
this._notebookEditor.createOutput(viewCell, renderResult, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const renderer = this._notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);
|
||||
|
||||
if (!renderer) {
|
||||
|
|
|
@ -9,10 +9,9 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
|||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { DiffElementViewModelBase, SideBySideDiffElementViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel';
|
||||
import { DiffSide, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser';
|
||||
import { ICellOutputViewModel, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets';
|
||||
import { ICellOutputViewModel, IInsetRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { BUILTIN_RENDERER_ID, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { DiffNestedCellViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffNestedCellViewModel';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
|
@ -28,7 +27,7 @@ interface IMimeTypeRenderer extends IQuickPickItem {
|
|||
export class OutputElement extends Disposable {
|
||||
readonly resizeListener = this._register(new DisposableStore());
|
||||
domNode!: HTMLElement;
|
||||
renderResult?: IRenderOutput;
|
||||
renderResult?: IInsetRenderOutput;
|
||||
|
||||
constructor(
|
||||
private _notebookEditor: INotebookTextDiffEditor,
|
||||
|
@ -46,7 +45,7 @@ export class OutputElement extends Disposable {
|
|||
|
||||
render(index: number, beforeElement?: HTMLElement) {
|
||||
const outputItemDiv = document.createElement('div');
|
||||
let result: IRenderOutput | undefined = undefined;
|
||||
let result: IInsetRenderOutput | undefined = undefined;
|
||||
|
||||
const [mimeTypes, pick] = this.output.resolveMimeTypes(this._notebookTextModel, undefined);
|
||||
const pickedMimeTypeRenderer = mimeTypes[pick];
|
||||
|
@ -80,14 +79,10 @@ export class OutputElement extends Disposable {
|
|||
|
||||
|
||||
if (mimeTypes.length !== 0) {
|
||||
if (pickedMimeTypeRenderer.rendererId !== BUILTIN_RENDERER_ID) {
|
||||
const renderer = this._notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);
|
||||
result = renderer
|
||||
? { type: RenderOutputType.Extension, renderer, source: this.output, mimeType: pickedMimeTypeRenderer.mimeType }
|
||||
: this._notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this._notebookTextModel.uri,);
|
||||
} else {
|
||||
result = this._notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this._notebookTextModel.uri);
|
||||
}
|
||||
const renderer = this._notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);
|
||||
result = renderer
|
||||
? { type: RenderOutputType.Extension, renderer, source: this.output, mimeType: pickedMimeTypeRenderer.mimeType }
|
||||
: this._renderMissingRenderer(this.output, pickedMimeTypeRenderer.mimeType);
|
||||
|
||||
this.output.pickedMimeType = pickedMimeTypeRenderer;
|
||||
}
|
||||
|
@ -106,52 +101,43 @@ export class OutputElement extends Disposable {
|
|||
this._outputContainer.appendChild(outputItemDiv);
|
||||
}
|
||||
|
||||
if (result.type !== RenderOutputType.Mainframe) {
|
||||
// this.viewCell.selfSizeMonitoring = true;
|
||||
this._notebookEditor.createOutput(
|
||||
this._diffElementViewModel,
|
||||
this._nestedCell,
|
||||
result,
|
||||
() => this.getOutputOffsetInCell(index),
|
||||
this._diffElementViewModel instanceof SideBySideDiffElementViewModel
|
||||
? this._diffSide
|
||||
: this._diffElementViewModel.type === 'insert' ? DiffSide.Modified : DiffSide.Original
|
||||
);
|
||||
} else {
|
||||
outputItemDiv.classList.add('foreground', 'output-element');
|
||||
outputItemDiv.style.position = 'absolute';
|
||||
}
|
||||
if (result.type === RenderOutputType.Html || result.type === RenderOutputType.Extension) {
|
||||
return;
|
||||
this._notebookEditor.createOutput(
|
||||
this._diffElementViewModel,
|
||||
this._nestedCell,
|
||||
result,
|
||||
() => this.getOutputOffsetInCell(index),
|
||||
this._diffElementViewModel instanceof SideBySideDiffElementViewModel
|
||||
? this._diffSide
|
||||
: this._diffElementViewModel.type === 'insert' ? DiffSide.Modified : DiffSide.Original
|
||||
);
|
||||
}
|
||||
|
||||
private _renderMissingRenderer(viewModel: ICellOutputViewModel, preferredMimeType: string | undefined): IInsetRenderOutput {
|
||||
if (!viewModel.model.outputs.length) {
|
||||
return this._renderMessage(viewModel, nls.localize('empty', "Cell has no output"));
|
||||
}
|
||||
|
||||
if (!preferredMimeType) {
|
||||
const mimeTypes = viewModel.model.outputs.map(op => op.mime);
|
||||
const mimeTypesMessage = mimeTypes.join(', ');
|
||||
return this._renderMessage(viewModel, nls.localize('noRenderer.2', "No renderer could be found for output. It has the following mimetypes: {0}", mimeTypesMessage));
|
||||
}
|
||||
|
||||
return this._renderSearchForMimetype(viewModel, preferredMimeType);
|
||||
}
|
||||
|
||||
let clientHeight = Math.ceil(outputItemDiv.clientHeight);
|
||||
const elementSizeObserver = getResizesObserver(outputItemDiv, undefined, () => {
|
||||
if (this._outputContainer && document.body.contains(this._outputContainer)) {
|
||||
const height = Math.ceil(elementSizeObserver.getHeight());
|
||||
private _renderSearchForMimetype(viewModel: ICellOutputViewModel, mimeType: string): IInsetRenderOutput {
|
||||
const query = `@tag:notebookRenderer ${mimeType}`;
|
||||
return {
|
||||
type: RenderOutputType.Html,
|
||||
source: viewModel,
|
||||
htmlContent: `<p>No renderer could be found for mimetype "${mimeType}", but one might be available on the Marketplace.</p>
|
||||
<a href="command:workbench.extensions.search?%22${query}%22" class="monaco-button monaco-text-button" tabindex="0" role="button" style="padding: 8px; text-decoration: none; color: rgb(255, 255, 255); background-color: rgb(14, 99, 156); max-width: 200px;">Search Marketplace</a>`
|
||||
};
|
||||
}
|
||||
|
||||
if (clientHeight === height) {
|
||||
return;
|
||||
}
|
||||
|
||||
clientHeight = height;
|
||||
|
||||
const currIndex = this.getCellOutputCurrentIndex();
|
||||
if (currIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateHeight(currIndex, height);
|
||||
}
|
||||
});
|
||||
elementSizeObserver.startObserving();
|
||||
this.resizeListener.add(elementSizeObserver);
|
||||
this.updateHeight(index, clientHeight);
|
||||
|
||||
const top = this.getOutputOffsetInContainer(index);
|
||||
outputItemDiv.style.top = `${top}px`;
|
||||
private _renderMessage(viewModel: ICellOutputViewModel, message: string): IInsetRenderOutput {
|
||||
return { type: RenderOutputType.Html, source: viewModel, htmlContent: `<p>${message}</p>` };
|
||||
}
|
||||
|
||||
private async pickActiveMimeTypeRenderer(notebookTextModel: NotebookTextModel, viewModel: ICellOutputViewModel) {
|
||||
|
@ -206,11 +192,7 @@ export class OutputElement extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
private generateRendererInfo(renderId: string | undefined): string {
|
||||
if (renderId === undefined || renderId === BUILTIN_RENDERER_ID) {
|
||||
return nls.localize('builtinRenderInfo', "built-in");
|
||||
}
|
||||
|
||||
private generateRendererInfo(renderId: string): string {
|
||||
const renderInfo = this._notebookService.getRendererInfo(renderId);
|
||||
|
||||
if (renderInfo) {
|
||||
|
|
|
@ -12,7 +12,6 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no
|
|||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget';
|
||||
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
|
||||
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
|
||||
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
|
||||
|
@ -35,7 +34,6 @@ export interface INotebookTextDiffEditor {
|
|||
getOverflowContainerDomNode(): HTMLElement;
|
||||
getLayoutInfo(): NotebookLayoutInfo;
|
||||
layoutNotebookCell(cell: DiffElementViewModelBase, height: number): void;
|
||||
getOutputRenderer(): OutputRenderer;
|
||||
createOutput(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, output: IInsetRenderOutput, getOffset: () => number, diffSide: DiffSide): void;
|
||||
showInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, displayOutput: ICellOutputViewModel, diffSide: DiffSide): void;
|
||||
removeInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, output: ICellOutputViewModel, diffSide: DiffSide): void;
|
||||
|
|
|
@ -32,7 +32,6 @@ import { CellUri, INotebookDiffEditorModel, INotebookDiffResult, NOTEBOOK_DIFF_E
|
|||
import { URI } from 'vs/base/common/uri';
|
||||
import { IDiffChange, IDiffResult } from 'vs/base/common/diff/diff';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
|
||||
import { SequencerByKey } from 'vs/base/common/async';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IMouseWheelEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
|
@ -66,7 +65,6 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
|
|||
protected _scopeContextKeyService!: IContextKeyService;
|
||||
private _model: INotebookDiffEditorModel | null = null;
|
||||
private readonly _modifiedResourceDisposableStore = this._register(new DisposableStore());
|
||||
private _outputRenderer: OutputRenderer;
|
||||
|
||||
get textModel() {
|
||||
return this._model?.modified.notebook;
|
||||
|
@ -108,7 +106,6 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
|
|||
const editorOptions = this.configurationService.getValue<ICodeEditorOptions>('editor');
|
||||
this._fontInfo = FontMeasurements.readFontInfo(BareFontInfo.createFromRawSettings(editorOptions, PixelRatio.value));
|
||||
this._revealFirst = true;
|
||||
this._outputRenderer = this.instantiationService.createInstance(OutputRenderer, this);
|
||||
}
|
||||
|
||||
toggleNotebookCellSelection(cell: IGenericCellViewModel) {
|
||||
|
@ -793,10 +790,6 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
|
|||
this._diffElementViewModels = [];
|
||||
}
|
||||
|
||||
getOutputRenderer(): OutputRenderer {
|
||||
return this._outputRenderer;
|
||||
}
|
||||
|
||||
deltaCellOutputContainerClassNames(diffSide: DiffSide, cellId: string, added: string[], removed: string[]) {
|
||||
if (diffSide === DiffSide.Original) {
|
||||
this._originalWebview?.deltaCellOutputContainerClassNames(cellId, added, removed);
|
||||
|
|
|
@ -186,8 +186,13 @@
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .codeOutput-focus-indicator-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:not(.selected) .monaco-editor .lines-content .selected-text,
|
||||
|
@ -215,6 +220,8 @@
|
|||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .output .cell-output-toolbar {
|
||||
left: -29px;
|
||||
width: 22px;
|
||||
z-index: var(--z-index-notebook-cell-output-toolbar);
|
||||
}
|
||||
|
||||
|
@ -341,25 +348,25 @@
|
|||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .cell-collapse-preview {
|
||||
padding: 0px 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .cell-collapse-preview .monaco-tokenized-source {
|
||||
font-size: var(--notebook-cell-input-preview-font-size);
|
||||
font-family: var(--notebook-cell-input-preview-font-family);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .cell-collapse-preview .monaco-tokenized-source {
|
||||
display: inline-block;
|
||||
white-space: normal;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .cell-collapse-preview .expandInputIcon {
|
||||
position: relative;
|
||||
left: 0px;
|
||||
padding: 2px;
|
||||
border-radius: 5px;
|
||||
vertical-align:middle;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
|
||||
cursor: pointer;
|
||||
z-index: var(--z-index-notebook-input-collapse-condicon);
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .cell-collapse-preview .expandInputIcon:before {
|
||||
|
@ -369,15 +376,6 @@
|
|||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .codicon {
|
||||
position: absolute;
|
||||
padding: 4px 6px;
|
||||
left: -30px;
|
||||
bottom: 0;
|
||||
cursor: pointer;
|
||||
z-index: var(--z-index-notebook-input-collapse-condicon);
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .output-collapse-container {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -428,10 +426,6 @@
|
|||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapsed .notebook-folding-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .notebook-folding-indicator.mouseover .codicon.codicon-notebook-expanded {
|
||||
opacity: 0;
|
||||
transition: opacity 0.1 s;
|
||||
|
@ -441,6 +435,7 @@
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .notebook-folding-indicator.mouseover .codicon.codicon-notebook-expanded,
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .notebook-folding-indicator.mouseover .codicon.codicon-notebook-expanded {
|
||||
opacity: 1;
|
||||
}
|
||||
|
@ -576,6 +571,8 @@
|
|||
position: absolute;
|
||||
flex-shrink: 0;
|
||||
z-index: var(--z-index-run-button-container);
|
||||
width: 35px;
|
||||
left: -35px;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container .monaco-toolbar {
|
||||
|
@ -594,7 +591,7 @@
|
|||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .run-button-container .monaco-toolbar,
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .run-button-container .monaco-toolbar,
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-run-toolbar-dropdown-active .run-button-container .monaco-toolbar,
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-output-hover .run-button-container .monaco-toolbar {
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-output-hover .run-button-container .monaco-toolbar {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
|
@ -605,6 +602,8 @@
|
|||
white-space: pre;
|
||||
box-sizing: border-box;
|
||||
opacity: .7;
|
||||
width: 35px;
|
||||
right: 0px;
|
||||
|
||||
/* Sizing hacks */
|
||||
bottom: 0px;
|
||||
|
@ -706,11 +705,17 @@
|
|||
border-color: var(--notebook-focused-cell-border-color) !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left .codeOutput-focus-indicator {
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left .codeOutput-focus-indicator-container {
|
||||
display: none;
|
||||
position: relative;
|
||||
width: 0px;
|
||||
cursor: pointer;
|
||||
pointer-events: all; /* Take pointer-events in markdown cell */
|
||||
width: 11px;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left .codeOutput-focus-indicator {
|
||||
width: 0px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left,
|
||||
|
|
|
@ -86,7 +86,6 @@ import 'vs/workbench/contrib/notebook/browser/contrib/undoRedo/notebookUndoRedo'
|
|||
import 'vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands';
|
||||
import 'vs/workbench/contrib/notebook/browser/contrib/viewportCustomMarkdown/viewportCustomMarkdown';
|
||||
import 'vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout';
|
||||
import 'vs/workbench/contrib/notebook/browser/contrib/codeRenderer/codeRenderer';
|
||||
import 'vs/workbench/contrib/notebook/browser/contrib/breakpoints/notebookBreakpoints';
|
||||
import 'vs/workbench/contrib/notebook/browser/contrib/execute/executionEditorProgress';
|
||||
import 'vs/workbench/contrib/notebook/browser/contrib/execute/execution';
|
||||
|
@ -95,7 +94,6 @@ import 'vs/workbench/contrib/notebook/browser/contrib/execute/execution';
|
|||
import 'vs/workbench/contrib/notebook/browser/diff/notebookDiffActions';
|
||||
|
||||
// Output renderers registration
|
||||
import 'vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform';
|
||||
import { editorOptionsRegistry } from 'vs/editor/common/config/editorOptions';
|
||||
import { NotebookExecutionStateService } from 'vs/workbench/contrib/notebook/browser/notebookExecutionStateServiceImpl';
|
||||
import { NotebookExecutionService } from 'vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl';
|
||||
|
@ -105,6 +103,7 @@ import { NotebookKeymapService } from 'vs/workbench/contrib/notebook/browser/not
|
|||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry';
|
||||
import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
|
||||
|
||||
/*--------------------------------------------------------------------------------------------- */
|
||||
|
||||
|
@ -395,7 +394,6 @@ class CellInfoContentProvider {
|
|||
|
||||
private _getResult(data: {
|
||||
notebook: URI;
|
||||
handle: number;
|
||||
outputId?: string | undefined;
|
||||
}, cell: NotebookCellTextModel) {
|
||||
let result: { content: string; mode: ILanguageSelection } | undefined = undefined;
|
||||
|
@ -437,7 +435,7 @@ class CellInfoContentProvider {
|
|||
}
|
||||
|
||||
const ref = await this._notebookModelResolverService.resolve(data.notebook);
|
||||
const cell = ref.object.notebook.cells.find(cell => cell.handle === data.handle);
|
||||
const cell = ref.object.notebook.cells.find(cell => !!cell.outputs.find(op => op.outputId === data.outputId));
|
||||
|
||||
if (!cell) {
|
||||
return null;
|
||||
|
@ -615,12 +613,35 @@ class ComplexNotebookWorkingCopyEditorHandler extends Disposable implements IWor
|
|||
}
|
||||
}
|
||||
|
||||
class NotebookLanguageSelectorScoreRefine {
|
||||
|
||||
constructor(
|
||||
@INotebookService private readonly _notebookService: INotebookService,
|
||||
@ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService,
|
||||
) {
|
||||
languageFeaturesService.setNotebookTypeResolver(this._getNotebookType.bind(this));
|
||||
}
|
||||
|
||||
private _getNotebookType(uri: URI): string | undefined {
|
||||
const cellUri = CellUri.parse(uri);
|
||||
if (!cellUri) {
|
||||
return undefined;
|
||||
}
|
||||
const notebook = this._notebookService.getNotebookTextModel(cellUri.notebook);
|
||||
if (!notebook) {
|
||||
return undefined;
|
||||
}
|
||||
return notebook.viewType;
|
||||
}
|
||||
}
|
||||
|
||||
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(NotebookContribution, LifecyclePhase.Starting);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(CellContentProvider, LifecyclePhase.Starting);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(CellInfoContentProvider, LifecyclePhase.Starting);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(RegisterSchemasContribution, LifecyclePhase.Starting);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(NotebookEditorManager, LifecyclePhase.Ready);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(NotebookLanguageSelectorScoreRefine, LifecyclePhase.Ready);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(SimpleNotebookWorkingCopyEditorHandler, LifecyclePhase.Ready);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(ComplexNotebookWorkingCopyEditorHandler, LifecyclePhase.Ready);
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import { IEditorPane } from 'vs/workbench/common/editor';
|
|||
import { CellViewModelStateChangeEvent, NotebookCellStateChangedEvent, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents';
|
||||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { CellKind, ICellOutput, INotebookCellStatusBarItem, INotebookRendererInfo, INotebookSearchOptions, IOrderedMimeType, IOutputItemDto, NotebookCellInternalMetadata, NotebookCellMetadata, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellKind, ICellOutput, INotebookCellStatusBarItem, INotebookRendererInfo, INotebookSearchOptions, IOrderedMimeType, NotebookCellInternalMetadata, NotebookCellMetadata, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { isCompositeNotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput';
|
||||
import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
|
||||
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
|
||||
|
@ -52,18 +52,10 @@ export const KERNEL_EXTENSIONS = new Map<string, string>([
|
|||
|
||||
//#region Output related types
|
||||
export const enum RenderOutputType {
|
||||
Mainframe,
|
||||
Html,
|
||||
Extension
|
||||
}
|
||||
|
||||
export interface IRenderMainframeOutput {
|
||||
type: RenderOutputType.Mainframe;
|
||||
supportAppend?: boolean;
|
||||
initHeight?: number;
|
||||
disposable?: IDisposable;
|
||||
}
|
||||
|
||||
export interface IRenderPlainHtmlOutput {
|
||||
type: RenderOutputType.Html;
|
||||
source: IDisplayOutputViewModel;
|
||||
|
@ -78,28 +70,6 @@ export interface IRenderOutputViaExtension {
|
|||
}
|
||||
|
||||
export type IInsetRenderOutput = IRenderPlainHtmlOutput | IRenderOutputViaExtension;
|
||||
export type IRenderOutput = IRenderMainframeOutput | IInsetRenderOutput;
|
||||
|
||||
export interface IOutputTransformContribution {
|
||||
getType(): RenderOutputType;
|
||||
getMimetypes(): string[];
|
||||
/**
|
||||
* Dispose this contribution.
|
||||
*/
|
||||
dispose(): void;
|
||||
|
||||
/**
|
||||
* Returns contents to place in the webview inset, or the {@link IRenderNoOutput}.
|
||||
* This call is allowed to have side effects, such as placing output
|
||||
* directly into the container element.
|
||||
*/
|
||||
render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement, notebookUri: URI): IRenderOutput;
|
||||
}
|
||||
|
||||
export interface IOutputRenderer {
|
||||
render(viewModel: ICellOutputViewModel, container: HTMLElement, preferredMimeType: string | undefined, notebookUri: URI): IRenderOutput;
|
||||
getContribution(preferredMimeType: string): IOutputTransformContribution | undefined;
|
||||
}
|
||||
|
||||
export interface ICellOutputViewModel extends IDisposable {
|
||||
cellViewModel: IGenericCellViewModel;
|
||||
|
@ -109,7 +79,6 @@ export interface ICellOutputViewModel extends IDisposable {
|
|||
model: ICellOutput;
|
||||
resolveMimeTypes(textModel: NotebookTextModel, kernelProvides: readonly string[] | undefined): [readonly IOrderedMimeType[], number];
|
||||
pickedMimeType: IOrderedMimeType | undefined;
|
||||
supportAppend(): boolean;
|
||||
hasMultiMimeType(): boolean;
|
||||
toRawJSON(): any;
|
||||
}
|
||||
|
@ -149,12 +118,6 @@ export interface ICommonCellInfo {
|
|||
cellUri: URI;
|
||||
}
|
||||
|
||||
export interface INotebookCellOutputLayoutInfo {
|
||||
width: number;
|
||||
height: number;
|
||||
fontInfo: FontInfo;
|
||||
}
|
||||
|
||||
export interface IFocusNotebookCellOptions {
|
||||
readonly skipReveal?: boolean;
|
||||
}
|
||||
|
@ -172,6 +135,7 @@ export interface CodeCellLayoutInfo {
|
|||
readonly fontInfo: FontInfo | null;
|
||||
readonly editorHeight: number;
|
||||
readonly editorWidth: number;
|
||||
readonly statusBarHeight: number;
|
||||
readonly totalHeight: number;
|
||||
readonly outputContainerOffset: number;
|
||||
readonly outputTotalHeight: number;
|
||||
|
@ -197,6 +161,7 @@ export interface MarkdownCellLayoutInfo {
|
|||
readonly fontInfo: FontInfo | null;
|
||||
readonly editorWidth: number;
|
||||
readonly editorHeight: number;
|
||||
readonly statusBarHeight: number;
|
||||
readonly previewHeight: number;
|
||||
readonly bottomToolbarOffset: number;
|
||||
readonly totalHeight: number;
|
||||
|
@ -226,7 +191,7 @@ export interface ICellViewModel extends IGenericCellViewModel {
|
|||
readonly model: NotebookCellTextModel;
|
||||
readonly id: string;
|
||||
readonly textBuffer: IReadonlyTextBuffer;
|
||||
readonly layoutInfo: { totalHeight: number; bottomToolbarOffset: number; editorWidth: number };
|
||||
readonly layoutInfo: { totalHeight: number; bottomToolbarOffset: number; editorWidth: number; editorHeight: number; statusBarHeight: number };
|
||||
readonly onDidChangeLayout: Event<ICommonCellViewModelLayoutChangeInfo>;
|
||||
readonly onDidChangeCellStatusBarItems: Event<void>;
|
||||
readonly onCellDecorationsChanged: Event<{ added: INotebookCellDecorationOptions[]; removed: INotebookCellDecorationOptions[] }>;
|
||||
|
@ -421,6 +386,7 @@ export interface INotebookEditor {
|
|||
readonly notebookOptions: NotebookOptions;
|
||||
readonly isDisposed: boolean;
|
||||
readonly activeKernel: INotebookKernel | undefined;
|
||||
readonly scrollTop: number;
|
||||
//#endregion
|
||||
|
||||
getLength(): number;
|
||||
|
@ -467,11 +433,6 @@ export interface INotebookEditor {
|
|||
|
||||
getVisibleRangesPlusViewportBelow(): ICellRange[];
|
||||
|
||||
/**
|
||||
* Fetch the output renderers for notebook outputs.
|
||||
*/
|
||||
getOutputRenderer(): IOutputRenderer;
|
||||
|
||||
/**
|
||||
* Focus the container of a cell (the monaco editor inside is not focused).
|
||||
*/
|
||||
|
@ -642,6 +603,8 @@ export interface INotebookEditor {
|
|||
findStop(): void;
|
||||
showProgress(): void;
|
||||
hideProgress(): void;
|
||||
|
||||
getAbsoluteTopOfElement(cell: ICellViewModel): number;
|
||||
}
|
||||
|
||||
export interface IActiveNotebookEditor extends INotebookEditor {
|
||||
|
|
|
@ -43,7 +43,7 @@ import { contrastBorder, diffInserted, diffRemoved, editorBackground, errorForeg
|
|||
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { PANEL_BORDER, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugColors';
|
||||
import { CellEditState, CellFindMatchWithIndex, CellFocusMode, CellLayoutContext, IActiveNotebookEditorDelegate, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IFocusNotebookCellOptions, IGenericCellViewModel, IInsetRenderOutput, IModelDecorationsChangeAccessor, INotebookCellOutputLayoutInfo, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorDelegate, INotebookEditorMouseEvent, INotebookEditorOptions, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookWebviewMessage, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellEditState, CellFindMatchWithIndex, CellFocusMode, CellLayoutContext, IActiveNotebookEditorDelegate, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IFocusNotebookCellOptions, IInsetRenderOutput, IModelDecorationsChangeAccessor, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorDelegate, INotebookEditorMouseEvent, INotebookEditorOptions, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookWebviewMessage, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions';
|
||||
import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService';
|
||||
import { notebookDebug } from 'vs/workbench/contrib/notebook/browser/notebookLogger';
|
||||
|
@ -52,7 +52,6 @@ import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/vie
|
|||
import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellDnd';
|
||||
import { NotebookCellList, NOTEBOOK_WEBVIEW_BOUNDARY } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList';
|
||||
import { INotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon';
|
||||
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
|
||||
import { BackLayerWebView } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView';
|
||||
import { CodeCellRenderer, MarkupCellRenderer, NotebookCellListDelegate } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer';
|
||||
import { IAckOutputHeight, IMarkupCellInitialization } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages';
|
||||
|
@ -66,7 +65,7 @@ import { NotebookEditorToolbar } from 'vs/workbench/contrib/notebook/browser/vie
|
|||
import { NotebookEditorContextKeys } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys';
|
||||
import { ListTopCellToolbar } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookTopCellToolbar';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { BUILTIN_RENDERER_ID, CellKind, INotebookSearchOptions, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellKind, INotebookSearchOptions, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys';
|
||||
import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService';
|
||||
import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
|
@ -332,8 +331,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
private readonly _editorFocus: IContextKey<boolean>;
|
||||
private readonly _outputFocus: IContextKey<boolean>;
|
||||
private readonly _editorEditable: IContextKey<boolean>;
|
||||
|
||||
private _outputRenderer: OutputRenderer;
|
||||
protected readonly _contributions = new Map<string, INotebookEditorContribution>();
|
||||
private _scrollBeyondLastLine: boolean;
|
||||
private readonly _insetModifyQueueByOutputId = new SequencerByKey<string>();
|
||||
|
@ -444,11 +441,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
}
|
||||
}));
|
||||
|
||||
const that = this;
|
||||
this._outputRenderer = this._register(this.instantiationService.createInstance(OutputRenderer, {
|
||||
get creationOptions() { return that.creationOptions; },
|
||||
getCellOutputLayoutInfo: that._getCellOutputLayoutInfo.bind(that)
|
||||
}));
|
||||
this._scrollBeyondLastLine = this.configurationService.getValue<boolean>('editor.scrollBeyondLastLine');
|
||||
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => {
|
||||
|
@ -760,16 +752,22 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
}`);
|
||||
} else {
|
||||
styleSheets.push(`
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-left .codeOutput-focus-indicator,
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.selected .cell-focus-indicator-left .codeOutput-focus-indicator {
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left .codeOutput-focus-indicator {
|
||||
border-left: 3px solid transparent;
|
||||
border-radius: 4px;
|
||||
margin-left: ${focusIndicatorLeftMargin}px;
|
||||
width: 0px;
|
||||
margin-left: ${focusIndicatorLeftMargin}px;
|
||||
border-color: var(--notebook-inactive-focused-cell-border-color) !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-left .codeOutput-focus-indicator:hover,
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.selected .cell-focus-indicator-left .codeOutput-focus-indicator:hover {
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-left .codeOutput-focus-indicator-container,
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-output-hover .cell-focus-indicator-left .codeOutput-focus-indicator-container,
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .markdown-cell-hover .cell-focus-indicator-left .codeOutput-focus-indicator-container,
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row:hover .cell-focus-indicator-left .codeOutput-focus-indicator-container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left .codeOutput-focus-indicator-container:hover .codeOutput-focus-indicator {
|
||||
border-left: 5px solid transparent;
|
||||
margin-left: ${focusIndicatorLeftMargin - 1}px;
|
||||
}
|
||||
|
@ -781,12 +779,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
border-color: var(--notebook-focused-cell-border-color) !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-inner-container .cell-focus-indicator-left .code-focus-indicator,
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-inner-container .cell-focus-indicator-left .output-focus-indicator {
|
||||
border-color: var(--notebook-inactive-focused-cell-border-color) !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-inner-container .cell-focus-indicator-left .output-focus-indicator {
|
||||
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-inner-container .cell-focus-indicator-left .output-focus-indicator {
|
||||
margin-top: ${focusIndicatorGap}px;
|
||||
}
|
||||
`);
|
||||
|
@ -849,10 +842,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
styleSheets.push(`.notebookOverlay .output { margin: 0px ${cellRightMargin}px 0px ${codeCellLeftMargin + cellRunGutter}px; }`);
|
||||
styleSheets.push(`.notebookOverlay .output { width: calc(100% - ${codeCellLeftMargin + cellRunGutter + cellRightMargin}px); }`);
|
||||
|
||||
// output toolbar
|
||||
styleSheets.push(`.monaco-workbench .notebookOverlay .output .cell-output-toolbar { left: -${cellRunGutter}px; }`);
|
||||
styleSheets.push(`.monaco-workbench .notebookOverlay .output .cell-output-toolbar { width: ${cellRunGutter}px; }`);
|
||||
|
||||
// output collapse button
|
||||
styleSheets.push(`.monaco-workbench .notebookOverlay .output .output-collapse-container .expandButton { left: -${cellRunGutter}px; }`);
|
||||
styleSheets.push(`.monaco-workbench .notebookOverlay .output .output-collapse-container .expandButton {
|
||||
|
@ -864,8 +853,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
// show more container
|
||||
styleSheets.push(`.notebookOverlay .output-show-more-container { margin: 0px ${cellRightMargin}px 0px ${codeCellLeftMargin + cellRunGutter}px; }`);
|
||||
styleSheets.push(`.notebookOverlay .output-show-more-container { width: calc(100% - ${codeCellLeftMargin + cellRunGutter + cellRightMargin}px); }`);
|
||||
styleSheets.push(`.notebookOverlay .cell .run-button-container { width: ${cellRunGutter}px; left: ${codeCellLeftMargin}px }`);
|
||||
styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .execution-count-label { left: ${codeCellLeftMargin}px; width: ${cellRunGutter}px; }`);
|
||||
|
||||
styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.markdown { padding-left: ${cellRunGutter}px; }`);
|
||||
styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator { left: ${(markdownCellGutter - 20) / 2 + markdownCellLeftMargin}px; }`);
|
||||
|
@ -882,6 +869,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .cell-collapse-preview {
|
||||
line-height: ${collapsedIndicatorHeight}px;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .cell-collapse-preview .monaco-tokenized-source {
|
||||
max-height: ${collapsedIndicatorHeight}px;
|
||||
}
|
||||
`);
|
||||
|
||||
styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-toolbar { height: ${bottomToolbarHeight}px }`);
|
||||
|
@ -1938,6 +1929,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
});
|
||||
}
|
||||
|
||||
get scrollTop() {
|
||||
return this._list.scrollTop;
|
||||
}
|
||||
|
||||
getAbsoluteTopOfElement(cell: ICellViewModel) {
|
||||
return this._list.getAbsoluteTopOfElement(cell);
|
||||
}
|
||||
|
||||
isScrolledToBottom() {
|
||||
return this._listViewInfoAccessor.isScrolledToBottom();
|
||||
}
|
||||
|
@ -2360,25 +2359,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
return;
|
||||
}
|
||||
|
||||
if (pickedMimeTypeRenderer.rendererId === BUILTIN_RENDERER_ID) {
|
||||
const renderer = this.getOutputRenderer().getContribution(pickedMimeTypeRenderer.mimeType);
|
||||
if (renderer?.getType() === RenderOutputType.Html) {
|
||||
const renderResult = renderer.render(output, output.model.outputs.filter(op => op.mime === pickedMimeTypeRenderer.mimeType)[0], DOM.$(''), this.textModel!.uri) as IInsetRenderOutput;
|
||||
if (!this._webview?.insetMapping.has(renderResult.source)) {
|
||||
const p = new Promise<void>(resolve => {
|
||||
this.onDidRenderOutput(e => {
|
||||
if (e.model === renderResult.source.model) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
this.createOutput(viewCell, renderResult, 0);
|
||||
await p;
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
const renderer = this._notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);
|
||||
|
||||
if (!renderer) {
|
||||
|
@ -2547,30 +2527,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
};
|
||||
}
|
||||
|
||||
private _getCellOutputLayoutInfo(cell: IGenericCellViewModel): INotebookCellOutputLayoutInfo {
|
||||
if (!this._list) {
|
||||
throw new Error('Editor is not initalized successfully');
|
||||
}
|
||||
|
||||
if (!this._fontInfo) {
|
||||
this._generateFontInfo();
|
||||
}
|
||||
|
||||
const {
|
||||
cellRunGutter,
|
||||
codeCellLeftMargin,
|
||||
cellRightMargin
|
||||
} = this._notebookOptions.getLayoutConfiguration();
|
||||
|
||||
const width = (this._dimension?.width ?? 0) - (codeCellLeftMargin + cellRunGutter + cellRightMargin) - 8 /** padding */ * 2;
|
||||
|
||||
return {
|
||||
width: Math.max(width, 0),
|
||||
height: this._dimension?.height ?? 0,
|
||||
fontInfo: this._fontInfo!
|
||||
};
|
||||
}
|
||||
|
||||
async createMarkupPreview(cell: MarkupCellViewModel) {
|
||||
if (!this._webview) {
|
||||
return;
|
||||
|
@ -2706,10 +2662,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
}
|
||||
}
|
||||
|
||||
getOutputRenderer(): OutputRenderer {
|
||||
return this._outputRenderer;
|
||||
}
|
||||
|
||||
//#region --- webview IPC ----
|
||||
postMessage(message: any) {
|
||||
if (this._webview?.isResolved()) {
|
||||
|
|
|
@ -225,6 +225,15 @@ export class NotebookKernelService extends Disposable implements INotebookKernel
|
|||
}
|
||||
}
|
||||
|
||||
preselectKernelForNotebook(kernel: INotebookKernel, notebook: INotebookTextModelLike): void {
|
||||
const key = NotebookTextModelLikeId.str(notebook);
|
||||
const oldKernel = this._notebookBindings.get(key);
|
||||
if (oldKernel !== kernel?.id) {
|
||||
this._notebookBindings.set(key, kernel.id);
|
||||
this._persistMementos();
|
||||
}
|
||||
}
|
||||
|
||||
updateKernelNotebookAffinity(kernel: INotebookKernel, notebook: URI, preference: number | undefined): void {
|
||||
const info = this._kernels.get(kernel.id);
|
||||
if (!info) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue