Merge from vscode 3d67364fbfcf676d93be64f949e9b33e7f1b969e (#5028)

This commit is contained in:
Anthony Dresser
2019-04-14 22:29:14 -07:00
committed by GitHub
parent 6dbf757385
commit 57242a2e13
210 changed files with 4898 additions and 3018 deletions
+4
View File
@@ -11,6 +11,10 @@ const bootstrap = require('./bootstrap');
// Enable ASAR in our forked processes
bootstrap.enableASARSupport();
if (process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']) {
bootstrap.injectNodeModuleLookupPath(process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']);
}
// Configure: pipe logging to parent process
if (!!process.send && process.env.PIPE_LOGGING === 'true') {
pipeLoggingToParent();
+32
View File
@@ -20,6 +20,38 @@ process.on('SIGPIPE', () => {
//#endregion
//#region Add support for redirecting the loading of node modules
exports.injectNodeModuleLookupPath = function (injectPath) {
if (!injectPath) {
throw new Error('Missing injectPath');
}
// @ts-ignore
const Module = require('module');
const path = require('path');
const nodeModulesPath = path.join(__dirname, '../node_modules');
// @ts-ignore
const originalResolveLookupPaths = Module._resolveLookupPaths;
// @ts-ignore
Module._resolveLookupPaths = function (moduleName, parent, newReturn) {
const result = originalResolveLookupPaths(moduleName, parent, newReturn);
const paths = newReturn ? result : result[1];
for (let i = 0, len = paths.length; i < len; i++) {
if (paths[i] === nodeModulesPath) {
paths.splice(i, 0, injectPath);
break;
}
}
return result;
};
};
//#endregion
//#region Add support for using node_modules.asar
/**
* @param {string=} nodeModulesPath
@@ -41,6 +41,7 @@ import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import { ISchemeTransformer } from 'vs/workbench/api/common/extHostLanguageFeatures';
export interface ISqlExtensionApiFactory {
vsCodeFactory(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@@ -58,9 +59,11 @@ export function createApiFactory(
extHostConfiguration: ExtHostConfiguration,
extensionService: ExtHostExtensionService,
logService: ExtHostLogService,
extHostStorage: ExtHostStorage
extHostStorage: ExtHostStorage,
schemeTransformer: ISchemeTransformer | null,
outputChannelName: string
): ISqlExtensionApiFactory {
let vsCodeFactory = extHostApi.createApiFactory(initData, rpcProtocol, extHostWorkspace, extHostConfiguration, extensionService, logService, extHostStorage);
let vsCodeFactory = extHostApi.createApiFactory(initData, rpcProtocol, extHostWorkspace, extHostConfiguration, extensionService, logService, extHostStorage, schemeTransformer, outputChannelName);
// Addressable instances
const extHostAccountManagement = rpcProtocol.set(SqlExtHostContext.ExtHostAccountManagement, new ExtHostAccountManagement(rpcProtocol));
@@ -1,42 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ITelemetryService, ITelemetryInfo, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
const fs = require('fs');
/**
* Write telemetry into a file for test purposes
*/
export class FileTelemetryService implements ITelemetryService {
_serviceBrand: undefined;
private _isFirst = true;
constructor(private _outputFile: string) {
}
publicLog(eventName: string, data?: ITelemetryData) {
let telemetryData = JSON.stringify(Object.assign({ eventName: eventName, data: data }));
if (this._outputFile) {
if (this._isFirst) {
fs.open(this._outputFile, fs.O_WRONLY | fs.O_CREAT, (err, fr) => {
fs.writeFileSync(this._outputFile, telemetryData + '\n');
this._isFirst = false;
});
} else {
fs.appendFileSync(this._outputFile, telemetryData + '\n');
}
}
return Promise.resolve<void>(null);
}
isOptedIn: true;
getTelemetryInfo(): Promise<ITelemetryInfo> {
return Promise.resolve({
instanceId: 'someValue.instanceId',
sessionId: 'someValue.sessionId',
machineId: 'someValue.machineId'
});
}
}
+4 -1
View File
@@ -7,6 +7,9 @@ import { ITelemetryService, ITelemetryData, ITelemetryInfo } from 'vs/platform/t
// Test stubs for commonly used objects
export class TelemetryServiceStub implements ITelemetryService {
setEnabled(value: boolean): void {
// no op;
}
_serviceBrand: any;
@@ -23,4 +26,4 @@ export class TelemetryServiceStub implements ITelemetryService {
}
isOptedIn: boolean;
}
}
+3 -5
View File
@@ -1,18 +1,16 @@
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"noEmit": true,
"types": [],
"paths": {},
"module": "amd",
"moduleResolution": "classic",
"noImplicitAny": false,
"removeComments": false,
"preserveConstEnums": true,
"target": "es5",
"sourceMap": false,
"experimentalDecorators": true,
"declaration": true,
"noImplicitReturns": true,
"baseUrl": ".",
"types": []
},
"include": [
"typings/require.d.ts",
+1 -1
View File
@@ -68,7 +68,7 @@ export const DataTransfers = {
FILES: 'Files',
/**
* Typicaly transfer type for copy/paste transfers.
* Typically transfer type for copy/paste transfers.
*/
TEXT: 'text/plain'
};
+104 -108
View File
@@ -467,28 +467,6 @@ export function getComputedStyle(el: HTMLElement): CSSStyleDeclaration {
return document.defaultView!.getComputedStyle(el, null);
}
// Adapted from WinJS
// Converts a CSS positioning string for the specified element to pixels.
const convertToPixels: (element: HTMLElement, value: string) => number = (function () {
return function (element: HTMLElement, value: string): number {
return parseFloat(value) || 0;
};
})();
function getDimension(element: HTMLElement, cssPropertyName: string, jsPropertyName: string): number {
let computedStyle: CSSStyleDeclaration = getComputedStyle(element);
let value = '0';
if (computedStyle) {
if (computedStyle.getPropertyValue) {
value = computedStyle.getPropertyValue(cssPropertyName);
} else {
// IE8
value = (<any>computedStyle).getAttribute(jsPropertyName);
}
}
return convertToPixels(element, value);
}
export function getClientArea(element: HTMLElement): Dimension {
// Try with DOM clientWidth / clientHeight
@@ -514,48 +492,66 @@ export function getClientArea(element: HTMLElement): Dimension {
throw new Error('Unable to figure out browser width and height');
}
const sizeUtils = {
class SizeUtils {
// Adapted from WinJS
// Converts a CSS positioning string for the specified element to pixels.
private static convertToPixels(element: HTMLElement, value: string): number {
return parseFloat(value) || 0;
}
getBorderLeftWidth: function (element: HTMLElement): number {
return getDimension(element, 'border-left-width', 'borderLeftWidth');
},
getBorderRightWidth: function (element: HTMLElement): number {
return getDimension(element, 'border-right-width', 'borderRightWidth');
},
getBorderTopWidth: function (element: HTMLElement): number {
return getDimension(element, 'border-top-width', 'borderTopWidth');
},
getBorderBottomWidth: function (element: HTMLElement): number {
return getDimension(element, 'border-bottom-width', 'borderBottomWidth');
},
private static getDimension(element: HTMLElement, cssPropertyName: string, jsPropertyName: string): number {
let computedStyle: CSSStyleDeclaration = getComputedStyle(element);
let value = '0';
if (computedStyle) {
if (computedStyle.getPropertyValue) {
value = computedStyle.getPropertyValue(cssPropertyName);
} else {
// IE8
value = (<any>computedStyle).getAttribute(jsPropertyName);
}
}
return SizeUtils.convertToPixels(element, value);
}
getPaddingLeft: function (element: HTMLElement): number {
return getDimension(element, 'padding-left', 'paddingLeft');
},
getPaddingRight: function (element: HTMLElement): number {
return getDimension(element, 'padding-right', 'paddingRight');
},
getPaddingTop: function (element: HTMLElement): number {
return getDimension(element, 'padding-top', 'paddingTop');
},
getPaddingBottom: function (element: HTMLElement): number {
return getDimension(element, 'padding-bottom', 'paddingBottom');
},
static getBorderLeftWidth(element: HTMLElement): number {
return SizeUtils.getDimension(element, 'border-left-width', 'borderLeftWidth');
}
static getBorderRightWidth(element: HTMLElement): number {
return SizeUtils.getDimension(element, 'border-right-width', 'borderRightWidth');
}
static getBorderTopWidth(element: HTMLElement): number {
return SizeUtils.getDimension(element, 'border-top-width', 'borderTopWidth');
}
static getBorderBottomWidth(element: HTMLElement): number {
return SizeUtils.getDimension(element, 'border-bottom-width', 'borderBottomWidth');
}
getMarginLeft: function (element: HTMLElement): number {
return getDimension(element, 'margin-left', 'marginLeft');
},
getMarginTop: function (element: HTMLElement): number {
return getDimension(element, 'margin-top', 'marginTop');
},
getMarginRight: function (element: HTMLElement): number {
return getDimension(element, 'margin-right', 'marginRight');
},
getMarginBottom: function (element: HTMLElement): number {
return getDimension(element, 'margin-bottom', 'marginBottom');
},
__commaSentinel: false
};
static getPaddingLeft(element: HTMLElement): number {
return SizeUtils.getDimension(element, 'padding-left', 'paddingLeft');
}
static getPaddingRight(element: HTMLElement): number {
return SizeUtils.getDimension(element, 'padding-right', 'paddingRight');
}
static getPaddingTop(element: HTMLElement): number {
return SizeUtils.getDimension(element, 'padding-top', 'paddingTop');
}
static getPaddingBottom(element: HTMLElement): number {
return SizeUtils.getDimension(element, 'padding-bottom', 'paddingBottom');
}
static getMarginLeft(element: HTMLElement): number {
return SizeUtils.getDimension(element, 'margin-left', 'marginLeft');
}
static getMarginTop(element: HTMLElement): number {
return SizeUtils.getDimension(element, 'margin-top', 'marginTop');
}
static getMarginRight(element: HTMLElement): number {
return SizeUtils.getDimension(element, 'margin-right', 'marginRight');
}
static getMarginBottom(element: HTMLElement): number {
return SizeUtils.getDimension(element, 'margin-bottom', 'marginBottom');
}
}
// ----------------------------------------------------------------------------------------
// Position & Dimension
@@ -594,8 +590,8 @@ export function getTopLeftOffset(element: HTMLElement): { left: number; top: num
}
if (element === offsetParent) {
left += sizeUtils.getBorderLeftWidth(element);
top += sizeUtils.getBorderTopWidth(element);
left += SizeUtils.getBorderLeftWidth(element);
top += SizeUtils.getBorderTopWidth(element);
top += element.offsetTop;
left += element.offsetLeft;
offsetParent = element.offsetParent;
@@ -686,33 +682,33 @@ export const StandardWindow: IStandardWindow = new class implements IStandardWin
// Adapted from WinJS
// Gets the width of the element, including margins.
export function getTotalWidth(element: HTMLElement): number {
let margin = sizeUtils.getMarginLeft(element) + sizeUtils.getMarginRight(element);
let margin = SizeUtils.getMarginLeft(element) + SizeUtils.getMarginRight(element);
return element.offsetWidth + margin;
}
export function getContentWidth(element: HTMLElement): number {
let border = sizeUtils.getBorderLeftWidth(element) + sizeUtils.getBorderRightWidth(element);
let padding = sizeUtils.getPaddingLeft(element) + sizeUtils.getPaddingRight(element);
let border = SizeUtils.getBorderLeftWidth(element) + SizeUtils.getBorderRightWidth(element);
let padding = SizeUtils.getPaddingLeft(element) + SizeUtils.getPaddingRight(element);
return element.offsetWidth - border - padding;
}
export function getTotalScrollWidth(element: HTMLElement): number {
let margin = sizeUtils.getMarginLeft(element) + sizeUtils.getMarginRight(element);
let margin = SizeUtils.getMarginLeft(element) + SizeUtils.getMarginRight(element);
return element.scrollWidth + margin;
}
// Adapted from WinJS
// Gets the height of the content of the specified element. The content height does not include borders or padding.
export function getContentHeight(element: HTMLElement): number {
let border = sizeUtils.getBorderTopWidth(element) + sizeUtils.getBorderBottomWidth(element);
let padding = sizeUtils.getPaddingTop(element) + sizeUtils.getPaddingBottom(element);
let border = SizeUtils.getBorderTopWidth(element) + SizeUtils.getBorderBottomWidth(element);
let padding = SizeUtils.getPaddingTop(element) + SizeUtils.getPaddingBottom(element);
return element.offsetHeight - border - padding;
}
// Adapted from WinJS
// Gets the height of the element, including its margins.
export function getTotalHeight(element: HTMLElement): number {
let margin = sizeUtils.getMarginTop(element) + sizeUtils.getMarginBottom(element);
let margin = SizeUtils.getMarginTop(element) + SizeUtils.getMarginBottom(element);
return element.offsetHeight + margin;
}
@@ -836,53 +832,53 @@ export function isHTMLElement(o: any): o is HTMLElement {
export const EventType = {
// Mouse
CLICK: 'click' as 'click',
DBLCLICK: 'dblclick' as 'dblclick',
MOUSE_UP: 'mouseup' as 'mouseup',
MOUSE_DOWN: 'mousedown' as 'mousedown',
MOUSE_OVER: 'mouseover' as 'mouseover',
MOUSE_MOVE: 'mousemove' as 'mousemove',
MOUSE_OUT: 'mouseout' as 'mouseout',
MOUSE_ENTER: 'mouseenter' as 'mouseenter',
MOUSE_LEAVE: 'mouseleave' as 'mouseleave',
CONTEXT_MENU: 'contextmenu' as 'contextmenu',
WHEEL: 'wheel' as 'wheel',
CLICK: 'click',
DBLCLICK: 'dblclick',
MOUSE_UP: 'mouseup',
MOUSE_DOWN: 'mousedown',
MOUSE_OVER: 'mouseover',
MOUSE_MOVE: 'mousemove',
MOUSE_OUT: 'mouseout',
MOUSE_ENTER: 'mouseenter',
MOUSE_LEAVE: 'mouseleave',
CONTEXT_MENU: 'contextmenu',
WHEEL: 'wheel',
// Keyboard
KEY_DOWN: 'keydown' as 'keydown',
KEY_PRESS: 'keypress' as 'keypress',
KEY_UP: 'keyup' as 'keyup',
KEY_DOWN: 'keydown',
KEY_PRESS: 'keypress',
KEY_UP: 'keyup',
// HTML Document
LOAD: 'load' as 'load',
UNLOAD: 'unload' as 'unload',
ABORT: 'abort' as 'abort',
ERROR: 'error' as 'error',
RESIZE: 'resize' as 'resize',
SCROLL: 'scroll' as 'scroll',
LOAD: 'load',
UNLOAD: 'unload',
ABORT: 'abort',
ERROR: 'error',
RESIZE: 'resize',
SCROLL: 'scroll',
// Form
SELECT: 'select' as 'select',
CHANGE: 'change' as 'change',
SUBMIT: 'submit' as 'submit',
RESET: 'reset' as 'reset',
FOCUS: 'focus' as 'focus',
FOCUS_IN: 'focusin' as 'focusin',
FOCUS_OUT: 'focusout' as 'focusout',
BLUR: 'blur' as 'blur',
INPUT: 'input' as 'input',
SELECT: 'select',
CHANGE: 'change',
SUBMIT: 'submit',
RESET: 'reset',
FOCUS: 'focus',
FOCUS_IN: 'focusin',
FOCUS_OUT: 'focusout',
BLUR: 'blur',
INPUT: 'input',
// Local Storage
STORAGE: 'storage' as 'storage',
STORAGE: 'storage',
// Drag
DRAG_START: 'dragstart' as 'dragstart',
DRAG: 'drag' as 'drag',
DRAG_ENTER: 'dragenter' as 'dragenter',
DRAG_LEAVE: 'dragleave' as 'dragleave',
DRAG_OVER: 'dragover' as 'dragover',
DROP: 'drop' as 'drop',
DRAG_END: 'dragend' as 'dragend',
DRAG_START: 'dragstart',
DRAG: 'drag',
DRAG_ENTER: 'dragenter',
DRAG_LEAVE: 'dragleave',
DRAG_OVER: 'dragover',
DROP: 'drop',
DRAG_END: 'dragend',
// Animation
ANIMATION_START: browser.isWebKit ? 'webkitAnimationStart' : 'animationstart',
ANIMATION_END: browser.isWebKit ? 'webkitAnimationEnd' : 'animationend',
ANIMATION_ITERATION: browser.isWebKit ? 'webkitAnimationIteration' : 'animationiteration'
};
} as const;
export interface EventLike {
preventDefault(): void;
+2 -2
View File
@@ -214,10 +214,10 @@ export class Gesture extends Disposable {
}
}
private newGestureEvent(type: string, intialTarget?: EventTarget): GestureEvent {
private newGestureEvent(type: string, initialTarget?: EventTarget): GestureEvent {
let event = <GestureEvent>(<any>document.createEvent('CustomEvent'));
event.initEvent(type, false, true);
event.initialTarget = intialTarget;
event.initialTarget = initialTarget;
return event;
}
@@ -126,7 +126,7 @@ export class BreadcrumbsWidget {
this._pendingLayout.dispose();
}
if (dim) {
// only meaure
// only measure
this._pendingLayout = this._updateDimensions(dim);
} else {
this._pendingLayout = this._updateScrollbar();
+22
View File
@@ -15,6 +15,10 @@
align-items: center;
}
.monaco-workbench .dialog-modal-block.dimmed {
background: rgba(0, 0, 0, 0.3);
}
/** Dialog: Container */
.monaco-workbench .dialog-box {
display: flex;
@@ -56,16 +60,22 @@
display: flex;
flex-grow: 1;
padding: 10px 15px 20px;
align-items: center;
}
.monaco-workbench .dialog-box .dialog-message-row .dialog-icon {
flex: 0 0 30px;
height: 30px;
padding-right: 4px;
padding-left: 4px;
background-position: center;
background-repeat: no-repeat;
}
.vs .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-pending {
background-image: url('pending.svg');
}
.vs .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-info {
background-image: url('info.svg');
}
@@ -78,6 +88,10 @@
background-image: url('error.svg');
}
.vs-dark .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-pending {
background-image: url('pending-dark.svg');
}
.vs-dark .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-info,
.hc-black .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-info {
background-image: url('info-inverse.svg');
@@ -93,6 +107,14 @@
background-image: url('error-inverse.svg');
}
.hc-black .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-pending {
background-image: url('pending-hc.svg');
}
.monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-pending {
background-size: 30px;
}
/** Dialog: Message Container */
.monaco-workbench .dialog-box .dialog-message-row .dialog-message-container {
display: flex;
+14 -4
View File
@@ -19,7 +19,7 @@ import { mnemonicButtonLabel } from 'vs/base/common/labels';
export interface IDialogOptions {
cancelId?: number;
detail?: string;
type?: 'none' | 'info' | 'error' | 'question' | 'warning';
type?: 'none' | 'info' | 'error' | 'question' | 'warning' | 'pending';
}
export interface IDialogStyles extends IButtonStyles {
@@ -33,6 +33,7 @@ export class Dialog extends Disposable {
private element: HTMLElement | undefined;
private modal: HTMLElement | undefined;
private buttonsContainer: HTMLElement | undefined;
private messageDetailElement: HTMLElement | undefined;
private iconElement: HTMLElement | undefined;
private toolbarContainer: HTMLElement | undefined;
private buttonGroup: ButtonGroup | undefined;
@@ -40,7 +41,7 @@ export class Dialog extends Disposable {
constructor(private container: HTMLElement, private message: string, private buttons: string[], private options: IDialogOptions) {
super();
this.modal = this.container.appendChild($('.dialog-modal-block'));
this.modal = this.container.appendChild($(`.dialog-modal-block${options.type === 'pending' ? '.dimmed' : ''}`));
this.element = this.modal.appendChild($('.dialog-box'));
hide(this.element);
@@ -56,13 +57,19 @@ export class Dialog extends Disposable {
messageElement.innerText = this.message;
}
const messageDetailElement = messageContainer.appendChild($('.dialog-message-detail'));
messageDetailElement.innerText = this.options.detail ? this.options.detail : message;
this.messageDetailElement = messageContainer.appendChild($('.dialog-message-detail'));
this.messageDetailElement.innerText = this.options.detail ? this.options.detail : message;
const toolbarRowElement = this.element.appendChild($('.dialog-toolbar-row'));
this.toolbarContainer = toolbarRowElement.appendChild($('.dialog-toolbar'));
}
updateMessage(message: string): void {
if (this.messageDetailElement) {
this.messageDetailElement.innerText = message;
}
}
async show(): Promise<number> {
return new Promise<number>((resolve) => {
if (!this.element || !this.buttonsContainer || !this.iconElement || !this.toolbarContainer) {
@@ -129,6 +136,9 @@ export class Dialog extends Disposable {
case 'warning':
addClass(this.iconElement, 'icon-warning');
break;
case 'pending':
addClass(this.iconElement, 'icon-pending');
break;
case 'none':
case 'info':
case 'question':
@@ -0,0 +1,31 @@
<?xml version='1.0' standalone='no' ?>
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='10px' height='10px'>
<style>
circle {
animation: ball 1.04s linear infinite;
}
circle:nth-child(2) { animation-delay: 0.13s; }
circle:nth-child(3) { animation-delay: 0.26s; }
circle:nth-child(4) { animation-delay: 0.39s; }
circle:nth-child(5) { animation-delay: 0.52s; }
circle:nth-child(6) { animation-delay: 0.65s; }
circle:nth-child(7) { animation-delay: 0.78s; }
circle:nth-child(8) { animation-delay: 0.91s; }
@keyframes ball {
from { opacity: 1; }
to { opacity: 0.3; }
}
</style>
<g style="fill:grey;">
<circle cx='5' cy='1' r='1' style='opacity:0.3;' />
<circle cx='7.8284' cy='2.1716' r='1' style='opacity:0.3;' />
<circle cx='9' cy='5' r='1' style='opacity:0.3;' />
<circle cx='7.8284' cy='7.8284' r='1' style='opacity:0.3;' />
<circle cx='5' cy='9' r='1' style='opacity:0.3;' />
<circle cx='2.1716' cy='7.8284' r='1' style='opacity:0.3;' />
<circle cx='1' cy='5' r='1' style='opacity:0.3;' />
<circle cx='2.1716' cy='2.1716' r='1' style='opacity:0.3;' />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,31 @@
<?xml version='1.0' standalone='no' ?>
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='10px' height='10px'>
<style>
circle {
animation: ball 1.04s linear infinite;
}
circle:nth-child(2) { animation-delay: 0.13s; }
circle:nth-child(3) { animation-delay: 0.26s; }
circle:nth-child(4) { animation-delay: 0.39s; }
circle:nth-child(5) { animation-delay: 0.52s; }
circle:nth-child(6) { animation-delay: 0.65s; }
circle:nth-child(7) { animation-delay: 0.78s; }
circle:nth-child(8) { animation-delay: 0.91s; }
@keyframes ball {
from { opacity: 1; }
to { opacity: 0.3; }
}
</style>
<g style="fill:white;">
<circle cx='5' cy='1' r='1' style='opacity:0.3;' />
<circle cx='7.8284' cy='2.1716' r='1' style='opacity:0.3;' />
<circle cx='9' cy='5' r='1' style='opacity:0.3;' />
<circle cx='7.8284' cy='7.8284' r='1' style='opacity:0.3;' />
<circle cx='5' cy='9' r='1' style='opacity:0.3;' />
<circle cx='2.1716' cy='7.8284' r='1' style='opacity:0.3;' />
<circle cx='1' cy='5' r='1' style='opacity:0.3;' />
<circle cx='2.1716' cy='2.1716' r='1' style='opacity:0.3;' />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+31
View File
@@ -0,0 +1,31 @@
<?xml version='1.0' standalone='no' ?>
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='10px' height='10px'>
<style>
circle {
animation: ball 1.04s linear infinite;
}
circle:nth-child(2) { animation-delay: 0.13s; }
circle:nth-child(3) { animation-delay: 0.26s; }
circle:nth-child(4) { animation-delay: 0.39s; }
circle:nth-child(5) { animation-delay: 0.52s; }
circle:nth-child(6) { animation-delay: 0.65s; }
circle:nth-child(7) { animation-delay: 0.78s; }
circle:nth-child(8) { animation-delay: 0.91s; }
@keyframes ball {
from { opacity: 1; }
to { opacity: 0.3; }
}
</style>
<g>
<circle cx='5' cy='1' r='1' style='opacity:0.3;' />
<circle cx='7.8284' cy='2.1716' r='1' style='opacity:0.3;' />
<circle cx='9' cy='5' r='1' style='opacity:0.3;' />
<circle cx='7.8284' cy='7.8284' r='1' style='opacity:0.3;' />
<circle cx='5' cy='9' r='1' style='opacity:0.3;' />
<circle cx='2.1716' cy='7.8284' r='1' style='opacity:0.3;' />
<circle cx='1' cy='5' r='1' style='opacity:0.3;' />
<circle cx='2.1716' cy='2.1716' r='1' style='opacity:0.3;' />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+3 -3
View File
@@ -502,7 +502,7 @@ function isMouseRightClick(event: UIEvent): boolean {
return event instanceof MouseEvent && event.button === 2;
}
const DefaultMultipleSelectionContoller = {
const DefaultMultipleSelectionController = {
isSelectionSingleChangeEvent,
isSelectionRangeChangeEvent
};
@@ -529,7 +529,7 @@ export class MouseController<T> implements IDisposable {
this.multipleSelectionSupport = !(list.options.multipleSelectionSupport === false);
if (this.multipleSelectionSupport) {
this.multipleSelectionController = list.options.multipleSelectionController || DefaultMultipleSelectionContoller;
this.multipleSelectionController = list.options.multipleSelectionController || DefaultMultipleSelectionController;
}
this.openController = list.options.openController || DefaultOpenController;
@@ -909,7 +909,7 @@ function getContiguousRangeContaining(range: number[], value: number): number[]
/**
* Given two sorted collections of numbers, returns the intersection
* betweem them (OR).
* between them (OR).
*/
function disjunction(one: number[], other: number[]): number[] {
const result: number[] = [];
+4 -3
View File
@@ -1,7 +1,7 @@
@font-face {
font-family: "octicons";
src: url("./octicons.ttf?4cd2299755e93a2430ba5703f4476584") format("truetype"),
url("./octicons.svg?4cd2299755e93a2430ba5703f4476584#octicons") format("svg");
src: url("./octicons.ttf?91284a5a76ea88faeb754359b7f7cd03") format("truetype"),
url("./octicons.svg?91284a5a76ea88faeb754359b7f7cd03#octicons") format("svg");
}
.octicon, .mega-octicon {
@@ -240,4 +240,5 @@ url("./octicons.svg?4cd2299755e93a2430ba5703f4476584#octicons") format("svg");
.octicon-fold-up:before { content: "\f105" }
.octicon-github-action:before { content: "\f106" }
.octicon-play:before { content: "\f107" }
.octicon-request-changes:before { content: "\f108" }
.octicon-remote:before { content: "\f108" }
.octicon-request-changes:before { content: "\f109" }
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 127 KiB

@@ -523,7 +523,7 @@ export class DomScrollableElement extends ScrollableElement {
}
public scanDomNode(): void {
// widh, scrollLeft, scrollWidth, height, scrollTop, scrollHeight
// width, scrollLeft, scrollWidth, height, scrollTop, scrollHeight
this.setScrollDimensions({
width: this._element.clientWidth,
scrollWidth: this._element.scrollWidth,
+1 -1
View File
@@ -33,7 +33,7 @@ export const enum TreeVisibility {
export interface ITreeFilterDataResult<TFilterData> {
/**
* Whether the node should be visibile.
* Whether the node should be visible.
*/
visibility: boolean | TreeVisibility;
+22
View File
@@ -5,6 +5,7 @@
import { ParseError, Node, JSONPath, Segment, parseTree, findNodeAtLocation } from './json';
import { Edit, format, isEOL, FormattingOptions } from './jsonFormatter';
import { mergeSort } from 'vs/base/common/arrays';
export function removeProperty(text: string, path: JSONPath, formattingOptions: FormattingOptions): Edit[] {
@@ -158,6 +159,27 @@ export function applyEdit(text: string, edit: Edit): string {
return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length);
}
export function applyEdits(text: string, edits: Edit[]): string {
let sortedEdits = mergeSort(edits, (a, b) => {
const diff = a.offset - b.offset;
if (diff === 0) {
return a.length - b.length;
}
return diff;
});
let lastModifiedOffset = text.length;
for (let i = sortedEdits.length - 1; i >= 0; i--) {
let e = sortedEdits[i];
if (e.offset + e.length <= lastModifiedOffset) {
text = applyEdit(text, e);
} else {
throw new Error('Overlapping edit');
}
lastModifiedOffset = e.offset;
}
return text;
}
export function isWS(text: string, offset: number) {
return '\r\n \t'.indexOf(text.charAt(offset)) !== -1;
}
+15 -10
View File
@@ -226,7 +226,7 @@ export class URI implements UriComponents {
// ---- modify to new -------------------------
public with(change: { scheme?: string; authority?: string | null; path?: string | null; query?: string | null; fragment?: string | null }): URI {
with(change: { scheme?: string; authority?: string | null; path?: string | null; query?: string | null; fragment?: string | null }): URI {
if (!change) {
return this;
@@ -279,7 +279,7 @@ export class URI implements UriComponents {
*
* @param value A string which represents an URI (see `URI#toString`).
*/
public static parse(value: string, _strict: boolean = false): URI {
static parse(value: string, _strict: boolean = false): URI {
const match = _regexp.exec(value);
if (!match) {
return new _URI(_empty, _empty, _empty, _empty, _empty);
@@ -315,7 +315,7 @@ export class URI implements UriComponents {
*
* @param path A file system path (see `URI#fsPath`)
*/
public static file(path: string): URI {
static file(path: string): URI {
let authority = _empty;
@@ -342,7 +342,7 @@ export class URI implements UriComponents {
return new _URI('file', authority, path, _empty, _empty);
}
public static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string }): URI {
static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string }): URI {
return new _URI(
components.scheme,
components.authority,
@@ -365,17 +365,22 @@ export class URI implements UriComponents {
*
* @param skipEncoding Do not encode the result, default is `false`
*/
public toString(skipEncoding: boolean = false): string {
toString(skipEncoding: boolean = false): string {
return _asFormatted(this, skipEncoding);
}
public toJSON(): object {
toJSON(): UriComponents {
return this;
}
static revive(data: UriComponents | any): URI {
static revive(data: UriComponents | URI): URI;
static revive(data: UriComponents | URI | undefined): URI | undefined;
static revive(data: UriComponents | URI | null): URI | null;
static revive(data: UriComponents | URI | undefined | null): URI | undefined | null;
static revive(data: UriComponents | URI | undefined | null): URI | undefined | null {
if (!data) {
return data;
// {{SQL CARBON EDIT}} @todo chlafren change back to data when we enable strict null checks
return undefined;
} else if (data instanceof URI) {
return data;
} else {
@@ -415,7 +420,7 @@ class _URI extends URI {
return this._fsPath;
}
public toString(skipEncoding: boolean = false): string {
toString(skipEncoding: boolean = false): string {
if (!skipEncoding) {
if (!this._formatted) {
this._formatted = _asFormatted(this, false);
@@ -427,7 +432,7 @@ class _URI extends URI {
}
}
toJSON(): object {
toJSON(): UriComponents {
const res = <UriState>{
$mid: 1
};
+8 -4
View File
@@ -14,6 +14,10 @@ export const UTF8_with_bom = 'utf8bom';
export const UTF16be = 'utf16be';
export const UTF16le = 'utf16le';
export const UTF16be_BOM = [0xFE, 0xFF];
export const UTF16le_BOM = [0xFF, 0xFE];
export const UTF8_BOM = [0xEF, 0xBB, 0xBF];
export interface IDecodeStreamOptions {
guessEncoding?: boolean;
minBytesRequiredForDetection?: number;
@@ -150,12 +154,12 @@ export function detectEncodingByBOMFromBuffer(buffer: Buffer | null, bytesRead:
const b1 = buffer.readUInt8(1);
// UTF-16 BE
if (b0 === 0xFE && b1 === 0xFF) {
if (b0 === UTF16be_BOM[0] && b1 === UTF16be_BOM[1]) {
return UTF16be;
}
// UTF-16 LE
if (b0 === 0xFF && b1 === 0xFE) {
if (b0 === UTF16le_BOM[0] && b1 === UTF16le_BOM[1]) {
return UTF16le;
}
@@ -166,7 +170,7 @@ export function detectEncodingByBOMFromBuffer(buffer: Buffer | null, bytesRead:
const b2 = buffer.readUInt8(2);
// UTF-8
if (b0 === 0xEF && b1 === 0xBB && b2 === 0xBF) {
if (b0 === UTF8_BOM[0] && b1 === UTF8_BOM[1] && b2 === UTF8_BOM[2]) {
return UTF8;
}
@@ -178,7 +182,7 @@ export function detectEncodingByBOMFromBuffer(buffer: Buffer | null, bytesRead:
* If no BOM is detected, null will be passed to callback.
*/
export function detectEncodingByBOM(file: string): Promise<string | null> {
return stream.readExactlyByFile(file, 3).then(({ buffer, bytesRead }) => detectEncodingByBOMFromBuffer(buffer, bytesRead));
return stream.readExactlyByFile(file, 3).then(({ buffer, bytesRead }) => detectEncodingByBOMFromBuffer(buffer, bytesRead), error => null);
}
const MINIMUM_THRESHOLD = 0.2;
+39
View File
@@ -0,0 +1,39 @@
#!/bin/sh
PAGESIZE=`getconf PAGESIZE`;
TOTAL_MEMORY=`cat /proc/meminfo | head -n 1 | awk '{print $2}'`;
# Mimic the output of ps -ax -o pid=,ppid=,pcpu=,pmem=,command=
# Read all numeric subdirectories in /proc
for pid in `cd /proc && ls -d [0-9]*`
do {
if [ -e /proc/$pid/stat ]
then
echo $pid;
# ppid is the word at index 4 in the stat file for the process
awk '{print $4}' /proc/$pid/stat;
# pcpu - calculation will be done later, this is a placeholder value
echo "0.0"
# pmem - ratio of the process's working set size to total memory.
# use the page size to convert to bytes, total memory is in KB
# multiplied by 100 to get percentage, extra 10 to be able to move
# the decimal over by one place
RESIDENT_SET_SIZE=`awk '{print $24}' /proc/$pid/stat`;
PERCENT_MEMORY=$(((1000 * $PAGESIZE * $RESIDENT_SET_SIZE) / ($TOTAL_MEMORY * 1024)));
if [ $PERCENT_MEMORY -lt 10 ]
then
# replace the last character with 0. the last character
echo $PERCENT_MEMORY | sed 's/.$/0.&/'; #pmem
else
# insert . before the last character
echo $PERCENT_MEMORY | sed 's/.$/.&/';
fi
# cmdline
xargs -0 < /proc/$pid/cmdline;
fi
} | tr "\n" "\t"; # Replace newlines with tab so that all info for a process is shown on one line
echo; # But add new lines between processes
done
+66 -42
View File
@@ -14,6 +14,7 @@ export function listProcesses(rootPid: number): Promise<ProcessItem> {
let rootItem: ProcessItem | undefined;
const map = new Map<number, ProcessItem>();
function addToTree(pid: number, ppid: number, cmd: string, load: number, mem: number) {
const parent = map.get(ppid);
@@ -162,64 +163,87 @@ export function listProcesses(rootPid: number): Promise<ProcessItem> {
}, windowsProcessTree.ProcessDataFlag.CommandLine | windowsProcessTree.ProcessDataFlag.Memory);
});
} else { // OS X & Linux
const CMD = '/bin/ps -ax -o pid=,ppid=,pcpu=,pmem=,command=';
const PID_CMD = /^\s*([0-9]+)\s+([0-9]+)\s+([0-9]+\.[0-9]+)\s+([0-9]+\.[0-9]+)\s+(.+)$/;
// Set numeric locale to ensure '.' is used as the decimal separator
exec(CMD, { maxBuffer: 1000 * 1024, env: { LC_NUMERIC: 'en_US.UTF-8' } }, (err, stdout, stderr) => {
if (err || stderr) {
reject(err || new Error(stderr.toString()));
} else {
const lines = stdout.toString().split('\n');
for (const line of lines) {
const matches = PID_CMD.exec(line.trim());
if (matches && matches.length === 6) {
addToTree(parseInt(matches[1]), parseInt(matches[2]), matches[5], parseFloat(matches[3]), parseFloat(matches[4]));
function calculateLinuxCpuUsage() {
// Flatten rootItem to get a list of all VSCode processes
let processes = [rootItem];
const pids: number[] = [];
while (processes.length) {
const process = processes.shift();
if (process) {
pids.push(process.pid);
if (process.children) {
processes = processes.concat(process.children);
}
}
}
if (process.platform === 'linux') {
// Flatten rootItem to get a list of all VSCode processes
let processes = [rootItem];
const pids: number[] = [];
while (processes.length) {
const process = processes.shift();
if (process) {
pids.push(process.pid);
if (process.children) {
processes = processes.concat(process.children);
}
}
// The cpu usage value reported on Linux is the average over the process lifetime,
// recalculate the usage over a one second interval
// JSON.stringify is needed to escape spaces, https://github.com/nodejs/node/issues/6803
let cmd = JSON.stringify(getPathFromAmdModule(require, 'vs/base/node/cpuUsage.sh'));
cmd += ' ' + pids.join(' ');
exec(cmd, {}, (err, stdout, stderr) => {
if (err || stderr) {
reject(err || new Error(stderr.toString()));
} else {
const cpuUsage = stdout.toString().split('\n');
for (let i = 0; i < pids.length; i++) {
const processInfo = map.get(pids[i])!;
processInfo.load = parseFloat(cpuUsage[i]);
}
// The cpu usage value reported on Linux is the average over the process lifetime,
// recalculate the usage over a one second interval
// JSON.stringify is needed to escape spaces, https://github.com/nodejs/node/issues/6803
let cmd = JSON.stringify(getPathFromAmdModule(require, 'vs/base/node/cpuUsage.sh'));
cmd += ' ' + pids.join(' ');
resolve(rootItem);
}
});
}
exec('which ps', {}, (err, stdout, stderr) => {
if (err || stderr) {
if (process.platform !== 'linux') {
reject(err || new Error(stderr.toString()));
} else {
const cmd = JSON.stringify(getPathFromAmdModule(require, 'vs/base/node/ps.sh'));
exec(cmd, {}, (err, stdout, stderr) => {
if (err || stderr) {
reject(err || new Error(stderr.toString()));
} else {
const cpuUsage = stdout.toString().split('\n');
for (let i = 0; i < pids.length; i++) {
const processInfo = map.get(pids[i])!;
processInfo.load = parseFloat(cpuUsage[i]);
}
resolve(rootItem);
parsePsOutput(stdout, addToTree);
calculateLinuxCpuUsage();
}
});
} else {
resolve(rootItem);
}
} else {
const ps = stdout.toString().trim();
const args = '-ax -o pid=,ppid=,pcpu=,pmem=,command=';
// Set numeric locale to ensure '.' is used as the decimal separator
exec(`${ps} ${args}`, { maxBuffer: 1000 * 1024, env: { LC_NUMERIC: 'en_US.UTF-8' } }, (err, stdout, stderr) => {
if (err || stderr) {
reject(err || new Error(stderr.toString()));
} else {
parsePsOutput(stdout, addToTree);
if (process.platform === 'linux') {
calculateLinuxCpuUsage();
} else {
resolve(rootItem);
}
}
});
}
});
}
});
}
function parsePsOutput(stdout: string, addToTree: (pid: number, ppid: number, cmd: string, load: number, mem: number) => void): void {
const PID_CMD = /^\s*([0-9]+)\s+([0-9]+)\s+([0-9]+\.[0-9]+)\s+([0-9]+\.[0-9]+)\s+(.+)$/;
const lines = stdout.toString().split('\n');
for (const line of lines) {
const matches = PID_CMD.exec(line.trim());
if (matches && matches.length === 6) {
addToTree(parseInt(matches[1]), parseInt(matches[2]), matches[5], parseFloat(matches[3]), parseFloat(matches[4]));
}
}
}
+2 -2
View File
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { URI } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import { isWindows } from 'vs/base/common/platform';
@@ -441,7 +441,7 @@ suite('URI', () => {
// let c = 100000;
// while (c-- > 0) {
for (let value of values) {
let data = value.toJSON();
let data = value.toJSON() as UriComponents;
let clone = URI.revive(data);
assert.equal(clone.scheme, value.scheme);
@@ -11,6 +11,14 @@ import { Readable } from 'stream';
import { getPathFromAmdModule } from 'vs/base/common/amd';
suite('Encoding', () => {
test('detectBOM does not return error for non existing file', async () => {
const file = getPathFromAmdModule(require, './fixtures/not-exist.css');
const detectedEncoding = await encoding.detectEncodingByBOM(file);
assert.equal(detectedEncoding, null);
});
test('detectBOM UTF-8', async () => {
const file = getPathFromAmdModule(require, './fixtures/some_utf8.css');
+2 -1
View File
@@ -8,7 +8,8 @@ import { getMachineId } from 'vs/base/node/id';
suite('ID', () => {
test('getMachineId', () => {
test('getMachineId', function () {
this.timeout(20000);
return getMachineId().then(id => {
assert.ok(id);
});
@@ -60,6 +60,7 @@ export function startup(configuration: IssueReporterConfiguration) {
const issueReporter = new IssueReporter(configuration);
issueReporter.render();
document.body.style.display = 'block';
issueReporter.setInitialFocus();
}
export class IssueReporter extends Disposable {
@@ -88,6 +89,8 @@ export class IssueReporter extends Disposable {
os: `${os.type()} ${os.arch()} ${os.release()}${isSnap ? ' snap' : ''}`
},
extensionsDisabled: !!this.environmentService.disableExtensions,
fileOnExtension: configuration.data.extensionId ? true : undefined,
selectedExtension: configuration.data.extensionId ? configuration.data.enabledExtensions.filter(extension => extension.id === configuration.data.extensionId)[0] : undefined
});
const issueReporterElement = this.getElementById('issue-reporter');
@@ -140,6 +143,21 @@ export class IssueReporter extends Disposable {
this.renderBlocks();
}
setInitialFocus() {
const { fileOnExtension } = this.issueReporterModel.getData();
if (fileOnExtension) {
const issueTitle = document.getElementById('issue-title');
if (issueTitle) {
issueTitle.focus();
}
} else {
const issueType = document.getElementById('issue-type');
if (issueType) {
issueType.focus();
}
}
}
private applyZoom(zoomLevel: number) {
webFrame.setZoomLevel(zoomLevel);
browser.setZoomFactor(webFrame.getZoomFactor());
@@ -699,9 +717,13 @@ export class IssueReporter extends Disposable {
private setSourceOptions(): void {
const sourceSelect = this.getElementById('issue-source')! as HTMLSelectElement;
const selected = sourceSelect.selectedIndex;
const { issueType, fileOnExtension } = this.issueReporterModel.getData();
let selected = sourceSelect.selectedIndex;
if (selected === -1 && fileOnExtension !== undefined) {
selected = fileOnExtension ? 2 : 1;
}
sourceSelect.innerHTML = '';
const { issueType } = this.issueReporterModel.getData();
if (issueType === IssueType.FeatureRequest) {
sourceSelect.append(...[
this.makeOption('', localize('selectSource', "Select source"), true),
@@ -961,10 +983,15 @@ export class IssueReporter extends Disposable {
return 0;
});
const makeOption = (extension: IOption) => `<option value="${extension.id}">${escape(extension.name)}</option>`;
const makeOption = (extension: IOption, selectedExtension?: IssueReporterExtensionData) => {
const selected = selectedExtension && extension.id === selectedExtension.id;
return `<option value="${extension.id}" ${selected ? 'selected' : ''}>${escape(extension.name)}</option>`;
};
const extensionsSelector = this.getElementById('extension-selector');
if (extensionsSelector) {
extensionsSelector.innerHTML = '<option></option>' + extensionOptions.map(makeOption).join('\n');
const { selectedExtension } = this.issueReporterModel.getData();
extensionsSelector.innerHTML = '<option></option>' + extensionOptions.map(extension => makeOption(extension, selectedExtension)).join('\n');
this.addEventListener('extension-selector', 'change', (e: Event) => {
const selectedExtensionId = (<HTMLInputElement>e.target).value;
@@ -29,8 +29,8 @@ const collapsedStateCache: Map<string, boolean> = new Map<string, boolean>();
let lastRequestTime: number;
interface FormattedProcessItem {
cpu: string;
memory: string;
cpu: number;
memory: number;
pid: string;
name: string;
formattedName: string;
@@ -66,8 +66,8 @@ function getProcessItem(processes: FormattedProcessItem[], item: ProcessItem, in
const formattedName = isRoot ? name : `${repeat(' ', indent)} ${name}`;
const memory = process.platform === 'win32' ? item.mem : (totalmem() * (item.mem / 100));
processes.push({
cpu: item.load.toFixed(0),
memory: (memory / MB).toFixed(0),
cpu: item.load,
memory: (memory / MB),
pid: item.pid.toFixed(0),
name,
formattedName,
@@ -202,13 +202,13 @@ function renderTableSection(sectionName: string, processList: FormattedProcessIt
p.pid === highestCPUProcess
? cpu.classList.add('centered', 'highest')
: cpu.classList.add('centered');
cpu.textContent = p.cpu.toString();
cpu.textContent = p.cpu.toFixed(0);
const memory = document.createElement('td');
p.pid === highestMemoryProcess
? memory.classList.add('centered', 'highest')
: memory.classList.add('centered');
memory.textContent = p.memory;
memory.textContent = p.memory.toFixed(0);
const pid = document.createElement('td');
pid.classList.add('centered');
@@ -1754,7 +1754,7 @@ export class EditorModeContext extends Disposable {
this._hasSignatureHelpProvider.set(modes.SignatureHelpProviderRegistry.has(model));
this._hasDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.has(model) || modes.DocumentRangeFormattingEditProviderRegistry.has(model));
this._hasDocumentSelectionFormattingProvider.set(modes.DocumentRangeFormattingEditProviderRegistry.has(model));
this._hasMultipleDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.all(model).length > 1 || modes.DocumentRangeFormattingEditProviderRegistry.all(model).length > 1);
this._hasMultipleDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.all(model).length + modes.DocumentRangeFormattingEditProviderRegistry.all(model).length > 1);
this._hasMultipleDocumentSelectionFormattingProvider.set(modes.DocumentRangeFormattingEditProviderRegistry.all(model).length > 1);
this._isInWalkThrough.set(model.uri.scheme === Schemas.walkThroughSnippet);
});
@@ -368,9 +368,6 @@ export class ReferenceWidget extends PeekViewWidget {
if (e.browserEvent instanceof KeyboardEvent) {
// todo@joh make this a command
goto = true;
} else if (e.browserEvent instanceof MouseEvent) {
aside = e.browserEvent.ctrlKey || e.browserEvent.metaKey || e.browserEvent.altKey;
goto = e.browserEvent.detail === 2;
}
if (aside) {
onEvent(e.elements[0], 'side');
@@ -197,7 +197,7 @@ export class SnippetController2 implements IEditorContribution {
insertText: option.value,
// insertText: `\${1|${after.concat(before).join(',')}|}$0`,
// snippetType: 'textmate',
sortText: repeat('a', i),
sortText: repeat('a', i + 1),
range: Range.fromPositions(this._editor.getPosition()!, this._editor.getPosition()!.delta(0, first.value.length))
};
}));
@@ -343,7 +343,7 @@ export class SuggestModel implements IDisposable {
const position = this._editor.getPosition();
const ctx = new LineContext(model, position, this._state === State.Auto, false);
this._onNewContext(ctx);
}, 25);
}, 0);
}
trigger(context: SuggestTriggerContext, retrigger: boolean = false, onlyFrom?: Set<CompletionItemProvider>, existingItems?: CompletionItem[]): void {
@@ -496,6 +496,9 @@ export class StandaloneTelemetryService implements ITelemetryService {
public isOptedIn = false;
public setEnabled(value: boolean): void {
}
public publicLog(eventName: string, data?: any): Promise<void> {
return Promise.resolve(undefined);
}
+5 -2
View File
@@ -171,8 +171,11 @@ declare namespace monaco {
* @param skipEncoding Do not encode the result, default is `false`
*/
toString(skipEncoding?: boolean): string;
toJSON(): object;
static revive(data: UriComponents | any): Uri;
toJSON(): UriComponents;
static revive(data: UriComponents | Uri): Uri;
static revive(data: UriComponents | Uri | undefined): Uri | undefined;
static revive(data: UriComponents | Uri | null): Uri | null;
static revive(data: UriComponents | Uri | undefined | null): Uri | undefined | null;
}
export interface UriComponents {
@@ -83,8 +83,21 @@ export interface IConfigurationRegistry {
}
export const enum ConfigurationScope {
/**
* Application specific configuration, which can be configured only in local user settings.
*/
APPLICATION = 1,
/**
* Machine specific configuration, which can be configured only in local and remote user settings.
*/
MACHINE,
/**
* Window specific configuration, which can be configured in the user or workspace settings.
*/
WINDOW,
/**
* Resource specific configuration, which can be configured in the user, workspace or folder settings.
*/
RESOURCE,
}
@@ -95,6 +108,10 @@ export interface IConfigurationPropertySchema extends IJSONSchema {
tags?: string[];
}
export interface IConfigurationExtensionInfo {
id: string;
}
export interface IConfigurationNode {
id?: string;
order?: number;
@@ -105,7 +122,7 @@ export interface IConfigurationNode {
allOf?: IConfigurationNode[];
overridable?: boolean;
scope?: ConfigurationScope;
contributedByExtension?: boolean;
extensionInfo?: IConfigurationExtensionInfo;
}
export interface IDefaultConfigurationExtension {
@@ -116,6 +133,7 @@ export interface IDefaultConfigurationExtension {
export const allSettings: { properties: {}, patternProperties: {} } = { properties: {}, patternProperties: {} };
export const applicationSettings: { properties: {}, patternProperties: {} } = { properties: {}, patternProperties: {} };
export const machineSettings: { properties: {}, patternProperties: {} } = { properties: {}, patternProperties: {} };
export const windowSettings: { properties: {}, patternProperties: {} } = { properties: {}, patternProperties: {} };
export const resourceSettings: { properties: {}, patternProperties: {} } = { properties: {}, patternProperties: {} };
@@ -186,6 +204,9 @@ class ConfigurationRegistry implements IConfigurationRegistry {
case ConfigurationScope.APPLICATION:
delete applicationSettings.properties[key];
break;
case ConfigurationScope.MACHINE:
delete machineSettings.properties[key];
break;
case ConfigurationScope.WINDOW:
delete windowSettings.properties[key];
break;
@@ -334,6 +355,9 @@ class ConfigurationRegistry implements IConfigurationRegistry {
case ConfigurationScope.APPLICATION:
applicationSettings.properties[key] = properties[key];
break;
case ConfigurationScope.MACHINE:
machineSettings.properties[key] = properties[key];
break;
case ConfigurationScope.WINDOW:
windowSettings.properties[key] = properties[key];
break;
@@ -371,6 +395,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
delete allSettings.patternProperties[this.overridePropertyPattern];
delete applicationSettings.patternProperties[this.overridePropertyPattern];
delete machineSettings.patternProperties[this.overridePropertyPattern];
delete windowSettings.patternProperties[this.overridePropertyPattern];
delete resourceSettings.patternProperties[this.overridePropertyPattern];
@@ -378,6 +403,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
allSettings.patternProperties[this.overridePropertyPattern] = patternProperties;
applicationSettings.patternProperties[this.overridePropertyPattern] = patternProperties;
machineSettings.patternProperties[this.overridePropertyPattern] = patternProperties;
windowSettings.patternProperties[this.overridePropertyPattern] = patternProperties;
resourceSettings.patternProperties[this.overridePropertyPattern] = patternProperties;
@@ -18,7 +18,6 @@ export interface ParsedArgs {
waitMarkerFilePath?: string;
diff?: boolean;
add?: boolean;
gitCredential?: string;
goto?: boolean;
'new-window'?: boolean;
'unity-launch'?: boolean; // Always open a new window, except if opening the first window or opening a file or folder as part of the launch.
+1 -1
View File
@@ -197,7 +197,7 @@ function wrapText(text: string, columns: number): string[] {
return lines;
}
export function buildHelpMessage(productName: string, executableName: string, version: string, isOptionSupported = (_: Option) => true): string {
export function buildHelpMessage(productName: string, executableName: string, version: string, isOptionSupported = (_: Option) => true, isPipeSupported = true): string {
const columns = (process.stdout).isTTY && (process.stdout).columns || 80;
let categories = new HelpCategories();
@@ -86,7 +86,7 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
get onDidUninstallExtension(): Event<DidUninstallExtensionEvent> { return this.channel.listen('onDidUninstallExtension'); }
zip(extension: ILocalExtension): Promise<URI> {
return Promise.resolve(this.channel.call('zip', [extension]).then(result => URI.revive(result)));
return Promise.resolve(this.channel.call('zip', [extension]).then(result => URI.revive(<UriComponents>result)));
}
unzip(zipLocation: URI, type: ExtensionType): Promise<IExtensionIdentifier> {
@@ -122,4 +122,4 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
getExtensionsReport(): Promise<IReportedExtension[]> {
return Promise.resolve(this.channel.call('getExtensionsReport'));
}
}
}
+34 -15
View File
@@ -124,11 +124,6 @@ export interface IFileService {
*/
resolveStreamContent(resource: URI, options?: IResolveContentOptions): Promise<IStreamContent>;
/**
* @deprecated use writeFile instead
*/
updateContent(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise<IFileStatWithMetadata>;
/**
* Updates the content replacing its previous value.
*/
@@ -148,18 +143,13 @@ export interface IFileService {
*/
copy(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>;
/**
* @deprecated use createFile2 instead
*/
createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise<IFileStatWithMetadata>;
/**
* Creates a new file with the given path and optional contents. The returned promise
* will have the stat model object as a result.
*
* The optional parameter content can be used as value to fill into the new file.
*/
createFile2(resource: URI, bufferOrReadable?: VSBuffer | VSBufferReadable, options?: ICreateFileOptions): Promise<IFileStatWithMetadata>;
createFile(resource: URI, bufferOrReadable?: VSBuffer | VSBufferReadable, options?: ICreateFileOptions): Promise<IFileStatWithMetadata>;
/**
* Creates a new folder with the given path. The returned promise
@@ -666,6 +656,7 @@ export interface ITextSnapshot {
*/
export function snapshotToString(snapshot: ITextSnapshot): string {
const chunks: string[] = [];
let chunk: string | null;
while (typeof (chunk = snapshot.read()) === 'string') {
chunks.push(chunk);
@@ -674,6 +665,22 @@ export function snapshotToString(snapshot: ITextSnapshot): string {
return chunks.join('');
}
export function stringToSnapshot(value: string): ITextSnapshot {
let done = false;
return {
read(): string | null {
if (!done) {
done = true;
return value;
}
return null;
}
};
}
export class TextSnapshotReadable implements VSBufferReadable {
private preambleHandled: boolean;
@@ -703,6 +710,22 @@ export class TextSnapshotReadable implements VSBufferReadable {
}
}
export function toBufferOrReadable(value: string): VSBuffer;
export function toBufferOrReadable(value: ITextSnapshot): VSBufferReadable;
export function toBufferOrReadable(value: string | ITextSnapshot): VSBuffer | VSBufferReadable;
export function toBufferOrReadable(value: string | ITextSnapshot | undefined): VSBuffer | VSBufferReadable | undefined;
export function toBufferOrReadable(value: string | ITextSnapshot | undefined): VSBuffer | VSBufferReadable | undefined {
if (typeof value === 'undefined') {
return undefined;
}
if (typeof value === 'string') {
return VSBuffer.fromString(value);
}
return new TextSnapshotReadable(value);
}
/**
* Streamable content and meta information of a file.
*/
@@ -1158,8 +1181,4 @@ export interface ILegacyFileService extends IDisposable {
resolveContent(resource: URI, options?: IResolveContentOptions): Promise<IContent>;
resolveStreamContent(resource: URI, options?: IResolveContentOptions): Promise<IStreamContent>;
updateContent(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise<IFileStatWithMetadata>;
createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise<IFileStatWithMetadata>;
}
@@ -15,7 +15,7 @@ import { isWindows, isMacintosh } from 'vs/base/common/platform';
import { IWorkspaceIdentifier, IWorkspacesMainService, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IHistoryMainService, IRecentlyOpened, isRecentWorkspace, isRecentFolder, IRecent, isRecentFile, IRecentFolder, IRecentWorkspace, IRecentFile } from 'vs/platform/history/common/history';
import { ThrottledDelayer } from 'vs/base/common/async';
import { isEqual as areResourcesEqual, dirname, originalFSPath } from 'vs/base/common/resources';
import { isEqual as areResourcesEqual, dirname, originalFSPath, basename } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -29,6 +29,12 @@ export class HistoryMainService implements IHistoryMainService {
private static readonly MAX_MACOS_DOCK_RECENT_FOLDERS = 10;
private static readonly MAX_MACOS_DOCK_RECENT_FILES = 5;
// Exclude some very common files from the dock/taskbar
private static readonly COMMON_FILES_FILTER = [
'COMMIT_EDITMSG',
'MERGE_MSG'
];
private static readonly recentlyOpenedStorageKey = 'openedPathsList';
_serviceBrand: any;
@@ -52,17 +58,29 @@ export class HistoryMainService implements IHistoryMainService {
const files: IRecentFile[] = [];
for (let curr of newlyAdded) {
// Workspace
if (isRecentWorkspace(curr)) {
if (!this.workspacesMainService.isUntitledWorkspace(curr.workspace) && indexOfWorkspace(workspaces, curr.workspace) === -1) {
workspaces.push(curr);
}
} else if (isRecentFolder(curr)) {
}
// Folder
else if (isRecentFolder(curr)) {
if (indexOfFolder(workspaces, curr.folderUri) === -1) {
workspaces.push(curr);
}
} else {
if (indexOfFile(files, curr.fileUri) === -1) {
}
// File
else {
const alreadyExistsInHistory = indexOfFile(files, curr.fileUri) >= 0;
const shouldBeFiltered = curr.fileUri.scheme === Schemas.file && HistoryMainService.COMMON_FILES_FILTER.indexOf(basename(curr.fileUri)) >= 0;
if (!alreadyExistsInHistory && !shouldBeFiltered) {
files.push(curr);
// Add to recent documents (Windows only, macOS later)
if (isWindows && curr.fileUri.scheme === Schemas.file) {
app.addRecentDocument(curr.fileUri.fsPath);
@@ -76,6 +94,7 @@ export class HistoryMainService implements IHistoryMainService {
if (workspaces.length > HistoryMainService.MAX_TOTAL_RECENT_ENTRIES) {
workspaces.length = HistoryMainService.MAX_TOTAL_RECENT_ENTRIES;
}
if (files.length > HistoryMainService.MAX_TOTAL_RECENT_ENTRIES) {
files.length = HistoryMainService.MAX_TOTAL_RECENT_ENTRIES;
}
@@ -143,7 +162,7 @@ export class HistoryMainService implements IHistoryMainService {
// Fill in files
for (let i = 0, entries = 0; i < mru.files.length && entries < HistoryMainService.MAX_MACOS_DOCK_RECENT_FILES; i++) {
const loc = location(mru.files[i]);
if (loc.scheme === Schemas.file) {
if (loc.scheme === Schemas.file && HistoryMainService.COMMON_FILES_FILTER.indexOf(basename(loc)) === -1) {
const filePath = originalFSPath(loc);
if (await exists(filePath)) {
app.addRecentDocument(filePath);
@@ -162,7 +181,6 @@ export class HistoryMainService implements IHistoryMainService {
}
getRecentlyOpened(currentWorkspace?: IWorkspaceIdentifier, currentFolder?: ISingleFolderWorkspaceIdentifier, currentFiles?: IPath[]): IRecentlyOpened {
const workspaces: Array<IRecentFolder | IRecentWorkspace> = [];
const files: IRecentFile[] = [];
@@ -170,6 +188,7 @@ export class HistoryMainService implements IHistoryMainService {
if (currentWorkspace && !this.workspacesMainService.isUntitledWorkspace(currentWorkspace)) {
workspaces.push({ workspace: currentWorkspace });
}
if (currentFolder) {
workspaces.push({ folderUri: currentFolder });
}
@@ -183,12 +202,14 @@ export class HistoryMainService implements IHistoryMainService {
}
}
}
this.addEntriesFromStorage(workspaces, files);
return { workspaces, files };
}
private addEntriesFromStorage(workspaces: Array<IRecentFolder | IRecentWorkspace>, files: IRecentFile[]) {
// Get from storage
let recents = this.getRecentlyOpenedFromStorage();
for (let recent of recents.workspaces) {
@@ -199,6 +220,7 @@ export class HistoryMainService implements IHistoryMainService {
workspaces.push(recent);
}
}
for (let recent of recents.files) {
let index = indexOfFile(files, recent.fileUri);
if (index >= 0) {
@@ -211,11 +233,13 @@ export class HistoryMainService implements IHistoryMainService {
private getRecentlyOpenedFromStorage(): IRecentlyOpened {
const storedRecents = this.stateService.getItem<RecentlyOpenedStorageData>(HistoryMainService.recentlyOpenedStorageKey);
return restoreRecentlyOpened(storedRecents);
}
private saveRecentlyOpened(recent: IRecentlyOpened): void {
const serialized = toStoreData(recent);
this.stateService.setItem(HistoryMainService.recentlyOpenedStorageKey, serialized);
}
@@ -268,16 +292,17 @@ export class HistoryMainService implements IHistoryMainService {
items: arrays.coalesce(this.getRecentlyOpened().workspaces.slice(0, 7 /* limit number of entries here */).map(recent => {
const workspace = isRecentWorkspace(recent) ? recent.workspace : recent.folderUri;
const title = recent.label || getSimpleWorkspaceLabel(workspace, this.environmentService.untitledWorkspacesHome);
let description;
let args;
if (isSingleFolderWorkspaceIdentifier(workspace)) {
const parentFolder = dirname(workspace);
description = nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), getPathLabel(parentFolder, this.environmentService));
description = nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), getPathLabel(dirname(workspace), this.environmentService));
args = `--folder-uri "${workspace.toString()}"`;
} else {
description = nls.localize('codeWorkspace', "Code Workspace");
args = `--file-uri "${workspace.configPath.toString()}"`;
}
return <Electron.JumpListItem>{
type: 'task',
title,
@@ -308,9 +333,11 @@ function location(recent: IRecent): URI {
if (isRecentFolder(recent)) {
return recent.folderUri;
}
if (isRecentFile(recent)) {
return recent.fileUri;
}
return recent.workspace.configPath;
}
@@ -55,7 +55,7 @@ export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefine
result.workspaces.push({ workspace: { id: workspace['id'], configPath: URI.file(workspace['configPath']) } });
} else if (workspace && typeof workspace['path'] === 'string' && typeof workspace['scheme'] === 'string') {
// added by 1.26-insiders
result.workspaces.push({ folderUri: URI.revive(workspace) });
result.workspaces.push({ folderUri: URI.revive(<UriComponents>workspace) });
}
}
}
+1
View File
@@ -56,6 +56,7 @@ export interface IssueReporterData extends WindowData {
styles: IssueReporterStyles;
enabledExtensions: IssueReporterExtensionData[];
issueType?: IssueType;
extensionId?: string;
}
export interface ISettingSearchResult {
+2 -1
View File
@@ -30,7 +30,8 @@ export const enum ProgressLocation {
Scm = 3,
Extensions = 5,
Window = 10,
Notification = 15
Notification = 15,
Dialog = 20
}
export interface IProgressOptions {
@@ -19,7 +19,6 @@ export interface IRemoteAgentEnvironment {
userHome: URI;
extensions: IExtensionDescription[];
os: OperatingSystem;
syncExtensions: boolean;
}
export interface RemoteAgentConnectionContext {
@@ -22,31 +22,30 @@ export interface IFileChangeDto {
export class RemoteExtensionsFileSystemProvider extends Disposable implements IFileSystemProvider {
private readonly _session: string;
private readonly _channel: IChannel;
private readonly session: string = generateUuid();
private readonly _onDidChange = this._register(new Emitter<IFileChange[]>());
readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChange.event;
public capabilities: FileSystemProviderCapabilities;
private readonly _onDidChangeCapabilities = this._register(new Emitter<void>());
readonly onDidChangeCapabilities: Event<void> = this._onDidChangeCapabilities.event;
constructor(channel: IChannel, environment: Promise<IRemoteAgentEnvironment | null>) {
private _capabilities: FileSystemProviderCapabilities;
get capabilities(): FileSystemProviderCapabilities { return this._capabilities; }
constructor(private readonly channel: IChannel, environment: Promise<IRemoteAgentEnvironment | null>) {
super();
this._session = generateUuid();
this._channel = channel;
this.setCaseSensitive(true);
environment.then(remoteAgentEnvironment => this.setCaseSensitive(!!(remoteAgentEnvironment && remoteAgentEnvironment.os === OperatingSystem.Linux)));
this._channel.listen<IFileChangeDto[]>('filechange', [this._session])((events) => {
this._onDidChange.fire(events.map(RemoteExtensionsFileSystemProvider._createFileChange));
});
this.registerListeners();
}
dispose(): void {
super.dispose();
private registerListeners(): void {
this._register(this.channel.listen<IFileChangeDto[]>('filechange', [this.session])((events) => {
this._onDidChange.fire(events.map(event => ({ resource: URI.revive(event.resource), type: event.type })));
}));
}
setCaseSensitive(isCaseSensitive: boolean) {
@@ -54,58 +53,55 @@ export class RemoteExtensionsFileSystemProvider extends Disposable implements IF
FileSystemProviderCapabilities.FileReadWrite
| FileSystemProviderCapabilities.FileFolderCopy
);
if (isCaseSensitive) {
capabilities |= FileSystemProviderCapabilities.PathCaseSensitive;
}
this.capabilities = capabilities;
this._capabilities = capabilities;
this._onDidChangeCapabilities.fire(undefined);
}
watch(resource: URI, opts: IWatchOptions): IDisposable {
const req = Math.random();
this._channel.call('watch', [this._session, req, resource, opts]);
return toDisposable(() => {
this._channel.call('unwatch', [this._session, req]);
});
}
private static _createFileChange(dto: IFileChangeDto): IFileChange {
return { resource: URI.revive(dto.resource), type: dto.type };
}
// --- forwarding calls
stat(resource: URI): Promise<IStat> {
return this._channel.call('stat', [resource]);
return this.channel.call('stat', [resource]);
}
async readFile(resource: URI): Promise<Uint8Array> {
const buff = <VSBuffer>await this._channel.call('readFile', [resource]);
const buff = <VSBuffer>await this.channel.call('readFile', [resource]);
return buff.buffer;
}
writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
const contents = VSBuffer.wrap(content);
return this._channel.call('writeFile', [resource, contents, opts]);
return this.channel.call('writeFile', [resource, VSBuffer.wrap(content), opts]);
}
delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
return this._channel.call('delete', [resource, opts]);
return this.channel.call('delete', [resource, opts]);
}
mkdir(resource: URI): Promise<void> {
return this._channel.call('mkdir', [resource]);
return this.channel.call('mkdir', [resource]);
}
readdir(resource: URI): Promise<[string, FileType][]> {
return this._channel.call('readdir', [resource]);
return this.channel.call('readdir', [resource]);
}
rename(resource: URI, target: URI, opts: FileOverwriteOptions): Promise<void> {
return this._channel.call('rename', [resource, target, opts]);
return this.channel.call('rename', [resource, target, opts]);
}
copy(resource: URI, target: URI, opts: FileOverwriteOptions): Promise<void> {
return this._channel.call('copy', [resource, target, opts]);
return this.channel.call('copy', [resource, target, opts]);
}
watch(resource: URI, opts: IWatchOptions): IDisposable {
const req = Math.random();
this.channel.call('watch', [this.session, req, resource, opts]);
return toDisposable(() => this.channel.call('unwatch', [this.session, req]));
}
}
@@ -3,69 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { binarySearch } from 'vs/base/common/arrays';
import { toDisposable } from 'vs/base/common/lifecycle';
import { globals } from 'vs/base/common/platform';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
import * as Errors from 'vs/base/common/errors';
import { safeStringify } from 'vs/base/common/objects';
import BaseErrorTelemetry, { ErrorEvent } from '../common/errorTelemetry';
/* __GDPR__FRAGMENT__
"ErrorEvent" : {
"stack": { "classification": "CustomerContent", "purpose": "PerformanceAndHealth" },
"message" : { "classification": "CustomerContent", "purpose": "PerformanceAndHealth" },
"filename" : { "classification": "CustomerContent", "purpose": "PerformanceAndHealth" },
"callstack": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"msg" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"file" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"line": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"column": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"uncaught_error_name": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"uncaught_error_msg": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"count": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth", "isMeasurement": true }
}
*/
interface ErrorEvent {
callstack: string;
msg?: string;
file?: string;
line?: number;
column?: number;
uncaught_error_name?: string;
uncaught_error_msg?: string;
count?: number;
}
namespace ErrorEvent {
export function compare(a: ErrorEvent, b: ErrorEvent) {
if (a.callstack < b.callstack) {
return -1;
} else if (a.callstack > b.callstack) {
return 1;
}
return 0;
}
}
export default class ErrorTelemetry {
public static ERROR_FLUSH_TIMEOUT: number = 5 * 1000;
private _telemetryService: ITelemetryService;
private _flushDelay: number;
private _flushHandle: any = -1;
private _buffer: ErrorEvent[] = [];
private _disposables: IDisposable[] = [];
constructor(telemetryService: ITelemetryService, flushDelay = ErrorTelemetry.ERROR_FLUSH_TIMEOUT) {
this._telemetryService = telemetryService;
this._flushDelay = flushDelay;
// (1) check for unexpected but handled errors
const unbind = Errors.errorHandler.addListener((err) => this._onErrorEvent(err));
this._disposables.push(toDisposable(unbind));
// (2) check for uncaught global errors
export default class ErrorTelemetry extends BaseErrorTelemetry {
protected installErrorListeners(): void {
let oldOnError: Function;
let that = this;
if (typeof globals.onerror === 'function') {
@@ -84,37 +27,7 @@ export default class ErrorTelemetry {
}));
}
dispose() {
clearTimeout(this._flushHandle);
this._flushBuffer();
this._disposables = dispose(this._disposables);
}
private _onErrorEvent(err: any): void {
if (!err) {
return;
}
// unwrap nested errors from loader
if (err.detail && err.detail.stack) {
err = err.detail;
}
// work around behavior in workerServer.ts that breaks up Error.stack
let callstack = Array.isArray(err.stack) ? err.stack.join('\n') : err.stack;
let msg = err.message ? err.message : safeStringify(err);
// errors without a stack are not useful telemetry
if (!callstack) {
return;
}
this._enqueue({ msg, callstack });
}
private _onUncaughtError(msg: string, file: string, line: number, column?: number, err?: any): void {
let data: ErrorEvent = {
callstack: msg,
msg,
@@ -138,38 +51,4 @@ export default class ErrorTelemetry {
this._enqueue(data);
}
private _enqueue(e: ErrorEvent): void {
const idx = binarySearch(this._buffer, e, ErrorEvent.compare);
if (idx < 0) {
e.count = 1;
this._buffer.splice(~idx, 0, e);
} else {
if (!this._buffer[idx].count) {
this._buffer[idx].count = 0;
}
this._buffer[idx].count! += 1;
}
if (this._flushHandle === -1) {
this._flushHandle = setTimeout(() => {
this._flushBuffer();
this._flushHandle = -1;
}, this._flushDelay);
}
}
private _flushBuffer(): void {
for (let error of this._buffer) {
/* __GDPR__
"UnhandledError" : {
"${include}": [ "${ErrorEvent}" ]
}
*/
// {{SQL CARBON EDIT}}
//this._telemetryService.publicLog('UnhandledError', error, true);
}
this._buffer.length = 0;
}
}
@@ -0,0 +1,137 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { binarySearch } from 'vs/base/common/arrays';
import * as Errors from 'vs/base/common/errors';
import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { safeStringify } from 'vs/base/common/objects';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
/* __GDPR__FRAGMENT__
"ErrorEvent" : {
"stack": { "classification": "CustomerContent", "purpose": "PerformanceAndHealth" },
"message" : { "classification": "CustomerContent", "purpose": "PerformanceAndHealth" },
"filename" : { "classification": "CustomerContent", "purpose": "PerformanceAndHealth" },
"callstack": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"msg" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"file" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"line": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"column": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"uncaught_error_name": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"uncaught_error_msg": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"count": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth", "isMeasurement": true }
}
*/
export interface ErrorEvent {
callstack: string;
msg?: string;
file?: string;
line?: number;
column?: number;
uncaught_error_name?: string;
uncaught_error_msg?: string;
count?: number;
}
export namespace ErrorEvent {
export function compare(a: ErrorEvent, b: ErrorEvent) {
if (a.callstack < b.callstack) {
return -1;
} else if (a.callstack > b.callstack) {
return 1;
}
return 0;
}
}
export default abstract class BaseErrorTelemetry {
public static ERROR_FLUSH_TIMEOUT: number = 5 * 1000;
private _telemetryService: ITelemetryService;
private _flushDelay: number;
private _flushHandle: any = -1;
private _buffer: ErrorEvent[] = [];
protected _disposables: IDisposable[] = [];
constructor(telemetryService: ITelemetryService, flushDelay = BaseErrorTelemetry.ERROR_FLUSH_TIMEOUT) {
this._telemetryService = telemetryService;
this._flushDelay = flushDelay;
// (1) check for unexpected but handled errors
const unbind = Errors.errorHandler.addListener((err) => this._onErrorEvent(err));
this._disposables.push(toDisposable(unbind));
// (2) install implementation-specific error listeners
this.installErrorListeners();
}
dispose() {
clearTimeout(this._flushHandle);
this._flushBuffer();
this._disposables = dispose(this._disposables);
}
protected installErrorListeners(): void {
// to override
}
private _onErrorEvent(err: any): void {
if (!err) {
return;
}
// unwrap nested errors from loader
if (err.detail && err.detail.stack) {
err = err.detail;
}
// work around behavior in workerServer.ts that breaks up Error.stack
let callstack = Array.isArray(err.stack) ? err.stack.join('\n') : err.stack;
let msg = err.message ? err.message : safeStringify(err);
// errors without a stack are not useful telemetry
if (!callstack) {
return;
}
this._enqueue({ msg, callstack });
}
protected _enqueue(e: ErrorEvent): void {
const idx = binarySearch(this._buffer, e, ErrorEvent.compare);
if (idx < 0) {
e.count = 1;
this._buffer.splice(~idx, 0, e);
} else {
if (!this._buffer[idx].count) {
this._buffer[idx].count = 0;
}
this._buffer[idx].count! += 1;
}
if (this._flushHandle === -1) {
this._flushHandle = setTimeout(() => {
this._flushBuffer();
this._flushHandle = -1;
}, this._flushDelay);
}
}
private _flushBuffer(): void {
for (let error of this._buffer) {
/* __GDPR__
"UnhandledError" : {
"${include}": [ "${ErrorEvent}" ]
}
*/
// {{SQL CARBON EDIT}}
// this._telemetryService.publicLog('UnhandledError', error, true);
}
this._buffer.length = 0;
}
}
@@ -29,6 +29,8 @@ export interface ITelemetryService {
*/
publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise<void>;
setEnabled(value: boolean): void;
getTelemetryInfo(): Promise<ITelemetryInfo>;
isOptedIn: boolean;
@@ -31,6 +31,7 @@ export class TelemetryService implements ITelemetryService {
private _commonProperties: Promise<{ [name: string]: any; }>;
private _piiPaths: string[];
private _userOptIn: boolean;
private _enabled: boolean;
private _disposables: IDisposable[] = [];
private _cleanupPatterns: RegExp[] = [];
@@ -43,6 +44,7 @@ export class TelemetryService implements ITelemetryService {
this._commonProperties = config.commonProperties || Promise.resolve({});
this._piiPaths = config.piiPaths || [];
this._userOptIn = true;
this._enabled = true;
// static cleanup pattern for: `file:///DANGEROUS/PATH/resources/app/Useful/Information`
this._cleanupPatterns = [/file:\/\/\/.*?\/resources\/app\//gi];
@@ -74,13 +76,17 @@ export class TelemetryService implements ITelemetryService {
}
}
setEnabled(value: boolean): void {
this._enabled = value;
}
private _updateUserOptIn(): void {
const config = this._configurationService.getValue<any>(TELEMETRY_SECTION_ID);
this._userOptIn = config ? config.enableTelemetry : this._userOptIn;
}
get isOptedIn(): boolean {
return this._userOptIn;
return this._userOptIn && this._enabled;
}
getTelemetryInfo(): Promise<ITelemetryInfo> {
@@ -100,7 +106,7 @@ export class TelemetryService implements ITelemetryService {
publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise<any> {
// don't send events when the user is optout
if (!this._userOptIn) {
if (!this.isOptedIn) {
return Promise.resolve(undefined);
}
@@ -14,6 +14,7 @@ export const NullTelemetryService = new class implements ITelemetryService {
publicLog(eventName: string, data?: ITelemetryData) {
return Promise.resolve(undefined);
}
setEnabled() { }
isOptedIn: true;
getTelemetryInfo(): Promise<ITelemetryInfo> {
return Promise.resolve({
@@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { onUnexpectedError } from 'vs/base/common/errors';
import BaseErrorTelemetry from '../common/errorTelemetry';
export default class ErrorTelemetry extends BaseErrorTelemetry {
protected installErrorListeners(): void {
// Print a console message when rejection isn't handled within N seconds. For details:
// see https://nodejs.org/api/process.html#process_event_unhandledrejection
// and https://nodejs.org/api/process.html#process_event_rejectionhandled
const unhandledPromises: Promise<any>[] = [];
process.on('unhandledRejection', (reason: any, promise: Promise<any>) => {
unhandledPromises.push(promise);
setTimeout(() => {
const idx = unhandledPromises.indexOf(promise);
if (idx >= 0) {
promise.catch(e => {
unhandledPromises.splice(idx, 1);
console.warn(`rejected promise not handled within 1 second: ${e}`);
if (e.stack) {
console.warn(`stack trace: ${e.stack}`);
}
onUnexpectedError(reason);
});
}
}, 1000);
});
process.on('rejectionHandled', (promise: Promise<any>) => {
const idx = unhandledPromises.indexOf(promise);
if (idx >= 0) {
unhandledPromises.splice(idx, 1);
}
});
// Print a console message when an exception isn't handled.
process.on('uncaughtException', (err: Error) => {
onUnexpectedError(err);
});
}
}
@@ -16,6 +16,8 @@ import * as json from 'vs/base/common/json';
import { Schemas } from 'vs/base/common/network';
import { normalizeDriveLetter } from 'vs/base/common/labels';
import { toSlashes } from 'vs/base/common/extpath';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
export const IWorkspacesMainService = createDecorator<IWorkspacesMainService>('workspacesMainService');
export const IWorkspacesService = createDecorator<IWorkspacesService>('workspacesService');
@@ -75,6 +77,7 @@ export interface IResolvedWorkspace extends IWorkspaceIdentifier {
export interface IStoredWorkspace {
folders: IStoredWorkspaceFolder[];
remoteAuthority?: string;
}
export interface IWorkspaceSavedEvent {
@@ -232,12 +235,15 @@ export function rewriteWorkspaceFileForNewLocation(rawWorkspaceContents: string,
// Preserve as much of the existing workspace as possible by using jsonEdit
// and only changing the folders portion.
let newRawWorkspaceContents = rawWorkspaceContents;
const edits = jsonEdit.setProperty(rawWorkspaceContents, ['folders'], rewrittenFolders, { insertSpaces: false, tabSize: 4, eol: (isLinux || isMacintosh) ? '\n' : '\r\n' });
edits.forEach(edit => {
newRawWorkspaceContents = jsonEdit.applyEdit(rawWorkspaceContents, edit);
});
return newRawWorkspaceContents;
const formattingOptions: FormattingOptions = { insertSpaces: false, tabSize: 4, eol: (isLinux || isMacintosh) ? '\n' : '\r\n' };
const edits = jsonEdit.setProperty(rawWorkspaceContents, ['folders'], rewrittenFolders, formattingOptions);
let newContent = jsonEdit.applyEdits(rawWorkspaceContents, edits);
if (storedWorkspace.remoteAuthority === getRemoteAuthority(targetConfigPathURI)) {
// unsaved remote workspaces have the remoteAuthority set. Remove it when no longer nexessary.
newContent = jsonEdit.applyEdits(newContent, jsonEdit.removeProperty(newContent, ['remoteAuthority'], formattingOptions));
}
return newContent;
}
function doParseStoredWorkspace(path: URI, contents: string): IStoredWorkspace {
+5
View File
@@ -5973,6 +5973,11 @@ declare module 'vscode' {
*/
export const appRoot: string;
/**
* The custom uri scheme the editor registers to in the operating system.
*/
export const uriScheme: string;
/**
* Represents the preferred user-language, like `de-CH`, `fr`, or `en-US`.
*/
-5
View File
@@ -616,11 +616,6 @@ declare module 'vscode' {
* An [event](#Event) that fires when the log level has changed.
*/
export const onDidChangeLogLevel: Event<LogLevel>;
/**
* The custom uri scheme the editor registers to in the operating system, like 'vscode', 'vscode-insiders'.
*/
export const uriScheme: string;
}
//#endregion
@@ -112,7 +112,7 @@ export class MainThreadDecorations implements MainThreadDecorationsShape {
const provider = this._provider.get(handle);
if (provider) {
const [emitter] = provider;
emitter.fire(resources && resources.map(URI.revive));
emitter.fire(resources && resources.map(r => URI.revive(r)));
}
}
@@ -162,7 +162,7 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable {
});
} else {
searchOp.addMatch({
resource: URI.revive(result)
resource: URI.revive(<UriComponents>result)
});
}
});
+8 -1
View File
@@ -47,7 +47,7 @@ export class OpenFolderAPICommand {
arg = { forceNewWindow: arg };
}
if (!uri) {
return executor.executeCommand('_files.pickFolderAndOpen', arg.forceNewWindow);
return executor.executeCommand('_files.pickFolderAndOpen', { forceNewWindow: arg.forceNewWindow });
}
const options: IOpenSettings = { forceNewWindow: arg.forceNewWindow, noRecentEntry: arg.noRecentEntry };
uri = URI.revive(uri);
@@ -144,6 +144,13 @@ export class RemoveFromRecentlyOpenedAPICommand {
}
CommandsRegistry.registerCommand(RemoveFromRecentlyOpenedAPICommand.ID, adjustHandler(RemoveFromRecentlyOpenedAPICommand.execute));
export class OpenIssueReporter {
public static ID = 'vscode.openIssueReporter';
public static execute(executor: ICommandsExecutor, extensionId: string): Promise<void> {
return executor.executeCommand('workbench.action.openIssueReporter', [extensionId]);
}
}
interface RecentEntry {
uri: URI;
type: 'workspace' | 'folder' | 'file';
@@ -35,16 +35,17 @@ const configurationEntrySchema: IJSONSchema = {
properties: {
isExecutable: {
type: 'boolean',
deprecationMessage: 'This property is deprecated. Instead use `scope` property and set it to `application` value.'
deprecationMessage: 'This property is deprecated. Instead use `scope` property and set it to `machine` value.'
},
scope: {
type: 'string',
enum: ['application', 'window', 'resource'],
enum: ['application', 'machine', 'window', 'resource'],
default: 'window',
enumDescriptions: [
nls.localize('scope.application.description', "Application specific configuration, which can be configured only in User settings."),
nls.localize('scope.window.description', "Window specific configuration, which can be configured in the User or Workspace settings."),
nls.localize('scope.resource.description', "Resource specific configuration, which can be configured in the User, Workspace or Folder settings.")
nls.localize('scope.application.description', "Application specific configuration, which can be configured only in local user settings."),
nls.localize('scope.machine.description', "Machine specific configuration, which can be configured only in local and remote user settings."),
nls.localize('scope.window.description', "Window specific configuration, which can be configured in the user or workspace settings."),
nls.localize('scope.resource.description', "Resource specific configuration, which can be configured in the user, workspace or folder settings.")
],
description: nls.localize('scope.description', "Scope in which the configuration is applicable. Available scopes are `window` and `resource`.")
},
@@ -161,7 +162,7 @@ configurationExtPoint.setHandler((extensions, { added, removed }) => {
validateProperties(configuration, extension);
configuration.id = node.id || extension.description.identifier.value;
configuration.contributedByExtension = true;
configuration.extensionInfo = { id: extension.description.identifier.value };
configuration.title = configuration.title || extension.description.displayName || extension.description.identifier.value;
configurations.push(configuration);
return configurations;
@@ -210,6 +211,8 @@ function validateProperties(configuration: IConfigurationNode, extension: IExten
if (propertyConfiguration.scope) {
if (propertyConfiguration.scope.toString() === 'application') {
propertyConfiguration.scope = ConfigurationScope.APPLICATION;
} else if (propertyConfiguration.scope.toString() === 'machine') {
propertyConfiguration.scope = ConfigurationScope.MACHINE;
} else if (propertyConfiguration.scope.toString() === 'resource') {
propertyConfiguration.scope = ConfigurationScope.RESOURCE;
} else {
@@ -15,7 +15,7 @@ import * as search from 'vs/workbench/contrib/search/common/search';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { CustomCodeAction } from 'vs/workbench/api/common/extHostLanguageFeatures';
import { ICommandsExecutor, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand } from './apiCommands';
import { ICommandsExecutor, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand, OpenIssueReporter } from './apiCommands';
import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
@@ -258,6 +258,13 @@ export class ExtHostApiCommands {
{ name: 'layout', description: 'The editor layout to set.', constraint: (value: EditorGroupLayout) => typeof value === 'object' && Array.isArray(value.groups) }
]
});
this._register(OpenIssueReporter.ID, adjustHandler(OpenIssueReporter.execute), {
description: 'Opens the issue reporter with the provided extension id as the selected source',
args: [
{ name: 'extensionId', description: 'extensionId to report an issue on', constraint: (value: any) => typeof value === 'string' }
]
});
}
// --- command impl
@@ -17,7 +17,7 @@ export class ExtHostDialogs {
showOpenDialog(options: vscode.OpenDialogOptions): Promise<URI[] | undefined> {
return this._proxy.$showOpenDialog(options).then(filepaths => {
return filepaths ? filepaths.map(URI.revive) : undefined;
return filepaths ? filepaths.map(p => URI.revive(p)) : undefined;
});
}
@@ -0,0 +1,59 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable } from 'vs/base/common/lifecycle';
import { IExtensionMemento } from 'vs/workbench/api/common/extHostExtensionActivator';
import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
export class ExtensionMemento implements IExtensionMemento {
private readonly _id: string;
private readonly _shared: boolean;
private readonly _storage: ExtHostStorage;
private readonly _init: Promise<ExtensionMemento>;
private _value: { [n: string]: any; };
private readonly _storageListener: IDisposable;
constructor(id: string, global: boolean, storage: ExtHostStorage) {
this._id = id;
this._shared = global;
this._storage = storage;
this._init = this._storage.getValue(this._shared, this._id, Object.create(null)).then(value => {
this._value = value;
return this;
});
this._storageListener = this._storage.onDidChangeStorage(e => {
if (e.shared === this._shared && e.key === this._id) {
this._value = e.value;
}
});
}
get whenReady(): Promise<ExtensionMemento> {
return this._init;
}
get<T>(key: string, defaultValue: T): T {
let value = this._value[key];
if (typeof value === 'undefined') {
value = defaultValue;
}
return value;
}
update(key: string, value: any): Promise<boolean> {
this._value[key] = value;
return this._storage
.setValue(this._shared, this._id, this._value)
.then(() => true);
}
dispose(): void {
this._storageListener.dispose();
}
}
+2 -1
View File
@@ -8,6 +8,7 @@ import * as vscode from 'vscode';
import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { VSBuffer } from 'vs/base/common/buffer';
export abstract class AbstractExtHostOutputChannel extends Disposable implements vscode.OutputChannel {
@@ -35,7 +36,7 @@ export abstract class AbstractExtHostOutputChannel extends Disposable implements
append(value: string): void {
this.validate();
this._offset += value ? Buffer.from(value).byteLength : 0;
this._offset += value ? VSBuffer.fromString(value).byteLength : 0;
}
update(): void {
@@ -478,8 +478,8 @@ export namespace WorkspaceEdit {
);
} else {
result.renameFile(
URI.revive((<ResourceFileEditDto>edit).oldUri),
URI.revive((<ResourceFileEditDto>edit).newUri),
URI.revive((<ResourceFileEditDto>edit).oldUri!),
URI.revive((<ResourceFileEditDto>edit).newUri!),
(<ResourceFileEditDto>edit).options
);
}
@@ -420,7 +420,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
}
return this._proxy.$startFileSearch(includePattern, includeFolder, excludePatternOrDisregardExcludes, maxResults, token)
.then(data => Array.isArray(data) ? data.map(URI.revive) : []);
.then(data => Array.isArray(data) ? data.map(d => URI.revive(d)) : []);
}
findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: ExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.TextSearchComplete | undefined> {
@@ -414,7 +414,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
function reviveWebviewOptions(options: WebviewInputOptions): WebviewInputOptions {
return {
...options,
localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(URI.revive) : undefined,
localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(r => URI.revive(r)) : undefined,
};
}
+16 -248
View File
@@ -3,11 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import * as path from 'vs/base/common/path';
import Severity from 'vs/base/common/severity';
import { URI } from 'vs/base/common/uri';
@@ -16,7 +14,7 @@ import { OverviewRulerLane } from 'vs/editor/common/model';
import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration';
import { score } from 'vs/editor/common/modes/languageSelector';
import * as files from 'vs/platform/files/common/files';
import { ExtHostContext, IInitData, IMainContext, MainContext, MainThreadKeytarShape, IEnvironment, MainThreadWindowShape, MainThreadTelemetryShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostContext, IInitData, IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands';
import { ExtHostClipboard } from 'vs/workbench/api/common/extHostClipboard';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
@@ -47,7 +45,7 @@ import { LogOutputChannelFactory } from 'vs/workbench/api/node/extHostOutputServ
import { ExtHostProgress } from 'vs/workbench/api/common/extHostProgress';
import { ExtHostQuickOpen } from 'vs/workbench/api/common/extHostQuickOpen';
import { ExtHostSCM } from 'vs/workbench/api/common/extHostSCM';
import { ExtHostSearch } from 'vs/workbench/api/node/extHostSearch';
import { ExtHostSearch, registerEHSearchProviders } from 'vs/workbench/api/node/extHostSearch';
import { ExtHostStatusBar } from 'vs/workbench/api/common/extHostStatusBar';
import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import { ExtHostTask } from 'vs/workbench/api/node/extHostTask';
@@ -60,7 +58,7 @@ import { ExtHostUrls } from 'vs/workbench/api/common/extHostUrls';
import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview';
import { ExtHostWindow } from 'vs/workbench/api/common/extHostWindow';
import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { throwProposedApiError, checkProposedApiEnabled, nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { throwProposedApiError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import * as vscode from 'vscode';
@@ -69,7 +67,7 @@ import { originalFSPath } from 'vs/base/common/resources';
import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
import { withNullAsUndefined } from 'vs/base/common/types';
import { values } from 'vs/base/common/collections';
import { endsWith } from 'vs/base/common/strings';
import { Schemas } from 'vs/base/common/network';
export interface IExtensionApiFactory {
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@@ -93,11 +91,11 @@ export function createApiFactory(
extHostConfiguration: ExtHostConfiguration,
extensionService: ExtHostExtensionService,
extHostLogService: ExtHostLogService,
extHostStorage: ExtHostStorage
extHostStorage: ExtHostStorage,
schemeTransformer: ISchemeTransformer | null,
outputChannelName: string
): IExtensionApiFactory {
const schemeTransformer: ISchemeTransformer | null = null;
// Addressable instances
rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService);
const extHostHeapService = rpcProtocol.set(ExtHostContext.ExtHostHeapService, new ExtHostHeapService());
@@ -131,6 +129,14 @@ export function createApiFactory(
const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, new ExtHostOutputService(LogOutputChannelFactory, initData.logsLocation, rpcProtocol));
rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage);
if (initData.remoteAuthority) {
extHostTask.registerTaskSystem(Schemas.vscodeRemote, {
scheme: Schemas.vscodeRemote,
authority: initData.remoteAuthority,
platform: process.platform
});
registerEHSearchProviders(extHostSearch, extHostLogService);
const cliServer = new CLIServer(extHostCommands);
process.env['VSCODE_IPC_HOOK_CLI'] = cliServer.ipcHandlePath;
}
@@ -147,8 +153,7 @@ export function createApiFactory(
const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments);
// Register an output channel for exthost log
const name = localize('extensionsLog', "Extension Host");
extHostOutputService.createOutputChannelFromLogFile(name, extHostLogService.logFile);
extHostOutputService.createOutputChannelFromLogFile(outputChannelName, extHostLogService.logFile);
// Register API-ish commands
ExtHostApiCommands.register(extHostCommands);
@@ -881,240 +886,3 @@ class Extension<T> implements vscode.Extension<T> {
return this._extensionService.activateByIdWithErrors(this._identifier, new ExtensionActivatedByAPI(false)).then(() => this.exports);
}
}
interface LoadFunction {
(request: string, parent: { filename: string; }, isMain: any): any;
}
interface INodeModuleFactory {
readonly nodeModuleName: string | string[];
load(request: string, parent: { filename: string; }, isMain: any, original: LoadFunction): any;
alternaiveModuleName?(name: string): string | undefined;
}
export class NodeModuleRequireInterceptor {
public static INSTANCE = new NodeModuleRequireInterceptor();
private readonly _factories: Map<string, INodeModuleFactory>;
private readonly _alternatives: ((moduleName: string) => string | undefined)[];
constructor() {
this._factories = new Map<string, INodeModuleFactory>();
this._alternatives = [];
this._installInterceptor(this._factories, this._alternatives);
}
private _installInterceptor(factories: Map<string, INodeModuleFactory>, alternatives: ((moduleName: string) => string | undefined)[]): void {
const node_module = <any>require.__$__nodeRequire('module');
const original = node_module._load;
node_module._load = function load(request: string, parent: { filename: string; }, isMain: any) {
for (let alternativeModuleName of alternatives) {
let alternative = alternativeModuleName(request);
if (alternative) {
request = alternative;
break;
}
}
if (!factories.has(request)) {
return original.apply(this, arguments);
}
return factories.get(request)!.load(request, parent, isMain, original);
};
}
public register(interceptor: INodeModuleFactory): void {
if (Array.isArray(interceptor.nodeModuleName)) {
for (let moduleName of interceptor.nodeModuleName) {
this._factories.set(moduleName, interceptor);
}
} else {
this._factories.set(interceptor.nodeModuleName, interceptor);
}
if (typeof interceptor.alternaiveModuleName === 'function') {
this._alternatives.push((moduleName) => {
return interceptor.alternaiveModuleName!(moduleName);
});
}
}
}
export class VSCodeNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName = 'vscode';
private readonly _extApiImpl = new Map<string, typeof vscode>();
private _defaultApiImpl: typeof vscode;
constructor(
private readonly _apiFactory: IExtensionApiFactory,
private readonly _extensionPaths: TernarySearchTree<IExtensionDescription>,
private readonly _extensionRegistry: ExtensionDescriptionRegistry,
private readonly _configProvider: ExtHostConfigProvider
) {
}
public load(request: string, parent: { filename: string; }): any {
// get extension id from filename and api for extension
const ext = this._extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
if (ext) {
let apiImpl = this._extApiImpl.get(ExtensionIdentifier.toKey(ext.identifier));
if (!apiImpl) {
apiImpl = this._apiFactory(ext, this._extensionRegistry, this._configProvider);
this._extApiImpl.set(ExtensionIdentifier.toKey(ext.identifier), apiImpl);
}
return apiImpl;
}
// fall back to a default implementation
if (!this._defaultApiImpl) {
let extensionPathsPretty = '';
this._extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.identifier.value}\n`);
console.warn(`Could not identify extension for 'vscode' require call from ${parent.filename}. These are the extension path mappings: \n${extensionPathsPretty}`);
this._defaultApiImpl = this._apiFactory(nullExtensionDescription, this._extensionRegistry, this._configProvider);
}
return this._defaultApiImpl;
}
}
interface IKeytarModule {
getPassword(service: string, account: string): Promise<string | null>;
setPassword(service: string, account: string, password: string): Promise<void>;
deletePassword(service: string, account: string): Promise<boolean>;
findPassword(service: string): Promise<string | null>;
}
export class KeytarNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName: string = 'keytar';
private alternativeNames: Set<string> | undefined;
private _impl: IKeytarModule;
constructor(mainThreadKeytar: MainThreadKeytarShape, environment: IEnvironment) {
if (environment.appRoot) {
let appRoot = environment.appRoot.fsPath;
if (process.platform === 'win32') {
appRoot = appRoot.replace(/\\/g, '/');
}
if (appRoot[appRoot.length - 1] === '/') {
appRoot = appRoot.substr(0, appRoot.length - 1);
}
this.alternativeNames = new Set();
this.alternativeNames.add(`${appRoot}/node_modules.asar/keytar`);
this.alternativeNames.add(`${appRoot}/node_modules/keytar`);
}
this._impl = {
getPassword: (service: string, account: string): Promise<string | null> => {
return mainThreadKeytar.$getPassword(service, account);
},
setPassword: (service: string, account: string, password: string): Promise<void> => {
return mainThreadKeytar.$setPassword(service, account, password);
},
deletePassword: (service: string, account: string): Promise<boolean> => {
return mainThreadKeytar.$deletePassword(service, account);
},
findPassword: (service: string): Promise<string | null> => {
return mainThreadKeytar.$findPassword(service);
}
};
}
public load(request: string, parent: { filename: string; }): any {
return this._impl;
}
public alternaiveModuleName(name: string): string | undefined {
const length = name.length;
// We need at least something like: `?/keytar` which requires
// more than 7 characters.
if (length <= 7 || !this.alternativeNames) {
return undefined;
}
const sep = length - 7;
if ((name.charAt(sep) === '/' || name.charAt(sep) === '\\') && endsWith(name, 'keytar')) {
name = name.replace(/\\/g, '/');
if (this.alternativeNames.has(name)) {
return 'keytar';
}
}
return undefined;
}
}
interface OpenOptions {
wait: boolean;
app: string | string[];
}
interface IOriginalOpen {
(target: string, options?: OpenOptions): Thenable<any>;
}
interface IOpenModule {
(target: string, options?: OpenOptions): Thenable<void>;
}
export class OpenNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName: string[] = ['open', 'opn'];
private _extensionId: string | undefined;
private _original: IOriginalOpen;
private _impl: IOpenModule;
constructor(mainThreadWindow: MainThreadWindowShape, private _mainThreadTelemerty: MainThreadTelemetryShape, private readonly _extensionPaths: TernarySearchTree<IExtensionDescription>) {
this._impl = (target, options) => {
const uri: URI = URI.parse(target);
// If we have options use the original method.
if (options) {
return this.callOriginal(target, options);
}
if (uri.scheme === 'http' || uri.scheme === 'https') {
return mainThreadWindow.$openUri(uri);
} else if (uri.scheme === 'mailto') {
return mainThreadWindow.$openUri(uri);
}
return this.callOriginal(target, options);
};
}
public load(request: string, parent: { filename: string; }, isMain: any, original: LoadFunction): any {
// get extension id from filename and api for extension
const extension = this._extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
if (extension) {
this._extensionId = extension.identifier.value;
this.sendShimmingTelemetry();
}
this._original = original(request, parent, isMain);
return this._impl;
}
private callOriginal(target: string, options: OpenOptions | undefined): Thenable<any> {
this.sendNoForwardTelemetry();
return this._original(target, options);
}
private sendShimmingTelemetry(): void {
if (!this._extensionId) {
return;
}
/* __GDPR__
"shimming.open" : {
"extension": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this._mainThreadTelemerty.$publicLog('shimming.open', { extension: this._extensionId });
}
private sendNoForwardTelemetry(): void {
if (!this._extensionId) {
return;
}
/* __GDPR__
"shimming.open.call.noForward" : {
"extension": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this._mainThreadTelemerty.$publicLog('shimming.open.call.noForward', { extension: this._extensionId });
}
}
+34 -2
View File
@@ -26,6 +26,12 @@ export interface StatusPipeArgs {
type: 'status';
}
export interface RunCommandPipeArgs {
type: 'command';
command: string;
args: string[];
}
export class CLIServer {
private _server: http.Server;
@@ -61,7 +67,7 @@ export class CLIServer {
req.setEncoding('utf8');
req.on('data', (d: string) => chunks.push(d));
req.on('end', () => {
const data: OpenCommandPipeArgs | StatusPipeArgs | any = JSON.parse(chunks.join(''));
const data: OpenCommandPipeArgs | StatusPipeArgs | RunCommandPipeArgs | any = JSON.parse(chunks.join(''));
switch (data.type) {
case 'open':
this.open(data, res);
@@ -69,6 +75,10 @@ export class CLIServer {
case 'status':
this.getStatus(data, res);
break;
case 'command':
this.runCommand(data, res)
.catch(console.error);
break;
default:
res.writeHead(404);
res.write(`Unkown message type: ${data.type}`, err => {
@@ -135,6 +145,28 @@ export class CLIServer {
}
}
private async runCommand(data: RunCommandPipeArgs, res: http.ServerResponse) {
try {
const { command, args } = data;
const result = await this._commands.executeCommand(command, ...args);
res.writeHead(200);
res.write(JSON.stringify(result), err => {
if (err) {
console.error(err);
}
});
res.end();
} catch (err) {
res.writeHead(500);
res.write(String(err), err => {
if (err) {
console.error(err);
}
});
res.end();
}
}
dispose(): void {
this._server.close();
@@ -142,4 +174,4 @@ export class CLIServer {
fs.unlinkSync(this._ipcHandlePath);
}
}
}
}
@@ -9,17 +9,16 @@ import * as path from 'vs/base/common/path';
import { createApiFactory, initializeExtensionApi, ISqlExtensionApiFactory } from 'sql/workbench/api/node/sqlExtHost.api.impl';
import { originalFSPath } from 'vs/base/common/resources';
import { Barrier } from 'vs/base/common/async';
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { dispose, toDisposable } from 'vs/base/common/lifecycle';
import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import * as pfs from 'vs/base/node/pfs';
import { ILogService } from 'vs/platform/log/common/log';
// {{SQL CARBON EDIT}} - Remove createApiFactory initializeExtensionApi, and IExtensionApiFactory imports
//import { createApiFactory, IExtensionApiFactory, NodeModuleRequireInterceptor, VSCodeNodeModuleFactory } from 'vs/workbench/api/node/extHost.api.impl';
import { NodeModuleRequireInterceptor, KeytarNodeModuleFactory, OpenNodeModuleFactory } from 'vs/workbench/api/node/extHost.api.impl';
import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape, IStaticWorkspaceData } from 'vs/workbench/api/common/extHost.protocol';
// import { createApiFactory, IExtensionApiFactory } from 'vs/workbench/api/node/extHost.api.impl';
import { NodeModuleRequireInterceptor, VSCodeNodeModuleFactory, KeytarNodeModuleFactory, OpenNodeModuleFactory } from 'vs/workbench/api/node/extHostRequireInterceptor';
import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionMemento, IExtensionModule, HostExtension } from 'vs/workbench/api/common/extHostExtensionActivator';
import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionModule, HostExtension } from 'vs/workbench/api/common/extHostExtensionActivator';
import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService';
import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
@@ -34,134 +33,26 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio
import { IWorkspace } from 'vs/platform/workspace/common/workspace';
import { Schemas } from 'vs/base/common/network';
import { withNullAsUndefined } from 'vs/base/common/types';
import { realpath } from 'vs/base/node/extpath';
import { VSBuffer } from 'vs/base/common/buffer';
class ExtensionMemento implements IExtensionMemento {
private readonly _id: string;
private readonly _shared: boolean;
private readonly _storage: ExtHostStorage;
private readonly _init: Promise<ExtensionMemento>;
private _value: { [n: string]: any; };
private readonly _storageListener: IDisposable;
constructor(id: string, global: boolean, storage: ExtHostStorage) {
this._id = id;
this._shared = global;
this._storage = storage;
this._init = this._storage.getValue(this._shared, this._id, Object.create(null)).then(value => {
this._value = value;
return this;
});
this._storageListener = this._storage.onDidChangeStorage(e => {
if (e.shared === this._shared && e.key === this._id) {
this._value = e.value;
}
});
}
get whenReady(): Promise<ExtensionMemento> {
return this._init;
}
get<T>(key: string, defaultValue: T): T {
let value = this._value[key];
if (typeof value === 'undefined') {
value = defaultValue;
}
return value;
}
update(key: string, value: any): Promise<boolean> {
this._value[key] = value;
return this._storage
.setValue(this._shared, this._id, this._value)
.then(() => true);
}
dispose(): void {
this._storageListener.dispose();
}
}
class ExtensionStoragePath {
private readonly _workspace?: IStaticWorkspaceData;
private readonly _environment: IEnvironment;
private readonly _ready: Promise<string | undefined>;
private _value?: string;
constructor(workspace: IStaticWorkspaceData | undefined, environment: IEnvironment) {
this._workspace = workspace;
this._environment = environment;
this._ready = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
}
get whenReady(): Promise<any> {
return this._ready;
}
workspaceValue(extension: IExtensionDescription): string | undefined {
if (this._value) {
return path.join(this._value, extension.identifier.value);
}
return undefined;
}
globalValue(extension: IExtensionDescription): string {
return path.join(this._environment.globalStorageHome.fsPath, extension.identifier.value.toLowerCase());
}
private async _getOrCreateWorkspaceStoragePath(): Promise<string | undefined> {
if (!this._workspace) {
return Promise.resolve(undefined);
}
if (!this._environment.appSettingsHome) {
return undefined;
}
const storageName = this._workspace.id;
const storagePath = path.join(this._environment.appSettingsHome.fsPath, 'workspaceStorage', storageName);
const exists = await pfs.dirExists(storagePath);
if (exists) {
return storagePath;
}
try {
await pfs.mkdirp(storagePath);
await pfs.writeFile(
path.join(storagePath, 'meta.json'),
JSON.stringify({
id: this._workspace.id,
configuration: this._workspace.configuration && URI.revive(this._workspace.configuration).toString(),
name: this._workspace.name
}, undefined, 2)
);
return storagePath;
} catch (e) {
console.error(e);
return undefined;
}
}
}
import { ISchemeTransformer } from 'vs/workbench/api/common/extHostLanguageFeatures';
import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento';
import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths';
interface ITestRunner {
run(testsRoot: string, clb: (error: Error, failures?: number) => void): void;
}
export interface IHostUtils {
exit(code?: number): void;
exists(path: string): Promise<boolean>;
realpath(path: string): Promise<string>;
}
export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private static readonly WORKSPACE_CONTAINS_TIMEOUT = 7000;
private readonly _nativeExit: (code?: number) => void;
private readonly _hostUtils: IHostUtils;
private readonly _initData: IInitData;
private readonly _extHostContext: IMainContext;
private readonly _extHostWorkspace: ExtHostWorkspace;
@@ -177,7 +68,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private readonly _readyToRunExtensions: Barrier;
private readonly _registry: ExtensionDescriptionRegistry;
private readonly _storage: ExtHostStorage;
private readonly _storagePath: ExtensionStoragePath;
private readonly _storagePath: ExtensionStoragePaths;
private readonly _activator: ExtensionsActivator;
private _extensionPathIndex: Promise<TernarySearchTree<IExtensionDescription>> | null;
private readonly _extensionApiFactory: ISqlExtensionApiFactory;
@@ -187,15 +78,17 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private _started: boolean;
constructor(
nativeExit: (code?: number) => void,
hostUtils: IHostUtils,
initData: IInitData,
extHostContext: IMainContext,
extHostWorkspace: ExtHostWorkspace,
extHostConfiguration: ExtHostConfiguration,
environment: IEnvironment,
extHostLogService: ExtHostLogService
extHostLogService: ExtHostLogService,
schemeTransformer: ISchemeTransformer | null,
outputChannelName: string
) {
this._nativeExit = nativeExit;
this._hostUtils = hostUtils;
this._initData = initData;
this._extHostContext = extHostContext;
this._extHostWorkspace = extHostWorkspace;
@@ -211,7 +104,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
this._readyToRunExtensions = new Barrier();
this._registry = new ExtensionDescriptionRegistry(initData.extensions);
this._storage = new ExtHostStorage(this._extHostContext);
this._storagePath = new ExtensionStoragePath(withNullAsUndefined(initData.workspace), initData.environment);
this._storagePath = new ExtensionStoragePaths(withNullAsUndefined(initData.workspace), initData.environment);
const hostExtensions = new Set<string>();
initData.hostExtensions.forEach((extensionId) => hostExtensions.add(ExtensionIdentifier.toKey(extensionId)));
@@ -234,7 +127,17 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
this._extensionPathIndex = null;
// initialize API first (i.e. do not release barrier until the API is initialized)
this._extensionApiFactory = createApiFactory(this._initData, this._extHostContext, this._extHostWorkspace, this._extHostConfiguration, this, this._extHostLogService, this._storage);
this._extensionApiFactory = createApiFactory(
this._initData,
this._extHostContext,
this._extHostWorkspace,
this._extHostConfiguration,
this,
this._extHostLogService,
this._storage,
schemeTransformer,
outputChannelName
);
this._resolvers = Object.create(null);
@@ -337,7 +240,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
if (!ext.main) {
return undefined;
}
return realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext));
return this._hostUtils.realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext));
});
this._extensionPathIndex = Promise.all(extensions).then(() => tree);
}
@@ -558,7 +461,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
// find exact path
for (const { uri } of workspace.folders) {
if (await pfs.exists(path.join(URI.revive(uri).fsPath, fileName))) {
if (await this._hostUtils.exists(path.join(URI.revive(uri).fsPath, fileName))) {
// the file was found
return (
this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${fileName}`))
@@ -668,7 +571,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return;
}
this._nativeExit(code);
this._hostUtils.exit(code);
}, 500);
}
@@ -752,12 +655,12 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
if (!extensionDescription) {
return;
}
const realpathValue = await realpath(extensionDescription.extensionLocation.fsPath);
const realpathValue = await this._hostUtils.realpath(extensionDescription.extensionLocation.fsPath);
trie.delete(URI.file(realpathValue).fsPath);
}));
await Promise.all(toAdd.map(async (extensionDescription) => {
const realpathValue = await realpath(extensionDescription.extensionLocation.fsPath);
const realpathValue = await this._hostUtils.realpath(extensionDescription.extensionLocation.fsPath);
trie.set(URI.file(realpathValue).fsPath, extensionDescription);
}));
@@ -774,7 +677,12 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
}
public async $test_down(size: number): Promise<VSBuffer> {
return VSBuffer.wrap(Buffer.alloc(size, Math.random() % 256));
let buff = VSBuffer.alloc(size);
let value = Math.random() % 256;
for (let i = 0; i < size; i++) {
buff.writeUint8(value, i);
}
return buff;
}
}
@@ -0,0 +1,253 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { MainThreadKeytarShape, IEnvironment, MainThreadWindowShape, MainThreadTelemetryShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import * as vscode from 'vscode';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { endsWith } from 'vs/base/common/strings';
import { IExtensionApiFactory } from 'vs/workbench/api/node/extHost.api.impl';
interface LoadFunction {
(request: string, parent: { filename: string; }, isMain: any): any;
}
interface INodeModuleFactory {
readonly nodeModuleName: string | string[];
load(request: string, parent: { filename: string; }, isMain: any, original: LoadFunction): any;
alternaiveModuleName?(name: string): string | undefined;
}
export class NodeModuleRequireInterceptor {
public static INSTANCE = new NodeModuleRequireInterceptor();
private readonly _factories: Map<string, INodeModuleFactory>;
private readonly _alternatives: ((moduleName: string) => string | undefined)[];
constructor() {
this._factories = new Map<string, INodeModuleFactory>();
this._alternatives = [];
this._installInterceptor(this._factories, this._alternatives);
}
private _installInterceptor(factories: Map<string, INodeModuleFactory>, alternatives: ((moduleName: string) => string | undefined)[]): void {
const node_module = <any>require.__$__nodeRequire('module');
const original = node_module._load;
node_module._load = function load(request: string, parent: { filename: string; }, isMain: any) {
for (let alternativeModuleName of alternatives) {
let alternative = alternativeModuleName(request);
if (alternative) {
request = alternative;
break;
}
}
if (!factories.has(request)) {
return original.apply(this, arguments);
}
return factories.get(request)!.load(request, parent, isMain, original);
};
}
public register(interceptor: INodeModuleFactory): void {
if (Array.isArray(interceptor.nodeModuleName)) {
for (let moduleName of interceptor.nodeModuleName) {
this._factories.set(moduleName, interceptor);
}
} else {
this._factories.set(interceptor.nodeModuleName, interceptor);
}
if (typeof interceptor.alternaiveModuleName === 'function') {
this._alternatives.push((moduleName) => {
return interceptor.alternaiveModuleName!(moduleName);
});
}
}
}
export class VSCodeNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName = 'vscode';
private readonly _extApiImpl = new Map<string, typeof vscode>();
private _defaultApiImpl: typeof vscode;
constructor(
private readonly _apiFactory: IExtensionApiFactory,
private readonly _extensionPaths: TernarySearchTree<IExtensionDescription>,
private readonly _extensionRegistry: ExtensionDescriptionRegistry,
private readonly _configProvider: ExtHostConfigProvider
) {
}
public load(request: string, parent: { filename: string; }): any {
// get extension id from filename and api for extension
const ext = this._extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
if (ext) {
let apiImpl = this._extApiImpl.get(ExtensionIdentifier.toKey(ext.identifier));
if (!apiImpl) {
apiImpl = this._apiFactory(ext, this._extensionRegistry, this._configProvider);
this._extApiImpl.set(ExtensionIdentifier.toKey(ext.identifier), apiImpl);
}
return apiImpl;
}
// fall back to a default implementation
if (!this._defaultApiImpl) {
let extensionPathsPretty = '';
this._extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.identifier.value}\n`);
console.warn(`Could not identify extension for 'vscode' require call from ${parent.filename}. These are the extension path mappings: \n${extensionPathsPretty}`);
this._defaultApiImpl = this._apiFactory(nullExtensionDescription, this._extensionRegistry, this._configProvider);
}
return this._defaultApiImpl;
}
}
interface IKeytarModule {
getPassword(service: string, account: string): Promise<string | null>;
setPassword(service: string, account: string, password: string): Promise<void>;
deletePassword(service: string, account: string): Promise<boolean>;
findPassword(service: string): Promise<string | null>;
}
export class KeytarNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName: string = 'keytar';
private alternativeNames: Set<string> | undefined;
private _impl: IKeytarModule;
constructor(mainThreadKeytar: MainThreadKeytarShape, environment: IEnvironment) {
if (environment.appRoot) {
let appRoot = environment.appRoot.fsPath;
if (process.platform === 'win32') {
appRoot = appRoot.replace(/\\/g, '/');
}
if (appRoot[appRoot.length - 1] === '/') {
appRoot = appRoot.substr(0, appRoot.length - 1);
}
this.alternativeNames = new Set();
this.alternativeNames.add(`${appRoot}/node_modules.asar/keytar`);
this.alternativeNames.add(`${appRoot}/node_modules/keytar`);
}
this._impl = {
getPassword: (service: string, account: string): Promise<string | null> => {
return mainThreadKeytar.$getPassword(service, account);
},
setPassword: (service: string, account: string, password: string): Promise<void> => {
return mainThreadKeytar.$setPassword(service, account, password);
},
deletePassword: (service: string, account: string): Promise<boolean> => {
return mainThreadKeytar.$deletePassword(service, account);
},
findPassword: (service: string): Promise<string | null> => {
return mainThreadKeytar.$findPassword(service);
}
};
}
public load(request: string, parent: { filename: string; }): any {
return this._impl;
}
public alternaiveModuleName(name: string): string | undefined {
const length = name.length;
// We need at least something like: `?/keytar` which requires
// more than 7 characters.
if (length <= 7 || !this.alternativeNames) {
return undefined;
}
const sep = length - 7;
if ((name.charAt(sep) === '/' || name.charAt(sep) === '\\') && endsWith(name, 'keytar')) {
name = name.replace(/\\/g, '/');
if (this.alternativeNames.has(name)) {
return 'keytar';
}
}
return undefined;
}
}
interface OpenOptions {
wait: boolean;
app: string | string[];
}
interface IOriginalOpen {
(target: string, options?: OpenOptions): Thenable<any>;
}
interface IOpenModule {
(target: string, options?: OpenOptions): Thenable<void>;
}
export class OpenNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName: string[] = ['open', 'opn'];
private _extensionId: string | undefined;
private _original: IOriginalOpen;
private _impl: IOpenModule;
constructor(mainThreadWindow: MainThreadWindowShape, private _mainThreadTelemerty: MainThreadTelemetryShape, private readonly _extensionPaths: TernarySearchTree<IExtensionDescription>) {
this._impl = (target, options) => {
const uri: URI = URI.parse(target);
// If we have options use the original method.
if (options) {
return this.callOriginal(target, options);
}
if (uri.scheme === 'http' || uri.scheme === 'https') {
return mainThreadWindow.$openUri(uri);
} else if (uri.scheme === 'mailto') {
return mainThreadWindow.$openUri(uri);
}
return this.callOriginal(target, options);
};
}
public load(request: string, parent: { filename: string; }, isMain: any, original: LoadFunction): any {
// get extension id from filename and api for extension
const extension = this._extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
if (extension) {
this._extensionId = extension.identifier.value;
this.sendShimmingTelemetry();
}
this._original = original(request, parent, isMain);
return this._impl;
}
private callOriginal(target: string, options: OpenOptions | undefined): Thenable<any> {
this.sendNoForwardTelemetry();
return this._original(target, options);
}
private sendShimmingTelemetry(): void {
if (!this._extensionId) {
return;
}
/* __GDPR__
"shimming.open" : {
"extension": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this._mainThreadTelemerty.$publicLog('shimming.open', { extension: this._extensionId });
}
private sendNoForwardTelemetry(): void {
if (!this._extensionId) {
return;
}
/* __GDPR__
"shimming.open.call.noForward" : {
"extension": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this._mainThreadTelemerty.$publicLog('shimming.open.call.noForward', { extension: this._extensionId });
}
}
@@ -0,0 +1,75 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'vs/base/common/path';
import { URI } from 'vs/base/common/uri';
import * as pfs from 'vs/base/node/pfs';
import { IEnvironment, IStaticWorkspaceData } from 'vs/workbench/api/common/extHost.protocol';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
export class ExtensionStoragePaths {
private readonly _workspace?: IStaticWorkspaceData;
private readonly _environment: IEnvironment;
private readonly _ready: Promise<string | undefined>;
private _value?: string;
constructor(workspace: IStaticWorkspaceData | undefined, environment: IEnvironment) {
this._workspace = workspace;
this._environment = environment;
this._ready = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
}
get whenReady(): Promise<any> {
return this._ready;
}
workspaceValue(extension: IExtensionDescription): string | undefined {
if (this._value) {
return path.join(this._value, extension.identifier.value);
}
return undefined;
}
globalValue(extension: IExtensionDescription): string {
return path.join(this._environment.globalStorageHome.fsPath, extension.identifier.value.toLowerCase());
}
private async _getOrCreateWorkspaceStoragePath(): Promise<string | undefined> {
if (!this._workspace) {
return Promise.resolve(undefined);
}
if (!this._environment.appSettingsHome) {
return undefined;
}
const storageName = this._workspace.id;
const storagePath = path.join(this._environment.appSettingsHome.fsPath, 'workspaceStorage', storageName);
const exists = await pfs.dirExists(storagePath);
if (exists) {
return storagePath;
}
try {
await pfs.mkdirp(storagePath);
await pfs.writeFile(
path.join(storagePath, 'meta.json'),
JSON.stringify({
id: this._workspace.id,
configuration: this._workspace.configuration && URI.revive(this._workspace.configuration).toString(),
name: this._workspace.name
}, undefined, 2)
);
return storagePath;
} catch (e) {
console.error(e);
return undefined;
}
}
}
+2 -1
View File
@@ -20,6 +20,7 @@ import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remot
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IFileService } from 'vs/platform/files/common/files';
import { FileService3 } from 'vs/workbench/services/files2/browser/fileService2';
import { Schemas } from 'vs/base/common/network';
class CodeRendererMain extends Disposable {
@@ -84,7 +85,7 @@ class CodeRendererMain extends Disposable {
if (connection) {
const channel = connection.getChannel<IChannel>(REMOTE_FILE_SYSTEM_CHANNEL_NAME);
const remoteFileSystemProvider = this._register(new RemoteExtensionsFileSystemProvider(channel, remoteAgentService.getEnvironment()));
fileService.registerProvider('vscode-remote', remoteFileSystemProvider);
fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider);
}
return { serviceCollection, logService };
@@ -61,7 +61,7 @@ import { ITunnelService } from 'vs/platform/remote/common/tunnel';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
export const workspaceResource = URI.from({
scheme: 'vscode-remote',
scheme: Schemas.vscodeRemote,
authority: document.location.host,
path: (<any>self).USER_HOME_DIR || '/'
});
@@ -821,6 +821,9 @@ export class SimpleTelemetryService implements ITelemetryService {
return Promise.resolve(undefined);
}
setEnabled(value: boolean): void {
}
getTelemetryInfo(): Promise<ITelemetryInfo> {
return Promise.resolve({
instanceId: 'someValue.instanceId',
@@ -5,7 +5,7 @@
import { Registry } from 'vs/platform/registry/common/platform';
import * as nls from 'vs/nls';
import { URI } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import { Action, IAction } from 'vs/base/common/actions';
import { IEditorQuickOpenEntry, IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
import { StatusbarItemDescriptor, IStatusbarRegistry, Extensions as StatusExtensions } from 'vs/workbench/browser/parts/statusbar/statusbar';
@@ -147,7 +147,7 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): UntitledEditorInput {
return instantiationService.invokeFunction<UntitledEditorInput>(accessor => {
const deserialized: ISerializedUntitledEditorInput = JSON.parse(serializedEditorInput);
const resource = !!deserialized.resourceJSON ? URI.revive(deserialized.resourceJSON) : URI.parse(deserialized.resource);
const resource = !!deserialized.resourceJSON ? URI.revive(<UriComponents>deserialized.resourceJSON) : URI.parse(deserialized.resource);
const filePath = resource.scheme === Schemas.untitled ? undefined : resource.scheme === Schemas.file ? resource.fsPath : resource.path;
const language = deserialized.modeId;
const encoding = deserialized.encoding;
@@ -163,7 +163,7 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
this._register(this.openWorkspaceButton.onClick(() => {
const model = this.editor.getModel();
if (model) {
this.windowService.openWindow([{ fileUri: model.uri }]);
this.windowService.openWindow([{ workspaceUri: model.uri }]);
}
}));
@@ -108,7 +108,7 @@ export class ResourceViewer {
}
class ImageView {
private static readonly MAX_IMAGE_SIZE = BinarySize.MB; // showing images inline is memory intense, so we have a limit
private static readonly MAX_IMAGE_SIZE = BinarySize.MB * 10; // showing images inline is memory intense, so we have a limit
private static readonly BASE64_MARKER = 'base64,';
static create(
@@ -266,7 +266,8 @@ export class PanelViewlet extends Viewlet {
let title = Registry.as<ViewletRegistry>(Extensions.Viewlets).getViewlet(this.getId()).name;
if (this.isSingleView()) {
title = `${title}: ${this.panelItems[0].panel.title}`;
const panelItemTitle = this.panelItems[0].panel.title;
title = panelItemTitle ? `${title}: ${panelItemTitle}` : title;
}
return title;
+2 -2
View File
@@ -324,8 +324,8 @@ export class NotificationViewItem extends Disposable implements INotificationVie
private static MAX_MESSAGE_LENGTH = 1000;
// Example link: "Some message with [link text](http://link.href)."
// RegEx: [, anything not ], ], (, http:|https:, //, no whitespace)
private static LINK_REGEX = /\[([^\]]+)\]\((https?:\/\/[^\)\s]+)\)/gi;
// RegEx: [, anything not ], ], (, http://|https://|command:, no whitespace)
private static LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:)[^\)\s]+)\)/gi;
private _expanded: boolean;
@@ -250,7 +250,8 @@ export class DebugHoverWidget implements IContentWidget {
}
private layoutTreeAndContainer(): void {
const treeHeight = Math.min(MAX_TREE_HEIGHT, this.tree.contentHeight);
const scrollBarHeight = 8;
const treeHeight = Math.min(MAX_TREE_HEIGHT, this.tree.contentHeight + scrollBarHeight);
this.treeContainer.style.height = `${treeHeight}px`;
this.tree.layout(treeHeight, 324);
}
+16 -2
View File
@@ -720,11 +720,25 @@ class ReplDelegate implements IListVirtualDelegate<IReplElement> {
getHeight(element: IReplElement): number {
// Give approximate heights. Repl has dynamic height so the tree will measure the actual height on its own.
const fontSize = this.configurationService.getValue<IDebugConfiguration>('debug').console.fontSize;
const rowHeight = Math.ceil(1.4 * fontSize);
if (element instanceof Expression && element.hasChildren) {
return Math.ceil(2 * 1.4 * fontSize);
return 2 * rowHeight;
}
return Math.ceil(1.4 * fontSize);
// In order to keep scroll position we need to give a good approximation to the tree
if (element instanceof SimpleReplElement) {
// For every 150 characters increase the number of lines needed
let count = Math.ceil(element.value.length / 150);
for (let i = 0; i < element.value.length; i++) {
if (element.value[i] === '\n' || element.value[i] === '\r\n') {
count++;
}
}
return Math.max(1, count) * rowHeight;
}
return rowHeight;
}
getTemplateId(element: IReplElement): string {
@@ -10,6 +10,7 @@ import { Expression, SimpleReplElement, RawObjectReplElement } from 'vs/workbenc
import { isUndefinedOrNull, isObject } from 'vs/base/common/types';
import { basenameOrAuthority } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { endsWith } from 'vs/base/common/strings';
const MAX_REPL_LENGTH = 10000;
let topReplElementCounter = 0;
@@ -39,8 +40,13 @@ export class ReplModel {
}
if (typeof data === 'string') {
const element = new SimpleReplElement(`topReplElement:${topReplElementCounter++}`, data.trimRight(), sev, source);
this.addReplElement(element);
const previousElement = this.replElements.length ? this.replElements[this.replElements.length - 1] : undefined;
if (previousElement instanceof SimpleReplElement && previousElement.severity === sev && !endsWith(previousElement.value, '\n') && !endsWith(previousElement.value, '\r\n')) {
previousElement.value += data;
} else {
const element = new SimpleReplElement(`topReplElement:${topReplElementCounter++}`, data, sev, source);
this.addReplElement(element);
}
} else {
// TODO@Isidor hack, we should introduce a new type which is an output that can fetch children like an expression
(<any>data).severity = sev;
@@ -509,7 +509,7 @@ export class DebugService implements IDebugService {
// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
if (isExtensionHostDebugging(session.configuration) && session.state === State.Running && session.configuration.noDebug) {
this.extensionHostDebugService.close(session.root.uri);
this.extensionHostDebugService.close(session.getId());
}
this.telemetryDebugSessionStop(session, adapterExitEvent);
@@ -556,8 +556,8 @@ export class DebugService implements IDebugService {
return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? session.restart() : undefined);
}
if (isExtensionHostDebugging(session.configuration) && session.root) {
return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? this.extensionHostDebugService.reload(session.root.uri) : undefined);
if (isExtensionHostDebugging(session.configuration)) {
return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? this.extensionHostDebugService.reload(session.getId()) : undefined);
}
const shouldFocus = this.viewModel.focusedSession && session.getId() === this.viewModel.focusedSession.getId();
@@ -423,34 +423,41 @@ suite('Debug - Model', () => {
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
const repl = new ReplModel(session);
repl.appendToRepl('first line\n', severity.Error);
repl.appendToRepl('second line', severity.Error);
repl.appendToRepl('third line', severity.Warning);
repl.appendToRepl('second line ', severity.Error);
repl.appendToRepl('third line ', severity.Error);
repl.appendToRepl('fourth line', severity.Error);
let elements = <SimpleReplElement[]>repl.getReplElements();
assert.equal(elements.length, 4);
assert.equal(elements[0].value, 'first line');
assert.equal(elements.length, 2);
assert.equal(elements[0].value, 'first line\n');
assert.equal(elements[0].severity, severity.Error);
assert.equal(elements[1].value, 'second line');
assert.equal(elements[1].value, 'second line third line fourth line');
assert.equal(elements[1].severity, severity.Error);
assert.equal(elements[2].value, 'third line');
assert.equal(elements[2].severity, severity.Warning);
assert.equal(elements[3].value, 'fourth line');
assert.equal(elements[3].severity, severity.Error);
repl.appendToRepl('1', severity.Warning);
elements = <SimpleReplElement[]>repl.getReplElements();
assert.equal(elements.length, 5);
assert.equal(elements[4].value, '1');
assert.equal(elements[4].severity, severity.Warning);
assert.equal(elements.length, 3);
assert.equal(elements[2].value, '1');
assert.equal(elements[2].severity, severity.Warning);
const keyValueObject = { 'key1': 2, 'key2': 'value' };
repl.appendToRepl(new RawObjectReplElement('fakeid', 'fake', keyValueObject), severity.Info);
const element = <RawObjectReplElement>repl.getReplElements()[5];
const element = <RawObjectReplElement>repl.getReplElements()[3];
assert.equal(element.value, 'Object');
assert.deepEqual(element.valueObj, keyValueObject);
repl.removeReplExpressions();
assert.equal(repl.getReplElements().length, 0);
repl.appendToRepl('1\n', severity.Info);
repl.appendToRepl('2', severity.Info);
repl.appendToRepl('3\n4', severity.Info);
repl.appendToRepl('5\n', severity.Info);
repl.appendToRepl('6', severity.Info);
elements = <SimpleReplElement[]>repl.getReplElements();
assert.equal(elements.length, 3);
assert.equal(elements[0], '1\n');
assert.equal(elements[1], '23\n45\n');
assert.equal(elements[2], '6');
});
});
@@ -28,7 +28,7 @@ import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension
import { RatingsWidget, InstallCountWidget, RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/electron-browser/extensionsWidgets';
import { EditorOptions } from 'vs/workbench/common/editor';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { CombinedInstallAction, UpdateAction, ExtensionEditorDropDownAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, SystemDisabledLabelAction, SystemDisabledWarningAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { CombinedInstallAction, UpdateAction, ExtensionEditorDropDownAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, DisabledLabelAction, SystemDisabledWarningAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
@@ -395,7 +395,7 @@ export class ExtensionEditor extends BaseEditor {
this.instantiationService.createInstance(RemoteInstallAction),
combinedInstallAction,
systemDisabledWarningAction,
this.instantiationService.createInstance(SystemDisabledLabelAction, systemDisabledWarningAction),
this.instantiationService.createInstance(DisabledLabelAction, systemDisabledWarningAction),
this.instantiationService.createInstance(MaliciousStatusLabelAction, true),
];
const extensionContainers: ExtensionContainers = this.instantiationService.createInstance(ExtensionContainers, [...actions, ...widgets]);
@@ -623,9 +623,9 @@ export class ExtensionEditor extends BaseEditor {
renderDashboardContributions(content, manifest, layout)
];
const isEmpty = !renders.reduce((v, r) => r || v, false);
scrollableContent.scanDomNode();
const isEmpty = !renders.some(x => x);
if (isEmpty) {
append(content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions");
append(this.content, content);
@@ -21,6 +21,7 @@ import { randomPort } from 'vs/base/node/ports';
import product from 'vs/platform/product/node/product';
import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
export class ExtensionHostProfileService extends Disposable implements IExtensionHostProfileService {
@@ -76,7 +77,8 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
return null;
}
if (!this._extensionService.canProfileExtensionHost()) {
const inspectPort = this._extensionService.getInspectPort();
if (!inspectPort) {
return this._dialogService.confirm({
type: 'info',
message: nls.localize('restart1', "Profile Extensions"),
@@ -92,7 +94,7 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
this._setState(ProfileSessionState.Starting);
return this._extensionService.startExtensionHostProfile().then((value) => {
return this._instantiationService.createInstance(ExtensionHostProfiler, inspectPort).start().then((value) => {
this._profileSession = value;
this._setState(ProfileSessionState.Running);
}, (err) => {
@@ -251,6 +251,11 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
description: localize('extensionsPolicy', "Sets the security policy for downloading extensions."),
scope: ConfigurationScope.APPLICATION,
default: ExtensionsPolicy.allowAll
},
'extensions.showInstalledExtensionsByDefault': {
type: 'boolean',
description: localize('extensions.showInstalledExtensionsByDefault', "When enabled, extensions view shows installed extensions view by default."),
default: false
}
}
});
@@ -16,7 +16,7 @@ import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
// {{SQL CARBON EDIT}}
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, AutoUpdateConfigurationKey, IExtensionContainer, EXTENSIONS_CONFIG, ExtensionsPolicy, ExtensionsPolicyKey } from 'vs/workbench/contrib/extensions/common/extensions';
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate';
import { IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, ExtensionIdentifier, IExtensionDescription, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
@@ -60,6 +60,7 @@ import { ILabelService } from 'vs/platform/label/common/label';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { isUIExtension } from 'vs/workbench/services/extensions/node/extensionsUtil';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
// {{SQL CARBON EDIT}}
@@ -183,7 +184,7 @@ export class InstallAction extends ExtensionAction {
return;
}
this.enabled = this.extensionsWorkbenchService.canInstall(this.extension) && this.extension.state === ExtensionState.Uninstalled;
this.enabled = this.extensionsWorkbenchService.canInstall(this.extension) && !this.extensionsWorkbenchService.local.some(e => areSameExtensions(e.identifier, this.extension.identifier));
this.class = this.extension.state === ExtensionState.Installing ? InstallAction.InstallingClass : InstallAction.Class;
this.updateLabel();
}
@@ -720,7 +721,7 @@ export class ManageExtensionAction extends ExtensionDropDownAction {
]);
groups.push([this.instantiationService.createInstance(UninstallAction)]);
groups.push([this.instantiationService.createInstance(InstallAnotherVersionAction)]);
groups.push([this.instantiationService.createInstance(ExtensionInfoAction)]);
groups.push([this.instantiationService.createInstance(ExtensionInfoAction), this.instantiationService.createInstance(ExtensionSettingsAction)]);
groups.forEach(group => group.forEach(extensionAction => extensionAction.extension = this.extension));
@@ -829,6 +830,27 @@ export class ExtensionInfoAction extends ExtensionAction {
}
}
export class ExtensionSettingsAction extends ExtensionAction {
static readonly ID = 'extensions.extensionSettings';
static readonly LABEL = localize('extensionSettingsAction', "Configure Extension Settings");
constructor(
@IPreferencesService private readonly preferencesService: IPreferencesService
) {
super(ExtensionSettingsAction.ID, ExtensionSettingsAction.LABEL);
this.update();
}
update(): void {
this.enabled = !!this.extension;
}
run(): Promise<any> {
this.preferencesService.openSettings(false, `@ext:${this.extension.identifier.id}`);
return Promise.resolve();
}
}
export class EnableForWorkspaceAction extends ExtensionAction {
static readonly ID = 'extensions.enableForWorkspace';
@@ -1195,9 +1217,10 @@ export class ReloadAction extends ExtensionAction {
}
const isUninstalled = this.extension.state === ExtensionState.Uninstalled;
const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0];
const isSameExtensionRunning = runningExtension && this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation);
if (isUninstalled) {
if (runningExtension) {
if (isSameExtensionRunning) {
this.enabled = true;
this.label = localize('reloadRequired', "Reload Required");
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
@@ -1210,7 +1233,6 @@ export class ReloadAction extends ExtensionAction {
const isEnabled = this.extensionEnablementService.isEnabled(this.extension.local);
if (runningExtension) {
// Extension is running
const isSameExtensionRunning = this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation);
const isSameVersionRunning = isSameExtensionRunning && this.extension.version === runningExtension.version;
if (isEnabled) {
if (!isSameVersionRunning && !this.extensionService.canAddExtension(toExtensionDescription(this.extension.local))) {
@@ -2484,6 +2506,7 @@ export class StatusLabelAction extends Action implements IExtensionContainer {
return canAddExtension() ? this.initialStatus === ExtensionState.Installed ? localize('updated', "Updated") : localize('installed', "Installed") : null;
}
if (currentStatus === ExtensionState.Uninstalling && this.status === ExtensionState.Uninstalled) {
this.initialStatus = this.status;
return canRemoveExtension() ? localize('uninstalled', "Uninstalled") : null;
}
}
@@ -2533,7 +2556,7 @@ export class MaliciousStatusLabelAction extends ExtensionAction {
}
}
export class SystemDisabledLabelAction extends ExtensionAction {
export class DisabledLabelAction extends ExtensionAction {
private static readonly Class = 'disable-status';
@@ -2542,19 +2565,26 @@ export class SystemDisabledLabelAction extends ExtensionAction {
constructor(
private readonly warningAction: SystemDisabledWarningAction,
@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService,
) {
super('extensions.systemDisabledLabel', warningAction.tooltip, `${SystemDisabledLabelAction.Class} hide`, false);
super('extensions.disabledLabel', warningAction.tooltip, `${DisabledLabelAction.Class} hide`, false);
warningAction.onDidChange(() => this.update(), this, this.disposables);
}
update(): void {
this.enabled = this.warningAction.enabled;
if (this.enabled) {
this.class = SystemDisabledLabelAction.Class;
this.class = `${DisabledLabelAction.Class} hide`;
this.label = '';
if (this.warningAction.enabled) {
this.enabled = true;
this.class = DisabledLabelAction.Class;
this.label = this.warningAction.tooltip;
} else {
this.class = `${SystemDisabledLabelAction.Class} hide`;
this.label = '';
return;
}
if (this.extension && this.extension.local && !this.extensionEnablementService.isEnabled(this.extension.local)) {
this.enabled = true;
this.class = DisabledLabelAction.Class;
this.label = localize('disabled by user', "This extension is disabled by the user.");
return;
}
}
@@ -2599,27 +2629,42 @@ export class SystemDisabledWarningAction extends ExtensionAction {
this.enabled = false;
this.class = `${SystemDisabledWarningAction.Class} hide`;
this.tooltip = '';
if (this.extension && this.extension.local && this._runningExtensions) {
if (this.extension && this.extension.local && this.extension.server && this._runningExtensions && this.workbenchEnvironmentService.configuration.remoteAuthority && this.extensionManagementServerService.remoteExtensionManagementServer) {
if (
// Remote Window
this.workbenchEnvironmentService.configuration.remoteAuthority
// Local Workspace Extension
&& this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService)
// Extension does not exist in remote
&& !this.extensionsWorkbenchService.local.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.extensionManagementServerService.remoteExtensionManagementServer)
this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService)
) {
this.enabled = true;
this.class = `${SystemDisabledWarningAction.Class}`;
const host = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.workbenchEnvironmentService.configuration.remoteAuthority) || localize('remote', "Remote");
this.tooltip = localize('disabled workspace Extension', "This extension is disabled because it cannot run in a window connected to the remote server.", host, host);
if (this.extensionsWorkbenchService.canInstall(this.extension)) {
this.tooltip = `${this.tooltip} ${localize('Install in remote server', "Install it in '{0}' server to enable.", host)}`;
this.tooltip = localize('disabled workspace Extension', "This extension from {0} server is disabled because it cannot run in a window connected to the remote server.", this.getServerLabel(this.extensionManagementServerService.localExtensionManagementServer));
if (!this.extensionsWorkbenchService.local.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.extensionManagementServerService.remoteExtensionManagementServer)
&& this.extensionsWorkbenchService.canInstall(this.extension)
) {
// Extension does not exist in remote
this.tooltip = `${this.tooltip} ${localize('Install in remote server', "Install it in {0} server to enable.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer))}`;
}
return;
}
const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0];
const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation) : null;
if (
// Not same as running extension
runningExtensionServer && this.extension.server !== runningExtensionServer
) {
this.enabled = true;
this.class = `${SystemDisabledWarningAction.Class}`;
this.tooltip = localize('disabled because running in another server', "This extension from {0} server is disabled because another instance of same extension from {1} server is enabled.", this.getServerLabel(this.extension.server), this.getServerLabel(runningExtensionServer));
return;
}
}
}
private getServerLabel(server: IExtensionManagementServer): string {
if (server === this.extensionManagementServerService.remoteExtensionManagementServer) {
return this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.workbenchEnvironmentService.configuration.remoteAuthority) || localize('remote', "Remote");
}
return server.label;
}
run(): Promise<any> {
return Promise.resolve(null);
}
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IExtensionService, IResponsiveStateChangeEvent, ICpuProfilerTarget, IExtensionHostProfile, ProfileSession } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionService, IResponsiveStateChangeEvent, IExtensionHostProfile, ProfileSession } from 'vs/workbench/services/extensions/common/extensions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { Disposable } from 'vs/base/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log';
@@ -21,11 +21,12 @@ import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { createSlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions';
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchContribution {
private readonly _session = new Map<ICpuProfilerTarget, CancellationTokenSource>();
private readonly _blame = new Set<string>();
private _session: CancellationTokenSource | undefined;
constructor(
@IExtensionService private readonly _extensionService: IExtensionService,
@@ -41,26 +42,29 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
}
private async _onDidChangeResponsiveChange(event: IResponsiveStateChangeEvent): Promise<void> {
const { target } = event;
if (!target.canProfileExtensionHost()) {
const port = this._extensionService.getInspectPort();
if (!port) {
return;
}
if (event.isResponsive && this._session.has(target)) {
if (event.isResponsive && this._session) {
// stop profiling when responsive again
this._session.get(target)!.cancel();
this._session.cancel();
} else if (!event.isResponsive && !this._session.has(target)) {
} else if (!event.isResponsive && !this._session) {
// start profiling if not yet profiling
const token = new CancellationTokenSource();
this._session.set(target, token);
const cts = new CancellationTokenSource();
this._session = cts;
let session: ProfileSession;
try {
session = await target.startExtensionHostProfile();
session = await this._instantiationService.createInstance(ExtensionHostProfiler, port).start();
} catch (err) {
this._session.delete(target);
this._session = undefined;
// fail silent as this is often
// caused by another party being
// connected already
@@ -69,7 +73,7 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
// wait 5 seconds or until responsive again
await new Promise(resolve => {
token.token.onCancellationRequested(resolve);
cts.token.onCancellationRequested(resolve);
setTimeout(resolve, 5e3);
});
@@ -79,7 +83,7 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
} catch (err) {
onUnexpectedError(err);
} finally {
this._session.delete(target);
this._session = undefined;
}
}
}
@@ -13,9 +13,9 @@ import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging';
import { Event } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionItem, StatusLabelAction, RemoteInstallAction, SystemDisabledWarningAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionItem, StatusLabelAction, RemoteInstallAction, SystemDisabledWarningAction, DisabledLabelAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/electron-browser/extensionsWidgets';
import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, TooltipWidget } from 'vs/workbench/contrib/extensions/electron-browser/extensionsWidgets';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -88,13 +88,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
});
actionbar.onDidRun(({ error }) => error && this.notificationService.error(error));
const widgets = [
recommendationWidget,
badgeWidget,
this.instantiationService.createInstance(Label, version, (e: IExtension) => e.version),
this.instantiationService.createInstance(InstallCountWidget, installCount, true),
this.instantiationService.createInstance(RatingsWidget, ratings, true)
];
const systemDisabledWarningAction = this.instantiationService.createInstance(SystemDisabledWarningAction);
const actions = [
this.instantiationService.createInstance(StatusLabelAction),
this.instantiationService.createInstance(UpdateAction),
@@ -102,10 +96,20 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
this.instantiationService.createInstance(InstallAction),
this.instantiationService.createInstance(RemoteInstallAction),
this.instantiationService.createInstance(MaliciousStatusLabelAction, false),
this.instantiationService.createInstance(SystemDisabledWarningAction),
systemDisabledWarningAction,
this.instantiationService.createInstance(ManageExtensionAction)
];
const extensionContainers: ExtensionContainers = this.instantiationService.createInstance(ExtensionContainers, [...actions, ...widgets]);
const disabledLabelAction = this.instantiationService.createInstance(DisabledLabelAction, systemDisabledWarningAction);
const tooltipWidget = this.instantiationService.createInstance(TooltipWidget, root, disabledLabelAction, recommendationWidget);
const widgets = [
recommendationWidget,
badgeWidget,
tooltipWidget,
this.instantiationService.createInstance(Label, version, (e: IExtension) => e.version),
this.instantiationService.createInstance(InstallCountWidget, installCount, true),
this.instantiationService.createInstance(RatingsWidget, ratings, true)
];
const extensionContainers: ExtensionContainers = this.instantiationService.createInstance(ExtensionContainers, [...actions, ...widgets, disabledLabelAction]);
actionbar.push(actions, actionOptions);
const disposables = [...actions, ...widgets, actionbar, extensionContainers];
@@ -53,7 +53,6 @@ import { createErrorWithActions } from 'vs/base/common/errorsWithActions';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { RemoteAuthorityContext as RemoteAuthorityContext } from 'vs/workbench/common/contextkeys';
import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet';
interface SearchInputEvent extends Event {
@@ -64,7 +63,10 @@ interface SearchInputEvent extends Event {
const NonEmptyWorkspaceContext = new RawContextKey<boolean>('nonEmptyWorkspace', false);
const DefaultViewsContext = new RawContextKey<boolean>('defaultExtensionViews', true);
const SearchMarketplaceExtensionsContext = new RawContextKey<boolean>('searchMarketplaceExtensions', false);
const SearchServerExtensionsContext = new RawContextKey<boolean>('searchServerExtensions', false);
const SearchIntalledExtensionsContext = new RawContextKey<boolean>('searchInstalledExtensions', false);
const SearchOutdatedExtensionsContext = new RawContextKey<boolean>('searchOutdatedExtensions', false);
const SearchEnabledExtensionsContext = new RawContextKey<boolean>('searchEnabledExtensions', false);
const SearchDisabledExtensionsContext = new RawContextKey<boolean>('searchDisabledExtensions', false);
const HasInstalledExtensionsContext = new RawContextKey<boolean>('hasInstalledExtensions', true);
const SearchBuiltInExtensionsContext = new RawContextKey<boolean>('searchBuiltInExtensions', false);
const RecommendedExtensionsContext = new RawContextKey<boolean>('recommendedExtensions', false);
@@ -72,10 +74,12 @@ const DefaultRecommendedExtensionsContext = new RawContextKey<boolean>('defaultR
const viewIdNameMappings: { [id: string]: string } = {
'extensions.listView': localize('marketPlace', "Marketplace"),
'extensions.enabledExtensionList': localize('enabledExtensions', "Enabled"),
'extensions.enabledExtensionList2': localize('enabledExtensions', "Enabled"),
'extensions.disabledExtensionList': localize('disabledExtensions', "Disabled"),
'extensions.disabledExtensionList2': localize('disabledExtensions', "Disabled"),
// {{SQL CARBON EDIT}}
// 'extensions.popularExtensionsList': localize('popularExtensions', "Popular"),
'extensions.recommendedList': localize('recommendedExtensions', "Marketplace"),
'extensions.recommendedList': localize('recommendedExtensions', "Recommended"),
'extensions.otherrecommendedList': localize('otherRecommendedExtensions', "Other Recommendations"),
'extensions.workspaceRecommendedList': localize('workspaceRecommendedExtensions', "Workspace Recommendations"),
'extensions.builtInExtensionsList': localize('builtInExtensions', "Features"),
@@ -98,6 +102,8 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
viewDescriptors.push(this.createDefaultDisabledExtensionsListViewDescriptor());
// {{SQL CARBON EDIT}}
// viewDescriptors.push(this.createDefaultPopularExtensionsListViewDescriptor());
viewDescriptors.push(this.createEnabledExtensionsListViewDescriptor());
viewDescriptors.push(this.createDisabledExtensionsListViewDescriptor());
viewDescriptors.push(this.createBuiltInExtensionsListViewDescriptor());
viewDescriptors.push(this.createBuiltInBasicsExtensionsListViewDescriptor());
viewDescriptors.push(this.createBuiltInThemesExtensionsListViewDescriptor());
@@ -133,7 +139,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
id,
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: EnabledExtensionsView },
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.isEqualTo('')),
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), ContextKeyExpr.not('config.extensions.showInstalledExtensionsByDefault')),
weight: 40,
canToggleVisibility: true,
order: 1
@@ -148,7 +154,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
id,
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: DisabledExtensionsView },
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.isEqualTo('')),
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), ContextKeyExpr.not('config.extensions.showInstalledExtensionsByDefault')),
weight: 10,
canToggleVisibility: true,
order: 3,
@@ -175,15 +181,21 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
private createExtensionsViewDescriptorsForServer(server: IExtensionManagementServer): IViewDescriptor[] {
return [{
id: `extensions.${server.authority}.installed`,
name: server.label,
name: localize('installed', "Installed"),
ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server] },
when: ContextKeyExpr.and(ContextKeyExpr.has('searchServerExtensions')),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchInstalledExtensions')),
weight: 100
}, {
id: `extensions.${server.authority}.outdated`,
name: localize('outdated', "Outdated"),
ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server] },
when: ContextKeyExpr.and(ContextKeyExpr.has('searchOutdatedExtensions')),
weight: 100
}, {
id: `extensions.${server.authority}.default`,
name: server.label,
name: localize('installed', "Installed"),
ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server] },
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.notEqualsTo('')),
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), ContextKeyExpr.has('config.extensions.showInstalledExtensionsByDefault')),
weight: 40,
order: 1
}];
@@ -235,6 +247,33 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
};
}
private createEnabledExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.enabledExtensionList2';
return {
id,
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: EnabledExtensionsView },
when: ContextKeyExpr.and(ContextKeyExpr.has('searchEnabledExtensions')),
weight: 40,
canToggleVisibility: true,
order: 1
};
}
private createDisabledExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.disabledExtensionList2';
return {
id,
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: DisabledExtensionsView },
when: ContextKeyExpr.and(ContextKeyExpr.has('searchDisabledExtensions')),
weight: 10,
canToggleVisibility: true,
order: 3,
collapsed: true
};
}
private createBuiltInExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.builtInExtensionsList';
return {
@@ -278,7 +317,10 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
private nonEmptyWorkspaceContextKey: IContextKey<boolean>;
private defaultViewsContextKey: IContextKey<boolean>;
private searchMarketplaceExtensionsContextKey: IContextKey<boolean>;
private searchServerExtensionsContextKey: IContextKey<boolean>;
private searchInstalledExtensionsContextKey: IContextKey<boolean>;
private searchOutdatedExtensionsContextKey: IContextKey<boolean>;
private searchEnabledExtensionsContextKey: IContextKey<boolean>;
private searchDisabledExtensionsContextKey: IContextKey<boolean>;
private hasInstalledExtensionsContextKey: IContextKey<boolean>;
private searchBuiltInExtensionsContextKey: IContextKey<boolean>;
private recommendedExtensionsContextKey: IContextKey<boolean>;
@@ -317,7 +359,10 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
this.nonEmptyWorkspaceContextKey = NonEmptyWorkspaceContext.bindTo(contextKeyService);
this.defaultViewsContextKey = DefaultViewsContext.bindTo(contextKeyService);
this.searchMarketplaceExtensionsContextKey = SearchMarketplaceExtensionsContext.bindTo(contextKeyService);
this.searchServerExtensionsContextKey = SearchServerExtensionsContext.bindTo(contextKeyService);
this.searchInstalledExtensionsContextKey = SearchIntalledExtensionsContext.bindTo(contextKeyService);
this.searchOutdatedExtensionsContextKey = SearchOutdatedExtensionsContext.bindTo(contextKeyService);
this.searchEnabledExtensionsContextKey = SearchEnabledExtensionsContext.bindTo(contextKeyService);
this.searchDisabledExtensionsContextKey = SearchDisabledExtensionsContext.bindTo(contextKeyService);
this.hasInstalledExtensionsContextKey = HasInstalledExtensionsContext.bindTo(contextKeyService);
this.searchBuiltInExtensionsContextKey = SearchBuiltInExtensionsContext.bindTo(contextKeyService);
this.recommendedExtensionsContextKey = RecommendedExtensionsContext.bindTo(contextKeyService);
@@ -457,7 +502,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
protected saveState(): void {
const value = this.searchBox.getValue();
if (ExtensionsListView.isInstalledExtensionsQuery(value)) {
if (ExtensionsListView.isLocalExtensionsQuery(value)) {
this.searchViewletState['query.value'] = value;
} else {
this.searchViewletState['query.value'] = '';
@@ -468,13 +513,14 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
private doSearch(): Promise<void> {
const value = this.normalizedQuery();
this.defaultViewsContextKey.set(!value);
const isServerExtensionsQuery = ExtensionsListView.isServerExtensionsQuery(value);
const isBuiltInExtensionsQuery = ExtensionsListView.isBuiltInExtensionsQuery(value);
const isRecommendedExtensionsQuery = ExtensionsListView.isRecommendedExtensionsQuery(value);
this.searchServerExtensionsContextKey.set(isServerExtensionsQuery);
this.searchBuiltInExtensionsContextKey.set(isBuiltInExtensionsQuery);
this.searchInstalledExtensionsContextKey.set(ExtensionsListView.isInstalledExtensionsQuery(value));
this.searchOutdatedExtensionsContextKey.set(ExtensionsListView.isOutdatedExtensionsQuery(value));
this.searchEnabledExtensionsContextKey.set(ExtensionsListView.isEnabledExtensionsQuery(value));
this.searchDisabledExtensionsContextKey.set(ExtensionsListView.isDisabledExtensionsQuery(value));
this.searchBuiltInExtensionsContextKey.set(ExtensionsListView.isBuiltInExtensionsQuery(value));
this.recommendedExtensionsContextKey.set(isRecommendedExtensionsQuery);
this.searchMarketplaceExtensionsContextKey.set(!!value && !isServerExtensionsQuery && !isBuiltInExtensionsQuery && !isRecommendedExtensionsQuery);
this.searchMarketplaceExtensionsContextKey.set(!!value && !ExtensionsListView.isLocalExtensionsQuery(value) && !isRecommendedExtensionsQuery);
this.nonEmptyWorkspaceContextKey.set(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY);
return this.progress(Promise.all(this.panels.map(view =>
@@ -9,7 +9,7 @@ import { assign } from 'vs/base/common/objects';
import { Event, Emitter } from 'vs/base/common/event';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/common/paging';
import { SortBy, SortOrder, IQueryOptions, IExtensionTipsService, IExtensionRecommendation, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement';
import { SortBy, SortOrder, IQueryOptions, IExtensionTipsService, IExtensionRecommendation, IExtensionManagementServer, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
@@ -41,7 +41,7 @@ import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list';
import { createErrorWithActions } from 'vs/base/common/errorsWithActions';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IAction } from 'vs/base/common/actions';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { ExtensionType, ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import product from 'vs/platform/product/node/product';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
@@ -96,7 +96,8 @@ export class ExtensionsListView extends ViewletPanel {
@IConfigurationService configurationService: IConfigurationService,
@IWorkspaceContextService protected contextService: IWorkspaceContextService,
@IExperimentService private readonly experimentService: IExperimentService,
@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService
@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService
) {
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService);
this.server = options.server;
@@ -234,7 +235,7 @@ export class ExtensionsListView extends ViewletPanel {
if (ids.length) {
return this.queryByIds(ids, options, token);
}
if (ExtensionsListView.isInstalledExtensionsQuery(query.value) || /@builtin/.test(query.value)) {
if (ExtensionsListView.isLocalExtensionsQuery(query.value) || /@builtin/.test(query.value)) {
return this.queryLocal(query, options);
}
return this.queryGallery(query, options, token);
@@ -329,7 +330,23 @@ export class ExtensionsListView extends ViewletPanel {
&& (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1)
&& (!categories.length || categories.some(category => (e.local && e.local.manifest.categories || []).some(c => c.toLowerCase() === category))));
return this.getPagedModel(this.sortExtensions(result, options));
if (options.sortBy !== undefined) {
result = this.sortExtensions(result, options);
} else {
const runningExtensions = await this.extensionService.getExtensions();
const runningExtensionsById = runningExtensions.reduce((result, e) => { result.set(ExtensionIdentifier.toKey(e.identifier.value), e); return result; }, new Map<string, IExtensionDescription>());
result = result.sort((e1, e2) => {
const running1 = runningExtensionsById.get(ExtensionIdentifier.toKey(e1.identifier.id));
const isE1Running = running1 && this.extensionManagementServerService.getExtensionManagementServer(running1.extensionLocation) === e1.server;
const running2 = runningExtensionsById.get(ExtensionIdentifier.toKey(e2.identifier.id));
const isE2Running = running2 && this.extensionManagementServerService.getExtensionManagementServer(running2.extensionLocation) === e2.server;
if ((isE1Running && isE2Running) || (!isE1Running && !isE2Running)) {
return e1.displayName.localeCompare(e2.displayName);
}
return isE1Running ? -1 : 1;
});
}
return this.getPagedModel(result);
}
@@ -785,12 +802,28 @@ export class ExtensionsListView extends ViewletPanel {
return /^\s*@builtin\s*$/i.test(query);
}
static isInstalledExtensionsQuery(query: string): boolean {
return /@installed|@outdated|@enabled|@disabled/i.test(query);
static isLocalExtensionsQuery(query: string): boolean {
return this.isInstalledExtensionsQuery(query)
|| this.isOutdatedExtensionsQuery(query)
|| this.isEnabledExtensionsQuery(query)
|| this.isDisabledExtensionsQuery(query)
|| this.isBuiltInExtensionsQuery(query);
}
static isServerExtensionsQuery(query: string): boolean {
return /@installed|@outdated/i.test(query);
static isInstalledExtensionsQuery(query: string): boolean {
return /@installed/i.test(query);
}
static isOutdatedExtensionsQuery(query: string): boolean {
return /@outdated/i.test(query);
}
static isEnabledExtensionsQuery(query: string): boolean {
return /@enabled/i.test(query);
}
static isDisabledExtensionsQuery(query: string): boolean {
return /@disabled/i.test(query);
}
static isRecommendedExtensionsQuery(query: string): boolean {
@@ -827,8 +860,12 @@ export class ExtensionsListView extends ViewletPanel {
}
}
function getServerLabel(server: IExtensionManagementServer, labelService: ILabelService, workbenchEnvironmentService: IWorkbenchEnvironmentService): string {
return workbenchEnvironmentService.configuration.remoteAuthority === server.authority ? labelService.getHostLabel(REMOTE_HOST_SCHEME, server.authority) || server.label : server.label;
function getViewTitleForServer(viewTitle: string, server: IExtensionManagementServer, labelService: ILabelService, workbenchEnvironmentService: IWorkbenchEnvironmentService): string {
const serverLabel = workbenchEnvironmentService.configuration.remoteAuthority === server.authority ? labelService.getHostLabel(REMOTE_HOST_SCHEME, server.authority) || server.label : server.label;
if (viewTitle && workbenchEnvironmentService.configuration.remoteAuthority) {
return `${serverLabel} - ${viewTitle}`;
}
return viewTitle ? viewTitle : serverLabel;
}
export class ServerExtensionsView extends ExtensionsListView {
@@ -852,17 +889,19 @@ export class ServerExtensionsView extends ExtensionsListView {
@IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService,
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
@ILabelService labelService: ILabelService,
@IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService
@IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService,
@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService
) {
options.title = getServerLabel(server, labelService, workbenchEnvironmentService);
const viewTitle = options.title;
options.title = getViewTitleForServer(viewTitle, server, labelService, workbenchEnvironmentService);
options.server = server;
super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, editorService, tipsService, modeService, telemetryService, configurationService, contextService, experimentService, workbenchThemeService);
this.disposables.push(labelService.onDidChangeFormatters(() => this.updateTitle(getServerLabel(server, labelService, workbenchEnvironmentService))));
super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, editorService, tipsService, modeService, telemetryService, configurationService, contextService, experimentService, workbenchThemeService, extensionManagementServerService);
this.disposables.push(labelService.onDidChangeFormatters(() => this.updateTitle(getViewTitleForServer(viewTitle, server, labelService, workbenchEnvironmentService))));
}
async show(query: string): Promise<IPagedModel<IExtension>> {
query = query ? query : '@installed';
if (!ExtensionsListView.isInstalledExtensionsQuery(query) && !ExtensionsListView.isBuiltInExtensionsQuery(query)) {
if (!ExtensionsListView.isLocalExtensionsQuery(query) && !ExtensionsListView.isBuiltInExtensionsQuery(query)) {
query = query += ' @installed';
}
return super.show(query.trim());
@@ -11,12 +11,13 @@ import * as platform from 'vs/base/common/platform';
import { localize } from 'vs/nls';
import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ILabelService } from 'vs/platform/label/common/label';
import { extensionButtonProminentBackground, extensionButtonProminentForeground } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { extensionButtonProminentBackground, extensionButtonProminentForeground, DisabledLabelAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { STATUS_BAR_HOST_NAME_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND } from 'vs/workbench/common/theme';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { Emitter, Event } from 'vs/base/common/event';
export abstract class ExtensionWidget extends Disposable implements IExtensionContainer {
private _extension: IExtension;
@@ -142,11 +143,53 @@ export class RatingsWidget extends ExtensionWidget {
}
}
export class TooltipWidget extends ExtensionWidget {
constructor(
private readonly parent: HTMLElement,
private readonly extensionLabelAction: DisabledLabelAction,
private readonly recommendationWidget: RecommendationWidget
) {
super();
this._register(this.extensionLabelAction.onDidChange(() => this.render()));
this._register(this.recommendationWidget.onDidChangeTooltip(() => this.render()));
}
render(): void {
this.parent.title = '';
this.parent.removeAttribute('aria-label');
if (this.extension) {
const title = this.getTitle();
this.parent.title = title;
this.parent.setAttribute('aria-label', localize('extension-arialabel', "{0}. {1} Press enter for extension details.", this.extension.displayName));
}
}
private getTitle(): string {
if (this.extensionLabelAction.enabled) {
return this.extensionLabelAction.label;
}
return this.recommendationWidget.tooltip;
}
}
export class RecommendationWidget extends ExtensionWidget {
private element?: HTMLElement;
private disposables: IDisposable[] = [];
private _tooltip: string;
get tooltip(): string { return this._tooltip; }
set tooltip(tooltip: string) {
if (this._tooltip !== tooltip) {
this._tooltip = tooltip;
this._onDidChangeTooltip.fire();
}
}
private _onDidChangeTooltip: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeTooltip: Event<void> = this._onDidChangeTooltip.event;
constructor(
private parent: HTMLElement,
@IThemeService private readonly themeService: IThemeService,
@@ -159,7 +202,7 @@ export class RecommendationWidget extends ExtensionWidget {
}
private clear(): void {
this.parent.title = '';
this.tooltip = '';
this.parent.setAttribute('aria-label', this.extension ? localize('viewExtensionDetailsAria', "{0}. Press enter for extension details.", this.extension.displayName) : '');
if (this.element) {
this.parent.removeChild(this.element);
@@ -186,8 +229,7 @@ export class RecommendationWidget extends ExtensionWidget {
};
applyBookmarkStyle(this.themeService.getTheme());
this.themeService.onThemeChange(applyBookmarkStyle, this, this.disposables);
this.parent.title = extRecommendations[this.extension.identifier.id.toLowerCase()].reasonText;
this.parent.setAttribute('aria-label', localize('viewRecommendedExtensionDetailsAria', "{0}. {1} Press enter for extension details.", this.extension.displayName, extRecommendations[this.extension.identifier.id.toLowerCase()].reasonText));
this.tooltip = extRecommendations[this.extension.identifier.id.toLowerCase()].reasonText;
}
}
@@ -227,7 +269,7 @@ export class RemoteBadgeWidget extends ExtensionWidget {
}
if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer) {
this.element = append(this.parent, $('div.extension-remote-badge'));
append(this.element, $('span.octicon.octicon-file-symlink-directory'));
append(this.element, $('span.octicon.octicon-remote'));
const applyBadgeStyle = () => {
if (!this.element) {
@@ -700,7 +700,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
}
private fromGallery(gallery: IGalleryExtension, maliciousExtensionSet: Set<string>): IExtension {
Promise.all([this.localExtensions.syncLocalWithGalleryExtension(gallery, maliciousExtensionSet), this.remoteExtensions ? this.localExtensions.syncLocalWithGalleryExtension(gallery, maliciousExtensionSet) : Promise.resolve(false)])
Promise.all([this.localExtensions.syncLocalWithGalleryExtension(gallery, maliciousExtensionSet), this.remoteExtensions ? this.remoteExtensions.syncLocalWithGalleryExtension(gallery, maliciousExtensionSet) : Promise.resolve(false)])
.then(result => {
if (result[0] || result[1]) {
this.eventuallyAutoUpdateExtensions();
@@ -12,7 +12,7 @@ import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/com
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/node/extensionsWorkbenchService';
import {
IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, IGalleryExtension, IQueryOptions,
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, IExtensionManagementServerService, EnablementState, ExtensionRecommendationReason, SortBy
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, IExtensionManagementServerService, EnablementState, ExtensionRecommendationReason, SortBy, IExtensionManagementServer
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
@@ -39,6 +39,7 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl';
import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService';
suite('ExtensionsListView Tests', () => {
@@ -89,11 +90,14 @@ suite('ExtensionsListView Tests', () => {
instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event);
instantiationService.stub(IRemoteAgentService, RemoteAgentService);
instantiationService.stub(IExtensionManagementServerService, <IExtensionManagementServerService>{
localExtensionManagementServer: {
extensionManagementService: instantiationService.get(IExtensionManagementService)
instantiationService.stub(IExtensionManagementServerService, new class extends ExtensionManagementServerService {
private _localExtensionManagementServer: IExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', authority: 'vscode-local' };
constructor() {
super(instantiationService.get(ISharedProcessService), instantiationService.get(IRemoteAgentService));
}
});
get localExtensionManagementServer(): IExtensionManagementServer { return this._localExtensionManagementServer; }
set localExtensionManagementServer(server: IExtensionManagementServer) { }
}());
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
@@ -151,14 +155,14 @@ suite('ExtensionsListView Tests', () => {
test('Test query types', () => {
assert.equal(ExtensionsListView.isBuiltInExtensionsQuery('@builtin'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@installed'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@enabled'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@disabled'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@outdated'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@installed searchText'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@enabled searchText'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@disabled searchText'), true);
assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@outdated searchText'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@installed'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@enabled'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@disabled'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@outdated'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@installed searchText'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@enabled searchText'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@disabled searchText'), true);
assert.equal(ExtensionsListView.isLocalExtensionsQuery('@outdated searchText'), true);
});
test('Test empty query equates to sort by install count', () => {
@@ -200,8 +204,8 @@ suite('ExtensionsListView Tests', () => {
await testableView.show('@installed first').then(result => {
assert.equal(result.length, 2, 'Unexpected number of results for @installed query');
assert.equal(result.get(0).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with search text.');
assert.equal(result.get(1).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with search text.');
assert.equal(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with search text.');
assert.equal(result.get(1).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with search text.');
});
await testableView.show('@disabled').then(result => {
@@ -242,27 +246,27 @@ suite('ExtensionsListView Tests', () => {
test('Test installed query with category', async () => {
await testableView.show('@installed category:themes').then(result => {
assert.equal(result.length, 2, 'Unexpected number of results for @installed query with category');
assert.equal(result.get(0).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with category.');
assert.equal(result.get(1).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with category.');
assert.equal(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with category.');
assert.equal(result.get(1).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with category.');
});
await testableView.show('@installed category:"themes"').then(result => {
assert.equal(result.length, 2, 'Unexpected number of results for @installed query with quoted category');
assert.equal(result.get(0).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with quoted category.');
assert.equal(result.get(1).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with quoted category.');
assert.equal(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with quoted category.');
assert.equal(result.get(1).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with quoted category.');
});
await testableView.show('@installed category:"programming languages"').then(result => {
assert.equal(result.length, 2, 'Unexpected number of results for @installed query with quoted category including space');
assert.equal(result.get(0).name, localDisabledLanguage.manifest.name, 'Unexpected extension for @installed query with quoted category inlcuding space.');
assert.equal(result.get(1).name, localEnabledLanguage.manifest.name, 'Unexpected extension for @installed query with quoted category including space.');
assert.equal(result.get(0).name, localEnabledLanguage.manifest.name, 'Unexpected extension for @installed query with quoted category including space.');
assert.equal(result.get(1).name, localDisabledLanguage.manifest.name, 'Unexpected extension for @installed query with quoted category inlcuding space.');
});
await testableView.show('@installed category:themes category:random').then(result => {
assert.equal(result.length, 3, 'Unexpected number of results for @installed query with multiple category');
assert.equal(result.get(0).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with multiple category.');
assert.equal(result.get(1).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with multiple category.');
assert.equal(result.get(2).name, localRandom.manifest.name, 'Unexpected extension for @installed query with multiple category.');
assert.equal(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with multiple category.');
assert.equal(result.get(1).name, localRandom.manifest.name, 'Unexpected extension for @installed query with multiple category.');
assert.equal(result.get(2).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with multiple category.');
});
await testableView.show('@enabled category:themes').then(result => {
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ShowViewletAction } from 'vs/workbench/browser/viewlet';
import * as nls from 'vs/nls';
import { sep } from 'vs/base/common/path';
@@ -159,7 +159,7 @@ class FileEditorInputFactory implements IEditorInputFactory {
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): FileEditorInput {
return instantiationService.invokeFunction<FileEditorInput>(accessor => {
const fileInput: ISerializedFileInput = JSON.parse(serializedEditorInput);
const resource = !!fileInput.resourceJSON ? URI.revive(fileInput.resourceJSON) : URI.parse(fileInput.resource);
const resource = !!fileInput.resourceJSON ? URI.revive(<UriComponents>fileInput.resourceJSON) : URI.parse(fileInput.resource);
const encoding = fileInput.encoding;
return accessor.get(IEditorService).createInput({ resource, encoding, forceFile: true }) as FileEditorInput;
@@ -160,9 +160,9 @@ export class SaveErrorHandler extends Disposable implements ISaveErrorHandler, I
if (isReadonly) {
if (triedToMakeWriteable) {
message = isWindows ? nls.localize('readonlySaveErrorAdmin', "Failed to save '{0}': File is write protected. Select 'Overwrite as Admin' to retry as administrator.", basename(resource)) : nls.localize('readonlySaveErrorSudo', "Failed to save '{0}': File is write protected. Select 'Overwrite as Sudo' to retry as superuser.", basename(resource));
message = isWindows ? nls.localize('readonlySaveErrorAdmin', "Failed to save '{0}': File is read-only. Select 'Overwrite as Admin' to retry as administrator.", basename(resource)) : nls.localize('readonlySaveErrorSudo', "Failed to save '{0}': File is read-only. Select 'Overwrite as Sudo' to retry as superuser.", basename(resource));
} else {
message = nls.localize('readonlySaveError', "Failed to save '{0}': File is write protected. Select 'Overwrite' to attempt to remove protection.", basename(resource));
message = nls.localize('readonlySaveError', "Failed to save '{0}': File is read-only. Select 'Overwrite' to attempt to make it writeable.", basename(resource));
}
} else if (isPermissionDenied) {
message = isWindows ? nls.localize('permissionDeniedSaveError', "Failed to save '{0}': Insufficient permissions. Select 'Retry as Admin' to retry as administrator.", basename(resource)) : nls.localize('permissionDeniedSaveErrorSudo', "Failed to save '{0}': Insufficient permissions. Select 'Retry as Sudo' to retry as superuser.", basename(resource));
@@ -6,9 +6,9 @@
import { Registry } from 'vs/platform/registry/common/platform';
import * as nls from 'vs/nls';
import product from 'vs/platform/product/node/product';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { SyncActionDescriptor, ICommandAction, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
import { OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, OpenProcessExplorer } from 'vs/workbench/contrib/issue/electron-browser/issueActions';
import { ReportPerformanceIssueUsingReporterAction, OpenProcessExplorer } from 'vs/workbench/contrib/issue/electron-browser/issueActions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issue';
import { WorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issueService';
@@ -19,8 +19,27 @@ const helpCategory = nls.localize('help', "Help");
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
if (!!product.reportIssueUrl) {
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenIssueReporterAction, OpenIssueReporterAction.ID, OpenIssueReporterAction.LABEL), 'Help: Open Issue Reporter', helpCategory);
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReportPerformanceIssueUsingReporterAction, ReportPerformanceIssueUsingReporterAction.ID, ReportPerformanceIssueUsingReporterAction.LABEL), 'Help: Report Performance Issue', helpCategory);
const OpenIssueReporterActionId = 'workbench.action.openIssueReporter';
const OpenIssueReporterActionLabel = nls.localize({ key: 'reportIssueInEnglish', comment: ['Translate this to "Report Issue in English" in all languages please!'] }, "Report Issue");
CommandsRegistry.registerCommand(OpenIssueReporterActionId, function (accessor, args?: [string]) {
let extensionId: string | undefined;
if (args && Array.isArray(args)) {
[extensionId] = args;
}
return accessor.get(IWorkbenchIssueService).openReporter({ extensionId });
});
const command: ICommandAction = {
id: OpenIssueReporterActionId,
title: { value: OpenIssueReporterActionLabel, original: 'Help: Open Issue Reporter' },
category: helpCategory
};
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command });
}
const developerCategory = nls.localize('developer', "Developer");
@@ -8,23 +8,6 @@ import * as nls from 'vs/nls';
import { IssueType } from 'vs/platform/issue/common/issue';
import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issue';
export class OpenIssueReporterAction extends Action {
static readonly ID = 'workbench.action.openIssueReporter';
static readonly LABEL = nls.localize({ key: 'reportIssueInEnglish', comment: ['Translate this to "Report Issue in English" in all languages please!'] }, "Report Issue");
constructor(
id: string,
label: string,
@IWorkbenchIssueService private readonly issueService: IWorkbenchIssueService
) {
super(id, label);
}
run(): Promise<boolean> {
return this.issueService.openReporter().then(() => true);
}
}
export class OpenProcessExplorer extends Action {
static readonly ID = 'workbench.action.openProcessExplorer';
static readonly LABEL = nls.localize('openProcessExplorer', "Open Process Explorer");

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