Merge branch 'main' into lramos15/modelEvent

This commit is contained in:
Logan Ramos 2021-11-24 14:25:55 -05:00
commit 6ce7f83df2
No known key found for this signature in database
GPG key ID: D9CCFF14F0B18183
102 changed files with 1727 additions and 939 deletions

View file

@ -72,6 +72,9 @@ jobs:
- name: Run Unit Tests (Electron)
run: .\scripts\test.bat
- name: Run Unit Tests (node.js)
run: yarn test-node
- name: Run Unit Tests (Browser)
run: yarn test-browser --browser chromium
@ -147,6 +150,10 @@ jobs:
id: electron-unit-tests
run: DISPLAY=:10 ./scripts/test.sh
- name: Run Unit Tests (node.js)
id: nodejs-unit-tests
run: yarn test-node
- name: Run Unit Tests (Browser)
id: browser-unit-tests
run: DISPLAY=:10 yarn test-browser --browser chromium
@ -222,6 +229,9 @@ jobs:
- name: Run Unit Tests (Electron)
run: DISPLAY=:10 ./scripts/test.sh
- name: Run Unit Tests (node.js)
run: yarn test-node
- name: Run Unit Tests (Browser)
run: DISPLAY=:10 yarn test-browser --browser chromium

View file

@ -1 +1 @@
2021-11-19T08:23:35Z
2021-11-24T12:04:58.681Z

View file

@ -48,23 +48,23 @@ steps:
git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro")
displayName: Merge distro
# - script: |
# mkdir -p .build
# node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash
# displayName: Prepare yarn cache flags
- script: |
mkdir -p .build
node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash
displayName: Prepare yarn cache flags
# - task: Cache@2
# inputs:
# key: "nodeModules | $(Agent.OS) | .build/yarnlockhash"
# path: .build/node_modules_cache
# cacheHitVar: NODE_MODULES_RESTORED
# displayName: Restore node_modules cache
- task: Cache@2
inputs:
key: "nodeModules | $(Agent.OS) | .build/yarnlockhash"
path: .build/node_modules_cache
cacheHitVar: NODE_MODULES_RESTORED
displayName: Restore node_modules cache
# - script: |
# set -e
# tar -xzf .build/node_modules_cache/cache.tgz
# condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
# displayName: Extract node_modules cache
- script: |
set -e
tar -xzf .build/node_modules_cache/cache.tgz
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Extract node_modules cache
- script: |
set -e
@ -101,13 +101,13 @@ steps:
displayName: Install dependencies
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
# - script: |
# set -e
# node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
# mkdir -p .build/node_modules_cache
# tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt
# condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
# displayName: Create node_modules archive
- script: |
set -e
node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
mkdir -p .build/node_modules_cache
tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Create node_modules archive
# This script brings in the right resources (images, icons, etc) based on the quality (insiders, stable, exploration)
- script: |
@ -179,6 +179,13 @@ steps:
timeoutInMinutes: 7
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
yarn test-node --build
displayName: Run unit tests (node.js)
timeoutInMinutes: 7
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
yarn test-browser --build --browser chromium --browser webkit --browser firefox --tfs "Browser Unit Tests"

View file

@ -47,23 +47,23 @@ steps:
git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro")
displayName: Merge distro
# - script: |
# mkdir -p .build
# node build/azure-pipelines/common/computeNodeModulesCacheKey.js "alpine" $ENABLE_TERRAPIN > .build/yarnlockhash
# displayName: Prepare yarn cache flags
- script: |
mkdir -p .build
node build/azure-pipelines/common/computeNodeModulesCacheKey.js "alpine" $ENABLE_TERRAPIN > .build/yarnlockhash
displayName: Prepare yarn cache flags
# - task: Cache@2
# inputs:
# key: "nodeModules | $(Agent.OS) | .build/yarnlockhash"
# path: .build/node_modules_cache
# cacheHitVar: NODE_MODULES_RESTORED
# displayName: Restore node_modules cache
- task: Cache@2
inputs:
key: "nodeModules | $(Agent.OS) | .build/yarnlockhash"
path: .build/node_modules_cache
cacheHitVar: NODE_MODULES_RESTORED
displayName: Restore node_modules cache
# - script: |
# set -e
# tar -xzf .build/node_modules_cache/cache.tgz
# condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
# displayName: Extract node_modules cache
- script: |
set -e
tar -xzf .build/node_modules_cache/cache.tgz
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Extract node_modules cache
- script: |
set -e
@ -89,13 +89,13 @@ steps:
displayName: Install dependencies
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
# - script: |
# set -e
# node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
# mkdir -p .build/node_modules_cache
# tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt
# condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
# displayName: Create node_modules archive
- script: |
set -e
node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
mkdir -p .build/node_modules_cache
tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Create node_modules archive
- script: |
set -e

View file

@ -38,23 +38,23 @@ steps:
git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro")
displayName: Merge distro
# - script: |
# mkdir -p .build
# node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash
# displayName: Prepare yarn cache flags
- script: |
mkdir -p .build
node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash
displayName: Prepare yarn cache flags
# - task: Cache@2
# inputs:
# key: "nodeModules | $(Agent.OS) | .build/yarnlockhash"
# path: .build/node_modules_cache
# cacheHitVar: NODE_MODULES_RESTORED
# displayName: Restore node_modules cache
- task: Cache@2
inputs:
key: "nodeModules | $(Agent.OS) | .build/yarnlockhash"
path: .build/node_modules_cache
cacheHitVar: NODE_MODULES_RESTORED
displayName: Restore node_modules cache
# - script: |
# set -e
# tar -xzf .build/node_modules_cache/cache.tgz
# condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
# displayName: Extract node_modules cache
- script: |
set -e
tar -xzf .build/node_modules_cache/cache.tgz
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Extract node_modules cache
- script: |
set -e
@ -110,13 +110,13 @@ steps:
displayName: Install dependencies
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
# - script: |
# set -e
# node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
# mkdir -p .build/node_modules_cache
# tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt
# condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
# displayName: Create node_modules archive
- script: |
set -e
node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
mkdir -p .build/node_modules_cache
tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Create node_modules archive
- script: |
set -e
@ -162,6 +162,13 @@ steps:
timeoutInMinutes: 7
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
yarn test-node --build
displayName: Run unit tests (node.js)
timeoutInMinutes: 7
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
yarn test-browser --build --browser chromium --tfs "Browser Unit Tests"

View file

@ -27,24 +27,24 @@ steps:
git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro")
displayName: Merge distro
# - script: |
# mkdir -p .build
# node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash
# displayName: Prepare yarn cache flags
- script: |
mkdir -p .build
node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash
displayName: Prepare yarn cache flags
# # using `genericNodeModules` instead of `nodeModules` here to avoid sharing the cache with builds running inside containers
# - task: Cache@2
# inputs:
# key: "genericNodeModules | $(Agent.OS) | .build/yarnlockhash"
# path: .build/node_modules_cache
# cacheHitVar: NODE_MODULES_RESTORED
# displayName: Restore node_modules cache
# using `genericNodeModules` instead of `nodeModules` here to avoid sharing the cache with builds running inside containers
- task: Cache@2
inputs:
key: "genericNodeModules | $(Agent.OS) | .build/yarnlockhash"
path: .build/node_modules_cache
cacheHitVar: NODE_MODULES_RESTORED
displayName: Restore node_modules cache
# - script: |
# set -e
# tar -xzf .build/node_modules_cache/cache.tgz
# condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
# displayName: Extract node_modules cache
- script: |
set -e
tar -xzf .build/node_modules_cache/cache.tgz
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Extract node_modules cache
- script: |
set -e
@ -77,13 +77,13 @@ steps:
displayName: Install dependencies
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
# - script: |
# set -e
# node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
# mkdir -p .build/node_modules_cache
# tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt
# condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
# displayName: Create node_modules archive
- script: |
set -e
node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
mkdir -p .build/node_modules_cache
tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Create node_modules archive
# Mixin must run before optimize, because the CSS loader will inline small SVGs
- script: |

View file

@ -38,23 +38,23 @@ steps:
git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro")
displayName: Merge distro
# - script: |
# mkdir -p .build
# node build/azure-pipelines/common/computeNodeModulesCacheKey.js "web" $ENABLE_TERRAPIN > .build/yarnlockhash
# displayName: Prepare yarn cache flags
- script: |
mkdir -p .build
node build/azure-pipelines/common/computeNodeModulesCacheKey.js "web" $ENABLE_TERRAPIN > .build/yarnlockhash
displayName: Prepare yarn cache flags
# - task: Cache@2
# inputs:
# key: "nodeModules | $(Agent.OS) | .build/yarnlockhash"
# path: .build/node_modules_cache
# cacheHitVar: NODE_MODULES_RESTORED
# displayName: Restore node_modules cache
- task: Cache@2
inputs:
key: "nodeModules | $(Agent.OS) | .build/yarnlockhash"
path: .build/node_modules_cache
cacheHitVar: NODE_MODULES_RESTORED
displayName: Restore node_modules cache
# - script: |
# set -e
# tar -xzf .build/node_modules_cache/cache.tgz
# condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
# displayName: Extract node_modules cache
- script: |
set -e
tar -xzf .build/node_modules_cache/cache.tgz
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Extract node_modules cache
- script: |
set -e
@ -80,13 +80,13 @@ steps:
displayName: Install dependencies
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
# - script: |
# set -e
# node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
# mkdir -p .build/node_modules_cache
# tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt
# condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
# displayName: Create node_modules archive
- script: |
set -e
node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
mkdir -p .build/node_modules_cache
tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Create node_modules archive
- script: |
set -e

View file

