Merge branch 'main' into joh/disposeSnapshot

This commit is contained in:
Johannes Rieken 2022-02-11 14:35:09 +01:00
commit 1735c9913d
No known key found for this signature in database
GPG key ID: 96634B5AF12F8798
169 changed files with 3413 additions and 2817 deletions

View file

@ -449,6 +449,7 @@
"vs/workbench/~",
"vs/workbench/services/*/~",
"vs/workbench/contrib/*/~",
"vscode-notebook-renderer", // Type of import
{
"when": "hasBrowser",
"pattern": "xterm"

View file

@ -1 +1 @@
2022-02-09T15:22:23.902Z
2022-02-10T01:18:08.504Z

View file

@ -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

View file

@ -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) {

View file

@ -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"
},

View file

@ -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"

View file

@ -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)) &&

View file

@ -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",

View file

@ -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"

View file

@ -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"
]
}
]

View file

@ -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;
}

View 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')));
}

View file

@ -16,7 +16,8 @@
{
"id": "razor",
"extensions": [
".cshtml"
".cshtml",
".razor"
],
"aliases": [
"Razor",

View file

@ -19,7 +19,6 @@
{
"name": "Types declaration and references",
"scope": [
"meta.return-type",
"support.class",
"support.type",
"entity.name.type",

View file

@ -353,7 +353,6 @@
{
"name": "Types declaration and references",
"scope": [
"meta.return-type",
"support.class",
"support.type",
"entity.name.type",

View file

@ -19,7 +19,6 @@
{
"name": "Types declaration and references",
"scope": [
"meta.return-type",
"support.class",
"support.type",
"entity.name.type",

View file

@ -6,7 +6,7 @@
"git": {
"name": "seti-ui",
"repositoryUrl": "https://github.com/jesseweed/seti-ui",
"commitHash": "bc194faed12b10692807f47b97f0ff963e4c9f24"
"commitHash": "529789dc4eee3e2fdc5985f082ee58adfb97696c"
}
},
"version": "0.1.0"

View file

@ -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"
}

View file

@ -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",

View file

@ -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"
},

View file

@ -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;
}
}
}

View file

@ -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 () {

View file

@ -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"
}
},
{

View file

@ -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"
}
},
{

View file

@ -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",

View file

@ -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",

View file

@ -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"
},

View file

@ -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"

View file

@ -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==

View file

@ -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"

View file

@ -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

View file

@ -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 {

View file

@ -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 {

View file

@ -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;
}

View file

@ -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,

View file

@ -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

View file

@ -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') {

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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();
}
}

View file

@ -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;

View file

@ -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 {

View file

@ -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);
}

View file

@ -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);
});
});

View file

@ -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;
}

View file

@ -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[] };

View file

@ -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
});
}
});

View file

@ -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
});
}
}

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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 {

View file

@ -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;

View file

@ -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> {

View file

@ -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.

View file

@ -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');

View file

@ -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

View file

@ -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;
}
}
}

View file

@ -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;
}
});
}
}

View file

@ -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);

View file

@ -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 {

View file

@ -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
};
}

View file

@ -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) { }

View file

@ -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];

View file

@ -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;

View file

@ -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

View file

@ -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);
}

View file

@ -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,

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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';

View file

@ -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, '');
});

View file

@ -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 {

View file

@ -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;
}
}
}

View file

@ -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) {

View file

@ -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');
}
}

View 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;
}

View file

@ -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.

View file

@ -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
};
}

View file

@ -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);
}
}

View file

@ -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));

View file

@ -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)`);
}
}
}

View file

@ -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),

View file

@ -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}**&nbsp;<span style="background-color:#8080802B;">**&nbsp;_v${this.extension.version}_**&nbsp;</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(`**&nbsp;**&nbsp;<span style="background-color:${extensionPreReleaseIcon ? Color.Format.CSS.formatHex(extensionPreReleaseIcon) : '#ffffff'};">&nbsp;$(${preReleaseIcon.id})&nbsp;${localize('pre-release-label', "Pre-Release")}&nbsp;</span>`);
markdown.appendMarkdown(`**&nbsp;**&nbsp;<span style="color:#ffffff;background-color:${extensionPreReleaseIcon ? Color.Format.CSS.formatHex(extensionPreReleaseIcon) : '#ffffff'};">&nbsp;$(${preReleaseIcon.id})&nbsp;${localize('pre-release-label', "Pre-Release")}&nbsp;</span>`);
}
markdown.appendText(`\n`);

View file

@ -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();

View file

@ -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;

View file

@ -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',

View file

@ -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' });
}
}

View file

@ -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,
};
}

View file

@ -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) {

View file

@ -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) {

View file

@ -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;

View file

@ -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);

View file

@ -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,

View file

@ -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);

View file

@ -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 {

View file

@ -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()) {

View file

@ -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