Archived
Merge from vscode 3d67364fbfcf676d93be64f949e9b33e7f1b969e (#5028)
This commit is contained in:
Vendored
+4
@@ -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();
|
||||
|
||||
Vendored
+32
@@ -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'
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 |
@@ -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 |
@@ -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[] = [];
|
||||
|
||||
@@ -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 |
Binary file not shown.
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
Executable
+39
@@ -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
@@ -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]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Vendored
+5
-2
@@ -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.
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ export interface IssueReporterData extends WindowData {
|
||||
styles: IssueReporterStyles;
|
||||
enabledExtensions: IssueReporterExtensionData[];
|
||||
issueType?: IssueType;
|
||||
extensionId?: string;
|
||||
}
|
||||
|
||||
export interface ISettingSearchResult {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Vendored
+5
@@ -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`.
|
||||
*/
|
||||
|
||||
Vendored
-5
@@ -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)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user