Archived
1
0

chore(vscode): update to 1.56.0

This commit is contained in:
Akash Satheesan
2021-04-30 20:25:17 +05:30
1749 changed files with 88014 additions and 43316 deletions

View File

@ -98,7 +98,7 @@ exports.removeGlobalNodeModuleLookupPaths = function () {
/**
* Helper to enable portable mode.
*
* @param {Partial<import('./vs/platform/product/common/productService').IProductConfiguration>} product
* @param {Partial<import('./vs/base/common/product').IProductConfiguration>} product
* @returns {{ portableDataPath: string; isPortable: boolean; }}
*/
exports.configurePortable = function (product) {

View File

@ -22,45 +22,65 @@
}
}(this, function () {
const bootstrapLib = bootstrap();
const preloadGlobals = globals();
const webFrame = preloadGlobals.webFrame;
const preloadGlobals = sandboxGlobals();
const safeProcess = preloadGlobals.process;
const configuration = parseWindowConfiguration();
const useCustomProtocol = safeProcess.sandboxed || typeof safeProcess.env['ENABLE_VSCODE_BROWSER_CODE_LOADING'] === 'string';
// Start to resolve process.env before anything gets load
// so that we can run loading and resolving in parallel
const whenEnvResolved = safeProcess.resolveEnv(configuration.userEnv);
const useCustomProtocol = safeProcess.sandboxed || typeof safeProcess.env['VSCODE_BROWSER_CODE_LOADING'] === 'string';
/**
* @typedef {import('./vs/base/parts/sandbox/common/sandboxTypes').ISandboxConfiguration} ISandboxConfiguration
*
* @param {string[]} modulePaths
* @param {(result: unknown, configuration: object) => Promise<unknown> | undefined} resultCallback
* @param {{ forceEnableDeveloperKeybindings?: boolean, disallowReloadKeybinding?: boolean, removeDeveloperKeybindingsAfterLoad?: boolean, canModifyDOM?: (config: object) => void, beforeLoaderConfig?: (config: object, loaderConfig: object) => void, beforeRequire?: () => void }=} options
* @param {(result: unknown, configuration: ISandboxConfiguration) => Promise<unknown> | undefined} resultCallback
* @param {{
* configureDeveloperSettings?: (config: ISandboxConfiguration) => {
* forceDisableShowDevtoolsOnError?: boolean,
* forceEnableDeveloperKeybindings?: boolean,
* disallowReloadKeybinding?: boolean,
* removeDeveloperKeybindingsAfterLoad?: boolean
* },
* canModifyDOM?: (config: ISandboxConfiguration) => void,
* beforeLoaderConfig?: (loaderConfig: object) => void,
* beforeRequire?: () => void
* }} [options]
*/
function load(modulePaths, resultCallback, options) {
async function load(modulePaths, resultCallback, options) {
// Apply zoom level early to avoid glitches
const zoomLevel = configuration.zoomLevel;
if (typeof zoomLevel === 'number' && zoomLevel !== 0) {
webFrame.setZoomLevel(zoomLevel);
}
// Error handler
safeProcess.on('uncaughtException', function (error) {
onUnexpectedError(error, enableDeveloperTools);
// Error handler (TODO@sandbox non-sandboxed only)
let showDevtoolsOnError = !!safeProcess.env['VSCODE_DEV'];
safeProcess.on('uncaughtException', function (/** @type {string | Error} */ error) {
onUnexpectedError(error, showDevtoolsOnError);
});
// Developer tools
const enableDeveloperTools = (safeProcess.env['VSCODE_DEV'] || !!configuration.extensionDevelopmentPath) && !configuration.extensionTestsPath;
let developerToolsUnbind;
if (enableDeveloperTools || (options && options.forceEnableDeveloperKeybindings)) {
developerToolsUnbind = registerDeveloperKeybindings(options && options.disallowReloadKeybinding);
// Await window configuration from preload
performance.mark('code/willWaitForWindowConfig');
/** @type {ISandboxConfiguration} */
const configuration = await preloadGlobals.context.resolveConfiguration();
performance.mark('code/didWaitForWindowConfig');
// Developer settings
const {
forceDisableShowDevtoolsOnError,
forceEnableDeveloperKeybindings,
disallowReloadKeybinding,
removeDeveloperKeybindingsAfterLoad
} = typeof options?.configureDeveloperSettings === 'function' ? options.configureDeveloperSettings(configuration) : {
forceDisableShowDevtoolsOnError: false,
forceEnableDeveloperKeybindings: false,
disallowReloadKeybinding: false,
removeDeveloperKeybindingsAfterLoad: false
};
showDevtoolsOnError = safeProcess.env['VSCODE_DEV'] && !forceDisableShowDevtoolsOnError;
const enableDeveloperKeybindings = safeProcess.env['VSCODE_DEV'] || forceEnableDeveloperKeybindings;
let developerDeveloperKeybindingsDisposable;
if (enableDeveloperKeybindings) {
developerDeveloperKeybindingsDisposable = registerDeveloperKeybindings(disallowReloadKeybinding);
}
// Enable ASAR support
globalThis.MonacoBootstrap.enableASARSupport(configuration.appRoot);
if (options && typeof options.canModifyDOM === 'function') {
// Signal DOM modifications are now OK
if (typeof options?.canModifyDOM === 'function') {
options.canModifyDOM(configuration);
}
@ -76,24 +96,22 @@
window.document.documentElement.setAttribute('lang', locale);
// do not advertise AMD to avoid confusing UMD modules loaded with nodejs
// Do not advertise AMD to avoid confusing UMD modules loaded with nodejs
if (!useCustomProtocol) {
window['define'] = undefined;
}
// replace the patched electron fs with the original node fs for all AMD code (TODO@sandbox non-sandboxed only)
// Replace the patched electron fs with the original node fs for all AMD code (TODO@sandbox non-sandboxed only)
if (!safeProcess.sandboxed) {
require.define('fs', [], function () { return require.__$__nodeRequire('original-fs'); });
}
window['MonacoEnvironment'] = {};
const baseUrl = useCustomProtocol ?
`${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32', scheme: 'vscode-file', fallbackAuthority: 'vscode-app' })}/out` :
`${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32' })}/out`;
const loaderConfig = {
baseUrl,
baseUrl: useCustomProtocol ?
`${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32', scheme: 'vscode-file', fallbackAuthority: 'vscode-app' })}/out` :
`${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32' })}/out`,
'vs/nls': nlsConfig,
preferScriptTags: useCustomProtocol
};
@ -129,7 +147,7 @@
loaderConfig.amdModulesPattern = /^vs\//;
}
// cached data config
// Cached data config
if (configuration.nodeCachedDataDir) {
loaderConfig.nodeCachedData = {
path: configuration.nodeCachedDataDir,
@ -137,29 +155,34 @@
};
}
if (options && typeof options.beforeLoaderConfig === 'function') {
options.beforeLoaderConfig(configuration, loaderConfig);
// Signal before require.config()
if (typeof options?.beforeLoaderConfig === 'function') {
options.beforeLoaderConfig(loaderConfig);
}
// Configure loader
require.config(loaderConfig);
// Handle pseudo NLS
if (nlsConfig.pseudo) {
require(['vs/nls'], function (nlsPlugin) {
nlsPlugin.setPseudoTranslation(nlsConfig.pseudo);
});
}
if (options && typeof options.beforeRequire === 'function') {
// Signal before require()
if (typeof options?.beforeRequire === 'function') {
options.beforeRequire();
}
// Actually require the main module as specified
require(modulePaths, async result => {
try {
// Wait for process environment being fully resolved
performance.mark('code/willWaitForShellEnv');
if (!safeProcess.env['VSCODE_SKIP_PROCESS_ENV_PATCHING'] /* TODO@bpasero for https://github.com/microsoft/vscode/issues/108804 */) {
await whenEnvResolved;
await safeProcess.shellEnv();
}
performance.mark('code/didWaitForShellEnv');
@ -168,39 +191,16 @@
if (callbackResult instanceof Promise) {
await callbackResult;
if (developerToolsUnbind && options && options.removeDeveloperKeybindingsAfterLoad) {
developerToolsUnbind();
if (developerDeveloperKeybindingsDisposable && removeDeveloperKeybindingsAfterLoad) {
developerDeveloperKeybindingsDisposable();
}
}
} catch (error) {
onUnexpectedError(error, enableDeveloperTools);
onUnexpectedError(error, enableDeveloperKeybindings);
}
}, onUnexpectedError);
}
/**
* Parses the contents of the window condiguration that
* is passed into the URL from the `electron-main` side.
*
* @returns {{
* zoomLevel?: number,
* extensionDevelopmentPath?: string[],
* extensionTestsPath?: string,
* userEnv?: { [key: string]: string | undefined },
* appRoot: string,
* nodeCachedDataDir?: string
* }}
*/
function parseWindowConfiguration() {
const rawConfiguration = (window.location.search || '').split(/[?&]/)
.filter(function (param) { return !!param; })
.map(function (param) { return param.split('='); })
.filter(function (param) { return param.length === 2; })
.reduce(function (r, param) { r[param[0]] = decodeURIComponent(param[1]); return r; }, {});
return JSON.parse(rawConfiguration['config'] || '{}') || {};
}
/**
* @param {boolean | undefined} disallowReloadKeybinding
* @returns {() => void}
@ -249,10 +249,10 @@
/**
* @param {string | Error} error
* @param {boolean} [enableDeveloperTools]
* @param {boolean} [showDevtoolsOnError]
*/
function onUnexpectedError(error, enableDeveloperTools) {
if (enableDeveloperTools) {
function onUnexpectedError(error, showDevtoolsOnError) {
if (showDevtoolsOnError) {
const ipcRenderer = preloadGlobals.ipcRenderer;
ipcRenderer.send('vscode:openDevTools');
}
@ -275,13 +275,12 @@
/**
* @return {typeof import('./vs/base/parts/sandbox/electron-sandbox/globals')}
*/
function globals() {
function sandboxGlobals() {
// @ts-ignore (defined in globals.js)
return window.vscode;
}
return {
load,
globals
load
};
}));

View File

@ -173,7 +173,7 @@
/**
* @returns {typeof import('./vs/base/parts/sandbox/electron-sandbox/globals') | undefined}
*/
function safeGlobals() {
function safeSandboxGlobals() {
const globals = (typeof self === 'object' ? self : typeof global === 'object' ? global : {});
return globals.vscode;
@ -183,13 +183,13 @@
* @returns {import('./vs/base/parts/sandbox/electron-sandbox/globals').ISandboxNodeProcess | NodeJS.Process}
*/
function safeProcess() {
if (typeof process !== 'undefined') {
return process; // Native environment (non-sandboxed)
const sandboxGlobals = safeSandboxGlobals();
if (sandboxGlobals) {
return sandboxGlobals.process; // Native environment (sandboxed)
}
const globals = safeGlobals();
if (globals) {
return globals.process; // Native environment (sandboxed)
if (typeof process !== 'undefined') {
return process; // Native environment (non-sandboxed)
}
return undefined;
@ -199,9 +199,9 @@
* @returns {import('./vs/base/parts/sandbox/electron-sandbox/electronTypes').IpcRenderer | undefined}
*/
function safeIpcRenderer() {
const globals = safeGlobals();
if (globals) {
return globals.ipcRenderer;
const sandboxGlobals = safeSandboxGlobals();
if (sandboxGlobals) {
return sandboxGlobals.ipcRenderer;
}
return undefined;

View File

@ -19,5 +19,8 @@ bootstrapNode.configurePortable(product);
// Enable ASAR support
bootstrap.enableASARSupport(undefined);
// Signal processes that we got launched as CLI
process.env['VSCODE_CLI'] = '1';
// Load CLI through AMD loader
require('./bootstrap-amd').load('vs/code/node/cli');

View File

@ -6,17 +6,22 @@
//@ts-check
'use strict';
/**
* @typedef {import('./vs/base/common/product').IProductConfiguration} IProductConfiguration
* @typedef {import('./vs/base/node/languagePacks').NLSConfiguration} NLSConfiguration
* @typedef {import('./vs/platform/environment/common/argv').NativeParsedArgs} NativeParsedArgs
*/
const perf = require('./vs/base/common/performance');
perf.mark('code/didStartMain');
const path = require('path');
const fs = require('fs');
const os = require('os');
const { getNLSConfiguration } = require('./vs/base/node/languagePacks');
const bootstrap = require('./bootstrap');
const bootstrapNode = require('./bootstrap-node');
const { getUserDataPath } = require('./vs/platform/environment/node/userDataPath');
/** @type {Partial<import('./vs/platform/product/common/productService').IProductConfiguration>} */
/** @type {Partial<IProductConfiguration>} */
const product = require('../product.json');
const { app, protocol, crashReporter } = require('electron');
@ -39,7 +44,9 @@ app.setPath('userData', userDataPath);
const argvConfig = configureCommandlineSwitchesSync(args);
// Configure crash reporter
perf.mark('code/willStartCrashReporter');
configureCrashReporter();
perf.mark('code/didStartCrashReporter');
// Set logs path before app 'ready' event if running portable
// to ensure that no 'logs' folder is created on disk at a
@ -53,10 +60,7 @@ if (portable && portable.isPortable) {
protocol.registerSchemesAsPrivileged([
{
scheme: 'vscode-webview',
privileges: { standard: true, secure: true, supportFetchAPI: true, corsEnabled: true }
}, {
scheme: 'vscode-webview-resource',
privileges: { secure: true, standard: true, supportFetchAPI: true, corsEnabled: true }
privileges: { standard: true, secure: true, supportFetchAPI: true, corsEnabled: true, allowServiceWorkers: true, }
},
{
scheme: 'vscode-file',
@ -74,13 +78,14 @@ const nodeCachedDataDir = getNodeCachedDir();
* Support user defined locale: load it early before app('ready')
* to have more things running in parallel.
*
* @type {Promise<import('./vs/base/node/languagePacks').NLSConfiguration> | undefined}
* @type {Promise<NLSConfiguration> | undefined}
*/
let nlsConfigurationPromise = undefined;
const metaDataFile = path.join(__dirname, 'nls.metadata.json');
const locale = getUserDefinedLocale(argvConfig);
if (locale) {
const { getNLSConfiguration } = require('./vs/base/node/languagePacks');
nlsConfigurationPromise = getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale);
}
@ -104,7 +109,7 @@ app.once('ready', function () {
* Main startup routine
*
* @param {string | undefined} cachedDataDir
* @param {import('./vs/base/node/languagePacks').NLSConfiguration} nlsConfig
* @param {NLSConfiguration} nlsConfig
*/
function startup(cachedDataDir, nlsConfig) {
nlsConfig._languagePackSupport = true;
@ -132,7 +137,7 @@ async function onReady() {
}
/**
* @param {import('./vs/platform/environment/common/argv').NativeParsedArgs} cliArgs
* @param {NativeParsedArgs} cliArgs
*/
function configureCommandlineSwitchesSync(cliArgs) {
const SUPPORTED_ELECTRON_SWITCHES = [
@ -159,16 +164,18 @@ function configureCommandlineSwitchesSync(cliArgs) {
'enable-proposed-api',
// TODO@sandbox remove me once testing is done on `vscode-file` protocol
// (all traces of `enable-browser-code-loading` and `ENABLE_VSCODE_BROWSER_CODE_LOADING`)
// (all traces of `enable-browser-code-loading` and `VSCODE_BROWSER_CODE_LOADING`)
'enable-browser-code-loading',
// Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'.
'log-level',
'log-level'
];
// Read argv config
const argvConfig = readArgvConfigSync();
let browserCodeLoadingStrategy = undefined;
Object.keys(argvConfig).forEach(argvKey => {
const argvValue = argvConfig[argvKey];
@ -204,8 +211,10 @@ function configureCommandlineSwitchesSync(cliArgs) {
break;
case 'enable-browser-code-loading':
if (typeof argvValue === 'string') {
process.env['ENABLE_VSCODE_BROWSER_CODE_LOADING'] = argvValue;
if (argvValue === false) {
browserCodeLoadingStrategy = undefined;
} else if (typeof argvValue === 'string') {
browserCodeLoadingStrategy = argvValue;
}
break;
@ -224,9 +233,9 @@ function configureCommandlineSwitchesSync(cliArgs) {
app.commandLine.appendSwitch('js-flags', jsFlags);
}
// Support __sandbox flag
if (cliArgs.__sandbox) {
process.env['ENABLE_VSCODE_BROWSER_CODE_LOADING'] = 'bypassHeatCheck';
// Configure vscode-file:// code loading environment
if (cliArgs.__sandbox || browserCodeLoadingStrategy) {
process.env['VSCODE_BROWSER_CODE_LOADING'] = browserCodeLoadingStrategy || 'bypassHeatCheck';
}
return argvConfig;
@ -411,7 +420,7 @@ function configureCrashReporter() {
}
/**
* @param {import('./vs/platform/environment/common/argv').NativeParsedArgs} cliArgs
* @param {NativeParsedArgs} cliArgs
* @returns {string | null}
*/
function getJSFlags(cliArgs) {
@ -431,7 +440,7 @@ function getJSFlags(cliArgs) {
}
/**
* @returns {import('./vs/platform/environment/common/argv').NativeParsedArgs}
* @returns {NativeParsedArgs}
*/
function parseCLIArgs() {
const minimist = require('minimist');
@ -549,7 +558,7 @@ function mkdirp(dir) {
/**
* Resolve the NLS configuration
*
* @return {Promise<import('./vs/base/node/languagePacks').NLSConfiguration>}
* @return {Promise<NLSConfiguration>}
*/
async function resolveNlsConfiguration() {
@ -569,6 +578,7 @@ async function resolveNlsConfiguration() {
// See above the comment about the loader and case sensitiviness
appLocale = appLocale.toLowerCase();
const { getNLSConfiguration } = require('./vs/base/node/languagePacks');
nlsConfiguration = await getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale);
if (!nlsConfiguration) {
nlsConfiguration = { locale: appLocale, availableLanguages: {} };

View File

@ -4,6 +4,7 @@
"moduleResolution": "node",
"experimentalDecorators": true,
"noImplicitReturns": true,
"noImplicitOverride": true,
"noUnusedLocals": true,
"allowUnreachableCode": false,
"strict": true,

View File

@ -12,7 +12,8 @@
"semver",
"sinon",
"winreg",
"trusted-types"
"trusted-types",
"wicg-file-system-access"
],
"plugins": [
{

View File

@ -2,9 +2,7 @@
"extends": "./tsconfig.base.json",
"compilerOptions": {
"noEmit": true,
"types": [
"trusted-types"
],
"types": ["trusted-types"],
"paths": {},
"module": "amd",
"moduleResolution": "classic",
@ -27,6 +25,7 @@
"vs/platform/*/browser/*"
],
"exclude": [
"node_modules/*"
"node_modules/*",
"vs/platform/files/browser/htmlFileSystemProvider.ts"
]
}

View File

@ -20,7 +20,7 @@ export interface IContextMenuDelegate {
getActions(): readonly IAction[];
getCheckedActionsRepresentation?(action: IAction): 'radio' | 'checkbox';
getActionViewItem?(action: IAction): IActionViewItem | undefined;
getActionsContext?(event?: IContextMenuEvent): any;
getActionsContext?(event?: IContextMenuEvent): unknown;
getKeyBinding?(action: IAction): ResolvedKeybinding | undefined;
getMenuClassName?(): string;
onHide?(didCancel: boolean): void;

View File

@ -42,7 +42,7 @@ export class DelayedDragHandler extends Disposable {
}
}
dispose(): void {
override dispose(): void {
super.dispose();
this.clearDragTimeout();
@ -89,7 +89,7 @@ export function applyDragImage(event: DragEvent, label: string | null, clazz: st
export interface IDragAndDropData {
update(dataTransfer: DataTransfer): void;
getData(): any;
getData(): unknown;
}
export class DragAndDropData<T> implements IDragAndDropData {

View File

@ -1193,18 +1193,21 @@ export function computeScreenAwareSize(cssPx: number): number {
* to change the location of the current page.
* See https://mathiasbynens.github.io/rel-noopener/
*/
export function windowOpenNoOpener(url: string): void {
export function windowOpenNoOpener(url: string): boolean {
if (browser.isElectron || browser.isEdgeLegacyWebView) {
// In VSCode, window.open() always returns null...
// The same is true for a WebView (see https://github.com/microsoft/monaco-editor/issues/628)
// Also call directly window.open in sandboxed Electron (see https://github.com/microsoft/monaco-editor/issues/2220)
window.open(url);
return true;
} else {
let newTab = window.open();
if (newTab) {
(newTab as any).opener = null;
newTab.location.href = url;
return true;
}
return false;
}
}
@ -1409,37 +1412,8 @@ export function multibyteAwareBtoa(str: string): string {
*/
export namespace WebFileSystemAccess {
// https://wicg.github.io/file-system-access/#dom-window-showdirectorypicker
export interface FileSystemAccess {
showDirectoryPicker: () => Promise<FileSystemDirectoryHandle>;
}
// https://wicg.github.io/file-system-access/#api-filesystemdirectoryhandle
export interface FileSystemDirectoryHandle {
readonly kind: 'directory',
readonly name: string,
getFileHandle: (name: string, options?: { create?: boolean }) => Promise<FileSystemFileHandle>;
getDirectoryHandle: (name: string, options?: { create?: boolean }) => Promise<FileSystemDirectoryHandle>;
}
// https://wicg.github.io/file-system-access/#api-filesystemfilehandle
export interface FileSystemFileHandle {
readonly kind: 'file',
readonly name: string,
createWritable: (options?: { keepExistingData?: boolean }) => Promise<FileSystemWritableFileStream>;
}
// https://wicg.github.io/file-system-access/#api-filesystemwritablefilestream
export interface FileSystemWritableFileStream {
write: (buffer: Uint8Array) => Promise<void>;
close: () => Promise<void>;
}
export function supported(obj: any & Window): obj is FileSystemAccess {
const candidate = obj as FileSystemAccess | undefined;
if (typeof candidate?.showDirectoryPicker === 'function') {
export function supported(obj: any & Window): boolean {
if (typeof obj?.showDirectoryPicker === 'function') {
return true;
}
@ -1589,7 +1563,7 @@ export class ModifierKeyEmitter extends Emitter<IModifierKeyStatus> {
return ModifierKeyEmitter.instance;
}
dispose() {
override dispose() {
super.dispose();
this._subscriptions.dispose();
}

View File

@ -9,7 +9,7 @@ export type EventHandler = HTMLElement | HTMLDocument | Window;
export interface IDomEvent {
<K extends keyof HTMLElementEventMap>(element: EventHandler, type: K, useCapture?: boolean): BaseEvent<HTMLElementEventMap[K]>;
(element: EventHandler, type: string, useCapture?: boolean): BaseEvent<any>;
(element: EventHandler, type: string, useCapture?: boolean): BaseEvent<unknown>;
}
export const domEvent: IDomEvent = (element: EventHandler, type: string, useCapture?: boolean) => {

View File

@ -16,6 +16,7 @@ export interface FormattedTextRenderOptions {
readonly className?: string;
readonly inline?: boolean;
readonly actionHandler?: IContentActionHandler;
readonly renderCodeSegements?: boolean;
}
export function renderText(text: string, options: FormattedTextRenderOptions = {}): HTMLElement {
@ -26,7 +27,7 @@ export function renderText(text: string, options: FormattedTextRenderOptions = {
export function renderFormattedText(formattedText: string, options: FormattedTextRenderOptions = {}): HTMLElement {
const element = createElement(options);
_renderFormattedText(element, parseFormattedText(formattedText), options.actionHandler);
_renderFormattedText(element, parseFormattedText(formattedText, !!options.renderCodeSegements), options.actionHandler, options.renderCodeSegements);
return element;
}
@ -75,6 +76,7 @@ const enum FormatType {
Italics,
Action,
ActionClose,
Code,
NewLine
}
@ -85,7 +87,7 @@ interface IFormatParseTree {
children?: IFormatParseTree[];
}
function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionHandler?: IContentActionHandler) {
function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionHandler?: IContentActionHandler, renderCodeSegements?: boolean) {
let child: Node | undefined;
if (treeNode.type === FormatType.Text) {
@ -94,6 +96,8 @@ function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionH
child = document.createElement('b');
} else if (treeNode.type === FormatType.Italics) {
child = document.createElement('i');
} else if (treeNode.type === FormatType.Code && renderCodeSegements) {
child = document.createElement('code');
} else if (treeNode.type === FormatType.Action && actionHandler) {
const a = document.createElement('a');
a.href = '#';
@ -114,12 +118,12 @@ function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionH
if (child && Array.isArray(treeNode.children)) {
treeNode.children.forEach((nodeChild) => {
_renderFormattedText(child!, nodeChild, actionHandler);
_renderFormattedText(child!, nodeChild, actionHandler, renderCodeSegements);
});
}
}
function parseFormattedText(content: string): IFormatParseTree {
function parseFormattedText(content: string, parseCodeSegments: boolean): IFormatParseTree {
const root: IFormatParseTree = {
type: FormatType.Root,
@ -134,19 +138,19 @@ function parseFormattedText(content: string): IFormatParseTree {
while (!stream.eos()) {
let next = stream.next();
const isEscapedFormatType = (next === '\\' && formatTagType(stream.peek()) !== FormatType.Invalid);
const isEscapedFormatType = (next === '\\' && formatTagType(stream.peek(), parseCodeSegments) !== FormatType.Invalid);
if (isEscapedFormatType) {
next = stream.next(); // unread the backslash if it escapes a format tag type
}
if (!isEscapedFormatType && isFormatTag(next) && next === stream.peek()) {
if (!isEscapedFormatType && isFormatTag(next, parseCodeSegments) && next === stream.peek()) {
stream.advance();
if (current.type === FormatType.Text) {
current = stack.pop()!;
}
const type = formatTagType(next);
const type = formatTagType(next, parseCodeSegments);
if (current.type === type || (current.type === FormatType.Action && type === FormatType.ActionClose)) {
current = stack.pop()!;
} else {
@ -200,11 +204,11 @@ function parseFormattedText(content: string): IFormatParseTree {
return root;
}
function isFormatTag(char: string): boolean {
return formatTagType(char) !== FormatType.Invalid;
function isFormatTag(char: string, supportCodeSegments: boolean): boolean {
return formatTagType(char, supportCodeSegments) !== FormatType.Invalid;
}
function formatTagType(char: string): FormatType {
function formatTagType(char: string, supportCodeSegments: boolean): FormatType {
switch (char) {
case '*':
return FormatType.Bold;
@ -214,6 +218,8 @@ function formatTagType(char: string): FormatType {
return FormatType.Action;
case ']':
return FormatType.ActionClose;
case '`':
return supportCodeSegments ? FormatType.Code : FormatType.Invalid;
default:
return FormatType.Invalid;
}

View File

@ -136,7 +136,7 @@ export class Gesture extends Disposable {
return 'ontouchstart' in window || navigator.maxTouchPoints > 0 || (window as Window).navigator.msMaxTouchPoints > 0;
}
public dispose(): void {
public override dispose(): void {
if (this.handle) {
this.handle.dispose();
this.handle = null;

View File

@ -27,12 +27,12 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem {
element: HTMLElement | undefined;
_context: any;
_context: unknown;
_action: IAction;
private _actionRunner: IActionRunner | undefined;
constructor(context: any, action: IAction, protected options: IBaseActionViewItemOptions = {}) {
constructor(context: unknown, action: IAction, protected options: IBaseActionViewItemOptions = {}) {
super();
this._context = context || this;
@ -174,6 +174,10 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem {
}
}
isFocused(): boolean {
return !!this.element?.classList.contains('focused');
}
blur(): void {
if (this.element) {
this.element.blur();
@ -212,7 +216,7 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem {
// implement in subclass
}
dispose(): void {
override dispose(): void {
if (this.element) {
this.element.remove();
this.element = undefined;
@ -231,7 +235,7 @@ export interface IActionViewItemOptions extends IBaseActionViewItemOptions {
export class ActionViewItem extends BaseActionViewItem {
protected label: HTMLElement | undefined;
protected options: IActionViewItemOptions;
protected override options: IActionViewItemOptions;
private cssClass?: string;
@ -244,7 +248,7 @@ export class ActionViewItem extends BaseActionViewItem {
this.cssClass = '';
}
render(container: HTMLElement): void {
override render(container: HTMLElement): void {
super.render(container);
if (this.element) {
@ -276,32 +280,36 @@ export class ActionViewItem extends BaseActionViewItem {
// Only set the tabIndex on the element once it is about to get focused
// That way this element wont be a tab stop when it is not needed #106441
focus(): void {
override focus(): void {
if (this.label) {
this.label.tabIndex = 0;
this.label.focus();
}
}
blur(): void {
override isFocused(): boolean {
return !!this.label && this.label?.tabIndex === 0;
}
override blur(): void {
if (this.label) {
this.label.tabIndex = -1;
}
}
setFocusable(focusable: boolean): void {
override setFocusable(focusable: boolean): void {
if (this.label) {
this.label.tabIndex = focusable ? 0 : -1;
}
}
updateLabel(): void {
override updateLabel(): void {
if (this.options.label && this.label) {
this.label.textContent = this.getAction().label;
}
}
updateTooltip(): void {
override updateTooltip(): void {
let title: string | null = null;
if (this.getAction().tooltip) {
@ -320,7 +328,7 @@ export class ActionViewItem extends BaseActionViewItem {
}
}
updateClass(): void {
override updateClass(): void {
if (this.cssClass && this.label) {
this.label.classList.remove(...this.cssClass.split(' '));
}
@ -343,7 +351,7 @@ export class ActionViewItem extends BaseActionViewItem {
}
}
updateEnabled(): void {
override updateEnabled(): void {
if (this.getAction().enabled) {
if (this.label) {
this.label.removeAttribute('aria-disabled');
@ -365,7 +373,7 @@ export class ActionViewItem extends BaseActionViewItem {
}
}
updateChecked(): void {
override updateChecked(): void {
if (this.label) {
if (this.getAction().checked) {
this.label.classList.add('checked');
@ -407,23 +415,23 @@ export class SelectActionViewItem extends BaseActionViewItem {
return option;
}
setFocusable(focusable: boolean): void {
override setFocusable(focusable: boolean): void {
this.selectBox.setFocusable(focusable);
}
focus(): void {
override focus(): void {
if (this.selectBox) {
this.selectBox.focus();
}
}
blur(): void {
override blur(): void {
if (this.selectBox) {
this.selectBox.blur();
}
}
render(container: HTMLElement): void {
override render(container: HTMLElement): void {
this.selectBox.render(container);
}
}

View File

@ -4,16 +4,17 @@
*--------------------------------------------------------------------------------------------*/
.monaco-action-bar {
text-align: right;
white-space: nowrap;
height: 100%;
}
.monaco-action-bar .actions-container {
display: flex;
margin: 0 auto;
padding: 0;
height: 100%;
width: 100%;
justify-content: flex-end;
align-items: center;
}
.monaco-action-bar.vertical .actions-container {
@ -21,9 +22,10 @@
}
.monaco-action-bar .action-item {
display: block;
align-items: center;
justify-content: center;
cursor: pointer;
display: inline-block;
transition: transform 50ms ease;
position: relative; /* DO NOT REMOVE - this is the key to preventing the ghosting icon bug in Chrome 42 */
}
@ -31,23 +33,22 @@
cursor: default;
}
.monaco-action-bar.animated .action-item.active {
transform: scale(1.272019649, 1.272019649); /* 1.272019649 = √φ */
}
.monaco-action-bar .action-item .icon,
.monaco-action-bar .action-item .codicon {
display: inline-block;
display: block;
}
.monaco-action-bar .action-item .codicon {
display: flex;
align-items: center;
width: 16px;
height: 16px;
}
.monaco-action-bar .action-label {
font-size: 11px;
margin-right: 4px;
padding: 3px;
border-radius: 5px;
}
.monaco-action-bar .action-item.disabled .action-label,
@ -74,10 +75,6 @@
margin-right: .8em;
}
.monaco-action-bar.animated.vertical .action-item.active {
transform: translate(5px, 0);
}
.secondary-actions .monaco-action-bar .action-label {
margin-left: 6px;
}

View File

@ -15,7 +15,7 @@ import { IActionViewItemOptions, ActionViewItem, BaseActionViewItem } from 'vs/b
export interface IActionViewItem extends IDisposable {
actionRunner: IActionRunner;
setActionContext(context: any): void;
setActionContext(context: unknown): void;
render(element: HTMLElement): void;
isEnabled(): boolean;
focus(fromRight?: boolean): void; // TODO@isidorn what is this?
@ -38,7 +38,7 @@ export interface ActionTrigger {
export interface IActionBarOptions {
readonly orientation?: ActionsOrientation;
readonly context?: any;
readonly context?: unknown;
readonly actionViewItemProvider?: IActionViewItemProvider;
readonly actionRunner?: IActionRunner;
readonly ariaLabel?: string;
@ -264,11 +264,11 @@ export class ActionBar extends Disposable implements IActionRunner {
}
}
get context(): any {
get context(): unknown {
return this._context;
}
set context(context: any) {
set context(context: unknown) {
this._context = context;
this.viewItems.forEach(i => i.setActionContext(context));
}
@ -525,11 +525,11 @@ export class ActionBar extends Disposable implements IActionRunner {
}
}
run(action: IAction, context?: unknown): Promise<void> {
return this._actionRunner.run(action, context);
async run(action: IAction, context?: unknown): Promise<void> {
await this._actionRunner.run(action, context);
}
dispose(): void {
override dispose(): void {
dispose(this.viewItems);
this.viewItems = [];

View File

@ -24,6 +24,9 @@
height: 100%;
outline: none;
}
.monaco-breadcrumbs.disabled .monaco-breadcrumb-item {
cursor: default;
}
.monaco-breadcrumbs .monaco-breadcrumb-item .codicon-breadcrumb-separator {
color: inherit;

View File

@ -56,6 +56,7 @@ export class BreadcrumbsWidget {
private readonly _nodes = new Array<HTMLDivElement>();
private readonly _freeNodes = new Array<HTMLDivElement>();
private _enabled: boolean = true;
private _focusedItemIdx: number = -1;
private _selectedItemIdx: number = -1;
@ -155,13 +156,18 @@ export class BreadcrumbsWidget {
content += `.monaco-breadcrumbs .monaco-breadcrumb-item.focused.selected { color: ${style.breadcrumbsFocusAndSelectionForeground}}\n`;
}
if (style.breadcrumbsHoverForeground) {
content += `.monaco-breadcrumbs .monaco-breadcrumb-item:hover:not(.focused):not(.selected) { color: ${style.breadcrumbsHoverForeground}}\n`;
content += `.monaco-breadcrumbs:not(.disabled ) .monaco-breadcrumb-item:hover:not(.focused):not(.selected) { color: ${style.breadcrumbsHoverForeground}}\n`;
}
if (this._styleElement.innerText !== content) {
this._styleElement.innerText = content;
}
}
setEnabled(value: boolean) {
this._enabled = value;
this._domNode.classList.toggle('disabled', !this._enabled);
}
domFocus(): void {
let idx = this._focusedItemIdx >= 0 ? this._focusedItemIdx : this._items.length - 1;
if (idx >= 0 && idx < this._items.length) {
@ -326,6 +332,9 @@ export class BreadcrumbsWidget {
}
private _onClick(event: IMouseEvent): void {
if (!this._enabled) {
return;
}
for (let el: HTMLElement | null = event.target; el; el = el.parentElement) {
let idx = this._nodes.indexOf(el as HTMLDivElement);
if (idx >= 0) {

View File

@ -22,8 +22,9 @@
text-decoration: none !important;
}
.monaco-button.disabled:focus,
.monaco-button.disabled {
opacity: 0.4;
opacity: 0.4 !important;
cursor: default;
}
@ -39,3 +40,15 @@
.monaco-button-dropdown > .monaco-dropdown-button {
margin-left: 1px;
}
.monaco-description-button {
flex-direction: column;
}
.monaco-description-button .monaco-button-label {
font-weight: 500;
}
.monaco-description-button .monaco-button-description {
font-style: italic;
}

View File

@ -50,6 +50,10 @@ export interface IButton extends IDisposable {
hasFocus(): boolean;
}
export interface IButtonWithDescription extends IButton {
description: string;
}
export class Button extends Disposable implements IButton {
private _element: HTMLElement;
@ -303,6 +307,207 @@ export class ButtonWithDropdown extends Disposable implements IButton {
}
}
export class ButtonWithDescription extends Disposable implements IButtonWithDescription {
private _element: HTMLElement;
private _labelElement: HTMLElement;
private _descriptionElement: HTMLElement;
private options: IButtonOptions;
private buttonBackground: Color | undefined;
private buttonHoverBackground: Color | undefined;
private buttonForeground: Color | undefined;
private buttonSecondaryBackground: Color | undefined;
private buttonSecondaryHoverBackground: Color | undefined;
private buttonSecondaryForeground: Color | undefined;
private buttonBorder: Color | undefined;
private _onDidClick = this._register(new Emitter<Event>());
get onDidClick(): BaseEvent<Event> { return this._onDidClick.event; }
private focusTracker: IFocusTracker;
constructor(container: HTMLElement, options?: IButtonOptions) {
super();
this.options = options || Object.create(null);
mixin(this.options, defaultOptions, false);
this.buttonForeground = this.options.buttonForeground;
this.buttonBackground = this.options.buttonBackground;
this.buttonHoverBackground = this.options.buttonHoverBackground;
this.buttonSecondaryForeground = this.options.buttonSecondaryForeground;
this.buttonSecondaryBackground = this.options.buttonSecondaryBackground;
this.buttonSecondaryHoverBackground = this.options.buttonSecondaryHoverBackground;
this.buttonBorder = this.options.buttonBorder;
this._element = document.createElement('a');
this._element.classList.add('monaco-button');
this._element.classList.add('monaco-description-button');
this._element.tabIndex = 0;
this._element.setAttribute('role', 'button');
this._labelElement = document.createElement('div');
this._labelElement.classList.add('monaco-button-label');
this._labelElement.tabIndex = -1;
this._element.appendChild(this._labelElement);
this._descriptionElement = document.createElement('div');
this._descriptionElement.classList.add('monaco-button-description');
this._descriptionElement.tabIndex = -1;
this._element.appendChild(this._descriptionElement);
container.appendChild(this._element);
this._register(Gesture.addTarget(this._element));
[EventType.CLICK, TouchEventType.Tap].forEach(eventType => {
this._register(addDisposableListener(this._element, eventType, e => {
if (!this.enabled) {
EventHelper.stop(e);
return;
}
this._onDidClick.fire(e);
}));
});
this._register(addDisposableListener(this._element, EventType.KEY_DOWN, e => {
const event = new StandardKeyboardEvent(e);
let eventHandled = false;
if (this.enabled && (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) {
this._onDidClick.fire(e);
eventHandled = true;
} else if (event.equals(KeyCode.Escape)) {
this._element.blur();
eventHandled = true;
}
if (eventHandled) {
EventHelper.stop(event, true);
}
}));
this._register(addDisposableListener(this._element, EventType.MOUSE_OVER, e => {
if (!this._element.classList.contains('disabled')) {
this.setHoverBackground();
}
}));
this._register(addDisposableListener(this._element, EventType.MOUSE_OUT, e => {
this.applyStyles(); // restore standard styles
}));
// Also set hover background when button is focused for feedback
this.focusTracker = this._register(trackFocus(this._element));
this._register(this.focusTracker.onDidFocus(() => this.setHoverBackground()));
this._register(this.focusTracker.onDidBlur(() => this.applyStyles())); // restore standard styles
this.applyStyles();
}
private setHoverBackground(): void {
let hoverBackground;
if (this.options.secondary) {
hoverBackground = this.buttonSecondaryHoverBackground ? this.buttonSecondaryHoverBackground.toString() : null;
} else {
hoverBackground = this.buttonHoverBackground ? this.buttonHoverBackground.toString() : null;
}
if (hoverBackground) {
this._element.style.backgroundColor = hoverBackground;
}
}
style(styles: IButtonStyles): void {
this.buttonForeground = styles.buttonForeground;
this.buttonBackground = styles.buttonBackground;
this.buttonHoverBackground = styles.buttonHoverBackground;
this.buttonSecondaryForeground = styles.buttonSecondaryForeground;
this.buttonSecondaryBackground = styles.buttonSecondaryBackground;
this.buttonSecondaryHoverBackground = styles.buttonSecondaryHoverBackground;
this.buttonBorder = styles.buttonBorder;
this.applyStyles();
}
private applyStyles(): void {
if (this._element) {
let background, foreground;
if (this.options.secondary) {
foreground = this.buttonSecondaryForeground ? this.buttonSecondaryForeground.toString() : '';
background = this.buttonSecondaryBackground ? this.buttonSecondaryBackground.toString() : '';
} else {
foreground = this.buttonForeground ? this.buttonForeground.toString() : '';
background = this.buttonBackground ? this.buttonBackground.toString() : '';
}
const border = this.buttonBorder ? this.buttonBorder.toString() : '';
this._element.style.color = foreground;
this._element.style.backgroundColor = background;
this._element.style.borderWidth = border ? '1px' : '';
this._element.style.borderStyle = border ? 'solid' : '';
this._element.style.borderColor = border;
}
}
get element(): HTMLElement {
return this._element;
}
set label(value: string) {
this._element.classList.add('monaco-text-button');
if (this.options.supportIcons) {
reset(this._labelElement, ...renderLabelWithIcons(value));
} else {
this._labelElement.textContent = value;
}
if (typeof this.options.title === 'string') {
this._element.title = this.options.title;
} else if (this.options.title) {
this._element.title = value;
}
}
set description(value: string) {
if (this.options.supportIcons) {
reset(this._descriptionElement, ...renderLabelWithIcons(value));
} else {
this._descriptionElement.textContent = value;
}
}
set icon(icon: CSSIcon) {
this._element.classList.add(...CSSIcon.asClassNameArray(icon));
}
set enabled(value: boolean) {
if (value) {
this._element.classList.remove('disabled');
this._element.setAttribute('aria-disabled', String(false));
this._element.tabIndex = 0;
} else {
this._element.classList.add('disabled');
this._element.setAttribute('aria-disabled', String(true));
}
}
get enabled() {
return !this._element.classList.contains('disabled');
}
focus(): void {
this._element.focus();
}
hasFocus(): boolean {
return this._element === document.activeElement;
}
}
export class ButtonBar extends Disposable {
private _buttons: IButton[] = [];
@ -321,6 +526,12 @@ export class ButtonBar extends Disposable {
return button;
}
addButtonWithDescription(options?: IButtonOptions): IButtonWithDescription {
const button = this._register(new ButtonWithDescription(this.container, options));
this.pushButton(button);
return button;
}
addButtonWithDropdown(options: IButtonWithDropdownOptions): IButton {
const button = this._register(new ButtonWithDropdown(this.container, options));
this.pushButton(button);

View File

@ -44,7 +44,7 @@ export class CheckboxActionViewItem extends BaseActionViewItem {
protected checkbox: Checkbox | undefined;
protected readonly disposables = new DisposableStore();
render(container: HTMLElement): void {
override render(container: HTMLElement): void {
this.element = container;
this.disposables.clear();
@ -59,7 +59,7 @@ export class CheckboxActionViewItem extends BaseActionViewItem {
this.element.appendChild(this.checkbox.domNode);
}
updateEnabled(): void {
override updateEnabled(): void {
if (this.checkbox) {
if (this.isEnabled()) {
this.checkbox.enable();
@ -69,33 +69,33 @@ export class CheckboxActionViewItem extends BaseActionViewItem {
}
}
updateChecked(): void {
override updateChecked(): void {
if (this.checkbox) {
this.checkbox.checked = this._action.checked;
}
}
focus(): void {
override focus(): void {
if (this.checkbox) {
this.checkbox.domNode.tabIndex = 0;
this.checkbox.focus();
}
}
blur(): void {
override blur(): void {
if (this.checkbox) {
this.checkbox.domNode.tabIndex = -1;
this.checkbox.domNode.blur();
}
}
setFocusable(focusable: boolean): void {
override setFocusable(focusable: boolean): void {
if (this.checkbox) {
this.checkbox.domNode.tabIndex = focusable ? 0 : -1;
}
}
dispose(): void {
override dispose(): void {
this.disposables.dispose();
super.dispose();
}

View File

@ -13,7 +13,10 @@
}
}
.codicon-sync.codicon-modifier-spin, .codicon-loading.codicon-modifier-spin, .codicon-gear.codicon-modifier-spin {
.codicon-sync.codicon-modifier-spin,
.codicon-loading.codicon-modifier-spin,
.codicon-gear.codicon-modifier-spin,
.codicon-notebook-state-executing.codicon-modifier-spin {
/* Use steps to throttle FPS to reduce CPU usage */
animation: codicon-spin 1.5s steps(30) infinite;
}

View File

@ -45,7 +45,7 @@ export interface IDelegate {
anchorAxisAlignment?: AnchorAxisAlignment; // default: vertical
canRelayout?: boolean; // default: true
onDOMEvent?(e: Event, activeElement: HTMLElement): void;
onHide?(data?: any): void;
onHide?(data?: unknown): void;
}
export interface IContextViewProvider {
@ -324,7 +324,7 @@ export class ContextView extends Disposable {
this.view.style.width = 'initial';
}
hide(data?: any): void {
hide(data?: unknown): void {
const delegate = this.delegate;
this.delegate = null;
@ -351,7 +351,7 @@ export class ContextView extends Disposable {
}
}
dispose(): void {
override dispose(): void {
this.hide();
super.dispose();

View File

@ -10,7 +10,7 @@
width: 100%;
left:0;
top:0;
z-index: 2000;
z-index: 2600;
display: flex;
justify-content: center;
align-items: center;
@ -34,17 +34,12 @@
/** Dialog: Title Actions Row */
.monaco-dialog-box .dialog-toolbar-row {
height: 22px;
padding-bottom: 4px;
}
.monaco-dialog-box .action-label {
height: 16px;
min-width: 16px;
background-size: 16px;
background-position: 50%;
background-repeat: no-repeat;
margin: 0px;
margin-left: 4px;
.monaco-dialog-box .dialog-toolbar-row .actions-container {
justify-content: flex-end;
}
/** Dialog: Message Row */
@ -144,6 +139,10 @@
overflow: hidden;
}
.monaco-dialog-box > .dialog-buttons-row > .dialog-buttons.centered {
justify-content: center;
}
.monaco-dialog-box > .dialog-buttons-row > .dialog-buttons > .monaco-button {
width: fit-content;
width: -moz-fit-content;

View File

@ -11,7 +11,7 @@ import { domEvent } from 'vs/base/browser/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Color } from 'vs/base/common/color';
import { ButtonBar, IButtonStyles } from 'vs/base/browser/ui/button/button';
import { ButtonBar, ButtonWithDescription, IButtonStyles } from 'vs/base/browser/ui/button/button';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { Action } from 'vs/base/common/actions';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
@ -34,6 +34,10 @@ export interface IDialogOptions {
readonly type?: 'none' | 'info' | 'error' | 'question' | 'warning' | 'pending';
readonly inputs?: IDialogInputOptions[];
readonly keyEventProcessor?: (event: StandardKeyboardEvent) => void;
readonly renderBody?: (container: HTMLElement) => void;
readonly icon?: Codicon;
readonly buttonDetails?: string[];
readonly disableCloseAction?: boolean;
}
export interface IDialogResult {
@ -53,6 +57,8 @@ export interface IDialogStyles extends IButtonStyles, ISimpleCheckboxStyles {
readonly inputBackground?: Color;
readonly inputForeground?: Color;
readonly inputBorder?: Color;
readonly textLinkForeground?: Color;
}
interface ButtonMapEntry {
@ -71,6 +77,7 @@ export class Dialog extends Disposable {
private modalElement: HTMLElement | undefined;
private readonly buttonsContainer: HTMLElement;
private readonly messageDetailElement: HTMLElement;
private readonly messageContainer: HTMLElement;
private readonly iconElement: HTMLElement;
private readonly checkbox: SimpleCheckbox | undefined;
private readonly toolbarContainer: HTMLElement;
@ -83,7 +90,7 @@ export class Dialog extends Disposable {
constructor(private container: HTMLElement, private message: string, buttons: string[], private options: IDialogOptions) {
super();
this.modalElement = this.container.appendChild($(`.monaco-dialog-modal-block${options.type === 'pending' ? '.dimmed' : ''}`));
this.modalElement = this.container.appendChild($(`.monaco-dialog-modal-block.dimmed`));
this.shadowElement = this.modalElement.appendChild($('.dialog-shadow'));
this.element = this.shadowElement.appendChild($('.monaco-dialog-box'));
this.element.setAttribute('role', 'dialog');
@ -95,20 +102,29 @@ export class Dialog extends Disposable {
const messageRowElement = this.element.appendChild($('.dialog-message-row'));
this.iconElement = messageRowElement.appendChild($('.dialog-icon'));
const messageContainer = messageRowElement.appendChild($('.dialog-message-container'));
this.messageContainer = messageRowElement.appendChild($('.dialog-message-container'));
if (this.options.detail) {
const messageElement = messageContainer.appendChild($('.dialog-message'));
if (this.options.detail || this.options.renderBody) {
const messageElement = this.messageContainer.appendChild($('.dialog-message'));
const messageTextElement = messageElement.appendChild($('.dialog-message-text'));
messageTextElement.innerText = this.message;
}
this.messageDetailElement = messageContainer.appendChild($('.dialog-message-detail'));
this.messageDetailElement.innerText = this.options.detail ? this.options.detail : message;
this.messageDetailElement = this.messageContainer.appendChild($('.dialog-message-detail'));
if (this.options.detail || !this.options.renderBody) {
this.messageDetailElement.innerText = this.options.detail ? this.options.detail : message;
} else {
this.messageDetailElement.style.display = 'none';
}
if (this.options.renderBody) {
const customBody = this.messageContainer.appendChild($('.dialog-message-body'));
this.options.renderBody(customBody);
}
if (this.options.inputs) {
this.inputs = this.options.inputs.map(input => {
const inputRowElement = messageContainer.appendChild($('.dialog-message-input'));
const inputRowElement = this.messageContainer.appendChild($('.dialog-message-input'));
const inputBox = this._register(new InputBox(inputRowElement, undefined, {
placeholder: input.placeholder,
@ -126,7 +142,7 @@ export class Dialog extends Disposable {
}
if (this.options.checkboxLabel) {
const checkboxRowElement = messageContainer.appendChild($('.dialog-checkbox-row'));
const checkboxRowElement = this.messageContainer.appendChild($('.dialog-checkbox-row'));
const checkbox = this.checkbox = this._register(new SimpleCheckbox(this.options.checkboxLabel, !!this.options.checkboxChecked));
@ -175,12 +191,16 @@ export class Dialog extends Disposable {
const buttonBar = this.buttonBar = this._register(new ButtonBar(this.buttonsContainer));
const buttonMap = this.rearrangeButtons(this.buttons, this.options.cancelId);
this.buttonsContainer.classList.toggle('centered');
// Handle button clicks
buttonMap.forEach((entry, index) => {
const button = this._register(buttonBar.addButton({ title: true }));
const primary = buttonMap[index].index === 0;
const button = this.options.buttonDetails ? this._register(buttonBar.addButtonWithDescription({ title: true, secondary: !primary })) : this._register(buttonBar.addButton({ title: true, secondary: !primary }));
button.label = mnemonicButtonLabel(buttonMap[index].label, true);
if (button instanceof ButtonWithDescription) {
button.description = this.options.buttonDetails![buttonMap[index].index];
}
this._register(button.onDidClick(e => {
if (e) {
EventHelper.stop(e);
@ -287,7 +307,7 @@ export class Dialog extends Disposable {
EventHelper.stop(e, true);
const evt = new StandardKeyboardEvent(e);
if (evt.equals(KeyCode.Escape)) {
if (!this.options.disableCloseAction && evt.equals(KeyCode.Escape)) {
resolve({
button: this.options.cancelId || 0,
checkboxChecked: this.checkbox ? this.checkbox.checked : undefined
@ -313,34 +333,41 @@ export class Dialog extends Disposable {
this.iconElement.classList.remove(...dialogErrorIcon.classNamesArray, ...dialogWarningIcon.classNamesArray, ...dialogInfoIcon.classNamesArray, ...Codicon.loading.classNamesArray, spinModifierClassName);
switch (this.options.type) {
case 'error':
this.iconElement.classList.add(...dialogErrorIcon.classNamesArray);
break;
case 'warning':
this.iconElement.classList.add(...dialogWarningIcon.classNamesArray);
break;
case 'pending':
this.iconElement.classList.add(...Codicon.loading.classNamesArray, spinModifierClassName);
break;
case 'none':
case 'info':
case 'question':
default:
this.iconElement.classList.add(...dialogInfoIcon.classNamesArray);
break;
if (this.options.icon) {
this.iconElement.classList.add(...this.options.icon.classNamesArray);
} else {
switch (this.options.type) {
case 'error':
this.iconElement.classList.add(...dialogErrorIcon.classNamesArray);
break;
case 'warning':
this.iconElement.classList.add(...dialogWarningIcon.classNamesArray);
break;
case 'pending':
this.iconElement.classList.add(...Codicon.loading.classNamesArray, spinModifierClassName);
break;
case 'none':
case 'info':
case 'question':
default:
this.iconElement.classList.add(...dialogInfoIcon.classNamesArray);
break;
}
}
const actionBar = this._register(new ActionBar(this.toolbarContainer, {}));
const action = this._register(new Action('dialog.close', nls.localize('dialogClose', "Close Dialog"), dialogCloseIcon.classNames, true, async () => {
resolve({
button: this.options.cancelId || 0,
checkboxChecked: this.checkbox ? this.checkbox.checked : undefined
});
}));
if (!this.options.disableCloseAction) {
const actionBar = this._register(new ActionBar(this.toolbarContainer, {}));
actionBar.push(action, { icon: true, label: false, });
const action = this._register(new Action('dialog.close', nls.localize('dialogClose', "Close Dialog"), dialogCloseIcon.classNames, true, async () => {
resolve({
button: this.options.cancelId || 0,
checkboxChecked: this.checkbox ? this.checkbox.checked : undefined
});
}));
actionBar.push(action, { icon: true, label: false, });
}
this.applyStyles();
@ -369,6 +396,7 @@ export class Dialog extends Disposable {
const bgColor = style.dialogBackground;
const shadowColor = style.dialogShadow ? `0 0px 8px ${style.dialogShadow}` : '';
const border = style.dialogBorder ? `1px solid ${style.dialogBorder}` : '';
const linkFgColor = style.textLinkForeground;
this.shadowElement.style.boxShadow = shadowColor;
@ -389,6 +417,12 @@ export class Dialog extends Disposable {
this.messageDetailElement.style.color = messageDetailColor.makeOpaque(bgColor).toString();
}
if (linkFgColor) {
for (const el of this.messageContainer.getElementsByTagName('a')) {
el.style.color = linkFgColor.toString();
}
}
let color;
switch (this.options.type) {
case 'error':
@ -417,7 +451,7 @@ export class Dialog extends Disposable {
this.applyStyles();
}
dispose(): void {
override dispose(): void {
super.dispose();
if (this.modalElement) {

View File

@ -11,8 +11,29 @@
.monaco-dropdown > .dropdown-label {
cursor: pointer;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.monaco-dropdown > .dropdown-label > .action-label.disabled {
cursor: default;
}
.monaco-dropdown-with-primary {
display: flex !important;
flex-direction: row;
border-radius: 5px;
}
.monaco-dropdown-with-primary > .action-container > .action-label {
margin-right: 0;
}
.monaco-dropdown-with-primary > .dropdown-action-container > .monaco-dropdown > .dropdown-label .codicon[class*='codicon-'] {
font-size: 12px;
padding-left: 0px;
padding-right: 0px;
line-height: 16px;
margin-left: -4px;
}

View File

@ -125,7 +125,7 @@ export class BaseDropdown extends ActionRunner {
this.hide();
}
dispose(): void {
override dispose(): void {
super.dispose();
this.hide();
@ -159,7 +159,7 @@ export class Dropdown extends BaseDropdown {
this.contextViewProvider = options.contextViewProvider;
}
show(): void {
override show(): void {
super.show();
this.element.classList.add('active');
@ -187,7 +187,7 @@ export class Dropdown extends BaseDropdown {
this.element.classList.remove('active');
}
hide(): void {
override hide(): void {
super.hide();
if (this.contextViewProvider) {
@ -250,7 +250,7 @@ export class DropdownMenu extends BaseDropdown {
this._actions = actions;
}
show(): void {
override show(): void {
super.show();
this.element.classList.add('active');
@ -269,7 +269,7 @@ export class DropdownMenu extends BaseDropdown {
});
}
hide(): void {
override hide(): void {
super.hide();
}

View File

@ -7,14 +7,15 @@ import 'vs/css!./dropdown';
import { Action, IAction, IActionRunner } from 'vs/base/common/actions';
import { IDisposable } from 'vs/base/common/lifecycle';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { append, $ } from 'vs/base/browser/dom';
import { KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { append, $, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { Emitter } from 'vs/base/common/event';
import { ActionViewItem, BaseActionViewItem, IActionViewItemOptions, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { IActionProvider, DropdownMenu, IDropdownMenuOptions, ILabelRenderer } from 'vs/base/browser/ui/dropdown/dropdown';
import { IContextMenuProvider } from 'vs/base/browser/contextmenu';
import { Codicon } from 'vs/base/common/codicons';
import { IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
export interface IKeybindingProvider {
(action: IAction): ResolvedKeybinding | undefined;
@ -42,23 +43,26 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem {
private _onDidChangeVisibility = this._register(new Emitter<boolean>());
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
protected override readonly options: IDropdownMenuActionViewItemOptions;
constructor(
action: IAction,
menuActionsOrProvider: readonly IAction[] | IActionProvider,
contextMenuProvider: IContextMenuProvider,
protected options: IDropdownMenuActionViewItemOptions = {}
options: IDropdownMenuActionViewItemOptions = Object.create(null)
) {
super(null, action, options);
this.menuActionsOrProvider = menuActionsOrProvider;
this.contextMenuProvider = contextMenuProvider;
this.options = options;
if (this.options.actionRunner) {
this.actionRunner = this.options.actionRunner;
}
}
render(container: HTMLElement): void {
override render(container: HTMLElement): void {
this.actionItem = container;
const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable | null => {
@ -123,7 +127,7 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem {
this.updateEnabled();
}
setActionContext(newContext: unknown): void {
override setActionContext(newContext: unknown): void {
super.setActionContext(newContext);
if (this.dropdownMenu) {
@ -141,7 +145,7 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem {
}
}
protected updateEnabled(): void {
protected override updateEnabled(): void {
const disabled = !this.getAction().enabled;
this.actionItem?.classList.toggle('disabled', disabled);
this.element?.classList.toggle('disabled', disabled);
@ -166,7 +170,7 @@ export class ActionWithDropdownActionViewItem extends ActionViewItem {
super(context, action, options);
}
render(container: HTMLElement): void {
override render(container: HTMLElement): void {
super.render(container);
if (this.element) {
this.element.classList.add('action-dropdown-item');
@ -181,6 +185,36 @@ export class ActionWithDropdownActionViewItem extends ActionViewItem {
};
this.dropdownMenuActionViewItem = new DropdownMenuActionViewItem(this._register(new Action('dropdownAction', undefined)), menuActionsProvider, this.contextMenuProvider, { classNames: ['dropdown', ...Codicon.dropDownButton.classNamesArray, ...(<IActionWithDropdownActionViewItemOptions>this.options).menuActionClassNames || []] });
this.dropdownMenuActionViewItem.render(this.element);
this._register(addDisposableListener(this.element, EventType.KEY_DOWN, e => {
const event = new StandardKeyboardEvent(e);
let handled: boolean = false;
if (this.dropdownMenuActionViewItem?.isFocused() && event.equals(KeyCode.LeftArrow)) {
handled = true;
this.dropdownMenuActionViewItem?.blur();
this.focus();
} else if (this.isFocused() && event.equals(KeyCode.RightArrow)) {
handled = true;
this.blur();
this.dropdownMenuActionViewItem?.focus();
}
if (handled) {
event.preventDefault();
event.stopPropagation();
}
}));
}
}
override blur(): void {
super.blur();
this.dropdownMenuActionViewItem?.blur();
}
override setFocusable(focusable: boolean): void {
super.setFocusable(focusable);
this.dropdownMenuActionViewItem?.setFocusable(focusable);
}
}

View File

@ -0,0 +1,106 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IContextMenuProvider } from 'vs/base/browser/contextmenu';
import { ActionViewItem, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem';
import { IAction } from 'vs/base/common/actions';
import * as DOM from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
export class DropdownWithPrimaryActionViewItem extends BaseActionViewItem {
private _primaryAction: ActionViewItem;
private _dropdown: DropdownMenuActionViewItem;
private _container: HTMLElement | null = null;
private toDispose: IDisposable[];
constructor(
primaryAction: IAction,
dropdownAction: IAction,
dropdownMenuActions: IAction[],
_className: string,
private readonly _contextMenuProvider: IContextMenuProvider,
dropdownIcon?: string
) {
super(null, primaryAction);
this._primaryAction = new ActionViewItem(undefined, primaryAction, {
icon: true,
label: false
});
this._dropdown = new DropdownMenuActionViewItem(dropdownAction, dropdownMenuActions, this._contextMenuProvider, {
menuAsChild: true
});
this.toDispose = [];
}
override render(container: HTMLElement): void {
this._container = container;
super.render(this._container);
this._container.classList.add('monaco-dropdown-with-primary');
const primaryContainer = DOM.$('.action-container');
this._primaryAction.render(DOM.append(this._container, primaryContainer));
const dropdownContainer = DOM.$('.dropdown-action-container');
this._dropdown.render(DOM.append(this._container, dropdownContainer));
this.toDispose.push(DOM.addDisposableListener(primaryContainer, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
const event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.RightArrow)) {
this._primaryAction.element!.tabIndex = -1;
this._dropdown.focus();
event.stopPropagation();
}
}));
this.toDispose.push(DOM.addDisposableListener(dropdownContainer, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
const event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.LeftArrow)) {
this._primaryAction.element!.tabIndex = 0;
this._dropdown.setFocusable(false);
this._primaryAction.element?.focus();
event.stopPropagation();
}
}));
}
override focus(fromRight?: boolean): void {
if (fromRight) {
this._dropdown.focus();
} else {
this._primaryAction.element!.tabIndex = 0;
this._primaryAction.element!.focus();
}
}
override blur(): void {
this._primaryAction.element!.tabIndex = -1;
this._dropdown.blur();
this._container!.blur();
}
override setFocusable(focusable: boolean): void {
if (focusable) {
this._primaryAction.element!.tabIndex = 0;
} else {
this._primaryAction.element!.tabIndex = -1;
this._dropdown.setFocusable(false);
}
}
override dispose(): void {
this.toDispose = dispose(this.toDispose);
}
update(dropdownAction: IAction, dropdownMenuActions: IAction[], dropdownIcon?: string): void {
this._dropdown?.dispose();
this._dropdown = new DropdownMenuActionViewItem(dropdownAction, dropdownMenuActions, this._contextMenuProvider, {
menuAsChild: true,
classNames: ['codicon', dropdownIcon || 'codicon-chevron-down']
});
if (this.element) {
this._dropdown.render(this.element);
}
}
}

View File

@ -382,7 +382,7 @@ export class ReplaceInput extends Widget {
this.domNode.style.width = newWidth + 'px';
}
public dispose(): void {
public override dispose(): void {
super.dispose();
}
}

View File

@ -521,51 +521,6 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
return { type: 'branch', data: node.children.map(c => SerializableGrid.serializeNode(c, orthogonal(orientation))), size };
}
private static deserializeNode<T extends ISerializableView>(json: ISerializedNode, orientation: Orientation, box: Box, deserializer: IViewDeserializer<T>): GridNode<T> {
if (!json || typeof json !== 'object') {
throw new Error('Invalid JSON');
}
if (json.type === 'branch') {
if (!Array.isArray(json.data)) {
throw new Error('Invalid JSON: \'data\' property of branch must be an array.');
}
const children: GridNode<T>[] = [];
let offset = 0;
for (const child of json.data) {
if (typeof child.size !== 'number') {
throw new Error('Invalid JSON: \'size\' property of node must be a number.');
}
const childSize = child.type === 'leaf' && child.visible === false ? 0 : child.size;
const childBox: Box = orientation === Orientation.HORIZONTAL
? { top: box.top, left: box.left + offset, width: childSize, height: box.height }
: { top: box.top + offset, left: box.left, width: box.width, height: childSize };
children.push(SerializableGrid.deserializeNode(child, orthogonal(orientation), childBox, deserializer));
offset += childSize;
}
return { children, box };
} else if (json.type === 'leaf') {
const view: T = deserializer.fromJSON(json.data);
return { view, box, cachedVisibleSize: json.visible === false ? json.size : undefined };
}
throw new Error('Invalid JSON: \'type\' property must be either \'branch\' or \'leaf\'.');
}
private static getFirstLeaf<T extends IView>(node: GridNode<T>): GridLeafNode<T> {
if (!isGridBranchNode(node)) {
return node;
}
return SerializableGrid.getFirstLeaf(node.children[0]);
}
static deserialize<T extends ISerializableView>(json: ISerializedGrid, deserializer: IViewDeserializer<T>, options: IGridOptions = {}): SerializableGrid<T> {
if (typeof json.orientation !== 'number') {
throw new Error('Invalid JSON: \'orientation\' property must be a number.');
@ -596,7 +551,7 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
};
}
layout(width: number, height: number): void {
override layout(width: number, height: number): void {
super.layout(width, height);
if (this.initialLayoutContext) {

View File

@ -30,8 +30,7 @@
}
.monaco-hover .markdown-hover > .hover-contents:not(.code-hover-contents) hr {
/* This is a strange rule but it avoids https://github.com/microsoft/vscode/issues/96795, just 100vw on its own caused the actual hover width to increase */
min-width: calc(100% + 100vw);
min-width: 100%;
}
.monaco-hover p,

View File

@ -10,6 +10,10 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle
const $ = dom.$;
export const enum HoverPosition {
LEFT, RIGHT, BELOW, ABOVE
}
export class HoverWidget extends Disposable {
public readonly containerDomNode: HTMLElement;

View File

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AnchorPosition } from 'vs/base/browser/ui/contextview/contextview';
import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { IDisposable } from 'vs/base/common/lifecycle';
@ -15,7 +15,7 @@ export interface IHoverDelegateTarget extends IDisposable {
export interface IHoverDelegateOptions {
text: IMarkdownString | string;
target: IHoverDelegateTarget | HTMLElement;
anchorPosition?: AnchorPosition;
hoverPosition?: HoverPosition;
}
export interface IHoverDelegate {

View File

@ -11,12 +11,12 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Range } from 'vs/base/common/range';
import { equals } from 'vs/base/common/objects';
import { IHoverDelegate, IHoverDelegateOptions, IHoverDelegateTarget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
import { AnchorPosition } from 'vs/base/browser/ui/contextview/contextview';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { isFunction, isString } from 'vs/base/common/types';
import { domEvent } from 'vs/base/browser/event';
import { localize } from 'vs/nls';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget';
export interface IIconLabelCreationOptions {
supportHighlights?: boolean;
@ -222,12 +222,12 @@ export class IconLabel extends Disposable {
let isHovering = false;
let tokenSource: CancellationTokenSource;
let hoverDisposable: IDisposable | undefined;
function mouseOver(this: HTMLElement, e: MouseEvent): any {
function mouseOver(this: HTMLElement, e: MouseEvent): void {
if (isHovering) {
return;
}
tokenSource = new CancellationTokenSource();
function mouseLeaveOrDown(this: HTMLElement, e: MouseEvent): any {
function mouseLeaveOrDown(this: HTMLElement, e: MouseEvent): void {
const isMouseDown = e.type === dom.EventType.MOUSE_DOWN;
if (isMouseDown) {
hoverDisposable?.dispose();
@ -245,7 +245,7 @@ export class IconLabel extends Disposable {
const mouseDownDisposable = domEvent(htmlElement, dom.EventType.MOUSE_DOWN, true)(mouseLeaveOrDown.bind(htmlElement));
isHovering = true;
function mouseMove(this: HTMLElement, e: MouseEvent): any {
function mouseMove(this: HTMLElement, e: MouseEvent): void {
mouseX = e.x;
}
const mouseMoveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_MOVE, true)(mouseMove.bind(htmlElement));
@ -260,7 +260,7 @@ export class IconLabel extends Disposable {
hoverOptions = {
text: localize('iconLabel.loading', "Loading..."),
target,
anchorPosition: AnchorPosition.BELOW
hoverPosition: HoverPosition.BELOW
};
hoverDisposable = IconLabel.adjustXAndShowCustomHover(hoverOptions, mouseX, hoverDelegate, isHovering);
@ -269,7 +269,7 @@ export class IconLabel extends Disposable {
hoverOptions = {
text: resolvedTooltip,
target,
anchorPosition: AnchorPosition.BELOW
hoverPosition: HoverPosition.BELOW
};
// awaiting the tooltip could take a while. Make sure we're still hovering.
hoverDisposable = IconLabel.adjustXAndShowCustomHover(hoverOptions, mouseX, hoverDelegate, isHovering);

View File

@ -610,7 +610,7 @@ export class InputBox extends Widget {
}
}
public dispose(): void {
public override dispose(): void {
this._hideMessage();
this.message = null;

View File

@ -11,13 +11,10 @@
.monaco-keybinding > .monaco-keybinding-key {
display: inline-block;
border: solid 1px rgba(204, 204, 204, 0.4);
border-bottom-color: rgba(187, 187, 187, 0.4);
border-style: solid;
border-width: 1px;
border-radius: 3px;
box-shadow: inset 0 -1px 0 rgba(187, 187, 187, 0.4);
background-color: rgba(221, 221, 221, 0.4);
vertical-align: middle;
color: #555;
font-size: 11px;
padding: 3px 5px;
margin: 0 2px;
@ -31,19 +28,10 @@
margin-right: 0;
}
.hc-black .monaco-keybinding > .monaco-keybinding-key,
.vs-dark .monaco-keybinding > .monaco-keybinding-key {
background-color: rgba(128, 128, 128, 0.17);
color: #ccc;
border: solid 1px rgba(51, 51, 51, 0.6);
border-bottom-color: rgba(68, 68, 68, 0.6);
box-shadow: inset 0 -1px 0 rgba(68, 68, 68, 0.6);
}
.monaco-keybinding > .monaco-keybinding-key-separator {
display: inline-block;
}
.monaco-keybinding > .monaco-keybinding-key-chord-separator {
width: 6px;
}
}

View File

@ -10,6 +10,8 @@ import { ResolvedKeybinding, ResolvedKeybindingPart } from 'vs/base/common/keyCo
import { UILabelProvider } from 'vs/base/common/keybindingLabels';
import * as dom from 'vs/base/browser/dom';
import { localize } from 'vs/nls';
import { IThemable } from 'vs/base/common/styler';
import { Color } from 'vs/base/common/color';
const $ = dom.$;
@ -26,18 +28,44 @@ export interface Matches {
chordPart: PartMatches;
}
export interface KeybindingLabelOptions {
renderUnboundKeybindings: boolean;
export interface KeybindingLabelOptions extends IKeybindingLabelStyles {
renderUnboundKeybindings?: boolean;
}
export class KeybindingLabel {
export interface IKeybindingLabelStyles {
keybindingLabelBackground?: Color;
keybindingLabelForeground?: Color;
keybindingLabelBorder?: Color;
keybindingLabelBottomBorder?: Color;
keybindingLabelShadow?: Color;
}
export class KeybindingLabel implements IThemable {
private domNode: HTMLElement;
private options: KeybindingLabelOptions;
private readonly keyElements = new Set<HTMLSpanElement>();
private keybinding: ResolvedKeybinding | undefined;
private matches: Matches | undefined;
private didEverRender: boolean;
constructor(container: HTMLElement, private os: OperatingSystem, private options?: KeybindingLabelOptions) {
private labelBackground: Color | undefined;
private labelForeground: Color | undefined;
private labelBorder: Color | undefined;
private labelBottomBorder: Color | undefined;
private labelShadow: Color | undefined;
constructor(container: HTMLElement, private os: OperatingSystem, options?: KeybindingLabelOptions) {
this.options = options || Object.create(null);
this.labelBackground = this.options.keybindingLabelBackground;
this.labelForeground = this.options.keybindingLabelForeground;
this.labelBorder = this.options.keybindingLabelBorder;
this.labelBottomBorder = this.options.keybindingLabelBottomBorder;
this.labelShadow = this.options.keybindingLabelShadow;
this.domNode = dom.append(container, $('.monaco-keybinding'));
this.didEverRender = false;
container.appendChild(this.domNode);
@ -58,7 +86,7 @@ export class KeybindingLabel {
}
private render() {
dom.clearNode(this.domNode);
this.clear();
if (this.keybinding) {
let [firstPart, chordPart] = this.keybinding.getParts();
@ -74,9 +102,16 @@ export class KeybindingLabel {
this.renderUnbound(this.domNode);
}
this.applyStyles();
this.didEverRender = true;
}
private clear(): void {
dom.clearNode(this.domNode);
this.keyElements.clear();
}
private renderPart(parent: HTMLElement, part: ResolvedKeybindingPart, match: PartMatches | null) {
const modifierLabels = UILabelProvider.modifierLabels[this.os];
if (part.ctrlKey) {
@ -98,14 +133,54 @@ export class KeybindingLabel {
}
private renderKey(parent: HTMLElement, label: string, highlight: boolean, separator: string): void {
dom.append(parent, $('span.monaco-keybinding-key' + (highlight ? '.highlight' : ''), undefined, label));
dom.append(parent, this.createKeyElement(label, highlight ? '.highlight' : ''));
if (separator) {
dom.append(parent, $('span.monaco-keybinding-key-separator', undefined, separator));
}
}
private renderUnbound(parent: HTMLElement): void {
dom.append(parent, $('span.monaco-keybinding-key', undefined, localize('unbound', "Unbound")));
dom.append(parent, this.createKeyElement(localize('unbound', "Unbound")));
}
private createKeyElement(label: string, extraClass = ''): HTMLElement {
const keyElement = $('span.monaco-keybinding-key' + extraClass, undefined, label);
this.keyElements.add(keyElement);
return keyElement;
}
style(styles: IKeybindingLabelStyles): void {
this.labelBackground = styles.keybindingLabelBackground;
this.labelForeground = styles.keybindingLabelForeground;
this.labelBorder = styles.keybindingLabelBorder;
this.labelBottomBorder = styles.keybindingLabelBottomBorder;
this.labelShadow = styles.keybindingLabelShadow;
this.applyStyles();
}
private applyStyles() {
if (this.element) {
for (const keyElement of this.keyElements) {
if (this.labelBackground) {
keyElement.style.backgroundColor = this.labelBackground?.toString();
}
if (this.labelBorder) {
keyElement.style.borderColor = this.labelBorder.toString();
}
if (this.labelBottomBorder) {
keyElement.style.borderBottomColor = this.labelBottomBorder.toString();
}
if (this.labelShadow) {
keyElement.style.boxShadow = `inset 0 -1px 0 ${this.labelShadow}`;
}
}
if (this.labelForeground) {
this.element.style.color = this.labelForeground.toString();
}
}
}
private static areSame(a: Matches | undefined, b: Matches | undefined): boolean {

View File

@ -339,7 +339,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
container.appendChild(this.domNode);
this.scrollableElement.onScroll(this.onScroll, this, this.disposables);
domEvent(this.rowsContainer, TouchEventType.Change)(this.onTouchChange, this, this.disposables);
domEvent(this.rowsContainer, TouchEventType.Change)(e => this.onTouchChange(e as GestureEvent), this, this.disposables);
// Prevent the monaco-scrollable-element from scrolling
// https://github.com/microsoft/vscode/issues/44181
@ -362,6 +362,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
updateOptions(options: IListViewOptionsUpdate) {
if (options.additionalScrollHeight !== undefined) {
this.additionalScrollHeight = options.additionalScrollHeight;
this.scrollableElement.setScrollDimensions({ scrollHeight: this.scrollHeight });
}
if (options.smoothScrolling !== undefined) {
@ -407,6 +408,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.items[index].size = size;
this.render(lastRenderRange, Math.max(0, this.lastRenderTop + heightDiff), this.lastRenderHeight, undefined, undefined, true);
this.setScrollTop(this.lastRenderTop);
this.eventuallyUpdateScrollDimensions();
@ -679,12 +681,12 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
if (this.supportDynamicHeights) {
this._rerender(this.scrollTop, this.renderHeight);
}
}
if (this.horizontalScrolling) {
this.scrollableElement.setScrollDimensions({
width: typeof width === 'number' ? width : getContentWidth(this.domNode)
});
}
if (this.horizontalScrolling) {
this.scrollableElement.setScrollDimensions({
width: typeof width === 'number' ? width : getContentWidth(this.domNode)
});
}
}
@ -898,7 +900,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
@memoize get onMouseOut(): Event<IListMouseEvent<T>> { return Event.map(domEvent(this.domNode, 'mouseout'), e => this.toMouseEvent(e)); }
@memoize get onContextMenu(): Event<IListMouseEvent<T>> { return Event.map(domEvent(this.domNode, 'contextmenu'), e => this.toMouseEvent(e)); }
@memoize get onTouchStart(): Event<IListTouchEvent<T>> { return Event.map(domEvent(this.domNode, 'touchstart'), e => this.toTouchEvent(e)); }
@memoize get onTap(): Event<IListGestureEvent<T>> { return Event.map(domEvent(this.rowsContainer, TouchEventType.Tap), e => this.toGestureEvent(e)); }
@memoize get onTap(): Event<IListGestureEvent<T>> { return Event.map(domEvent(this.rowsContainer, TouchEventType.Tap), e => this.toGestureEvent(e as GestureEvent)); }
private toMouseEvent(browserEvent: MouseEvent): IListMouseEvent<T> {
const index = this.getItemIndexFromEventTarget(browserEvent.target || null);

View File

@ -188,7 +188,7 @@ class SelectionTrait<T> extends Trait<T> {
super('selected');
}
renderIndex(index: number, container: HTMLElement): void {
override renderIndex(index: number, container: HTMLElement): void {
super.renderIndex(index, container);
if (this.setAriaSelected) {
@ -1635,13 +1635,13 @@ export class List<T> implements ISpliceable<T>, IThemable, IDisposable {
this.view.setScrollTop(m * clamp(relativeTop, 0, 1) + elementTop);
} else {
const viewItemBottom = elementTop + elementHeight;
const wrapperBottom = scrollTop + this.view.renderHeight;
const scrollBottom = scrollTop + this.view.renderHeight;
if (elementTop < scrollTop && viewItemBottom >= wrapperBottom) {
if (elementTop < scrollTop && viewItemBottom >= scrollBottom) {
// The element is already overflowing the viewport, no-op
} else if (elementTop < scrollTop) {
} else if (elementTop < scrollTop || (viewItemBottom >= scrollBottom && elementHeight >= this.view.renderHeight)) {
this.view.setScrollTop(elementTop);
} else if (viewItemBottom >= wrapperBottom) {
} else if (viewItemBottom >= scrollBottom) {
this.view.setScrollTop(viewItemBottom - this.view.renderHeight);
}
}

View File

@ -37,7 +37,7 @@ export enum Direction {
}
export interface IMenuOptions {
context?: any;
context?: unknown;
actionViewItemProvider?: IActionViewItemProvider;
actionRunner?: IActionRunner;
getKeyBinding?: (action: IAction) => ResolvedKeybinding | undefined;
@ -264,7 +264,7 @@ export class Menu extends ActionBar {
}
}
getContainer(): HTMLElement {
override getContainer(): HTMLElement {
return this.scrollableElement.getDomNode();
}
@ -309,7 +309,7 @@ export class Menu extends ActionBar {
}
}
protected updateFocus(fromRight?: boolean): void {
protected override updateFocus(fromRight?: boolean): void {
super.updateFocus(fromRight, true);
if (typeof this.focusedItem !== 'undefined') {
@ -385,7 +385,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
public container: HTMLElement | undefined;
protected options: IMenuItemOptions;
protected override options: IMenuItemOptions;
protected item: HTMLElement | undefined;
private runOnceToEnableMouseUp: RunOnceScheduler;
@ -465,7 +465,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
this._register(this.runOnceToEnableMouseUp);
}
render(container: HTMLElement): void {
override render(container: HTMLElement): void {
super.render(container);
if (!this.element) {
@ -504,12 +504,12 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
this.updateChecked();
}
blur(): void {
override blur(): void {
super.blur();
this.applyStyle();
}
focus(): void {
override focus(): void {
super.focus();
if (this.item) {
@ -526,7 +526,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
}
}
updateLabel(): void {
override updateLabel(): void {
if (!this.label) {
return;
}
@ -579,7 +579,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
}
}
updateTooltip(): void {
override updateTooltip(): void {
let title: string | null = null;
if (this.getAction().tooltip) {
@ -598,7 +598,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
}
}
updateClass(): void {
override updateClass(): void {
if (this.cssClass && this.item) {
this.item.classList.remove(...this.cssClass.split(' '));
}
@ -614,7 +614,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
}
}
updateEnabled(): void {
override updateEnabled(): void {
if (this.getAction().enabled) {
if (this.element) {
this.element.classList.remove('disabled');
@ -639,7 +639,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
}
}
updateChecked(): void {
override updateChecked(): void {
if (!this.item) {
return;
}
@ -724,7 +724,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
}, 750);
}
render(container: HTMLElement): void {
override render(container: HTMLElement): void {
super.render(container);
if (!this.element) {
@ -783,7 +783,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
}));
}
updateEnabled(): void {
override updateEnabled(): void {
// override on submenu entry
// native menus do not observe enablement on sumbenus
// we mimic that behavior
@ -794,7 +794,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
this.createSubmenu(selectFirst);
}
onClick(e: EventLike): void {
override onClick(e: EventLike): void {
// stop clicking from trying to run an action
EventHelper.stop(e, true);
@ -925,7 +925,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
}
}
protected applyStyle(): void {
protected override applyStyle(): void {
super.applyStyle();
if (!this.menuStyle) {
@ -944,7 +944,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
}
}
dispose(): void {
override dispose(): void {
super.dispose();
this.hideScheduler.dispose();

View File

@ -422,7 +422,7 @@ export class MenuBar extends Disposable {
}
}
dispose(): void {
override dispose(): void {
super.dispose();
this.menuCache.forEach(menuBarMenu => {

View File

@ -211,7 +211,7 @@ export class Sash extends Disposable {
this._register(domEvent(this.el, 'mouseleave')(() => Sash.onMouseLeave(this)));
this._register(Gesture.addTarget(this.el));
this._register(domEvent(this.el, EventType.Start)(this.onTouchStart, this));
this._register(domEvent(this.el, EventType.Start)(e => this.onTouchStart(e as GestureEvent), this));
if (typeof options.size === 'number') {
this.size = options.size;
@ -430,6 +430,10 @@ export class Sash extends Disposable {
}
}
clearSashHoverState(): void {
Sash.onMouseLeave(this);
}
layout(): void {
if (this.orientation === Orientation.VERTICAL) {
const verticalProvider = (<IVerticalSashLayoutProvider>this.layoutProvider);
@ -484,7 +488,7 @@ export class Sash extends Disposable {
return undefined;
}
dispose(): void {
override dispose(): void {
super.dispose();
this.el.remove();
}

View File

@ -214,7 +214,7 @@ export abstract class AbstractScrollableElement extends Widget {
this._domNode.appendChild(this._topShadowDomNode.domNode);
this._topLeftShadowDomNode = createFastDomNode(document.createElement('div'));
this._topLeftShadowDomNode.setClassName('shadow top-left-corner');
this._topLeftShadowDomNode.setClassName('shadow');
this._domNode.appendChild(this._topLeftShadowDomNode.domNode);
} else {
this._leftShadowDomNode = null;
@ -239,7 +239,7 @@ export abstract class AbstractScrollableElement extends Widget {
this._revealOnScroll = true;
}
public dispose(): void {
public override dispose(): void {
this._mouseWheelToDispose = dispose(this._mouseWheelToDispose);
super.dispose();
}
@ -484,9 +484,12 @@ export abstract class AbstractScrollableElement extends Widget {
const enableTop = scrollState.scrollTop > 0;
const enableLeft = scrollState.scrollLeft > 0;
this._leftShadowDomNode!.setClassName('shadow' + (enableLeft ? ' left' : ''));
this._topShadowDomNode!.setClassName('shadow' + (enableTop ? ' top' : ''));
this._topLeftShadowDomNode!.setClassName('shadow top-left-corner' + (enableTop ? ' top' : '') + (enableLeft ? ' left' : ''));
const leftClassName = (enableLeft ? ' left' : '');
const topClassName = (enableTop ? ' top' : '');
const topLeftClassName = (enableLeft || enableTop ? ' top-left-corner' : '');
this._leftShadowDomNode!.setClassName(`shadow${leftClassName}`);
this._topShadowDomNode!.setClassName(`shadow${topClassName}`);
this._topLeftShadowDomNode!.setClassName(`shadow${topLeftClassName}${topClassName}${leftClassName}`);
}
}

View File

@ -1046,7 +1046,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
}
}
public dispose(): void {
public override dispose(): void {
this.hideSelectDropDown(false);
super.dispose();
}

View File

@ -52,6 +52,14 @@
margin-left: auto;
}
.monaco-pane-view .pane > .pane-header > .actions .action-item {
margin-right: 4px;
}
.monaco-pane-view .pane > .pane-header > .actions .action-label {
padding: 2px;
}
/* TODO: actions should be part of the pane, but they aren't yet */
.monaco-pane-view .pane:hover > .pane-header.expanded > .actions,
.monaco-pane-view .pane:focus-within > .pane-header.expanded > .actions,
@ -60,22 +68,6 @@
display: initial;
}
/* TODO: actions should be part of the pane, but they aren't yet */
.monaco-pane-view .pane > .pane-header > .actions .action-label.icon,
.monaco-pane-view .pane > .pane-header > .actions .action-label.codicon {
width: 28px;
height: 22px;
background-size: 16px;
background-position: center center;
background-repeat: no-repeat;
margin-right: 0;
display: flex;
align-items: center;
justify-content: center;
color: inherit;
outline-offset: -2px;
}
.monaco-pane-view .pane > .pane-header .monaco-action-bar .action-item.select-container {
cursor: default;
}

View File

@ -568,7 +568,7 @@ export class PaneView extends Disposable {
}, 200);
}
dispose(): void {
override dispose(): void {
super.dispose();
this.paneItems.forEach(i => i.disposable.dispose());

View File

@ -1022,7 +1022,7 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
return undefined;
}
dispose(): void {
override dispose(): void {
super.dispose();
this.viewItems.forEach(i => i.dispose());

View File

@ -3,6 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-toolbar {
height: 100%;
}
.monaco-toolbar .toolbar-toggle-more {
display: inline-block;
padding: 0;

View File

@ -181,7 +181,7 @@ export class ToolBar extends Disposable {
this.actionBar.clear();
}
dispose(): void {
override dispose(): void {
this.clear();
super.dispose();
}
@ -202,7 +202,7 @@ class ToggleMenuAction extends Action {
this.toggleDropdownMenu = toggleDropdownMenu;
}
async run(): Promise<void> {
override async run(): Promise<void> {
this.toggleDropdownMenu();
}

View File

@ -29,11 +29,11 @@ import { treeItemExpandedIcon, treeFilterOnTypeOnIcon, treeFilterOnTypeOffIcon,
class TreeElementsDragAndDropData<T, TFilterData, TContext> extends ElementsDragAndDropData<T, TContext> {
set context(context: TContext | undefined) {
override set context(context: TContext | undefined) {
this.data.context = context;
}
get context(): TContext | undefined {
override get context(): TContext | undefined {
return this.data.context;
}
@ -1091,7 +1091,7 @@ class TreeNodeListMouseController<T, TFilterData, TRef> extends MouseController<
super(list);
}
protected onViewPointer(e: IListMouseEvent<ITreeNode<T, TFilterData>>): void {
protected override onViewPointer(e: IListMouseEvent<ITreeNode<T, TFilterData>>): void {
if (isInputElement(e.browserEvent.target as HTMLElement) || isMonacoEditor(e.browserEvent.target as HTMLElement)) {
return;
}
@ -1141,7 +1141,7 @@ class TreeNodeListMouseController<T, TFilterData, TRef> extends MouseController<
super.onViewPointer(e);
}
protected onDoubleClick(e: IListMouseEvent<ITreeNode<T, TFilterData>>): void {
protected override onDoubleClick(e: IListMouseEvent<ITreeNode<T, TFilterData>>): void {
const onTwistie = (e.browserEvent.target as HTMLElement).classList.contains('monaco-tl-twistie');
if (onTwistie || !this.tree.expandOnDoubleClick) {
@ -1175,11 +1175,11 @@ class TreeNodeList<T, TFilterData, TRef> extends List<ITreeNode<T, TFilterData>>
super(user, container, virtualDelegate, renderers, options);
}
protected createMouseController(options: ITreeNodeListOptions<T, TFilterData, TRef>): MouseController<ITreeNode<T, TFilterData>> {
protected override createMouseController(options: ITreeNodeListOptions<T, TFilterData, TRef>): MouseController<ITreeNode<T, TFilterData>> {
return new TreeNodeListMouseController(this, options.tree);
}
splice(start: number, deleteCount: number, elements: ITreeNode<T, TFilterData>[] = []): void {
override splice(start: number, deleteCount: number, elements: ITreeNode<T, TFilterData>[] = []): void {
super.splice(start, deleteCount, elements);
if (elements.length === 0) {
@ -1217,7 +1217,7 @@ class TreeNodeList<T, TFilterData, TRef> extends List<ITreeNode<T, TFilterData>>
}
}
setFocus(indexes: number[], browserEvent?: UIEvent, fromAPI = false): void {
override setFocus(indexes: number[], browserEvent?: UIEvent, fromAPI = false): void {
super.setFocus(indexes, browserEvent);
if (!fromAPI) {
@ -1225,7 +1225,7 @@ class TreeNodeList<T, TFilterData, TRef> extends List<ITreeNode<T, TFilterData>>
}
}
setSelection(indexes: number[], browserEvent?: UIEvent, fromAPI = false): void {
override setSelection(indexes: number[], browserEvent?: UIEvent, fromAPI = false): void {
super.setSelection(indexes, browserEvent);
if (!fromAPI) {
@ -1233,7 +1233,7 @@ class TreeNodeList<T, TFilterData, TRef> extends List<ITreeNode<T, TFilterData>>
}
}
setAnchor(index: number | undefined, fromAPI = false): void {
override setAnchor(index: number | undefined, fromAPI = false): void {
super.setAnchor(index);
if (!fromAPI) {

View File

@ -157,11 +157,11 @@ function asTreeContextMenuEvent<TInput, T>(e: ITreeContextMenuEvent<IAsyncDataTr
class AsyncDataTreeElementsDragAndDropData<TInput, T, TContext> extends ElementsDragAndDropData<T, TContext> {
set context(context: TContext | undefined) {
override set context(context: TContext | undefined) {
this.data.context = context;
}
get context(): TContext | undefined {
override get context(): TContext | undefined {
return this.data.context;
}
@ -1131,7 +1131,7 @@ export interface ICompressibleAsyncDataTreeOptionsUpdate extends IAsyncDataTreeO
export class CompressibleAsyncDataTree<TInput, T, TFilterData = void> extends AsyncDataTree<TInput, T, TFilterData> {
protected readonly tree!: CompressibleObjectTree<IAsyncDataTreeNode<TInput, T>, TFilterData>;
protected override readonly tree!: CompressibleObjectTree<IAsyncDataTreeNode<TInput, T>, TFilterData>;
protected readonly compressibleNodeMapper: CompressibleAsyncDataTreeNodeMapper<TInput, T, TFilterData> = new WeakMapper(node => new CompressibleAsyncDataTreeNodeWrapper(node));
private filter?: ITreeFilter<T, TFilterData>;
@ -1148,7 +1148,7 @@ export class CompressibleAsyncDataTree<TInput, T, TFilterData = void> extends As
this.filter = options.filter;
}
protected createTree(
protected override createTree(
user: string,
container: HTMLElement,
delegate: IListVirtualDelegate<T>,
@ -1162,18 +1162,18 @@ export class CompressibleAsyncDataTree<TInput, T, TFilterData = void> extends As
return new CompressibleObjectTree(user, container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions);
}
protected asTreeElement(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): ICompressedTreeElement<IAsyncDataTreeNode<TInput, T>> {
protected override asTreeElement(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): ICompressedTreeElement<IAsyncDataTreeNode<TInput, T>> {
return {
incompressible: this.compressionDelegate.isIncompressible(node.element as T),
...super.asTreeElement(node, viewStateContext)
};
}
updateOptions(options: ICompressibleAsyncDataTreeOptionsUpdate = {}): void {
override updateOptions(options: ICompressibleAsyncDataTreeOptionsUpdate = {}): void {
this.tree.updateOptions(options);
}
getViewState(): IAsyncDataTreeViewState {
override getViewState(): IAsyncDataTreeViewState {
if (!this.identityProvider) {
throw new TreeError(this.user, 'Can\'t get tree view state without an identity provider');
}
@ -1201,7 +1201,7 @@ export class CompressibleAsyncDataTree<TInput, T, TFilterData = void> extends As
return { focus, selection, expanded, scrollTop: this.scrollTop };
}
protected render(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): void {
protected override render(node: IAsyncDataTreeNode<TInput, T>, viewStateContext?: IAsyncDataTreeViewStateContext<TInput, T>): void {
if (!this.identityProvider) {
return super.render(node, viewStateContext);
}
@ -1277,7 +1277,7 @@ export class CompressibleAsyncDataTree<TInput, T, TFilterData = void> extends As
// For compressed async data trees, `TreeVisibility.Recurse` doesn't currently work
// and we have to filter everything beforehand
// Related to #85193 and #85835
protected processChildren(children: Iterable<T>): Iterable<T> {
protected override processChildren(children: Iterable<T>): Iterable<T> {
if (this.filter) {
children = Iterable.filter(children, e => {
const result = this.filter!.filter(e, TreeVisibility.Visible);

View File

@ -23,7 +23,7 @@ export interface IDataTreeViewState {
export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | null, TFilterData, T | null> {
protected model!: ObjectTreeModel<T, TFilterData>;
protected override model!: ObjectTreeModel<T, TFilterData>;
private input: TInput | undefined;
private identityProvider: IIdentityProvider<T> | undefined;

View File

@ -14,7 +14,7 @@ export interface IIndexTreeOptions<T, TFilterData = void> extends IAbstractTreeO
export class IndexTree<T, TFilterData = void> extends AbstractTree<T, TFilterData, number[]> {
protected model!: IndexTreeModel<T, TFilterData>;
protected override model!: IndexTreeModel<T, TFilterData>;
constructor(
user: string,

View File

@ -38,9 +38,9 @@ export interface IObjectTreeSetChildrenOptions<T> {
export class ObjectTree<T extends NonNullable<any>, TFilterData = void> extends AbstractTree<T | null, TFilterData, T | null> {
protected model!: IObjectTreeModel<T, TFilterData>;
protected override model!: IObjectTreeModel<T, TFilterData>;
get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<T | null, TFilterData>> { return this.model.onDidChangeCollapseState; }
override get onDidChangeCollapseState(): Event<ICollapseStateChangeEvent<T | null, TFilterData>> { return this.model.onDidChangeCollapseState; }
constructor(
user: string,
@ -194,7 +194,7 @@ export interface ICompressibleObjectTreeOptionsUpdate extends IAbstractTreeOptio
export class CompressibleObjectTree<T extends NonNullable<any>, TFilterData = void> extends ObjectTree<T, TFilterData> implements ICompressedTreeNodeProvider<T, TFilterData> {
protected model!: CompressibleObjectTreeModel<T, TFilterData>;
protected override model!: CompressibleObjectTreeModel<T, TFilterData>;
constructor(
user: string,
@ -208,15 +208,15 @@ export class CompressibleObjectTree<T extends NonNullable<any>, TFilterData = vo
super(user, container, delegate, compressibleRenderers, asObjectTreeOptions<T, TFilterData>(compressedTreeNodeProvider, options));
}
setChildren(element: T | null, children: Iterable<ICompressedTreeElement<T>> = Iterable.empty(), options?: IObjectTreeSetChildrenOptions<T>): void {
override setChildren(element: T | null, children: Iterable<ICompressedTreeElement<T>> = Iterable.empty(), options?: IObjectTreeSetChildrenOptions<T>): void {
this.model.setChildren(element, children, options);
}
protected createModel(user: string, view: IList<ITreeNode<T, TFilterData>>, options: ICompressibleObjectTreeOptions<T, TFilterData>): ITreeModel<T | null, TFilterData, T | null> {
protected override createModel(user: string, view: IList<ITreeNode<T, TFilterData>>, options: ICompressibleObjectTreeOptions<T, TFilterData>): ITreeModel<T | null, TFilterData, T | null> {
return new CompressibleObjectTreeModel(user, view, options);
}
updateOptions(optionsUpdate: ICompressibleObjectTreeOptionsUpdate = {}): void {
override updateOptions(optionsUpdate: ICompressibleObjectTreeOptionsUpdate = {}): void {
super.updateOptions(optionsUpdate);
if (typeof optionsUpdate.compressionEnabled !== 'undefined') {

View File

@ -13,11 +13,9 @@ export class CollapseAllAction<TInput, T, TFilterData = void> extends Action {
super('vs.tree.collapse', nls.localize('collapse all', "Collapse All"), 'collapse-all', enabled);
}
async run(): Promise<any> {
override async run(): Promise<any> {
this.viewer.collapseAll();
this.viewer.setSelection([]);
this.viewer.setFocus([]);
this.viewer.domFocus();
this.viewer.focusFirst();
}
}

View File

@ -10,7 +10,7 @@ import { Event, Emitter } from 'vs/base/common/event';
export interface ITelemetryData {
readonly from?: string;
readonly target?: string;
[key: string]: any;
[key: string]: unknown;
}
export type WorkbenchActionExecutedClassification = {
@ -30,13 +30,14 @@ export interface IAction extends IDisposable {
class: string | undefined;
enabled: boolean;
checked: boolean;
run(event?: any): Promise<any>;
run(event?: unknown): Promise<unknown>;
}
export interface IActionRunner extends IDisposable {
run(action: IAction, context?: any): Promise<any>;
readonly onDidRun: Event<IRunEvent>;
readonly onBeforeRun: Event<IRunEvent>;
run(action: IAction, context?: unknown): Promise<unknown>;
}
export interface IActionChangeEvent {
@ -58,9 +59,9 @@ export class Action extends Disposable implements IAction {
protected _cssClass: string | undefined;
protected _enabled: boolean = true;
protected _checked: boolean = false;
protected readonly _actionCallback?: (event?: any) => Promise<any>;
protected readonly _actionCallback?: (event?: unknown) => Promise<unknown>;
constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback?: (event?: any) => Promise<any>) {
constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback?: (event?: unknown) => Promise<unknown>) {
super();
this._id = id;
this._label = label;
@ -148,19 +149,16 @@ export class Action extends Disposable implements IAction {
}
}
run(event?: any, _data?: ITelemetryData): Promise<any> {
async run(event?: unknown, data?: ITelemetryData): Promise<void> {
if (this._actionCallback) {
return this._actionCallback(event);
await this._actionCallback(event);
}
return Promise.resolve(true);
}
}
export interface IRunEvent {
readonly action: IAction;
readonly result?: any;
readonly error?: any;
readonly error?: Error;
}
export class ActionRunner extends Disposable implements IActionRunner {
@ -171,24 +169,25 @@ export class ActionRunner extends Disposable implements IActionRunner {
private _onDidRun = this._register(new Emitter<IRunEvent>());
readonly onDidRun = this._onDidRun.event;
async run(action: IAction, context?: any): Promise<any> {
async run(action: IAction, context?: unknown): Promise<void> {
if (!action.enabled) {
return Promise.resolve(null);
return;
}
this._onBeforeRun.fire({ action: action });
this._onBeforeRun.fire({ action });
let error: Error | undefined = undefined;
try {
const result = await this.runAction(action, context);
this._onDidRun.fire({ action: action, result: result });
} catch (error) {
this._onDidRun.fire({ action: action, error: error });
await this.runAction(action, context);
} catch (e) {
error = e;
}
this._onDidRun.fire({ action, error });
}
protected runAction(action: IAction, context?: any): Promise<any> {
const res = context ? action.run(context) : action.run();
return Promise.resolve(res);
protected async runAction(action: IAction, context?: unknown): Promise<void> {
await action.run(context);
}
}
@ -198,6 +197,7 @@ export class Separator extends Action {
constructor(label?: string) {
super(Separator.ID, label, label ? 'separator text' : 'separator');
this.checked = false;
this.enabled = false;
}
@ -213,6 +213,7 @@ export class SubmenuAction implements IAction {
readonly checked: boolean = false;
private readonly _actions: readonly IAction[];
get actions(): readonly IAction[] { return this._actions; }
constructor(id: string, label: string, actions: readonly IAction[], cssClass?: string) {
this.id = id;
@ -227,15 +228,13 @@ export class SubmenuAction implements IAction {
// to bridge into the rendering world.
}
get actions(): readonly IAction[] {
return this._actions;
}
async run(): Promise<any> { }
async run(): Promise<void> { }
}
export class EmptySubmenuAction extends Action {
static readonly ID = 'vs.actions.empty';
constructor() {
super(EmptySubmenuAction.ID, nls.localize('submenu.empty', '(empty)'), undefined, false);
}

View File

@ -381,7 +381,7 @@ export class AutoOpenBarrier extends Barrier {
this._timeout = setTimeout(() => this.open(), autoOpenTimeMs);
}
open(): void {
override open(): void {
clearTimeout(this._timeout);
super.open();
}
@ -772,7 +772,7 @@ export class RunOnceWorker<T> extends RunOnceScheduler {
}
}
protected doRun(): void {
protected override doRun(): void {
const units = this.units;
this.units = [];
@ -781,7 +781,7 @@ export class RunOnceWorker<T> extends RunOnceScheduler {
}
}
dispose(): void {
override dispose(): void {
this.units = [];
super.dispose();
@ -942,7 +942,7 @@ export class TaskSequentializer {
}
setPending(taskId: number, promise: Promise<void>, onCancel?: () => void,): Promise<void> {
this._pending = { taskId: taskId, cancel: () => onCancel?.(), promise };
this._pending = { taskId, cancel: () => onCancel?.(), promise };
promise.then(() => this.donePending(taskId), () => this.donePending(taskId));
@ -1179,7 +1179,7 @@ export namespace Promises {
* Interface of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
*/
interface PromiseWithAllSettled<T> {
allSettled<T>(promises: Promise<T>[]): Promise<ReadonlyArray<IResolvedPromise<T> | IRejectedPromise>>;
allSettled<T>(promises: Promise<T>[]): Promise<readonly (IResolvedPromise<T> | IRejectedPromise)[]>;
}
/**
@ -1188,7 +1188,7 @@ export namespace Promises {
* in the order of the original passed in promises array.
* See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
*/
export async function allSettled<T>(promises: Promise<T>[]): Promise<ReadonlyArray<IResolvedPromise<T> | IRejectedPromise>> {
export async function allSettled<T>(promises: Promise<T>[]): Promise<readonly (IResolvedPromise<T> | IRejectedPromise)[]> {
if (typeof (Promise as unknown as PromiseWithAllSettled<T>).allSettled === 'function') {
return allSettledNative(promises); // in some environments we can benefit from native implementation
}
@ -1196,11 +1196,11 @@ export namespace Promises {
return allSettledShim(promises);
}
async function allSettledNative<T>(promises: Promise<T>[]): Promise<ReadonlyArray<IResolvedPromise<T> | IRejectedPromise>> {
async function allSettledNative<T>(promises: Promise<T>[]): Promise<readonly (IResolvedPromise<T> | IRejectedPromise)[]> {
return (Promise as unknown as PromiseWithAllSettled<T>).allSettled(promises);
}
async function allSettledShim<T>(promises: Promise<T>[]): Promise<ReadonlyArray<IResolvedPromise<T> | IRejectedPromise>> {
async function allSettledShim<T>(promises: Promise<T>[]): Promise<readonly (IResolvedPromise<T> | IRejectedPromise)[]> {
return Promise.all(promises.map(promise => (promise.then(value => {
const fulfilled: IResolvedPromise<T> = { status: 'fulfilled', value };

View File

@ -235,3 +235,11 @@ export function streamToBufferReadableStream(stream: streams.ReadableStreamEvent
export function newWriteableBufferStream(options?: streams.WriteableStreamOptions): streams.WriteableStream<VSBuffer> {
return streams.newWriteableStream<VSBuffer>(chunks => VSBuffer.concat(chunks), options);
}
export function prefixedBufferReadable(prefix: VSBuffer, readable: VSBufferReadable): VSBufferReadable {
return streams.prefixedReadable(prefix, readable, chunks => VSBuffer.concat(chunks));
}
export function prefixedBufferStream(prefix: VSBuffer, stream: VSBufferReadableStream): VSBufferReadableStream {
return streams.prefixedStream(prefix, stream, chunks => VSBuffer.concat(chunks));
}

View File

@ -49,6 +49,16 @@ export function registerCodicon(id: string, def: Codicon): Codicon {
return new Codicon(id, def);
}
// Selects all codicon names encapsulated in the `$()` syntax and wraps the
// results with spaces so that screen readers can read the text better.
export function getCodiconAriaLabel(text: string | undefined) {
if (!text) {
return '';
}
return text.replace(/\$\((.*?)\)/g, (_match, codiconName) => ` ${codiconName} `).trim();
}
export class Codicon implements CSSIcon {
constructor(public readonly id: string, public readonly definition: Codicon | IconDefinition, public description?: string) {
_registry.add(this);
@ -131,7 +141,6 @@ export namespace Codicon {
export const tagAdd = new Codicon('tag-add', { fontCharacter: '\\ea66' });
export const tagRemove = new Codicon('tag-remove', { fontCharacter: '\\ea66' });
export const person = new Codicon('person', { fontCharacter: '\\ea67' });
export const personAdd = new Codicon('person-add', { fontCharacter: '\\ea67' });
export const personFollow = new Codicon('person-follow', { fontCharacter: '\\ea67' });
export const personOutline = new Codicon('person-outline', { fontCharacter: '\\ea67' });
export const personFilled = new Codicon('person-filled', { fontCharacter: '\\ea67' });
@ -550,7 +559,20 @@ export namespace Codicon {
export const debugRerun = new Codicon('debug-rerun', { fontCharacter: '\\ebc0' });
export const workspaceTrusted = new Codicon('workspace-trusted', { fontCharacter: '\\ebc1' });
export const workspaceUntrusted = new Codicon('workspace-untrusted', { fontCharacter: '\\ebc2' });
export const workspaceUnknown = new Codicon('workspace-unknown', { fontCharacter: '\\ebc3' });
export const workspaceUnspecified = new Codicon('workspace-unspecified', { fontCharacter: '\\ebc3' });
export const terminalCmd = new Codicon('terminal-cmd', { fontCharacter: '\\ebc4' });
export const terminalDebian = new Codicon('terminal-debian', { fontCharacter: '\\ebc5' });
export const terminalLinux = new Codicon('terminal-linux', { fontCharacter: '\\ebc6' });
export const terminalPowershell = new Codicon('terminal-powershell', { fontCharacter: '\\ebc7' });
export const terminalTmux = new Codicon('terminal-tmux', { fontCharacter: '\\ebc8' });
export const terminalUbuntu = new Codicon('terminal-ubuntu', { fontCharacter: '\\ebc9' });
export const terminalBash = new Codicon('terminal-bash', { fontCharacter: '\\ebca' });
export const arrowSwap = new Codicon('arrow-swap', { fontCharacter: '\\ebcb' });
export const copy = new Codicon('copy', { fontCharacter: '\\ebcc' });
export const personAdd = new Codicon('person-add', { fontCharacter: '\\ebcd' });
export const filterFilled = new Codicon('filter-filled', { fontCharacter: '\\ebce' });
export const wand = new Codicon('wand', { fontCharacter: '\\ebcf' });
export const debugLineByLine = new Codicon('debug-line-by-line', { fontCharacter: '\\ebd0' });
export const dropDownButton = new Codicon('drop-down-button', Codicon.chevronDown.definition);
}

View File

@ -66,6 +66,24 @@ export function groupBy<T>(data: T[], groupFn: (element: T) => string): IStringD
return result;
}
/**
* Groups the collection into a dictionary based on the provided
* group function.
*/
export function groupByNumber<T>(data: T[], groupFn: (element: T) => number): Map<number, T[]> {
const result = new Map<number, T[]>();
for (const element of data) {
const key = groupFn(element);
let target = result.get(key);
if (!target) {
target = [];
result.set(key, target);
}
target.push(element);
}
return result;
}
export function fromMap<T>(original: Map<string, T>): IStringDictionary<T> {
const result: IStringDictionary<T> = Object.create(null);
if (original) {

View File

@ -229,11 +229,11 @@ export class ExpectedError extends Error {
}
export interface IErrorOptions {
actions?: ReadonlyArray<IAction>;
actions?: readonly IAction[];
}
export interface IErrorWithActions {
actions?: ReadonlyArray<IAction>;
actions?: readonly IAction[];
}
export function isErrorWithActions(obj: unknown): obj is IErrorWithActions {

View File

@ -674,7 +674,7 @@ export class PauseableEmitter<T> extends Emitter<T> {
}
}
fire(event: T): void {
override fire(event: T): void {
if (this._listeners) {
if (this._isPaused !== 0) {
this._eventQueue.push(event);

View File

@ -12,6 +12,8 @@ import { illegalArgument } from 'vs/base/common/errors';
* But these are "more general", as they should work across browsers & OS`s.
*/
export const enum KeyCode {
DependsOnKbLayout = -1,
/**
* Placed first to cover the 0 value of the enum.
*/

View File

@ -887,7 +887,7 @@ export class LinkedMap<K, V> implements Map<K, V> {
this._tail = undefined;
}
else if (item === this._head) {
// This can only happend if size === 1 which is handle
// This can only happen if size === 1 which is handled
// by the case above.
if (!item.next) {
throw new Error('Invalid list');
@ -896,7 +896,7 @@ export class LinkedMap<K, V> implements Map<K, V> {
this._head = item.next;
}
else if (item === this._tail) {
// This can only happend if size === 1 which is handle
// This can only happen if size === 1 which is handled
// by the case above.
if (!item.previous) {
throw new Error('Invalid list');
@ -1028,7 +1028,7 @@ export class LRUCache<K, V> extends LinkedMap<K, V> {
this.checkTrim();
}
get(key: K, touch: Touch = Touch.AsNew): V | undefined {
override get(key: K, touch: Touch = Touch.AsNew): V | undefined {
return super.get(key, touch);
}
@ -1036,7 +1036,7 @@ export class LRUCache<K, V> extends LinkedMap<K, V> {
return super.get(key, Touch.None);
}
set(key: K, value: V): this {
override set(key: K, value: V): this {
super.set(key, value, Touch.AsNew);
this.checkTrim();
return this;
@ -1048,3 +1048,47 @@ export class LRUCache<K, V> extends LinkedMap<K, V> {
}
}
}
/**
* Wraps the map in type that only implements readonly properties. Useful
* in the extension host to prevent the consumer from making any mutations.
*/
export class ReadonlyMapView<K, V> implements ReadonlyMap<K, V>{
readonly #source: ReadonlyMap<K, V>;
public get size() {
return this.#source.size;
}
constructor(source: ReadonlyMap<K, V>) {
this.#source = source;
}
forEach(callbackfn: (value: V, key: K, map: ReadonlyMap<K, V>) => void, thisArg?: any): void {
this.#source.forEach(callbackfn, thisArg);
}
get(key: K): V | undefined {
return this.#source.get(key);
}
has(key: K): boolean {
return this.#source.has(key);
}
entries(): IterableIterator<[K, V]> {
return this.#source.entries();
}
keys(): IterableIterator<K> {
return this.#source.keys();
}
values(): IterableIterator<V> {
return this.#source.values();
}
[Symbol.iterator](): IterableIterator<[K, V]> {
return this.#source.entries();
}
}

View File

@ -66,6 +66,8 @@ export namespace Schemas {
export const vscodeWorkspaceTrust = 'vscode-workspace-trust';
export const vscodeTerminal = 'vscode-terminal';
export const webviewPanel = 'webview-panel';
/**
@ -73,11 +75,6 @@ export namespace Schemas {
*/
export const vscodeWebview = 'vscode-webview';
/**
* Scheme used for loading resources inside of webviews.
*/
export const vscodeWebviewResource = 'vscode-webview-resource';
/**
* Scheme used for extension pages
*/
@ -88,6 +85,11 @@ export namespace Schemas {
* files with our custom protocol handler (desktop only).
*/
export const vscodeFileResource = 'vscode-file';
/**
* Scheme used for temporary resources
*/
export const tmp = 'tmp';
}
class RemoteAuthoritiesImpl {
@ -162,7 +164,7 @@ class FileAccessImpl {
}
// Only convert the URI if we are in a native context and it has `file:` scheme
// and we have explicitly enabled the conversion (sandbox, or ENABLE_VSCODE_BROWSER_CODE_LOADING)
// and we have explicitly enabled the conversion (sandbox, or VSCODE_BROWSER_CODE_LOADING)
if (platform.isNative && (__forceCodeFileUri || platform.isPreferringBrowserCodeLoad) && uri.scheme === Schemas.file) {
return uri.with({
scheme: Schemas.vscodeFileResource,

View File

@ -24,7 +24,7 @@ interface NLSConfig {
}
export interface IProcessEnvironment {
[key: string]: string;
[key: string]: string | undefined;
}
/**
@ -53,12 +53,12 @@ declare const self: unknown;
export const globals: any = (typeof self === 'object' ? self : typeof global === 'object' ? global : {});
let nodeProcess: INodeProcess | undefined = undefined;
if (typeof process !== 'undefined') {
// Native environment (non-sandboxed)
nodeProcess = process;
} else if (typeof globals.vscode !== 'undefined') {
if (typeof globals.vscode !== 'undefined') {
// Native environment (sandboxed)
nodeProcess = globals.vscode.process;
} else if (typeof process !== 'undefined') {
// Native environment (non-sandboxed)
nodeProcess = process;
}
const isElectronRenderer = typeof nodeProcess?.versions?.electron === 'string' && nodeProcess.type === 'renderer';
@ -71,7 +71,7 @@ export const browserCodeLoadingCacheStrategy: 'none' | 'code' | 'bypassHeatCheck
}
// Otherwise, only enabled conditionally
const env = nodeProcess?.env['ENABLE_VSCODE_BROWSER_CODE_LOADING'];
const env = nodeProcess?.env['VSCODE_BROWSER_CODE_LOADING'];
if (typeof env === 'string') {
if (env === 'none' || env === 'code' || env === 'bypassHeatCheck' || env === 'bypassHeatCheckAndEagerCompile') {
return env;

View File

@ -6,20 +6,10 @@
import { isWindows, isMacintosh, setImmediate, globals, INodeProcess } from 'vs/base/common/platform';
let safeProcess: INodeProcess & { nextTick: (callback: (...args: any[]) => void) => void; };
// Native node.js environment
declare const process: INodeProcess;
if (typeof process !== 'undefined') {
safeProcess = {
get platform() { return process.platform; },
get env() { return process.env; },
cwd() { return process.env['VSCODE_CWD'] || process.cwd(); },
nextTick(callback: (...args: any[]) => void): void { return process.nextTick!(callback); }
};
}
// Native sandbox environment
else if (typeof globals.vscode !== 'undefined') {
if (typeof globals.vscode !== 'undefined') {
const sandboxProcess: INodeProcess = globals.vscode.process;
safeProcess = {
get platform() { return sandboxProcess.platform; },
@ -29,6 +19,16 @@ else if (typeof globals.vscode !== 'undefined') {
};
}
// Native node.js environment
else if (typeof process !== 'undefined') {
safeProcess = {
get platform() { return process.platform; },
get env() { return process.env; },
cwd() { return process.env['VSCODE_CWD'] || process.cwd(); },
nextTick(callback: (...args: any[]) => void): void { return process.nextTick!(callback); }
};
}
// Web environment
else {
safeProcess = {

View File

@ -0,0 +1,168 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IStringDictionary } from 'vs/base/common/collections';
export interface IBuiltInExtension {
readonly name: string;
readonly version: string;
readonly repo: string;
readonly metadata: any;
}
export type ConfigurationSyncStore = {
url: string,
insidersUrl: string,
stableUrl: string,
canSwitch: boolean,
authenticationProviders: IStringDictionary<{ scopes: string[] }>
};
export type ExtensionUntrustedWorkspaceSupport = {
readonly default?: boolean | 'limited',
readonly override?: boolean | 'limited'
};
export interface IProductConfiguration {
readonly version: string;
readonly date?: string;
readonly quality?: string;
readonly commit?: string;
readonly nameShort: string;
readonly nameLong: string;
readonly win32AppUserModelId?: string;
readonly win32MutexName?: string;
readonly applicationName: string;
readonly urlProtocol: string;
readonly dataFolderName: string; // location for extensions (e.g. ~/.vscode-insiders)
readonly builtInExtensions?: IBuiltInExtension[];
readonly downloadUrl?: string;
readonly updateUrl?: string;
readonly webEndpointUrl?: string;
readonly target?: string;
readonly settingsSearchBuildId?: number;
readonly settingsSearchUrl?: string;
readonly tasConfig?: {
endpoint: string;
telemetryEventName: string;
featuresTelemetryPropertyName: string;
assignmentContextTelemetryPropertyName: string;
};
readonly experimentsUrl?: string;
readonly extensionsGallery?: {
readonly serviceUrl: string;
readonly itemUrl: string;
readonly controlUrl: string;
readonly recommendationsUrl: string;
};
readonly extensionTips?: { [id: string]: string; };
readonly extensionImportantTips?: IStringDictionary<ImportantExtensionTip>;
readonly configBasedExtensionTips?: { [id: string]: IConfigBasedExtensionTip; };
readonly exeBasedExtensionTips?: { [id: string]: IExeBasedExtensionTip; };
readonly remoteExtensionTips?: { [remoteName: string]: IRemoteExtensionTip; };
readonly extensionKeywords?: { [extension: string]: readonly string[]; };
readonly keymapExtensionTips?: readonly string[];
readonly trustedExtensionUrlPublicKeys?: { [id: string]: string[]; };
readonly crashReporter?: {
readonly companyName: string;
readonly productName: string;
};
readonly enableTelemetry?: boolean;
readonly aiConfig?: {
readonly asimovKey: string;
};
readonly sendASmile?: {
readonly reportIssueUrl: string,
readonly requestFeatureUrl: string
};
readonly documentationUrl?: string;
readonly releaseNotesUrl?: string;
readonly keyboardShortcutsUrlMac?: string;
readonly keyboardShortcutsUrlLinux?: string;
readonly keyboardShortcutsUrlWin?: string;
readonly introductoryVideosUrl?: string;
readonly tipsAndTricksUrl?: string;
readonly newsletterSignupUrl?: string;
readonly twitterUrl?: string;
readonly requestFeatureUrl?: string;
readonly reportIssueUrl?: string;
readonly reportMarketplaceIssueUrl?: string;
readonly licenseUrl?: string;
readonly privacyStatementUrl?: string;
readonly telemetryOptOutUrl?: string;
readonly npsSurveyUrl?: string;
readonly cesSurveyUrl?: string;
readonly surveys?: readonly ISurveyData[];
readonly checksums?: { [path: string]: string; };
readonly checksumFailMoreInfoUrl?: string;
readonly appCenter?: IAppCenterConfiguration;
readonly portable?: string;
readonly extensionKind?: { readonly [extensionId: string]: ('ui' | 'workspace' | 'web')[]; };
readonly extensionSyncedKeys?: { readonly [extensionId: string]: string[]; };
readonly extensionAllowedProposedApi?: readonly string[];
readonly extensionUntrustedWorkspaceSupport?: { readonly [extensionId: string]: ExtensionUntrustedWorkspaceSupport };
readonly extensionVirtualWorkspacesSupport?: { readonly [extensionId: string]: { default?: boolean, override?: boolean } };
readonly msftInternalDomains?: string[];
readonly linkProtectionTrustedDomains?: readonly string[];
readonly 'configurationSync.store'?: ConfigurationSyncStore;
readonly darwinUniversalAssetId?: string;
}
export type ImportantExtensionTip = { name: string; languages?: string[]; pattern?: string; isExtensionPack?: boolean };
export interface IAppCenterConfiguration {
readonly 'win32-ia32': string;
readonly 'win32-x64': string;
readonly 'linux-x64': string;
readonly 'darwin': string;
}
export interface IConfigBasedExtensionTip {
configPath: string;
configName: string;
recommendations: IStringDictionary<{ name: string, remotes?: string[], important?: boolean, isExtensionPack?: boolean }>;
}
export interface IExeBasedExtensionTip {
friendlyName: string;
windowsPath?: string;
important?: boolean;
recommendations: IStringDictionary<{ name: string, important?: boolean, isExtensionPack?: boolean }>;
}
export interface IRemoteExtensionTip {
friendlyName: string;
extensionId: string;
}
export interface ISurveyData {
surveyId: string;
surveyUrl: string;
languageId: string;
editCount: number;
userProbability: number;
}

View File

@ -9,6 +9,7 @@ import { KeyCode } from 'vs/base/common/keyCodes';
* keyboardEvent.code
*/
export const enum ScanCode {
DependsOnKbLayout = -1,
None,
Hyper,
@ -468,11 +469,11 @@ export class ScanCodeBinding {
(function () {
for (let i = 0; i <= ScanCode.MAX_VALUE; i++) {
IMMUTABLE_CODE_TO_KEY_CODE[i] = -1;
IMMUTABLE_CODE_TO_KEY_CODE[i] = KeyCode.DependsOnKbLayout;
}
for (let i = 0; i <= KeyCode.MAX_VALUE; i++) {
IMMUTABLE_KEY_CODE_TO_CODE[i] = -1;
IMMUTABLE_KEY_CODE_TO_CODE[i] = ScanCode.DependsOnKbLayout;
}
function define(code: ScanCode, keyCode: KeyCode): void {

View File

@ -223,7 +223,7 @@ export class Scrollable extends Disposable {
this._smoothScrolling = null;
}
public dispose(): void {
public override dispose(): void {
if (this._smoothScrolling) {
this._smoothScrolling.dispose();
this._smoothScrolling = null;

View File

@ -18,6 +18,7 @@ namespace Severity {
const _warning = 'warning';
const _warn = 'warn';
const _info = 'info';
const _ignore = 'ignore';
/**
* Parses 'error', 'warning', 'warn', 'info' in call casings
@ -41,6 +42,15 @@ namespace Severity {
}
return Severity.Ignore;
}
export function toString(severity: Severity): string {
switch (severity) {
case Severity.Error: return _error;
case Severity.Warning: return _warning;
case Severity.Info: return _info;
default: return _ignore;
}
}
}
export default Severity;

View File

@ -142,15 +142,21 @@ export interface ReadableBufferedStream<T> {
}
export function isReadableStream<T>(obj: unknown): obj is ReadableStream<T> {
const candidate = obj as ReadableStream<T>;
const candidate = obj as ReadableStream<T> | undefined;
if (!candidate) {
return false;
}
return candidate && [candidate.on, candidate.pause, candidate.resume, candidate.destroy].every(fn => typeof fn === 'function');
return [candidate.on, candidate.pause, candidate.resume, candidate.destroy].every(fn => typeof fn === 'function');
}
export function isReadableBufferedStream<T>(obj: unknown): obj is ReadableBufferedStream<T> {
const candidate = obj as ReadableBufferedStream<T>;
const candidate = obj as ReadableBufferedStream<T> | undefined;
if (!candidate) {
return false;
}
return candidate && isReadableStream(candidate.stream) && Array.isArray(candidate.buffer) && typeof candidate.ended === 'boolean';
return isReadableStream(candidate.stream) && Array.isArray(candidate.buffer) && typeof candidate.ended === 'boolean';
}
export interface IReducer<T> {
@ -625,6 +631,16 @@ export function toStream<T>(t: T, reducer: IReducer<T>): ReadableStream<T> {
return stream;
}
/**
* Helper to create an empty stream
*/
export function emptyStream(): ReadableStream<never> {
const stream = newWriteableStream<never>(() => { throw new Error('not supported'); });
stream.end();
return stream;
}
/**
* Helper to convert a T into a Readable<T>.
*/
@ -658,3 +674,71 @@ export function transform<Original, Transformed>(stream: ReadableStreamEvents<Or
return target;
}
/**
* Helper to take an existing readable that will
* have a prefix injected to the beginning.
*/
export function prefixedReadable<T>(prefix: T, readable: Readable<T>, reducer: IReducer<T>): Readable<T> {
let prefixHandled = false;
return {
read: () => {
const chunk = readable.read();
// Handle prefix only once
if (!prefixHandled) {
prefixHandled = true;
// If we have also a read-result, make
// sure to reduce it to a single result
if (chunk !== null) {
return reducer([prefix, chunk]);
}
// Otherwise, just return prefix directly
return prefix;
}
return chunk;
}
};
}
/**
* Helper to take an existing stream that will
* have a prefix injected to the beginning.
*/
export function prefixedStream<T>(prefix: T, stream: ReadableStream<T>, reducer: IReducer<T>): ReadableStream<T> {
let prefixHandled = false;
const target = newWriteableStream<T>(reducer);
listenStream(stream, {
onData: data => {
// Handle prefix only once
if (!prefixHandled) {
prefixHandled = true;
return target.write(reducer([prefix, data]));
}
return target.write(data);
},
onError: error => target.error(error),
onEnd: () => {
// Handle prefix only once
if (!prefixHandled) {
prefixHandled = true;
target.write(prefix);
}
target.end();
}
});
return target;
}

View File

@ -240,19 +240,19 @@ export function withUndefinedAsNull<T>(x: T | undefined): T | null {
return typeof x === 'undefined' ? null : x;
}
type AddFirstParameterToFunction<T, TargetFunctionsReturnType, FirstParameter> = T extends (...args: any[]) => TargetFunctionsReturnType ?
// Function: add param to function
(firstArg: FirstParameter, ...args: Parameters<T>) => ReturnType<T> :
// Else: just leave as is
T;
/**
* Allows to add a first parameter to functions of a type.
*/
export type AddFirstParameterToFunctions<Target, TargetFunctionsReturnType, FirstParameter> = {
// For every property
[K in keyof Target]:
// Function: add param to function
Target[K] extends (...args: any[]) => TargetFunctionsReturnType ? (firstArg: FirstParameter, ...args: Parameters<Target[K]>) => ReturnType<Target[K]> :
// Else: just leave as is
Target[K]
// For every property
[K in keyof Target]: AddFirstParameterToFunction<Target[K], TargetFunctionsReturnType, FirstParameter>;
};
/**
@ -286,3 +286,7 @@ export function NotImplementedProxy<T>(name: string): { new(): T } {
}
};
}
export function assertNever(value: never) {
throw new Error('Unreachable');
}

View File

@ -418,14 +418,14 @@ class Uri extends URI {
_formatted: string | null = null;
_fsPath: string | null = null;
get fsPath(): string {
override get fsPath(): string {
if (!this._fsPath) {
this._fsPath = uriToFsPath(this, false);
}
return this._fsPath;
}
toString(skipEncoding: boolean = false): string {
override toString(skipEncoding: boolean = false): string {
if (!skipEncoding) {
if (!this._formatted) {
this._formatted = _asFormatted(this, false);
@ -437,7 +437,7 @@ class Uri extends URI {
}
}
toJSON(): UriComponents {
override toJSON(): UriComponents {
const res = <UriState>{
$mid: 1
};

View File

@ -18,7 +18,7 @@ export class LineDecoder {
private stringDecoder: sd.StringDecoder;
private remaining: string | null;
constructor(encoding: string = 'utf8') {
constructor(encoding: BufferEncoding = 'utf8') {
this.stringDecoder = new sd.StringDecoder(encoding);
this.remaining = null;
}

View File

@ -56,8 +56,9 @@ export const virtualMachineHint: { value(): number } = new class {
const interfaces = networkInterfaces();
for (let name in interfaces) {
if (Object.prototype.hasOwnProperty.call(interfaces, name)) {
for (const { mac, internal } of interfaces[name]) {
const networkInterface = interfaces[name];
if (networkInterface) {
for (const { mac, internal } of networkInterface) {
if (!internal) {
interfaceCount += 1;
if (this._isVirtualMachineMacAdress(mac.toUpperCase())) {

View File

@ -34,10 +34,13 @@ function doGetMac(): Promise<string> {
return new Promise((resolve, reject) => {
try {
const ifaces = networkInterfaces();
for (const [, infos] of Object.entries(ifaces)) {
for (const info of infos) {
if (validateMacAddress(info.mac)) {
return resolve(info.mac);
for (let name in ifaces) {
const networkInterface = ifaces[name];
if (networkInterface) {
for (const { mac } of networkInterface) {
if (validateMacAddress(mac)) {
return resolve(mac);
}
}
}
}

View File

@ -379,7 +379,7 @@ export class LineProcess extends AbstractProcess<LineData> {
this.stderrLineDecoder = stderrLineDecoder;
}
protected handleClose(data: any, cc: ValueCallback<SuccessData>, pp: ProgressCallback<LineData>, ee: ErrorCallback): void {
protected override handleClose(data: any, cc: ValueCallback<SuccessData>, pp: ProgressCallback<LineData>, ee: ErrorCallback): void {
const stdoutLine = this.stdoutLineDecoder ? this.stdoutLineDecoder.end() : null;
if (stdoutLine) {
pp({ line: stdoutLine, source: Source.stdout });

View File

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as os from 'os';
import { release, userInfo } from 'os';
import * as platform from 'vs/base/common/platform';
import { getFirstAvailablePowerShellInstallation } from 'vs/base/node/powershell';
import * as processes from 'vs/base/node/processes';
@ -11,10 +11,10 @@ import * as processes from 'vs/base/node/processes';
/**
* Gets the detected default shell for the _system_, not to be confused with VS Code's _default_
* shell that the terminal uses by default.
* @param p The platform to detect the shell of.
* @param os The platform to detect the shell of.
*/
export async function getSystemShell(p: platform.Platform, env: platform.IProcessEnvironment): Promise<string> {
if (p === platform.Platform.Windows) {
export async function getSystemShell(os: platform.OperatingSystem, env: platform.IProcessEnvironment): Promise<string> {
if (os === platform.OperatingSystem.Windows) {
if (platform.isWindows) {
return getSystemShellWindows();
}
@ -22,11 +22,11 @@ export async function getSystemShell(p: platform.Platform, env: platform.IProces
return processes.getWindowsShell(env);
}
return getSystemShellUnixLike(p, env);
return getSystemShellUnixLike(os, env);
}
export function getSystemShellSync(p: platform.Platform, env: platform.IProcessEnvironment): string {
if (p === platform.Platform.Windows) {
export function getSystemShellSync(os: platform.OperatingSystem, env: platform.IProcessEnvironment): string {
if (os === platform.OperatingSystem.Windows) {
if (platform.isWindows) {
return getSystemShellWindowsSync(env);
}
@ -34,13 +34,13 @@ export function getSystemShellSync(p: platform.Platform, env: platform.IProcessE
return processes.getWindowsShell(env);
}
return getSystemShellUnixLike(p, env);
return getSystemShellUnixLike(os, env);
}
let _TERMINAL_DEFAULT_SHELL_UNIX_LIKE: string | null = null;
function getSystemShellUnixLike(p: platform.Platform, env: platform.IProcessEnvironment): string {
function getSystemShellUnixLike(os: platform.OperatingSystem, env: platform.IProcessEnvironment): string {
// Only use $SHELL for the current OS
if (platform.isLinux && p === platform.Platform.Mac || platform.isMacintosh && p === platform.Platform.Linux) {
if (platform.isLinux && os === platform.OperatingSystem.Macintosh || platform.isMacintosh && os === platform.OperatingSystem.Linux) {
return '/bin/bash';
}
@ -55,7 +55,7 @@ function getSystemShellUnixLike(p: platform.Platform, env: platform.IProcessEnvi
try {
// It's possible for $SHELL to be unset, this API reads /etc/passwd. See https://github.com/github/codespaces/issues/1639
// Node docs: "Throws a SystemError if a user has no username or homedir."
unixLikeTerminal = os.userInfo().shell;
unixLikeTerminal = userInfo().shell;
} catch (err) { }
}
@ -86,7 +86,7 @@ function getSystemShellWindowsSync(env: platform.IProcessEnvironment): string {
return _TERMINAL_DEFAULT_SHELL_WINDOWS;
}
const isAtLeastWindows10 = platform.isWindows && parseFloat(os.release()) >= 10;
const isAtLeastWindows10 = platform.isWindows && parseFloat(release()) >= 10;
const is32ProcessOn64Windows = env.hasOwnProperty('PROCESSOR_ARCHITEW6432');
const powerShellPath = `${env['windir']}\\${is32ProcessOn64Windows ? 'Sysnative' : 'System32'}\\WindowsPowerShell\\v1.0\\powershell.exe`;
return isAtLeastWindows10 ? powerShellPath : processes.getWindowsShell(env);

View File

@ -72,7 +72,7 @@ export class Client extends IPCClient implements IDisposable {
this.protocol = protocol;
}
dispose(): void {
override dispose(): void {
this.protocol.disconnect();
}
}

View File

@ -252,7 +252,7 @@ class ProtocolReader extends Disposable {
return this._incomingData.read(this._incomingData.byteLength);
}
public dispose(): void {
public override dispose(): void {
this._isDisposed = true;
super.dispose();
}
@ -412,7 +412,7 @@ export class Client<TContext = string> extends IPCClient<TContext> {
super(protocol, id, ipcLogger);
}
dispose(): void {
override dispose(): void {
super.dispose();
const socket = this.protocol.getSocket();
this.protocol.sendDisconnect();

View File

@ -32,7 +32,7 @@ export class Client extends IPCClient implements IDisposable {
this.protocol = protocol;
}
dispose(): void {
override dispose(): void {
this.protocol.disconnect();
}
}

View File

@ -266,7 +266,7 @@ export class WebSocketNodeSocket extends Disposable implements ISocket {
this._register(this.socket.onClose(() => this._onClose.fire()));
}
public dispose(): void {
public override dispose(): void {
if (this._zlibDeflateFlushWaitingCount > 0) {
// Wait for any outstanding writes to finish before disposing
this._register(this._onDidZlibFlush.event(() => {
@ -581,7 +581,7 @@ export class Server extends IPCServer {
this.server = server;
}
dispose(): void {
override dispose(): void {
super.dispose();
if (this.server) {
this.server.close();

View File

@ -66,7 +66,7 @@ class TestIPCClient extends IPCClient<string> {
super(protocol, id);
}
dispose(): void {
override dispose(): void {
this._onDidDisconnect.fire();
super.dispose();
}
@ -253,7 +253,7 @@ suite('Base IPC', function () {
test('call success', async function () {
const r = await ipcService.marco();
return assert.equal(r, 'polo');
return assert.strictEqual(r, 'polo');
});
test('call error', async function () {
@ -261,7 +261,7 @@ suite('Base IPC', function () {
await ipcService.error('nice error');
return assert.fail('should not reach here');
} catch (err) {
return assert.equal(err.message, 'nice error');
return assert.strictEqual(err.message, 'nice error');
}
});
@ -304,20 +304,20 @@ suite('Base IPC', function () {
ipcService.onPong(msg => messages.push(msg));
await timeout(0);
assert.deepEqual(messages, []);
assert.deepStrictEqual(messages, []);
service.ping('hello');
await timeout(0);
assert.deepEqual(messages, ['hello']);
assert.deepStrictEqual(messages, ['hello']);
service.ping('world');
await timeout(0);
assert.deepEqual(messages, ['hello', 'world']);
assert.deepStrictEqual(messages, ['hello', 'world']);
});
test('buffers in arrays', async function () {
const r = await ipcService.buffersLength([VSBuffer.alloc(2), VSBuffer.alloc(3)]);
return assert.equal(r, 5);
return assert.strictEqual(r, 5);
});
});
@ -345,7 +345,7 @@ suite('Base IPC', function () {
test('call success', async function () {
const r = await ipcService.marco();
return assert.equal(r, 'polo');
return assert.strictEqual(r, 'polo');
});
test('call error', async function () {
@ -353,7 +353,7 @@ suite('Base IPC', function () {
await ipcService.error('nice error');
return assert.fail('should not reach here');
} catch (err) {
return assert.equal(err.message, 'nice error');
return assert.strictEqual(err.message, 'nice error');
}
});
@ -363,15 +363,15 @@ suite('Base IPC', function () {
ipcService.onPong(msg => messages.push(msg));
await timeout(0);
assert.deepEqual(messages, []);
assert.deepStrictEqual(messages, []);
service.ping('hello');
await timeout(0);
assert.deepEqual(messages, ['hello']);
assert.deepStrictEqual(messages, ['hello']);
service.ping('world');
await timeout(0);
assert.deepEqual(messages, ['hello', 'world']);
assert.deepStrictEqual(messages, ['hello', 'world']);
});
test('marshalling uri', async function () {
@ -383,7 +383,7 @@ suite('Base IPC', function () {
test('buffers in arrays', async function () {
const r = await ipcService.buffersLength([VSBuffer.alloc(2), VSBuffer.alloc(3)]);
return assert.equal(r, 5);
return assert.strictEqual(r, 5);
});
});
@ -411,7 +411,7 @@ suite('Base IPC', function () {
test('call extra context', async function () {
const r = await ipcService.context();
return assert.equal(r, 'Super Context');
return assert.strictEqual(r, 'Super Context');
});
});
@ -461,7 +461,7 @@ suite('Base IPC', function () {
clientService1.ping('hello 1');
await timeout(1);
assert.deepEqual(pings, ['hello 1']);
assert.deepStrictEqual(pings, ['hello 1']);
const client2 = server.createConnection('client2');
const clientService2 = new TestService();
@ -472,19 +472,19 @@ suite('Base IPC', function () {
clientService2.ping('hello 2');
await timeout(1);
assert.deepEqual(pings, ['hello 1', 'hello 2']);
assert.deepStrictEqual(pings, ['hello 1', 'hello 2']);
client1.dispose();
clientService1.ping('hello 1');
await timeout(1);
assert.deepEqual(pings, ['hello 1', 'hello 2']);
assert.deepStrictEqual(pings, ['hello 1', 'hello 2']);
await timeout(1);
clientService2.ping('hello again 2');
await timeout(1);
assert.deepEqual(pings, ['hello 1', 'hello 2', 'hello again 2']);
assert.deepStrictEqual(pings, ['hello 1', 'hello 2', 'hello again 2']);
client2.dispose();
server.dispose();

View File

@ -22,8 +22,8 @@ suite('IPC, Child Process', () => {
const service = new TestServiceClient(channel);
const result = service.pong('ping').then(r => {
assert.equal(r.incoming, 'ping');
assert.equal(r.outgoing, 'pong');
assert.strictEqual(r.incoming, 'ping');
assert.strictEqual(r.outgoing, 'pong');
});
return result.finally(() => client.dispose());
@ -37,7 +37,7 @@ suite('IPC, Child Process', () => {
const event = new Promise((c, e) => {
service.onMarco(({ answer }) => {
try {
assert.equal(answer, 'polo');
assert.strictEqual(answer, 'polo');
c(undefined);
} catch (err) {
e(err);
@ -60,17 +60,17 @@ suite('IPC, Child Process', () => {
const disposable = service.onMarco(() => count++);
const result = service.marco().then(async answer => {
assert.equal(answer, 'polo');
assert.equal(count, 1);
assert.strictEqual(answer, 'polo');
assert.strictEqual(count, 1);
const answer_1 = await service.marco();
assert.equal(answer_1, 'polo');
assert.equal(count, 2);
assert.strictEqual(answer_1, 'polo');
assert.strictEqual(count, 2);
disposable.dispose();
const answer_2 = await service.marco();
assert.equal(answer_2, 'polo');
assert.equal(count, 2);
assert.strictEqual(answer_2, 'polo');
assert.strictEqual(count, 2);
});
return result.finally(() => client.dispose());

View File

@ -14,6 +14,7 @@
.quick-input-titlebar {
display: flex;
align-items: center;
}
.quick-input-left-action-bar {
@ -22,10 +23,6 @@
flex: 1;
}
.quick-input-left-action-bar.monaco-action-bar .actions-container {
justify-content: flex-start;
}
.quick-input-title {
padding: 3px 0px;
text-align: center;
@ -37,12 +34,14 @@
flex: 1;
}
.quick-input-right-action-bar > .actions-container {
justify-content: flex-end;
}
.quick-input-titlebar .monaco-action-bar .action-label.codicon {
margin: 0;
width: 19px;
height: 100%;
background-position: center;
background-repeat: no-repeat;
padding: 2px;
}
.quick-input-description {
@ -260,10 +259,8 @@
}
.quick-input-list .quick-input-list-entry-action-bar .action-label.codicon {
margin: 0;
height: 100%;
padding: 0 2px;
vertical-align: middle;
margin-right: 4px;
padding: 2px;
}
.quick-input-list .quick-input-list-entry-action-bar {
@ -274,10 +271,6 @@
margin-right: 4px; /* separate from scrollbar */
}
.quick-input-list .quick-input-list-entry-action-bar .action-label.codicon {
margin-right: 4px; /* separate actions */
}
.quick-input-list .quick-input-list-entry .quick-input-list-entry-action-bar .action-label.always-visible,
.quick-input-list .quick-input-list-entry:hover .quick-input-list-entry-action-bar .action-label,
.quick-input-list .monaco-list-row.focused .quick-input-list-entry-action-bar .action-label {

View File

@ -32,6 +32,7 @@ import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { escape } from 'vs/base/common/strings';
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
import { isString } from 'vs/base/common/types';
import { IKeybindingLabelStyles } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
export interface IQuickInputOptions {
idPrefix: string;
@ -57,6 +58,7 @@ export interface IQuickInputStyles {
countBadge: ICountBadgetyles;
button: IButtonStyles;
progressBar: IProgressBarStyles;
keybindingLabel: IKeybindingLabelStyles;
list: IListStyles & { pickerGroupBorder?: Color; pickerGroupForeground?: Color; };
}
@ -410,7 +412,7 @@ class QuickInput extends Disposable implements IQuickInput {
readonly onDispose = this.onDisposeEmitter.event;
dispose(): void {
override dispose(): void {
this.hide();
this.onDisposeEmitter.fire();
@ -694,7 +696,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
}
}
show() {
override show() {
if (!this.visible) {
this.visibleDisposables.add(
this.ui.inputBox.onDidChange(value => {
@ -884,20 +886,11 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
});
}
protected update() {
protected override update() {
if (!this.visible) {
return;
}
let hideInput = false;
let inputShownJustForScreenReader = false;
if (!!this._hideInput && this._items.length > 0) {
if (this.ui.isScreenReaderOptimized()) {
// Always show input if screen reader attached https://github.com/microsoft/vscode/issues/94360
inputShownJustForScreenReader = true;
} else {
hideInput = true;
}
}
const hideInput = !!this._hideInput && this._items.length > 0;
this.ui.container.classList.toggle('hidden-input', hideInput && !this.description);
const visibilities: Visibilities = {
title: !!this.title || !!this.step || !!this.buttons.length,
@ -925,13 +918,9 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
if (this.ui.inputBox.placeholder !== (this.placeholder || '')) {
this.ui.inputBox.placeholder = (this.placeholder || '');
}
if (inputShownJustForScreenReader) {
this.ui.inputBox.ariaLabel = '';
} else {
const ariaLabel = this.ariaLabel || this.placeholder || QuickPick.DEFAULT_ARIA_LABEL;
if (this.ui.inputBox.ariaLabel !== ariaLabel) {
this.ui.inputBox.ariaLabel = ariaLabel;
}
const ariaLabel = this.ariaLabel || this.placeholder || QuickPick.DEFAULT_ARIA_LABEL;
if (this.ui.inputBox.ariaLabel !== ariaLabel) {
this.ui.inputBox.ariaLabel = ariaLabel;
}
this.ui.list.matchOnDescription = this.matchOnDescription;
this.ui.list.matchOnDetail = this.matchOnDetail;
@ -1063,7 +1052,7 @@ class InputBox extends QuickInput implements IInputBox {
readonly onDidAccept = this.onDidAcceptEmitter.event;
show() {
override show() {
if (!this.visible) {
this.visibleDisposables.add(
this.ui.inputBox.onDidChange(value => {
@ -1079,7 +1068,7 @@ class InputBox extends QuickInput implements IInputBox {
super.show();
}
protected update() {
protected override update() {
if (!this.visible) {
return;
}
@ -1388,8 +1377,12 @@ export class QuickInputController extends Disposable {
const index = input.items.indexOf(event.item);
if (index !== -1) {
const items = input.items.slice();
items.splice(index, 1);
const removed = items.splice(index, 1);
const activeItems = input.activeItems.filter((ai) => ai !== removed[0]);
input.items = items;
if (activeItems) {
input.activeItems = activeItems;
}
}
}
})),
@ -1729,6 +1722,34 @@ export class QuickInputController extends Disposable {
if (this.styles.list.pickerGroupForeground) {
content.push(`.quick-input-list .quick-input-list-separator { color: ${this.styles.list.pickerGroupForeground}; }`);
}
if (
this.styles.keybindingLabel.keybindingLabelBackground ||
this.styles.keybindingLabel.keybindingLabelBorder ||
this.styles.keybindingLabel.keybindingLabelBottomBorder ||
this.styles.keybindingLabel.keybindingLabelShadow ||
this.styles.keybindingLabel.keybindingLabelForeground
) {
content.push('.quick-input-list .monaco-keybinding > .monaco-keybinding-key {');
if (this.styles.keybindingLabel.keybindingLabelBackground) {
content.push(`background-color: ${this.styles.keybindingLabel.keybindingLabelBackground};`);
}
if (this.styles.keybindingLabel.keybindingLabelBorder) {
// Order matters here. `border-color` must come before `border-bottom-color`.
content.push(`border-color: ${this.styles.keybindingLabel.keybindingLabelBorder};`);
}
if (this.styles.keybindingLabel.keybindingLabelBottomBorder) {
content.push(`border-bottom-color: ${this.styles.keybindingLabel.keybindingLabelBottomBorder};`);
}
if (this.styles.keybindingLabel.keybindingLabelShadow) {
content.push(`box-shadow: inset 0 -1px 0 ${this.styles.keybindingLabel.keybindingLabelShadow};`);
}
if (this.styles.keybindingLabel.keybindingLabelForeground) {
content.push(`color: ${this.styles.keybindingLabel.keybindingLabelForeground};`);
}
content.push('}');
}
const newStyles = content.join('\n');
if (newStyles !== this.ui.styleSheet.textContent) {
this.ui.styleSheet.textContent = newStyles;

View File

@ -27,6 +27,7 @@ import { IQuickInputOptions } from 'vs/base/parts/quickinput/browser/quickInput'
import { IListOptions, List, IListStyles, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
import { localize } from 'vs/nls';
import { getCodiconAriaLabel } from 'vs/base/common/codicons';
const $ = dom.$;
@ -427,7 +428,7 @@ export class QuickInputList {
const saneDescription = item.description && item.description.replace(/\r?\n/g, ' ');
const saneDetail = item.detail && item.detail.replace(/\r?\n/g, ' ');
const saneAriaLabel = item.ariaLabel || [saneLabel, saneDescription, saneDetail]
.map(s => s && parseLabelWithIcons(s).text)
.map(s => getCodiconAriaLabel(s))
.filter(s => !!s)
.join(', ');
@ -603,6 +604,7 @@ export class QuickInputList {
// Filter by value (since we support icons in labels, use $(..) aware fuzzy matching)
else {
let currentSeparator: IQuickPickSeparator | undefined;
this.elements.forEach(element => {
const labelHighlights = this.matchOnLabel ? withNullAsUndefined(matchesFuzzyIconAware(query, parseLabelWithIcons(element.saneLabel))) : undefined;
const descriptionHighlights = this.matchOnDescription ? withNullAsUndefined(matchesFuzzyIconAware(query, parseLabelWithIcons(element.saneDescription || ''))) : undefined;
@ -621,6 +623,16 @@ export class QuickInputList {
element.hidden = !element.item.alwaysShow;
}
element.separator = undefined;
// we can show the separator unless the list gets sorted by match
if (!this.sortByLabel) {
const previous = element.index && this.inputElements[element.index - 1];
currentSeparator = previous && previous.type === 'separator' ? previous : currentSeparator;
if (currentSeparator && !element.hidden) {
element.separator = currentSeparator;
currentSeparator = undefined;
}
}
});
}

View File

@ -363,7 +363,7 @@ export interface IQuickPickItemButtonContext<T extends IQuickPickItem> extends I
export type QuickPickInput<T = IQuickPickItem> = T | IQuickPickSeparator;
//region Fuzzy Scorer Support
//#region Fuzzy Scorer Support
export type IQuickPickItemWithResource = IQuickPickItem & { resource?: URI };

View File

@ -0,0 +1,52 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IProcessEnvironment } from 'vs/base/common/platform';
import { IProductConfiguration } from 'vs/base/common/product';
// #######################################################################
// ### ###
// ### Types we need in a common layer for reuse ###
// ### ###
// #######################################################################
/**
* The common properties required for any sandboxed
* renderer to function.
*/
export interface ISandboxConfiguration {
/**
* Identifier of the sandboxed renderer.
*/
windowId: number;
/**
* Absolute installation path.
*/
appRoot: string;
/**
* Per window process environment.
*/
userEnv: IProcessEnvironment;
/**
* Product configuration.
*/
product: IProductConfiguration;
/**
* Configured zoom level.
*/
zoomLevel?: number;
/**
* @deprecated to be removed soon
*/
nodeCachedDataDir?: string;
}

View File

@ -9,6 +9,122 @@
const { ipcRenderer, webFrame, crashReporter, contextBridge } = require('electron');
//#region Utilities
/**
* @param {string} channel
* @returns {true | never}
*/
function validateIPC(channel) {
if (!channel || !channel.startsWith('vscode:')) {
throw new Error(`Unsupported event IPC channel '${channel}'`);
}
return true;
}
/**
* @param {string} type
* @returns {type is 'uncaughtException'}
*/
function validateProcessEventType(type) {
if (type !== 'uncaughtException') {
throw new Error(`Unsupported process event '${type}'`);
}
return true;
}
/**
* @param {string} key the name of the process argument to parse
* @returns {string | undefined}
*/
function parseArgv(key) {
for (const arg of process.argv) {
if (arg.indexOf(`--${key}=`) === 0) {
return arg.split('=')[1];
}
}
return undefined;
}
//#endregion
//#region Resolve Configuration
/**
* @typedef {import('../common/sandboxTypes').ISandboxConfiguration} ISandboxConfiguration
*/
/** @type {ISandboxConfiguration | undefined} */
let configuration = undefined;
/** @type {Promise<ISandboxConfiguration>} */
const resolveConfiguration = (async () => {
const windowConfigIpcChannel = parseArgv('vscode-window-config');
if (!windowConfigIpcChannel) {
throw new Error('Preload: did not find expected vscode-window-config in renderer process arguments list.');
}
try {
if (validateIPC(windowConfigIpcChannel)) {
// Resolve configuration from electron-main
configuration = await ipcRenderer.invoke(windowConfigIpcChannel);
// Apply `userEnv` directly
Object.assign(process.env, configuration.userEnv);
// Apply zoom level early before even building the
// window DOM elements to avoid UI flicker. We always
// have to set the zoom level from within the window
// because Chrome has it's own way of remembering zoom
// settings per origin (if vscode-file:// is used) and
// we want to ensure that the user configuration wins.
webFrame.setZoomLevel(configuration.zoomLevel ?? 0);
return configuration;
}
} catch (error) {
throw new Error(`Preload: unable to fetch vscode-window-config: ${error}`);
}
})();
//#endregion
//#region Resolve Shell Environment
/**
* If VSCode is not run from a terminal, we should resolve additional
* shell specific environment from the OS shell to ensure we are seeing
* all development related environment variables. We do this from the
* main process because it may involve spawning a shell.
*
* @type {Promise<typeof process.env>}
*/
const resolveShellEnv = (async () => {
// Resolve `userEnv` from configuration and
// `shellEnv` from the main side
const [userEnv, shellEnv] = await Promise.all([
(async () => (await resolveConfiguration).userEnv)(),
ipcRenderer.invoke('vscode:fetchShellEnv')
]);
if (!process.env['VSCODE_SKIP_PROCESS_ENV_PATCHING'] /* TODO@bpasero for https://github.com/microsoft/vscode/issues/108804 */) {
// Assign all keys of the shell environment to our process environment
// But make sure that the user environment wins in the end over shell environment
Object.assign(process.env, shellEnv, userEnv);
}
return { ...process.env, ...shellEnv, ...userEnv };
})();
//#endregion
//#region Globals Definition
// #######################################################################
// ### ###
// ### !!! DO NOT USE GET/SET PROPERTIES ANYWHERE HERE !!! ###
@ -17,14 +133,21 @@
// ### ###
// #######################################################################
/**
* @type {import('../electron-sandbox/globals')}
*/
const globals = {
/**
* A minimal set of methods exposed from Electron's `ipcRenderer`
* to support communication to main process.
*
* @type {import('../electron-sandbox/electronTypes').IpcRenderer}
* @typedef {import('../electron-sandbox/electronTypes').IpcRenderer} IpcRenderer
* @typedef {import('electron').IpcRendererEvent} IpcRendererEvent
*
* @type {IpcRenderer}
*/
ipcRenderer: {
/**
@ -50,8 +173,8 @@
/**
* @param {string} channel
* @param {(event: import('electron').IpcRendererEvent, ...args: any[]) => void} listener
* @returns {import('../electron-sandbox/electronTypes').IpcRenderer}
* @param {(event: IpcRendererEvent, ...args: any[]) => void} listener
* @returns {IpcRenderer}
*/
on(channel, listener) {
if (validateIPC(channel)) {
@ -63,8 +186,8 @@
/**
* @param {string} channel
* @param {(event: import('electron').IpcRendererEvent, ...args: any[]) => void} listener
* @returns {import('../electron-sandbox/electronTypes').IpcRenderer}
* @param {(event: IpcRendererEvent, ...args: any[]) => void} listener
* @returns {IpcRenderer}
*/
once(channel, listener) {
if (validateIPC(channel)) {
@ -76,8 +199,8 @@
/**
* @param {string} channel
* @param {(event: import('electron').IpcRendererEvent, ...args: any[]) => void} listener
* @returns {import('../electron-sandbox/electronTypes').IpcRenderer}
* @param {(event: IpcRendererEvent, ...args: any[]) => void} listener
* @returns {IpcRenderer}
*/
removeListener(channel, listener) {
if (validateIPC(channel)) {
@ -100,7 +223,7 @@
*/
connect(channelRequest, channelResponse, requestNonce) {
if (validateIPC(channelRequest) && validateIPC(channelResponse)) {
const responseListener = (/** @type {import('electron').IpcRendererEvent} */ e, /** @type {string} */ responseNonce) => {
const responseListener = (/** @type {IpcRendererEvent} */ e, /** @type {string} */ responseNonce) => {
// validate that the nonce from the response is the same
// as when requested. and if so, use `postMessage` to
// send the `MessagePort` safely over, even when context
@ -157,7 +280,9 @@
* Note: when `sandbox` is enabled, the only properties available
* are https://github.com/electron/electron/blob/master/docs/api/process.md#sandbox
*
* @type {import('../electron-sandbox/globals').ISandboxNodeProcess}
* @typedef {import('../electron-sandbox/globals').ISandboxNodeProcess} ISandboxNodeProcess
*
* @type {ISandboxNodeProcess}
*/
process: {
get platform() { return process.platform; },
@ -178,16 +303,8 @@
/**
* @returns {Promise<typeof process.env>}
*/
getShellEnv() {
return shellEnv;
},
/**
* @param {{[key: string]: string}} userEnv
* @returns {Promise<void>}
*/
resolveEnv(userEnv) {
return resolveEnv(userEnv);
shellEnv() {
return resolveShellEnv;
},
/**
@ -200,7 +317,7 @@
/**
* @param {string} type
* @param {Function} callback
* @returns {import('../electron-sandbox/globals').ISandboxNodeProcess}
* @returns {ISandboxNodeProcess}
*/
on(type, callback) {
if (validateProcessEventType(type)) {
@ -210,6 +327,37 @@
return this;
}
}
},
/**
* Some information about the context we are running in.
*
* @type {import('../electron-sandbox/globals').ISandboxContext}
*/
context: {
/**
* A configuration object made accessible from the main side
* to configure the sandbox browser window.
*
* Note: intentionally not using a getter here because the
* actual value will be set after `resolveConfiguration`
* has finished.
*
* @returns {ISandboxConfiguration | undefined}
*/
configuration() {
return configuration;
},
/**
* Allows to await the resolution of the configuration object.
*
* @returns {Promise<ISandboxConfiguration>}
*/
async resolveConfiguration() {
return resolveConfiguration;
}
}
};
@ -231,69 +379,4 @@
// @ts-ignore
window.vscode = globals;
}
//#region Utilities
/**
* @param {string} channel
* @returns {true | never}
*/
function validateIPC(channel) {
if (!channel || !channel.startsWith('vscode:')) {
throw new Error(`Unsupported event IPC channel '${channel}'`);
}
return true;
}
/**
* @param {string} type
* @returns {type is 'uncaughtException'}
*/
function validateProcessEventType(type) {
if (type !== 'uncaughtException') {
throw new Error(`Unsupported process event '${type}'`);
}
return true;
}
/** @type {Promise<typeof process.env> | undefined} */
let shellEnv = undefined;
/**
* If VSCode is not run from a terminal, we should resolve additional
* shell specific environment from the OS shell to ensure we are seeing
* all development related environment variables. We do this from the
* main process because it may involve spawning a shell.
*
* @param {{[key: string]: string}} userEnv
* @returns {Promise<void>}
*/
async function resolveEnv(userEnv) {
if (!shellEnv) {
// Apply `userEnv` directly
Object.assign(process.env, userEnv);
// Resolve `shellEnv` from the main side
shellEnv = new Promise(function (resolve) {
ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnvResult) {
if (!process.env['VSCODE_SKIP_PROCESS_ENV_PATCHING'] /* TODO@bpasero for https://github.com/microsoft/vscode/issues/108804 */) {
// Assign all keys of the shell environment to our process environment
// But make sure that the user environment wins in the end over shell environment
Object.assign(process.env, shellEnvResult, userEnv);
}
resolve({ ...process.env, ...shellEnvResult, ...userEnv });
});
ipcRenderer.send('vscode:fetchShellEnv');
});
}
await shellEnv;
}
//#endregion
}());

View File

@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { globals, INodeProcess, IProcessEnvironment } from 'vs/base/common/platform';
import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes';
import { ProcessMemoryInfo, CrashReporter, IpcRenderer, WebFrame } from 'vs/base/parts/sandbox/electron-sandbox/electronTypes';
/**
@ -74,8 +75,8 @@ export interface ISandboxNodeProcess extends INodeProcess {
getProcessMemoryInfo: () => Promise<ProcessMemoryInfo>;
/**
* A custom method we add to `process`: Resolve the true process environment to use and
* apply it to `process.env`.
* Returns a process environment that includes all shell environment variables even if
* the application was not started from a shell / terminal / console.
*
* There are different layers of environment that will apply:
* - `process.env`: this is the actual environment of the process before this method
@ -86,17 +87,8 @@ export interface ISandboxNodeProcess extends INodeProcess {
* from a terminal and changed certain variables
*
* The order of overwrites is `process.env` < `shellEnv` < `userEnv`.
*
* It is critical that every process awaits this method early on startup to get the right
* set of environment in `process.env`.
*/
resolveEnv(userEnv: IProcessEnvironment): Promise<void>;
/**
* Returns a process environment that includes any shell environment even if the application
* was not started from a shell / terminal / console.
*/
getShellEnv(): Promise<IProcessEnvironment>;
shellEnv(): Promise<IProcessEnvironment>;
}
export interface IpcMessagePort {
@ -114,8 +106,24 @@ export interface IpcMessagePort {
connect(channelRequest: string, channelResponse: string, requestNonce: string): void;
}
export interface ISandboxContext {
/**
* A configuration object made accessible from the main side
* to configure the sandbox browser window. Will be `undefined`
* for as long as `resolveConfiguration` is not awaited.
*/
configuration(): ISandboxConfiguration | undefined;
/**
* Allows to await the resolution of the configuration object.
*/
resolveConfiguration(): Promise<ISandboxConfiguration>;
}
export const ipcRenderer: IpcRenderer = globals.vscode.ipcRenderer;
export const ipcMessagePort: IpcMessagePort = globals.vscode.ipcMessagePort;
export const webFrame: WebFrame = globals.vscode.webFrame;
export const crashReporter: CrashReporter = globals.vscode.crashReporter;
export const process: ISandboxNodeProcess = globals.vscode.process;
export const context: ISandboxContext = globals.vscode.context;

View File

@ -4,13 +4,17 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { ipcRenderer, crashReporter, webFrame, process } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { ipcRenderer, crashReporter, webFrame, context, process } from 'vs/base/parts/sandbox/electron-sandbox/globals';
suite('Sandbox', () => {
test('globals', () => {
test('globals', async () => {
assert.ok(typeof ipcRenderer.send === 'function');
assert.ok(typeof crashReporter.addExtraParameter === 'function');
assert.ok(typeof webFrame.setZoomLevel === 'function');
assert.ok(typeof process.platform === 'string');
const config = await context.resolveConfiguration();
assert.ok(config);
assert.ok(context.configuration());
});
});

View File

@ -320,7 +320,7 @@ export class Storage extends Disposable implements IStorage {
return new Promise(resolve => this.whenFlushedCallbacks.push(resolve));
}
dispose(): void {
override dispose(): void {
this.flushDelayer.cancel(); // workaround https://github.com/microsoft/vscode/issues/116777
this.flushDelayer.dispose();

View File

@ -111,7 +111,7 @@ flakySuite('Storage Library', function () {
class TestSQLiteStorageDatabase extends SQLiteStorageDatabase {
private readonly _onDidChangeItemsExternal = new Emitter<IStorageItemsChangeEvent>();
get onDidChangeItemsExternal(): Event<IStorageItemsChangeEvent> { return this._onDidChangeItemsExternal.event; }
override get onDidChangeItemsExternal(): Event<IStorageItemsChangeEvent> { return this._onDidChangeItemsExternal.event; }
fireDidChangeItemsExternal(event: IStorageItemsChangeEvent): void {
this._onDidChangeItemsExternal.fire(event);

View File

@ -59,8 +59,8 @@ suite('Comparers', () => {
// name-only comparisons
assert(compareFileNames('a', 'A') !== compareLocale('a', 'A'), 'the same letter does not sort by locale');
assert(compareFileNames('â', 'Â') !== compareLocale('â', 'Â'), 'the same accented letter does not sort by locale');
assert.notDeepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileNames), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases do not sort in locale order');
assert.notDeepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileNames), ['email', 'Email', 'émail', 'Émail'].sort(compareLocale), 'the same base characters with different case or accents do not sort in locale order');
assert.notDeepStrictEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileNames), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases do not sort in locale order');
assert.notDeepStrictEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileNames), ['email', 'Email', 'émail', 'Émail'].sort(compareLocale), 'the same base characters with different case or accents do not sort in locale order');
// numeric comparisons
assert(compareFileNames('abc02.txt', 'abc002.txt') > 0, 'filenames with equivalent numbers and leading zeros sort in unicode order');

View File

@ -44,8 +44,14 @@ suite('FormattedTextRenderer', () => {
result = renderFormattedText('__italics__');
assert.strictEqual(result.innerHTML, '<i>italics</i>');
result = renderFormattedText('this string has **bold** and __italics__');
assert.strictEqual(result.innerHTML, 'this string has <b>bold</b> and <i>italics</i>');
result = renderFormattedText('``code``');
assert.strictEqual(result.innerHTML, '``code``');
result = renderFormattedText('``code``', { renderCodeSegements: true });
assert.strictEqual(result.innerHTML, '<code>code</code>');
result = renderFormattedText('this string has **bold**, __italics__, and ``code``!!', { renderCodeSegements: true });
assert.strictEqual(result.innerHTML, 'this string has <b>bold</b>, <i>italics</i>, and <code>code</code>!!');
});
test('no formatting', () => {
@ -96,6 +102,26 @@ suite('FormattedTextRenderer', () => {
assert.strictEqual(callbackCalled, true);
});
test('fancier action', () => {
let callbackCalled = false;
let result: HTMLElement = renderFormattedText('``__**[[action]]**__``', {
renderCodeSegements: true,
actionHandler: {
callback(content) {
assert.strictEqual(content, '0');
callbackCalled = true;
},
disposeables: store
}
});
assert.strictEqual(result.innerHTML, '<code><i><b><a href="#">action</a></b></i></code>');
let event: MouseEvent = <any>document.createEvent('MouseEvent');
event.initEvent('click', true, true);
result.firstChild!.firstChild!.firstChild!.firstChild!.dispatchEvent(event);
assert.strictEqual(callbackCalled, true);
});
test('escaped formatting', () => {
let result: HTMLElement = renderFormattedText('\\*\\*bold\\*\\*');
assert.strictEqual(result.children.length, 0);

Some files were not shown because too many files have changed in this diff Show More