@ -42,25 +42,25 @@ steps:
exec { git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") }
displayName: Merge distro
# - powershell: |
# "$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch
# "$env:ENABLE_TERRAPIN" | Out-File -Encoding ascii -NoNewLine .build\terrapin
# node build/azure-pipelines/common/computeNodeModulesCacheKey.js > .build/yarnlockhash
# displayName: Prepare yarn cache flags
- powershell: |
"$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch
"$env:ENABLE_TERRAPIN" | Out-File -Encoding ascii -NoNewLine .build\terrapin
node build/azure-pipelines/common/computeNodeModulesCacheKey.js > .build/yarnlockhash
displayName: Prepare yarn cache flags
# - task: Cache@2
# inputs:
# key: "nodeModules | $(Agent.OS) | .build/arch, .build/terrapin, .build/yarnlockhash"
# path: .build/node_modules_cache
# cacheHitVar: NODE_MODULES_RESTORED
# displayName: Restore node_modules cache
- task: Cache@2
inputs:
key: "nodeModules | $(Agent.OS) | .build/arch, .build/terrapin, .build/yarnlockhash"
path: .build/node_modules_cache
cacheHitVar: NODE_MODULES_RESTORED
displayName: Restore node_modules cache
# - powershell: |
# . build/azure-pipelines/win32/exec.ps1
# $ErrorActionPreference = "Stop"
# exec { 7z.exe x .build/node_modules_cache/cache.7z -aos }
# condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
# displayName: Extract node_modules cache
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { 7z.exe x .build/node_modules_cache/cache.7z -aos }
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Extract node_modules cache
- powershell: |
. build/azure-pipelines/win32/exec.ps1
@ -84,14 +84,14 @@ steps:
displayName: Install dependencies
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
# - powershell: |
# . build/azure-pipelines/win32/exec.ps1
# $ErrorActionPreference = "Stop"
# exec { node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt }
# exec { mkdir -Force .build/node_modules_cache }
# exec { 7z.exe a .build/node_modules_cache/cache.7z -mx3 `@.build/node_modules_list.txt }
# condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
# displayName: Create node_modules archive
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt }
exec { mkdir -Force .build/node_modules_cache }
exec { 7z.exe a .build/node_modules_cache/cache.7z -mx3 `@.build/node_modules_list.txt }
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Create node_modules archive
- powershell: |
. build/azure-pipelines/win32/exec.ps1
@ -150,6 +150,14 @@ steps:
timeoutInMinutes: 7
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { yarn test-node --build }
displayName: Run unit tests (node.js)
timeoutInMinutes: 7
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"

View file

@ -209,6 +209,7 @@ exports.watchExtensionMedia = watchExtensionMedia;
const compileExtensionMediaBuildTask = task.define('compile-extension-media-build', () => ext.buildExtensionMedia(false, '.build/extensions'));
gulp.task(compileExtensionMediaBuildTask);
exports.compileExtensionMediaBuildTask = compileExtensionMediaBuildTask;
//#endregion

View file

@ -25,7 +25,7 @@ const File = require('vinyl');
const fs = require('fs');
const glob = require('glob');
const { compileBuildTask } = require('./gulpfile.compile');
const { compileExtensionsBuildTask } = require('./gulpfile.extensions');
const { compileExtensionsBuildTask, compileExtensionMediaBuildTask } = require('./gulpfile.extensions');
const { vscodeWebEntryPoints, vscodeWebResourceIncludes, createVSCodeWebFileContentMapper } = require('./gulpfile.vscode.web');
const cp = require('child_process');
@ -381,6 +381,7 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa
const serverTask = task.define(`vscode-${type}${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series(
compileBuildTask,
compileExtensionsBuildTask,
compileExtensionMediaBuildTask,
minified ? minifyTask : optimizeTask,
serverTaskCI
));

View file

@ -31,7 +31,7 @@ const { config } = require('./lib/electron');
const createAsar = require('./lib/asar').createAsar;
const minimist = require('minimist');
const { compileBuildTask } = require('./gulpfile.compile');
const { compileExtensionsBuildTask } = require('./gulpfile.extensions');
const { compileExtensionsBuildTask, compileExtensionMediaBuildTask } = require('./gulpfile.extensions');
const { getSettingsSearchBuildId, shouldSetupSettingsSearch } = require('./azure-pipelines/upload-configuration');
// Build
@ -379,6 +379,7 @@ BUILD_TARGETS.forEach(buildTarget => {
const vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series(
compileBuildTask,
compileExtensionsBuildTask,
compileExtensionMediaBuildTask,
minified ? minifyVSCodeTask : optimizeVSCodeTask,
vscodeTaskCI
));

View file

@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.63.0",
"distro": "f5a2c7f01629f6d3c0dc65e3fbb3818033206bf2",
"distro": "1aa3ab55b3cceca22ca6d647dc0095d562d23c8d",
"author": {
"name": "Microsoft Corporation"
},
@ -11,6 +11,7 @@
"scripts": {
"test": "mocha",
"test-browser": "node test/unit/browser/index.js",
"test-node": "mocha test/unit/node/index.js --delay --ui=tdd --exit",
"preinstall": "node build/npm/preinstall.js",
"postinstall": "node build/npm/postinstall.js",
"compile": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js compile",
@ -27,7 +28,6 @@
"watch-extensions": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js watch-extensions watch-extension-media",
"watch-extensionsd": "deemon yarn watch-extensions",
"kill-watch-extensionsd": "deemon --kill yarn watch-extensions",
"mocha": "mocha test/unit/node/all.js --delay --ui=tdd",
"precommit": "node build/hygiene.js",
"gulp": "node --max_old_space_size=8192 ./node_modules/gulp/bin/gulp.js",
"electron": "node build/lib/electron",
@ -169,7 +169,6 @@
"istanbul-lib-report": "^3.0.0",
"istanbul-lib-source-maps": "^4.0.0",
"istanbul-reports": "^3.0.0",
"jsdom-no-contextify": "^3.1.0",
"lazy.js": "^0.4.2",
"merge-options": "^1.0.1",
"mime": "^1.4.1",

View file

@ -7,6 +7,8 @@
"licenseName": "MIT",
"licenseUrl": "https://github.com/microsoft/vscode/blob/main/LICENSE.txt",
"serverGreeting": [],
"serverLicense": [],
"serverLicensePrompt": "",
"win32DirName": "Microsoft Code OSS",
"win32NameVersion": "Microsoft Code OSS",
"win32RegValueName": "CodeOSS",

View file

@ -939,9 +939,15 @@ class FocusTracker extends Disposable implements IFocusTracker {
private _refreshStateHandler: () => void;
private static hasFocusWithin(element: HTMLElement): boolean {
const shadowRoot = getShadowRoot(element);
const activeElement = (shadowRoot ? shadowRoot.activeElement : document.activeElement);
return isAncestor(activeElement, element);
}
constructor(element: HTMLElement | Window) {
super();
let hasFocus = isAncestor(document.activeElement, <HTMLElement>element);
let hasFocus = FocusTracker.hasFocusWithin(<HTMLElement>element);
let loosingFocus = false;
const onFocus = () => {
@ -966,7 +972,7 @@ class FocusTracker extends Disposable implements IFocusTracker {
};
this._refreshStateHandler = () => {
let currentNodeHasFocus = isAncestor(document.activeElement, <HTMLElement>element);
let currentNodeHasFocus = FocusTracker.hasFocusWithin(<HTMLElement>element);
if (currentNodeHasFocus !== hasFocus) {
if (hasFocus) {
onBlur();

View file

@ -178,10 +178,7 @@ export class ActionWithDropdownActionViewItem extends ActionViewItem {
const menuActionsProvider = {
getActions: () => {
const actionsProvider = (<IActionWithDropdownActionViewItemOptions>this.options).menuActionsOrProvider;
return [this._action, ...(Array.isArray(actionsProvider)
? actionsProvider
: (actionsProvider as IActionProvider).getActions()) // TODO: microsoft/TypeScript#42768
];
return Array.isArray(actionsProvider) ? actionsProvider : (actionsProvider as IActionProvider).getActions(); // TODO: microsoft/TypeScript#42768
}
};
this.dropdownMenuActionViewItem = new DropdownMenuActionViewItem(this._register(new Action('dropdownAction', undefined)), menuActionsProvider, this.contextMenuProvider, { classNames: ['dropdown', ...Codicon.dropDownButton.classNamesArray, ...(<IActionWithDropdownActionViewItemOptions>this.options).menuActionClassNames || []] });

View file

@ -1337,3 +1337,281 @@ export namespace Promises {
}
//#endregion
//#region
const enum AsyncIterableSourceState {
Initial,
DoneOK,
DoneError,
}
/**
* An object that allows to emit async values asynchronously or bring the iterable to an error state using `reject()`.
* This emitter is valid only for the duration of the executor (until the promise returned by the executor settles).
*/
export interface AsyncIterableEmitter<T> {
/**
* The value will be appended at the end.
*
* **NOTE** If `reject()` has already been called, this method has no effect.
*/
emitOne(value: T): void;
/**
* The values will be appended at the end.
*
* **NOTE** If `reject()` has already been called, this method has no effect.
*/
emitMany(values: T[]): void;
/**
* Writing an error will permanently invalidate this iterable.
* The current users will receive an error thrown, as will all future users.
*
* **NOTE** If `reject()` have already been called, this method has no effect.
*/
reject(error: Error): void;
}
/**
* An executor for the `AsyncIterableObject` that has access to an emitter.
*/
export interface AyncIterableExecutor<T> {
/**
* @param emitter An object that allows to emit async values valid only for the duration of the executor.
*/
(emitter: AsyncIterableEmitter<T>): void | Promise<void>
}
/**
* A rich implementation for an `AsyncIterable<T>`.
*/
export class AsyncIterableObject<T> implements AsyncIterable<T> {
public static fromArray<T>(items: T[]): AsyncIterableObject<T> {
return new AsyncIterableObject<T>((writer) => {
writer.emitMany(items);
});
}
public static fromPromise<T>(promise: Promise<T[]>): AsyncIterableObject<T> {
return new AsyncIterableObject<T>(async (emitter) => {
emitter.emitMany(await promise);
});
}
public static fromPromises<T>(promises: Promise<T>[]): AsyncIterableObject<T> {
return new AsyncIterableObject<T>(async (emitter) => {
await Promise.all(promises.map(async (p) => emitter.emitOne(await p)));
});
}
public static merge<T>(iterables: AsyncIterable<T>[]): AsyncIterableObject<T> {
return new AsyncIterableObject(async (emitter) => {
await Promise.all(iterables.map(async (iterable) => {
for await(const item of iterable) {
emitter.emitOne(item);
}
}));
});
}
public static EMPTY = AsyncIterableObject.fromArray<any>([]);
private _state: AsyncIterableSourceState;
private _results: T[];
private _error: Error | null;
private readonly _onStateChanged: Emitter<void>;
constructor(executor: AyncIterableExecutor<T>) {
this._state = AsyncIterableSourceState.Initial;
this._results = [];
this._error = null;
this._onStateChanged = new Emitter<void>();
queueMicrotask(async () => {
const writer: AsyncIterableEmitter<T> = {
emitOne: (item) => this.emitOne(item),
emitMany: (items) => this.emitMany(items),
reject: (error) => this.reject(error)
};
try {
await Promise.resolve(executor(writer));
this.resolve();
} catch (err) {
this.reject(err);
} finally {
writer.emitOne = undefined!;
writer.emitMany = undefined!;
writer.reject = undefined!;
}
});
}
[Symbol.asyncIterator](): AsyncIterator<T, undefined, undefined> {
let i = 0;
return {
next: async () => {
do {
if (this._state === AsyncIterableSourceState.DoneError) {
throw this._error;
}
if (i < this._results.length) {
return { done: false, value: this._results[i++] };
}
if (this._state === AsyncIterableSourceState.DoneOK) {
return { done: true, value: undefined };
}
await Event.toPromise(this._onStateChanged.event);
} while (true);
}
};
}
public static map<T, R>(iterable: AsyncIterable<T>, mapFn: (item: T) => R): AsyncIterableObject<R> {
return new AsyncIterableObject<R>(async (emitter) => {
for await(const item of iterable) {
emitter.emitOne(mapFn(item));
}
});
}
public map<R>(mapFn: (item: T) => R): AsyncIterableObject<R> {
return AsyncIterableObject.map(this, mapFn);
}
public static filter<T>(iterable: AsyncIterable<T>, filterFn: (item: T) => boolean): AsyncIterableObject<T> {
return new AsyncIterableObject<T>(async (emitter) => {
for await(const item of iterable) {
if (filterFn(item)) {
emitter.emitOne(item);
}
}
});
}
public filter(filterFn: (item: T) => boolean): AsyncIterableObject<T> {
return AsyncIterableObject.filter(this, filterFn);
}
public static coalesce<T>(iterable: AsyncIterable<T | undefined | null>): AsyncIterableObject<T> {
return <AsyncIterableObject<T>>AsyncIterableObject.filter(iterable, item => !!item);
}
public coalesce(): AsyncIterableObject<NonNullable<T>> {
return AsyncIterableObject.coalesce(this) as AsyncIterableObject<NonNullable<T>>;
}
public static async toPromise<T>(iterable: AsyncIterable<T>): Promise<T[]> {
const result: T[] = [];
for await (const item of iterable) {
result.push(item);
}
return result;
}
public toPromise(): Promise<T[]> {
return AsyncIterableObject.toPromise(this);
}
/**
* The value will be appended at the end.
*
* **NOTE** If `resolve()` or `reject()` have already been called, this method has no effect.
*/
private emitOne(value: T): void {
if (this._state !== AsyncIterableSourceState.Initial) {
return;
}
// it is important to add new values at the end,
// as we may have iterators already running on the array
this._results.push(value);
this._onStateChanged.fire();
}
/**
* The values will be appended at the end.
*
* **NOTE** If `resolve()` or `reject()` have already been called, this method has no effect.
*/
private emitMany(values: T[]): void {
if (this._state !== AsyncIterableSourceState.Initial) {
return;
}
// it is important to add new values at the end,
// as we may have iterators already running on the array
this._results = this._results.concat(values);
this._onStateChanged.fire();
}
/**
* Calling `resolve()` will mark the result array as complete.
*
* **NOTE** `resolve()` must be called, otherwise all consumers of this iterable will hang indefinitely, similar to a non-resolved promise.
* **NOTE** If `resolve()` or `reject()` have already been called, this method has no effect.
*/
private resolve(): void {
if (this._state !== AsyncIterableSourceState.Initial) {
return;
}
this._state = AsyncIterableSourceState.DoneOK;
this._onStateChanged.fire();
}
/**
* Writing an error will permanently invalidate this iterable.
* The current users will receive an error thrown, as will all future users.
*
* **NOTE** If `resolve()` or `reject()` have already been called, this method has no effect.
*/
private reject(error: Error) {
if (this._state !== AsyncIterableSourceState.Initial) {
return;
}
this._state = AsyncIterableSourceState.DoneError;
this._error = error;
this._onStateChanged.fire();
}
}
export class CancelableAsyncIterableObject<T> extends AsyncIterableObject<T> {
constructor(
private readonly _source: CancellationTokenSource,
executor: AyncIterableExecutor<T>
) {
super(executor);
}
cancel(): void {
this._source.cancel();
}
}
export function createCancelableAsyncIterable<T>(callback: (token: CancellationToken) => AsyncIterable<T>): CancelableAsyncIterableObject<T> {
const source = new CancellationTokenSource();
const innerIterable = callback(source.token);
return new CancelableAsyncIterableObject<T>(source, async (emitter) => {
const subscription = source.token.onCancellationRequested(() => {
subscription.dispose();
source.dispose();
emitter.reject(canceled());
});
try {
for await (const item of innerIterable) {
if (source.token.isCancellationRequested) {
// canceled in the meantime
return;
}
emitter.emitOne(item);
}
subscription.dispose();
source.dispose();
} catch (err) {
subscription.dispose();
source.dispose();
emitter.reject(err);
}
});
}
//#endregion

View file

@ -120,6 +120,8 @@ export interface IProductConfiguration {
readonly showTelemetryOptOut?: boolean;
readonly serverGreeting: string[];
readonly serverLicense?: string[];
readonly serverLicensePrompt?: string;
readonly npsSurveyUrl?: string;
readonly cesSurveyUrl?: string;

View file

@ -48,7 +48,6 @@ suite('Event', function () {
let doc = new Samples.Document3();
document.createElement('div').onclick = function () { };
let subscription = doc.onDidChange(counter.onEvent, counter);
doc.setText('far');

View file

@ -2516,7 +2516,7 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
renderHorizontalEndLineAtTheBottom = true;
}
}
// TODO: Consider indentation when computing guideVisibleColumn
const start = pair.openingBracketRange.getStartPosition();
const end = (pair.closingBracketRange?.getStartPosition() ?? pair.range.getEndPosition());

View file

@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AsyncIterableObject } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Color, RGBA } from 'vs/base/common/color';
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
@ -54,7 +55,11 @@ export class ColorHoverParticipant implements IEditorHoverParticipant<ColorHover
return [];
}
public async computeAsync(anchor: HoverAnchor, lineDecorations: IModelDecoration[], token: CancellationToken): Promise<ColorHover[]> {
public computeAsync(anchor: HoverAnchor, lineDecorations: IModelDecoration[], token: CancellationToken): AsyncIterableObject<ColorHover> {
return AsyncIterableObject.fromPromise(this._computeAsync(anchor, lineDecorations, token));
}
private async _computeAsync(anchor: HoverAnchor, lineDecorations: IModelDecoration[], token: CancellationToken): Promise<ColorHover[]> {
if (!this._editor.hasModel()) {
return [];
}

View file

@ -3,31 +3,45 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { coalesce } from 'vs/base/common/arrays';
import { AsyncIterableObject } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { registerModelAndPositionCommand } from 'vs/editor/browser/editorExtensions';
import { Position } from 'vs/editor/common/core/position';
import { ITextModel } from 'vs/editor/common/model';
import { Hover, HoverProviderRegistry } from 'vs/editor/common/modes';
import { Hover, HoverProvider, HoverProviderRegistry } from 'vs/editor/common/modes';
export function getHover(model: ITextModel, position: Position, token: CancellationToken): Promise<Hover[]> {
const supports = HoverProviderRegistry.ordered(model);
const promises = supports.map(support => {
return Promise.resolve(support.provideHover(model, position, token)).then(hover => {
return hover && isValid(hover) ? hover : undefined;
}, err => {
onUnexpectedExternalError(err);
return undefined;
});
});
return Promise.all(promises).then(coalesce);
export class HoverProviderResult {
constructor(
public readonly provider: HoverProvider,
public readonly hover: Hover,
public readonly ordinal: number
) {}
}
registerModelAndPositionCommand('_executeHoverProvider', (model, position) => getHover(model, position, CancellationToken.None));
async function executeProvider(provider: HoverProvider, ordinal: number, model: ITextModel, position: Position, token: CancellationToken): Promise<HoverProviderResult | undefined> {
try {
const result = await Promise.resolve(provider.provideHover(model, position, token));
if (result && isValid(result)) {
return new HoverProviderResult(provider, result, ordinal);
}
} catch (err) {
onUnexpectedExternalError(err);
}
return undefined;
}
export function getHover(model: ITextModel, position: Position, token: CancellationToken): AsyncIterableObject<HoverProviderResult> {
const providers = HoverProviderRegistry.ordered(model);
const promises = providers.map((provider, index) => executeProvider(provider, index, model, position, token));
return AsyncIterableObject.fromPromises(promises).coalesce();
}
export function getHoverPromise(model: ITextModel, position: Position, token: CancellationToken): Promise<Hover[]> {
return getHover(model, position, token).map(item => item.hover).toPromise();
}
registerModelAndPositionCommand('_executeHoverProvider', (model, position) => getHoverPromise(model, position, CancellationToken.None));
function isValid(result: Hover) {
const hasRange = (typeof result.range !== 'undefined');

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from 'vs/base/common/async';
import { AsyncIterableObject, CancelableAsyncIterableObject, createCancelableAsyncIterable, RunOnceScheduler } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedError } from 'vs/base/common/errors';
@ -12,24 +12,24 @@ export interface IHoverComputer<Result> {
/**
* This is called after half the hover time
*/
computeAsync?: (token: CancellationToken) => Promise<Result>;
computeAsync?: (token: CancellationToken) => AsyncIterableObject<Result>;
/**
* This is called after all the hover time
*/
computeSync?: () => Result;
computeSync?: () => Result[];
/**
* This is called whenever one of the compute* methods returns a truey value
*/
onResult: (result: Result, isFromSynchronousComputation: boolean) => void;
onResult: (result: Result[], isFromSynchronousComputation: boolean) => void;
/**
* This is what will be sent as progress/complete to the computation promise
*/
getResult: () => Result;
getResult: () => Result[];
getResultWithLoadingMessage: () => Result;
getResultWithLoadingMessage: () => Result[];
}
@ -37,7 +37,8 @@ const enum ComputeHoverOperationState {
IDLE = 0,
FIRST_WAIT = 1,
SECOND_WAIT = 2,
WAITING_FOR_ASYNC_COMPUTATION = 3
WAITING_FOR_ASYNC_COMPUTATION = 3,
WAITING_FOR_ASYNC_COMPUTATION_SHOWING_LOADING = 4,
}
export const enum HoverStartMode {
@ -54,14 +55,14 @@ export class HoverOperation<Result> {
private readonly _firstWaitScheduler: RunOnceScheduler;
private readonly _secondWaitScheduler: RunOnceScheduler;
private readonly _loadingMessageScheduler: RunOnceScheduler;
private _asyncComputationPromise: CancelablePromise<Result> | null;
private _asyncComputationPromiseDone: boolean;
private _asyncIterable: CancelableAsyncIterableObject<Result> | null;
private _asyncIterableDone: boolean;
private readonly _completeCallback: (r: Result) => void;
private readonly _completeCallback: (r: Result[]) => void;
private readonly _errorCallback: ((err: any) => void) | null | undefined;
private readonly _progressCallback: (progress: any) => void;
constructor(computer: IHoverComputer<Result>, success: (r: Result) => void, error: ((err: any) => void) | null | undefined, progress: (progress: any) => void, hoverTime: number) {
constructor(computer: IHoverComputer<Result>, success: (r: Result[]) => void, error: ((err: any) => void) | null | undefined, progress: (progress: any) => void, hoverTime: number) {
this._computer = computer;
this._state = ComputeHoverOperationState.IDLE;
this._hoverTime = hoverTime;
@ -70,8 +71,8 @@ export class HoverOperation<Result> {
this._secondWaitScheduler = new RunOnceScheduler(() => this._triggerSyncComputation(), 0);
this._loadingMessageScheduler = new RunOnceScheduler(() => this._showLoadingMessage(), 0);
this._asyncComputationPromise = null;
this._asyncComputationPromiseDone = false;
this._asyncIterable = null;
this._asyncIterableDone = false;
this._completeCallback = success;
this._errorCallback = error;
@ -99,15 +100,26 @@ export class HoverOperation<Result> {
this._secondWaitScheduler.schedule(this._secondWaitTime());
if (this._computer.computeAsync) {
this._asyncComputationPromiseDone = false;
this._asyncComputationPromise = createCancelablePromise(token => this._computer.computeAsync!(token));
this._asyncComputationPromise.then((asyncResult: Result) => {
this._asyncComputationPromiseDone = true;
this._withAsyncResult(asyncResult);
}, (e) => this._onError(e));
this._asyncIterableDone = false;
this._asyncIterable = createCancelableAsyncIterable(token => this._computer.computeAsync!(token));
(async () => {
try {
for await (const item of this._asyncIterable!) {
if (item) {
this._computer.onResult([item], false);
this._onProgress();
}
}
this._asyncIterableDone = true;
this._withAsyncResult();
} catch (e) {
this._onError(e);
}
})();
} else {
this._asyncComputationPromiseDone = true;
this._asyncIterableDone = true;
}
}
@ -116,34 +128,31 @@ export class HoverOperation<Result> {
this._computer.onResult(this._computer.computeSync(), true);
}
if (this._asyncComputationPromiseDone) {
if (this._asyncIterableDone) {
this._state = ComputeHoverOperationState.IDLE;
this._onComplete(this._computer.getResult());
this._onComplete();
} else {
this._state = ComputeHoverOperationState.WAITING_FOR_ASYNC_COMPUTATION;
this._onProgress(this._computer.getResult());
this._onProgress();
}
}
private _showLoadingMessage(): void {
if (this._state === ComputeHoverOperationState.WAITING_FOR_ASYNC_COMPUTATION) {
this._onProgress(this._computer.getResultWithLoadingMessage());
this._state = ComputeHoverOperationState.WAITING_FOR_ASYNC_COMPUTATION_SHOWING_LOADING;
this._onProgress();
}
}
private _withAsyncResult(asyncResult: Result): void {
if (asyncResult) {
this._computer.onResult(asyncResult, false);
}
if (this._state === ComputeHoverOperationState.WAITING_FOR_ASYNC_COMPUTATION) {
private _withAsyncResult(): void {
if (this._state === ComputeHoverOperationState.WAITING_FOR_ASYNC_COMPUTATION || this._state === ComputeHoverOperationState.WAITING_FOR_ASYNC_COMPUTATION_SHOWING_LOADING) {
this._state = ComputeHoverOperationState.IDLE;
this._onComplete(this._computer.getResult());
this._onComplete();
}
}
private _onComplete(value: Result): void {
this._completeCallback(value);
private _onComplete(): void {
this._completeCallback(this._computer.getResult());
}
private _onError(error: any): void {
@ -154,8 +163,12 @@ export class HoverOperation<Result> {
}
}
private _onProgress(value: Result): void {
this._progressCallback(value);
private _onProgress(): void {
if (this._state === ComputeHoverOperationState.WAITING_FOR_ASYNC_COMPUTATION_SHOWING_LOADING) {
this._progressCallback(this._computer.getResultWithLoadingMessage());
} else {
this._progressCallback(this._computer.getResult());
}
}
public start(mode: HoverStartMode): void {
@ -181,22 +194,12 @@ export class HoverOperation<Result> {
}
public cancel(): void {
this._firstWaitScheduler.cancel();
this._secondWaitScheduler.cancel();
this._loadingMessageScheduler.cancel();
if (this._state === ComputeHoverOperationState.FIRST_WAIT) {
this._firstWaitScheduler.cancel();
}
if (this._state === ComputeHoverOperationState.SECOND_WAIT) {
this._secondWaitScheduler.cancel();
if (this._asyncComputationPromise) {
this._asyncComputationPromise.cancel();
this._asyncComputationPromise = null;
}
}
if (this._state === ComputeHoverOperationState.WAITING_FOR_ASYNC_COMPUTATION) {
if (this._asyncComputationPromise) {
this._asyncComputationPromise.cancel();
this._asyncComputationPromise = null;
}
if (this._asyncIterable) {
this._asyncIterable.cancel();
this._asyncIterable = null;
}
this._state = ComputeHoverOperationState.IDLE;
}

View file

@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AsyncIterableObject } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IEditorMouseEvent } from 'vs/editor/browser/editorBrowser';
@ -85,7 +86,7 @@ export interface IEditorHoverAction {
export interface IEditorHoverParticipant<T extends IHoverPart = IHoverPart> {
suggestHoverAnchor?(mouseEvent: IEditorMouseEvent): HoverAnchor | null;
computeSync(anchor: HoverAnchor, lineDecorations: IModelDecoration[]): T[];
computeAsync?(anchor: HoverAnchor, lineDecorations: IModelDecoration[], token: CancellationToken): Promise<T[]>;
computeAsync?(anchor: HoverAnchor, lineDecorations: IModelDecoration[], token: CancellationToken): AsyncIterableObject<T>;
createLoadingMessage?(anchor: HoverAnchor): T | null;
renderHoverParts(hoverParts: T[], fragment: DocumentFragment, statusBar: IEditorHoverStatusBar): IDisposable;
}

View file

@ -5,6 +5,7 @@
import * as dom from 'vs/base/browser/dom';
import { asArray } from 'vs/base/common/arrays';
import { AsyncIterableObject } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IMarkdownString, isEmptyMarkdownString, MarkdownString } from 'vs/base/common/htmlContent';
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
@ -28,7 +29,8 @@ export class MarkdownHover implements IHoverPart {
constructor(
public readonly owner: IEditorHoverParticipant<MarkdownHover>,
public readonly range: Range,
public readonly contents: IMarkdownString[]
public readonly contents: IMarkdownString[],
public readonly ordinal: number
) { }
public isValidForHoverAnchor(anchor: HoverAnchor): boolean {
@ -51,7 +53,7 @@ export class MarkdownHoverParticipant implements IEditorHoverParticipant<Markdow
) { }
public createLoadingMessage(anchor: HoverAnchor): MarkdownHover | null {
return new MarkdownHover(this, anchor.range, [new MarkdownString().appendText(nls.localize('modesContentHover.loading', "Loading..."))]);
return new MarkdownHover(this, anchor.range, [new MarkdownString().appendText(nls.localize('modesContentHover.loading', "Loading..."))], 2000);
}
public computeSync(anchor: HoverAnchor, lineDecorations: IModelDecoration[]): MarkdownHover[] {
@ -63,6 +65,20 @@ export class MarkdownHoverParticipant implements IEditorHoverParticipant<Markdow
const lineNumber = anchor.range.startLineNumber;
const maxColumn = model.getLineMaxColumn(lineNumber);
const result: MarkdownHover[] = [];
let index = 1000;
const lineLength = model.getLineLength(lineNumber);
const languageId = model.getLanguageIdAtPosition(anchor.range.startLineNumber, anchor.range.startColumn);
const maxTokenizationLineLength = this._configurationService.getValue<number>('editor.maxTokenizationLineLength', {
overrideIdentifier: languageId
});
if (typeof maxTokenizationLineLength === 'number' && lineLength >= maxTokenizationLineLength) {
result.push(new MarkdownHover(this, anchor.range, [{
value: nls.localize('too many characters', "Tokenization is skipped for long lines for performance reasons. This can be configured via `editor.maxTokenizationLineLength`.")
}], index++));
}
for (const d of lineDecorations) {
const startColumn = (d.range.startLineNumber === lineNumber) ? d.range.startColumn : 1;
const endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn;
@ -73,48 +89,30 @@ export class MarkdownHoverParticipant implements IEditorHoverParticipant<Markdow
}
const range = new Range(anchor.range.startLineNumber, startColumn, anchor.range.startLineNumber, endColumn);
result.push(new MarkdownHover(this, range, asArray(hoverMessage)));
}
const lineLength = model.getLineLength(lineNumber);
const languageId = model.getLanguageIdAtPosition(anchor.range.startLineNumber, anchor.range.startColumn);
const maxTokenizationLineLength = this._configurationService.getValue<number>('editor.maxTokenizationLineLength', {
overrideIdentifier: languageId
});
if (typeof maxTokenizationLineLength === 'number' && lineLength >= maxTokenizationLineLength) {
result.push(new MarkdownHover(this, anchor.range, [{
value: nls.localize('too many characters', "Tokenization is skipped for long lines for performance reasons. This can be configured via `editor.maxTokenizationLineLength`.")
}]));
result.push(new MarkdownHover(this, range, asArray(hoverMessage), index++));
}
return result;
}
public async computeAsync(anchor: HoverAnchor, lineDecorations: IModelDecoration[], token: CancellationToken): Promise<MarkdownHover[]> {
public computeAsync(anchor: HoverAnchor, lineDecorations: IModelDecoration[], token: CancellationToken): AsyncIterableObject<MarkdownHover> {
if (!this._editor.hasModel() || anchor.type !== HoverAnchorType.Range) {
return Promise.resolve([]);
return AsyncIterableObject.EMPTY;
}
const model = this._editor.getModel();
if (!HoverProviderRegistry.has(model)) {
return Promise.resolve([]);
return AsyncIterableObject.EMPTY;
}
const hovers = await getHover(model, new Position(
anchor.range.startLineNumber,
anchor.range.startColumn
), token);
const result: MarkdownHover[] = [];
for (const hover of hovers) {
if (isEmptyMarkdownString(hover.contents)) {
continue;
}
const rng = hover.range ? Range.lift(hover.range) : anchor.range;
result.push(new MarkdownHover(this, rng, hover.contents));
}
return result;
const position = new Position(anchor.range.startLineNumber, anchor.range.startColumn);
return getHover(model, position, token)
.filter(item => !isEmptyMarkdownString(item.hover.contents))
.map(item => {
const rng = item.hover.range ? Range.lift(item.hover.range) : anchor.range;
return new MarkdownHover(this, rng, item.hover.contents, item.ordinal);
});
}
public renderHoverParts(hoverParts: MarkdownHover[], fragment: DocumentFragment, statusBar: IEditorHoverStatusBar): IDisposable {
@ -130,6 +128,10 @@ export function renderMarkdownHovers(
modeService: IModeService,
openerService: IOpenerService,
): IDisposable {
// Sort hover parts to keep them stable since they might come in async, out-of-order
hoverParts.sort((a, b) => a.ordinal - b.ordinal);
const disposables = new DisposableStore();
for (const hoverPart of hoverParts) {
for (const contents of hoverPart.contents) {

View file

@ -7,7 +7,7 @@ import * as dom from 'vs/base/browser/dom';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { HoverAction, HoverWidget } from 'vs/base/browser/ui/hover/hoverWidget';
import { Widget } from 'vs/base/browser/ui/widget';
import { coalesce, flatten } from 'vs/base/common/arrays';
import { coalesce } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
@ -32,6 +32,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { Context as SuggestContext } from 'vs/editor/contrib/suggest/suggest';
import { UnicodeHighlighterHoverParticipant } from 'vs/editor/contrib/unicodeHighlighter/unicodeHighlighter';
import { AsyncIterableObject } from 'vs/base/common/async';
const $ = dom.$;
@ -67,7 +68,7 @@ class EditorHoverStatusBar extends Disposable implements IEditorHoverStatusBar {
}
}
class ModesContentComputer implements IHoverComputer<IHoverPart[]> {
class ModesContentComputer implements IHoverComputer<IHoverPart> {
private readonly _editor: ICodeEditor;
private _result: IHoverPart[];
@ -121,22 +122,22 @@ class ModesContentComputer implements IHoverComputer<IHoverPart[]> {
});
}
public async computeAsync(token: CancellationToken): Promise<IHoverPart[]> {
public computeAsync(token: CancellationToken): AsyncIterableObject<IHoverPart> {
const anchor = this._anchor;
if (!this._editor.hasModel() || !anchor) {
return Promise.resolve([]);
return AsyncIterableObject.EMPTY;
}
const lineDecorations = ModesContentComputer._getLineDecorations(this._editor, anchor);
const allResults = await Promise.all(this._participants.map(p => this._computeAsync(p, lineDecorations, anchor, token)));
return flatten(allResults);
return AsyncIterableObject.merge(
this._participants.map(participant => this._computeAsync(participant, lineDecorations, anchor, token))
);
}
private async _computeAsync(participant: IEditorHoverParticipant, lineDecorations: IModelDecoration[], anchor: HoverAnchor, token: CancellationToken): Promise<IHoverPart[]> {
private _computeAsync(participant: IEditorHoverParticipant, lineDecorations: IModelDecoration[], anchor: HoverAnchor, token: CancellationToken): AsyncIterableObject<IHoverPart> {
if (!participant.computeAsync) {
return [];
return AsyncIterableObject.EMPTY;
}
return participant.computeAsync(anchor, lineDecorations, token);
}
@ -201,9 +202,10 @@ export class ModesContentHoverWidget extends Widget implements IContentWidget, I
public readonly allowEditorOverflow = true;
private _messages: IHoverPart[];
private _messagesAreComplete: boolean;
private _lastAnchor: HoverAnchor | null;
private readonly _computer: ModesContentComputer;
private readonly _hoverOperation: HoverOperation<IHoverPart[]>;
private readonly _hoverOperation: HoverOperation<IHoverPart>;
private _highlightDecorations: string[];
private _isChangingDecorations: boolean;
private _shouldFocus: boolean;
@ -257,6 +259,7 @@ export class ModesContentHoverWidget extends Widget implements IContentWidget, I
this._stoleFocus = false;
this._messages = [];
this._messagesAreComplete = false;
this._lastAnchor = null;
this._computer = new ModesContentComputer(this._editor, this._participants);
this._highlightDecorations = [];
@ -462,7 +465,7 @@ export class ModesContentHoverWidget extends Widget implements IContentWidget, I
const filteredMessages = this._messages.filter((m) => m.isValidForHoverAnchor(anchor));
if (filteredMessages.length === 0) {
this.hide();
} else if (filteredMessages.length === this._messages.length) {
} else if (filteredMessages.length === this._messages.length && this._messagesAreComplete) {
// no change
return;
} else {
@ -521,6 +524,7 @@ export class ModesContentHoverWidget extends Widget implements IContentWidget, I
private _withResult(result: IHoverPart[], complete: boolean): void {
this._messages = result;
this._messagesAreComplete = complete;
if (this._lastAnchor && this._messages.length > 0) {
this._renderMessages(this._lastAnchor, this._messages);
@ -561,8 +565,11 @@ export class ModesContentHoverWidget extends Widget implements IContentWidget, I
const statusBar = disposables.add(new EditorHoverStatusBar(this._keybindingService));
for (const [participant, participantHoverParts] of hoverParts) {
disposables.add(participant.renderHoverParts(participantHoverParts, fragment, statusBar));
for (const participant of this._participants) {
if (hoverParts.has(participant)) {
const participantHoverParts = hoverParts.get(participant)!;
disposables.add(participant.renderHoverParts(participantHoverParts, fragment, statusBar));
}
}
if (statusBar.hasContent) {

View file

@ -22,7 +22,7 @@ export interface IHoverMessage {
value: IMarkdownString;
}
class MarginComputer implements IHoverComputer<IHoverMessage[]> {
class MarginComputer implements IHoverComputer<IHoverMessage> {
private readonly _editor: ICodeEditor;
private _lineNumber: number;
@ -100,7 +100,7 @@ export class ModesGlyphHoverWidget extends Widget implements IOverlayWidget {
private readonly _markdownRenderer: MarkdownRenderer;
private readonly _computer: MarginComputer;
private readonly _hoverOperation: HoverOperation<IHoverMessage[]>;
private readonly _hoverOperation: HoverOperation<IHoverMessage>;
private readonly _renderDisposeables = this._register(new DisposableStore());
constructor(

View file

@ -119,26 +119,31 @@ export interface UnicodeHighlighterDecorationInfo {
}
type RemoveDeriveFromWorkspaceTrust<T> = T extends DeriveFromWorkspaceTrust ? never : T;
type ResolvedOptions = { [TKey in keyof InternalUnicodeHighlightOptions]: RemoveDeriveFromWorkspaceTrust<InternalUnicodeHighlightOptions[TKey]> };
function resolveOptions(_trusted: boolean, options: InternalUnicodeHighlightOptions): { [TKey in keyof InternalUnicodeHighlightOptions]: RemoveDeriveFromWorkspaceTrust<InternalUnicodeHighlightOptions[TKey]> } {
/*
// TODO@hediet enable some settings by default (depending on trust).
// For now, make it opt in, so there is some time to test it without breaking anyone.
function resolveOptions(trusted: boolean, options: InternalUnicodeHighlightOptions): ResolvedOptions {
let defaults;
if (trusted) {
defaults = {
nonBasicASCII: false,
ambiguousCharacters: true,
invisibleCharacters: true,
includeComments: true,
};
} else {
defaults = {
nonBasicASCII: true,
ambiguousCharacters: true,
invisibleCharacters: true,
includeComments: false,
};
}
return {
nonBasicASCII: options.nonBasicASCII !== deriveFromWorkspaceTrust ? options.nonBasicASCII : (trusted ? false : true),
ambiguousCharacters: options.ambiguousCharacters !== deriveFromWorkspaceTrust ? options.ambiguousCharacters : (trusted ? false : true),
invisibleCharacters: options.invisibleCharacters !== deriveFromWorkspaceTrust ? options.invisibleCharacters : (trusted ? true : true),
includeComments: options.includeComments !== deriveFromWorkspaceTrust ? options.includeComments : (trusted ? true : false),
allowedCharacters: options.allowedCharacters ?? [],
};
*/
return {
nonBasicASCII: options.nonBasicASCII !== deriveFromWorkspaceTrust ? options.nonBasicASCII : false,
ambiguousCharacters: options.ambiguousCharacters !== deriveFromWorkspaceTrust ? options.ambiguousCharacters : false,
invisibleCharacters: options.invisibleCharacters !== deriveFromWorkspaceTrust ? options.invisibleCharacters : false,
includeComments: options.includeComments !== deriveFromWorkspaceTrust ? options.includeComments : true,
nonBasicASCII: options.nonBasicASCII !== deriveFromWorkspaceTrust ? options.nonBasicASCII : defaults.nonBasicASCII,
ambiguousCharacters: options.ambiguousCharacters !== deriveFromWorkspaceTrust ? options.ambiguousCharacters : defaults.ambiguousCharacters,
invisibleCharacters: options.invisibleCharacters !== deriveFromWorkspaceTrust ? options.invisibleCharacters : defaults.invisibleCharacters,
includeComments: options.includeComments !== deriveFromWorkspaceTrust ? options.includeComments : defaults.includeComments,
allowedCharacters: options.allowedCharacters ?? [],
};
}
@ -306,6 +311,7 @@ export class UnicodeHighlighterHoverParticipant implements IEditorHoverParticipa
const result: MarkdownHover[] = [];
let index = 300;
for (const d of lineDecorations) {
const highlightInfo = unicodeHighlighter.getDecorationInfo(d.id);
@ -366,7 +372,7 @@ export class UnicodeHighlighterHoverParticipant implements IEditorHoverParticipa
isTrusted: true,
}];
result.push(new MarkdownHover(this, d.range, contents));
result.push(new MarkdownHover(this, d.range, contents, index++));
}
return result;
}

View file

@ -6,9 +6,7 @@
import { Event } from 'vs/base/common/event';
import * as types from 'vs/base/common/types';
import { URI, UriComponents } from 'vs/base/common/uri';
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
export const IConfigurationService = createDecorator<IConfigurationService>('configurationService');
@ -262,23 +260,6 @@ export function merge(base: any, add: any, overwrite: boolean): void {
});
}
export function getConfigurationKeys(): string[] {
const properties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
return Object.keys(properties);
}
export function getDefaultValues(): any {
const valueTreeRoot: any = Object.create(null);
const properties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
for (let key in properties) {
let value = properties[key].default;
addToValueTree(valueTreeRoot, key, value, message => console.error(`Conflict in default settings: ${message}`));
}
return valueTreeRoot;
}
export function getMigratedSettingValue<T>(configurationService: IConfigurationService, currentSettingName: string, legacySettingName: string): T {
const setting = configurationService.inspect<T>(currentSettingName);
const legacySetting = configurationService.inspect<T>(legacySettingName);

View file

@ -13,7 +13,7 @@ import * as objects from 'vs/base/common/objects';
import { IExtUri } from 'vs/base/common/resources';
import * as types from 'vs/base/common/types';
import { URI, UriComponents } from 'vs/base/common/uri';
import { addToValueTree, ConfigurationTarget, getConfigurationKeys, getConfigurationValue, getDefaultValues, IConfigurationChange, IConfigurationChangeEvent, IConfigurationCompareResult, IConfigurationData, IConfigurationModel, IConfigurationOverrides, IConfigurationUpdateOverrides, IConfigurationValue, IOverrides, removeFromValueTree, toValuesTree } from 'vs/platform/configuration/common/configuration';
import { addToValueTree, ConfigurationTarget, getConfigurationValue, IConfigurationChange, IConfigurationChangeEvent, IConfigurationCompareResult, IConfigurationData, IConfigurationModel, IConfigurationOverrides, IConfigurationUpdateOverrides, IConfigurationValue, IOverrides, removeFromValueTree, toValuesTree } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope, Extensions, IConfigurationPropertySchema, IConfigurationRegistry, overrideIdentifiersFromKey, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry';
import { IFileService } from 'vs/platform/files/common/files';
import { Registry } from 'vs/platform/registry/common/platform';
@ -234,10 +234,17 @@ export class ConfigurationModel implements IConfigurationModel {
export class DefaultConfigurationModel extends ConfigurationModel {
constructor() {
const contents = getDefaultValues();
const keys = getConfigurationKeys();
constructor(configurationDefaultsOverrides: IStringDictionary<any> = {}) {
const properties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
const keys = Object.keys(properties);
const contents: any = Object.create(null);
const overrides: IOverrides[] = [];
for (const key in properties) {
const defaultOverrideValue = configurationDefaultsOverrides[key];
const value = defaultOverrideValue !== undefined ? defaultOverrideValue : properties[key].default;
addToValueTree(contents, key, value, message => console.error(`Conflict in default settings: ${message}`));
}
for (const key of Object.keys(contents)) {
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
overrides.push({
@ -247,6 +254,7 @@ export class DefaultConfigurationModel extends ConfigurationModel {
});
}
}
super(contents, keys, overrides);
}
}
@ -587,21 +595,12 @@ export class Configuration {
this._foldersConsolidatedConfigurations.delete(resource);
}
compareAndUpdateDefaultConfiguration(defaults: ConfigurationModel, keys: string[]): IConfigurationChange {
const overrides: [string, string[]][] = [];
for (const key of keys) {
for (const overrideIdentifier of overrideIdentifiersFromKey(key)) {
const fromKeys = this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier);
const toKeys = defaults.getKeysForOverrideIdentifier(overrideIdentifier);
const keys = [
...toKeys.filter(key => fromKeys.indexOf(key) === -1),
...fromKeys.filter(key => toKeys.indexOf(key) === -1),
...fromKeys.filter(key => !objects.equals(this._defaultConfiguration.override(overrideIdentifier).getValue(key), defaults.override(overrideIdentifier).getValue(key)))
];
overrides.push([overrideIdentifier, keys]);
}
compareAndUpdateDefaultConfiguration(defaults: ConfigurationModel): IConfigurationChange {
const { added, updated, removed, overrides } = compare(this._defaultConfiguration, defaults);
const keys = [...added, ...updated, ...removed];
if (keys.length) {
this.updateDefaultConfiguration(defaults);
}
this.updateDefaultConfiguration(defaults);
return { keys, overrides };
}

View file

@ -55,6 +55,11 @@ export interface IConfigurationRegistry {
*/
deregisterDefaultConfigurations(defaultConfigurations: IStringDictionary<any>[]): void;
/**
* Return the registered configuration defaults overrides
*/
getConfigurationDefaultsOverrides(): IStringDictionary<any>;
/**
* Signal that the schema of a configuration setting has changes. It is currently only supported to change enumeration values.
* Property or default value changes are not allowed.
@ -65,13 +70,13 @@ export interface IConfigurationRegistry {
* Event that fires whenver a configuration has been
* registered.
*/
onDidSchemaChange: Event<void>;
readonly onDidSchemaChange: Event<void>;
/**
* Event that fires whenver a configuration has been
* registered.
*/
onDidUpdateConfiguration: Event<string[]>;
readonly onDidUpdateConfiguration: Event<{ properties: string[], defaultsOverrides?: boolean }>;
/**
* Returns all configuration nodes contributed to this registry.
@ -190,7 +195,7 @@ const contributionRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensio
class ConfigurationRegistry implements IConfigurationRegistry {
private readonly defaultValues: IStringDictionary<any>;
private readonly configurationDefaultsOverrides: IStringDictionary<any>;
private readonly defaultLanguageConfigurationOverridesNode: IConfigurationNode;
private readonly configurationContributors: IConfigurationNode[];
private readonly configurationProperties: { [qualifiedKey: string]: IJSONSchema };
@ -201,11 +206,11 @@ class ConfigurationRegistry implements IConfigurationRegistry {
private readonly _onDidSchemaChange = new Emitter<void>();
readonly onDidSchemaChange: Event<void> = this._onDidSchemaChange.event;
private readonly _onDidUpdateConfiguration: Emitter<string[]> = new Emitter<string[]>();
readonly onDidUpdateConfiguration: Event<string[]> = this._onDidUpdateConfiguration.event;
private readonly _onDidUpdateConfiguration = new Emitter<{ properties: string[], defaultsOverrides?: boolean }>();
readonly onDidUpdateConfiguration = this._onDidUpdateConfiguration.event;
constructor() {
this.defaultValues = {};
this.configurationDefaultsOverrides = {};
this.defaultLanguageConfigurationOverridesNode = {
id: 'defaultOverrides',
title: nls.localize('defaultLanguageConfigurationOverrides.title', "Default Language Configuration Overrides"),
@ -229,7 +234,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
this._onDidSchemaChange.fire();
this._onDidUpdateConfiguration.fire(properties);
this._onDidUpdateConfiguration.fire({ properties });
}
public deregisterConfigurations(configurations: IConfigurationNode[]): void {
@ -237,7 +242,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
this._onDidSchemaChange.fire();
this._onDidUpdateConfiguration.fire(properties);
this._onDidUpdateConfiguration.fire({ properties });
}
public updateConfigurations({ add, remove }: { add: IConfigurationNode[], remove: IConfigurationNode[] }): void {
@ -247,7 +252,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
this._onDidSchemaChange.fire();
this._onDidUpdateConfiguration.fire(distinct(properties));
this._onDidUpdateConfiguration.fire({ properties: distinct(properties) });
}
public registerDefaultConfigurations(defaultConfigurations: IStringDictionary<any>[]): void {
@ -259,10 +264,10 @@ class ConfigurationRegistry implements IConfigurationRegistry {
properties.push(key);
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
this.defaultValues[key] = { ...(this.defaultValues[key] || {}), ...defaultConfiguration[key] };
this.configurationDefaultsOverrides[key] = { ...(this.configurationDefaultsOverrides[key] || {}), ...defaultConfiguration[key] };
const property: IConfigurationPropertySchema = {
type: 'object',
default: this.defaultValues[key],
default: this.configurationDefaultsOverrides[key],
description: nls.localize('defaultLanguageConfiguration.description', "Configure settings to be overridden for {0} language.", key),
$ref: resourceLanguageSettingsSchemaId
};
@ -270,7 +275,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
this.configurationProperties[key] = property;
this.defaultLanguageConfigurationOverridesNode.properties![key] = property;
} else {
this.defaultValues[key] = defaultConfiguration[key];
this.configurationDefaultsOverrides[key] = defaultConfiguration[key];
const property = this.configurationProperties[key];
if (property) {
this.updatePropertyDefaultValue(key, property);
@ -282,7 +287,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
this.registerOverrideIdentifiers(overrideIdentifiers);
this._onDidSchemaChange.fire();
this._onDidUpdateConfiguration.fire(properties);
this._onDidUpdateConfiguration.fire({ properties, defaultsOverrides: true });
}
public deregisterDefaultConfigurations(defaultConfigurations: IStringDictionary<any>[]): void {
@ -290,7 +295,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
for (const defaultConfiguration of defaultConfigurations) {
for (const key in defaultConfiguration) {
properties.push(key);
delete this.defaultValues[key];
delete this.configurationDefaultsOverrides[key];
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
delete this.configurationProperties[key];
delete this.defaultLanguageConfigurationOverridesNode.properties![key];
@ -306,7 +311,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
this.updateOverridePropertyPatternKey();
this._onDidSchemaChange.fire();
this._onDidUpdateConfiguration.fire(properties);
this._onDidUpdateConfiguration.fire({ properties, defaultsOverrides: true });
}
public notifyConfigurationSchemaUpdated(...configurations: IConfigurationNode[]) {
@ -417,6 +422,10 @@ class ConfigurationRegistry implements IConfigurationRegistry {
return this.excludedConfigurationProperties;
}
getConfigurationDefaultsOverrides(): IStringDictionary<any> {
return this.configurationDefaultsOverrides;
}
private registerJSONConfiguration(configuration: IConfigurationNode) {
const register = (configuration: IConfigurationNode) => {
let properties = configuration.properties;
@ -476,6 +485,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
case ConfigurationScope.RESOURCE:
case ConfigurationScope.LANGUAGE_OVERRIDABLE:
delete resourceSettings.properties[key];
delete this.resourceLanguageSettingsSchema.properties![key];
break;
}
}
@ -517,7 +527,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
}
private updatePropertyDefaultValue(key: string, property: IConfigurationPropertySchema): void {
let defaultValue = this.defaultValues[key];
let defaultValue = this.configurationDefaultsOverrides[key];
if (types.isUndefined(defaultValue)) {
defaultValue = property.default;
}

View file

@ -34,7 +34,7 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
this.configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel());
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reloadConfiguration(), 50));
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidUpdateConfiguration(configurationProperties => this.onDidDefaultConfigurationChange(configurationProperties)));
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidUpdateConfiguration(() => this.onDidDefaultConfigurationChange()));
this._register(this.userConfiguration.onDidChange(() => this.reloadConfigurationScheduler.schedule()));
}
@ -89,9 +89,9 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
this.trigger(change, previous, ConfigurationTarget.USER);
}
private onDidDefaultConfigurationChange(keys: string[]): void {
private onDidDefaultConfigurationChange(): void {
const previous = this.configuration.toData();
const change = this.configuration.compareAndUpdateDefaultConfiguration(new DefaultConfigurationModel(), keys);
const change = this.configuration.compareAndUpdateDefaultConfiguration(new DefaultConfigurationModel());
this.trigger(change, previous, ConfigurationTarget.DEFAULT);
}

View file

@ -443,6 +443,43 @@ suite('CustomConfigurationModel', () => {
});
});
suite('CustomConfigurationModel', () => {
test('Default configuration model uses overrides', () => {
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
'id': 'a',
'order': 1,
'title': 'a',
'type': 'object',
'properties': {
'a': {
'description': 'a',
'type': 'boolean',
'default': false,
}
}
});
assert.strictEqual(true, new DefaultConfigurationModel().getValue('a'));
});
test('Default configuration model uses overrides', () => {
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
'id': 'a',
'order': 1,
'title': 'a',
'type': 'object',
'properties': {
'a': {
'description': 'a',
'type': 'boolean',
'default': false,
}
}
});
assert.strictEqual(false, new DefaultConfigurationModel({ a: false }).getValue('a'));
});
});
suite('Configuration', () => {
test('Test inspect for overrideIdentifiers', () => {
@ -488,9 +525,9 @@ suite('Configuration', () => {
'[markdown]': {
'editor.wordWrap': 'off'
}
}), ['editor.lineNumbers', '[markdown]']);
}));
assert.deepStrictEqual(actual, { keys: ['editor.lineNumbers', '[markdown]'], overrides: [['markdown', ['editor.wordWrap']]] });
assert.deepStrictEqual(actual, { keys: ['[markdown]', 'editor.lineNumbers'], overrides: [['markdown', ['editor.wordWrap']]] });
});
@ -853,7 +890,7 @@ suite('ConfigurationChangeEvent', () => {
'[markdown]': {
'editor.wordWrap': 'off'
}
}), ['editor.lineNumbers', '[markdown]']),
})),
configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
'[json]': {
'editor.lineNumbers': 'relative'

View file

@ -6,7 +6,9 @@
import { Emitter } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { getConfigurationKeys, getConfigurationValue, IConfigurationChangeEvent, IConfigurationOverrides, IConfigurationService, IConfigurationValue, isConfigurationOverrides } from 'vs/platform/configuration/common/configuration';
import { getConfigurationValue, IConfigurationChangeEvent, IConfigurationOverrides, IConfigurationService, IConfigurationValue, isConfigurationOverrides } from 'vs/platform/configuration/common/configuration';
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
export class TestConfigurationService implements IConfigurationService {
public _serviceBrand: undefined;
@ -68,7 +70,7 @@ export class TestConfigurationService implements IConfigurationService {
public keys() {
return {
default: getConfigurationKeys(),
default: Object.keys(Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties()),
user: Object.keys(this.configuration),
workspace: [],
workspaceFolder: []

View file

@ -158,7 +158,7 @@ export abstract class BaseWindowDriver implements IWindowDriver {
throw new Error(`Xterm not found: ${selector}`);
}
xterm._core._coreService.triggerDataEvent(text);
xterm._core.coreService.triggerDataEvent(text);
}
getLocaleInfo(): Promise<ILocaleInfo> {

View file

@ -53,6 +53,7 @@ export interface IEnvironmentService {
untitledWorkspacesHome: URI;
globalStorageHome: URI;
workspaceStorageHome: URI;
cacheHome: URI;
// --- settings sync
userDataSyncHome: URI;

View file

@ -57,6 +57,9 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron
@memoize
get tmpDir(): URI { return URI.file(this.paths.tmpDir); }
@memoize
get cacheHome(): URI { return URI.file(this.userDataPath); }
@memoize
get userRoamingDataHome(): URI { return this.appSettingsHome; }

View file

@ -264,8 +264,6 @@ export interface IExtensionManifest {
readonly repository?: { url: string; };
readonly bugs?: { url: string; };
readonly enabledApiProposals?: readonly string[];
/** @deprecated */
readonly enableProposedApi?: boolean;
readonly api?: string;
readonly scripts?: { [key: string]: string; };
readonly capabilities?: IExtensionCapabilities;
@ -348,9 +346,6 @@ export interface IExtensionDescription extends IExtensionManifest {
readonly isUserBuiltin: boolean;
readonly isUnderDevelopment: boolean;
readonly extensionLocation: URI;
/** @deprecated */
enableProposedApi?: boolean;
}
export function isLanguagePackExtension(manifest: IExtensionManifest): boolean {

View file

@ -8,7 +8,7 @@ import { realpathSync } from 'fs';
import { tmpdir } from 'os';
import { timeout } from 'vs/base/common/async';
import { dirname, join, sep } from 'vs/base/common/path';
import { isLinux, isWindows } from 'vs/base/common/platform';
import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
import { Promises, RimRafMode } from 'vs/base/node/pfs';
import { flakySuite, getPathFromAmdModule, getRandomTestPath } from 'vs/base/test/node/testUtils';
import { FileChangeType } from 'vs/platform/files/common/files';
@ -240,22 +240,26 @@ flakySuite('Recursive Watcher (parcel)', () => {
await Promises.writeFile(anotherNewFilePath, 'Hello Another World');
await changeFuture;
await timeout(1500); // ensure the previous added event is flushed by now (can happen on macOS with fsevents)
// Skip following asserts on macOS where the fs-events service
// does not really give a full guarantee about the correlation
// of an event to a change.
if (!isMacintosh) {
// Read file does not emit event
changeFuture = awaitEvent(service, anotherNewFilePath, FileChangeType.UPDATED, 'unexpected-event-from-read-file');
await Promises.readFile(anotherNewFilePath);
await Promise.race([timeout(100), changeFuture]);
// Read file does not emit event
changeFuture = awaitEvent(service, anotherNewFilePath, FileChangeType.UPDATED, 'unexpected-event-from-read-file');
await Promises.readFile(anotherNewFilePath);
await Promise.race([timeout(100), changeFuture]);
// Stat file does not emit event
changeFuture = awaitEvent(service, anotherNewFilePath, FileChangeType.UPDATED, 'unexpected-event-from-stat');
await Promises.stat(anotherNewFilePath);
await Promise.race([timeout(100), changeFuture]);
// Stat file does not emit event
changeFuture = awaitEvent(service, anotherNewFilePath, FileChangeType.UPDATED, 'unexpected-event-from-stat');
await Promises.stat(anotherNewFilePath);
await Promise.race([timeout(100), changeFuture]);
// Stat folder does not emit event
changeFuture = awaitEvent(service, copiedFolderpath, FileChangeType.UPDATED, 'unexpected-event-from-stat');
await Promises.stat(copiedFolderpath);
await Promise.race([timeout(100), changeFuture]);
// Stat folder does not emit event
changeFuture = awaitEvent(service, copiedFolderpath, FileChangeType.UPDATED, 'unexpected-event-from-stat');
await Promises.stat(copiedFolderpath);
await Promise.race([timeout(100), changeFuture]);
}
// Delete file
changeFuture = awaitEvent(service, copiedFilepath, FileChangeType.DELETED);

View file

@ -8,6 +8,7 @@
const perf = require('../base/common/performance');
const performance = require('perf_hooks').performance;
const product = require('../../../product.json');
const readline = require('readline');
perf.mark('code/server/start');
// @ts-ignore
@ -24,7 +25,7 @@ async function start() {
// Do a quick parse to determine if a server or the cli needs to be started
const parsedArgs = minimist(process.argv.slice(2), {
boolean: ['start-server', 'list-extensions', 'print-ip-address', 'help', 'version'],
boolean: ['start-server', 'list-extensions', 'print-ip-address', 'help', 'version', 'accept-server-license-terms'],
string: ['install-extension', 'install-builtin-extension', 'uninstall-extension', 'locate-extension', 'socket-path', 'host', 'port', 'pick-port']
});
@ -57,6 +58,21 @@ async function start() {
const http = require('http');
const os = require('os');
if (Array.isArray(product.serverLicense) && product.serverLicense.length) {
console.log(product.serverLicense.join('\n'));
if (product.serverLicensePrompt && parsedArgs['accept-server-license-terms'] !== true) {
try {
const accept = await prompt(product.serverLicensePrompt);
if (!accept) {
process.exit();
}
} catch (e) {
console.log(e);
process.exit();
}
}
}
let firstRequest = true;
let firstWebSocket = true;
@ -218,4 +234,30 @@ function loadCode() {
});
}
/**
* @param {string} question
* @returns { Promise<boolean> }
*/
function prompt(question) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise((resolve, reject) => {
rl.question(question + ' ', async function (data) {
rl.close();
const str = data.toString().trim().toLowerCase();
if (str === '' || str === 'y' || str === 'yes') {
resolve(true);
} else if (str === 'n' || str === 'no') {
resolve(false);
} else {
process.stdout.write('\nInvalid Response. Answer either yes (y, yes) or no (n, no)\n');
resolve(await prompt(question));
}
});
});
}
start();

View file

@ -57,6 +57,7 @@ export const serverOptions: OptionDescriptions<ServerParsedArgs> = {
'help': OPTIONS['help'],
'version': OPTIONS['version'],
'accept-server-license-terms': { type: 'boolean' },
_: OPTIONS['_']
};
@ -136,6 +137,8 @@ export interface ServerParsedArgs {
help: boolean;
version: boolean;
'accept-server-license-terms': boolean;
_: string[];
}

View file

@ -8,13 +8,14 @@ import * as objects from 'vs/base/common/objects';
import { Registry } from 'vs/platform/registry/common/platform';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { IConfigurationNode, IConfigurationRegistry, Extensions, resourceLanguageSettingsSchemaId, validateProperty, ConfigurationScope, OVERRIDE_PROPERTY_PATTERN, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry';
import { IConfigurationNode, IConfigurationRegistry, Extensions, resourceLanguageSettingsSchemaId, validateProperty, ConfigurationScope, OVERRIDE_PROPERTY_PATTERN, OVERRIDE_PROPERTY_REGEX, windowSettings, resourceSettings, machineOverridableSettings } from 'vs/platform/configuration/common/configurationRegistry';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { workspaceSettingsSchemaId, launchSchemaId, tasksSchemaId } from 'vs/workbench/services/configuration/common/configuration';
import { isObject } from 'vs/base/common/types';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IStringDictionary } from 'vs/base/common/collections';
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
const configurationEntrySchema: IJSONSchema = {
@ -111,21 +112,34 @@ const configurationEntrySchema: IJSONSchema = {
}
};
const configurationDefaultsSchemaId = 'vscode://schemas/settings/configurationDefaults';
const configurationDefaultsSchema: IJSONSchema = {
type: 'object',
description: nls.localize('configurationDefaults.description', 'Contribute defaults for configurations'),
properties: {},
patternProperties: {
[OVERRIDE_PROPERTY_PATTERN]: {
type: 'object',
default: {},
$ref: resourceLanguageSettingsSchemaId,
}
},
additionalProperties: false
};
jsonRegistry.registerSchema(configurationDefaultsSchemaId, configurationDefaultsSchema);
configurationRegistry.onDidSchemaChange(() => {
configurationDefaultsSchema.properties = {
...machineOverridableSettings.properties,
...windowSettings.properties,
...resourceSettings.properties
};
});
// BEGIN VSCode extension point `configurationDefaults`
const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint<IConfigurationNode>({
extensionPoint: 'configurationDefaults',
jsonSchema: {
description: nls.localize('vscode.extension.contributes.defaultConfiguration', 'Contributes default editor configuration settings by language.'),
type: 'object',
patternProperties: {
[OVERRIDE_PROPERTY_PATTERN]: {
type: 'object',
default: {},
$ref: resourceLanguageSettingsSchemaId,
}
},
errorMessage: nls.localize('config.property.defaultConfiguration.languageExpected', "Language selector expected (e.g. [\"java\"])"),
additionalProperties: false
$ref: configurationDefaultsSchemaId,
}
});
defaultConfigurationExtPoint.setHandler((extensions, { added, removed }) => {
@ -134,12 +148,17 @@ defaultConfigurationExtPoint.setHandler((extensions, { added, removed }) => {
configurationRegistry.deregisterDefaultConfigurations(removedDefaultConfigurations);
}
if (added.length) {
const registeredProperties = configurationRegistry.getConfigurationProperties();
const allowedScopes = [ConfigurationScope.MACHINE_OVERRIDABLE, ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE, ConfigurationScope.LANGUAGE_OVERRIDABLE];
const addedDefaultConfigurations = added.map<IStringDictionary<any>>(extension => {
const defaults: IStringDictionary<any> = objects.deepClone(extension.value);
for (const key of Object.keys(defaults)) {
if (!OVERRIDE_PROPERTY_REGEX.test(key) || typeof defaults[key] !== 'object') {
extension.collector.warn(nls.localize('config.property.defaultConfiguration.warning', "Cannot register configuration defaults for '{0}'. Only defaults for language specific settings are supported.", key));
delete defaults[key];
if (!OVERRIDE_PROPERTY_REGEX.test(key)) {
const registeredPropertyScheme = registeredProperties[key];
if (registeredPropertyScheme.scope && !allowedScopes.includes(registeredPropertyScheme.scope)) {
extension.collector.warn(nls.localize('config.property.defaultConfiguration.warning', "Cannot register configuration defaults for '{0}'. Only defaults for machine-overridable, window, resource and language overridable scoped settings are supported.", key));
delete defaults[key];
}
}
}
return defaults;
@ -273,7 +292,6 @@ configurationExtPoint.setHandler((extensions, { added, removed }) => {
});
// END VSCode extension point `configuration`
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
jsonRegistry.registerSchema('vscode://schemas/workspaceConfig', {
allowComments: true,
allowTrailingCommas: true,

View file

@ -29,7 +29,7 @@ import { setFullscreen } from 'vs/base/browser/browser';
import { URI } from 'vs/base/common/uri';
import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces';
import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService';
import { ConfigurationCache } from 'vs/workbench/services/configuration/browser/configurationCache';
import { ConfigurationCache } from 'vs/workbench/services/configuration/common/configurationCache';
import { ISignService } from 'vs/platform/sign/common/sign';
import { SignService } from 'vs/platform/sign/browser/signService';
import type { IWorkbenchConstructionOptions, IWorkspace, IWorkbench } from 'vs/workbench/workbench.web.api';
@ -356,7 +356,8 @@ class BrowserMain extends Disposable {
}
private async createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise<WorkspaceService> {
const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, uriIdentityService, logService);
const configurationCache = new ConfigurationCache([Schemas.file, Schemas.userData, Schemas.tmp] /* Cache all non native resources */, environmentService, fileService);
const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache }, environmentService, fileService, remoteAgentService, uriIdentityService, logService);
try {
await workspaceService.initialize(payload);

View file

@ -212,10 +212,16 @@ declare module DebugProtocol {
export interface OutputEvent extends Event {
// event: 'output';
body: {
/** The output category. If not specified, 'console' is assumed.
Values: 'console', 'stdout', 'stderr', 'telemetry', etc.
/** The output category. If not specified or if the category is not understand by the client, 'console' is assumed.
Values:
'console': Show the output in the client's default message UI, e.g. a 'debug console'. This category should only be used for informational output from the debugger (as opposed to the debuggee).
'important': A hint for the client to show the ouput in the client's UI for important and highly visible information, e.g. as a popup notification. This category should only be used for important messages from the debugger (as opposed to the debuggee). Since this category value is a hint, clients might ignore the hint and assume the 'console' category.
'stdout': Show the output as normal program output from the debuggee.
'stderr': Show the output as error program output from the debuggee.
'telemetry': Send the output to telemetry instead of showing it to the user.
etc.
*/
category?: 'console' | 'stdout' | 'stderr' | 'telemetry' | string;
category?: 'console' | 'important' | 'stdout' | 'stderr' | 'telemetry' | string;
/** The output to report. */
output: string;
/** Support for keeping an output log organized by grouping related messages.
@ -854,7 +860,7 @@ declare module DebugProtocol {
}
/** Continue request; value of command field is 'continue'.
The request starts the debuggee to run again.
The request resumes execution of all threads. If the debug adapter supports single thread execution (see capability 'supportsSingleThreadExecutionRequests') setting the 'singleThread' argument to true resumes only the specified thread. If not all threads were resumed, the 'allThreadsContinued' attribute of the response must be set to false.
*/
export interface ContinueRequest extends Request {
// command: 'continue';
@ -863,24 +869,23 @@ declare module DebugProtocol {
/** Arguments for 'continue' request. */
export interface ContinueArguments {
/** Continue execution for the specified thread (if possible).
If the backend cannot continue on a single thread but will continue on all threads, it should set the 'allThreadsContinued' attribute in the response to true.
*/
/** Specifies the active thread. If the debug adapter supports single thread execution (see 'supportsSingleThreadExecutionRequests') and the optional argument 'singleThread' is true, only the thread with this ID is resumed. */
threadId: number;
/** If this optional flag is true, execution is resumed only for the thread with given 'threadId'. */
singleThread?: boolean;
}
/** Response to 'continue' request. */
export interface ContinueResponse extends Response {
body: {
/** If true, the 'continue' request has ignored the specified thread and continued all threads instead.
If this attribute is missing a value of 'true' is assumed for backward compatibility.
*/
/** The value true (or a missing property) signals to the client that all threads have been resumed. The value false must be returned if not all threads were resumed. */
allThreadsContinued?: boolean;
};
}
/** Next request; value of command field is 'next'.
The request starts the debuggee to run again for one step.
The request executes one step (in the given granularity) for the specified thread and allows all other threads to run freely by resuming them.
If the debug adapter supports single thread execution (see capability 'supportsSingleThreadExecutionRequests') setting the 'singleThread' argument to true prevents other suspended threads from resuming.
The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.
*/
export interface NextRequest extends Request {
@ -890,8 +895,10 @@ declare module DebugProtocol {
/** Arguments for 'next' request. */
export interface NextArguments {
/** Execute 'next' for this thread. */
/** Specifies the thread for which to resume execution for one step (of the given granularity). */
threadId: number;
/** If this optional flag is true, all other suspended threads are not resumed. */
singleThread?: boolean;
/** Optional granularity to step. If no granularity is specified, a granularity of 'statement' is assumed. */
granularity?: SteppingGranularity;
}
@ -901,8 +908,9 @@ declare module DebugProtocol {
}
/** StepIn request; value of command field is 'stepIn'.
The request starts the debuggee to step into a function/method if possible.
If it cannot step into a target, 'stepIn' behaves like 'next'.
The request resumes the given thread to step into a function/method and allows all other threads to run freely by resuming them.
If the debug adapter supports single thread execution (see capability 'supportsSingleThreadExecutionRequests') setting the 'singleThread' argument to true prevents other suspended threads from resuming.
If the request cannot step into a target, 'stepIn' behaves like the 'next' request.
The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.
If there are multiple function/method calls (or other targets) on the source line,
the optional argument 'targetId' can be used to control into which target the 'stepIn' should occur.
@ -915,8 +923,10 @@ declare module DebugProtocol {
/** Arguments for 'stepIn' request. */
export interface StepInArguments {
/** Execute 'stepIn' for this thread. */
/** Specifies the thread for which to resume execution for one step-into (of the given granularity). */
threadId: number;
/** If this optional flag is true, all other suspended threads are not resumed. */
singleThread?: boolean;
/** Optional id of the target to step into. */
targetId?: number;
/** Optional granularity to step. If no granularity is specified, a granularity of 'statement' is assumed. */
@ -928,7 +938,8 @@ declare module DebugProtocol {
}
/** StepOut request; value of command field is 'stepOut'.
The request starts the debuggee to run again for one step.
The request resumes the given thread to step out (return) from a function/method and allows all other threads to run freely by resuming them.
If the debug adapter supports single thread execution (see capability 'supportsSingleThreadExecutionRequests') setting the 'singleThread' argument to true prevents other suspended threads from resuming.
The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.
*/
export interface StepOutRequest extends Request {
@ -938,8 +949,10 @@ declare module DebugProtocol {
/** Arguments for 'stepOut' request. */
export interface StepOutArguments {
/** Execute 'stepOut' for this thread. */
/** Specifies the thread for which to resume execution for one step-out (of the given granularity). */
threadId: number;
/** If this optional flag is true, all other suspended threads are not resumed. */
singleThread?: boolean;
/** Optional granularity to step. If no granularity is specified, a granularity of 'statement' is assumed. */
granularity?: SteppingGranularity;
}
@ -949,7 +962,8 @@ declare module DebugProtocol {
}
/** StepBack request; value of command field is 'stepBack'.
The request starts the debuggee to run one step backwards.
The request executes one backward step (in the given granularity) for the specified thread and allows all other threads to run backward freely by resuming them.
If the debug adapter supports single thread execution (see capability 'supportsSingleThreadExecutionRequests') setting the 'singleThread' argument to true prevents other suspended threads from resuming.
The debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.
Clients should only call this request if the capability 'supportsStepBack' is true.
*/
@ -960,8 +974,10 @@ declare module DebugProtocol {
/** Arguments for 'stepBack' request. */
export interface StepBackArguments {
/** Execute 'stepBack' for this thread. */
/** Specifies the thread for which to resume execution for one step backwards (of the given granularity). */
threadId: number;
/** If this optional flag is true, all other suspended threads are not resumed. */
singleThread?: boolean;
/** Optional granularity to step. If no granularity is specified, a granularity of 'statement' is assumed. */
granularity?: SteppingGranularity;
}
@ -971,7 +987,7 @@ declare module DebugProtocol {
}
/** ReverseContinue request; value of command field is 'reverseContinue'.
The request starts the debuggee to run backward.
The request resumes backward execution of all threads. If the debug adapter supports single thread execution (see capability 'supportsSingleThreadExecutionRequests') setting the 'singleThread' argument to true resumes only the specified thread. If not all threads were resumed, the 'allThreadsContinued' attribute of the response must be set to false.
Clients should only call this request if the capability 'supportsStepBack' is true.
*/
export interface ReverseContinueRequest extends Request {
@ -981,8 +997,10 @@ declare module DebugProtocol {
/** Arguments for 'reverseContinue' request. */
export interface ReverseContinueArguments {
/** Execute 'reverseContinue' for this thread. */
/** Specifies the active thread. If the debug adapter supports single thread execution (see 'supportsSingleThreadExecutionRequests') and the optional argument 'singleThread' is true, only the thread with this ID is resumed. */
threadId: number;
/** If this optional flag is true, backward execution is resumed only for the thread with given 'threadId'. */
singleThread?: boolean;
}
/** Response to 'reverseContinue' request. This is just an acknowledgement, so no body field is required. */
@ -1702,6 +1720,8 @@ declare module DebugProtocol {
supportsInstructionBreakpoints?: boolean;
/** The debug adapter supports 'filterOptions' as an argument on the 'setExceptionBreakpoints' request. */
supportsExceptionFilterOptions?: boolean;
/** The debug adapter supports the 'singleThread' property on the execution requests ('continue', 'next', 'stepIn', 'stepOut', 'reverseContinue', 'stepBack'). */
supportsSingleThreadExecutionRequests?: boolean;
}
/** An ExceptionBreakpointsFilter is shown in the UI as an filter option for configuring how exceptions are dealt with. */

View file

@ -149,7 +149,10 @@ suite('Debug - Debugger', () => {
assert.deepStrictEqual(ae!.args, debuggerContribution.args);
});
test('merge platform specific attributes', () => {
test('merge platform specific attributes', function () {
if (!process.versions.electron) {
this.skip(); //TODO@debug this test fails when run in node.js environments
}
const ae = ExecutableDebugAdapter.platformAdapterExecutable([extensionDescriptor1, extensionDescriptor2], 'mock')!;
assert.strictEqual(ae.command, platform.isLinux ? 'linuxRuntime' : (platform.isMacintosh ? 'osxRuntime' : 'winRuntime'));
const xprogram = platform.isLinux ? 'linuxProgram' : (platform.isMacintosh ? 'osxProgram' : 'winProgram');

View file

@ -302,7 +302,7 @@ export class ExperimentService extends Disposable implements IExperimentService
type ExperimentsClassification = {
experiments: { classification: 'SystemMetaData', purpose: 'FeatureInsight'; };
};
this.telemetryService.publicLog2<{ experiments: IExperiment[]; }, ExperimentsClassification>('experiments', { experiments: this._experiments });
this.telemetryService.publicLog2<{ experiments: string[]; }, ExperimentsClassification>('experiments', { experiments: this._experiments.map(e => e.id) });
});
});
}

View file

@ -30,7 +30,7 @@ import {
UpdateAction, ReloadAction, EnableDropDownAction, DisableDropDownAction, ExtensionStatusLabelAction, SetFileIconThemeAction, SetColorThemeAction,
RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ToggleSyncExtensionAction, SetProductIconThemeAction,
ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, UninstallAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction,
InstallAnotherVersionAction, ExtensionEditorManageExtensionAction, WebInstallAction, UsePreReleaseVersionAction, StopUsingPreReleaseVersionAction
InstallAnotherVersionAction, ExtensionEditorManageExtensionAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction
} from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
@ -426,7 +426,7 @@ export class ExtensionEditor extends EditorPane {
}
const widgets = [
this.instantiationService.createInstance(PreReleaseIndicatorWidget, template.preRelease),
this.instantiationService.createInstance(PreReleaseIndicatorWidget, template.preRelease, { label: true, icon: false }),
remoteBadge,
this.instantiationService.createInstance(InstallCountWidget, template.installCount, false),
this.instantiationService.createInstance(RatingsWidget, template.rating, false)
@ -454,8 +454,8 @@ export class ExtensionEditor extends EditorPane {
this.instantiationService.createInstance(InstallAnotherVersionAction),
]
]),
this.instantiationService.createInstance(UsePreReleaseVersionAction),
this.instantiationService.createInstance(StopUsingPreReleaseVersionAction),
this.instantiationService.createInstance(SwitchToPreReleaseVersionAction),
this.instantiationService.createInstance(SwitchToReleasedVersionAction),
this.instantiationService.createInstance(ToggleSyncExtensionAction),
new ExtensionEditorManageExtensionAction(this.scopedContextKeyService || this.contextKeyService, this.instantiationService),
];

View file

@ -15,7 +15,7 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWo
import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, DefaultViewsContext, ExtensionsSortByContext, WORKSPACE_RECOMMENDATIONS_VIEW_ID, IWorkspaceRecommendedExtensionsView, AutoUpdateConfigurationKey, HasOutdatedExtensionsContext, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, ExtensionEditorTab } from 'vs/workbench/contrib/extensions/common/extensions';
import { ReinstallAction, InstallSpecificVersionOfExtensionAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, PromptExtensionInstallFailureAction, SearchExtensionsAction, UsePreReleaseVersionAction, StopUsingPreReleaseVersionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { ReinstallAction, InstallSpecificVersionOfExtensionAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, PromptExtensionInstallFailureAction, SearchExtensionsAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor';
import { StatusUpdater, MaliciousExtensionChecker, ExtensionsViewletViewsContribution, ExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/browser/extensionsViewlet';
@ -1163,9 +1163,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi
id: MenuId.ExtensionContext,
group: '0_install',
order: 0,
when: ContextKeyExpr.or(
ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.not('galleryExtensionIsPreReleaseVersion')),
ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.not('showPreReleaseVersion')))
when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.not('showPreReleaseVersion'))
},
run: async (accessor: ServicesAccessor, extensionId: string) => {
const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService);
@ -1180,9 +1178,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi
id: MenuId.ExtensionContext,
group: '0_install',
order: 1,
when: ContextKeyExpr.or(
ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.has('galleryExtensionIsPreReleaseVersion'), ContextKeyExpr.equals('extensionStatus', 'installed')),
ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.has('showPreReleaseVersion')))
when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.has('showPreReleaseVersion'))
},
run: async (accessor: ServicesAccessor, extensionId: string) => {
const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService);
@ -1191,8 +1187,8 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi
}
});
this.registerExtensionAction({
id: UsePreReleaseVersionAction.ID,
title: UsePreReleaseVersionAction.TITLE,
id: SwitchToPreReleaseVersionAction.ID,
title: SwitchToPreReleaseVersionAction.TITLE,
menu: {
id: MenuId.ExtensionContext,
group: '0_install',
@ -1208,8 +1204,8 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi
}
});
this.registerExtensionAction({
id: StopUsingPreReleaseVersionAction.ID,
title: StopUsingPreReleaseVersionAction.TITLE,
id: SwitchToReleasedVersionAction.ID,
title: SwitchToReleasedVersionAction.TITLE,
menu: {
id: MenuId.ExtensionContext,
group: '0_install',

View file

@ -237,11 +237,11 @@ export class ActionWithDropDownAction extends ExtensionAction {
actions = actions.length ? actions.slice(0, actions.length - 1) : actions;
this.action = actions[0];
this._menuActions = actions.slice(1);
this._menuActions = actions.length > 1 ? actions : [];
this.enabled = !!this.action;
if (this.action) {
this.label = this.action.label;
this.label = this.getLabel(this.action as ExtensionAction);
this.tooltip = this.action.tooltip;
}
@ -257,6 +257,10 @@ export class ActionWithDropDownAction extends ExtensionAction {
const enabledActions = this.extensionActions.filter(a => a.enabled);
return enabledActions[0].run();
}
protected getLabel(action: ExtensionAction): string {
return action.label;
}
}
export abstract class AbstractInstallAction extends ExtensionAction {
@ -360,8 +364,16 @@ export abstract class AbstractInstallAction extends ExtensionAction {
this.label = this.getLabel();
}
protected getLabel(): string {
return this.installPreReleaseVersion && this.extension?.hasPreReleaseVersion ? localize('install pre-release', "Install Pre-release Version") : localize('install', "Install");
getLabel(primary?: boolean): string {
/* install pre-release version */
if (this.installPreReleaseVersion && this.extension?.hasPreReleaseVersion) {
return primary ? localize('install pre-release', "Install Pre-release") : localize('install pre-release version', "Install Pre-release Version");
}
/* install released version that has a pre release version */
if (this.extension?.hasPreReleaseVersion) {
return primary ? localize('install', "Install") : localize('install released version', "Install Released Version");
}
return localize('install', "Install");
}
protected getInstallOptions(): InstallOptions {
@ -391,8 +403,8 @@ export class InstallAction extends AbstractInstallAction {
Event.filter(userDataSyncResourceEnablementService.onDidChangeResourceEnablement, e => e[0] === SyncResource.Extensions))(() => this.update()));
}
protected override getLabel(): string {
const baseLabel = super.getLabel();
override getLabel(primary?: boolean): string {
const baseLabel = super.getLabel(primary);
const donotSyncLabel = localize('do no sync', "Do not sync");
const isMachineScoped = this.getInstallOptions().isMachineScoped;
@ -477,19 +489,24 @@ export class InstallDropdownAction extends ActionWithDropDownAction {
constructor(
@IInstantiationService instantiationService: IInstantiationService,
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
) {
super(`extensions.installActions`, '', [
[
instantiationService.createInstance(InstallAndSyncAction, false),
instantiationService.createInstance(InstallAndSyncAction, true),
instantiationService.createInstance(InstallAndSyncAction, extensionsWorkbenchService.preferPreReleases),
instantiationService.createInstance(InstallAndSyncAction, !extensionsWorkbenchService.preferPreReleases),
],
[
instantiationService.createInstance(InstallAction, false),
instantiationService.createInstance(InstallAction, true),
instantiationService.createInstance(InstallAction, extensionsWorkbenchService.preferPreReleases),
instantiationService.createInstance(InstallAction, !extensionsWorkbenchService.preferPreReleases),
]
]);
}
protected override getLabel(action: AbstractInstallAction): string {
return action.getLabel(true);
}
}
export class InstallingLabelAction extends ExtensionAction {
@ -1056,17 +1073,17 @@ export class MenuItemExtensionAction extends ExtensionAction {
}
}
export class UsePreReleaseVersionAction extends ExtensionAction {
export class SwitchToPreReleaseVersionAction extends ExtensionAction {
static readonly ID = 'workbench.extensions.action.usePreReleaseVersion';
static readonly TITLE = { value: localize('use pre-release version', "Use Pre-release Version"), original: 'Use Pre-release Version' };
static readonly ID = 'workbench.extensions.action.switchToPreReleaseVersion';
static readonly TITLE = { value: localize('switch to pre-release version', "Switch to Pre-release Version"), original: 'Switch to Pre-release Version' };
private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} hide-when-disabled`;
constructor(
@ICommandService private readonly commandService: ICommandService,
) {
super(UsePreReleaseVersionAction.ID, UsePreReleaseVersionAction.TITLE.value, UsePreReleaseVersionAction.Class);
super(SwitchToPreReleaseVersionAction.ID, SwitchToPreReleaseVersionAction.TITLE.value, SwitchToPreReleaseVersionAction.Class);
this.update();
}
@ -1078,21 +1095,21 @@ export class UsePreReleaseVersionAction extends ExtensionAction {
if (!this.enabled) {
return;
}
return this.commandService.executeCommand(UsePreReleaseVersionAction.ID, this.extension?.identifier.id);
return this.commandService.executeCommand(SwitchToPreReleaseVersionAction.ID, this.extension?.identifier.id);
}
}
export class StopUsingPreReleaseVersionAction extends ExtensionAction {
export class SwitchToReleasedVersionAction extends ExtensionAction {
static readonly ID = 'workbench.extensions.action.stopUsingPreReleaseVersion';
static readonly TITLE = { value: localize('stop using pre-release version', "Stop Using Pre-release Version"), original: 'Stop Using Pre-release Version' };
static readonly ID = 'workbench.extensions.action.switchToReleasedVersion';
static readonly TITLE = { value: localize('switch to released version', "Switch to Released Version"), original: 'Switch to Released Version' };
private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} hide-when-disabled`;
constructor(
@ICommandService private readonly commandService: ICommandService,
) {
super(StopUsingPreReleaseVersionAction.ID, StopUsingPreReleaseVersionAction.TITLE.value, StopUsingPreReleaseVersionAction.Class);
super(SwitchToReleasedVersionAction.ID, SwitchToReleasedVersionAction.TITLE.value, SwitchToReleasedVersionAction.Class);
this.update();
}
@ -1104,7 +1121,7 @@ export class StopUsingPreReleaseVersionAction extends ExtensionAction {
if (!this.enabled) {
return;
}
return this.commandService.executeCommand(StopUsingPreReleaseVersionAction.ID, this.extension?.identifier.id);
return this.commandService.executeCommand(SwitchToReleasedVersionAction.ID, this.extension?.identifier.id);
}
}

View file

@ -138,7 +138,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
extensionPackBadgeWidget,
headerRemoteBadgeWidget,
extensionHoverWidget,
this.instantiationService.createInstance(PreReleaseIndicatorWidget, preRelease),
this.instantiationService.createInstance(PreReleaseIndicatorWidget, preRelease, { icon: true, label: false }),
this.instantiationService.createInstance(SyncIgnoredWidget, syncIgnore),
this.instantiationService.createInstance(ExtensionActivationStatusWidget, activationStatus, true),
this.instantiationService.createInstance(InstallCountWidget, installCount, true),

View file

@ -160,7 +160,10 @@ export class RatingsWidget extends ExtensionWidget {
export class PreReleaseIndicatorWidget extends ExtensionWidget {
constructor(private container: HTMLElement) {
constructor(
private readonly container: HTMLElement,
private readonly options: { label: boolean, icon: boolean },
) {
super();
container.classList.add('extension-pre-release');
this.render();
@ -177,7 +180,12 @@ export class PreReleaseIndicatorWidget extends ExtensionWidget {
return;
}
append(this.container, $('span' + ThemeIcon.asCSSSelector(preReleaseIcon)));
if (this.options?.icon) {
append(this.container, $('span' + ThemeIcon.asCSSSelector(preReleaseIcon)));
}
if (this.options?.label) {
append(this.container, $('span.pre-releaselabel', undefined, localize('pre-release-label', "Pre-release")));
}
}
}
@ -576,8 +584,8 @@ export class ExtensionHoverWidget extends ExtensionWidget {
}
const extensionPreReleaseIcon = this.themeService.getColorTheme().getColor(extensionPreReleaseIconColor);
const preReleaseVersionLink = `[${localize('Show prerelease version', "Pre-release version")}](${URI.parse(`command:workbench.extensions.action.showPreReleaseVersion?${encodeURIComponent(JSON.stringify([extension.identifier.id]))}`)})`;
const message = localize('has prerelease', "There is a new {0} of this extension available in the Marketplace.", preReleaseVersionLink);
return `<span style="color:${extensionPreReleaseIcon ? Color.Format.CSS.formatHex(extensionPreReleaseIcon) : '#ffffff'};">$(${preReleaseIcon.id})</span>&nbsp;${message}.`;
const message = localize('has prerelease', "This extension has a {0} available", preReleaseVersionLink);
return `<span style="color:${extensionPreReleaseIcon ? Color.Format.CSS.formatHex(extensionPreReleaseIcon) : '#ffffff'};">$(${preReleaseIcon.id})</span>&nbsp;${message}`;
}
}
@ -609,10 +617,4 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`${ThemeIcon.asCSSSelector(verifiedPublisherIcon)} { color: ${extensionVerifiedPublisherIcon}; }`);
}
const extensionPreReleaseIcon = theme.getColor(extensionPreReleaseIconColor);
if (extensionPreReleaseIcon) {
collector.addRule(`.extension-bookmark .pre-release { border-top-color: ${extensionPreReleaseIcon}; }`);
collector.addRule(`.extension-bookmark .pre-release ${ThemeIcon.asCSSSelector(preReleaseIcon)} { color: #ffffff; }`);
collector.addRule(`${ThemeIcon.asCSSSelector(preReleaseIcon)} { color: ${extensionPreReleaseIcon}; }`);
}
});

View file

@ -40,7 +40,7 @@ import { FileAccess } from 'vs/base/common/network';
import { IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions';
import { IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { isBoolean } from 'vs/base/common/types';
import { isBoolean, isUndefined } from 'vs/base/common/types';
import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService';
import { IExtensionService, IExtensionsStatus } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor';
@ -587,6 +587,8 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
private readonly _onChange: Emitter<IExtension | undefined> = new Emitter<IExtension | undefined>();
get onChange(): Event<IExtension | undefined> { return this._onChange.event; }
readonly preferPreReleases = this.productService.quality !== 'stable';
private installing: IExtension[] = [];
constructor(
@ -613,6 +615,10 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
@IExtensionService private readonly extensionService: IExtensionService,
) {
super();
const preferPreReleasesValue = configurationService.getValue('_extensions.preferPreReleases');
if (!isUndefined(preferPreReleasesValue)) {
this.preferPreReleases = !!preferPreReleasesValue;
}
this.hasOutdatedExtensionsContextKey = HasOutdatedExtensionsContext.bindTo(contextKeyService);
if (extensionManagementServerService.localExtensionManagementServer) {
this.localExtensions = this._register(instantiationService.createInstance(Extensions, extensionManagementServerService.localExtensionManagementServer, ext => this.getExtensionState(ext)));
@ -741,6 +747,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
const options: IQueryOptions = CancellationToken.isCancellationToken(arg1) ? {} : arg1;
const token: CancellationToken = CancellationToken.isCancellationToken(arg1) ? arg1 : arg2;
options.text = options.text ? this.resolveQueryText(options.text) : options.text;
options.includePreRelease = isUndefined(options.includePreRelease) ? this.preferPreReleases : options.includePreRelease;
const report = await this.extensionManagementService.getExtensionsReport();
const maliciousSet = getMaliciousExtensionsSet(report);

View file

@ -82,6 +82,10 @@
overflow: hidden;
}
.extension-list-item > .details > .header-container > .header > .extension-pre-release .codicon {
color: var(--vscode-extensionIcon-preReleaseForeground) !important;
}
.extension-list-item > .details > .header-container > .header > .activation-status,
.extension-list-item > .details > .header-container > .header > .install-count,
.extension-list-item > .details > .header-container > .header > .ratings {

View file

@ -82,17 +82,24 @@
white-space: nowrap;
}
.extension-editor > .header > .details > .title > .pre-release {
display: flex;
margin-left: 4px;
}
.extension-editor > .header > .details > .title > .builtin {
font-size: 10px;
font-style: italic;
margin-left: 10px;
}
.extension-editor > .header > .details > .title > .pre-release {
display: flex;
font-size: 10px;
margin-left: 10px;
padding: 0px 4px;
border-radius: 4px;
user-select: none;
-webkit-user-select: none;
background-color: var(--vscode-extensionIcon-preReleaseForeground);
color: #ffffff;
}
.monaco-workbench.vs .extension-editor > .header > .details > .title > .preview {
color: white;
}

View file

@ -45,6 +45,11 @@
position: relative;
}
.extension-bookmark > .pre-release {
border-top-color: var(--vscode-extensionIcon-preReleaseForeground);
color: #ffffff;
}
.extension-bookmark > .recommendation > .codicon,
.extension-bookmark > .pre-release > .codicon {
position: absolute;
@ -53,3 +58,8 @@
color: inherit;
font-size: 80%;
}
/* codicon colors */
.codicon .codicon-extensions-pre-release {
color: var(--vscode-extensionIcon-preReleaseForeground);
}

View file

@ -84,10 +84,11 @@ export const IExtensionsWorkbenchService = createDecorator<IExtensionsWorkbenchS
export interface IExtensionsWorkbenchService {
readonly _serviceBrand: undefined;
onChange: Event<IExtension | undefined>;
local: IExtension[];
installed: IExtension[];
outdated: IExtension[];
readonly onChange: Event<IExtension | undefined>;
readonly preferPreReleases: boolean;
readonly local: IExtension[];
readonly installed: IExtension[];
readonly outdated: IExtension[];
queryLocal(server?: IExtensionManagementServer): Promise<IExtension[]>;
queryGallery(token: CancellationToken): Promise<IPager<IExtension>>;
queryGallery(options: IQueryOptions, token: CancellationToken): Promise<IPager<IExtension>>;

View file

@ -46,6 +46,7 @@ import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/comm
import { Schemas } from 'vs/base/common/network';
import { platform } from 'vs/base/common/platform';
import { arch } from 'vs/base/common/process';
import { IProductService } from 'vs/platform/product/common/productService';
suite('ExtensionsListView Tests', () => {
@ -81,6 +82,7 @@ suite('ExtensionsListView Tests', () => {
instantiationService = new TestInstantiationService();
instantiationService.stub(ITelemetryService, NullTelemetryService);
instantiationService.stub(ILogService, NullLogService);
instantiationService.stub(IProductService, {});
instantiationService.stub(IWorkspaceContextService, new TestContextService());
instantiationService.stub(IConfigurationService, new TestConfigurationService());

View file

@ -323,7 +323,8 @@ MenuRegistry.appendMenuItem(MenuId.NotebookToolbar, {
NOTEBOOK_EDITOR_EDITABLE.isEqualTo(true),
ContextKeyExpr.notEquals('config.notebook.insertToolbarLocation', 'betweenCells'),
ContextKeyExpr.notEquals('config.notebook.insertToolbarLocation', 'hidden'),
ContextKeyExpr.notEquals(`config.${NotebookSetting.globalToolbarShowLabel}`, false)
ContextKeyExpr.notEquals(`config.${NotebookSetting.globalToolbarShowLabel}`, false),
ContextKeyExpr.notEquals(`config.${NotebookSetting.globalToolbarShowLabel}`, 'never')
)
});

View file

@ -870,7 +870,7 @@
}
.output-show-more {
padding: 8px 0;
padding: 8px 0 0 0;
}
.cell-contributed-items.cell-contributed-items-left {

View file

@ -13,7 +13,7 @@ import { format } from 'vs/base/common/jsonFormatter';
import { applyEdits } from 'vs/base/common/jsonEdit';
import { ITextModel, ITextBufferFactory, DefaultEndOfLine, ITextBuffer } from 'vs/editor/common/model';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { ILanguageSelection, IModeService } from 'vs/editor/common/services/modeService';
import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService';
import * as nls from 'vs/nls';
import { Extensions, IConfigurationPropertySchema, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
@ -100,6 +100,7 @@ import { NotebookExecutionService } from 'vs/workbench/contrib/notebook/browser/
import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService';
import { INotebookKeymapService } from 'vs/workbench/contrib/notebook/common/notebookKeymapService';
import { NotebookKeymapService } from 'vs/workbench/contrib/notebook/browser/notebookKeymapServiceImpl';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
/*--------------------------------------------------------------------------------------------- */
@ -372,25 +373,55 @@ class CellInfoContentProvider {
return result;
}
private parseStreamOutput(resource: URI, op?: ICellOutput) {
private parseStreamOutput(op?: ICellOutput): { content: string, mode: ILanguageSelection } | undefined {
if (!op) {
return;
}
const streamOutputData = getStreamOutputData(op.outputs);
if (streamOutputData) {
const result = this._modelService.createModel(
streamOutputData,
this._modeService.create('plaintext'),
resource
);
return result;
return {
content: streamOutputData,
mode: this._modeService.create('plaintext')
};
}
return;
}
private _getResult(data: {
notebook: URI;
handle: number;
outputId?: string | undefined;
}, cell: NotebookCellTextModel) {
let result: { content: string, mode: ILanguageSelection } | undefined = undefined;
const mode = this._modeService.create('json');
const op = cell.outputs.find(op => op.outputId === data.outputId);
const streamOutputData = this.parseStreamOutput(op);
if (streamOutputData) {
result = streamOutputData;
return result;
}
const content = JSON.stringify(cell.outputs.map(output => ({
metadata: output.metadata,
outputItems: output.outputs.map(opit => ({
mimeType: opit.mime,
data: opit.data.toString()
}))
})));
const edits = format(content, undefined, {});
const outputSource = applyEdits(content, edits);
result = {
content: outputSource,
mode
};
return result;
}
async provideOutputTextContent(resource: URI): Promise<ITextModel | null> {
const existing = this._modelService.getModel(resource);
if (existing) {
@ -403,48 +434,37 @@ class CellInfoContentProvider {
}
const ref = await this._notebookModelResolverService.resolve(data.notebook);
let result: ITextModel | null = null;
const cell = ref.object.notebook.cells.find(cell => cell.handle === data.handle);
const mode = this._modeService.create('json');
for (const cell of ref.object.notebook.cells) {
if (cell.handle !== data.handle) {
continue;
}
const op = cell.outputs.find(op => op.outputId === data.outputId);
const streamOutputData = this.parseStreamOutput(resource, op);
if (streamOutputData) {
result = streamOutputData;
break;
}
const content = JSON.stringify(cell.outputs.map(output => ({
metadata: output.metadata,
outputItems: output.outputs.map(opit => ({
mimeType: opit.mime,
data: opit.data.toString()
}))
})));
const edits = format(content, undefined, {});
const outputSource = applyEdits(content, edits);
result = this._modelService.createModel(
outputSource,
mode,
resource
);
break;
if (!cell) {
return null;
}
const result = this._getResult(data, cell);
if (result) {
const once = result.onWillDispose(() => {
const model = this._modelService.createModel(result.content, result.mode, resource);
const cellModelListener = Event.any(cell.onDidChangeOutputs, cell.onDidChangeOutputItems)(() => {
const newResult = this._getResult(data, cell);
if (!newResult) {
return;
}
model.setValue(newResult.content);
model.setMode(newResult.mode.languageId);
});
const once = model.onWillDispose(() => {
once.dispose();
cellModelListener.dispose();
ref.dispose();
});
return model;
}
return result;
return null;
}
}

View file

@ -5,6 +5,7 @@
import * as DOM from 'vs/base/browser/dom';
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
import { Codicon } from 'vs/base/common/codicons';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
@ -22,7 +23,7 @@ const SIZE_LIMIT = 65535;
function generateViewMoreElement(notebookUri: URI, cellViewModel: IGenericCellViewModel, outputId: string, disposables: DisposableStore, openerService: IOpenerService): HTMLElement {
const md: IMarkdownString = {
value: `[show more (open the raw output data in a text editor) ...](command:workbench.action.openLargeOutput?${outputId})`,
value: `Output exceeds [size limit](command:workbench.action.openSettings?["notebook.output.textLineLimit"]), open the full output data[ in a text editor](command:workbench.action.openLargeOutput?${outputId})`,
isTrusted: true,
supportThemeIcons: true
};
@ -36,6 +37,10 @@ function generateViewMoreElement(notebookUri: URI, cellViewModel: IGenericCellVi
openerService.open(CellUri.generateCellOutputUri(notebookUri, cellViewModel.handle, outputId));
}
if (content.startsWith('command:workbench.action.openSettings')) {
openerService.open(content, { allowCommands: true });
}
return;
},
disposables: disposables
@ -83,12 +88,14 @@ export function truncatedArrayOfString(notebookUri: URI, cellViewModel: IGeneric
return;
}
container.appendChild(generateViewMoreElement(notebookUri, cellViewModel, outputId, disposables, openerService));
const pre = DOM.$('pre');
container.appendChild(pre);
pre.appendChild(handleANSIOutput(buffer.getValueInRange(new Range(1, 1, linesLimit - 5, buffer.getLineLastNonWhitespaceColumn(linesLimit - 5)), EndOfLinePreference.TextDefined), linkDetector, themeService, undefined));
// view more ...
container.appendChild(generateViewMoreElement(notebookUri, cellViewModel, outputId, disposables, openerService));
DOM.append(container, DOM.$('span' + Codicon.toolBarMore.cssSelector));
const lineCount = buffer.getLineCount();
const pre2 = DOM.$('div');

View file

@ -15,12 +15,15 @@ import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeText
import { TextModel } from 'vs/editor/common/model/textModel';
import { IModeService } from 'vs/editor/common/services/modeService';
import { NotebookCellOutputTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel';
import { CellInternalMetadataChangedEvent, CellKind, ICell, ICellOutput, IOutputDto, NotebookCellInternalMetadata, NotebookCellMetadata, NotebookCellOutputsSplice, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellInternalMetadataChangedEvent, CellKind, ICell, ICellOutput, IOutputDto, IOutputItemDto, NotebookCellInternalMetadata, NotebookCellMetadata, NotebookCellOutputsSplice, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
export class NotebookCellTextModel extends Disposable implements ICell {
private readonly _onDidChangeOutputs = this._register(new Emitter<NotebookCellOutputsSplice>());
onDidChangeOutputs: Event<NotebookCellOutputsSplice> = this._onDidChangeOutputs.event;
private readonly _onDidChangeOutputItems = this._register(new Emitter<void>());
onDidChangeOutputItems: Event<void> = this._onDidChangeOutputItems.event;
private readonly _onDidChangeContent = this._register(new Emitter<'content' | 'language' | 'mime'>());
onDidChangeContent: Event<'content' | 'language' | 'mime'> = this._onDidChangeContent.event;
@ -264,6 +267,24 @@ export class NotebookCellTextModel extends Disposable implements ICell {
this._onDidChangeOutputs.fire(splice);
}
changeOutputItems(outputId: string, append: boolean, items: IOutputItemDto[]): boolean {
const outputIndex = this.outputs.findIndex(output => output.outputId === outputId);
if (outputIndex < 0) {
return false;
}
const output = this.outputs[outputIndex];
if (append) {
output.appendData(items);
} else {
output.replaceData(items);
}
this._onDidChangeOutputItems.fire();
return true;
}
private _outputNotEqualFastCheck(left: ICellOutput[], right: ICellOutput[]) {
if (left.length !== right.length) {
return false;

View file

@ -955,53 +955,41 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
}
private _appendNotebookCellOutputItems(cell: NotebookCellTextModel, outputId: string, items: IOutputItemDto[]) {
const outputIndex = cell.outputs.findIndex(output => output.outputId === outputId);
if (cell.changeOutputItems(outputId, true, items)) {
this._pauseableEmitter.fire({
rawEvents: [{
kind: NotebookCellsChangeType.OutputItem,
index: this._cells.indexOf(cell),
outputId: outputId,
outputItems: items,
append: true,
transient: this.transientOptions.transientOutputs
if (outputIndex < 0) {
return;
}],
versionId: this.versionId,
synchronous: true,
endSelectionState: undefined
});
}
const output = cell.outputs[outputIndex];
output.appendData(items);
this._pauseableEmitter.fire({
rawEvents: [{
kind: NotebookCellsChangeType.OutputItem,
index: this._cells.indexOf(cell),
outputId: output.outputId,
outputItems: items,
append: true,
transient: this.transientOptions.transientOutputs
}],
versionId: this.versionId,
synchronous: true,
endSelectionState: undefined
});
}
private _replaceNotebookCellOutputItems(cell: NotebookCellTextModel, outputId: string, items: IOutputItemDto[]) {
const outputIndex = cell.outputs.findIndex(output => output.outputId === outputId);
if (cell.changeOutputItems(outputId, false, items)) {
this._pauseableEmitter.fire({
rawEvents: [{
kind: NotebookCellsChangeType.OutputItem,
index: this._cells.indexOf(cell),
outputId: outputId,
outputItems: items,
append: false,
transient: this.transientOptions.transientOutputs
if (outputIndex < 0) {
return;
}],
versionId: this.versionId,
synchronous: true,
endSelectionState: undefined
});
}
const output = cell.outputs[outputIndex];
output.replaceData(items);
this._pauseableEmitter.fire({
rawEvents: [{
kind: NotebookCellsChangeType.OutputItem,
index: this._cells.indexOf(cell),
outputId: output.outputId,
outputItems: items,
append: false,
transient: this.transientOptions.transientOutputs
}],
versionId: this.versionId,
synchronous: true,
endSelectionState: undefined
});
}
private _moveCellToIdx(index: number, length: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean, beforeSelections: ISelectionState | undefined, endSelections: ISelectionState | undefined): boolean {

View file

@ -133,6 +133,10 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend
const editableSetting = this.getSetting(setting);
return !!(editableSetting && this.editSettingActionRenderer.activateOnSetting(editableSetting));
}
public override dispose(): void {
this.preferencesModel.dispose();
}
}
export class WorkspaceSettingsRenderer extends UserSettingsRenderer implements IPreferencesRenderer {

View file

@ -194,12 +194,14 @@ export interface ITerminalService extends ITerminalInstanceHost {
resolveLocation(location?: ITerminalLocationOptions): TerminalLocation | undefined
setNativeDelegate(nativeCalls: ITerminalServiceNativeDelegate): void;
toggleDevTools(open?: boolean): Promise<void>;
handleNewRegisteredBackend(backend: ITerminalBackend): void;
}
export interface ITerminalServiceNativeDelegate {
getWindowCount(): Promise<number>;
openDevTools(): Promise<void>;
toggleDevTools(): Promise<void>;
}
/**
@ -754,7 +756,7 @@ export interface ITerminalInstance {
addDisposable(disposable: IDisposable): void;
toggleEscapeSequenceLogging(): void;
toggleEscapeSequenceLogging(): Promise<boolean>;
getInitialCwd(): Promise<string>;
getCwd(): Promise<string>;

View file

@ -1156,8 +1156,10 @@ export function registerTerminalActions() {
precondition: TerminalContextKeys.processSupported
});
}
run(accessor: ServicesAccessor) {
accessor.get(ITerminalService).activeInstance?.toggleEscapeSequenceLogging();
async run(accessor: ServicesAccessor) {
const terminalService = accessor.get(ITerminalService);
const toggledOn = await terminalService.activeInstance?.toggleEscapeSequenceLogging();
terminalService.toggleDevTools(toggledOn);
}
});
registerAction2(class extends Action2 {

View file

@ -1751,9 +1751,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}
}
async toggleEscapeSequenceLogging(): Promise<void> {
async toggleEscapeSequenceLogging(): Promise<boolean> {
const xterm = await this._xtermReadyPromise;
xterm.raw.options.logLevel = xterm.raw.options.logLevel === 'debug' ? 'info' : 'debug';
return xterm.raw.options.logLevel === 'debug';
}
async getInitialCwd(): Promise<string> {

View file

@ -540,6 +540,13 @@ export class TerminalService implements ITerminalService {
this._nativeDelegate = nativeDelegate;
}
async toggleDevTools(open?: boolean): Promise<void> {
if (open) {
this._nativeDelegate?.openDevTools();
} else {
this._nativeDelegate?.toggleDevTools();
}
}
private _shouldReviveProcesses(reason: ShutdownReason): boolean {
if (!this._configHelper.config.enablePersistentSessions) {
return false;

View file

@ -20,7 +20,7 @@ export interface IXtermCore {
height: number;
};
_coreService: {
coreService: {
triggerDataEvent(data: string, wasUserInput?: boolean): void;
};

View file

@ -31,7 +31,9 @@ export class TerminalNativeContribution extends Disposable implements IWorkbench
this._register(nativeHostService.onDidResumeOS(() => this._onOsResume()));
this._terminalService.setNativeDelegate({
getWindowCount: () => nativeHostService.getWindowCount()
getWindowCount: () => nativeHostService.getWindowCount(),
openDevTools: () => nativeHostService.openDevTools(),
toggleDevTools: () => nativeHostService.toggleDevTools()
});
const connection = remoteAgentService.getConnection();

View file

@ -19,6 +19,7 @@ import * as extpath from 'vs/base/common/extpath';
import { FuzzyScore } from 'vs/base/common/filters';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable, dispose, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { fuzzyContains } from 'vs/base/common/strings';
import { isDefined } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import 'vs/css!./media/testing';
@ -907,7 +908,7 @@ class TestsFilter implements ITreeFilter<TestExplorerTreeElement> {
const data = e.label.toLowerCase();
for (const { include, text } of this.state.globList) {
if (data.includes(text)) {
if (fuzzyContains(data, text)) {
included = include ? FilterResult.Include : FilterResult.Exclude;
}
}

View file

@ -30,7 +30,7 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA
import { FileService } from 'vs/platform/files/common/fileService';
import { IFileService } from 'vs/platform/files/common/files';
import { RemoteFileSystemProvider } from 'vs/workbench/services/remote/common/remoteAgentFileSystemChannel';
import { ConfigurationCache } from 'vs/workbench/services/configuration/electron-sandbox/configurationCache';
import { ConfigurationCache } from 'vs/workbench/services/configuration/common/configurationCache';
import { ISignService } from 'vs/platform/sign/common/sign';
import { basename } from 'vs/base/common/path';
import { IProductService } from 'vs/platform/product/common/productService';
@ -50,6 +50,7 @@ import { registerWindowDriver } from 'vs/platform/driver/electron-sandbox/driver
import { safeStringify } from 'vs/base/common/objects';
import { ISharedProcessWorkerWorkbenchService, SharedProcessWorkerWorkbenchService } from 'vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessWorkerWorkbenchService';
import { isMacintosh } from 'vs/base/common/platform';
import { Schemas } from 'vs/base/common/network';
export abstract class SharedDesktopMain extends Disposable {
@ -333,7 +334,8 @@ export abstract class SharedDesktopMain extends Disposable {
}
private async createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: INativeWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise<WorkspaceService> {
const workspaceService = new WorkspaceService({ remoteAuthority: environmentService.remoteAuthority, configurationCache: new ConfigurationCache(URI.file(environmentService.userDataPath), fileService) }, environmentService, fileService, remoteAgentService, uriIdentityService, logService);
const configurationCache = new ConfigurationCache([Schemas.file, Schemas.userData] /* Cache all non native resources */, environmentService, fileService);
const workspaceService = new WorkspaceService({ remoteAuthority: environmentService.remoteAuthority, configurationCache }, environmentService, fileService, remoteAgentService, uriIdentityService, logService);
try {
await workspaceService.initialize(payload);

View file

@ -9,13 +9,13 @@ import * as errors from 'vs/base/common/errors';
import { Disposable, IDisposable, dispose, toDisposable, MutableDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { RunOnceScheduler, timeout } from 'vs/base/common/async';
import { FileChangeType, FileChangesEvent, IFileService, whenProviderRegistered, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { ConfigurationModel, ConfigurationModelParser, ConfigurationParseOptions, UserSettings } from 'vs/platform/configuration/common/configurationModels';
import { ConfigurationModel, ConfigurationModelParser, ConfigurationParseOptions, DefaultConfigurationModel, UserSettings } from 'vs/platform/configuration/common/configurationModels';
import { WorkspaceConfigurationModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels';
import { TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY, IConfigurationCache, ConfigurationKey, REMOTE_MACHINE_SCOPES, FOLDER_SCOPES, WORKSPACE_SCOPES } from 'vs/workbench/services/configuration/common/configuration';
import { IStoredWorkspaceFolder, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService';
import { WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { ConfigurationScope, Extensions, IConfigurationRegistry, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry';
import { equals } from 'vs/base/common/objects';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { hash } from 'vs/base/common/hash';
@ -24,6 +24,92 @@ import { ILogService } from 'vs/platform/log/common/log';
import { IStringDictionary } from 'vs/base/common/collections';
import { ResourceMap } from 'vs/base/common/map';
import { joinPath } from 'vs/base/common/resources';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { isObject } from 'vs/base/common/types';
export class DefaultConfiguration extends Disposable {
private readonly configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
private cachedConfigurationDefaultsOverrides: IStringDictionary<any> = {};
private readonly cacheKey: ConfigurationKey = { type: 'defaults', key: 'configurationDefaultsOverrides' };
private readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidChangeConfiguration.event;
constructor(
private readonly configurationCache: IConfigurationCache,
environmentService: IWorkbenchEnvironmentService,
) {
super();
if (environmentService.options?.configurationDefaults) {
this.configurationRegistry.registerDefaultConfigurations([environmentService.options.configurationDefaults]);
}
}
private _configurationModel: ConfigurationModel | undefined;
get configurationModel(): ConfigurationModel {
if (!this._configurationModel) {
this._configurationModel = new DefaultConfigurationModel(this.cachedConfigurationDefaultsOverrides);
}
return this._configurationModel;
}
async initialize(): Promise<ConfigurationModel> {
await this.initializeCachedConfigurationDefaultsOverrides();
this._register(this.configurationRegistry.onDidUpdateConfiguration(({ defaultsOverrides }) => this.onDidUpdateConfiguration(defaultsOverrides)));
return this.configurationModel;
}
reload(): ConfigurationModel {
this.cachedConfigurationDefaultsOverrides = {};
this._configurationModel = undefined;
this.updateCachedConfigurationDefaultsOverrides();
return this.configurationModel;
}
private initiaizeCachedConfigurationDefaultsOverridesPromise: Promise<void> | undefined;
private initializeCachedConfigurationDefaultsOverrides(): Promise<void> {
if (!this.initiaizeCachedConfigurationDefaultsOverridesPromise) {
this.initiaizeCachedConfigurationDefaultsOverridesPromise = (async () => {
try {
const content = await this.configurationCache.read(this.cacheKey);
if (content) {
this.cachedConfigurationDefaultsOverrides = JSON.parse(content);
}
} catch (error) { /* ignore */ }
this.cachedConfigurationDefaultsOverrides = isObject(this.cachedConfigurationDefaultsOverrides) ? this.cachedConfigurationDefaultsOverrides : {};
})();
}
return this.initiaizeCachedConfigurationDefaultsOverridesPromise;
}
private onDidUpdateConfiguration(defaultsOverrides?: boolean): void {
this._configurationModel = undefined;
this._onDidChangeConfiguration.fire(this.configurationModel);
if (defaultsOverrides) {
this.updateCachedConfigurationDefaultsOverrides();
}
}
private async updateCachedConfigurationDefaultsOverrides(): Promise<void> {
const cachedConfigurationDefaultsOverrides: IStringDictionary<any> = {};
const configurationDefaultsOverrides = this.configurationRegistry.getConfigurationDefaultsOverrides();
for (const key of Object.keys(configurationDefaultsOverrides)) {
if (!OVERRIDE_PROPERTY_REGEX.test(key) && configurationDefaultsOverrides[key] !== undefined) {
cachedConfigurationDefaultsOverrides[key] = configurationDefaultsOverrides[key];
}
}
try {
if (Object.keys(cachedConfigurationDefaultsOverrides).length) {
await this.configurationCache.write(this.cacheKey, JSON.stringify(cachedConfigurationDefaultsOverrides));
} else {
await this.configurationCache.remove(this.cacheKey);
}
} catch (error) {/* Ignore error */ }
}
}
export class UserConfiguration extends Disposable {

View file

@ -1,26 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IConfigurationCache, ConfigurationKey } from 'vs/workbench/services/configuration/common/configuration';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
export class ConfigurationCache implements IConfigurationCache {
needsCaching(resource: URI): boolean {
// Cache all non user data resources
return ![Schemas.file, Schemas.userData, Schemas.tmp].includes(resource.scheme);
}
async read(key: ConfigurationKey): Promise<string> {
return '';
}
async write(key: ConfigurationKey, content: string): Promise<void> {
}
async remove(key: ConfigurationKey): Promise<void> {
}
}

View file

@ -11,8 +11,8 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { Queue, Barrier, runWhenIdle, Promises } from 'vs/base/common/async';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { IWorkspaceContextService, Workspace as BaseWorkspace, WorkbenchState, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkspaceFolder, toWorkspaceFolder, isWorkspaceFolder, IWorkspaceFoldersWillChangeEvent } from 'vs/platform/workspace/common/workspace';
import { ConfigurationModel, DefaultConfigurationModel, ConfigurationChangeEvent, AllKeysConfigurationChangeEvent, mergeChanges } from 'vs/platform/configuration/common/configurationModels';
import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange, ConfigurationTargetToString, IConfigurationUpdateOverrides, isConfigurationUpdateOverrides } from 'vs/platform/configuration/common/configuration';
import { ConfigurationModel, ConfigurationChangeEvent, AllKeysConfigurationChangeEvent, mergeChanges } from 'vs/platform/configuration/common/configurationModels';
import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange, ConfigurationTargetToString, IConfigurationUpdateOverrides, isConfigurationUpdateOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Configuration } from 'vs/workbench/services/configuration/common/configurationModels';
import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, IWorkbenchConfigurationService, RestrictedSettings } from 'vs/workbench/services/configuration/common/configuration';
import { Registry } from 'vs/platform/registry/common/platform';
@ -20,7 +20,7 @@ import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resour
import { IWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IWorkspaceInitializationPayload, IEmptyWorkspaceIdentifier, useSlashForPath, getStoredWorkspaceFolder, isSingleFolderWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, toWorkspaceFolders } from 'vs/platform/workspaces/common/workspaces';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ConfigurationEditingService, EditableConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditingService';
import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration, UserConfiguration } from 'vs/workbench/services/configuration/browser/configuration';
import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration, UserConfiguration, DefaultConfiguration } from 'vs/workbench/services/configuration/browser/configuration';
import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService';
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
import { mark } from 'vs/base/common/performance';
@ -35,6 +35,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
import { delta, distinct } from 'vs/base/common/arrays';
import { forEach, IStringDictionary } from 'vs/base/common/collections';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
class Workspace extends BaseWorkspace {
initialized: boolean = false;
@ -50,7 +51,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat
private readonly configurationCache: IConfigurationCache;
private _configuration: Configuration;
private initialized: boolean = false;
private defaultConfiguration: DefaultConfigurationModel;
private defaultConfiguration: DefaultConfiguration;
private localUserConfiguration: UserConfiguration;
private remoteUserConfiguration: RemoteUserConfiguration | null = null;
private workspaceConfiguration: WorkspaceConfiguration;
@ -102,20 +103,15 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat
super();
this.configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
// register defaults before creating default configuration model
// so that the model is not required to be updated after registering
if (environmentService.options?.configurationDefaults) {
this.configurationRegistry.registerDefaultConfigurations([environmentService.options.configurationDefaults]);
}
this.initRemoteUserConfigurationBarrier = new Barrier();
this.completeWorkspaceBarrier = new Barrier();
this.defaultConfiguration = new DefaultConfigurationModel();
this.defaultConfiguration = new DefaultConfiguration(configurationCache, environmentService);
this.configurationCache = configurationCache;
this.fileService = fileService;
this.uriIdentityService = uriIdentityService;
this.logService = logService;
this._configuration = new Configuration(this.defaultConfiguration, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), this.workspace);
this._configuration = new Configuration(this.defaultConfiguration.configurationModel, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), this.workspace);
this.cachedFolderConfigs = new ResourceMap<FolderConfiguration>();
this.localUserConfiguration = this._register(new UserConfiguration(environmentService.settingsResource, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService, uriIdentityService, logService));
this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration)));
@ -138,7 +134,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat
});
}));
this._register(this.configurationRegistry.onDidUpdateConfiguration(configurationProperties => this.onDefaultConfigurationChanged(configurationProperties)));
this._register(this.defaultConfiguration.onDidChangeConfiguration(configurationModel => this.onDefaultConfigurationChanged(configurationModel)));
this.workspaceEditingQueue = new Queue<void>();
}
@ -350,6 +346,10 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat
}
switch (target) {
case ConfigurationTarget.DEFAULT:
await this.reloadDefaultConfiguration();
return;
case ConfigurationTarget.USER:
const { local, remote } = await this.reloadUserConfiguration();
await this.loadConfiguration(local, remote);
@ -566,6 +566,8 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat
}
private async initializeConfiguration(): Promise<void> {
await this.defaultConfiguration.initialize();
mark('code/willInitUserConfiguration');
const { local, remote } = await this.initializeUserConfiguration();
mark('code/didInitUserConfiguration');
@ -580,6 +582,10 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat
return { local, remote };
}
private async reloadDefaultConfiguration(): Promise<void> {
this.onDefaultConfigurationChanged(this.defaultConfiguration.reload());
}
private async reloadUserConfiguration(): Promise<{ local: ConfigurationModel, remote: ConfigurationModel }> {
const [local, remote] = await Promise.all([this.reloadLocalUserConfiguration(true), this.reloadRemoteUserConfiguration(true)]);
return { local, remote };
@ -630,7 +636,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat
folderConfigurations.forEach((folderConfiguration, index) => folderConfigurationModels.set(folders[index].uri, folderConfiguration));
const currentConfiguration = this._configuration;
this._configuration = new Configuration(this.defaultConfiguration, userConfigurationModel, remoteUserConfigurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), this.workspace);
this._configuration = new Configuration(this.defaultConfiguration.configurationModel, userConfigurationModel, remoteUserConfigurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), this.workspace);
if (this.initialized) {
const change = this._configuration.compare(currentConfiguration);
@ -654,11 +660,10 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat
}
}
private onDefaultConfigurationChanged(keys: string[]): void {
this.defaultConfiguration = new DefaultConfigurationModel();
private onDefaultConfigurationChanged(configurationModel: ConfigurationModel): void {
if (this.workspace) {
const previousData = this._configuration.toData();
const change = this._configuration.compareAndUpdateDefaultConfiguration(this.defaultConfiguration, keys);
const change = this._configuration.compareAndUpdateDefaultConfiguration(configurationModel);
if (this.remoteUserConfiguration) {
this._configuration.updateLocalUserConfiguration(this.localUserConfiguration.reparse());
this._configuration.updateRemoteUserConfiguration(this.remoteUserConfiguration.reparse());
@ -1101,5 +1106,16 @@ class RegisterConfigurationSchemasContribution extends Disposable implements IWo
}
}
class ResetConfigurationDefaultsOverridesCache extends Disposable implements IWorkbenchContribution {
constructor(
@IConfigurationService configurationService: IConfigurationService,
@IExtensionService extensionService: IExtensionService,
) {
super();
extensionService.whenInstalledExtensionsRegistered().then(() => configurationService.reloadConfiguration(ConfigurationTarget.DEFAULT));
}
}
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchContributionsRegistry.registerWorkbenchContribution(RegisterConfigurationSchemasContribution, LifecyclePhase.Restored);
workbenchContributionsRegistry.registerWorkbenchContribution(ResetConfigurationDefaultsOverridesCache, LifecyclePhase.Ready);

View file

@ -36,7 +36,7 @@ WORKSPACE_STANDALONE_CONFIGURATIONS[LAUNCH_CONFIGURATION_KEY] = `${FOLDER_CONFIG
export const USER_STANDALONE_CONFIGURATIONS = Object.create(null);
USER_STANDALONE_CONFIGURATIONS[TASKS_CONFIGURATION_KEY] = `${TASKS_CONFIGURATION_KEY}.json`;
export type ConfigurationKey = { type: 'user' | 'workspaces' | 'folder', key: string };
export type ConfigurationKey = { type: 'defaults' | 'user' | 'workspaces' | 'folder', key: string };
export interface IConfigurationCache {

View file

@ -5,22 +5,28 @@
import { IConfigurationCache, ConfigurationKey } from 'vs/workbench/services/configuration/common/configuration';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files';
import { joinPath } from 'vs/base/common/resources';
import { VSBuffer } from 'vs/base/common/buffer';
import { Queue } from 'vs/base/common/async';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
export class ConfigurationCache implements IConfigurationCache {
private readonly cacheHome: URI;
private readonly cachedConfigurations: Map<string, CachedConfiguration> = new Map<string, CachedConfiguration>();
constructor(private readonly cacheHome: URI, private readonly fileService: IFileService) {
constructor(
private readonly donotCacheResourcesWithSchemes: string[],
environmentService: IEnvironmentService,
private readonly fileService: IFileService
) {
this.cacheHome = environmentService.cacheHome;
}
needsCaching(resource: URI): boolean {
// Cache all non native resources
return ![Schemas.file, Schemas.userData].includes(resource.scheme);
return !this.donotCacheResourcesWithSchemes.includes(resource.scheme);
}
read(key: ConfigurationKey): Promise<string> {

View file

@ -0,0 +1,146 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { Event } from 'vs/base/common/event';
import { joinPath } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
import { DefaultConfiguration } from 'vs/workbench/services/configuration/browser/configuration';
import { ConfigurationKey, IConfigurationCache } from 'vs/workbench/services/configuration/common/configuration';
import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { TestEnvironmentService, TestProductService } from 'vs/workbench/test/browser/workbenchTestServices';
export class ConfigurationCache implements IConfigurationCache {
private readonly cache = new Map<string, string>();
needsCaching(resource: URI): boolean { return false; }
async read({ type, key }: ConfigurationKey): Promise<string> { return this.cache.get(`${type}:${key}`) || ''; }
async write({ type, key }: ConfigurationKey, content: string): Promise<void> { this.cache.set(`${type}:${key}`, content); }
async remove({ type, key }: ConfigurationKey): Promise<void> { this.cache.delete(`${type}:${key}`); }
}
suite('DefaultConfiguration', () => {
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
const cacheKey: ConfigurationKey = { type: 'defaults', key: 'configurationDefaultsOverrides' };
let configurationCache: ConfigurationCache;
setup(() => {
configurationCache = new ConfigurationCache();
configurationRegistry.registerConfiguration({
'id': 'test.configurationDefaultsOverride',
'type': 'object',
'properties': {
'test.configurationDefaultsOverride': {
'type': 'string',
'default': 'defaultValue',
}
}
});
});
teardown(() => {
configurationRegistry.deregisterConfigurations(configurationRegistry.getConfigurations());
configurationRegistry.deregisterDefaultConfigurations([configurationRegistry.getConfigurationDefaultsOverrides()]);
});
test('configuration default overrides are read from environment', async () => {
const environmentService = new BrowserWorkbenchEnvironmentService({ logsPath: joinPath(URI.file('tests').with({ scheme: 'vscode-tests' }), 'logs'), workspaceId: '', configurationDefaults: { 'test.configurationDefaultsOverride': 'envOverrideValue' } }, TestProductService);
const testObject = new DefaultConfiguration(configurationCache, environmentService);
assert.deepStrictEqual(testObject.configurationModel.getValue('test.configurationDefaultsOverride'), 'envOverrideValue');
});
test('configuration default overrides are read from cache', async () => {
await configurationCache.write(cacheKey, JSON.stringify({ 'test.configurationDefaultsOverride': 'overrideValue' }));
const testObject = new DefaultConfiguration(configurationCache, TestEnvironmentService);
const actual = await testObject.initialize();
assert.deepStrictEqual(actual.getValue('test.configurationDefaultsOverride'), 'overrideValue');
});
test('configuration default overrides read from cache override environment', async () => {
const environmentService = new BrowserWorkbenchEnvironmentService({ logsPath: joinPath(URI.file('tests').with({ scheme: 'vscode-tests' }), 'logs'), workspaceId: '', configurationDefaults: { 'test.configurationDefaultsOverride': 'envOverrideValue' } }, TestProductService);
await configurationCache.write(cacheKey, JSON.stringify({ 'test.configurationDefaultsOverride': 'overrideValue' }));
const testObject = new DefaultConfiguration(configurationCache, environmentService);
const actual = await testObject.initialize();
assert.deepStrictEqual(actual.getValue('test.configurationDefaultsOverride'), 'overrideValue');
});
test('configuration default overrides are read from cache when default configuration changed', async () => {
await configurationCache.write(cacheKey, JSON.stringify({ 'test.configurationDefaultsOverride': 'overrideValue' }));
const testObject = new DefaultConfiguration(configurationCache, TestEnvironmentService);
await testObject.initialize();
const promise = Event.toPromise(testObject.onDidChangeConfiguration);
configurationRegistry.registerConfiguration({
'id': 'test.configurationDefaultsOverride',
'type': 'object',
'properties': {
'test.configurationDefaultsOverride1': {
'type': 'string',
'default': 'defaultValue',
}
}
});
const actual = await promise;
assert.deepStrictEqual(actual.getValue('test.configurationDefaultsOverride'), 'overrideValue');
});
test('configuration default overrides are not read from cache after reload', async () => {
await configurationCache.write(cacheKey, JSON.stringify({ 'test.configurationDefaultsOverride': 'overrideValue' }));
const testObject = new DefaultConfiguration(configurationCache, TestEnvironmentService);
await testObject.initialize();
const actual = testObject.reload();
assert.deepStrictEqual(actual.getValue('test.configurationDefaultsOverride'), 'defaultValue');
});
test('cache is reset after reload', async () => {
await configurationCache.write(cacheKey, JSON.stringify({ 'test.configurationDefaultsOverride': 'overrideValue' }));
const testObject = new DefaultConfiguration(configurationCache, TestEnvironmentService);
await testObject.initialize();
testObject.reload();
assert.deepStrictEqual(await configurationCache.read(cacheKey), '');
});
test('configuration default overrides are written in cache', async () => {
const testObject = new DefaultConfiguration(configurationCache, TestEnvironmentService);
await testObject.initialize();
const promise = Event.toPromise(testObject.onDidChangeConfiguration);
configurationRegistry.registerDefaultConfigurations([{ 'test.configurationDefaultsOverride': 'newoverrideValue' }]);
await promise;
const actual = JSON.parse(await configurationCache.read(cacheKey));
assert.deepStrictEqual(actual, { 'test.configurationDefaultsOverride': 'newoverrideValue' });
});
test('configuration default overrides are removed from cache if there are no overrides', async () => {
const testObject = new DefaultConfiguration(configurationCache, TestEnvironmentService);
await testObject.initialize();
const promise = Event.toPromise(testObject.onDidChangeConfiguration);
configurationRegistry.registerConfiguration({
'id': 'test.configurationDefaultsOverride',
'type': 'object',
'properties': {
'test.configurationDefaultsOverride1': {
'type': 'string',
'default': 'defaultValue',
}
}
});
await promise;
assert.deepStrictEqual(await configurationCache.read(cacheKey), '');
});
});

View file

@ -14,7 +14,7 @@ import * as uuid from 'vs/base/common/uuid';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService';
import { ConfigurationEditingService, ConfigurationEditingErrorCode, EditableConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditingService';
import { WORKSPACE_STANDALONE_CONFIGURATIONS, FOLDER_SETTINGS_PATH, USER_STANDALONE_CONFIGURATIONS } from 'vs/workbench/services/configuration/common/configuration';
import { WORKSPACE_STANDALONE_CONFIGURATIONS, FOLDER_SETTINGS_PATH, USER_STANDALONE_CONFIGURATIONS, IConfigurationCache } from 'vs/workbench/services/configuration/common/configuration';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
@ -36,7 +36,6 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
import { joinPath } from 'vs/base/common/resources';
import { VSBuffer } from 'vs/base/common/buffer';
import { ConfigurationCache } from 'vs/workbench/services/configuration/browser/configurationCache';
import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteAgentServiceImpl';
import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { getSingleFolderWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces';
@ -44,6 +43,13 @@ import { IUserConfigurationFileService, UserConfigurationFileService } from 'vs/
const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' });
export class ConfigurationCache implements IConfigurationCache {
needsCaching(resource: URI): boolean { return false; }
async read(): Promise<string> { return ''; }
async write(): Promise<void> { }
async remove(): Promise<void> { }
}
suite('ConfigurationEditingService', () => {
let instantiationService: TestInstantiationService;

View file

@ -40,7 +40,6 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService';
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
import { ConfigurationCache as BrowserConfigurationCache } from 'vs/workbench/services/configuration/browser/configurationCache';
import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteAgentServiceImpl';
import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remoteAuthorityResolverService';
@ -54,8 +53,11 @@ function convertToWorkspacePayload(folder: URI): ISingleFolderWorkspaceIdentifie
};
}
class ConfigurationCache extends BrowserConfigurationCache {
override needsCaching() { return false; }
export class ConfigurationCache implements IConfigurationCache {
needsCaching(resource: URI): boolean { return false; }
async read(): Promise<string> { return ''; }
async write(): Promise<void> { }
async remove(): Promise<void> { }
}
const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' });
@ -2191,44 +2193,6 @@ suite('WorkspaceConfigurationService - Remote Folder', () => {
});
suite('ConfigurationService - Configuration Defaults', () => {
const disposableStore: DisposableStore = new DisposableStore();
suiteSetup(() => {
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration({
'id': '_test',
'type': 'object',
'properties': {
'configurationService.defaultOverridesSetting': {
'type': 'string',
'default': 'isSet',
},
}
});
});
teardown(() => disposableStore.clear());
test('when default value is not overriden', () => {
const testObject = createConfigurationService({});
assert.deepStrictEqual(testObject.getValue('configurationService.defaultOverridesSetting'), 'isSet');
});
test('when default value is overriden', () => {
const testObject = createConfigurationService({ 'configurationService.defaultOverridesSetting': 'overriddenValue' });
assert.deepStrictEqual(testObject.getValue('configurationService.defaultOverridesSetting'), 'overriddenValue');
});
function createConfigurationService(configurationDefaults: Record<string, any>): IConfigurationService {
const remoteAgentService = (<TestInstantiationService>workbenchInstantiationService(undefined, disposableStore)).createInstance(RemoteAgentService, null);
const environmentService = new BrowserWorkbenchEnvironmentService({ logsPath: joinPath(ROOT, 'logs'), workspaceId: '', configurationDefaults }, TestProductService);
const fileService = new FileService(new NullLogService());
return disposableStore.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService()));
}
});
function getWorkspaceId(configPath: URI): string {
let workspaceConfigPath = configPath.toString();
if (!isLinux) {

View file

@ -128,6 +128,9 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
@memoize
get snippetsHome(): URI { return joinPath(this.userRoamingDataHome, 'snippets'); }
@memoize
get cacheHome(): URI { return joinPath(this.userRoamingDataHome, 'caches'); }
@memoize
get globalStorageHome(): URI { return URI.joinPath(this.userRoamingDataHome, 'globalStorage'); }

View file

@ -1197,9 +1197,6 @@ class ProposedApiController {
}
extension.enabledApiProposals = productEnabledProposals;
// todo@jrieken REMOVE, legacy flag is turned on
extension.enableProposedApi = true;
return;
}
@ -1209,11 +1206,10 @@ class ProposedApiController {
return;
}
if (!extension.isBuiltin && (extension.enableProposedApi || isNonEmptyArray(extension.enabledApiProposals))) {
if (!extension.isBuiltin && isNonEmptyArray(extension.enabledApiProposals)) {
// restrictive: extension cannot use proposed API in this context and its declaration is nulled
this._logService.critical(`Extension '${extension.identifier.value} CANNOT USE these API proposals '${extension.enabledApiProposals?.join(', ') ?? '*'}'. You MUST start in extension development mode or use the --enable-proposed-api command line flag`);
extension.enabledApiProposals = [];
extension.enableProposedApi = false;
}
}
}

View file

@ -19,7 +19,6 @@ export const nullExtensionDescription = Object.freeze(<IExtensionDescription>{
name: 'Null Extension Description',
version: '0.0.0',
publisher: 'vscode',
enableProposedApi: false,
engines: { vscode: '' },
extensionLocation: URI.parse('void:location'),
isBuiltin: false,
@ -135,10 +134,10 @@ export interface IExtensionHost {
}
export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): boolean {
if (extension.enabledApiProposals?.includes(proposal)) {
return true;
if (!extension.enabledApiProposals) {
return false;
}
return Boolean(extension.enableProposedApi);
return extension.enabledApiProposals.includes(proposal);
}
export function checkProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): void {

View file

@ -309,7 +309,6 @@ export interface IRelaxedExtensionDescription {
vscode: string;
};
main?: string;
enableProposedApi?: boolean;
}
class ExtensionManifestValidator extends ExtensionManifestHandler {

View file

@ -322,6 +322,9 @@ suite('Encoding', () => {
});
test('toDecodeStream - decodes buffer entirely', async function () {
if (!process.versions.electron) {
this.skip(); // TODO@bpasero enable once we ship Electron 16
}
const emojis = Buffer.from('🖥️💻💾');
const incompleteEmojis = emojis.slice(0, emojis.length - 1);

View file

@ -24,7 +24,7 @@ import { getDocumentSymbols } from 'vs/editor/contrib/documentSymbols/documentSy
import * as modes from 'vs/editor/common/modes';
import { getCodeLensModel } from 'vs/editor/contrib/codelens/codelens';
import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition, getDeclarationsAtPosition, getReferencesAtPosition } from 'vs/editor/contrib/gotoSymbol/goToSymbol';
import { getHover } from 'vs/editor/contrib/hover/getHover';
import { getHoverPromise } from 'vs/editor/contrib/hover/getHover';
import { getOccurrencesAtPosition } from 'vs/editor/contrib/wordHighlighter/wordHighlighter';
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { getWorkspaceSymbols } from 'vs/workbench/contrib/search/common/search';
@ -374,7 +374,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
getHover(model, new EditorPosition(1, 1), CancellationToken.None).then(value => {
getHoverPromise(model, new EditorPosition(1, 1), CancellationToken.None).then(value => {
assert.strictEqual(value.length, 1);
let [entry] = value;
assert.deepStrictEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 5 });
@ -391,7 +391,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
getHover(model, new EditorPosition(1, 1), CancellationToken.None).then(value => {
getHoverPromise(model, new EditorPosition(1, 1), CancellationToken.None).then(value => {
assert.strictEqual(value.length, 1);
let [entry] = value;
assert.deepStrictEqual(entry.range, { startLineNumber: 4, startColumn: 1, endLineNumber: 9, endColumn: 8 });
@ -414,7 +414,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
const value = await getHover(model, new EditorPosition(1, 1), CancellationToken.None);
const value = await getHoverPromise(model, new EditorPosition(1, 1), CancellationToken.None);
assert.strictEqual(value.length, 2);
let [first, second] = (value as modes.Hover[]);
assert.strictEqual(first.contents[0].value, 'registered second');
@ -436,7 +436,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
getHover(model, new EditorPosition(1, 1), CancellationToken.None).then(value => {
getHoverPromise(model, new EditorPosition(1, 1), CancellationToken.None).then(value => {
assert.strictEqual(value.length, 1);
});
});

View file

@ -145,6 +145,7 @@ import { FindReplaceState } from 'vs/editor/contrib/find/findState';
import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput';
import { DeserializedTerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorSerializer';
import { IGroupChangeEvent } from 'vs/workbench/common/editor/editorGroupModel';
import { env } from 'vs/base/common/process';
export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput {
return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined, undefined, undefined, undefined);
@ -1824,7 +1825,7 @@ export class TestTerminalProfileResolverService implements ITerminalProfileResol
async getDefaultProfile(options: IShellLaunchConfigResolveOptions): Promise<ITerminalProfile> { return { path: '/default', profileName: 'Default', isDefault: true }; }
async getDefaultShell(options: IShellLaunchConfigResolveOptions): Promise<string> { return '/default'; }
async getDefaultShellArgs(options: IShellLaunchConfigResolveOptions): Promise<string | string[]> { return []; }
async getEnvironment(): Promise<IProcessEnvironment> { return process.env; }
async getEnvironment(): Promise<IProcessEnvironment> { return env; }
getSafeConfigValue(key: string, os: OperatingSystem): unknown | undefined { return undefined; }
getSafeConfigValueFullKey(key: string): unknown | undefined { return undefined; }
createProfileFromShellAndShellArgs(shell?: unknown, shellArgs?: unknown): Promise<string | ITerminalProfile> { throw new Error('Method not implemented.'); }

View file

@ -39,7 +39,7 @@ export class QuickAccess {
}
}
async openFile(fileName: string): Promise<void> {
async openQuickAccessAndWait(fileName: string, exactMatch?: boolean): Promise<void> {
let retries = 0;
let fileFound = false;
while (++retries < 10) {
@ -49,7 +49,7 @@ export class QuickAccess {
await this.quickInput.waitForQuickInputElements(names => {
const name = names[0];
if (name === fileName) {
if (exactMatch && name === fileName) {
fileFound = true;
return true;
}
@ -59,7 +59,12 @@ export class QuickAccess {
return true;
}
return false;
if (!exactMatch) {
fileFound = true;
return !!name;
} else {
return false;
}
});
if (!retry) {
@ -73,6 +78,10 @@ export class QuickAccess {
if (!fileFound) {
throw new Error(`Quick open file search was unable to find '${fileName}' after 10 attempts, giving up.`);
}
}
async openFile(fileName: string): Promise<void> {
await this.openQuickAccessAndWait(fileName, true);
await this.code.dispatchKeybinding('enter');
await this.editors.waitForActiveTab(fileName);

View file

@ -26,7 +26,8 @@ export enum TerminalCommandIdWithValue {
ChangeColor = 'workbench.action.terminal.changeColor',
ChangeIcon = 'workbench.action.terminal.changeIcon',
NewWithProfile = 'workbench.action.terminal.newWithProfile',
SelectDefaultProfile = 'workbench.action.terminal.selectDefaultShell'
SelectDefaultProfile = 'workbench.action.terminal.selectDefaultShell',
AttachToSession = 'workbench.action.terminal.attachToSession',
}
export enum TerminalCommandId {
@ -41,7 +42,8 @@ export enum TerminalCommandId {
MoveToPanel = 'workbench.action.terminal.moveToTerminalPanel',
MoveToEditor = 'workbench.action.terminal.moveToEditor',
NewWithProfile = 'workbench.action.terminal.newWithProfile',
SelectDefaultProfile = 'workbench.action.terminal.selectDefaultShell'
SelectDefaultProfile = 'workbench.action.terminal.selectDefaultShell',
DetachSession = 'workbench.action.terminal.detachSession',
}
interface TerminalLabel {
name?: string,
@ -111,6 +113,27 @@ export class Terminal {
}
}
async getTerminalGroups(): Promise<TerminalGroup[]> {
const tabCount = (await this.code.waitForElements(Selector.Tabs, true)).length;
console.log('tabCount', tabCount);
const groups: TerminalGroup[] = [];
for (let i = 0; i < tabCount; i++) {
const instance = await this.code.waitForElement(`${Selector.Tabs}[data-index="${i}"] ${Selector.TabsEntry}`);
console.log('instance', instance);
const label: TerminalLabel = {
name: instance.textContent.replace(/^[├┌└]\s*/, '')
};
// It's a new group if the the tab does not start with ├ or └
if (instance.textContent.match(/^[├└]/)) {
groups[groups.length - 1].push(label);
} else {
groups.push([label]);
}
}
console.log('groups', groups);
return groups;
}
async getSingleTabName(): Promise<string> {
return await (await this.code.waitForElement(Selector.SingleTab, singleTab => !!singleTab && singleTab?.textContent.length > 1)).textContent;
}
@ -139,6 +162,10 @@ export class Terminal {
}
}
async assertTerminalViewHidden(): Promise<void> {
await this.code.waitForElement(Selector.TerminalView, result => result === undefined);
}
async clickPlusButton(): Promise<void> {
await this.code.waitAndClick(Selector.PlusButton);
}

View file

@ -88,7 +88,7 @@ export function setup(opts: minimist.ParsedArgs) {
'jsconfig.json'
];
await app.workbench.quickaccess.openQuickAccess('.js');
await app.workbench.quickaccess.openQuickAccessAndWait('.js');
await app.workbench.quickinput.waitForQuickInputElements(names => expectedNames.every(n => names.some(m => n === m)));
await app.code.dispatchKeybinding('escape');
});
@ -101,7 +101,7 @@ export function setup(opts: minimist.ParsedArgs) {
'package.json'
];
await app.workbench.quickaccess.openQuickAccess('a.s');
await app.workbench.quickaccess.openQuickAccessAndWait('a.s');
await app.workbench.quickinput.waitForQuickInputElements(names => expectedNames.every(n => names.some(m => n === m)));
await app.code.dispatchKeybinding('escape');
});

View file

@ -4,22 +4,16 @@
*--------------------------------------------------------------------------------------------*/
import { ParsedArgs } from 'minimist';
import { Terminal, TerminalCommandId, TerminalCommandIdWithValue } from '../../../../automation/out';
import { afterSuite, beforeSuite } from '../../utils';
import { Application, Terminal, TerminalCommandId, TerminalCommandIdWithValue } from '../../../../automation/out';
export function setup(opts: ParsedArgs) {
describe('Terminal Editors', () => {
let terminal: Terminal;
beforeSuite(opts);
afterSuite(opts);
before(function () {
terminal = this.app.workbench.terminal;
});
afterEach(async () => {
await terminal.runCommand(TerminalCommandId.KillAll);
// Acquire automation API
before(async function () {
const app = this.app as Application;
terminal = app.workbench.terminal;
});
// TODO: This was flaky in CI
@ -30,7 +24,8 @@ export function setup(opts: ParsedArgs) {
await terminal.assertSingleTab({ color }, true);
});
it('should update icon of the tab', async () => {
// TODO: Flaky https://github.com/microsoft/vscode/issues/137808
it.skip('should update icon of the tab', async () => {
await terminal.runCommand(TerminalCommandId.CreateNewEditor);
const icon = 'symbol-method';
await terminal.runCommandWithValue(TerminalCommandIdWithValue.ChangeIcon, icon);

View file

@ -0,0 +1,102 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ParsedArgs } from 'minimist';
import { Application, Terminal, TerminalCommandId, TerminalCommandIdWithValue } from '../../../../automation/out';
export function setup(opts: ParsedArgs) {
describe('Terminal Persistence', () => {
// Acquire automation API
let terminal: Terminal;
before(function () {
const app = this.app as Application;
terminal = app.workbench.terminal;
});
describe('detach/attach', () => {
// https://github.com/microsoft/vscode/issues/137799
it.skip('should support basic reconnection', async () => {
await terminal.runCommand(TerminalCommandId.CreateNew);
// TODO: Handle passing in an actual regex, not string
await terminal.assertTerminalGroups([
[{ name: '.*' }]
]);
// Get the terminal name
await terminal.assertTerminalGroups([
[{ name: '.*' }]
]);
const name = (await terminal.getTerminalGroups())[0][0].name!;
// Detach
await terminal.runCommand(TerminalCommandId.DetachSession);
await terminal.assertTerminalViewHidden();
// Attach
await terminal.runCommandWithValue(TerminalCommandIdWithValue.AttachToSession, name);
await terminal.assertTerminalGroups([
[{ name }]
]);
});
it('should persist buffer content', async () => {
await terminal.runCommand(TerminalCommandId.CreateNew);
// TODO: Handle passing in an actual regex, not string
await terminal.assertTerminalGroups([
[{ name: '.*' }]
]);
// Get the terminal name
await terminal.assertTerminalGroups([
[{ name: '.*' }]
]);
const name = (await terminal.getTerminalGroups())[0][0].name!;
// Write in terminal
await terminal.runCommandInTerminal('echo terminal_test_content');
await terminal.waitForTerminalText(buffer => buffer.some(e => e.includes('terminal_test_content')));
// Detach
await terminal.runCommand(TerminalCommandId.DetachSession);
await terminal.assertTerminalViewHidden();
// Attach
await terminal.runCommandWithValue(TerminalCommandIdWithValue.AttachToSession, name);
await terminal.assertTerminalGroups([
[{ name }]
]);
await terminal.waitForTerminalText(buffer => buffer.some(e => e.includes('terminal_test_content')));
});
// TODO: This is currently flaky because it takes time to send over the new icon to the backend
it.skip('should persist terminal icon', async () => {
await terminal.runCommand(TerminalCommandId.CreateNew);
// TODO: Handle passing in an actual regex, not string
await terminal.assertTerminalGroups([
[{ name: '.*' }]
]);
// Get the terminal name
const name = (await terminal.getTerminalGroups())[0][0].name!;
// Set the icon
await terminal.runCommandWithValue(TerminalCommandIdWithValue.ChangeIcon, 'symbol-method');
await terminal.assertSingleTab({ icon: 'symbol-method' });
// Detach
await terminal.runCommand(TerminalCommandId.DetachSession);
await terminal.assertTerminalViewHidden();
// Attach
await terminal.runCommandWithValue(TerminalCommandIdWithValue.AttachToSession, name);
await terminal.assertTerminalGroups([
[{ name }]
]);
// TODO: This fails due to a bug
await terminal.assertSingleTab({ icon: 'symbol-method' });
});
});
});
}

View file

@ -4,25 +4,18 @@
*--------------------------------------------------------------------------------------------*/
import { ParsedArgs } from 'minimist';
import { Terminal, TerminalCommandId, TerminalCommandIdWithValue } from '../../../../automation';
import { afterSuite, beforeSuite } from '../../utils';
import { Application, Terminal, TerminalCommandId, TerminalCommandIdWithValue } from '../../../../automation';
const CONTRIBUTED_PROFILE_NAME = `JavaScript Debug Terminal`;
const ANY_PROFILE_NAME = '^((?!JavaScript Debug Terminal).)*$';
export function setup(opts: ParsedArgs) {
describe('Terminal Profiles', () => {
// Acquire automation API
let terminal: Terminal;
beforeSuite(opts);
afterSuite(opts);
before(function () {
terminal = this.app.workbench.terminal;
});
afterEach(async () => {
await terminal.runCommand(TerminalCommandId.KillAll);
const app = this.app as Application;
terminal = app.workbench.terminal;
});
it('should launch the default profile', async () => {

View file

@ -1,20 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ParsedArgs } from 'minimist';
import { Application } from '../../../../automation';
import { afterSuite, beforeSuite } from '../../utils';
export function setup(opts: ParsedArgs) {
describe('Terminal Reconnection', () => {
beforeSuite(opts);
afterSuite(opts);
it.skip('should reconnect to a single terminal on reload', async () => {
const app = this.app as Application;
console.log(app);
});
});
}

View file

@ -5,22 +5,15 @@
import { ParsedArgs } from 'minimist';
import { Terminal, TerminalCommandId, TerminalCommandIdWithValue } from '../../../../automation/out';
import { afterSuite, beforeSuite } from '../../utils';
import { Application, Terminal, TerminalCommandId, TerminalCommandIdWithValue } from '../../../../automation/out';
export function setup(opts: ParsedArgs) {
describe('Terminal Tabs', () => {
// Acquire automation API
let terminal: Terminal;
beforeSuite(opts);
afterSuite(opts);
before(function () {
terminal = this.app.workbench.terminal;
});
afterEach(async () => {
await terminal.runCommand(TerminalCommandId.KillAll);
const app = this.app as Application;
terminal = app.workbench.terminal;
});
it('clicking the plus button should create a terminal and display the tabs view showing no split decorations', async () => {
@ -67,7 +60,8 @@ export function setup(opts: ParsedArgs) {
await terminal.assertSingleTab({ name });
});
it('should reset the tab name to the default value when no name is provided', async () => {
// Flaky: https://github.com/microsoft/vscode/issues/137795
it.skip('should reset the tab name to the default value when no name is provided', async () => {
await terminal.runCommand(TerminalCommandId.Show);
const defaultName = await terminal.getSingleTabName();
const name = 'my terminal name';

View file

@ -0,0 +1,47 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import minimist = require('minimist');
import { Application, Terminal, TerminalCommandId } from '../../../../automation/out';
import { afterSuite, beforeSuite } from '../../utils';
import { setup as setupTerminalEditorsTests } from './terminal-editors.test';
import { setup as setupTerminalPersistenceTests } from './terminal-persistence.test';
import { setup as setupTerminalProfileTests } from './terminal-profiles.test';
import { setup as setupTerminalTabsTests } from './terminal-tabs.test';
export function setup(opts: minimist.ParsedArgs) {
describe('Terminal', () => {
// TODO: Enable terminal tests for non-web when the desktop driver is moved to playwright
if (!opts.web) {
return;
}
beforeSuite(opts);
afterSuite(opts);
let terminal: Terminal;
before(async function () {
// Fetch terminal automation API
const app = this.app as Application;
terminal = app.workbench.terminal;
// Always show tabs to make getting terminal groups easier
await app.workbench.settingsEditor.addUserSetting('terminal.integrated.tabs.hideCondition', '"never"');
// Close the settings editor
await app.workbench.quickaccess.runCommand('workbench.action.closeAllEditors');
});
afterEach(async () => {
// Kill all terminals between every test for a consistent testing environment
await terminal.runCommand(TerminalCommandId.KillAll);
});
setupTerminalEditorsTests(opts);
setupTerminalPersistenceTests(opts);
setupTerminalProfileTests(opts);
setupTerminalTabsTests(opts);
});
}

View file

@ -28,9 +28,7 @@ import { setup as setupExtensionTests } from './areas/extensions/extensions.test
import { setup as setupMultirootTests } from './areas/multiroot/multiroot.test';
import { setup as setupLocalizationTests } from './areas/workbench/localization.test';
import { setup as setupLaunchTests } from './areas/workbench/launch.test';
import { setup as setupTerminalProfileTests } from './areas/terminal/terminal-profiles.test';
import { setup as setupTerminalTabsTests } from './areas/terminal/terminal-tabs.test';
import { setup as setupTerminalEditorsTests } from './areas/terminal/terminal-editors.test';
import { setup as setupTerminalTests } from './areas/terminal/terminal.test';
try {
gracefulify(fs);
@ -361,14 +359,10 @@ describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => {
setupNotebookTests(opts);
setupLanguagesTests(opts);
setupEditorTests(opts);
setupTerminalTests(opts);
setupStatusbarTests(opts);
setupExtensionTests(opts);
if (!opts.web) { setupMultirootTests(opts); }
if (!opts.web) { setupLocalizationTests(opts); }
if (!opts.web) { setupLaunchTests(opts); }
// TODO: Enable terminal tests for non-web
if (opts.web) { setupTerminalProfileTests(opts); }
if (opts.web) { setupTerminalTabsTests(opts); }
if (opts.web) { setupTerminalEditorsTests(opts); }
});

View file

@ -1,49 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const yaserver = require('yaserver');
const http = require('http');
const glob = require('glob');
const path = require('path');
const fs = require('fs');
const REPO_ROOT = path.join(__dirname, '../../../');
const PORT = 8887;
function template(str, env) {
return str.replace(/{{\s*([\w_\-]+)\s*}}/g, function (all, part) {
return env[part];
});
}
yaserver.createServer({ rootDir: REPO_ROOT }).then((staticServer) => {
const server = http.createServer((req, res) => {
if (req.url === '' || req.url === '/') {
glob('**/vs/{base,platform,editor}/**/test/{common,browser}/**/*.test.js', {
cwd: path.join(REPO_ROOT, 'out'),
// ignore: ['**/test/{node,electron*}/**/*.js']
}, function (err, files) {
if (err) { console.log(err); process.exit(0); }
var modules = files
.map(function (file) { return file.replace(/\.js$/, ''); });
fs.readFile(path.join(__dirname, 'index.html'), 'utf8', function (err, templateString) {
if (err) { console.log(err); process.exit(0); }
res.end(template(templateString, {
modules: JSON.stringify(modules)
}));
});
});
} else {
return staticServer.handle(req, res);
}
});
server.listen(PORT, () => {
console.log(`http://localhost:${PORT}/`);
});
});

View file

@ -1,12 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
define([], function() {
return {
load: function(name, req, load) {
load({});
}
};
});

View file

@ -1,30 +0,0 @@
<html>
<head>
<meta charset="utf-8">
<title>VSCode Tests</title>
<link href="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.css" rel="stylesheet" />
</head>
<body>
<div id="mocha"></div>
<script src="/out/vs/loader.js"></script>
<script src="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.js"></script>
<script>
mocha.setup('tdd');
require.config({
baseUrl: '/out',
paths: {
assert: '/test/unit/assert.js',
sinon: '/node_modules/sinon/pkg/sinon.js',
'sinon-test': '/node_modules/sinon-test/dist/sinon-test.js'
}
});
require({{ modules }}, function () {
mocha.run();
});
</script>
</body>
</html>

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