Merge from vscode b12f623603e2fc1c5b3037115fa37c1a6acc4165 (#6760)

This commit is contained in:
Anthony Dresser
2019-08-15 02:19:31 -07:00
committed by GitHub
parent 4966ed8b42
commit 58bfba4b47
161 changed files with 2072 additions and 1317 deletions
@@ -0,0 +1,220 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as DOM from 'vs/base/browser/dom';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { DisposableStore } from 'vs/base/common/lifecycle';
export interface IContentActionHandler {
callback: (content: string, event?: IMouseEvent) => void;
readonly disposeables: DisposableStore;
}
export interface FormattedTextRenderOptions {
readonly className?: string;
readonly inline?: boolean;
readonly actionHandler?: IContentActionHandler;
}
export function renderText(text: string, options: FormattedTextRenderOptions = {}): HTMLElement {
const element = createElement(options);
element.textContent = text;
return element;
}
export function renderFormattedText(formattedText: string, options: FormattedTextRenderOptions = {}): HTMLElement {
const element = createElement(options);
_renderFormattedText(element, parseFormattedText(formattedText), options.actionHandler);
return element;
}
export function createElement(options: FormattedTextRenderOptions): HTMLElement {
const tagName = options.inline ? 'span' : 'div';
const element = document.createElement(tagName);
if (options.className) {
element.className = options.className;
}
return element;
}
class StringStream {
private source: string;
private index: number;
constructor(source: string) {
this.source = source;
this.index = 0;
}
public eos(): boolean {
return this.index >= this.source.length;
}
public next(): string {
const next = this.peek();
this.advance();
return next;
}
public peek(): string {
return this.source[this.index];
}
public advance(): void {
this.index++;
}
}
const enum FormatType {
Invalid,
Root,
Text,
Bold,
Italics,
Action,
ActionClose,
NewLine
}
interface IFormatParseTree {
type: FormatType;
content?: string;
index?: number;
children?: IFormatParseTree[];
}
function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionHandler?: IContentActionHandler) {
let child: Node | undefined;
if (treeNode.type === FormatType.Text) {
child = document.createTextNode(treeNode.content || '');
} else if (treeNode.type === FormatType.Bold) {
child = document.createElement('b');
} else if (treeNode.type === FormatType.Italics) {
child = document.createElement('i');
} else if (treeNode.type === FormatType.Action && actionHandler) {
const a = document.createElement('a');
a.href = '#';
actionHandler.disposeables.add(DOM.addStandardDisposableListener(a, 'click', (event) => {
actionHandler.callback(String(treeNode.index), event);
}));
child = a;
} else if (treeNode.type === FormatType.NewLine) {
child = document.createElement('br');
} else if (treeNode.type === FormatType.Root) {
child = element;
}
if (child && element !== child) {
element.appendChild(child);
}
if (child && Array.isArray(treeNode.children)) {
treeNode.children.forEach((nodeChild) => {
_renderFormattedText(child!, nodeChild, actionHandler);
});
}
}
function parseFormattedText(content: string): IFormatParseTree {
const root: IFormatParseTree = {
type: FormatType.Root,
children: []
};
let actionViewItemIndex = 0;
let current = root;
const stack: IFormatParseTree[] = [];
const stream = new StringStream(content);
while (!stream.eos()) {
let next = stream.next();
const isEscapedFormatType = (next === '\\' && formatTagType(stream.peek()) !== FormatType.Invalid);
if (isEscapedFormatType) {
next = stream.next(); // unread the backslash if it escapes a format tag type
}
if (!isEscapedFormatType && isFormatTag(next) && next === stream.peek()) {
stream.advance();
if (current.type === FormatType.Text) {
current = stack.pop()!;
}
const type = formatTagType(next);
if (current.type === type || (current.type === FormatType.Action && type === FormatType.ActionClose)) {
current = stack.pop()!;
} else {
const newCurrent: IFormatParseTree = {
type: type,
children: []
};
if (type === FormatType.Action) {
newCurrent.index = actionViewItemIndex;
actionViewItemIndex++;
}
current.children!.push(newCurrent);
stack.push(current);
current = newCurrent;
}
} else if (next === '\n') {
if (current.type === FormatType.Text) {
current = stack.pop()!;
}
current.children!.push({
type: FormatType.NewLine
});
} else {
if (current.type !== FormatType.Text) {
const textCurrent: IFormatParseTree = {
type: FormatType.Text,
content: next
};
current.children!.push(textCurrent);
stack.push(current);
current = textCurrent;
} else {
current.content += next;
}
}
}
if (current.type === FormatType.Text) {
current = stack.pop()!;
}
if (stack.length) {
// incorrectly formatted string literal
}
return root;
}
function isFormatTag(char: string): boolean {
return formatTagType(char) !== FormatType.Invalid;
}
function formatTagType(char: string): FormatType {
switch (char) {
case '*':
return FormatType.Bold;
case '_':
return FormatType.Italics;
case '[':
return FormatType.Action;
case ']':
return FormatType.ActionClose;
default:
return FormatType.Invalid;
}
}
@@ -4,55 +4,25 @@
*--------------------------------------------------------------------------------------------*/
import * as DOM from 'vs/base/browser/dom';
import { defaultGenerator } from 'vs/base/common/idGenerator';
import { escape } from 'vs/base/common/strings';
import { removeMarkdownEscapes, IMarkdownString, parseHrefAndDimensions } from 'vs/base/common/htmlContent';
import * as marked from 'vs/base/common/marked/marked';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { createElement, FormattedTextRenderOptions } from 'vs/base/browser/formattedTextRenderer';
import { onUnexpectedError } from 'vs/base/common/errors';
import { URI } from 'vs/base/common/uri';
import { IMarkdownString, parseHrefAndDimensions, removeMarkdownEscapes } from 'vs/base/common/htmlContent';
import { defaultGenerator } from 'vs/base/common/idGenerator';
import * as marked from 'vs/base/common/marked/marked';
import { parse } from 'vs/base/common/marshalling';
import { cloneAndChange } from 'vs/base/common/objects';
import { escape } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
export interface IContentActionHandler {
callback: (content: string, event?: IMouseEvent) => void;
readonly disposeables: DisposableStore;
}
export interface RenderOptions {
className?: string;
inline?: boolean;
actionHandler?: IContentActionHandler;
export interface MarkdownRenderOptions extends FormattedTextRenderOptions {
codeBlockRenderer?: (modeId: string, value: string) => Promise<string>;
codeBlockRenderCallback?: () => void;
}
function createElement(options: RenderOptions): HTMLElement {
const tagName = options.inline ? 'span' : 'div';
const element = document.createElement(tagName);
if (options.className) {
element.className = options.className;
}
return element;
}
export function renderText(text: string, options: RenderOptions = {}): HTMLElement {
const element = createElement(options);
element.textContent = text;
return element;
}
export function renderFormattedText(formattedText: string, options: RenderOptions = {}): HTMLElement {
const element = createElement(options);
_renderFormattedText(element, parseFormattedText(formattedText), options.actionHandler);
return element;
}
/**
* Create html nodes for the given content element.
*/
export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions = {}): HTMLElement {
export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRenderOptions = {}): HTMLElement {
const element = createElement(options);
const _uriMassage = function (part: string): string {
@@ -219,190 +189,3 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions
return element;
}
// --- formatted string parsing
class StringStream {
private source: string;
private index: number;
constructor(source: string) {
this.source = source;
this.index = 0;
}
public eos(): boolean {
return this.index >= this.source.length;
}
public next(): string {
const next = this.peek();
this.advance();
return next;
}
public peek(): string {
return this.source[this.index];
}
public advance(): void {
this.index++;
}
}
const enum FormatType {
Invalid,
Root,
Text,
Bold,
Italics,
Action,
ActionClose,
NewLine
}
interface IFormatParseTree {
type: FormatType;
content?: string;
index?: number;
children?: IFormatParseTree[];
}
function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionHandler?: IContentActionHandler) {
let child: Node | undefined;
if (treeNode.type === FormatType.Text) {
child = document.createTextNode(treeNode.content || '');
}
else if (treeNode.type === FormatType.Bold) {
child = document.createElement('b');
}
else if (treeNode.type === FormatType.Italics) {
child = document.createElement('i');
}
else if (treeNode.type === FormatType.Action && actionHandler) {
const a = document.createElement('a');
a.href = '#';
actionHandler.disposeables.add(DOM.addStandardDisposableListener(a, 'click', (event) => {
actionHandler.callback(String(treeNode.index), event);
}));
child = a;
}
else if (treeNode.type === FormatType.NewLine) {
child = document.createElement('br');
}
else if (treeNode.type === FormatType.Root) {
child = element;
}
if (child && element !== child) {
element.appendChild(child);
}
if (child && Array.isArray(treeNode.children)) {
treeNode.children.forEach((nodeChild) => {
_renderFormattedText(child!, nodeChild, actionHandler);
});
}
}
function parseFormattedText(content: string): IFormatParseTree {
const root: IFormatParseTree = {
type: FormatType.Root,
children: []
};
let actionViewItemIndex = 0;
let current = root;
const stack: IFormatParseTree[] = [];
const stream = new StringStream(content);
while (!stream.eos()) {
let next = stream.next();
const isEscapedFormatType = (next === '\\' && formatTagType(stream.peek()) !== FormatType.Invalid);
if (isEscapedFormatType) {
next = stream.next(); // unread the backslash if it escapes a format tag type
}
if (!isEscapedFormatType && isFormatTag(next) && next === stream.peek()) {
stream.advance();
if (current.type === FormatType.Text) {
current = stack.pop()!;
}
const type = formatTagType(next);
if (current.type === type || (current.type === FormatType.Action && type === FormatType.ActionClose)) {
current = stack.pop()!;
} else {
const newCurrent: IFormatParseTree = {
type: type,
children: []
};
if (type === FormatType.Action) {
newCurrent.index = actionViewItemIndex;
actionViewItemIndex++;
}
current.children!.push(newCurrent);
stack.push(current);
current = newCurrent;
}
} else if (next === '\n') {
if (current.type === FormatType.Text) {
current = stack.pop()!;
}
current.children!.push({
type: FormatType.NewLine
});
} else {
if (current.type !== FormatType.Text) {
const textCurrent: IFormatParseTree = {
type: FormatType.Text,
content: next
};
current.children!.push(textCurrent);
stack.push(current);
current = textCurrent;
} else {
current.content += next;
}
}
}
if (current.type === FormatType.Text) {
current = stack.pop()!;
}
if (stack.length) {
// incorrectly formatted string literal
}
return root;
}
function isFormatTag(char: string): boolean {
return formatTagType(char) !== FormatType.Invalid;
}
function formatTagType(char: string): FormatType {
switch (char) {
case '*':
return FormatType.Bold;
case '_':
return FormatType.Italics;
case '[':
return FormatType.Action;
case ']':
return FormatType.ActionClose;
default:
return FormatType.Invalid;
}
}
+3 -2
View File
@@ -8,7 +8,8 @@ import 'vs/css!./inputBox';
import * as nls from 'vs/nls';
import * as Bal from 'vs/base/browser/browser';
import * as dom from 'vs/base/browser/dom';
import { RenderOptions, renderFormattedText, renderText } from 'vs/base/browser/htmlContentRenderer';
import { MarkdownRenderOptions } from 'vs/base/browser/markdownRenderer';
import { renderFormattedText, renderText } from 'vs/base/browser/formattedTextRenderer';
import * as aria from 'vs/base/browser/ui/aria/aria';
import { IAction } from 'vs/base/common/actions';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
@@ -472,7 +473,7 @@ export class InputBox extends Widget {
div = dom.append(container, $('.monaco-inputbox-container'));
layout();
const renderOptions: RenderOptions = {
const renderOptions: MarkdownRenderOptions = {
inline: true,
className: 'monaco-inputbox-message'
};
@@ -18,7 +18,7 @@ import { domEvent } from 'vs/base/browser/event';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { ISelectBoxDelegate, ISelectOptionItem, ISelectBoxOptions, ISelectBoxStyles, ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
import { isMacintosh } from 'vs/base/common/platform';
import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer';
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
// {{SQL CARBON EDIT}} import color
import { Color } from 'vs/base/common/color';
+15
View File
@@ -729,3 +729,18 @@ export function getNLines(str: string, n = 1): string {
str.substr(0, idx) :
str;
}
/**
* Produces 'a'-'z', followed by 'A'-'Z'... followed by 'a'-'z', etc.
*/
export function singleLetterHash(n: number): string {
const LETTERS_CNT = (CharCode.Z - CharCode.A + 1);
n = n % (2 * LETTERS_CNT);
if (n < LETTERS_CNT) {
return String.fromCharCode(CharCode.a + n);
}
return String.fromCharCode(CharCode.A + n - LETTERS_CNT);
}
@@ -2,12 +2,12 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as marked from 'vs/base/common/marked/marked';
import { renderMarkdown, renderText, renderFormattedText } from 'vs/base/browser/htmlContentRenderer';
import { renderText, renderFormattedText } from 'vs/base/browser/formattedTextRenderer';
import { DisposableStore } from 'vs/base/common/lifecycle';
suite('HtmlContent', () => {
suite('FormattedTextRenderer', () => {
const store = new DisposableStore();
setup(() => {
@@ -101,36 +101,4 @@ suite('HtmlContent', () => {
assert.strictEqual(result.children.length, 0);
assert.strictEqual(result.innerHTML, '**bold**');
});
test('image rendering conforms to default', () => {
const markdown = { value: `![image](someimageurl 'caption')` };
const result: HTMLElement = renderMarkdown(markdown);
const renderer = new marked.Renderer();
const imageFromMarked = marked(markdown.value, {
sanitize: true,
renderer
}).trim();
assert.strictEqual(result.innerHTML, imageFromMarked);
});
test('image rendering conforms to default without title', () => {
const markdown = { value: `![image](someimageurl)` };
const result: HTMLElement = renderMarkdown(markdown);
const renderer = new marked.Renderer();
const imageFromMarked = marked(markdown.value, {
sanitize: true,
renderer
}).trim();
assert.strictEqual(result.innerHTML, imageFromMarked);
});
test('image width from title params', () => {
let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|width=100 'caption')` });
assert.strictEqual(result.innerHTML, `<p><img src="someimageurl" alt="image" title="caption" width="100"></p>`);
});
test('image height from title params', () => {
let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=100 'caption')` });
assert.strictEqual(result.innerHTML, `<p><img src="someimageurl" alt="image" title="caption" height="100"></p>`);
});
test('image width and height from title params', () => {
let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=200,width=100 'caption')` });
assert.strictEqual(result.innerHTML, `<p><img src="someimageurl" alt="image" title="caption" width="100" height="200"></p>`);
});
});
@@ -0,0 +1,47 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as marked from 'vs/base/common/marked/marked';
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
suite('MarkdownRenderer', () => {
test('image rendering conforms to default', () => {
const markdown = { value: `![image](someimageurl 'caption')` };
const result: HTMLElement = renderMarkdown(markdown);
const renderer = new marked.Renderer();
const imageFromMarked = marked(markdown.value, {
sanitize: true,
renderer
}).trim();
assert.strictEqual(result.innerHTML, imageFromMarked);
});
test('image rendering conforms to default without title', () => {
const markdown = { value: `![image](someimageurl)` };
const result: HTMLElement = renderMarkdown(markdown);
const renderer = new marked.Renderer();
const imageFromMarked = marked(markdown.value, {
sanitize: true,
renderer
}).trim();
assert.strictEqual(result.innerHTML, imageFromMarked);
});
test('image width from title params', () => {
let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|width=100 'caption')` });
assert.strictEqual(result.innerHTML, `<p><img src="someimageurl" alt="image" title="caption" width="100"></p>`);
});
test('image height from title params', () => {
let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=100 'caption')` });
assert.strictEqual(result.innerHTML, `<p><img src="someimageurl" alt="image" title="caption" height="100"></p>`);
});
test('image width and height from title params', () => {
let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=200,width=100 'caption')` });
assert.strictEqual(result.innerHTML, `<p><img src="someimageurl" alt="image" title="caption" width="100" height="200"></p>`);
});
});
+19 -14
View File
@@ -19,26 +19,31 @@ function getWorker(workerId: string, label: string): Worker | Promise<Worker> {
// ESM-comment-begin
if (typeof require === 'function') {
// check if the JS lives on a different origin
const workerMain = require.toUrl('./' + workerId);
if (/^(http:)|(https:)|(file:)/.test(workerMain)) {
const currentUrl = String(window.location);
const currentOrigin = currentUrl.substr(0, currentUrl.length - window.location.hash.length - window.location.search.length - window.location.pathname.length);
if (workerMain.substring(0, currentOrigin.length) !== currentOrigin) {
// this is the cross-origin case
// i.e. the webpage is running at a different origin than where the scripts are loaded from
const workerBaseUrl = workerMain.substr(0, workerMain.length - 'vs/base/worker/workerMain.js'.length);
const js = `/*${label}*/self.MonacoEnvironment={baseUrl: '${workerBaseUrl}'};importScripts('${workerMain}');/*${label}*/`;
const url = `data:text/javascript;charset=utf-8,${encodeURIComponent(js)}`;
return new Worker(url);
}
}
return new Worker(workerMain + '#' + label);
const workerUrl = getWorkerBootstrapUrl(workerMain, label);
return new Worker(workerUrl, { name: label });
}
// ESM-comment-end
throw new Error(`You must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker`);
}
export function getWorkerBootstrapUrl(scriptPath: string, label: string): string {
if (/^(http:)|(https:)|(file:)/.test(scriptPath)) {
const currentUrl = String(window.location);
const currentOrigin = currentUrl.substr(0, currentUrl.length - window.location.hash.length - window.location.search.length - window.location.pathname.length);
if (scriptPath.substring(0, currentOrigin.length) !== currentOrigin) {
// this is the cross-origin case
// i.e. the webpage is running at a different origin than where the scripts are loaded from
const myPath = 'vs/base/worker/defaultWorkerFactory.js';
const workerBaseUrl = require.toUrl(myPath).slice(0, -myPath.length);
const js = `/*${label}*/self.MonacoEnvironment={baseUrl: '${workerBaseUrl}'};importScripts('${scriptPath}');/*${label}*/`;
const url = `data:text/javascript;charset=utf-8,${encodeURIComponent(js)}`;
return url;
}
}
return scriptPath + '#' + label;
}
function isPromiseLike<T>(obj: any): obj is PromiseLike<T> {
if (typeof obj.then === 'function') {
return true;
+1 -1
View File
@@ -7,7 +7,7 @@ import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, p
import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform';
import { WindowsManager } from 'vs/code/electron-main/windows';
import { IWindowsService, OpenContext, ActiveWindowManager, IURIToOpen } from 'vs/platform/windows/common/windows';
import { WindowsChannel } from 'vs/platform/windows/node/windowsIpc';
import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc';
import { WindowsService } from 'vs/platform/windows/electron-main/windowsService';
import { ILifecycleService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { getShellEnvironment } from 'vs/code/node/shellEnv';
@@ -49,7 +49,7 @@ export interface IPointerHandlerHelper {
*/
getLastViewCursorsRenderData(): IViewCursorRenderData[];
shouldSuppressMouseDownOnViewZone(viewZoneId: number): boolean;
shouldSuppressMouseDownOnViewZone(viewZoneId: string): boolean;
shouldSuppressMouseDownOnWidget(widgetId: string): boolean;
/**
@@ -19,7 +19,7 @@ import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
import { CursorColumns } from 'vs/editor/common/controller/cursorCommon';
export interface IViewZoneData {
viewZoneId: number;
viewZoneId: string;
positionBefore: Position | null;
positionAfter: Position | null;
position: Position;
+4 -4
View File
@@ -83,17 +83,17 @@ export interface IViewZoneChangeAccessor {
* @param zone Zone to create
* @return A unique identifier to the view zone.
*/
addZone(zone: IViewZone): number;
addZone(zone: IViewZone): string;
/**
* Remove a zone
* @param id A unique identifier to the view zone, as returned by the `addZone` call.
*/
removeZone(id: number): void;
removeZone(id: string): void;
/**
* Change a zone's position.
* The editor will rescan the `afterLineNumber` and `afterColumn` properties of a view zone.
*/
layoutZone(id: number): void;
layoutZone(id: string): void;
}
/**
@@ -399,7 +399,7 @@ export interface ICodeEditor extends editorCommon.IEditor {
*/
onWillType(listener: (text: string) => void): IDisposable;
/**
* An event emitted before interpreting typed characters (on the keyboard).
* An event emitted after interpreting typed characters (on the keyboard).
* @event
* @internal
*/
@@ -55,8 +55,7 @@ export class OpenerService implements IOpenerService {
if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https) || equalsIgnoreCase(scheme, Schemas.mailto)) {
// open http or default mail application
dom.windowOpenNoOpener(encodeURI(resource.toString(true)));
return Promise.resolve(true);
return this.openExternal(resource);
} else if (equalsIgnoreCase(scheme, Schemas.command)) {
// run command or bail out if command isn't known
@@ -100,4 +99,10 @@ export class OpenerService implements IOpenerService {
).then(() => true);
}
}
openExternal(resource: URI): Promise<boolean> {
dom.windowOpenNoOpener(encodeURI(resource.toString(true)));
return Promise.resolve(true);
}
}
+4 -4
View File
@@ -248,7 +248,7 @@ export class View extends ViewEventHandler {
getLastViewCursorsRenderData: () => {
return this.viewCursors.getLastRenderData() || [];
},
shouldSuppressMouseDownOnViewZone: (viewZoneId: number) => {
shouldSuppressMouseDownOnViewZone: (viewZoneId: string) => {
return this.viewZones.shouldSuppressMouseDownOnViewZone(viewZoneId);
},
shouldSuppressMouseDownOnWidget: (widgetId: string) => {
@@ -473,17 +473,17 @@ export class View extends ViewEventHandler {
this._renderOnce(() => {
const changeAccessor: editorBrowser.IViewZoneChangeAccessor = {
addZone: (zone: editorBrowser.IViewZone): number => {
addZone: (zone: editorBrowser.IViewZone): string => {
zonesHaveChanged = true;
return this.viewZones.addZone(zone);
},
removeZone: (id: number): void => {
removeZone: (id: string): void => {
if (!id) {
return;
}
zonesHaveChanged = this.viewZones.removeZone(id) || zonesHaveChanged;
},
layoutZone: (id: number): void => {
layoutZone: (id: string): void => {
if (!id) {
return;
}
@@ -14,7 +14,7 @@ import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel';
export interface IMyViewZone {
whitespaceId: number;
whitespaceId: string;
delegate: IViewZone;
isVisible: boolean;
domNode: FastDomNode<HTMLElement>;
@@ -74,7 +74,7 @@ export class ViewZones extends ViewPart {
const id = keys[i];
const zone = this._zones[id];
const props = this._computeWhitespaceProps(zone.delegate);
if (this._context.viewLayout.changeWhitespace(parseInt(id, 10), props.afterViewLineNumber, props.heightInPx)) {
if (this._context.viewLayout.changeWhitespace(id, props.afterViewLineNumber, props.heightInPx)) {
this._safeCallOnComputedHeight(zone.delegate, props.heightInPx);
hadAChange = true;
}
@@ -183,7 +183,7 @@ export class ViewZones extends ViewPart {
};
}
public addZone(zone: IViewZone): number {
public addZone(zone: IViewZone): string {
const props = this._computeWhitespaceProps(zone);
const whitespaceId = this._context.viewLayout.addWhitespace(props.afterViewLineNumber, this._getZoneOrdinal(zone), props.heightInPx, props.minWidthInPx);
@@ -200,18 +200,18 @@ export class ViewZones extends ViewPart {
myZone.domNode.setPosition('absolute');
myZone.domNode.domNode.style.width = '100%';
myZone.domNode.setDisplay('none');
myZone.domNode.setAttribute('monaco-view-zone', myZone.whitespaceId.toString());
myZone.domNode.setAttribute('monaco-view-zone', myZone.whitespaceId);
this.domNode.appendChild(myZone.domNode);
if (myZone.marginDomNode) {
myZone.marginDomNode.setPosition('absolute');
myZone.marginDomNode.domNode.style.width = '100%';
myZone.marginDomNode.setDisplay('none');
myZone.marginDomNode.setAttribute('monaco-view-zone', myZone.whitespaceId.toString());
myZone.marginDomNode.setAttribute('monaco-view-zone', myZone.whitespaceId);
this.marginDomNode.appendChild(myZone.marginDomNode);
}
this._zones[myZone.whitespaceId.toString()] = myZone;
this._zones[myZone.whitespaceId] = myZone;
this.setShouldRender();
@@ -219,10 +219,10 @@ export class ViewZones extends ViewPart {
return myZone.whitespaceId;
}
public removeZone(id: number): boolean {
if (this._zones.hasOwnProperty(id.toString())) {
const zone = this._zones[id.toString()];
delete this._zones[id.toString()];
public removeZone(id: string): boolean {
if (this._zones.hasOwnProperty(id)) {
const zone = this._zones[id];
delete this._zones[id];
this._context.viewLayout.removeWhitespace(zone.whitespaceId);
zone.domNode.removeAttribute('monaco-visible-view-zone');
@@ -242,10 +242,10 @@ export class ViewZones extends ViewPart {
return false;
}
public layoutZone(id: number): boolean {
public layoutZone(id: string): boolean {
let changed = false;
if (this._zones.hasOwnProperty(id.toString())) {
const zone = this._zones[id.toString()];
if (this._zones.hasOwnProperty(id)) {
const zone = this._zones[id];
const props = this._computeWhitespaceProps(zone.delegate);
// const newOrdinal = this._getZoneOrdinal(zone.delegate);
changed = this._context.viewLayout.changeWhitespace(zone.whitespaceId, props.afterViewLineNumber, props.heightInPx) || changed;
@@ -259,9 +259,9 @@ export class ViewZones extends ViewPart {
return changed;
}
public shouldSuppressMouseDownOnViewZone(id: number): boolean {
if (this._zones.hasOwnProperty(id.toString())) {
const zone = this._zones[id.toString()];
public shouldSuppressMouseDownOnViewZone(id: string): boolean {
if (this._zones.hasOwnProperty(id)) {
const zone = this._zones[id];
return Boolean(zone.delegate.suppressMouseDown);
}
return false;
@@ -314,7 +314,7 @@ export class ViewZones extends ViewPart {
let hasVisibleZone = false;
for (let i = 0, len = visibleWhitespaces.length; i < len; i++) {
visibleZones[visibleWhitespaces[i].id.toString()] = visibleWhitespaces[i];
visibleZones[visibleWhitespaces[i].id] = visibleWhitespaces[i];
hasVisibleZone = true;
}
@@ -72,7 +72,7 @@ interface IDiffEditorWidgetStyle {
}
class VisualEditorState {
private _zones: number[];
private _zones: string[];
private _zonesMap: { [zoneId: string]: boolean; };
private _decorations: string[];
+1 -16
View File
@@ -110,21 +110,6 @@ export function createTextBuffer(value: string | model.ITextBufferFactory, defau
let MODEL_ID = 0;
/**
* Produces 'a'-'z', followed by 'A'-'Z'... followed by 'a'-'z', etc.
*/
function singleLetter(result: number): string {
const LETTERS_CNT = (CharCode.Z - CharCode.A + 1);
result = result % (2 * LETTERS_CNT);
if (result < LETTERS_CNT) {
return String.fromCharCode(CharCode.a + result);
}
return String.fromCharCode(CharCode.A + result - LETTERS_CNT);
}
const LIMIT_FIND_COUNT = 999;
export const LONG_LINE_BOUNDARY = 10000;
@@ -343,7 +328,7 @@ export class TextModel extends Disposable implements model.ITextModel {
}
});
this._instanceId = singleLetter(MODEL_ID);
this._instanceId = strings.singleLetterHash(MODEL_ID);
this._lastDecorationId = 0;
this._decorations = Object.create(null);
this._decorationsTree = new DecorationsTrees();
@@ -63,14 +63,14 @@ export class LinesLayout {
* @param heightInPx The height of the whitespace, in pixels.
* @return An id that can be used later to mutate or delete the whitespace
*/
public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): number {
public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string {
return this._whitespaces.insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth);
}
/**
* Change properties associated with a certain whitespace.
*/
public changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean {
public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean {
return this._whitespaces.changeWhitespace(id, newAfterLineNumber, newHeight);
}
@@ -80,7 +80,7 @@ export class LinesLayout {
* @param id The whitespace to remove
* @return Returns true if the whitespace is found and it is removed.
*/
public removeWhitespace(id: number): boolean {
public removeWhitespace(id: string): boolean {
return this._whitespaces.removeWhitespace(id);
}
@@ -173,13 +173,13 @@ export class ViewLayout extends Disposable implements IViewLayout {
// ---- IVerticalLayoutProvider
public addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): number {
public addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): string {
return this._linesLayout.insertWhitespace(afterLineNumber, ordinal, height, minWidth);
}
public changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean {
public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean {
return this._linesLayout.changeWhitespace(id, newAfterLineNumber, newHeight);
}
public removeWhitespace(id: number): boolean {
public removeWhitespace(id: string): boolean {
return this._linesLayout.removeWhitespace(id);
}
public getVerticalOffsetForLineNumber(lineNumber: number): number {
@@ -3,8 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as strings from 'vs/base/common/strings';
export interface IEditorWhitespace {
readonly id: number;
readonly id: string;
readonly afterLineNumber: number;
readonly heightInLines: number;
}
@@ -15,6 +17,10 @@ export interface IEditorWhitespace {
*/
export class WhitespaceComputer {
private static INSTANCE_COUNT = 0;
private readonly _instanceId: string;
/**
* heights[i] is the height in pixels for whitespace at index i
*/
@@ -48,7 +54,7 @@ export class WhitespaceComputer {
/**
* ids[i] is the whitespace id of whitespace at index i
*/
private readonly _ids: number[];
private readonly _ids: string[];
/**
* index at which a whitespace is positioned (inside heights, afterLineNumbers, prefixSum members)
@@ -65,6 +71,7 @@ export class WhitespaceComputer {
private _minWidth: number;
constructor() {
this._instanceId = strings.singleLetterHash(++WhitespaceComputer.INSTANCE_COUNT);
this._heights = [];
this._minWidths = [];
this._ids = [];
@@ -113,21 +120,20 @@ export class WhitespaceComputer {
* @param heightInPx The height of the whitespace, in pixels.
* @return An id that can be used later to mutate or delete the whitespace
*/
public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): number {
public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string {
afterLineNumber = afterLineNumber | 0;
ordinal = ordinal | 0;
heightInPx = heightInPx | 0;
minWidth = minWidth | 0;
let id = (++this._lastWhitespaceId);
let id = this._instanceId + (++this._lastWhitespaceId);
let insertionIndex = WhitespaceComputer.findInsertionIndex(this._afterLineNumbers, afterLineNumber, this._ordinals, ordinal);
this._insertWhitespaceAtIndex(id, insertionIndex, afterLineNumber, ordinal, heightInPx, minWidth);
this._minWidth = -1; /* marker for not being computed */
return id;
}
private _insertWhitespaceAtIndex(id: number, insertIndex: number, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): void {
id = id | 0;
private _insertWhitespaceAtIndex(id: string, insertIndex: number, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): void {
insertIndex = insertIndex | 0;
afterLineNumber = afterLineNumber | 0;
ordinal = ordinal | 0;
@@ -150,15 +156,14 @@ export class WhitespaceComputer {
}
}
this._whitespaceId2Index[id.toString()] = insertIndex;
this._whitespaceId2Index[id] = insertIndex;
this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, insertIndex - 1);
}
/**
* Change properties associated with a certain whitespace.
*/
public changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean {
id = id | 0;
public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean {
newAfterLineNumber = newAfterLineNumber | 0;
newHeight = newHeight | 0;
@@ -175,13 +180,11 @@ export class WhitespaceComputer {
* @param newHeightInPx The new height of the whitespace, in pixels
* @return Returns true if the whitespace is found and if the new height is different than the old height
*/
public changeWhitespaceHeight(id: number, newHeightInPx: number): boolean {
id = id | 0;
public changeWhitespaceHeight(id: string, newHeightInPx: number): boolean {
newHeightInPx = newHeightInPx | 0;
let sid = id.toString();
if (this._whitespaceId2Index.hasOwnProperty(sid)) {
let index = this._whitespaceId2Index[sid];
if (this._whitespaceId2Index.hasOwnProperty(id)) {
let index = this._whitespaceId2Index[id];
if (this._heights[index] !== newHeightInPx) {
this._heights[index] = newHeightInPx;
this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, index - 1);
@@ -198,13 +201,11 @@ export class WhitespaceComputer {
* @param newAfterLineNumber The new line number the whitespace will follow
* @return Returns true if the whitespace is found and if the new line number is different than the old line number
*/
public changeWhitespaceAfterLineNumber(id: number, newAfterLineNumber: number): boolean {
id = id | 0;
public changeWhitespaceAfterLineNumber(id: string, newAfterLineNumber: number): boolean {
newAfterLineNumber = newAfterLineNumber | 0;
let sid = id.toString();
if (this._whitespaceId2Index.hasOwnProperty(sid)) {
let index = this._whitespaceId2Index[sid];
if (this._whitespaceId2Index.hasOwnProperty(id)) {
let index = this._whitespaceId2Index[id];
if (this._afterLineNumbers[index] !== newAfterLineNumber) {
// `afterLineNumber` changed for this whitespace
@@ -236,14 +237,10 @@ export class WhitespaceComputer {
* @param id The whitespace to remove
* @return Returns true if the whitespace is found and it is removed.
*/
public removeWhitespace(id: number): boolean {
id = id | 0;
let sid = id.toString();
if (this._whitespaceId2Index.hasOwnProperty(sid)) {
let index = this._whitespaceId2Index[sid];
delete this._whitespaceId2Index[sid];
public removeWhitespace(id: string): boolean {
if (this._whitespaceId2Index.hasOwnProperty(id)) {
let index = this._whitespaceId2Index[id];
delete this._whitespaceId2Index[id];
this._removeWhitespaceAtIndex(index);
this._minWidth = -1; /* marker for not being computed */
return true;
@@ -459,7 +456,7 @@ export class WhitespaceComputer {
* @param index The index of the whitespace.
* @return `id` of whitespace at `index`.
*/
public getIdForWhitespaceIndex(index: number): number {
public getIdForWhitespaceIndex(index: number): string {
index = index | 0;
return this._ids[index];
+4 -4
View File
@@ -17,7 +17,7 @@ import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceCompute
import { ITheme } from 'vs/platform/theme/common/themeService';
export interface IViewWhitespaceViewportData {
readonly id: number;
readonly id: string;
readonly afterLineNumber: number;
readonly verticalOffset: number;
readonly height: number;
@@ -74,15 +74,15 @@ export interface IViewLayout {
* Reserve rendering space.
* @return an identifier that can be later used to remove or change the whitespace.
*/
addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): number;
addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): string;
/**
* Change the properties of a whitespace.
*/
changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean;
changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean;
/**
* Remove rendering space
*/
removeWhitespace(id: number): boolean;
removeWhitespace(id: string): boolean;
/**
* Get the layout information for whitespaces currently in the viewport
*/
@@ -193,7 +193,7 @@ export class CodeLensWidget {
private readonly _editor: editorBrowser.ICodeEditor;
private readonly _viewZone!: CodeLensViewZone;
private readonly _viewZoneId!: number;
private readonly _viewZoneId!: string;
private readonly _contentWidget!: CodeLensContentWidget;
private _decorationIds: string[];
private _data: CodeLensItem[];
+2 -10
View File
@@ -116,7 +116,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private readonly _replaceFocusTracker: dom.IFocusTracker;
private readonly _replaceInputFocused: IContextKey<boolean>;
private _viewZone?: FindWidgetViewZone;
private _viewZoneId?: number;
private _viewZoneId?: string;
private _resizeSash!: Sash;
private _resized!: boolean;
@@ -224,15 +224,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
if (!this._isVisible) {
return;
}
if (this._viewZoneId === undefined) {
return;
}
this._codeEditor.changeViewZones((accessor) => {
if (this._viewZoneId) {
accessor.removeZone(this._viewZoneId);
}
this._viewZoneId = undefined;
});
this._viewZoneId = undefined;
}));
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { renderMarkdown, RenderOptions } from 'vs/base/browser/htmlContentRenderer';
import { renderMarkdown, MarkdownRenderOptions } from 'vs/base/browser/markdownRenderer';
import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener';
import { IModeService } from 'vs/editor/common/services/modeService';
import { URI } from 'vs/base/common/uri';
@@ -33,7 +33,7 @@ export class MarkdownRenderer extends Disposable {
super();
}
private getOptions(disposeables: DisposableStore): RenderOptions {
private getOptions(disposeables: DisposableStore): MarkdownRenderOptions {
return {
codeBlockRenderer: (languageAlias, value) => {
// In markdown,
@@ -27,6 +27,7 @@ export const KnownSnippetVariableNames: { [key: string]: true } = Object.freeze(
'CURRENT_DAY_NAME_SHORT': true,
'CURRENT_MONTH_NAME': true,
'CURRENT_MONTH_NAME_SHORT': true,
'CURRENT_SECONDS_UNIX': true,
'SELECTION': true,
'CLIPBOARD': true,
'TM_SELECTED_TEXT': true,
@@ -245,6 +246,8 @@ export class TimeBasedVariableResolver implements VariableResolver {
return TimeBasedVariableResolver.monthNames[new Date().getMonth()];
} else if (name === 'CURRENT_MONTH_NAME_SHORT') {
return TimeBasedVariableResolver.monthNamesShort[new Date().getMonth()];
} else if (name === 'CURRENT_SECONDS_UNIX') {
return String(Math.floor(Date.now() / 1000));
}
return undefined;
@@ -281,6 +281,7 @@ suite('Snippet Variables Resolver', function () {
assertVariableResolve3(resolver, 'CURRENT_DAY_NAME_SHORT');
assertVariableResolve3(resolver, 'CURRENT_MONTH_NAME');
assertVariableResolve3(resolver, 'CURRENT_MONTH_NAME_SHORT');
assertVariableResolve3(resolver, 'CURRENT_SECONDS_UNIX');
});
test('creating snippet - format-condition doesn\'t work #53617', function () {
@@ -51,7 +51,7 @@ const WIDGET_ID = 'vs.editor.contrib.zoneWidget';
export class ViewZoneDelegate implements IViewZone {
public domNode: HTMLElement;
public id: number = 0; // A valid zone id should be greater than 0
public id: string = ''; // A valid zone id should be greater than 0
public afterLineNumber: number;
public afterColumn: number;
public heightInLines: number;
@@ -7,7 +7,7 @@ import 'vs/css!./accessibilityHelp';
import * as browser from 'vs/base/browser/browser';
import * as dom from 'vs/base/browser/dom';
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
import { renderFormattedText } from 'vs/base/browser/htmlContentRenderer';
import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer';
import { alert } from 'vs/base/browser/ui/aria/aria';
import { Widget } from 'vs/base/browser/ui/widget';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
+3 -3
View File
@@ -3609,17 +3609,17 @@ declare namespace monaco.editor {
* @param zone Zone to create
* @return A unique identifier to the view zone.
*/
addZone(zone: IViewZone): number;
addZone(zone: IViewZone): string;
/**
* Remove a zone
* @param id A unique identifier to the view zone, as returned by the `addZone` call.
*/
removeZone(id: number): void;
removeZone(id: string): void;
/**
* Change a zone's position.
* The editor will rescan the `afterLineNumber` and `afterColumn` properties of a view zone.
*/
layoutZone(id: number): void;
layoutZone(id: string): void;
}
/**
@@ -392,10 +392,10 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
@IProductService private readonly productService: IProductService,
@optional(IStorageService) private readonly storageService: IStorageService,
) {
const config = productService.extensionsGallery;
const config = productService.productConfiguration.extensionsGallery;
this.extensionsGalleryUrl = config && config.serviceUrl;
this.extensionsControlUrl = config && config.controlUrl;
this.commonHeadersPromise = resolveMarketplaceHeaders(productService.version, this.environmentService, this.fileService, this.storageService);
this.commonHeadersPromise = resolveMarketplaceHeaders(productService.productConfiguration.version, this.environmentService, this.fileService, this.storageService);
}
private api(path = ''): string {
@@ -440,7 +440,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
const versionAsset = rawExtension.versions.filter(v => v.version === version)[0];
if (versionAsset) {
const extension = toExtension(rawExtension, versionAsset, 0, query);
if (extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.version)) {
if (extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.productConfiguration.version)) {
return extension;
}
}
@@ -788,7 +788,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return this.queryGallery(query, CancellationToken.None).then(({ galleryExtensions }) => {
if (galleryExtensions.length) {
if (compatible) {
return Promise.all(galleryExtensions[0].versions.map(v => this.getEngine(v).then(engine => isEngineValid(engine, this.productService.version) ? v : null)))
return Promise.all(galleryExtensions[0].versions.map(v => this.getEngine(v).then(engine => isEngineValid(engine, this.productService.productConfiguration.version) ? v : null)))
.then(versions => versions
.filter(v => !!v)
.map(v => ({ version: v!.version, date: v!.lastUpdated })));
@@ -874,7 +874,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
if (!engine) {
return null;
}
if (isEngineValid(engine, this.productService.version)) {
if (isEngineValid(engine, this.productService.productConfiguration.version)) {
return Promise.resolve(version);
}
}
@@ -906,7 +906,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
const version = versions[0];
return this.getEngine(version)
.then(engine => {
if (!isEngineValid(engine, this.productService.version)) {
if (!isEngineValid(engine, this.productService.productConfiguration.version)) {
return this.getLastValidExtensionVersionRecursively(extension, versions.slice(1));
}
@@ -986,4 +986,4 @@ export async function resolveMarketplaceHeaders(version: string, environmentServ
return headers;
}
}
@@ -106,7 +106,7 @@ export interface IExtensionContributions {
localizations?: ILocalization[];
}
export type ExtensionKind = 'ui' | 'workspace';
export type ExtensionKind = 'ui' | 'workspace' | 'web';
export function isIExtensionIdentifier(thing: any): thing is IExtensionIdentifier {
return thing
+12 -2
View File
@@ -9,7 +9,6 @@ import { IDisposable } from 'vs/base/common/lifecycle';
export const IOpenerService = createDecorator<IOpenerService>('openerService');
export interface IOpener {
open(resource: URI, options?: { openToSide?: boolean }): Promise<boolean>;
}
@@ -18,6 +17,9 @@ export interface IOpenerService {
_serviceBrand: any;
/**
* Register a participant that can handle the open() call.
*/
registerOpener(opener: IOpener): IDisposable;
/**
@@ -27,10 +29,18 @@ export interface IOpenerService {
* @return A promise that resolves when the opening is done.
*/
open(resource: URI, options?: { openToSide?: boolean }): Promise<boolean>;
/**
* Opens a URL externally.
*
* @param url A resource to open externally.
*/
openExternal(resource: URI): Promise<boolean>;
}
export const NullOpenerService: IOpenerService = Object.freeze({
_serviceBrand: undefined,
registerOpener() { return { dispose() { } }; },
open() { return Promise.resolve(false); }
open() { return Promise.resolve(false); },
openExternal() { return Promise.resolve(false); }
});
@@ -10,44 +10,18 @@ export class ProductService implements IProductService {
_serviceBrand!: ServiceIdentifier<IProductService>;
private readonly productConfiguration: IProductConfiguration | null;
readonly productConfiguration: IProductConfiguration;
constructor() {
const element = document.getElementById('vscode-remote-product-configuration');
this.productConfiguration = element ? JSON.parse(element.getAttribute('data-settings')!) : null;
this.productConfiguration = {
...element ? JSON.parse(element.getAttribute('data-settings')!) : {
version: '1.38.0-unknown',
nameLong: 'Unknown',
extensionAllowedProposedApi: [],
}, ...{ urlProtocol: '', enableTelemetry: false },
...{ vscodeVersion: '1.35.0' } // {{SQL CARBON EDIT}} add vscodeversion
};
}
get version(): string { return this.productConfiguration && this.productConfiguration.version ? this.productConfiguration.version : '1.38.0-unknown'; }
get vscodeVersion(): string { return '1.35.0'; } // {{SQL CARBON EDIT}} add vscodeversion
get recommendedExtensionsByScenario(): { [area: string]: Array<string> } { return this.productConfiguration ? this.productConfiguration.recommendedExtensionsByScenario : {}; }// {{SQL CARBON EDIT}} add getter
get commit(): string | undefined { return this.productConfiguration ? this.productConfiguration.commit : undefined; }
get nameLong(): string { return this.productConfiguration ? this.productConfiguration.nameLong : 'Unknown'; }
get urlProtocol(): string { return ''; }
get extensionAllowedProposedApi(): readonly string[] { return this.productConfiguration ? this.productConfiguration.extensionAllowedProposedApi : []; }
get uiExtensions(): readonly string[] | undefined { return this.productConfiguration ? this.productConfiguration.uiExtensions : undefined; }
get enableTelemetry(): boolean { return false; }
get sendASmile(): { reportIssueUrl: string, requestFeatureUrl: string } | undefined { return this.productConfiguration ? this.productConfiguration.sendASmile : undefined; }
get extensionsGallery() { return this.productConfiguration ? this.productConfiguration.extensionsGallery : undefined; }
get settingsSearchBuildId(): number | undefined { return this.productConfiguration ? this.productConfiguration.settingsSearchBuildId : undefined; }
get settingsSearchUrl(): string | undefined { return this.productConfiguration ? this.productConfiguration.settingsSearchUrl : undefined; }
get experimentsUrl(): string | undefined { return this.productConfiguration ? this.productConfiguration.experimentsUrl : undefined; }
get extensionKeywords(): { [extension: string]: readonly string[]; } | undefined { return this.productConfiguration ? this.productConfiguration.extensionKeywords : undefined; }
get extensionAllowedBadgeProviders(): readonly string[] | undefined { return this.productConfiguration ? this.productConfiguration.extensionAllowedBadgeProviders : undefined; }
get aiConfig() { return this.productConfiguration ? this.productConfiguration.aiConfig : undefined; }
}
+7 -40
View File
@@ -11,40 +11,7 @@ export interface IProductService {
_serviceBrand: ServiceIdentifier<any>;
readonly version: string;
readonly vscodeVersion: string; // {{SQL CARBON EDIT}} add vscode version
readonly recommendedExtensionsByScenario: { [area: string]: Array<string> }; // {{SQL CARBON EDIT}} add getter
readonly commit?: string;
readonly date?: string;
readonly nameLong: string;
readonly urlProtocol: string;
readonly extensionAllowedProposedApi: readonly string[];
readonly uiExtensions?: readonly string[];
readonly enableTelemetry: boolean;
readonly extensionsGallery?: {
readonly serviceUrl: string;
readonly itemUrl: string;
readonly controlUrl: string;
readonly recommendationsUrl: string;
};
readonly sendASmile?: {
readonly reportIssueUrl: string;
readonly requestFeatureUrl: string;
};
readonly settingsSearchBuildId?: number;
readonly settingsSearchUrl?: string;
readonly experimentsUrl?: string;
readonly extensionKeywords?: { [extension: string]: readonly string[]; };
readonly extensionAllowedBadgeProviders?: readonly string[];
readonly aiConfig?: {
readonly asimovKey: string;
};
readonly productConfiguration: IProductConfiguration;
}
export interface IProductConfiguration {
@@ -76,10 +43,12 @@ export interface IProductConfiguration {
readonly controlUrl: string;
readonly recommendationsUrl: string;
};
extensionTips: { [id: string]: string; };
recommendedExtensions: string[]; // {{SQL CARBON EDIT}}
recommendedExtensionsByScenario: { [area: string]: Array<string> }; // {{SQL CARBON EDIT}}
extensionImportantTips: { [id: string]: { name: string; pattern: string; isExtensionPack?: boolean }; };
readonly extensionTips: { [id: string]: string; };
readonly recommendedExtensions: string[]; // {{SQL CARBON EDIT}}
readonly recommendedExtensionsByScenario: { [area: string]: Array<string> }; // {{SQL CARBON EDIT}}
readonly vscodeVersion: string; // {{SQL CARBON EDIT}} add vscode version
readonly gettingStartedUrl: string; // {SQL CARBON EDIT}
readonly extensionImportantTips: { [id: string]: { name: string; pattern: string; isExtensionPack?: boolean }; };
readonly exeBasedExtensionTips: { [id: string]: IExeBasedExtensionTip; };
readonly extensionKeywords: { [extension: string]: readonly string[]; };
readonly extensionAllowedBadgeProviders: readonly string[];
@@ -100,8 +69,6 @@ export interface IProductConfiguration {
};
readonly documentationUrl: string;
readonly releaseNotesUrl: string;
readonly gettingStartedUrl: string; // {SQL CARBON EDIT}
readonly vscodeVersion: string; // {SQL CARBON EDIT}
readonly keyboardShortcutsUrlMac: string;
readonly keyboardShortcutsUrlLinux: string;
readonly keyboardShortcutsUrlWin: string;
+7 -30
View File
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IProductService } from 'vs/platform/product/common/product';
import { IProductService, IProductConfiguration } from 'vs/platform/product/common/product';
import product from 'vs/platform/product/node/product';
import pkg from 'vs/platform/product/node/package';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
@@ -12,35 +12,12 @@ export class ProductService implements IProductService {
_serviceBrand!: ServiceIdentifier<IProductService>;
get version(): string { return pkg.version; }
readonly productConfiguration: IProductConfiguration;
get vscodeVersion(): string { return '1.35.0'; } // {{SQL CARBON EDIT}} add vscodeversion
constructor() {
this.productConfiguration = {
...product, ...{ version: pkg.version }, ...{ vscodeVersion: '1.35.0' } // {{SQL CARBON EDIT}} add vscodeversion}
};
}
get recommendedExtensionsByScenario(): { [area: string]: Array<string> } { return product.recommendedExtensionsByScenario; }// {{SQL CARBON EDIT}} add getter
get commit(): string | undefined { return product.commit; }
get nameLong(): string { return product.nameLong; }
get urlProtocol(): string { return product.urlProtocol; }
get extensionAllowedProposedApi(): readonly string[] { return product.extensionAllowedProposedApi; }
get uiExtensions(): readonly string[] | undefined { return product.uiExtensions; }
get enableTelemetry(): boolean { return product.enableTelemetry; }
get sendASmile(): { reportIssueUrl: string, requestFeatureUrl: string } { return product.sendASmile; }
get extensionsGallery() { return product.extensionsGallery; }
get settingsSearchBuildId(): number | undefined { return product.settingsSearchBuildId; }
get settingsSearchUrl(): string | undefined { return product.settingsSearchUrl; }
get experimentsUrl(): string | undefined { return product.experimentsUrl; }
get extensionKeywords(): { [extension: string]: readonly string[]; } | undefined { return product.extensionKeywords; }
get extensionAllowedBadgeProviders(): readonly string[] | undefined { return product.extensionAllowedBadgeProviders; }
}
@@ -144,8 +144,10 @@ export class BrowserStorageService extends Disposable implements IStorageService
// Signal as event so that clients can still store data
this._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN });
// Close DBs
this.globalStorage.close();
this.workspaceStorage.close();
// We explicitly do not close our DBs because writing data onBeforeUnload()
// can result in unexpected results. Namely, it seems that - even though this
// operation is async - sometimes it is being triggered on unload and
// succeeds. Often though, the DBs turn out to be empty because the write
// never had a chance to complete.
}
}
@@ -13,17 +13,14 @@ import { URI } from 'vs/base/common/uri';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
export class WindowsService implements IWindowsService {
_serviceBrand: any;
_serviceBrand!: ServiceIdentifier<any>;
private channel: IChannel;
constructor(@IMainProcessService mainProcessService: IMainProcessService) {
this.channel = mainProcessService.getChannel('windows');
}
get onWindowOpen(): Event<number> { return this.channel.listen('onWindowOpen'); }
get onWindowFocus(): Event<number> { return this.channel.listen('onWindowFocus'); }
get onWindowBlur(): Event<number> { return this.channel.listen('onWindowBlur'); }
@@ -31,6 +28,10 @@ export class WindowsService implements IWindowsService {
get onWindowUnmaximize(): Event<number> { return this.channel.listen('onWindowUnmaximize'); }
get onRecentlyOpenedChange(): Event<void> { return this.channel.listen('onRecentlyOpenedChange'); }
constructor(@IMainProcessService mainProcessService: IMainProcessService) {
this.channel = mainProcessService.getChannel('windows');
}
pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise<void> {
return this.channel.call('pickFileFolderAndOpen', options);
}
@@ -21,7 +21,7 @@ class EditorWebviewZone implements IViewZone {
readonly afterColumn: number;
readonly heightInLines: number;
private _id?: number;
private _id?: string;
// suppressMouseDown?: boolean | undefined;
// heightInPx?: number | undefined;
// minWidthInPx?: number | undefined;
@@ -327,7 +327,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) {
return true;
}
if (this._productService.urlProtocol === link.scheme) {
if (this._productService.productConfiguration.urlProtocol === link.scheme) {
return true;
}
return !!webview.webview.contentOptions.enableCommandUris && link.scheme === 'command';
@@ -6,12 +6,13 @@
import { Event } from 'vs/base/common/event';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, ExtHostWindowShape, IExtHostContext, MainContext, MainThreadWindowShape, IOpenUriOptions } from '../common/extHost.protocol';
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { extractLocalHostUriMetaDataForPortMapping } from 'vs/workbench/contrib/webview/common/portMapping';
import { IOpenerService } from 'vs/platform/opener/common/opener';
@extHostNamedCustomer(MainContext.MainThreadWindow)
export class MainThreadWindow implements MainThreadWindowShape {
@@ -23,7 +24,7 @@ export class MainThreadWindow implements MainThreadWindowShape {
constructor(
extHostContext: IExtHostContext,
@IWindowService private readonly windowService: IWindowService,
@IWindowsService private readonly windowsService: IWindowsService,
@IOpenerService private readonly openerService: IOpenerService,
@ITunnelService private readonly tunnelService: ITunnelService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
) {
@@ -58,7 +59,7 @@ export class MainThreadWindow implements MainThreadWindowShape {
}
}
return this.windowsService.openExternal(encodeURI(uri.toString(true)));
return this.openerService.openExternal(uri);
}
private getOrCreateTunnel(remotePort: number): Promise<RemoteTunnel> | undefined {
@@ -19,6 +19,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { VIEWLET_ID as EXPLORER } from 'vs/workbench/contrib/files/common/files';
import { VIEWLET_ID as SCM } from 'vs/workbench/contrib/scm/common/scm';
import { VIEWLET_ID as DEBUG } from 'vs/workbench/contrib/debug/common/debug';
import { VIEWLET_ID as REMOTE } from 'vs/workbench/contrib/remote/common/remote.contribution';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ShowViewletAction } from 'vs/workbench/browser/viewlet';
@@ -79,6 +80,7 @@ interface IUserFriendlyViewDescriptor {
id: string;
name: string;
when?: string;
group?: string;
}
const viewDescriptor: IJSONSchema = {
@@ -99,6 +101,27 @@ const viewDescriptor: IJSONSchema = {
}
};
const nestableViewDescriptor: IJSONSchema = {
type: 'object',
properties: {
id: {
description: localize('vscode.extension.contributes.view.id', 'Identifier of the view. Use this to register a data provider through `vscode.window.registerTreeDataProviderForView` API. Also to trigger activating your extension by registering `onView:${id}` event to `activationEvents`.'),
type: 'string'
},
name: {
description: localize('vscode.extension.contributes.view.name', 'The human-readable name of the view. Will be shown'),
type: 'string'
},
when: {
description: localize('vscode.extension.contributes.view.when', 'Condition which must be true to show this view'),
type: 'string'
},
group: {
description: localize('vscode.extension.contributes.view.group', 'Nested group in the viewlet'),
type: 'string'
}
}
};
const viewsContribution: IJSONSchema = {
description: localize('vscode.extension.contributes.views', "Contributes views to the editor"),
type: 'object',
@@ -126,6 +149,12 @@ const viewsContribution: IJSONSchema = {
type: 'array',
items: viewDescriptor,
default: []
},
'remote': {
description: localize('views.remote', "Contributes views to Remote container in the Activity bar"),
type: 'array',
items: nestableViewDescriptor,
default: []
}
},
additionalProperties: {
@@ -376,6 +405,12 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
return null;
}
const order = ExtensionIdentifier.equals(extension.description.identifier, container.extensionId)
? index + 1
: container.orderDelegate
? container.orderDelegate.getOrder(item.group)
: undefined;
const viewDescriptor = <ICustomViewDescriptor>{
id: item.id,
name: item.name,
@@ -384,9 +419,10 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
canToggleVisibility: true,
collapsed: this.showCollapsed(container),
treeView: this.instantiationService.createInstance(CustomTreeView, item.id, item.name, container),
order: ExtensionIdentifier.equals(extension.description.identifier, container.extensionId) ? index + 1 : undefined,
order: order,
extensionId: extension.description.identifier,
originalContainerId: entry.key
originalContainerId: entry.key,
group: item.group
};
viewIds.push(viewDescriptor.id);
@@ -440,6 +476,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
case 'explorer': return this.viewContainersRegistry.get(EXPLORER);
case 'debug': return this.viewContainersRegistry.get(DEBUG);
case 'scm': return this.viewContainersRegistry.get(SCM);
case 'remote': return this.viewContainersRegistry.get(REMOTE);
default: return this.viewContainersRegistry.get(`workbench.view.extension.${value}`);
}
}
@@ -5,7 +5,7 @@
import * as nls from 'vs/nls';
import * as path from 'vs/base/common/path';
import { originalFSPath } from 'vs/base/common/resources';
import { originalFSPath, joinPath } from 'vs/base/common/resources';
import { Barrier } from 'vs/base/common/async';
import { dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { TernarySearchTree } from 'vs/base/common/map';
@@ -332,14 +332,14 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
return Promise.all<any>([
this._loadCommonJSModule(extensionDescription.main, activationTimesBuilder),
this._loadCommonJSModule(joinPath(extensionDescription.extensionLocation, extensionDescription.main), activationTimesBuilder),
this._loadExtensionContext(extensionDescription)
]).then(values => {
return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
});
}
protected abstract _loadCommonJSModule<T>(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;
protected abstract _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;
private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise<vscode.ExtensionContext> {
@@ -536,7 +536,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
let testRunner: ITestRunner | INewTestRunner | undefined;
let requireError: Error | undefined;
try {
testRunner = await this._loadCommonJSModule(extensionTestsPath, new ExtensionActivationTimesBuilder(false));
testRunner = await this._loadCommonJSModule(URI.file(extensionTestsPath), new ExtensionActivationTimesBuilder(false));
} catch (error) {
requireError = error;
}
@@ -11,6 +11,9 @@ import { connectProxyResolver } from 'vs/workbench/services/extensions/node/prox
import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { ExtHostDownloadService } from 'vs/workbench/api/node/extHostDownloadService';
import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { createAzdataApiFactory, createSqlopsApiFactory } from 'sql/workbench/api/common/sqlExtHost.api.impl'; // {{SQL CARBON EDIT}} use our extension initalizer
import { AzdataNodeModuleFactory, SqlopsNodeModuleFactory } from 'sql/workbench/api/node/extHostRequireInterceptor'; // {{SQL CARBON EDIT}} use our extension initalizer
@@ -61,12 +64,15 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
};
}
protected _loadCommonJSModule<T>(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
protected _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
if (module.scheme !== Schemas.file) {
throw new Error(`Cannot load URI: '${module}', must be of file-scheme`);
}
let r: T | null = null;
activationTimesBuilder.codeLoadingStart();
this._logService.info(`ExtensionService#loadCommonJSModule ${modulePath}`);
this._logService.info(`ExtensionService#loadCommonJSModule ${module.toString(true)}`);
try {
r = require.__$__nodeRequire<T>(modulePath);
r = require.__$__nodeRequire<T>(module.fsPath);
} catch (e) {
return Promise.reject(e);
} finally {
@@ -6,13 +6,16 @@
import { createApiFactoryAndRegisterActors, IExtensionApiFactory } from 'vs/workbench/api/common/extHost.api.impl';
import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHostExtensionActivator';
import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { endsWith } from 'vs/base/common/strings';
import { endsWith, startsWith } from 'vs/base/common/strings';
import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import * as vscode from 'vscode';
import { TernarySearchTree } from 'vs/base/common/map';
import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { joinPath } from 'vs/base/common/resources';
class ApiInstances {
@@ -52,11 +55,8 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
this._apiInstances = new ApiInstances(apiFactory, extensionPath, this._registry, configProvider);
}
protected _loadCommonJSModule<T>(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
protected _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
// make sure modulePath ends with `.js`
const suffix = '.js';
modulePath = endsWith(modulePath, suffix) ? modulePath : modulePath + suffix;
interface FakeCommonJSSelf {
module?: object;
@@ -69,30 +69,66 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
// FAKE commonjs world that only collects exports
const patchSelf: FakeCommonJSSelf = <any>self;
const module = { exports: {} };
patchSelf.module = module;
patchSelf.exports = module.exports;
patchSelf.window = self; // <- that's improper but might help extensions that aren't authored correctly
// FAKE require function that only works for the vscode-module
patchSelf.require = (module: string) => {
if (module !== 'vscode') {
throw new Error(`Cannot load module '${module}'`);
const moduleStack: URI[] = [];
patchSelf.require = (mod: string) => {
const parent = moduleStack[moduleStack.length - 1];
if (mod === 'vscode') {
return this._apiInstances!.get(parent.fsPath);
}
return this._apiInstances!.get(modulePath);
if (!startsWith(mod, '.')) {
throw new Error(`Cannot load module '${mod}'`);
}
const exports = Object.create(null);
patchSelf.module = { exports };
patchSelf.exports = exports;
const next = joinPath(parent, '..', ensureSuffix(mod, '.js'));
moduleStack.push(next);
importScripts(asDomUri(next).toString(true));
moduleStack.pop();
return exports;
};
try {
activationTimesBuilder.codeLoadingStart();
importScripts(modulePath);
const exports = Object.create(null);
patchSelf.module = { exports };
patchSelf.exports = exports;
module = module.with({ path: ensureSuffix(module.path, '.js') });
moduleStack.push(module);
importScripts(asDomUri(module).toString(true));
moduleStack.pop();
} finally {
activationTimesBuilder.codeLoadingStop();
}
return Promise.resolve(module.exports as T);
return Promise.resolve<T>(exports);
}
async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {
throw new Error('Not supported');
}
}
// todo@joh this is a copy of `dom.ts#asDomUri`
function asDomUri(uri: URI): URI {
if (Schemas.vscodeRemote === uri.scheme) {
// rewrite vscode-remote-uris to uris of the window location
// so that they can be intercepted by the service worker
return URI.parse(window.location.href).with({ path: '/vscode-remote', query: JSON.stringify(uri) });
}
return uri;
}
function ensureSuffix(path: string, suffix: string): string {
return endsWith(path, suffix) ? path : path + suffix;
}
+6 -4
View File
@@ -1177,10 +1177,12 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
}
private createGridDescriptor(): ISerializedGrid {
const width = this.storageService.getNumber(Storage.GRID_WIDTH, StorageScope.GLOBAL, 600);
const height = this.storageService.getNumber(Storage.GRID_HEIGHT, StorageScope.GLOBAL, 400);
const sideBarSize = this.storageService.getNumber(Storage.SIDEBAR_SIZE, StorageScope.GLOBAL, 300);
const panelSize = this.storageService.getNumber(Storage.PANEL_SIZE, StorageScope.GLOBAL, 300);
const workbenchDimensions = getClientArea(this.parent);
const width = this.storageService.getNumber(Storage.GRID_WIDTH, StorageScope.GLOBAL, workbenchDimensions.width);
const height = this.storageService.getNumber(Storage.GRID_HEIGHT, StorageScope.GLOBAL, workbenchDimensions.height);
// At some point, we will not fall back to old keys from legacy layout, but for now, let's migrate the keys
const sideBarSize = this.storageService.getNumber(Storage.SIDEBAR_SIZE, StorageScope.GLOBAL, this.storageService.getNumber('workbench.sidebar.width', StorageScope.GLOBAL, Math.min(workbenchDimensions.width / 4, 300))!);
const panelSize = this.storageService.getNumber(Storage.PANEL_SIZE, StorageScope.GLOBAL, this.storageService.getNumber(this.state.panel.position === Position.BOTTOM ? 'workbench.panel.height' : 'workbench.panel.width', StorageScope.GLOBAL, workbenchDimensions.height / 3));
const titleBarHeight = this.titleBarPartView.minimumHeight;
const statusBarHeight = this.statusBarPartView.minimumHeight;
@@ -74,7 +74,7 @@ export interface IEditorOpeningEvent extends IEditorIdentifier {
* Allows to prevent the opening of an editor by providing a callback
* that will be executed instead. By returning another editor promise
* it is possible to override the opening with another editor. It is ok
* to return a promise that resolves to NULL to prevent the opening
* to return a promise that resolves to `undefined` to prevent the opening
* alltogether.
*/
prevent(callback: () => undefined | Promise<IEditor | undefined>): void;
+13 -1
View File
@@ -382,7 +382,19 @@ export class ContributableViewsModel extends Disposable {
return 0;
}
return (this.getViewOrder(a) - this.getViewOrder(b)) || (a.id < b.id ? -1 : 1);
return (this.getViewOrder(a) - this.getViewOrder(b)) || this.getGroupOrderResult(a, b) || (a.id < b.id ? -1 : 1);
}
private getGroupOrderResult(a: IViewDescriptor, b: IViewDescriptor) {
if (!a.group || !b.group) {
return 0;
}
if (a.group === b.group) {
return 0;
}
return a.group < b.group ? -1 : 1;
}
private getViewOrder(viewDescriptor: IViewDescriptor): number {
+17 -24
View File
@@ -22,7 +22,7 @@ import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { ITunnelService } from 'vs/platform/remote/common/tunnel';
// tslint:disable-next-line: import-patterns
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { addDisposableListener, EventType, windowOpenNoOpener } from 'vs/base/browser/dom';
import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService';
import { pathsToEditors } from 'vs/workbench/common/editor';
@@ -31,14 +31,14 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { toStoreData, restoreRecentlyOpened } from 'vs/platform/history/common/historyStorage';
// tslint:disable-next-line: import-patterns
import { IExperimentService, IExperiment, ExperimentActionType, ExperimentState } from 'vs/workbench/contrib/experiments/common/experimentService';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IProductService } from 'vs/platform/product/common/product';
import Severity from 'vs/base/common/severity';
import { localize } from 'vs/nls';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
// tslint:disable-next-line: import-patterns
import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats';
//#region Extension Tips
@@ -751,13 +751,13 @@ export class SimpleWindowsService implements IWindowsService {
async openAboutDialog(): Promise<void> {
const detail = localize('aboutDetail',
"Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}",
this.productService.version || 'Unknown',
this.productService.commit || 'Unknown',
this.productService.date || 'Unknown',
this.productService.productConfiguration.version || 'Unknown',
this.productService.productConfiguration.commit || 'Unknown',
this.productService.productConfiguration.date || 'Unknown',
navigator.userAgent
);
const result = await this.dialogService.show(Severity.Info, this.productService.nameLong, [localize('copy', "Copy"), localize('ok', "OK")], { detail });
const result = await this.dialogService.show(Severity.Info, this.productService.productConfiguration.nameLong, [localize('copy', "Copy"), localize('ok', "OK")], { detail });
if (result === 0) {
this.clipboardService.writeText(detail);
@@ -855,33 +855,26 @@ registerSingleton(ITunnelService, SimpleTunnelService);
//#endregion
//#region experiments
//#region workspace stats
class WorkspaceStatsService implements IWorkspaceStatsService {
class ExperimentService implements IExperimentService {
_serviceBrand: any;
async getExperimentById(id: string): Promise<IExperiment> {
return {
enabled: false,
id: '',
state: ExperimentState.NoRun
};
getTags(): Promise<Tags> {
return Promise.resolve({});
}
async getExperimentsByType(type: ExperimentActionType): Promise<IExperiment[]> {
return [];
getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined {
return undefined;
}
async getCuratedExtensionsList(curatedExtensionsKey: string): Promise<string[]> {
return [];
getHashedRemotesFromUri(workspaceUri: URI, stripEndingDotGit?: boolean): Promise<string[]> {
return Promise.resolve([]);
}
markAsCompleted(experimentId: string): void { }
onExperimentEnabled: Event<IExperiment> = Event.None;
}
registerSingleton(IExperimentService, ExperimentService);
registerSingleton(IWorkspaceStatsService, WorkspaceStatsService);
//#endregion
+10 -4
View File
@@ -51,7 +51,7 @@ export interface IViewContainersRegistry {
*
* @returns the registered ViewContainer.
*/
registerViewContainer(id: string, hideIfEmpty?: boolean, extensionId?: ExtensionIdentifier): ViewContainer;
registerViewContainer(id: string, hideIfEmpty?: boolean, extensionId?: ExtensionIdentifier, viewOrderDelegate?: ViewOrderDelegate): ViewContainer;
/**
* Deregisters the given view container
@@ -67,8 +67,12 @@ export interface IViewContainersRegistry {
get(id: string): ViewContainer | undefined;
}
interface ViewOrderDelegate {
getOrder(group?: string): number | undefined;
}
export class ViewContainer {
protected constructor(readonly id: string, readonly hideIfEmpty: boolean, readonly extensionId?: ExtensionIdentifier) { }
protected constructor(readonly id: string, readonly hideIfEmpty: boolean, readonly extensionId?: ExtensionIdentifier, readonly orderDelegate?: ViewOrderDelegate) { }
}
class ViewContainersRegistryImpl extends Disposable implements IViewContainersRegistry {
@@ -85,7 +89,7 @@ class ViewContainersRegistryImpl extends Disposable implements IViewContainersRe
return values(this.viewContainers);
}
registerViewContainer(id: string, hideIfEmpty?: boolean, extensionId?: ExtensionIdentifier): ViewContainer {
registerViewContainer(id: string, hideIfEmpty?: boolean, extensionId?: ExtensionIdentifier, viewOrderDelegate?: ViewOrderDelegate): ViewContainer {
const existing = this.viewContainers.get(id);
if (existing) {
return existing;
@@ -93,7 +97,7 @@ class ViewContainersRegistryImpl extends Disposable implements IViewContainersRe
const viewContainer = new class extends ViewContainer {
constructor() {
super(id, !!hideIfEmpty, extensionId);
super(id, !!hideIfEmpty, extensionId, viewOrderDelegate);
}
};
this.viewContainers.set(id, viewContainer);
@@ -126,6 +130,8 @@ export interface IViewDescriptor {
readonly when?: ContextKeyExpr;
readonly group?: string;
readonly order?: number;
readonly weight?: number;
@@ -7,7 +7,7 @@ import 'vs/css!./accessibility';
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
import { renderFormattedText } from 'vs/base/browser/htmlContentRenderer';
import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer';
import { alert } from 'vs/base/browser/ui/aria/aria';
import { Widget } from 'vs/base/browser/ui/widget';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
@@ -5,7 +5,7 @@
import * as dom from 'vs/base/browser/dom';
import * as nls from 'vs/nls';
import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer';
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
@@ -543,9 +543,9 @@ class FunctionBreakpointInputRenderer implements IListRenderer<IFunctionBreakpoi
}
}
export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): Promise<IEditor | null> {
export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): Promise<IEditor | undefined> {
if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) {
return Promise.resolve(null);
return Promise.resolve(undefined);
}
const selection = breakpoint.endLineNumber ? {
@@ -35,6 +35,7 @@ import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/c
import { onUnexpectedError } from 'vs/base/common/errors';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { CancellationToken } from 'vs/base/common/cancellation';
import { withUndefinedAsNull } from 'vs/base/common/types';
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
jsonRegistry.registerSchema(launchSchemaId, launchSchema);
@@ -577,7 +578,7 @@ class Launch extends AbstractLaunch implements ILaunch {
pinned: created,
revealIfVisible: true
},
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor, created })));
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor: withUndefinedAsNull(editor), created })));
}, (error: Error) => {
throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error.message));
});
@@ -613,7 +614,7 @@ class WorkspaceLaunch extends AbstractLaunch implements ILaunch {
return this.editorService.openEditor({
resource: this.contextService.getWorkspace().configuration!,
options: { preserveFocus }
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor, created: false }));
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor: withUndefinedAsNull(editor), created: false }));
}
}
@@ -647,6 +648,6 @@ class UserLaunch extends AbstractLaunch implements ILaunch {
}
openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Promise<{ editor: IEditor | null, created: boolean }> {
return this.preferencesService.openGlobalSettings(false, { preserveFocus }).then(editor => ({ editor, created: false }));
return this.preferencesService.openGlobalSettings(false, { preserveFocus }).then(editor => ({ editor: withUndefinedAsNull(editor), created: false }));
}
}
@@ -175,7 +175,7 @@ export class DebugSession implements IDebugSession {
return this.raw!.initialize({
clientID: 'vscode',
clientName: this.productService.nameLong,
clientName: this.productService.productConfiguration.nameLong,
adapterID: this.configuration.type,
pathFormat: 'path',
linesStartAt1: true,
@@ -13,6 +13,7 @@ import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/
import { Schemas } from 'vs/base/common/network';
import { isUri } from 'vs/workbench/contrib/debug/common/debugUtils';
import { ITextEditor } from 'vs/workbench/common/editor';
import { withUndefinedAsNull } from 'vs/base/common/types';
export const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source");
@@ -104,7 +105,7 @@ export class Source {
revealInCenterIfOutsideViewport: true,
pinned: pinned || (!preserveFocus && !this.inMemory)
}
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(withUndefinedAsNull);
}
static getEncodedDebugData(modelUri: uri): { name: string, path: string, sessionId?: string, sourceReference?: number } {
@@ -4,12 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService';
import { ExperimentService } from 'vs/workbench/contrib/experiments/electron-browser/experimentService';
import { IExperimentService, ExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/electron-browser/experimentalPrompt';
import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/browser/experimentalPrompt';
registerSingleton(IExperimentService, ExperimentService, true);
@@ -4,7 +4,23 @@
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import { Emitter, Event } from 'vs/base/common/event';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { language } from 'vs/base/common/platform';
import { Disposable } from 'vs/base/common/lifecycle';
import { match } from 'vs/base/common/glob';
import { IRequestService, asJson } from 'vs/platform/request/common/request';
import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles';
import { CancellationToken } from 'vs/base/common/cancellation';
import { distinct } from 'vs/base/common/arrays';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { IProductService } from 'vs/platform/product/common/product';
import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats';
export const enum ExperimentState {
Evaluating,
@@ -56,4 +72,388 @@ export interface IExperimentService {
onExperimentEnabled: Event<IExperiment>;
}
export const IExperimentService = createDecorator<IExperimentService>('experimentService');
export const IExperimentService = createDecorator<IExperimentService>('experimentService');
interface IExperimentStorageState {
enabled: boolean;
state: ExperimentState;
editCount?: number;
lastEditedDate?: string;
}
interface IRawExperiment {
id: string;
enabled?: boolean;
condition?: {
insidersOnly?: boolean;
newUser?: boolean;
displayLanguage?: string;
installedExtensions?: {
excludes?: string[];
includes?: string[];
},
fileEdits?: {
filePathPattern?: string;
workspaceIncludes?: string[];
workspaceExcludes?: string[];
minEditCount: number;
},
experimentsPreviouslyRun?: {
excludes?: string[];
includes?: string[];
}
userProbability?: number;
};
action?: IExperimentAction;
}
export class ExperimentService extends Disposable implements IExperimentService {
_serviceBrand: any;
private _experiments: IExperiment[] = [];
private _loadExperimentsPromise: Promise<void>;
private _curatedMapping = Object.create(null);
private readonly _onExperimentEnabled = this._register(new Emitter<IExperiment>());
onExperimentEnabled: Event<IExperiment> = this._onExperimentEnabled.event;
constructor(
@IStorageService private readonly storageService: IStorageService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@ITextFileService private readonly textFileService: ITextFileService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IRequestService private readonly requestService: IRequestService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IProductService private readonly productService: IProductService,
@IWorkspaceStatsService private readonly workspaceStatsService: IWorkspaceStatsService
) {
super();
this._loadExperimentsPromise = Promise.resolve(this.lifecycleService.when(LifecyclePhase.Eventually)).then(() => this.loadExperiments());
}
public getExperimentById(id: string): Promise<IExperiment> {
return this._loadExperimentsPromise.then(() => {
return this._experiments.filter(x => x.id === id)[0];
});
}
public getExperimentsByType(type: ExperimentActionType): Promise<IExperiment[]> {
return this._loadExperimentsPromise.then(() => {
if (type === ExperimentActionType.Custom) {
return this._experiments.filter(x => x.enabled && (!x.action || x.action.type === type));
}
return this._experiments.filter(x => x.enabled && x.action && x.action.type === type);
});
}
public getCuratedExtensionsList(curatedExtensionsKey: string): Promise<string[]> {
return this._loadExperimentsPromise.then(() => {
for (const experiment of this._experiments) {
if (experiment.enabled
&& experiment.state === ExperimentState.Run
&& this._curatedMapping[experiment.id]
&& this._curatedMapping[experiment.id].curatedExtensionsKey === curatedExtensionsKey) {
return this._curatedMapping[experiment.id].curatedExtensionsList;
}
}
return [];
});
}
public markAsCompleted(experimentId: string): void {
const storageKey = 'experiments.' + experimentId;
const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {});
experimentState.state = ExperimentState.Complete;
this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL);
}
protected getExperiments(): Promise<IRawExperiment[]> {
if (!this.productService.productConfiguration.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) {
return Promise.resolve([]);
}
return this.requestService.request({ type: 'GET', url: this.productService.productConfiguration.experimentsUrl }, CancellationToken.None).then(context => {
if (context.res.statusCode !== 200) {
return Promise.resolve(null);
}
return asJson(context).then((result: any) => {
return result && Array.isArray(result['experiments']) ? result['experiments'] : [];
});
}, () => Promise.resolve(null));
}
private loadExperiments(): Promise<any> {
return this.getExperiments().then(rawExperiments => {
// Offline mode
if (!rawExperiments) {
const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []);
if (Array.isArray(allExperimentIdsFromStorage)) {
allExperimentIdsFromStorage.forEach(experimentId => {
const storageKey = 'experiments.' + experimentId;
const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), null);
if (experimentState) {
this._experiments.push({
id: experimentId,
enabled: experimentState.enabled,
state: experimentState.state
});
}
});
}
return Promise.resolve(null);
}
// Clear disbaled/deleted experiments from storage
const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []);
const enabledExperiments = rawExperiments.filter(experiment => !!experiment.enabled).map(experiment => experiment.id.toLowerCase());
if (Array.isArray(allExperimentIdsFromStorage)) {
allExperimentIdsFromStorage.forEach(experiment => {
if (enabledExperiments.indexOf(experiment) === -1) {
this.storageService.remove(`experiments.${experiment}`, StorageScope.GLOBAL);
}
});
}
if (enabledExperiments.length) {
this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.GLOBAL);
} else {
this.storageService.remove('allExperiments', StorageScope.GLOBAL);
}
const promises = rawExperiments.map(experiment => {
const processedExperiment: IExperiment = {
id: experiment.id,
enabled: !!experiment.enabled,
state: !!experiment.enabled ? ExperimentState.Evaluating : ExperimentState.NoRun
};
if (experiment.action) {
processedExperiment.action = {
type: ExperimentActionType[experiment.action.type] || ExperimentActionType.Custom,
properties: experiment.action.properties
};
if (processedExperiment.action.type === ExperimentActionType.Prompt) {
((<IExperimentActionPromptProperties>processedExperiment.action.properties).commands || []).forEach(x => {
if (x.curatedExtensionsKey && Array.isArray(x.curatedExtensionsList)) {
this._curatedMapping[experiment.id] = x;
}
});
}
if (!processedExperiment.action.properties) {
processedExperiment.action.properties = {};
}
}
this._experiments.push(processedExperiment);
if (!processedExperiment.enabled) {
return Promise.resolve(null);
}
const storageKey = 'experiments.' + experiment.id;
const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {});
if (!experimentState.hasOwnProperty('enabled')) {
experimentState.enabled = processedExperiment.enabled;
}
if (!experimentState.hasOwnProperty('state')) {
experimentState.state = processedExperiment.enabled ? ExperimentState.Evaluating : ExperimentState.NoRun;
} else {
processedExperiment.state = experimentState.state;
}
return this.shouldRunExperiment(experiment, processedExperiment).then((state: ExperimentState) => {
experimentState.state = processedExperiment.state = state;
this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL);
if (state === ExperimentState.Run) {
this.fireRunExperiment(processedExperiment);
}
return Promise.resolve(null);
});
});
return Promise.all(promises).then(() => {
type ExperimentsClassification = {
experiments: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
this.telemetryService.publicLog2<{ experiments: IExperiment[] }, ExperimentsClassification>('experiments', { experiments: this._experiments });
});
});
}
private fireRunExperiment(experiment: IExperiment) {
this._onExperimentEnabled.fire(experiment);
const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []);
if (runExperimentIdsFromStorage.indexOf(experiment.id) === -1) {
runExperimentIdsFromStorage.push(experiment.id);
}
// Ensure we dont store duplicates
const distinctExperiments = distinct(runExperimentIdsFromStorage);
if (runExperimentIdsFromStorage.length !== distinctExperiments.length) {
this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.GLOBAL);
}
}
private checkExperimentDependencies(experiment: IRawExperiment): boolean {
const experimentsPreviouslyRun = experiment.condition ? experiment.condition.experimentsPreviouslyRun : undefined;
if (experimentsPreviouslyRun) {
const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []);
let includeCheck = true;
let excludeCheck = true;
const includes = experimentsPreviouslyRun.includes;
if (Array.isArray(includes)) {
includeCheck = runExperimentIdsFromStorage.some(x => includes.indexOf(x) > -1);
}
const excludes = experimentsPreviouslyRun.excludes;
if (includeCheck && Array.isArray(excludes)) {
excludeCheck = !runExperimentIdsFromStorage.some(x => excludes.indexOf(x) > -1);
}
if (!includeCheck || !excludeCheck) {
return false;
}
}
return true;
}
private shouldRunExperiment(experiment: IRawExperiment, processedExperiment: IExperiment): Promise<ExperimentState> {
if (processedExperiment.state !== ExperimentState.Evaluating) {
return Promise.resolve(processedExperiment.state);
}
if (!experiment.enabled) {
return Promise.resolve(ExperimentState.NoRun);
}
const condition = experiment.condition;
if (!condition) {
return Promise.resolve(ExperimentState.Run);
}
if (!this.checkExperimentDependencies(experiment)) {
return Promise.resolve(ExperimentState.NoRun);
}
if (this.environmentService.appQuality === 'stable' && condition.insidersOnly === true) {
return Promise.resolve(ExperimentState.NoRun);
}
const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL);
if ((condition.newUser === true && !isNewUser)
|| (condition.newUser === false && isNewUser)) {
return Promise.resolve(ExperimentState.NoRun);
}
if (typeof condition.displayLanguage === 'string') {
let localeToCheck = condition.displayLanguage.toLowerCase();
let displayLanguage = language!.toLowerCase();
if (localeToCheck !== displayLanguage) {
const a = displayLanguage.indexOf('-');
const b = localeToCheck.indexOf('-');
if (a > -1) {
displayLanguage = displayLanguage.substr(0, a);
}
if (b > -1) {
localeToCheck = localeToCheck.substr(0, b);
}
if (displayLanguage !== localeToCheck) {
return Promise.resolve(ExperimentState.NoRun);
}
}
}
if (!condition.userProbability) {
condition.userProbability = 1;
}
let extensionsCheckPromise = Promise.resolve(true);
const installedExtensions = condition.installedExtensions;
if (installedExtensions) {
extensionsCheckPromise = this.extensionManagementService.getInstalled(ExtensionType.User).then(locals => {
let includesCheck = true;
let excludesCheck = true;
const localExtensions = locals.map(local => `${local.manifest.publisher.toLowerCase()}.${local.manifest.name.toLowerCase()}`);
if (Array.isArray(installedExtensions.includes) && installedExtensions.includes.length) {
const extensionIncludes = installedExtensions.includes.map(e => e.toLowerCase());
includesCheck = localExtensions.some(e => extensionIncludes.indexOf(e) > -1);
}
if (Array.isArray(installedExtensions.excludes) && installedExtensions.excludes.length) {
const extensionExcludes = installedExtensions.excludes.map(e => e.toLowerCase());
excludesCheck = !localExtensions.some(e => extensionExcludes.indexOf(e) > -1);
}
return includesCheck && excludesCheck;
});
}
const storageKey = 'experiments.' + experiment.id;
const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {});
return extensionsCheckPromise.then(success => {
const fileEdits = condition.fileEdits;
if (!success || !fileEdits || typeof fileEdits.minEditCount !== 'number') {
const runExperiment = success && typeof condition.userProbability === 'number' && Math.random() < condition.userProbability;
return runExperiment ? ExperimentState.Run : ExperimentState.NoRun;
}
experimentState.editCount = experimentState.editCount || 0;
if (experimentState.editCount >= fileEdits.minEditCount) {
return ExperimentState.Run;
}
const onSaveHandler = this.textFileService.models.onModelsSaved(e => {
const date = new Date().toDateString();
const latestExperimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {});
if (latestExperimentState.state !== ExperimentState.Evaluating) {
onSaveHandler.dispose();
return;
}
e.forEach(async event => {
if (event.kind !== StateChange.SAVED
|| latestExperimentState.state !== ExperimentState.Evaluating
|| date === latestExperimentState.lastEditedDate
|| (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount)
) {
return;
}
let filePathCheck = true;
let workspaceCheck = true;
if (typeof fileEdits.filePathPattern === 'string') {
filePathCheck = match(fileEdits.filePathPattern, event.resource.fsPath);
}
if (Array.isArray(fileEdits.workspaceIncludes) && fileEdits.workspaceIncludes.length) {
const tags = await this.workspaceStatsService.getTags();
workspaceCheck = !!tags && fileEdits.workspaceIncludes.some(x => !!tags[x]);
}
if (workspaceCheck && Array.isArray(fileEdits.workspaceExcludes) && fileEdits.workspaceExcludes.length) {
const tags = await this.workspaceStatsService.getTags();
workspaceCheck = !!tags && !fileEdits.workspaceExcludes.some(x => !!tags[x]);
}
if (filePathCheck && workspaceCheck) {
latestExperimentState.editCount = (latestExperimentState.editCount || 0) + 1;
latestExperimentState.lastEditedDate = date;
this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL);
}
});
if (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) {
processedExperiment.state = latestExperimentState.state = (typeof condition.userProbability === 'number' && Math.random() < condition.userProbability && this.checkExperimentDependencies(experiment)) ? ExperimentState.Run : ExperimentState.NoRun;
this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL);
if (latestExperimentState.state === ExperimentState.Run && experiment.action && ExperimentActionType[experiment.action.type] === ExperimentActionType.Prompt) {
this.fireRunExperiment(processedExperiment);
}
}
});
this._register(onSaveHandler);
return ExperimentState.Evaluating;
});
}
}
function safeParse(text: string | undefined, defaultObject: any) {
try {
return text ? JSON.parse(text) || defaultObject : defaultObject;
} catch (e) {
return defaultObject;
}
}
@@ -1,407 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { language } from 'vs/base/common/platform';
import { Disposable } from 'vs/base/common/lifecycle';
import { match } from 'vs/base/common/glob';
import { IRequestService, asJson } from 'vs/platform/request/common/request';
import { Emitter, Event } from 'vs/base/common/event';
import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles';
import { CancellationToken } from 'vs/base/common/cancellation';
import { distinct } from 'vs/base/common/arrays';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { ExperimentState, IExperimentAction, IExperimentService, IExperiment, ExperimentActionType, IExperimentActionPromptProperties } from 'vs/workbench/contrib/experiments/common/experimentService';
import { IProductService } from 'vs/platform/product/common/product';
import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/electron-browser/workspaceStatsService';
interface IExperimentStorageState {
enabled: boolean;
state: ExperimentState;
editCount?: number;
lastEditedDate?: string;
}
interface IRawExperiment {
id: string;
enabled?: boolean;
condition?: {
insidersOnly?: boolean;
newUser?: boolean;
displayLanguage?: string;
installedExtensions?: {
excludes?: string[];
includes?: string[];
},
fileEdits?: {
filePathPattern?: string;
workspaceIncludes?: string[];
workspaceExcludes?: string[];
minEditCount: number;
},
experimentsPreviouslyRun?: {
excludes?: string[];
includes?: string[];
}
userProbability?: number;
};
action?: IExperimentAction;
}
export class ExperimentService extends Disposable implements IExperimentService {
_serviceBrand: any;
private _experiments: IExperiment[] = [];
private _loadExperimentsPromise: Promise<void>;
private _curatedMapping = Object.create(null);
private readonly _onExperimentEnabled = this._register(new Emitter<IExperiment>());
onExperimentEnabled: Event<IExperiment> = this._onExperimentEnabled.event;
constructor(
@IStorageService private readonly storageService: IStorageService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@ITextFileService private readonly textFileService: ITextFileService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IRequestService private readonly requestService: IRequestService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IProductService private readonly productService: IProductService,
@IWorkspaceStatsService private readonly workspaceStatsService: IWorkspaceStatsService
) {
super();
this._loadExperimentsPromise = Promise.resolve(this.lifecycleService.when(LifecyclePhase.Eventually)).then(() => this.loadExperiments());
}
public getExperimentById(id: string): Promise<IExperiment> {
return this._loadExperimentsPromise.then(() => {
return this._experiments.filter(x => x.id === id)[0];
});
}
public getExperimentsByType(type: ExperimentActionType): Promise<IExperiment[]> {
return this._loadExperimentsPromise.then(() => {
if (type === ExperimentActionType.Custom) {
return this._experiments.filter(x => x.enabled && (!x.action || x.action.type === type));
}
return this._experiments.filter(x => x.enabled && x.action && x.action.type === type);
});
}
public getCuratedExtensionsList(curatedExtensionsKey: string): Promise<string[]> {
return this._loadExperimentsPromise.then(() => {
for (const experiment of this._experiments) {
if (experiment.enabled
&& experiment.state === ExperimentState.Run
&& this._curatedMapping[experiment.id]
&& this._curatedMapping[experiment.id].curatedExtensionsKey === curatedExtensionsKey) {
return this._curatedMapping[experiment.id].curatedExtensionsList;
}
}
return [];
});
}
public markAsCompleted(experimentId: string): void {
const storageKey = 'experiments.' + experimentId;
const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {});
experimentState.state = ExperimentState.Complete;
this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL);
}
protected getExperiments(): Promise<IRawExperiment[]> {
if (!this.productService.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) {
return Promise.resolve([]);
}
return this.requestService.request({ type: 'GET', url: this.productService.experimentsUrl }, CancellationToken.None).then(context => {
if (context.res.statusCode !== 200) {
return Promise.resolve(null);
}
return asJson(context).then((result: any) => {
return result && Array.isArray(result['experiments']) ? result['experiments'] : [];
});
}, () => Promise.resolve(null));
}
private loadExperiments(): Promise<any> {
return this.getExperiments().then(rawExperiments => {
// Offline mode
if (!rawExperiments) {
const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []);
if (Array.isArray(allExperimentIdsFromStorage)) {
allExperimentIdsFromStorage.forEach(experimentId => {
const storageKey = 'experiments.' + experimentId;
const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), null);
if (experimentState) {
this._experiments.push({
id: experimentId,
enabled: experimentState.enabled,
state: experimentState.state
});
}
});
}
return Promise.resolve(null);
}
// Clear disbaled/deleted experiments from storage
const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []);
const enabledExperiments = rawExperiments.filter(experiment => !!experiment.enabled).map(experiment => experiment.id.toLowerCase());
if (Array.isArray(allExperimentIdsFromStorage)) {
allExperimentIdsFromStorage.forEach(experiment => {
if (enabledExperiments.indexOf(experiment) === -1) {
this.storageService.remove(`experiments.${experiment}`, StorageScope.GLOBAL);
}
});
}
if (enabledExperiments.length) {
this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.GLOBAL);
} else {
this.storageService.remove('allExperiments', StorageScope.GLOBAL);
}
const promises = rawExperiments.map(experiment => {
const processedExperiment: IExperiment = {
id: experiment.id,
enabled: !!experiment.enabled,
state: !!experiment.enabled ? ExperimentState.Evaluating : ExperimentState.NoRun
};
if (experiment.action) {
processedExperiment.action = {
type: ExperimentActionType[experiment.action.type] || ExperimentActionType.Custom,
properties: experiment.action.properties
};
if (processedExperiment.action.type === ExperimentActionType.Prompt) {
((<IExperimentActionPromptProperties>processedExperiment.action.properties).commands || []).forEach(x => {
if (x.curatedExtensionsKey && Array.isArray(x.curatedExtensionsList)) {
this._curatedMapping[experiment.id] = x;
}
});
}
if (!processedExperiment.action.properties) {
processedExperiment.action.properties = {};
}
}
this._experiments.push(processedExperiment);
if (!processedExperiment.enabled) {
return Promise.resolve(null);
}
const storageKey = 'experiments.' + experiment.id;
const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {});
if (!experimentState.hasOwnProperty('enabled')) {
experimentState.enabled = processedExperiment.enabled;
}
if (!experimentState.hasOwnProperty('state')) {
experimentState.state = processedExperiment.enabled ? ExperimentState.Evaluating : ExperimentState.NoRun;
} else {
processedExperiment.state = experimentState.state;
}
return this.shouldRunExperiment(experiment, processedExperiment).then((state: ExperimentState) => {
experimentState.state = processedExperiment.state = state;
this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL);
if (state === ExperimentState.Run) {
this.fireRunExperiment(processedExperiment);
}
return Promise.resolve(null);
});
});
return Promise.all(promises).then(() => {
type ExperimentsClassification = {
experiments: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
this.telemetryService.publicLog2<{ experiments: IExperiment[] }, ExperimentsClassification>('experiments', { experiments: this._experiments });
});
});
}
private fireRunExperiment(experiment: IExperiment) {
this._onExperimentEnabled.fire(experiment);
const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []);
if (runExperimentIdsFromStorage.indexOf(experiment.id) === -1) {
runExperimentIdsFromStorage.push(experiment.id);
}
// Ensure we dont store duplicates
const distinctExperiments = distinct(runExperimentIdsFromStorage);
if (runExperimentIdsFromStorage.length !== distinctExperiments.length) {
this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.GLOBAL);
}
}
private checkExperimentDependencies(experiment: IRawExperiment): boolean {
const experimentsPreviouslyRun = experiment.condition ? experiment.condition.experimentsPreviouslyRun : undefined;
if (experimentsPreviouslyRun) {
const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []);
let includeCheck = true;
let excludeCheck = true;
const includes = experimentsPreviouslyRun.includes;
if (Array.isArray(includes)) {
includeCheck = runExperimentIdsFromStorage.some(x => includes.indexOf(x) > -1);
}
const excludes = experimentsPreviouslyRun.excludes;
if (includeCheck && Array.isArray(excludes)) {
excludeCheck = !runExperimentIdsFromStorage.some(x => excludes.indexOf(x) > -1);
}
if (!includeCheck || !excludeCheck) {
return false;
}
}
return true;
}
private shouldRunExperiment(experiment: IRawExperiment, processedExperiment: IExperiment): Promise<ExperimentState> {
if (processedExperiment.state !== ExperimentState.Evaluating) {
return Promise.resolve(processedExperiment.state);
}
if (!experiment.enabled) {
return Promise.resolve(ExperimentState.NoRun);
}
const condition = experiment.condition;
if (!condition) {
return Promise.resolve(ExperimentState.Run);
}
if (!this.checkExperimentDependencies(experiment)) {
return Promise.resolve(ExperimentState.NoRun);
}
if (this.environmentService.appQuality === 'stable' && condition.insidersOnly === true) {
return Promise.resolve(ExperimentState.NoRun);
}
const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL);
if ((condition.newUser === true && !isNewUser)
|| (condition.newUser === false && isNewUser)) {
return Promise.resolve(ExperimentState.NoRun);
}
if (typeof condition.displayLanguage === 'string') {
let localeToCheck = condition.displayLanguage.toLowerCase();
let displayLanguage = language!.toLowerCase();
if (localeToCheck !== displayLanguage) {
const a = displayLanguage.indexOf('-');
const b = localeToCheck.indexOf('-');
if (a > -1) {
displayLanguage = displayLanguage.substr(0, a);
}
if (b > -1) {
localeToCheck = localeToCheck.substr(0, b);
}
if (displayLanguage !== localeToCheck) {
return Promise.resolve(ExperimentState.NoRun);
}
}
}
if (!condition.userProbability) {
condition.userProbability = 1;
}
let extensionsCheckPromise = Promise.resolve(true);
const installedExtensions = condition.installedExtensions;
if (installedExtensions) {
extensionsCheckPromise = this.extensionManagementService.getInstalled(ExtensionType.User).then(locals => {
let includesCheck = true;
let excludesCheck = true;
const localExtensions = locals.map(local => `${local.manifest.publisher.toLowerCase()}.${local.manifest.name.toLowerCase()}`);
if (Array.isArray(installedExtensions.includes) && installedExtensions.includes.length) {
const extensionIncludes = installedExtensions.includes.map(e => e.toLowerCase());
includesCheck = localExtensions.some(e => extensionIncludes.indexOf(e) > -1);
}
if (Array.isArray(installedExtensions.excludes) && installedExtensions.excludes.length) {
const extensionExcludes = installedExtensions.excludes.map(e => e.toLowerCase());
excludesCheck = !localExtensions.some(e => extensionExcludes.indexOf(e) > -1);
}
return includesCheck && excludesCheck;
});
}
const storageKey = 'experiments.' + experiment.id;
const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {});
return extensionsCheckPromise.then(success => {
const fileEdits = condition.fileEdits;
if (!success || !fileEdits || typeof fileEdits.minEditCount !== 'number') {
const runExperiment = success && typeof condition.userProbability === 'number' && Math.random() < condition.userProbability;
return runExperiment ? ExperimentState.Run : ExperimentState.NoRun;
}
experimentState.editCount = experimentState.editCount || 0;
if (experimentState.editCount >= fileEdits.minEditCount) {
return ExperimentState.Run;
}
const onSaveHandler = this.textFileService.models.onModelsSaved(e => {
const date = new Date().toDateString();
const latestExperimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {});
if (latestExperimentState.state !== ExperimentState.Evaluating) {
onSaveHandler.dispose();
return;
}
e.forEach(async event => {
if (event.kind !== StateChange.SAVED
|| latestExperimentState.state !== ExperimentState.Evaluating
|| date === latestExperimentState.lastEditedDate
|| (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount)
) {
return;
}
let filePathCheck = true;
let workspaceCheck = true;
if (typeof fileEdits.filePathPattern === 'string') {
filePathCheck = match(fileEdits.filePathPattern, event.resource.fsPath);
}
if (Array.isArray(fileEdits.workspaceIncludes) && fileEdits.workspaceIncludes.length) {
const tags = await this.workspaceStatsService.getTags();
workspaceCheck = !!tags && fileEdits.workspaceIncludes.some(x => !!tags[x]);
}
if (workspaceCheck && Array.isArray(fileEdits.workspaceExcludes) && fileEdits.workspaceExcludes.length) {
const tags = await this.workspaceStatsService.getTags();
workspaceCheck = !!tags && !fileEdits.workspaceExcludes.some(x => !!tags[x]);
}
if (filePathCheck && workspaceCheck) {
latestExperimentState.editCount = (latestExperimentState.editCount || 0) + 1;
latestExperimentState.lastEditedDate = date;
this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL);
}
});
if (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) {
processedExperiment.state = latestExperimentState.state = (typeof condition.userProbability === 'number' && Math.random() < condition.userProbability && this.checkExperimentDependencies(experiment)) ? ExperimentState.Run : ExperimentState.NoRun;
this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL);
if (latestExperimentState.state === ExperimentState.Run && experiment.action && ExperimentActionType[experiment.action.type] === ExperimentActionType.Prompt) {
this.fireRunExperiment(processedExperiment);
}
}
});
this._register(onSaveHandler);
return ExperimentState.Evaluating;
});
}
}
function safeParse(text: string | undefined, defaultObject: any) {
try {
return text ? JSON.parse(text) || defaultObject : defaultObject;
} catch (e) {
return defaultObject;
}
}
@@ -4,8 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { ExperimentActionType, ExperimentState, IExperiment } from 'vs/workbench/contrib/experiments/common/experimentService';
import { ExperimentService } from 'vs/workbench/contrib/experiments/electron-browser/experimentService';
import { ExperimentActionType, ExperimentState, IExperiment, ExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { TestLifecycleService } from 'vs/workbench/test/workbenchTestServices';
@@ -12,7 +12,7 @@ import { TestNotificationService } from 'vs/platform/notification/test/common/te
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/electron-browser/experimentalPrompt';
import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/browser/experimentalPrompt';
import { ExperimentActionType, ExperimentState, IExperiment, IExperimentActionPromptProperties, IExperimentService, LocalizedPromptText } from 'vs/workbench/contrib/experiments/common/experimentService';
import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/electron-browser/experimentService.test';
import { TestLifecycleService } from 'vs/workbench/test/workbenchTestServices';
@@ -79,10 +79,10 @@ export function toExtensionDescription(local: ILocalExtension): IExtensionDescri
const promptDownloadManually = (extension: IGalleryExtension | undefined, message: string, error: Error,
instantiationService: IInstantiationService, notificationService: INotificationService, openerService: IOpenerService, productService: IProductService) => {
if (!extension || error.name === INSTALL_ERROR_INCOMPATIBLE || error.name === INSTALL_ERROR_MALICIOUS || !productService.extensionsGallery) {
if (!extension || error.name === INSTALL_ERROR_INCOMPATIBLE || error.name === INSTALL_ERROR_MALICIOUS || !productService.productConfiguration.extensionsGallery) {
return Promise.reject(error);
} else {
const downloadUrl = `${productService.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`;
const downloadUrl = `${productService.productConfiguration.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`;
notificationService.prompt(Severity.Error, message, [{
label: localize('download', "Download Manually"),
run: () => openerService.open(URI.parse(downloadUrl)).then(() => {
@@ -435,7 +435,7 @@ export class ExtensionsListView extends ViewletPanel {
// {{SQL CARBON EDIT}}
if (this.productService) {
let promiseRecommendedExtensionsByScenario: Promise<IPagedModel<IExtension>> | undefined;
Object.keys(this.productService.recommendedExtensionsByScenario).forEach(scenarioType => {
Object.keys(this.productService.productConfiguration.recommendedExtensionsByScenario).forEach(scenarioType => {
let re = new RegExp('@' + scenarioType, 'i');
if (re.test(query.value)) {
promiseRecommendedExtensionsByScenario = this.getRecommendedExtensionsByScenario(token, scenarioType);
@@ -944,7 +944,7 @@ export class ServerExtensionsView extends ExtensionsListView {
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService,
@IProductService productService: IProductService,
@IContextKeyService contextKeyService: IContextKeyService,
@IContextKeyService contextKeyService: IContextKeyService
) {
options.server = server;
super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, editorService, tipsService, telemetryService, configurationService, contextService, experimentService, workbenchThemeService, extensionManagementServerService, productService, contextKeyService);
@@ -960,7 +960,7 @@ export class ServerExtensionsView extends ExtensionsListView {
}
getActions(): IAction[] {
if (this.extensionManagementServerService.localExtensionManagementServer === this.server) {
if (this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManagementServerService.localExtensionManagementServer === this.server) {
const installLocalExtensionsInRemoteAction = this._register(this.instantiationService.createInstance(InstallLocalExtensionsInRemoteAction, false));
installLocalExtensionsInRemoteAction.class = 'octicon octicon-cloud-download';
return [installLocalExtensionsInRemoteAction];
@@ -118,20 +118,19 @@ class Extension implements IExtension {
}
get url(): string | undefined {
if (!this.productService.extensionsGallery || !this.gallery) {
if (!this.productService.productConfiguration.extensionsGallery || !this.gallery) {
return undefined;
}
return this.productService.extensionsGallery.itemUrl && `${this.productService.extensionsGallery.itemUrl}?itemName=${this.publisher}.${this.name}`; // {{SQL CARBON EDIT}} add check for itemurl
return `${this.productService.productConfiguration.extensionsGallery.itemUrl}?itemName=${this.publisher}.${this.name}`;
}
// {{SQL CARBON EDIT}}
get downloadPage(): string {
if (!this.productService.extensionsGallery) {
if (!this.productService.productConfiguration.extensionsGallery) {
return null;
}
// {{SQL CARBON EDIT}}
return this.gallery && this.gallery.assets && this.gallery.assets.downloadPage && this.gallery.assets.downloadPage.uri;
}
@@ -632,7 +631,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
text = text.replace(extensionRegex, (m, ext) => {
// Get curated keywords
const lookup = this.productService.extensionKeywords || {};
const lookup = this.productService.productConfiguration.extensionKeywords || {};
const keywords = lookup[ext] || [];
// Get mode name
@@ -832,9 +831,9 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
// This is the execution path for install/update extension from marketplace.
// Check both the vscode version and azure data studio version
// The check is added here because we want to fail fast instead of downloading the VSIX and then fail.
if (gallery.properties.engine && (!isEngineValid(gallery.properties.engine, this.productService.vscodeVersion)
|| (gallery.properties.azDataEngine && !isEngineValid(gallery.properties.azDataEngine, this.productService.version)))) {
return Promise.reject(new Error(nls.localize('incompatible2', "Unable to install version '{2}' of extension '{0}' as it is not compatible with Azure Data Studio '{1}'.", extension.gallery!.identifier.id, this.productService.version, gallery.version)));
if (gallery.properties.engine && (!isEngineValid(gallery.properties.engine, this.productService.productConfiguration.vscodeVersion)
|| (gallery.properties.azDataEngine && !isEngineValid(gallery.properties.azDataEngine, this.productService.productConfiguration.version)))) {
return Promise.reject(new Error(nls.localize('incompatible2', "Unable to install version '{2}' of extension '{0}' as it is not compatible with Azure Data Studio '{1}'.", extension.gallery!.identifier.id, this.productService.productConfiguration.version, gallery.version)));
}
return this.installWithProgress(async () => {
@@ -1077,7 +1076,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
get allowedBadgeProviders(): string[] {
if (!this._extensionAllowedBadgeProviders) {
this._extensionAllowedBadgeProviders = (this.productService.extensionAllowedBadgeProviders || []).map(s => s.toLowerCase());
this._extensionAllowedBadgeProviders = (this.productService.productConfiguration.extensionAllowedBadgeProviders || []).map(s => s.toLowerCase());
}
return this._extensionAllowedBadgeProviders;
}
@@ -14,7 +14,6 @@ import { IExtensionTipsService, ExtensionRecommendationReason, IExtensionsConfig
import { IModelService } from 'vs/editor/common/services/modelService';
import { ITextModel } from 'vs/editor/common/model';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import product from 'vs/platform/product/node/product';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
// {{SQL CARBON EDIT}}
import { ShowRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction, InstallRecommendedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
@@ -28,13 +27,11 @@ import { IFileService } from 'vs/platform/files/common/files';
import { IExtensionsConfiguration, ConfigurationKey, ShowRecommendationsOnlyOnDemandKey, IExtensionsViewlet, IExtensionsWorkbenchService, EXTENSIONS_CONFIG, ExtensionsPolicyKey, ExtensionsPolicy } from 'vs/workbench/contrib/extensions/common/extensions';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import * as pfs from 'vs/base/node/pfs';
import * as os from 'os';
import { flatten, distinct, shuffle, coalesce } from 'vs/base/common/arrays';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { guessMimeTypes, MIME_UNKNOWN } from 'vs/base/common/mime';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { getHashedRemotesFromUri } from 'vs/workbench/contrib/stats/electron-browser/workspaceStats';
import { IRequestService, asJson } from 'vs/platform/request/common/request';
import { isNumber } from 'vs/base/common/types';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
@@ -47,11 +44,11 @@ import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/wo
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { extname } from 'vs/base/common/resources';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IExeBasedExtensionTip } from 'vs/platform/product/common/product';
import { IExeBasedExtensionTip, IProductService } from 'vs/platform/product/common/product';
import { timeout } from 'vs/base/common/async';
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; // {{SQL CARBON EDIT}}
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; // {{SQL CARBON EDIT}}
import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats';
const milliSecondsInADay = 1000 * 60 * 60 * 24;
const choiceNever = localize('neverShowAgain', "Don't Show Again");
@@ -117,8 +114,9 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService,
@IExperimentService private readonly experimentService: IExperimentService,
@ITextFileService private readonly textFileService: ITextFileService,
@IAdsTelemetryService private readonly adsTelemetryService: IAdsTelemetryService
@IAdsTelemetryService private readonly adsTelemetryService: IAdsTelemetryService, // {{SQL CARBON EDIT}}
@IWorkspaceStatsService private readonly workspaceStatsService: IWorkspaceStatsService,
@IProductService private readonly productService: IProductService
) {
super();
@@ -128,8 +126,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
return;
}
if (product.extensionsGallery && product.extensionsGallery.recommendationsUrl) {
this._extensionsRecommendationsUrl = product.extensionsGallery.recommendationsUrl;
if (this.productService.productConfiguration.extensionsGallery && this.productService.productConfiguration.extensionsGallery.recommendationsUrl) {
this._extensionsRecommendationsUrl = this.productService.productConfiguration.extensionsGallery.recommendationsUrl;
}
this.sessionSeed = +new Date();
@@ -258,7 +256,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
}
getKeymapRecommendations(): IExtensionRecommendation[] {
return (product.keymapExtensionTips || [])
return (this.productService.productConfiguration.keymapExtensionTips || [])
.filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId))
.map(extensionId => (<IExtensionRecommendation>{ extensionId, sources: ['application'] }));
}
@@ -615,10 +613,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
return Object.keys(this._fileBasedRecommendations)
.sort((a, b) => {
if (this._fileBasedRecommendations[a].recommendedTime === this._fileBasedRecommendations[b].recommendedTime) {
if (!product.extensionImportantTips || caseInsensitiveGet(product.extensionImportantTips, a)) {
if (!this.productService.productConfiguration.extensionImportantTips || caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, a)) {
return -1;
}
if (caseInsensitiveGet(product.extensionImportantTips, b)) {
if (caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, b)) {
return 1;
}
}
@@ -629,13 +627,13 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
}
/**
* Parse all file based recommendations from product.extensionTips
* Retire existing recommendations if they are older than a week or are not part of product.extensionTips anymore
* Parse all file based recommendations from this.productService.productConfiguration.extensionTips
* Retire existing recommendations if they are older than a week or are not part of this.productService.productConfiguration.extensionTips anymore
*/
private fetchFileBasedRecommendations() {
const extensionTips = product.extensionTips;
const extensionTips = this.productService.productConfiguration.extensionTips;
// {{SQL CARBON EDIT}}
this._recommendations = product.recommendedExtensions;
this._recommendations = this.productService.productConfiguration.recommendedExtensions;
if (!extensionTips) {
return;
}
@@ -652,7 +650,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
}
});
forEach(product.extensionImportantTips, entry => {
forEach(this.productService.productConfiguration.extensionImportantTips, entry => {
let { key: id, value } = entry;
const { pattern } = value;
let ids = this._availableRecommendations[pattern];
@@ -714,7 +712,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
let { key: pattern, value: ids } = entry;
if (match(pattern, model.uri.toString())) {
for (let id of ids) {
if (caseInsensitiveGet(product.extensionImportantTips, id)) {
if (caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, id)) {
recommendationsToSuggest.push(id);
}
const filedBasedRecommendation = this._fileBasedRecommendations[id.toLowerCase()] || { recommendedTime: now, sources: [] };
@@ -768,7 +766,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
}
const id = recommendationsToSuggest[0];
const entry = caseInsensitiveGet(product.extensionImportantTips, id);
const entry = caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, id);
if (!entry) {
return false;
}
@@ -994,14 +992,14 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
}
/**
* If user has any of the tools listed in product.exeBasedExtensionTips, fetch corresponding recommendations
* If user has any of the tools listed in this.productService.productConfiguration.exeBasedExtensionTips, fetch corresponding recommendations
*/
private fetchExecutableRecommendations(important: boolean): Promise<void> {
const homeDir = os.homedir();
let foundExecutables: Set<string> = new Set<string>();
let findExecutable = (exeName: string, tip: IExeBasedExtensionTip, path: string) => {
return pfs.fileExists(path).then(exists => {
return this.fileService.exists(URI.file(path)).then(exists => {
if (exists && !foundExecutables.has(exeName)) {
foundExecutables.add(exeName);
(tip['recommendations'] || []).forEach(extensionId => {
@@ -1018,7 +1016,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
let promises: Promise<void>[] = [];
// Loop through recommended extensions
forEach(product.exeBasedExtensionTips, entry => {
forEach(this.productService.productConfiguration.exeBasedExtensionTips, entry => {
if (typeof entry.value !== 'object' || !Array.isArray(entry.value['recommendations'])) {
return;
}
@@ -1090,7 +1088,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
const storageKey = 'extensionsAssistant/dynamicWorkspaceRecommendations';
const workspaceUri = this.contextService.getWorkspace().folders[0].uri;
return Promise.all([getHashedRemotesFromUri(workspaceUri, this.fileService, this.textFileService, false), getHashedRemotesFromUri(workspaceUri, this.fileService, this.textFileService, true)]).then(([hashedRemotes1, hashedRemotes2]) => {
return Promise.all([this.workspaceStatsService.getHashedRemotesFromUri(workspaceUri, false), this.workspaceStatsService.getHashedRemotesFromUri(workspaceUri, true)]).then(([hashedRemotes1, hashedRemotes2]) => {
const hashedRemotes = (hashedRemotes1 || []).concat(hashedRemotes2 || []);
if (!hashedRemotes.length) {
return undefined;
@@ -1248,7 +1246,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
return Promise.reject(new Error(localize('scenarioTypeUndefined', 'The scenario type for extension recommendations must be provided.')));
}
return Promise.resolve((product.recommendedExtensionsByScenario[scenarioType] || [])
return Promise.resolve((this.productService.productConfiguration.recommendedExtensionsByScenario[scenarioType] || [])
.filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId))
.map(extensionId => (<IExtensionRecommendation>{ extensionId, sources: ['application'] })));
}
@@ -52,6 +52,7 @@ import { NullLogService } from 'vs/platform/log/common/log';
import { Schemas } from 'vs/base/common/network';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
import { IFileService } from 'vs/platform/files/common/files';
import { IProductService } from 'vs/platform/product/common/product';
const mockExtensionGallery: IGalleryExtension[] = [
aGalleryExtension('MockExtension1', {
@@ -35,8 +35,7 @@ import { URLService } from 'vs/platform/url/common/urlService';
import { URI } from 'vs/base/common/uri';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { SinonStub } from 'sinon';
import { IExperimentService, ExperimentState, ExperimentActionType } from 'vs/workbench/contrib/experiments/common/experimentService';
import { ExperimentService } from 'vs/workbench/contrib/experiments/electron-browser/experimentService';
import { IExperimentService, ExperimentState, ExperimentActionType, ExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl';
import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
@@ -73,8 +73,8 @@ export class FeedbackDropdown extends Dropdown {
this.feedbackDelegate = options.feedbackService;
this.maxFeedbackCharacters = this.feedbackDelegate.getCharacterLimit(this.sentiment);
if (productService.sendASmile) {
this.requestFeatureLink = productService.sendASmile.requestFeatureUrl;
if (productService.productConfiguration.sendASmile) {
this.requestFeatureLink = productService.productConfiguration.sendASmile.requestFeatureUrl;
}
this.integrityService.isPure().then(result => {
@@ -58,7 +58,7 @@ export class FeedbackStatusbarConribution extends Disposable implements IWorkben
) {
super();
if (productService.sendASmile) {
if (productService.productConfiguration.sendASmile) {
this.entry = this._register(statusbarService.addEntry(this.getStatusEntry(), 'status.feedback', localize('status.feedback', "Tweet Feedback"), StatusbarAlignment.RIGHT, -100 /* towards the end of the right hand side */));
CommandsRegistry.registerCommand('_feedback.open', () => this.toggleFeedback());
@@ -7,16 +7,15 @@ import * as nls from 'vs/nls';
import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/binaryEditor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { URI } from 'vs/base/common/uri';
import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/contrib/files/common/files';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IOpenerService } from 'vs/platform/opener/common/opener';
/**
* An implementation of editor for binary files like images.
@@ -28,7 +27,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor {
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IWindowsService private readonly windowsService: IWindowsService,
@IOpenerService private readonly openerService: IOpenerService,
@IEditorService private readonly editorService: IEditorService,
@IStorageService storageService: IStorageService,
@IFileService fileService: IFileService,
@@ -39,7 +38,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor {
BinaryFileEditor.ID,
{
openInternal: (input, options) => this.openInternal(input, options),
openExternal: resource => this.openExternal(resource)
openExternal: resource => this.openerService.openExternal(resource)
},
telemetryService,
themeService,
@@ -58,13 +57,6 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor {
}
}
private async openExternal(resource: URI): Promise<void> {
const didOpen = await this.windowsService.openExternal(resource.toString());
if (!didOpen) {
return this.windowsService.showItemInFolder(resource);
}
}
getTitle(): string | null {
return this.input ? this.input.getName() : nls.localize('binaryFileEditor', "Binary File Viewer");
}
@@ -34,6 +34,7 @@ import { ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet';
import { KeyChord, KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { Registry } from 'vs/platform/registry/common/platform';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { withUndefinedAsNull } from 'vs/base/common/types';
export class ExplorerViewletViewsContribution extends Disposable implements IWorkbenchContribution {
@@ -202,7 +203,7 @@ export class ExplorerViewlet extends ViewContainerViewlet {
openEditorsView.setStructuralRefreshDelay(delay);
}
let openedEditor: IEditor | null = null;
let openedEditor: IEditor | undefined;
try {
openedEditor = await this.editorService.openEditor(editor, options, group);
} catch (error) {
@@ -214,7 +215,7 @@ export class ExplorerViewlet extends ViewContainerViewlet {
}
}
return openedEditor;
return withUndefinedAsNull(openedEditor);
});
const explorerInstantiator = this.instantiationService.createChild(new ServiceCollection([IEditorService, delegatingEditorService]));
@@ -503,7 +503,7 @@ export class GlobalCompareResourcesAction extends Action {
override: this.editorService.openEditor({
leftResource: activeResource,
rightResource: resource
}).then(() => null)
}).then(() => undefined)
};
}
@@ -158,7 +158,11 @@ export class ExplorerService implements IExplorerService {
// Stat needs to be resolved first and then revealed
const options: IResolveFileOptions = { resolveTo: [resource], resolveMetadata: this.sortOrder === 'modified' };
const workspaceFolder = this.contextService.getWorkspaceFolder(resource);
const rootUri = workspaceFolder ? workspaceFolder.uri : this.roots[0].resource;
if (workspaceFolder === null) {
return Promise.resolve(undefined);
}
const rootUri = workspaceFolder.uri;
const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop()!;
try {
@@ -534,8 +534,10 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
const span1 = dom.append(container, dom.$('span'));
span1.textContent = Messages.MARKERS_PANEL_NO_PROBLEMS_FILTERS;
const link = dom.append(container, dom.$('a.messageAction'));
link.textContent = localize('clearFilter', "Clear Filter.");
link.textContent = localize('clearFilter', "Clear Filter");
link.setAttribute('tabIndex', '0');
const span2 = dom.append(container, dom.$('span'));
span2.textContent = '.';
dom.addStandardDisposableListener(link, dom.EventType.CLICK, () => this.filterAction.filterText = '');
dom.addStandardDisposableListener(link, dom.EventType.KEY_DOWN, (e: IKeyboardEvent) => {
if (e.equals(KeyCode.Enter) || e.equals(KeyCode.Space)) {
@@ -158,9 +158,9 @@ export class KeyboardLayoutPickerAction extends Action {
await this.fileService.resolve(file).then(undefined, (error) => {
return this.fileService.createFile(file, VSBuffer.fromString(KeyboardLayoutPickerAction.DEFAULT_CONTENT));
}).then((stat): Promise<IEditor | null> | null => {
}).then((stat): Promise<IEditor | undefined> | undefined => {
if (!stat) {
return null;
return undefined;
}
return this.editorService.openEditor({
resource: stat.resource,
@@ -225,7 +225,7 @@ export class OpenFolderSettingsAction extends Action {
return this.preferencesService.openFolderSettings(workspaceFolder.uri);
}
return null;
return undefined;
});
}
@@ -73,7 +73,7 @@ export class PreferencesSearchService extends Disposable implements IPreferences
};
} else {
return {
urlBase: this.productService.settingsSearchUrl
urlBase: this.productService.productConfiguration.settingsSearchUrl
};
}
}
@@ -364,7 +364,7 @@ class RemoteSearchProvider implements ISearchProvider {
const extensions = await this.installedExtensions;
const filters = this.options.newExtensionsOnly ?
[`diminish eq 'latest'`] :
this.getVersionFilters(extensions, this.productService.settingsSearchBuildId);
this.getVersionFilters(extensions, this.productService.productConfiguration.settingsSearchBuildId);
const filterStr = filters
.slice(filterPage * RemoteSearchProvider.MAX_REQUEST_FILTERS, (filterPage + 1) * RemoteSearchProvider.MAX_REQUEST_FILTERS)
@@ -563,4 +563,4 @@ export class SettingMatches {
endColumn: setting.valueRange.startColumn + match.end + 1
};
}
}
}
@@ -36,7 +36,7 @@ import { ISettingsGroup } from 'vs/workbench/services/preferences/common/prefere
export class SettingsHeaderWidget extends Widget implements IViewZone {
private id: number;
private id: string;
private _domNode: HTMLElement;
protected titleContainer: HTMLElement;
@@ -121,7 +121,7 @@ export class DefaultSettingsHeaderWidget extends SettingsHeaderWidget {
export class SettingsGroupTitleWidget extends Widget implements IViewZone {
private id: number;
private id: string;
private _afterLineNumber: number;
private _domNode: HTMLElement;
@@ -466,12 +466,12 @@ export class SettingsEditor2 extends BaseEditor {
}
}
switchToSettingsFile(): Promise<IEditor | null> {
switchToSettingsFile(): Promise<IEditor | undefined> {
const query = parseQuery(this.searchWidget.getValue());
return this.openSettingsFile(query.query);
}
private openSettingsFile(query?: string): Promise<IEditor | null> {
private openSettingsFile(query?: string): Promise<IEditor | undefined> {
const currentSettingsTarget = this.settingsTargetsWidget.settingsTarget;
const options: ISettingsEditorOptions = { query };
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as DOM from 'vs/base/browser/dom';
import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer';
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
@@ -0,0 +1,11 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M8.354 13.854H7.646L6.793 13H1.5L1 12.5V2.5L1.5 2H7L7.354 2.146L8 2.793L8.646 2.146L9 2H14.5L15 2.5V12.5L14.5 13H9.207L8.354 13.854ZM2 12H7L7.354 12.146L8 12.793L8.646 12.146L9 12H14V3H9.207L8.354 3.854H7.646L6.793 3H2V12Z" fill="#C5C5C5"/>
<path d="M8.51101 3.40405H7.53201V13.5001H8.51101V3.40405Z" fill="#C5C5C5"/>
<path d="M6 5H3V6H6V5Z" fill="#C5C5C5"/>
<path d="M6 7H3V8H6V7Z" fill="#C5C5C5"/>
<path d="M6 9H3V10H6V9Z" fill="#C5C5C5"/>
<path d="M13 5H10V6H13V5Z" fill="#C5C5C5"/>
<path d="M13 7H10V8H13V7Z" fill="#C5C5C5"/>
<path d="M13 9H10V10H13V9Z" fill="#C5C5C5"/>
</svg>

After

Width:  |  Height:  |  Size: 696 B

@@ -0,0 +1,11 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M8.354 13.854H7.646L6.793 13H1.5L1 12.5V2.5L1.5 2H7L7.354 2.146L8 2.793L8.646 2.146L9 2H14.5L15 2.5V12.5L14.5 13H9.207L8.354 13.854ZM2 12H7L7.354 12.146L8 12.793L8.646 12.146L9 12H14V3H9.207L8.354 3.854H7.646L6.793 3H2V12Z" fill="white"/>
<path d="M8.51101 3.40405H7.53201V13.5001H8.51101V3.40405Z" fill="white"/>
<path d="M6 5H3V6H6V5Z" fill="white"/>
<path d="M6 7H3V8H6V7Z" fill="white"/>
<path d="M6 9H3V10H6V9Z" fill="white"/>
<path d="M13 5H10V6H13V5Z" fill="white"/>
<path d="M13 7H10V8H13V7Z" fill="white"/>
<path d="M13 9H10V10H13V9Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 680 B

@@ -0,0 +1,11 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M8.354 13.854H7.646L6.793 13H1.5L1 12.5V2.5L1.5 2H7L7.354 2.146L8 2.793L8.646 2.146L9 2H14.5L15 2.5V12.5L14.5 13H9.207L8.354 13.854ZM2 12H7L7.354 12.146L8 12.793L8.646 12.146L9 12H14V3H9.207L8.354 3.854H7.646L6.793 3H2V12Z" fill="#424242"/>
<path d="M8.51101 3.40405H7.53201V13.5001H8.51101V3.40405Z" fill="#424242"/>
<path d="M6 5H3V6H6V5Z" fill="#424242"/>
<path d="M6 7H3V8H6V7Z" fill="#424242"/>
<path d="M6 9H3V10H6V9Z" fill="#424242"/>
<path d="M13 5H10V6H13V5Z" fill="#424242"/>
<path d="M13 7H10V8H13V7Z" fill="#424242"/>
<path d="M13 9H10V10H13V9Z" fill="#424242"/>
</svg>

After

Width:  |  Height:  |  Size: 696 B

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M15 3.78418C14.8086 4.07129 14.5921 4.33902 14.3506 4.5874C14.109 4.83577 13.847 5.0625 13.5644 5.26757C13.569 5.33138 13.5724 5.39404 13.5747 5.45556C13.577 5.51709 13.5781 5.57975 13.5781 5.64355C13.5781 6.21777 13.5166 6.78515 13.3935 7.3457C13.2705 7.90624 13.0928 8.44856 12.8603 8.97265C12.5231 9.73372 12.1016 10.4139 11.5957 11.0132C11.0898 11.6125 10.5168 12.1195 9.87646 12.5342C9.23616 12.9489 8.53775 13.2656 7.78125 13.4844C7.02474 13.7031 6.23177 13.8125 5.40234 13.8125C4.61848 13.8125 3.854 13.7043 3.10888 13.4878C2.36377 13.2713 1.6608 12.9489 1 12.5205C1.22331 12.5478 1.45117 12.5615 1.68359 12.5615C2.33529 12.5615 2.96533 12.4567 3.57373 12.2471C4.18212 12.0374 4.74153 11.7321 5.25195 11.331C4.94661 11.3265 4.65266 11.2752 4.37011 11.1772C4.08756 11.0793 3.82893 10.9425 3.59423 10.7671C3.35953 10.5916 3.15445 10.3831 2.979 10.1416C2.80354 9.90006 2.66796 9.63346 2.57226 9.34179C2.6634 9.35546 2.75341 9.36686 2.84228 9.37597C2.93115 9.38509 3.02115 9.38964 3.1123 9.38964C3.36751 9.38964 3.61815 9.35547 3.86425 9.28711C3.52701 9.21875 3.21826 9.09798 2.93798 8.9248C2.65771 8.75162 2.41503 8.54085 2.20996 8.29248C2.00488 8.04411 1.84537 7.76383 1.73144 7.45166C1.61751 7.13948 1.56054 6.8125 1.56054 6.4707V6.43652C1.96158 6.66439 2.3968 6.78515 2.8662 6.79883C2.66568 6.66211 2.48681 6.50602 2.32959 6.33056C2.17236 6.15511 2.03906 5.96484 1.92968 5.75976C1.82031 5.55469 1.736 5.33822 1.67675 5.11035C1.61751 4.88249 1.58789 4.64779 1.58789 4.40625C1.58789 4.15104 1.61979 3.90153 1.68359 3.65772C1.74739 3.4139 1.84537 3.18262 1.97753 2.96387C2.34212 3.41504 2.74772 3.8195 3.19433 4.17725C3.64094 4.53499 4.11604 4.84147 4.61962 5.09668C5.1232 5.35189 5.65071 5.55355 6.20214 5.70166C6.75357 5.84977 7.31868 5.9375 7.89745 5.96484C7.87011 5.86003 7.85074 5.75179 7.83934 5.64014C7.82795 5.52848 7.82225 5.41797 7.82225 5.30859C7.82225 4.91211 7.89745 4.53955 8.04784 4.19092C8.19823 3.84228 8.40331 3.53809 8.66307 3.27832C8.92284 3.01855 9.22704 2.81348 9.57567 2.66309C9.92431 2.5127 10.2969 2.4375 10.6933 2.4375C11.0944 2.4375 11.4761 2.51611 11.8384 2.67334C12.2007 2.83057 12.5185 3.05501 12.792 3.34668C13.1155 3.28288 13.4289 3.19287 13.7319 3.07666C14.035 2.96045 14.3278 2.81803 14.6103 2.64941C14.5055 2.9821 14.3449 3.28516 14.1284 3.55859C13.9119 3.83203 13.6533 4.05762 13.3525 4.23535C13.9267 4.16699 14.4759 4.0166 15 3.78418Z" fill="#C8C8C8"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M15 3.78418C14.8086 4.07129 14.5921 4.33902 14.3506 4.5874C14.109 4.83577 13.847 5.0625 13.5644 5.26757C13.569 5.33138 13.5724 5.39404 13.5747 5.45556C13.577 5.51709 13.5781 5.57975 13.5781 5.64355C13.5781 6.21777 13.5166 6.78515 13.3935 7.3457C13.2705 7.90624 13.0928 8.44856 12.8603 8.97265C12.5231 9.73372 12.1016 10.4139 11.5957 11.0132C11.0898 11.6125 10.5168 12.1195 9.87646 12.5342C9.23616 12.9489 8.53775 13.2656 7.78125 13.4844C7.02474 13.7031 6.23177 13.8125 5.40234 13.8125C4.61848 13.8125 3.854 13.7043 3.10888 13.4878C2.36377 13.2713 1.6608 12.9489 1 12.5205C1.22331 12.5478 1.45117 12.5615 1.68359 12.5615C2.33529 12.5615 2.96533 12.4567 3.57373 12.2471C4.18212 12.0374 4.74153 11.7321 5.25195 11.331C4.94661 11.3265 4.65266 11.2752 4.37011 11.1772C4.08756 11.0793 3.82893 10.9425 3.59423 10.7671C3.35953 10.5916 3.15445 10.3831 2.979 10.1416C2.80354 9.90006 2.66796 9.63346 2.57226 9.34179C2.6634 9.35546 2.75341 9.36686 2.84228 9.37597C2.93115 9.38509 3.02115 9.38964 3.1123 9.38964C3.36751 9.38964 3.61815 9.35547 3.86425 9.28711C3.52701 9.21875 3.21826 9.09798 2.93798 8.9248C2.65771 8.75162 2.41503 8.54085 2.20996 8.29248C2.00488 8.04411 1.84537 7.76383 1.73144 7.45166C1.61751 7.13948 1.56054 6.8125 1.56054 6.4707V6.43652C1.96158 6.66439 2.3968 6.78515 2.8662 6.79883C2.66568 6.66211 2.48681 6.50602 2.32959 6.33056C2.17236 6.15511 2.03906 5.96484 1.92968 5.75976C1.82031 5.55469 1.736 5.33822 1.67675 5.11035C1.61751 4.88249 1.58789 4.64779 1.58789 4.40625C1.58789 4.15104 1.61979 3.90153 1.68359 3.65772C1.74739 3.4139 1.84537 3.18262 1.97753 2.96387C2.34212 3.41504 2.74772 3.8195 3.19433 4.17725C3.64094 4.53499 4.11604 4.84147 4.61962 5.09668C5.1232 5.35189 5.65071 5.55355 6.20214 5.70166C6.75357 5.84977 7.31868 5.9375 7.89745 5.96484C7.87011 5.86003 7.85074 5.75179 7.83934 5.64014C7.82795 5.52848 7.82225 5.41797 7.82225 5.30859C7.82225 4.91211 7.89745 4.53955 8.04784 4.19092C8.19823 3.84228 8.40331 3.53809 8.66307 3.27832C8.92284 3.01855 9.22704 2.81348 9.57567 2.66309C9.92431 2.5127 10.2969 2.4375 10.6933 2.4375C11.0944 2.4375 11.4761 2.51611 11.8384 2.67334C12.2007 2.83057 12.5185 3.05501 12.792 3.34668C13.1155 3.28288 13.4289 3.19287 13.7319 3.07666C14.035 2.96045 14.3278 2.81803 14.6103 2.64941C14.5055 2.9821 14.3449 3.28516 14.1284 3.55859C13.9119 3.83203 13.6533 4.05762 13.3525 4.23535C13.9267 4.16699 14.4759 4.0166 15 3.78418Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M15 3.78418C14.8086 4.07129 14.5921 4.33902 14.3506 4.5874C14.109 4.83577 13.847 5.0625 13.5644 5.26757C13.569 5.33138 13.5724 5.39404 13.5747 5.45556C13.577 5.51709 13.5781 5.57975 13.5781 5.64355C13.5781 6.21777 13.5166 6.78515 13.3935 7.3457C13.2705 7.90624 13.0928 8.44856 12.8603 8.97265C12.5231 9.73372 12.1016 10.4139 11.5957 11.0132C11.0898 11.6125 10.5168 12.1195 9.87646 12.5342C9.23616 12.9489 8.53775 13.2656 7.78125 13.4844C7.02474 13.7031 6.23177 13.8125 5.40234 13.8125C4.61848 13.8125 3.854 13.7043 3.10888 13.4878C2.36377 13.2713 1.6608 12.9489 1 12.5205C1.22331 12.5478 1.45117 12.5615 1.68359 12.5615C2.33529 12.5615 2.96533 12.4567 3.57373 12.2471C4.18212 12.0374 4.74153 11.7321 5.25195 11.331C4.94661 11.3265 4.65266 11.2752 4.37011 11.1772C4.08756 11.0793 3.82893 10.9425 3.59423 10.7671C3.35953 10.5916 3.15445 10.3831 2.979 10.1416C2.80354 9.90006 2.66796 9.63346 2.57226 9.34179C2.6634 9.35546 2.75341 9.36686 2.84228 9.37597C2.93115 9.38509 3.02115 9.38964 3.1123 9.38964C3.36751 9.38964 3.61815 9.35547 3.86425 9.28711C3.52701 9.21875 3.21826 9.09798 2.93798 8.9248C2.65771 8.75162 2.41503 8.54085 2.20996 8.29248C2.00488 8.04411 1.84537 7.76383 1.73144 7.45166C1.61751 7.13948 1.56054 6.8125 1.56054 6.4707V6.43652C1.96158 6.66439 2.3968 6.78515 2.8662 6.79883C2.66568 6.66211 2.48681 6.50602 2.32959 6.33056C2.17236 6.15511 2.03906 5.96484 1.92968 5.75976C1.82031 5.55469 1.736 5.33822 1.67675 5.11035C1.61751 4.88249 1.58789 4.64779 1.58789 4.40625C1.58789 4.15104 1.61979 3.90153 1.68359 3.65772C1.74739 3.4139 1.84537 3.18262 1.97753 2.96387C2.34212 3.41504 2.74772 3.8195 3.19433 4.17725C3.64094 4.53499 4.11604 4.84147 4.61962 5.09668C5.1232 5.35189 5.65071 5.55355 6.20214 5.70166C6.75357 5.84977 7.31868 5.9375 7.89745 5.96484C7.87011 5.86003 7.85074 5.75179 7.83934 5.64014C7.82795 5.52848 7.82225 5.41797 7.82225 5.30859C7.82225 4.91211 7.89745 4.53955 8.04784 4.19092C8.19823 3.84228 8.40331 3.53809 8.66307 3.27832C8.92284 3.01855 9.22704 2.81348 9.57567 2.66309C9.92431 2.5127 10.2969 2.4375 10.6933 2.4375C11.0944 2.4375 11.4761 2.51611 11.8384 2.67334C12.2007 2.83057 12.5185 3.05501 12.792 3.34668C13.1155 3.28288 13.4289 3.19287 13.7319 3.07666C14.035 2.96045 14.3278 2.81803 14.6103 2.64941C14.5055 2.9821 14.3449 3.28516 14.1284 3.55859C13.9119 3.83203 13.6533 4.05762 13.3525 4.23535C13.9267 4.16699 14.4759 4.0166 15 3.78418Z" fill="#424242"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.59454 6.25211L8 1L6.40546 6.25211H1L5.37311 9.65247L3.7496 15L8 11.695L12.2504 15L10.6269 9.65247L15 6.25211H9.59454ZM2.34804 6.72168H6.72044L8 2.50706L8.00002 2.50708L6.72046 6.7217H2.34805L2.34804 6.72168ZM5.88539 9.4722L4.57848 13.7769L4.57851 13.7769L5.8854 9.47221L5.88539 9.4722ZM13.6519 6.7217L13.652 6.72168H9.27956L9.27958 6.7217H13.6519ZM4.90113 7.6229H7.26847L8.00002 5.21332L8.73156 7.6229H11.0989L9.18369 9.1121L9.91523 11.5217L8.00002 10.0325L6.0848 11.5217L6.81635 9.1121L4.90113 7.6229Z" fill="#C5C5C5"/>
</svg>

After

Width:  |  Height:  |  Size: 678 B

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.59454 6.25211L8 1L6.40546 6.25211H1L5.37311 9.65247L3.7496 15L8 11.695L12.2504 15L10.6269 9.65247L15 6.25211H9.59454ZM2.34804 6.72168H6.72044L8 2.50706L8.00002 2.50708L6.72046 6.7217H2.34805L2.34804 6.72168ZM5.88539 9.4722L4.57848 13.7769L4.57851 13.7769L5.8854 9.47221L5.88539 9.4722ZM13.6519 6.7217L13.652 6.72168H9.27956L9.27958 6.7217H13.6519ZM4.90113 7.6229H7.26847L8.00002 5.21332L8.73156 7.6229H11.0989L9.18369 9.1121L9.91523 11.5217L8.00002 10.0325L6.0848 11.5217L6.81635 9.1121L4.90113 7.6229Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 676 B

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.59454 6.25211L8 1L6.40546 6.25211H1L5.37311 9.65247L3.7496 15L8 11.695L12.2504 15L10.6269 9.65247L15 6.25211H9.59454ZM2.34804 6.72168H6.72044L8 2.50706L8.00002 2.50708L6.72046 6.7217H2.34805L2.34804 6.72168ZM5.88539 9.4722L4.57848 13.7769L4.57851 13.7769L5.8854 9.47221L5.88539 9.4722ZM13.6519 6.7217L13.652 6.72168H9.27956L9.27958 6.7217H13.6519ZM4.90113 7.6229H7.26847L8.00002 5.21332L8.73156 7.6229H11.0989L9.18369 9.1121L9.91523 11.5217L8.00002 10.0325L6.0848 11.5217L6.81635 9.1121L4.90113 7.6229Z" fill="#424242"/>
</svg>

After

Width:  |  Height:  |  Size: 678 B

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M14.5 2H1.5L1 2.5V11.5L1.5 12H4V14.5L4.854 14.854L7.707 12H14.5L15 11.5V2.5L14.5 2ZM14 11H7.5L7.146 11.146L5 13.293V11.5L4.5 11H2V3H14V11Z" fill="#C5C5C5"/>
</svg>

After

Width:  |  Height:  |  Size: 271 B

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M14.5 2H1.5L1 2.5V11.5L1.5 12H4V14.5L4.854 14.854L7.707 12H14.5L15 11.5V2.5L14.5 2ZM14 11H7.5L7.146 11.146L5 13.293V11.5L4.5 11H2V3H14V11Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 269 B

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M14.5 2H1.5L1 2.5V11.5L1.5 12H4V14.5L4.854 14.854L7.707 12H14.5L15 11.5V2.5L14.5 2ZM14 11H7.5L7.146 11.146L5 13.293V11.5L4.5 11H2V3H14V11Z" fill="#424242"/>
</svg>

After

Width:  |  Height:  |  Size: 271 B

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 7.5C13 10.5376 10.5376 13 7.5 13C4.46243 13 2 10.5376 2 7.5C2 4.46243 4.46243 2 7.5 2C10.5376 2 13 4.46243 13 7.5ZM14 7.5C14 11.0899 11.0899 14 7.5 14C3.91015 14 1 11.0899 1 7.5C1 3.91015 3.91015 1 7.5 1C11.0899 1 14 3.91015 14 7.5ZM7 9V4H8V9H7ZM7 10V11H8V10H7Z" fill="#C5C5C5"/>
</svg>

After

Width:  |  Height:  |  Size: 438 B

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 7.5C13 10.5376 10.5376 13 7.5 13C4.46243 13 2 10.5376 2 7.5C2 4.46243 4.46243 2 7.5 2C10.5376 2 13 4.46243 13 7.5ZM14 7.5C14 11.0899 11.0899 14 7.5 14C3.91015 14 1 11.0899 1 7.5C1 3.91015 3.91015 1 7.5 1C11.0899 1 14 3.91015 14 7.5ZM7 9V4H8V9H7ZM7 10V11H8V10H7Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 436 B

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 7.5C13 10.5376 10.5376 13 7.5 13C4.46243 13 2 10.5376 2 7.5C2 4.46243 4.46243 2 7.5 2C10.5376 2 13 4.46243 13 7.5ZM14 7.5C14 11.0899 11.0899 14 7.5 14C3.91015 14 1 11.0899 1 7.5C1 3.91015 3.91015 1 7.5 1C11.0899 1 14 3.91015 14 7.5ZM7 9V4H8V9H7ZM7 10V11H8V10H7Z" fill="#424242"/>
</svg>

After

Width:  |  Height:  |  Size: 438 B

@@ -0,0 +1,13 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.34375 2.125H21.6562L22.4375 2.90625V11.5048C21.9519 11.1401 21.4272 10.8346 20.875 10.593V3.6875H2.125V17.75H9.9375C9.9375 18.2593 9.9873 18.7698 10.0876 19.2741C10.3167 20.4256 10.8012 21.5058 11.5 22.4375H5.25V20.875H9.9375V19.3125H1.34375L0.5625 18.5312V2.90625L1.34375 2.125Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.1998 17.1526L20.3527 19.3056L21 18.6591L19.4935 17.1526L21 15.6465L20.3527 15L18.1998 17.1526ZM16.5065 18.7528L15 17.2464L15.6473 16.5998L17.7999 18.7528L15.6473 20.9062L15 20.2593L16.5065 18.7528Z" fill="white"/>
<circle cx="17.75" cy="17.75" r="5.625" stroke="white" stroke-width="1.25"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 939 B

@@ -0,0 +1,419 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./remoteViewlet';
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { URI } from 'vs/base/common/uri';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { VIEWLET_ID, VIEW_CONTAINER } from 'vs/workbench/contrib/remote/common/remote.contribution';
import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet';
import { IAddedViewDescriptorRef } from 'vs/workbench/browser/parts/views/views';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IViewDescriptor, IViewsRegistry, Extensions } from 'vs/workbench/common/views';
import { Registry } from 'vs/platform/registry/common/platform';
import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService';
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { ITreeRenderer, ITreeNode, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
import { Event } from 'vs/base/common/event';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet';
interface HelpInformation {
extensionDescription: IExtensionDescription;
getStarted?: string;
documentation?: string;
feedback?: string;
issues?: string;
}
const remoteHelpExtPoint = ExtensionsRegistry.registerExtensionPoint<HelpInformation>({
extensionPoint: 'remoteHelp',
jsonSchema: {
description: nls.localize('RemoteHelpInformationExtPoint', 'Contributes help information for Remote'),
type: 'object',
properties: {
'getStarted': {
description: nls.localize('RemoteHelpInformationExtPoint.getStarted', "The url to your project's Getting Started page"),
type: 'string'
},
'documentation': {
description: nls.localize('RemoteHelpInformationExtPoint.documentation', "The url to your project's documentation page"),
type: 'string'
},
'feedback': {
description: nls.localize('RemoteHelpInformationExtPoint.feedback', "The url to your project's feedback reporter"),
type: 'string'
},
'issues': {
description: nls.localize('RemoteHelpInformationExtPoint.issues', "The url to your project's issues list"),
type: 'string'
}
}
}
});
interface IViewModel {
helpInformations: HelpInformation[];
}
class HelpTreeVirtualDelegate implements IListVirtualDelegate<IHelpItem> {
getHeight(element: IHelpItem): number {
return 22;
}
getTemplateId(element: IHelpItem): string {
return 'HelpItemTemplate';
}
}
interface IHelpItemTemplateData {
parent: HTMLElement;
icon: HTMLElement;
}
class HelpTreeRenderer implements ITreeRenderer<HelpModel | IHelpItem, IHelpItem, IHelpItemTemplateData> {
templateId: string = 'HelpItemTemplate';
renderTemplate(container: HTMLElement): IHelpItemTemplateData {
dom.addClass(container, 'remote-help-tree-node-item');
const icon = dom.append(container, dom.$('.remote-help-tree-node-item-icon'));
const data = <IHelpItemTemplateData>Object.create(null);
data.parent = container;
data.icon = icon;
return data;
}
renderElement(element: ITreeNode<IHelpItem, IHelpItem>, index: number, templateData: IHelpItemTemplateData, height: number | undefined): void {
const container = templateData.parent;
dom.append(container, templateData.icon);
dom.addClass(templateData.icon, element.element.key);
const labelContainer = dom.append(container, dom.$('.help-item-label'));
labelContainer.innerText = element.element.label;
}
disposeTemplate(templateData: IHelpItemTemplateData): void {
}
}
class HelpDataSource implements IAsyncDataSource<any, any> {
hasChildren(element: any) {
return element instanceof HelpModel;
}
getChildren(element: any) {
if (element instanceof HelpModel) {
return element.items;
}
return [];
}
}
interface IHelpItem {
key: string;
label: string;
handleClick(): Promise<void>;
}
class HelpItem implements IHelpItem {
constructor(
public key: string,
public label: string,
public values: { extensionDescription: IExtensionDescription; url: string }[],
private openerService: IOpenerService,
private quickInputService: IQuickInputService
) {
}
async handleClick() {
if (this.values.length > 1) {
let actions = this.values.map(value => {
return {
label: value.extensionDescription.displayName || value.extensionDescription.identifier.value,
description: value.url
};
});
const action = await this.quickInputService.pick(actions, { placeHolder: nls.localize('pickRemoteExtension', "Select url to open") });
if (action) {
await this.openerService.open(URI.parse(action.label));
}
} else {
await this.openerService.open(URI.parse(this.values[0].url));
}
}
}
class IssueReporterItem implements IHelpItem {
constructor(
public key: string,
public label: string,
public extensionDescriptions: IExtensionDescription[],
private quickInputService: IQuickInputService,
private commandService: ICommandService
) {
}
async handleClick() {
if (this.extensionDescriptions.length > 1) {
let actions = this.extensionDescriptions.map(extension => {
return {
label: extension.displayName || extension.identifier.value,
identifier: extension.identifier
};
});
const action = await this.quickInputService.pick(actions, { placeHolder: nls.localize('pickRemoteExtensionToReportIssue', "Select an extension to report issue") });
if (action) {
await this.commandService.executeCommand('workbench.action.openIssueReporter', [action.identifier.value]);
}
} else {
await this.commandService.executeCommand('workbench.action.openIssueReporter', [this.extensionDescriptions[0].identifier.value]);
}
}
}
class HelpModel {
items: IHelpItem[];
constructor(
viewModel: IViewModel,
openerService: IOpenerService,
quickInputService: IQuickInputService,
commandService: ICommandService
) {
let helpItems: IHelpItem[] = [];
const getStarted = viewModel.helpInformations.filter(info => info.getStarted);
if (getStarted.length) {
helpItems.push(new HelpItem(
'getStarted',
nls.localize('remote.help.getStarted', "Get Started"),
getStarted.map((info: HelpInformation) => ({
extensionDescription: info.extensionDescription,
url: info.getStarted!
})),
openerService,
quickInputService
));
}
const documentation = viewModel.helpInformations.filter(info => info.documentation);
if (documentation.length) {
helpItems.push(new HelpItem(
'documentation',
nls.localize('remote.help.documentation', "Read Documentation"),
documentation.map((info: HelpInformation) => ({
extensionDescription: info.extensionDescription,
url: info.documentation!
})),
openerService,
quickInputService
));
}
const feedback = viewModel.helpInformations.filter(info => info.feedback);
if (feedback.length) {
helpItems.push(new HelpItem(
'feedback',
nls.localize('remote.help.feedback', "Provide Feedback"),
feedback.map((info: HelpInformation) => ({
extensionDescription: info.extensionDescription,
url: info.feedback!
})),
openerService,
quickInputService
));
}
const issues = viewModel.helpInformations.filter(info => info.issues);
if (issues.length) {
helpItems.push(new HelpItem(
'issues',
nls.localize('remote.help.issues', "Review Issues"),
issues.map((info: HelpInformation) => ({
extensionDescription: info.extensionDescription,
url: info.issues!
})),
openerService,
quickInputService
));
}
if (helpItems.length) {
helpItems.push(new IssueReporterItem(
'issueReporter',
nls.localize('remote.help.report', "Report Issue"),
viewModel.helpInformations.map(info => info.extensionDescription),
quickInputService,
commandService
));
}
if (helpItems.length) {
this.items = helpItems;
}
}
}
class HelpPanel extends ViewletPanel {
static readonly ID = '~remote.helpPanel';
static readonly TITLE = nls.localize('remote.help', "Help and feedback");
private tree!: WorkbenchAsyncDataTree<any, any, any>;
constructor(
protected viewModel: IViewModel,
options: IViewletPanelOptions,
@IKeybindingService protected keybindingService: IKeybindingService,
@IContextMenuService protected contextMenuService: IContextMenuService,
@IContextKeyService protected contextKeyService: IContextKeyService,
@IConfigurationService protected configurationService: IConfigurationService,
@IInstantiationService protected readonly instantiationService: IInstantiationService,
@IOpenerService protected openerService: IOpenerService,
@IQuickInputService protected quickInputService: IQuickInputService,
@ICommandService protected commandService: ICommandService
) {
super(options, keybindingService, contextMenuService, configurationService, contextKeyService);
}
protected renderBody(container: HTMLElement): void {
dom.addClass(container, 'remote-help');
const treeContainer = document.createElement('div');
dom.addClass(treeContainer, 'remote-help-content');
container.appendChild(treeContainer);
this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree,
treeContainer,
new HelpTreeVirtualDelegate(),
[new HelpTreeRenderer()],
new HelpDataSource(),
{
keyboardSupport: true,
}
);
const model = new HelpModel(this.viewModel, this.openerService, this.quickInputService, this.commandService);
this.tree.setInput(model);
const helpItemNavigator = this._register(new TreeResourceNavigator2(this.tree, { openOnFocus: false, openOnSelection: false }));
this._register(Event.debounce(helpItemNavigator.onDidOpenResource, (last, event) => event, 75, true)(e => {
e.element.handleClick();
}));
}
protected layoutBody(height: number, width: number): void {
this.tree.layout(height, width);
}
}
class HelpPanelDescriptor implements IViewDescriptor {
readonly id = HelpPanel.ID;
readonly name = HelpPanel.TITLE;
readonly ctorDescriptor: { ctor: any, arguments?: any[] };
readonly canToggleVisibility = true;
readonly hideByDefault = false;
readonly workspace = true;
constructor(viewModel: IViewModel) {
this.ctorDescriptor = { ctor: HelpPanel, arguments: [viewModel] };
}
}
export class RemoteViewlet extends ViewContainerViewlet implements IViewModel {
private helpPanelDescriptor = new HelpPanelDescriptor(this);
helpInformations: HelpInformation[] = [];
constructor(
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@ITelemetryService telemetryService: ITelemetryService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IStorageService storageService: IStorageService,
@IConfigurationService configurationService: IConfigurationService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService
) {
super(VIEWLET_ID, `${VIEWLET_ID}.state`, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
remoteHelpExtPoint.setHandler((extensions) => {
let helpInformation: HelpInformation[] = [];
for (let extension of extensions) {
this._handleRemoteInfoExtensionPoint(extension, helpInformation);
}
this.helpInformations = helpInformation;
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
if (this.helpInformations.length) {
viewsRegistry.registerViews([this.helpPanelDescriptor], VIEW_CONTAINER);
} else {
viewsRegistry.deregisterViews([this.helpPanelDescriptor], VIEW_CONTAINER);
}
});
}
private _handleRemoteInfoExtensionPoint(extension: IExtensionPointUser<HelpInformation>, helpInformation: HelpInformation[]) {
if (!extension.value.documentation && !extension.value.feedback && !extension.value.getStarted && !extension.value.issues) {
return;
}
helpInformation.push({
extensionDescription: extension.description,
getStarted: extension.value.getStarted,
documentation: extension.value.documentation,
feedback: extension.value.feedback,
issues: extension.value.issues
});
}
onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPanel[] {
// too late, already added to the view model
return super.onDidAddViews(added);
}
getTitle(): string {
const title = nls.localize('remote.explorer', "Remote Explorer");
return title;
}
}
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor(
RemoteViewlet,
VIEWLET_ID,
nls.localize('remote.explorer', "Remote Explorer"),
'remote',
4
));
@@ -0,0 +1,92 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench .activitybar>.content .monaco-action-bar .action-label.remote {
-webkit-mask: url('remote-activity-bar.svg') no-repeat 50% 50%;
}
.remote-help-content .monaco-list .monaco-list-row .remote-help-tree-node-item {
display: flex;
height: 22px;
line-height: 22px;
flex: 1;
text-overflow: ellipsis;
overflow: hidden;
flex-wrap: nowrap;
}
.remote-help-content .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon {
background-size: 16px;
background-position: left center;
background-repeat: no-repeat;
padding-right: 6px;
width: 16px;
height: 22px;
-webkit-font-smoothing: antialiased;
}
.remote-help-content .monaco-list .monaco-list-row .monaco-tl-twistie {
width: 0px !important;
}
.vs .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.getStarted {
background-image: url('help-getting-started-light.svg')
}
.vs .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.documentation {
background-image: url('help-documentation-light.svg')
}
.vs .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.feedback {
background-image: url('help-feedback-light.svg')
}
.vs .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issues {
background-image: url('help-review-issues-light.svg')
}
.vs .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issueReporter {
background-image: url('help-report-issue-light.svg')
}
.vs-dark .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.getStarted {
background-image: url('help-getting-started-dark.svg')
}
.vs-dark .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.documentation {
background-image: url('help-documentation-dark.svg')
}
.vs-dark .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.feedback {
background-image: url('help-feedback-dark.svg')
}
.vs-dark .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issues {
background-image: url('help-review-issues-dark.svg')
}
.vs-dark .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issueReporter {
background-image: url('help-report-issue-dark.svg')
}
.hc-black .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.getStarted {
background-image: url('help-getting-started-hc.svg')
}
.hc-black .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.documentation {
background-image: url('help-documentation-hc.svg')
}
.hc-black .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.feedback {
background-image: url('help-feedback-hc.svg')
}
.hc-black .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issues {
background-image: url('help-review-issues-hc.svg')
}
.hc-black .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issueReporter {
background-image: url('help-report-issue-hc.svg')
}
@@ -17,6 +17,34 @@ import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/c
import { localize } from 'vs/nls';
import { joinPath } from 'vs/base/common/resources';
import { Disposable } from 'vs/base/common/lifecycle';
import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views';
export const VIEWLET_ID = 'workbench.view.remote';
export const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(
VIEWLET_ID,
true,
undefined,
{
getOrder: (group?: string) => {
if (!group) {
return undefined; // {{SQL CARBON EDIT}} strict-null-checks
}
let matches = /^targets@(\d+)$/.exec(group);
if (matches) {
return -1000;
}
matches = /^details@(\d+)$/.exec(group);
if (matches) {
return -500;
}
return undefined; // {{SQL CARBON EDIT}} strict-null-checks
}
}
);
export class LabelContribution implements IWorkbenchContribution {
constructor(

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