mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 17:19:48 +01:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -25,7 +25,6 @@ const WEB_MAIN = path.join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench'
|
||||
|
||||
const args = minimist(process.argv, {
|
||||
boolean: [
|
||||
'watch',
|
||||
'no-launch',
|
||||
'help'
|
||||
],
|
||||
@@ -33,19 +32,20 @@ const args = minimist(process.argv, {
|
||||
'scheme',
|
||||
'host',
|
||||
'port',
|
||||
'local_port'
|
||||
'local_port',
|
||||
'extension'
|
||||
],
|
||||
});
|
||||
|
||||
if (args.help) {
|
||||
console.log(
|
||||
'yarn web [options]\n' +
|
||||
' --watch Watch extensions that require browser specific builds\n' +
|
||||
' --no-launch Do not open VSCode web in the browser\n' +
|
||||
' --scheme Protocol (https or http)\n' +
|
||||
' --host Remote host\n' +
|
||||
' --port Remote/Local port\n' +
|
||||
' --local_port Local port override\n' +
|
||||
' --no-launch Do not open VSCode web in the browser\n' +
|
||||
' --scheme Protocol (https or http)\n' +
|
||||
' --host Remote host\n' +
|
||||
' --port Remote/Local port\n' +
|
||||
' --local_port Local port override\n' +
|
||||
' --extension Path of an extension to include\n' +
|
||||
' --help\n' +
|
||||
'[Example]\n' +
|
||||
' yarn web --scheme https --host example.com --port 8080 --local_port 30000'
|
||||
@@ -61,76 +61,114 @@ const AUTHORITY = process.env.VSCODE_AUTHORITY || `${HOST}:${PORT}`;
|
||||
|
||||
const exists = (path) => util.promisify(fs.exists)(path);
|
||||
const readFile = (path) => util.promisify(fs.readFile)(path);
|
||||
const readdir = (path) => util.promisify(fs.readdir)(path);
|
||||
const readdirWithFileTypes = (path) => util.promisify(fs.readdir)(path, { withFileTypes: true });
|
||||
|
||||
let unbuiltExensions = [];
|
||||
|
||||
async function initialize() {
|
||||
async function getBuiltInExtensionInfos(extensionsRoot) {
|
||||
const builtinExtensions = [];
|
||||
|
||||
const children = await util.promisify(fs.readdir)(EXTENSIONS_ROOT, { withFileTypes: true });
|
||||
const folders = children.filter(c => !c.isFile());
|
||||
await Promise.all(folders.map(async folder => {
|
||||
const folderName = folder.name;
|
||||
const extensionPath = path.join(EXTENSIONS_ROOT, folderName);
|
||||
|
||||
let children = [];
|
||||
try {
|
||||
children = await util.promisify(fs.readdir)(extensionPath);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0];
|
||||
const readmePath = readme ? path.join(extensionPath, readme) : undefined;
|
||||
const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0];
|
||||
const changelogPath = changelog ? path.join(extensionPath, changelog) : undefined;
|
||||
|
||||
const packageJSONPath = path.join(EXTENSIONS_ROOT, folderName, 'package.json');
|
||||
if (await exists(packageJSONPath)) {
|
||||
try {
|
||||
let packageJSON = JSON.parse((await readFile(packageJSONPath)).toString());
|
||||
if (packageJSON.main && !packageJSON.browser) {
|
||||
return; // unsupported
|
||||
}
|
||||
|
||||
if (packageJSON.browser) {
|
||||
packageJSON.main = packageJSON.browser;
|
||||
|
||||
let mainFilePath = path.join(EXTENSIONS_ROOT, folderName, packageJSON.browser);
|
||||
if (path.extname(mainFilePath) !== '.js') {
|
||||
mainFilePath += '.js';
|
||||
}
|
||||
if (!await exists(mainFilePath)) {
|
||||
unbuiltExensions.push(path.relative(EXTENSIONS_ROOT, mainFilePath));
|
||||
}
|
||||
}
|
||||
packageJSON.extensionKind = ['web']; // enable for Web
|
||||
|
||||
const packageNLSPath = path.join(folderName, 'package.nls.json');
|
||||
const packageNLSExists = await exists(path.join(EXTENSIONS_ROOT, packageNLSPath));
|
||||
if (packageNLSExists) {
|
||||
packageJSON = extensions.translatePackageJSON(packageJSON, path.join(EXTENSIONS_ROOT, packageNLSPath)); // temporary, until fixed in core
|
||||
}
|
||||
builtinExtensions.push({
|
||||
extensionPath: folderName,
|
||||
packageJSON,
|
||||
packageNLSPath: packageNLSExists ? packageNLSPath : undefined,
|
||||
readmePath,
|
||||
changelogPath
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
const children = await readdirWithFileTypes(extensionsRoot);
|
||||
await Promise.all(children.map(async child => {
|
||||
if (child.isDirectory()) {
|
||||
const info = await getBuiltInExtensionInfo(path.join(extensionsRoot, child.name));
|
||||
if (info) {
|
||||
builtinExtensions.push(info);
|
||||
}
|
||||
}
|
||||
}));
|
||||
if (unbuiltExensions.length) {
|
||||
fancyLog(`${ansiColors.yellow('Warning')}: Make sure to run ${ansiColors.cyan('yarn gulp watch-web')}\nCould not find the following browser main files: \n${unbuiltExensions.join('\n')}`);
|
||||
}
|
||||
return builtinExtensions;
|
||||
}
|
||||
|
||||
const builtinExtensionsPromise = initialize();
|
||||
async function getBuiltInExtensionInfo(extensionPath) {
|
||||
const packageJSON = await getExtensionPackageJSON(extensionPath);
|
||||
if (!packageJSON) {
|
||||
return undefined;
|
||||
}
|
||||
const builtInExtensionPath = path.basename(extensionPath);
|
||||
|
||||
let children = [];
|
||||
try {
|
||||
children = await readdir(extensionPath);
|
||||
} catch (error) {
|
||||
console.log(`Can not read extension folder ${extensionPath}: ${error}`);
|
||||
return;
|
||||
}
|
||||
const readme = children.find(child => /^readme(\.txt|\.md|)$/i.test(child));
|
||||
const changelog = children.find(child => /^changelog(\.txt|\.md|)$/i.test(child));
|
||||
const packageJSONNLS = children.find(child => /^package.nls.json$/i.test(child));
|
||||
return {
|
||||
extensionPath: builtInExtensionPath,
|
||||
packageJSON,
|
||||
packageNLSPath: packageJSONNLS ? `${builtInExtensionPath}/${packageJSONNLS}` : undefined,
|
||||
readmePath: readme ? `${builtInExtensionPath}/${readme}` : undefined,
|
||||
changelogPath: changelog ? `${builtInExtensionPath}/${changelog}` : undefined
|
||||
};
|
||||
}
|
||||
|
||||
async function getDefaultExtensionInfos() {
|
||||
const extensions = [];
|
||||
const locations = {};
|
||||
|
||||
let extensionArg = args['extension'];
|
||||
if (!extensionArg) {
|
||||
return { extensions, locations }
|
||||
}
|
||||
|
||||
const extensionPaths = Array.isArray(extensionArg) ? extensionArg : [extensionArg];
|
||||
await Promise.all(extensionPaths.map(async extensionPath => {
|
||||
extensionPath = path.resolve(process.cwd(), extensionPath);
|
||||
const packageJSON = await getExtensionPackageJSON(extensionPath);
|
||||
if (packageJSON) {
|
||||
const extensionId = `${packageJSON.publisher}.${packageJSON.name}`;
|
||||
extensions.push({
|
||||
packageJSON,
|
||||
extensionLocation: { scheme: SCHEME, authority: AUTHORITY, path: `/extension/${extensionId}` }
|
||||
});
|
||||
locations[extensionId] = extensionPath;
|
||||
}
|
||||
}));
|
||||
return { extensions, locations };
|
||||
}
|
||||
|
||||
async function getExtensionPackageJSON(extensionPath) {
|
||||
|
||||
const packageJSONPath = path.join(extensionPath, 'package.json');
|
||||
if (await exists(packageJSONPath)) {
|
||||
try {
|
||||
let packageJSON = JSON.parse((await readFile(packageJSONPath)).toString());
|
||||
if (packageJSON.main && !packageJSON.browser) {
|
||||
return; // unsupported
|
||||
}
|
||||
|
||||
if (packageJSON.browser) {
|
||||
packageJSON.main = packageJSON.browser;
|
||||
|
||||
let mainFilePath = path.join(extensionPath, packageJSON.browser);
|
||||
if (path.extname(mainFilePath) !== '.js') {
|
||||
mainFilePath += '.js';
|
||||
}
|
||||
if (!await exists(mainFilePath)) {
|
||||
fancyLog(`${ansiColors.yellow('Warning')}: Could not find ${mainFilePath}. Use ${ansiColors.cyan('yarn gulp watch-web')} to build the built-in extensions.`);
|
||||
}
|
||||
}
|
||||
packageJSON.extensionKind = ['web']; // enable for Web
|
||||
|
||||
const packageNLSPath = path.join(extensionPath, 'package.nls.json');
|
||||
const packageNLSExists = await exists(packageNLSPath);
|
||||
if (packageNLSExists) {
|
||||
packageJSON = extensions.translatePackageJSON(packageJSON, packageNLSPath); // temporary, until fixed in core
|
||||
}
|
||||
|
||||
return packageJSON;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
const builtinExtensionsPromise = getBuiltInExtensionInfos(EXTENSIONS_ROOT);
|
||||
const defaultExtensionsPromise = getDefaultExtensionInfos();
|
||||
|
||||
const mapCallbackUriToRequestId = new Map();
|
||||
|
||||
@@ -158,9 +196,13 @@ const server = http.createServer((req, res) => {
|
||||
// static requests
|
||||
return handleStatic(req, res, parsedUrl);
|
||||
}
|
||||
if (/^\/static-extension\//.test(pathname)) {
|
||||
// static extension requests
|
||||
return handleStaticExtension(req, res, parsedUrl);
|
||||
if (/^\/extension\//.test(pathname)) {
|
||||
// default extension requests
|
||||
return handleExtension(req, res, parsedUrl);
|
||||
}
|
||||
if (/^\/builtin-extension\//.test(pathname)) {
|
||||
// builtin extension requests
|
||||
return handleBuiltinExtension(req, res, parsedUrl);
|
||||
}
|
||||
if (pathname === '/') {
|
||||
// main web
|
||||
@@ -211,13 +253,34 @@ function handleStatic(req, res, parsedUrl) {
|
||||
* @param {import('http').ServerResponse} res
|
||||
* @param {import('url').UrlWithParsedQuery} parsedUrl
|
||||
*/
|
||||
function handleStaticExtension(req, res, parsedUrl) {
|
||||
async function handleExtension(req, res, parsedUrl) {
|
||||
// Strip `/extension/` from the path
|
||||
const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/extension/'.length));
|
||||
const firstSlash = relativePath.indexOf('/');
|
||||
if (firstSlash === -1) {
|
||||
return serveError(req, res, 400, `Bad request.`);
|
||||
}
|
||||
const extensionId = relativePath.substr(0, firstSlash);
|
||||
const { locations } = await defaultExtensionsPromise;
|
||||
|
||||
// Strip `/static-extension/` from the path
|
||||
const relativeFilePath = path.normalize(decodeURIComponent(parsedUrl.pathname.substr('/static-extension/'.length)));
|
||||
const extensionPath = locations[extensionId];
|
||||
if (!extensionPath) {
|
||||
return serveError(req, res, 400, `Bad request.`);
|
||||
}
|
||||
|
||||
const filePath = path.join(EXTENSIONS_ROOT, relativeFilePath);
|
||||
const filePath = path.join(extensionPath, relativePath.substr(firstSlash + 1));
|
||||
return serveFile(req, res, filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('http').IncomingMessage} req
|
||||
* @param {import('http').ServerResponse} res
|
||||
* @param {import('url').UrlWithParsedQuery} parsedUrl
|
||||
*/
|
||||
async function handleBuiltinExtension(req, res, parsedUrl) {
|
||||
// Strip `/builtin-extension/` from the path
|
||||
const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/builtin-extension/'.length));
|
||||
const filePath = path.join(EXTENSIONS_ROOT, relativePath);
|
||||
return serveFile(req, res, filePath);
|
||||
}
|
||||
|
||||
@@ -254,13 +317,15 @@ async function handleRoot(req, res) {
|
||||
}
|
||||
|
||||
const builtinExtensions = await builtinExtensionsPromise;
|
||||
const { extensions } = await defaultExtensionsPromise;
|
||||
|
||||
const webConfigJSON = escapeAttribute(JSON.stringify({
|
||||
folderUri: folderUri,
|
||||
builtinExtensionsServiceUrl: `${SCHEME}://${AUTHORITY}/static-extension`
|
||||
staticExtensions: extensions,
|
||||
builtinExtensionsServiceUrl: `${SCHEME}://${AUTHORITY}/builtin-extension`
|
||||
}));
|
||||
|
||||
const data = (await util.promisify(fs.readFile)(WEB_MAIN)).toString()
|
||||
const data = (await readFile(WEB_MAIN)).toString()
|
||||
.replace('{{WORKBENCH_WEB_CONFIGURATION}}', () => webConfigJSON) // use a replace function to avoid that regexp replace patterns ($&, $0, ...) are applied
|
||||
.replace('{{WORKBENCH_BUILTIN_EXTENSIONS}}', () => escapeAttribute(JSON.stringify(builtinExtensions)))
|
||||
.replace('{{WEBVIEW_ENDPOINT}}', '')
|
||||
@@ -422,10 +487,6 @@ async function serveFile(req, res, filePath, responseHeaders = Object.create(nul
|
||||
|
||||
// Sanity checks
|
||||
filePath = path.normalize(filePath); // ensure no "." and ".."
|
||||
if (filePath.indexOf(`${APP_ROOT}${path.sep}`) !== 0) {
|
||||
// invalid location outside of APP_ROOT
|
||||
return serveError(req, res, 400, `Bad request.`);
|
||||
}
|
||||
|
||||
const stat = await util.promisify(fs.stat)(filePath);
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import Severity from 'vs/base/common/severity';
|
||||
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
|
||||
import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test';
|
||||
|
||||
@@ -650,9 +651,9 @@ export interface ITreeItem {
|
||||
}
|
||||
|
||||
export class ResolvableTreeItem implements ITreeItem {
|
||||
handle: string;
|
||||
handle!: string;
|
||||
parentHandle?: string;
|
||||
collapsibleState: TreeItemCollapsibleState;
|
||||
collapsibleState!: TreeItemCollapsibleState;
|
||||
label?: ITreeItemLabel;
|
||||
description?: string | boolean;
|
||||
icon?: UriComponents;
|
||||
@@ -668,20 +669,7 @@ export class ResolvableTreeItem implements ITreeItem {
|
||||
private resolved: boolean = false;
|
||||
private _hasResolve: boolean = false;
|
||||
constructor(treeItem: ITreeItem, resolve?: (() => Promise<ITreeItem | undefined>)) {
|
||||
this.handle = treeItem.handle;
|
||||
this.parentHandle = treeItem.parentHandle;
|
||||
this.collapsibleState = treeItem.collapsibleState;
|
||||
this.label = treeItem.label;
|
||||
this.description = treeItem.description;
|
||||
this.icon = treeItem.icon;
|
||||
this.iconDark = treeItem.iconDark;
|
||||
this.themeIcon = treeItem.themeIcon;
|
||||
this.resourceUri = treeItem.resourceUri;
|
||||
this.tooltip = treeItem.tooltip;
|
||||
this.contextValue = treeItem.contextValue;
|
||||
this.command = treeItem.command;
|
||||
this.children = treeItem.children;
|
||||
this.accessibilityInformation = treeItem.accessibilityInformation;
|
||||
mixin(this, treeItem);
|
||||
this._hasResolve = !!resolve;
|
||||
this.resolve = async () => {
|
||||
if (resolve && !this.resolved) {
|
||||
|
||||
@@ -94,13 +94,16 @@ export function convertBufferRangeToViewport(bufferRange: IBufferRange, viewport
|
||||
}
|
||||
|
||||
export function getXtermLineContent(buffer: IBuffer, lineStart: number, lineEnd: number, cols: number): string {
|
||||
let line = '';
|
||||
let content = '';
|
||||
for (let i = lineStart; i <= lineEnd; i++) {
|
||||
// Make sure only 0 to cols are considered as resizing when windows mode is enabled will
|
||||
// retain buffer data outside of the terminal width as reflow is disabled.
|
||||
line += buffer.getLine(i)!.translateToString(true, 0, cols);
|
||||
const line = buffer.getLine(i);
|
||||
if (line) {
|
||||
content += line.translateToString(true, 0, cols);
|
||||
}
|
||||
}
|
||||
return line;
|
||||
return content;
|
||||
}
|
||||
|
||||
export function positionIsInRange(position: IBufferCellPosition, range: IBufferRange): boolean {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution, CheckForVSCodeUpdateAction, CONTEXT_UPDATE_STATE } from 'vs/workbench/contrib/update/browser/update';
|
||||
import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution, CheckForVSCodeUpdateAction, CONTEXT_UPDATE_STATE, SwitchProductQualityContribution } from 'vs/workbench/contrib/update/browser/update';
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { StateType } from 'vs/platform/update/common/update';
|
||||
@@ -18,6 +18,7 @@ const workbench = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensio
|
||||
|
||||
workbench.registerWorkbenchContribution(ProductContribution, LifecyclePhase.Restored);
|
||||
workbench.registerWorkbenchContribution(UpdateContribution, LifecyclePhase.Restored);
|
||||
workbench.registerWorkbenchContribution(SwitchProductQualityContribution, LifecyclePhase.Restored);
|
||||
|
||||
const actionRegistry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
|
||||
export const CONTEXT_UPDATE_STATE = new RawContextKey<string>('updateState', StateType.Idle);
|
||||
|
||||
@@ -181,7 +182,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu
|
||||
@IActivityService private readonly activityService: IActivityService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
|
||||
) {
|
||||
super();
|
||||
@@ -221,7 +222,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu
|
||||
case StateType.Idle:
|
||||
if (state.error) {
|
||||
this.onError(state.error);
|
||||
} else if (this.state.type === StateType.CheckingForUpdates && this.state.context === this.workbenchEnvironmentService.configuration.sessionId) {
|
||||
} else if (this.state.type === StateType.CheckingForUpdates && this.state.context === this.environmentService.configuration.sessionId) {
|
||||
this.onUpdateNotAvailable();
|
||||
}
|
||||
break;
|
||||
@@ -402,7 +403,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu
|
||||
}
|
||||
|
||||
private registerGlobalActivityActions(): void {
|
||||
CommandsRegistry.registerCommand('update.check', () => this.updateService.checkForUpdates(this.workbenchEnvironmentService.configuration.sessionId));
|
||||
CommandsRegistry.registerCommand('update.check', () => this.updateService.checkForUpdates(this.environmentService.configuration.sessionId));
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
|
||||
group: '6_update',
|
||||
command: {
|
||||
@@ -477,6 +478,48 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu
|
||||
}
|
||||
}
|
||||
|
||||
export class SwitchProductQualityContribution extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.registerGlobalActivityActions();
|
||||
}
|
||||
|
||||
private registerGlobalActivityActions(): void {
|
||||
const quality = this.productService.quality;
|
||||
const productQualityChangeHandler = this.environmentService.options?.productQualityChangeHandler;
|
||||
if (productQualityChangeHandler && (quality === 'stable' || quality === 'insider')) {
|
||||
const newQuality = quality === 'stable' ? 'insider' : 'stable';
|
||||
const commandId = `update.switchQuality.${newQuality}`;
|
||||
CommandsRegistry.registerCommand(commandId, async accessor => {
|
||||
const dialogService = accessor.get(IDialogService);
|
||||
|
||||
const res = await dialogService.confirm({
|
||||
type: 'info',
|
||||
message: nls.localize('relaunchMessage', "Changing the version requires a reload to take effect"),
|
||||
detail: newQuality === 'insider' ? nls.localize('relaunchDetailInsiders', "Press the reload button to switch to the insiders version.") : nls.localize('relaunchDetailStable', "Press the reload button to switch to the stable version."),
|
||||
primaryButton: nls.localize('reload', "&&Reload")
|
||||
});
|
||||
|
||||
if (res.confirmed) {
|
||||
productQualityChangeHandler(newQuality);
|
||||
}
|
||||
});
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
|
||||
group: '6_update',
|
||||
command: {
|
||||
id: commandId,
|
||||
title: newQuality === 'insider' ? nls.localize('switchToInsiders', "Switch to Insiders Version...") : nls.localize('switchToStable', "Switch to Stable Version...")
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CheckForVSCodeUpdateAction extends Action {
|
||||
|
||||
static readonly ID = CheckForVSCodeUpdateActionId;
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { ExtHostTreeViewsShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { mock } from 'vs/base/test/common/mock';
|
||||
import { ITreeItem, IViewsRegistry, Extensions, ViewContainerLocation, IViewContainersRegistry, ITreeViewDescriptor, ITreeView, ViewContainer, IViewDescriptorService, TreeItemCollapsibleState } from 'vs/workbench/common/views';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { MainThreadTreeViews } from 'vs/workbench/api/browser/mainThreadTreeViews';
|
||||
import { TestViewsService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { TestExtensionService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { CustomTreeView } from 'vs/workbench/contrib/views/browser/treeView';
|
||||
import { ViewDescriptorService } from 'vs/workbench/services/views/browser/viewDescriptorService';
|
||||
|
||||
suite('MainThreadHostTreeView', function () {
|
||||
const testTreeViewId = 'testTreeView';
|
||||
const customValue = 'customValue';
|
||||
const ViewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
|
||||
|
||||
interface CustomTreeItem extends ITreeItem {
|
||||
customProp: string;
|
||||
}
|
||||
|
||||
class MockExtHostTreeViewsShape extends mock<ExtHostTreeViewsShape>() {
|
||||
async $getChildren(treeViewId: string, treeItemHandle?: string): Promise<ITreeItem[]> {
|
||||
return [<CustomTreeItem>{ handle: 'testItem1', collapsibleState: TreeItemCollapsibleState.Expanded, customProp: customValue }];
|
||||
}
|
||||
|
||||
async $hasResolve(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
$setVisible(): void { }
|
||||
}
|
||||
|
||||
let container: ViewContainer;
|
||||
let mainThreadTreeViews: MainThreadTreeViews;
|
||||
let extHostTreeViewsShape: MockExtHostTreeViewsShape;
|
||||
|
||||
setup(async () => {
|
||||
const instantiationService: TestInstantiationService = <TestInstantiationService>workbenchInstantiationService();
|
||||
const viewDescriptorService = instantiationService.createInstance(ViewDescriptorService);
|
||||
instantiationService.stub(IViewDescriptorService, viewDescriptorService);
|
||||
container = Registry.as<IViewContainersRegistry>(Extensions.ViewContainersRegistry).registerViewContainer({ id: 'testContainer', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
|
||||
const viewDescriptor: ITreeViewDescriptor = {
|
||||
id: testTreeViewId,
|
||||
ctorDescriptor: null!,
|
||||
name: 'Test View 1',
|
||||
treeView: instantiationService.createInstance(CustomTreeView, 'testTree', 'Test Title'),
|
||||
};
|
||||
ViewsRegistry.registerViews([viewDescriptor], container);
|
||||
|
||||
const testExtensionService = new TestExtensionService();
|
||||
extHostTreeViewsShape = new MockExtHostTreeViewsShape();
|
||||
mainThreadTreeViews = new MainThreadTreeViews(
|
||||
new class implements IExtHostContext {
|
||||
remoteAuthority = '';
|
||||
assertRegistered() { }
|
||||
set(v: any): any { return null; }
|
||||
getProxy(): any {
|
||||
return extHostTreeViewsShape;
|
||||
}
|
||||
}, new TestViewsService(), new TestNotificationService(), testExtensionService, new NullLogService());
|
||||
mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false });
|
||||
await testExtensionService.whenInstalledExtensionsRegistered();
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
ViewsRegistry.deregisterViews(ViewsRegistry.getViews(container), container);
|
||||
});
|
||||
|
||||
test('getChildren keeps custom properties', async () => {
|
||||
const treeView: ITreeView = (<ITreeViewDescriptor>ViewsRegistry.getView(testTreeViewId)).treeView;
|
||||
const children = await treeView.dataProvider?.getChildren({ handle: 'root', collapsibleState: TreeItemCollapsibleState.Expanded });
|
||||
assert(children!.length === 1, 'Exactly one child should be returned');
|
||||
assert((<CustomTreeItem>children![0]).customProp === customValue, 'Tree Items should keep custom properties');
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
@@ -38,10 +38,12 @@ interface IExternalUriResolver {
|
||||
}
|
||||
|
||||
interface ITunnelProvider {
|
||||
|
||||
/**
|
||||
* Support for creating tunnels.
|
||||
*/
|
||||
tunnelFactory?: ITunnelFactory;
|
||||
|
||||
/**
|
||||
* Support for filtering candidate ports
|
||||
*/
|
||||
@@ -169,14 +171,23 @@ interface IDefaultEditor {
|
||||
}
|
||||
|
||||
interface IDefaultLayout {
|
||||
/** @deprecated Use views instead */
|
||||
/** @deprecated Use views instead (TODO@eamodio remove eventually) */
|
||||
readonly sidebar?: IDefaultSideBarLayout;
|
||||
/** @deprecated Use views instead */
|
||||
/** @deprecated Use views instead (TODO@eamodio remove eventually) */
|
||||
readonly panel?: IDefaultPanelLayout;
|
||||
readonly views?: IDefaultView[];
|
||||
readonly editors?: IDefaultEditor[];
|
||||
}
|
||||
|
||||
interface IProductQualityChangeHandler {
|
||||
|
||||
/**
|
||||
* Handler is being called when the user wants to switch between
|
||||
* `insider` or `stable` product qualities.
|
||||
*/
|
||||
(newQuality: 'insider' | 'stable'): void;
|
||||
}
|
||||
|
||||
interface IWorkbenchConstructionOptions {
|
||||
|
||||
//#region Connection related configuration
|
||||
@@ -265,11 +276,6 @@ interface IWorkbenchConstructionOptions {
|
||||
*/
|
||||
readonly urlCallbackProvider?: IURLCallbackProvider;
|
||||
|
||||
/**
|
||||
* Support for update reporting.
|
||||
*/
|
||||
readonly updateProvider?: IUpdateProvider;
|
||||
|
||||
/**
|
||||
* Support adding additional properties to telemetry.
|
||||
*/
|
||||
@@ -291,6 +297,21 @@ interface IWorkbenchConstructionOptions {
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Update/Quality related
|
||||
|
||||
/**
|
||||
* Support for update reporting
|
||||
*/
|
||||
readonly updateProvider?: IUpdateProvider;
|
||||
|
||||
/**
|
||||
* Support for product quality switching
|
||||
*/
|
||||
readonly productQualityChangeHandler?: IProductQualityChangeHandler;
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Branding
|
||||
|
||||
/**
|
||||
@@ -426,9 +447,10 @@ export {
|
||||
// LogLevel
|
||||
LogLevel,
|
||||
|
||||
// Updates
|
||||
// Updates/Quality
|
||||
IUpdateProvider,
|
||||
IUpdate,
|
||||
IProductQualityChangeHandler,
|
||||
|
||||
// Telemetry
|
||||
ICommonTelemetryPropertiesResolver,
|
||||
|
||||
Reference in New Issue
Block a user