mirror of
https://github.com/Microsoft/vscode
synced 2024-10-13 06:48:17 +00:00
Merge branch 'master' into sandy081/fix-84258
This commit is contained in:
commit
bb556c735f
2
.github/commands.yml
vendored
2
.github/commands.yml
vendored
|
@ -96,7 +96,7 @@
|
|||
{
|
||||
type: 'comment',
|
||||
name: 'confirmationPending',
|
||||
allowUsers: ['cleidigh', 'usernamehw'],
|
||||
allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'],
|
||||
action: 'updateLabels',
|
||||
addLabel: 'confirmation-pending',
|
||||
removeLabel: 'confirmed'
|
||||
|
|
|
@ -202,6 +202,10 @@
|
|||
"name": "vs/workbench/services/actions",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/services/authToken",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/services/bulkEdit",
|
||||
"project": "vscode-workbench"
|
||||
|
|
|
@ -229,8 +229,11 @@ class Preview extends Disposable {
|
|||
// Show blank image
|
||||
return encodeURI('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAEElEQVR42gEFAPr/AP///wAI/AL+Sr4t6gAAAABJRU5ErkJggg==');
|
||||
|
||||
|
||||
default:
|
||||
// Avoid adding cache busting if there is already a query string
|
||||
if (resource.query) {
|
||||
return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString(true));
|
||||
}
|
||||
return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString(true) + `?version=${version}`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -252,6 +252,9 @@
|
|||
],
|
||||
"colors": {
|
||||
|
||||
"editor.background": "#000c18",
|
||||
"editor.foreground": "#6688cc",
|
||||
|
||||
// Base
|
||||
// "foreground": "",
|
||||
"focusBorder": "#596F99",
|
||||
|
@ -295,8 +298,6 @@
|
|||
"scrollbarSlider.hoverBackground": "#3B3F5188",
|
||||
|
||||
// Editor
|
||||
"editor.background": "#000c18",
|
||||
// "editor.foreground": "#6688cc",
|
||||
"editorWidget.background": "#262641",
|
||||
"editorCursor.foreground": "#ddbb88",
|
||||
"editorWhitespace.foreground": "#103050",
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
"v8-inspect-profiler": "^0.0.20",
|
||||
"vscode-minimist": "^1.2.1",
|
||||
"vscode-nsfw": "1.2.8",
|
||||
"vscode-proxy-agent": "^0.5.1",
|
||||
"vscode-proxy-agent": "^0.5.2",
|
||||
"vscode-ripgrep": "^1.5.7",
|
||||
"vscode-sqlite3": "4.0.9",
|
||||
"vscode-textmate": "^4.3.0",
|
||||
|
@ -65,6 +65,7 @@
|
|||
"@types/chokidar": "2.1.3",
|
||||
"@types/cookie": "^0.3.3",
|
||||
"@types/graceful-fs": "4.1.2",
|
||||
"@types/http-proxy-agent": "^2.0.1",
|
||||
"@types/iconv-lite": "0.0.1",
|
||||
"@types/keytar": "^4.4.0",
|
||||
"@types/mocha": "2.2.39",
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"spdlog": "^0.11.1",
|
||||
"vscode-minimist": "^1.2.1",
|
||||
"vscode-nsfw": "1.2.8",
|
||||
"vscode-proxy-agent": "^0.5.1",
|
||||
"vscode-proxy-agent": "^0.5.2",
|
||||
"vscode-ripgrep": "^1.5.7",
|
||||
"vscode-textmate": "^4.3.0",
|
||||
"xterm": "4.3.0-beta17",
|
||||
|
@ -27,7 +27,7 @@
|
|||
"yazl": "^2.4.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"vscode-windows-ca-certs": "0.1.0",
|
||||
"vscode-windows-ca-certs": "0.2.0",
|
||||
"vscode-windows-registry": "1.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -384,10 +384,10 @@ vscode-nsfw@1.2.8:
|
|||
lodash.isundefined "^3.0.1"
|
||||
nan "^2.10.0"
|
||||
|
||||
vscode-proxy-agent@^0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-proxy-agent/-/vscode-proxy-agent-0.5.1.tgz#7fd15e157c02176a0dca9f87840ad0991a62ca57"
|
||||
integrity sha512-Nnkc7gBk9iAbbZURYZm3p/wvDvRVwDvdzEvDqF1Jh40p6przwQU/JTlV1XLrmd4cCwh6TOAWD81Z3cq+K7Xdmw==
|
||||
vscode-proxy-agent@^0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-proxy-agent/-/vscode-proxy-agent-0.5.2.tgz#0c90d24d353957b841d741da7b2701e3f0a044c4"
|
||||
integrity sha512-1cCNPxrWIrmUwS+1XGaXxkh3G1y7z2fpXl1sT74OZvELaryQWYb3NMxMLJJ4Q/CpPLEyuhp/bAN7nzHxxFcQ5Q==
|
||||
dependencies:
|
||||
debug "^3.1.0"
|
||||
http-proxy-agent "^2.1.0"
|
||||
|
@ -406,10 +406,10 @@ vscode-textmate@^4.3.0:
|
|||
dependencies:
|
||||
oniguruma "^7.2.0"
|
||||
|
||||
vscode-windows-ca-certs@0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-windows-ca-certs/-/vscode-windows-ca-certs-0.1.0.tgz#d58eeb40b536130918cfde2b01e6dc7e5c1bd757"
|
||||
integrity sha512-ZfZbfJIE09Q0dwGqmqTj7kuAq4g6lul9WPJvo0DkKjln8/FL+dY3wUKIKbYwWQp4x56SBTLBq3tJkD72xQ9Gqw==
|
||||
vscode-windows-ca-certs@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-windows-ca-certs/-/vscode-windows-ca-certs-0.2.0.tgz#086f0f4de57e2760a35ac6920831bff246237115"
|
||||
integrity sha512-YBrJRT0zos+Yb1Qdn73GD8QZr7pa2IE96b5Y1hmmp6XeR8aYB7Iiq5gDAF/+/AxL+caSR9KPZQ6jiYWh5biD7w==
|
||||
dependencies:
|
||||
node-addon-api "1.6.2"
|
||||
|
||||
|
|
7
src/bootstrap.js
vendored
7
src/bootstrap.js
vendored
|
@ -263,7 +263,12 @@ exports.configurePortable = function () {
|
|||
}
|
||||
|
||||
if (isTempPortable) {
|
||||
process.env[process.platform === 'win32' ? 'TEMP' : 'TMPDIR'] = portableTempPath;
|
||||
if (process.platform === 'win32') {
|
||||
process.env['TMP'] = portableTempPath;
|
||||
process.env['TEMP'] = portableTempPath;
|
||||
} else {
|
||||
process.env['TMPDIR'] = portableTempPath;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -147,13 +147,9 @@ function configureCommandlineSwitchesSync(cliArgs) {
|
|||
if (argvValue === true || argvValue === 'true') {
|
||||
if (argvKey === 'disable-hardware-acceleration') {
|
||||
app.disableHardwareAcceleration(); // needs to be called explicitly
|
||||
} else if (argvKey === 'disable-color-correct-rendering') {
|
||||
app.commandLine.appendSwitch('disable-color-correct-rendering'); // needs to be called exactly like this (https://github.com/microsoft/vscode/issues/84154)
|
||||
} else {
|
||||
app.commandLine.appendArgument(argvKey);
|
||||
app.commandLine.appendSwitch(argvKey);
|
||||
}
|
||||
} else {
|
||||
app.commandLine.appendSwitch(argvKey, argvValue);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
20
src/typings/http-proxy-agent.d.ts
vendored
20
src/typings/http-proxy-agent.d.ts
vendored
|
@ -1,20 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'http-proxy-agent' {
|
||||
|
||||
interface IHttpProxyAgentOptions {
|
||||
host: string;
|
||||
port: number;
|
||||
auth?: string;
|
||||
}
|
||||
|
||||
class HttpProxyAgent {
|
||||
constructor(proxy: string);
|
||||
constructor(opts: IHttpProxyAgentOptions);
|
||||
}
|
||||
|
||||
export = HttpProxyAgent;
|
||||
}
|
6
src/typings/vscode-proxy-agent.d.ts
vendored
6
src/typings/vscode-proxy-agent.d.ts
vendored
|
@ -1,6 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'vscode-proxy-agent';
|
6
src/typings/vscode-windows-ca-certs.d.ts
vendored
6
src/typings/vscode-windows-ca-certs.d.ts
vendored
|
@ -1,6 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'vscode-windows-ca-certs';
|
|
@ -6,6 +6,12 @@
|
|||
import * as browser from 'vs/base/browser/browser';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
|
||||
export const enum KeyboardSupport {
|
||||
Always,
|
||||
FullScreen,
|
||||
None
|
||||
}
|
||||
|
||||
/**
|
||||
* Browser feature we can support in current platform, browser and environment.
|
||||
*/
|
||||
|
@ -37,9 +43,17 @@ export const BrowserFeatures = {
|
|||
return true;
|
||||
})()
|
||||
},
|
||||
/*
|
||||
* Full Keyboard Support in Full Screen Mode or Standablone
|
||||
*/
|
||||
fullKeyboard: !!(<any>navigator).keyboard || browser.isSafari,
|
||||
keyboard: (() => {
|
||||
if (platform.isNative || browser.isStandalone) {
|
||||
return KeyboardSupport.Always;
|
||||
}
|
||||
|
||||
if ((<any>navigator).keyboard || browser.isSafari) {
|
||||
return KeyboardSupport.FullScreen;
|
||||
}
|
||||
|
||||
return KeyboardSupport.None;
|
||||
})(),
|
||||
|
||||
touch: 'ontouchstart' in window || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0
|
||||
};
|
||||
|
|
|
@ -59,7 +59,7 @@ import { IElectronService } from 'vs/platform/electron/node/electron';
|
|||
import { LoggerService } from 'vs/platform/log/node/loggerService';
|
||||
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
|
||||
import { IAuthTokenService } from 'vs/platform/auth/common/auth';
|
||||
import { AuthTokenService } from 'vs/platform/auth/common/authTokenService';
|
||||
import { AuthTokenService } from 'vs/platform/auth/electron-browser/authTokenService';
|
||||
import { AuthTokenChannel } from 'vs/platform/auth/common/authTokenIpc';
|
||||
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
|
||||
import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService';
|
||||
|
|
|
@ -28,7 +28,9 @@ suite('Windows Native Helpers', () => {
|
|||
});
|
||||
|
||||
test('vscode-windows-ca-certs', async () => {
|
||||
const windowsCerts = await import('vscode-windows-ca-certs');
|
||||
const windowsCerts = await new Promise<any>((resolve, reject) => {
|
||||
require(['vscode-windows-ca-certs'], resolve, reject);
|
||||
});
|
||||
assert.ok(windowsCerts, 'Unable to load vscode-windows-ca-certs dependency.');
|
||||
});
|
||||
|
||||
|
|
|
@ -406,6 +406,10 @@ export class TextAreaHandler extends ViewPart {
|
|||
this._textAreaInput.focusTextArea();
|
||||
}
|
||||
|
||||
public refreshFocusState() {
|
||||
this._textAreaInput.refreshFocusState();
|
||||
}
|
||||
|
||||
// --- end view API
|
||||
|
||||
private _primaryCursorVisibleRange: HorizontalPosition | null = null;
|
||||
|
|
|
@ -163,7 +163,7 @@ export class TextAreaInput extends Disposable {
|
|||
private _isDoingComposition: boolean;
|
||||
private _nextCommand: ReadFromTextArea;
|
||||
|
||||
constructor(host: ITextAreaInputHost, textArea: FastDomNode<HTMLTextAreaElement>) {
|
||||
constructor(host: ITextAreaInputHost, private textArea: FastDomNode<HTMLTextAreaElement>) {
|
||||
super();
|
||||
this._host = host;
|
||||
this._textArea = this._register(new TextAreaWrapper(textArea));
|
||||
|
@ -483,6 +483,14 @@ export class TextAreaInput extends Disposable {
|
|||
return this._hasFocus;
|
||||
}
|
||||
|
||||
public refreshFocusState(): void {
|
||||
if (document.body.contains(this.textArea.domNode) && document.activeElement === this.textArea.domNode) {
|
||||
this._setHasFocus(true);
|
||||
} else {
|
||||
this._setHasFocus(false);
|
||||
}
|
||||
}
|
||||
|
||||
private _setHasFocus(newHasFocus: boolean): void {
|
||||
if (this._hasFocus === newHasFocus) {
|
||||
// no change
|
||||
|
|
|
@ -13,7 +13,7 @@ import { equalsIgnoreCase } from 'vs/base/common/strings';
|
|||
import { URI } from 'vs/base/common/uri';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions } from 'vs/platform/opener/common/opener';
|
||||
import { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions, ResolveExternalUriOptions, IResolvedExternalUri, IExternalOpener } from 'vs/platform/opener/common/opener';
|
||||
import { EditorOpenContext } from 'vs/platform/editor/common/editor';
|
||||
|
||||
export class OpenerService extends Disposable implements IOpenerService {
|
||||
|
@ -23,12 +23,22 @@ export class OpenerService extends Disposable implements IOpenerService {
|
|||
private readonly _openers = new LinkedList<IOpener>();
|
||||
private readonly _validators = new LinkedList<IValidator>();
|
||||
private readonly _resolvers = new LinkedList<IExternalUriResolver>();
|
||||
private _externalOpener: IExternalOpener;
|
||||
|
||||
constructor(
|
||||
@ICodeEditorService private readonly _editorService: ICodeEditorService,
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
) {
|
||||
super();
|
||||
|
||||
// Default external opener is going through window.open()
|
||||
this._externalOpener = {
|
||||
openExternal: href => {
|
||||
dom.windowOpenNoOpener(href);
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
registerOpener(opener: IOpener): IDisposable {
|
||||
|
@ -49,6 +59,10 @@ export class OpenerService extends Disposable implements IOpenerService {
|
|||
return { dispose: remove };
|
||||
}
|
||||
|
||||
setExternalOpener(externalOpener: IExternalOpener): void {
|
||||
this._externalOpener = externalOpener;
|
||||
}
|
||||
|
||||
async open(resource: URI, options?: OpenOptions): Promise<boolean> {
|
||||
|
||||
// no scheme ?!?
|
||||
|
@ -75,7 +89,7 @@ export class OpenerService extends Disposable implements IOpenerService {
|
|||
return this._doOpen(resource, options);
|
||||
}
|
||||
|
||||
async resolveExternalUri(resource: URI, options?: { readonly allowTunneling?: boolean }): Promise<{ resolved: URI, dispose(): void }> {
|
||||
async resolveExternalUri(resource: URI, options?: ResolveExternalUriOptions): Promise<IResolvedExternalUri> {
|
||||
for (const resolver of this._resolvers.toArray()) {
|
||||
const result = await resolver.resolveExternalUri(resource, options);
|
||||
if (result) {
|
||||
|
@ -89,13 +103,8 @@ export class OpenerService extends Disposable implements IOpenerService {
|
|||
private async _doOpen(resource: URI, options: OpenOptions | undefined): Promise<boolean> {
|
||||
const { scheme, path, query, fragment } = resource;
|
||||
|
||||
if (equalsIgnoreCase(scheme, Schemas.mailto) || options?.openExternal) {
|
||||
// open default mail application
|
||||
return this._doOpenExternal(resource, options);
|
||||
}
|
||||
|
||||
if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https)) {
|
||||
// open link in default browser
|
||||
if (options?.openExternal || equalsIgnoreCase(scheme, Schemas.mailto) || equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https)) {
|
||||
// open externally
|
||||
return this._doOpenExternal(resource, options);
|
||||
}
|
||||
|
||||
|
@ -149,9 +158,10 @@ export class OpenerService extends Disposable implements IOpenerService {
|
|||
|
||||
private async _doOpenExternal(resource: URI, options: OpenOptions | undefined): Promise<boolean> {
|
||||
const { resolved } = await this.resolveExternalUri(resource, options);
|
||||
dom.windowOpenNoOpener(encodeURI(resolved.toString(true)));
|
||||
|
||||
return true;
|
||||
// TODO@Jo neither encodeURI nor toString(true) should be needed
|
||||
// once we go with URL and not URI
|
||||
return this._externalOpener.openExternal(encodeURI(resolved.toString(true)));
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
|
|
@ -528,6 +528,10 @@ export class View extends ViewEventHandler {
|
|||
return this._textAreaHandler.isFocused();
|
||||
}
|
||||
|
||||
public refreshFocusState() {
|
||||
this._textAreaHandler.refreshFocusState();
|
||||
}
|
||||
|
||||
public addContentWidget(widgetData: IContentWidgetData): void {
|
||||
this.contentWidgets.addWidget(widgetData.widget);
|
||||
this.layoutContentWidget(widgetData);
|
||||
|
|
|
@ -869,9 +869,11 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
|||
}
|
||||
|
||||
public onVisible(): void {
|
||||
this._modelData?.view.refreshFocusState();
|
||||
}
|
||||
|
||||
public onHide(): void {
|
||||
this._modelData?.view.refreshFocusState();
|
||||
}
|
||||
|
||||
public getContribution<T extends editorCommon.IEditorContribution>(id: string): T {
|
||||
|
|
|
@ -107,8 +107,6 @@ abstract class SymbolNavigationAction extends EditorAction {
|
|||
|
||||
protected abstract _getNoResultFoundMessage(info: IWordAtPosition | null): string;
|
||||
|
||||
protected abstract _getMetaTitle(model: ReferencesModel): string;
|
||||
|
||||
protected abstract _getAlternativeCommand(): string;
|
||||
|
||||
protected abstract _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues;
|
||||
|
@ -171,7 +169,7 @@ abstract class SymbolNavigationAction extends EditorAction {
|
|||
export class DefinitionAction extends SymbolNavigationAction {
|
||||
|
||||
protected async _getLocationModel(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise<ReferencesModel> {
|
||||
return new ReferencesModel(await getDefinitionsAtPosition(model, position, token));
|
||||
return new ReferencesModel(await getDefinitionsAtPosition(model, position, token), nls.localize('def.title', 'Definitions'));
|
||||
}
|
||||
|
||||
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
|
||||
|
@ -180,10 +178,6 @@ export class DefinitionAction extends SymbolNavigationAction {
|
|||
: nls.localize('generic.noResults', "No definition found");
|
||||
}
|
||||
|
||||
protected _getMetaTitle(model: ReferencesModel): string {
|
||||
return model.references.length > 1 ? nls.localize('meta.title', " – {0} definitions", model.references.length) : '';
|
||||
}
|
||||
|
||||
protected _getAlternativeCommand(): string {
|
||||
return 'editor.action.goToReferences';
|
||||
}
|
||||
|
@ -295,7 +289,7 @@ registerEditorAction(class PeekDefinitionAction extends DefinitionAction {
|
|||
class DeclarationAction extends SymbolNavigationAction {
|
||||
|
||||
protected async _getLocationModel(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise<ReferencesModel> {
|
||||
return new ReferencesModel(await getDeclarationsAtPosition(model, position, token));
|
||||
return new ReferencesModel(await getDeclarationsAtPosition(model, position, token), nls.localize('decl.title', 'Declarations'));
|
||||
}
|
||||
|
||||
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
|
||||
|
@ -304,10 +298,6 @@ class DeclarationAction extends SymbolNavigationAction {
|
|||
: nls.localize('decl.generic.noResults', "No declaration found");
|
||||
}
|
||||
|
||||
protected _getMetaTitle(model: ReferencesModel): string {
|
||||
return model.references.length > 1 ? nls.localize('decl.meta.title', " – {0} declarations", model.references.length) : '';
|
||||
}
|
||||
|
||||
protected _getAlternativeCommand(): string {
|
||||
return 'editor.action.goToReferences';
|
||||
}
|
||||
|
@ -352,10 +342,6 @@ registerEditorAction(class GoToDeclarationAction extends DeclarationAction {
|
|||
? nls.localize('decl.noResultWord', "No declaration found for '{0}'", info.word)
|
||||
: nls.localize('decl.generic.noResults', "No declaration found");
|
||||
}
|
||||
|
||||
protected _getMetaTitle(model: ReferencesModel): string {
|
||||
return model.references.length > 1 ? nls.localize('decl.meta.title', " – {0} declarations", model.references.length) : '';
|
||||
}
|
||||
});
|
||||
|
||||
registerEditorAction(class PeekDeclarationAction extends DeclarationAction {
|
||||
|
@ -384,7 +370,7 @@ registerEditorAction(class PeekDeclarationAction extends DeclarationAction {
|
|||
class TypeDefinitionAction extends SymbolNavigationAction {
|
||||
|
||||
protected async _getLocationModel(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise<ReferencesModel> {
|
||||
return new ReferencesModel(await getTypeDefinitionsAtPosition(model, position, token));
|
||||
return new ReferencesModel(await getTypeDefinitionsAtPosition(model, position, token), nls.localize('typedef.title', 'Type Definitions'));
|
||||
}
|
||||
|
||||
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
|
||||
|
@ -393,10 +379,6 @@ class TypeDefinitionAction extends SymbolNavigationAction {
|
|||
: nls.localize('goToTypeDefinition.generic.noResults', "No type definition found");
|
||||
}
|
||||
|
||||
protected _getMetaTitle(model: ReferencesModel): string {
|
||||
return model.references.length > 1 ? nls.localize('meta.typeDefinitions.title', " – {0} type definitions", model.references.length) : '';
|
||||
}
|
||||
|
||||
protected _getAlternativeCommand(): string {
|
||||
return 'editor.action.goToReferences';
|
||||
}
|
||||
|
@ -470,7 +452,7 @@ registerEditorAction(class PeekTypeDefinitionAction extends TypeDefinitionAction
|
|||
class ImplementationAction extends SymbolNavigationAction {
|
||||
|
||||
protected async _getLocationModel(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise<ReferencesModel> {
|
||||
return new ReferencesModel(await getImplementationsAtPosition(model, position, token));
|
||||
return new ReferencesModel(await getImplementationsAtPosition(model, position, token), nls.localize('impl.title', 'Implementations'));
|
||||
}
|
||||
|
||||
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
|
||||
|
@ -479,10 +461,6 @@ class ImplementationAction extends SymbolNavigationAction {
|
|||
: nls.localize('goToImplementation.generic.noResults', "No implementation found");
|
||||
}
|
||||
|
||||
protected _getMetaTitle(model: ReferencesModel): string {
|
||||
return model.references.length > 1 ? nls.localize('meta.implementations.title', " – {0} implementations", model.references.length) : '';
|
||||
}
|
||||
|
||||
protected _getAlternativeCommand(): string {
|
||||
return '';
|
||||
}
|
||||
|
@ -561,7 +539,7 @@ registerEditorAction(class PeekImplementationAction extends ImplementationAction
|
|||
class ReferencesAction extends SymbolNavigationAction {
|
||||
|
||||
protected async _getLocationModel(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise<ReferencesModel> {
|
||||
return new ReferencesModel(await getReferencesAtPosition(model, position, token));
|
||||
return new ReferencesModel(await getReferencesAtPosition(model, position, token), nls.localize('ref.title', 'References'));
|
||||
}
|
||||
|
||||
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
|
||||
|
@ -570,12 +548,6 @@ class ReferencesAction extends SymbolNavigationAction {
|
|||
: nls.localize('references.noGeneric', "No references found");
|
||||
}
|
||||
|
||||
protected _getMetaTitle(model: ReferencesModel): string {
|
||||
return model.references.length > 1
|
||||
? nls.localize('meta.titleReference', " – {0} references", model.references.length)
|
||||
: '';
|
||||
}
|
||||
|
||||
protected _getAlternativeCommand(): string {
|
||||
return '';
|
||||
}
|
||||
|
@ -667,7 +639,7 @@ class GenericGoToLocationAction extends SymbolNavigationAction {
|
|||
}
|
||||
|
||||
protected async _getLocationModel(_model: ITextModel, _position: corePosition.Position, _token: CancellationToken): Promise<ReferencesModel | undefined> {
|
||||
return new ReferencesModel(this._references);
|
||||
return new ReferencesModel(this._references, nls.localize('generic.title', 'Locations'));
|
||||
}
|
||||
|
||||
protected _getNoResultFoundMessage(info: IWordAtPosition | null): string {
|
||||
|
@ -678,7 +650,6 @@ class GenericGoToLocationAction extends SymbolNavigationAction {
|
|||
return this._gotoMultipleBehaviour ?? editor.getOption(EditorOption.gotoLocation).multipleReferences;
|
||||
}
|
||||
|
||||
protected _getMetaTitle() { return ''; }
|
||||
protected _getAlternativeCommand() { return ''; }
|
||||
}
|
||||
|
||||
|
@ -736,7 +707,7 @@ CommandsRegistry.registerCommand({
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const references = createCancelablePromise(token => getReferencesAtPosition(control.getModel(), corePosition.Position.lift(position), token).then(references => new ReferencesModel(references)));
|
||||
const references = createCancelablePromise(token => getReferencesAtPosition(control.getModel(), corePosition.Position.lift(position), token).then(references => new ReferencesModel(references, nls.localize('ref.title', 'References'))));
|
||||
const range = new Range(position.lineNumber, position.column, position.lineNumber, position.column);
|
||||
return Promise.resolve(controller.toggleWidget(range, references, false));
|
||||
});
|
||||
|
|
|
@ -14,14 +14,14 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
|
|||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ReferencesModel, OneReference } from '../referencesModel';
|
||||
import { ReferenceWidget, LayoutData, ctxReferenceWidgetSearchTreeFocused } from './referencesWidget';
|
||||
import { ReferenceWidget, LayoutData } from './referencesWidget';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Location } from 'vs/editor/common/modes';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { getOuterEditor, PeekContext } from 'vs/editor/contrib/peekView/peekView';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IListService, WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
|
||||
|
@ -151,10 +151,8 @@ export abstract class ReferencesController implements editorCommon.IEditorContri
|
|||
if (this._widget && this._model && this._editor.hasModel()) { // might have been closed
|
||||
|
||||
// set title
|
||||
if (this._model.references.length === 1) {
|
||||
this._widget.setMetaTitle(nls.localize('metaTitle.1', "1 result"));
|
||||
} else if (!this._model.isEmpty) {
|
||||
this._widget.setMetaTitle(nls.localize('metaTitle.N', "{0} results", this._model.references.length));
|
||||
if (!this._model.isEmpty) {
|
||||
this._widget.setMetaTitle(nls.localize('metaTitle.N', "{0} ({1})", this._model.title, this._model.references.length));
|
||||
} else {
|
||||
this._widget.setMetaTitle('');
|
||||
}
|
||||
|
@ -361,12 +359,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
|||
mac: {
|
||||
primary: KeyMod.WinCtrl | KeyCode.Enter
|
||||
},
|
||||
when: ContextKeyExpr.and(ctxReferenceSearchVisible, ctxReferenceWidgetSearchTreeFocused),
|
||||
when: ContextKeyExpr.and(ctxReferenceSearchVisible, WorkbenchListFocusContextKey),
|
||||
handler(accessor: ServicesAccessor) {
|
||||
const listService = accessor.get(IListService);
|
||||
const focus = listService.lastFocusedList && listService.lastFocusedList.getFocus();
|
||||
if (focus instanceof OneReference) {
|
||||
withController(accessor, controller => controller.openReference(focus, true));
|
||||
const focus = <any[]>listService.lastFocusedList?.getFocus();
|
||||
if (Array.isArray(focus) && focus[0] instanceof OneReference) {
|
||||
withController(accessor, controller => controller.openReference(focus[0], true));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -101,7 +101,7 @@ export class StringRepresentationProvider implements IKeyboardNavigationLabelPro
|
|||
export class IdentityProvider implements IIdentityProvider<TreeElement> {
|
||||
|
||||
getId(element: TreeElement): { toString(): string; } {
|
||||
return element.uri;
|
||||
return element instanceof OneReference ? element.id : element.uri;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ import { Location } from 'vs/editor/common/modes';
|
|||
import { ITextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { AriaProvider, DataSource, Delegate, FileReferencesRenderer, OneReferenceRenderer, TreeElement, StringRepresentationProvider, IdentityProvider } from 'vs/editor/contrib/gotoSymbol/peek/referencesTree';
|
||||
import * as nls from 'vs/nls';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
|
||||
|
@ -182,8 +181,6 @@ export interface SelectionEvent {
|
|||
readonly element?: Location;
|
||||
}
|
||||
|
||||
export const ctxReferenceWidgetSearchTreeFocused = new RawContextKey<boolean>('referenceSearchTreeFocused', true);
|
||||
|
||||
/**
|
||||
* ZoneWidget that is shown inside the editor
|
||||
*/
|
||||
|
@ -319,7 +316,6 @@ export class ReferenceWidget extends peekView.PeekViewWidget {
|
|||
this._instantiationService.createInstance(DataSource),
|
||||
treeOptions
|
||||
);
|
||||
ctxReferenceWidgetSearchTreeFocused.bindTo(this._tree.contextKeyService);
|
||||
|
||||
// split stuff
|
||||
this._splitView.addView({
|
||||
|
|
|
@ -149,6 +149,7 @@ export class ReferencesModel implements IDisposable {
|
|||
|
||||
private readonly _disposables = new DisposableStore();
|
||||
private readonly _links: LocationLink[];
|
||||
private readonly _title: string;
|
||||
|
||||
readonly groups: FileReferences[] = [];
|
||||
readonly references: OneReference[] = [];
|
||||
|
@ -156,8 +157,9 @@ export class ReferencesModel implements IDisposable {
|
|||
readonly _onDidChangeReferenceRange = new Emitter<OneReference>();
|
||||
readonly onDidChangeReferenceRange: Event<OneReference> = this._onDidChangeReferenceRange.event;
|
||||
|
||||
constructor(links: LocationLink[]) {
|
||||
constructor(links: LocationLink[], title: string) {
|
||||
this._links = links;
|
||||
this._title = title;
|
||||
|
||||
// grouping and sorting
|
||||
const [providersFirst] = links;
|
||||
|
@ -192,7 +194,11 @@ export class ReferencesModel implements IDisposable {
|
|||
}
|
||||
|
||||
clone(): ReferencesModel {
|
||||
return new ReferencesModel(this._links);
|
||||
return new ReferencesModel(this._links, this._title);
|
||||
}
|
||||
|
||||
get title(): string {
|
||||
return this._title;
|
||||
}
|
||||
|
||||
get isEmpty(): boolean {
|
||||
|
|
|
@ -21,7 +21,7 @@ suite('references', function () {
|
|||
}, {
|
||||
uri: URI.file('/src/can'),
|
||||
range: new Range(1, 1, 1, 1)
|
||||
}]);
|
||||
}], 'FOO');
|
||||
|
||||
let ref = model.nearestReference(URI.file('/src/can'), new Position(1, 1));
|
||||
assert.equal(ref!.uri.path, '/src/can');
|
||||
|
|
|
@ -14,6 +14,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
|
|||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ColorIdentifier, Extensions, IColorRegistry } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { Extensions as ThemingExtensions, ICssStyleCollector, IIconTheme, IThemingRegistry } from 'vs/platform/theme/common/themeService';
|
||||
import { TokenStyle, TokenClassification, ProbeScope } from 'vs/platform/theme/common/tokenClassificationRegistry';
|
||||
|
||||
const VS_THEME_NAME = 'vs';
|
||||
const VS_DARK_THEME_NAME = 'vs-dark';
|
||||
|
@ -23,6 +24,7 @@ const colorRegistry = Registry.as<IColorRegistry>(Extensions.ColorContribution);
|
|||
const themingRegistry = Registry.as<IThemingRegistry>(ThemingExtensions.ThemingContribution);
|
||||
|
||||
class StandaloneTheme implements IStandaloneTheme {
|
||||
|
||||
public readonly id: string;
|
||||
public readonly themeName: string;
|
||||
|
||||
|
@ -128,6 +130,14 @@ class StandaloneTheme implements IStandaloneTheme {
|
|||
}
|
||||
return this._tokenTheme;
|
||||
}
|
||||
|
||||
getTokenStyle(classification: TokenClassification, useDefault?: boolean | undefined): TokenStyle | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
resolveScopes(scopes: ProbeScope[]): TokenStyle | undefined {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function isBuiltinTheme(themeName: string): themeName is BuiltinTheme {
|
||||
|
|
|
@ -54,7 +54,11 @@ suite('TokenizationSupport2Adapter', () => {
|
|||
|
||||
defines: (color: ColorIdentifier): boolean => {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
},
|
||||
|
||||
getTokenStyle: () => undefined,
|
||||
resolveScopes: () => undefined
|
||||
|
||||
};
|
||||
}
|
||||
public getIconTheme(): IIconTheme {
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export const enum AuthTokenStatus {
|
||||
Disabled = 'Disabled',
|
||||
|
@ -19,11 +20,10 @@ export interface IAuthTokenService {
|
|||
|
||||
readonly status: AuthTokenStatus;
|
||||
readonly onDidChangeStatus: Event<AuthTokenStatus>;
|
||||
readonly _onDidGetCallback: Emitter<URI>;
|
||||
|
||||
getToken(): Promise<string | null>;
|
||||
updateToken(token: string): Promise<void>;
|
||||
getToken(): Promise<string | undefined>;
|
||||
refreshToken(): Promise<void>;
|
||||
deleteToken(): Promise<void>;
|
||||
|
||||
login(callbackUri?: URI): Promise<void>;
|
||||
logout(): Promise<void>;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,12 @@ export class AuthTokenChannel implements IServerChannel {
|
|||
switch (command) {
|
||||
case '_getInitialStatus': return Promise.resolve(this.service.status);
|
||||
case 'getToken': return this.service.getToken();
|
||||
case 'updateToken': return this.service.updateToken(args[0]);
|
||||
case 'exchangeCodeForToken':
|
||||
this.service._onDidGetCallback.fire(args);
|
||||
return Promise.resolve();
|
||||
case 'refreshToken': return this.service.refreshToken();
|
||||
case 'deleteToken': return this.service.deleteToken();
|
||||
case 'login': return this.service.login(args);
|
||||
case 'logout': return this.service.logout();
|
||||
}
|
||||
throw new Error('Invalid call');
|
||||
}
|
||||
|
|
266
src/vs/platform/auth/electron-browser/authTokenService.ts
Normal file
266
src/vs/platform/auth/electron-browser/authTokenService.ts
Normal file
|
@ -0,0 +1,266 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as crypto from 'crypto';
|
||||
import * as https from 'https';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth';
|
||||
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { shell } from 'electron';
|
||||
|
||||
const SERVICE_NAME = 'VS Code';
|
||||
const ACCOUNT = 'MyAccount';
|
||||
|
||||
const redirectUrlAAD = 'https://vscode-redirect.azurewebsites.net/';
|
||||
const activeDirectoryEndpointUrl = 'https://login.microsoftonline.com/';
|
||||
const activeDirectoryResourceId = 'https://management.core.windows.net/';
|
||||
|
||||
const clientId = 'aebc6443-996d-45c2-90f0-388ff96faa56';
|
||||
const tenantId = 'common';
|
||||
|
||||
function parseQuery(uri: URI) {
|
||||
return uri.query.split('&').reduce((prev: any, current) => {
|
||||
const queryString = current.split('=');
|
||||
prev[queryString[0]] = queryString[1];
|
||||
return prev;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function toQuery(obj: any): string {
|
||||
return Object.keys(obj).map(key => `${key}=${obj[key]}`).join('&');
|
||||
}
|
||||
|
||||
function toBase64UrlEncoding(base64string: string) {
|
||||
return base64string.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); // Need to use base64url encoding
|
||||
}
|
||||
|
||||
export interface IToken {
|
||||
expiresIn: string; // How long access token is valid, in seconds
|
||||
expiresOn: string; // When the access token expires in epoch time
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
export class AuthTokenService extends Disposable implements IAuthTokenService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private _status: AuthTokenStatus = AuthTokenStatus.Disabled;
|
||||
get status(): AuthTokenStatus { return this._status; }
|
||||
private _onDidChangeStatus: Emitter<AuthTokenStatus> = this._register(new Emitter<AuthTokenStatus>());
|
||||
readonly onDidChangeStatus: Event<AuthTokenStatus> = this._onDidChangeStatus.event;
|
||||
|
||||
public readonly _onDidGetCallback: Emitter<URI> = this._register(new Emitter<URI>());
|
||||
readonly onDidGetCallback: Event<URI> = this._onDidGetCallback.event;
|
||||
|
||||
private _activeToken: IToken | undefined;
|
||||
|
||||
constructor(
|
||||
@ICredentialsService private readonly credentialsService: ICredentialsService,
|
||||
@IProductService productService: IProductService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
) {
|
||||
super();
|
||||
if (productService.settingsSyncStoreUrl && configurationService.getValue('configurationSync.enableAuth')) {
|
||||
this.credentialsService.getPassword(SERVICE_NAME, ACCOUNT).then(storedRefreshToken => {
|
||||
if (storedRefreshToken) {
|
||||
this.refresh(storedRefreshToken);
|
||||
} else {
|
||||
this._status = AuthTokenStatus.Inactive;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async login(callbackUri: URI): Promise<void> {
|
||||
const nonce = generateUuid();
|
||||
const port = (callbackUri.authority.match(/:([0-9]*)$/) || [])[1] || (callbackUri.scheme === 'https' || callbackUri.scheme === 'http' ? 443 : 80);
|
||||
const state = `${callbackUri.scheme},${port},${encodeURIComponent(nonce)},${encodeURIComponent(callbackUri.query)}`;
|
||||
const signInUrl = `${activeDirectoryEndpointUrl}${tenantId}/oauth2/authorize`;
|
||||
|
||||
const codeVerifier = toBase64UrlEncoding(crypto.randomBytes(32).toString('base64'));
|
||||
const codeChallenge = toBase64UrlEncoding(crypto.createHash('sha256').update(codeVerifier).digest('base64'));
|
||||
|
||||
let uri = URI.parse(signInUrl);
|
||||
uri = uri.with({
|
||||
query: `response_type=code&client_id=${encodeURIComponent(clientId)}&redirect_uri=${redirectUrlAAD}&state=${encodeURIComponent(state)}&resource=${activeDirectoryResourceId}&prompt=select_account&code_challenge_method=S256&code_challenge=${codeChallenge}`
|
||||
});
|
||||
|
||||
await shell.openExternal(uri.toString(true));
|
||||
|
||||
const timeoutPromise = new Promise((resolve: (value: IToken) => void, reject) => {
|
||||
const wait = setTimeout(() => {
|
||||
clearTimeout(wait);
|
||||
reject('Login timed out.');
|
||||
}, 1000 * 60 * 5);
|
||||
});
|
||||
|
||||
return Promise.race([this.exchangeCodeForToken(clientId, tenantId, codeVerifier, state), timeoutPromise]).then(token => {
|
||||
this.setToken(token);
|
||||
});
|
||||
}
|
||||
|
||||
public getToken(): Promise<string | undefined> {
|
||||
if (this.status === AuthTokenStatus.Disabled) {
|
||||
throw new Error('Not enabled');
|
||||
}
|
||||
return Promise.resolve(this._activeToken?.accessToken);
|
||||
}
|
||||
|
||||
public async refreshToken(): Promise<void> {
|
||||
if (this.status === AuthTokenStatus.Disabled) {
|
||||
throw new Error('Not enabled');
|
||||
}
|
||||
|
||||
if (!this._activeToken) {
|
||||
throw new Error('No token to refresh');
|
||||
}
|
||||
|
||||
this.refresh(this._activeToken.refreshToken);
|
||||
}
|
||||
|
||||
private setToken(token: IToken) {
|
||||
this._activeToken = token;
|
||||
this.credentialsService.setPassword(SERVICE_NAME, ACCOUNT, token.refreshToken);
|
||||
this.setStatus(AuthTokenStatus.Active);
|
||||
}
|
||||
|
||||
private async exchangeCodeForToken(clientId: string, tenantId: string, codeVerifier: string, state: string): Promise<IToken> {
|
||||
let uriEventListener: IDisposable;
|
||||
return new Promise((resolve: (value: IToken) => void, reject) => {
|
||||
uriEventListener = this.onDidGetCallback(async (uri: URI) => {
|
||||
try {
|
||||
const query = parseQuery(uri);
|
||||
const code = query.code;
|
||||
|
||||
if (query.state !== state) {
|
||||
return;
|
||||
}
|
||||
|
||||
const postData = toQuery({
|
||||
grant_type: 'authorization_code',
|
||||
code: code,
|
||||
client_id: clientId,
|
||||
code_verifier: codeVerifier,
|
||||
redirect_uri: redirectUrlAAD
|
||||
});
|
||||
|
||||
const post = https.request({
|
||||
host: 'login.microsoftonline.com',
|
||||
path: `/${tenantId}/oauth2/token`,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Length': postData.length
|
||||
}
|
||||
}, result => {
|
||||
const buffer: Buffer[] = [];
|
||||
result.on('data', (chunk: Buffer) => {
|
||||
buffer.push(chunk);
|
||||
});
|
||||
result.on('end', () => {
|
||||
if (result.statusCode === 200) {
|
||||
const json = JSON.parse(Buffer.concat(buffer).toString());
|
||||
resolve({
|
||||
expiresIn: json.access_token,
|
||||
expiresOn: json.expires_on,
|
||||
accessToken: json.access_token,
|
||||
refreshToken: json.refresh_token
|
||||
});
|
||||
} else {
|
||||
reject(new Error('Bad!'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
post.write(postData);
|
||||
|
||||
post.end();
|
||||
post.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}).then(result => {
|
||||
uriEventListener.dispose();
|
||||
return result;
|
||||
}).catch(err => {
|
||||
uriEventListener.dispose();
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
private async refresh(refreshToken: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const postData = toQuery({
|
||||
refresh_token: refreshToken,
|
||||
client_id: clientId,
|
||||
grant_type: 'refresh_token',
|
||||
resource: activeDirectoryResourceId
|
||||
});
|
||||
|
||||
const post = https.request({
|
||||
host: 'login.microsoftonline.com',
|
||||
path: `/${tenantId}/oauth2/token`,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Length': postData.length
|
||||
}
|
||||
}, result => {
|
||||
const buffer: Buffer[] = [];
|
||||
result.on('data', (chunk: Buffer) => {
|
||||
buffer.push(chunk);
|
||||
});
|
||||
result.on('end', () => {
|
||||
if (result.statusCode === 200) {
|
||||
const json = JSON.parse(Buffer.concat(buffer).toString());
|
||||
this.setToken({
|
||||
expiresIn: json.access_token,
|
||||
expiresOn: json.expires_on,
|
||||
accessToken: json.access_token,
|
||||
refreshToken: json.refresh_token
|
||||
});
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error('Bad!'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
post.write(postData);
|
||||
|
||||
post.end();
|
||||
post.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async logout(): Promise<void> {
|
||||
if (this.status === AuthTokenStatus.Disabled) {
|
||||
throw new Error('Not enabled');
|
||||
}
|
||||
await this.credentialsService.deletePassword(SERVICE_NAME, ACCOUNT);
|
||||
this._activeToken = undefined;
|
||||
this.setStatus(AuthTokenStatus.Inactive);
|
||||
}
|
||||
|
||||
private setStatus(status: AuthTokenStatus): void {
|
||||
if (this._status !== status) {
|
||||
this._status = status;
|
||||
this._onDidChangeStatus.fire(status);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -28,11 +28,21 @@ type OpenExternalOptions = { readonly openExternal?: boolean; readonly allowTunn
|
|||
|
||||
export type OpenOptions = OpenInternalOptions & OpenExternalOptions;
|
||||
|
||||
export type ResolveExternalUriOptions = { readonly allowTunneling?: boolean };
|
||||
|
||||
export interface IResolvedExternalUri extends IDisposable {
|
||||
resolved: URI;
|
||||
}
|
||||
|
||||
export interface IOpener {
|
||||
open(resource: URI, options?: OpenInternalOptions): Promise<boolean>;
|
||||
open(resource: URI, options?: OpenExternalOptions): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface IExternalOpener {
|
||||
openExternal(href: string): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface IValidator {
|
||||
shouldOpen(resource: URI): Promise<boolean>;
|
||||
}
|
||||
|
@ -61,6 +71,12 @@ export interface IOpenerService {
|
|||
*/
|
||||
registerExternalUriResolver(resolver: IExternalUriResolver): IDisposable;
|
||||
|
||||
/**
|
||||
* Sets the handler for opening externally. If not provided,
|
||||
* a default handler will be used.
|
||||
*/
|
||||
setExternalOpener(opener: IExternalOpener): void;
|
||||
|
||||
/**
|
||||
* Opens a resource, like a webaddress, a document uri, or executes command.
|
||||
*
|
||||
|
@ -70,7 +86,10 @@ export interface IOpenerService {
|
|||
open(resource: URI, options?: OpenInternalOptions): Promise<boolean>;
|
||||
open(resource: URI, options?: OpenExternalOptions): Promise<boolean>;
|
||||
|
||||
resolveExternalUri(resource: URI, options?: { readonly allowTunneling?: boolean }): Promise<{ resolved: URI, dispose(): void }>;
|
||||
/**
|
||||
* Resolve a resource to its external form.
|
||||
*/
|
||||
resolveExternalUri(resource: URI, options?: ResolveExternalUriOptions): Promise<IResolvedExternalUri>;
|
||||
}
|
||||
|
||||
export const NullOpenerService: IOpenerService = Object.freeze({
|
||||
|
@ -78,6 +97,7 @@ export const NullOpenerService: IOpenerService = Object.freeze({
|
|||
registerOpener() { return Disposable.None; },
|
||||
registerValidator() { return Disposable.None; },
|
||||
registerExternalUriResolver() { return Disposable.None; },
|
||||
open() { return Promise.resolve(false); },
|
||||
setExternalOpener() { },
|
||||
async open() { return false; },
|
||||
async resolveExternalUri(uri: URI) { return { resolved: uri, dispose() { } }; },
|
||||
});
|
||||
|
|
|
@ -39,7 +39,7 @@ export async function getProxyAgent(rawRequestURL: string, options: IOptions = {
|
|||
|
||||
const opts = {
|
||||
host: proxyEndpoint.hostname || '',
|
||||
port: Number(proxyEndpoint.port),
|
||||
port: proxyEndpoint.port || (proxyEndpoint.protocol === 'https' ? '443' : '80'),
|
||||
auth: proxyEndpoint.auth,
|
||||
rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true
|
||||
};
|
||||
|
|
|
@ -103,7 +103,7 @@ class ColorRegistry implements IColorRegistry {
|
|||
public registerColor(id: string, defaults: ColorDefaults | null, description: string, needsTransparency = false, deprecationMessage?: string): ColorIdentifier {
|
||||
let colorContribution: ColorContribution = { id, description, defaults, needsTransparency, deprecationMessage };
|
||||
this.colorsById[id] = colorContribution;
|
||||
let propertySchema: IJSONSchema = { type: 'string', description, format: 'color-hex', defaultSnippets: [{ body: '#ff0000' }] };
|
||||
let propertySchema: IJSONSchema = { type: 'string', description, format: 'color-hex', defaultSnippets: [{ body: '${1:#ff0000}' }] };
|
||||
if (deprecationMessage) {
|
||||
propertySchema.deprecationMessage = deprecationMessage;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import * as platform from 'vs/platform/registry/common/platform';
|
|||
import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { TokenStyle, TokenClassification, ProbeScope } from 'vs/platform/theme/common/tokenClassificationRegistry';
|
||||
|
||||
export const IThemeService = createDecorator<IThemeService>('themeService');
|
||||
|
||||
|
@ -59,6 +60,10 @@ export interface ITheme {
|
|||
* default color will be used.
|
||||
*/
|
||||
defines(color: ColorIdentifier): boolean;
|
||||
|
||||
getTokenStyle(classification: TokenClassification, useDefault?: boolean): TokenStyle | undefined;
|
||||
|
||||
resolveScopes(scopes: ProbeScope[]): TokenStyle | undefined;
|
||||
}
|
||||
|
||||
export interface IIconTheme {
|
||||
|
|
478
src/vs/platform/theme/common/tokenClassificationRegistry.ts
Normal file
478
src/vs/platform/theme/common/tokenClassificationRegistry.ts
Normal file
|
@ -0,0 +1,478 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as platform from 'vs/platform/registry/common/platform';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
|
||||
|
||||
// ------ API types
|
||||
|
||||
export const TOKEN_TYPE_WILDCARD = '*';
|
||||
export const TOKEN_TYPE_WILDCARD_NUM = -1;
|
||||
|
||||
// qualified string [type|*](.modifier)*
|
||||
export type TokenClassificationString = string;
|
||||
|
||||
export interface TokenClassification {
|
||||
type: number;
|
||||
modifiers: number;
|
||||
}
|
||||
|
||||
export interface TokenTypeOrModifierContribution {
|
||||
readonly num: number;
|
||||
readonly id: string;
|
||||
readonly description: string;
|
||||
readonly deprecationMessage: string | undefined;
|
||||
}
|
||||
|
||||
|
||||
export interface TokenStyleData {
|
||||
foreground?: Color;
|
||||
bold?: boolean;
|
||||
underline?: boolean;
|
||||
italic?: boolean;
|
||||
}
|
||||
|
||||
export class TokenStyle implements Readonly<TokenStyleData> {
|
||||
constructor(
|
||||
public readonly foreground?: Color,
|
||||
public readonly bold?: boolean,
|
||||
public readonly underline?: boolean,
|
||||
public readonly italic?: boolean,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TokenStyle {
|
||||
export function fromData(data: { foreground?: Color, bold?: boolean, underline?: boolean, italic?: boolean }) {
|
||||
return new TokenStyle(data.foreground, data.bold, data.underline, data.italic);
|
||||
}
|
||||
}
|
||||
|
||||
export type ProbeScope = string[];
|
||||
|
||||
export interface TokenStyleFunction {
|
||||
(theme: ITheme): TokenStyle | undefined;
|
||||
}
|
||||
|
||||
export interface TokenStyleDefaults {
|
||||
scopesToProbe: ProbeScope[];
|
||||
light: TokenStyleValue | null;
|
||||
dark: TokenStyleValue | null;
|
||||
hc: TokenStyleValue | null;
|
||||
}
|
||||
|
||||
export interface TokenStylingDefaultRule {
|
||||
classification: TokenClassification;
|
||||
matchScore: number;
|
||||
defaults: TokenStyleDefaults;
|
||||
}
|
||||
|
||||
export interface TokenStylingRule {
|
||||
classification: TokenClassification;
|
||||
matchScore: number;
|
||||
value: TokenStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* A TokenStyle Value is either a token style literal, or a TokenClassificationString
|
||||
*/
|
||||
export type TokenStyleValue = TokenStyle | TokenClassificationString;
|
||||
|
||||
// TokenStyle registry
|
||||
export const Extensions = {
|
||||
TokenClassificationContribution: 'base.contributions.tokenClassification'
|
||||
};
|
||||
|
||||
export interface ITokenClassificationRegistry {
|
||||
|
||||
readonly onDidChangeSchema: Event<void>;
|
||||
|
||||
/**
|
||||
* Register a token type to the registry.
|
||||
* @param id The TokenType id as used in theme description files
|
||||
* @description the description
|
||||
*/
|
||||
registerTokenType(id: string, description: string): void;
|
||||
|
||||
/**
|
||||
* Register a token modifier to the registry.
|
||||
* @param id The TokenModifier id as used in theme description files
|
||||
* @description the description
|
||||
*/
|
||||
registerTokenModifier(id: string, description: string): void;
|
||||
|
||||
getTokenClassificationFromString(str: TokenClassificationString): TokenClassification | undefined;
|
||||
getTokenClassification(type: string, modifiers: string[]): TokenClassification | undefined;
|
||||
|
||||
getTokenStylingRule(classification: TokenClassification, value: TokenStyle): TokenStylingRule;
|
||||
|
||||
/**
|
||||
* Register a TokenStyle default to the registry.
|
||||
* @param selector The rule selector
|
||||
* @param defaults The default values
|
||||
*/
|
||||
registerTokenStyleDefault(selector: TokenClassification, defaults: TokenStyleDefaults): void;
|
||||
|
||||
/**
|
||||
* Deregister a TokenType from the registry.
|
||||
*/
|
||||
deregisterTokenType(id: string): void;
|
||||
|
||||
/**
|
||||
* Deregister a TokenModifier from the registry.
|
||||
*/
|
||||
deregisterTokenModifier(id: string): void;
|
||||
|
||||
/**
|
||||
* Get all TokenType contributions
|
||||
*/
|
||||
getTokenTypes(): TokenTypeOrModifierContribution[];
|
||||
|
||||
/**
|
||||
* Get all TokenModifier contributions
|
||||
*/
|
||||
getTokenModifiers(): TokenTypeOrModifierContribution[];
|
||||
|
||||
/**
|
||||
* Resolves a token classification against the given rules and default rules from the registry.
|
||||
*/
|
||||
resolveTokenStyle(classification: TokenClassification, themingRules: TokenStylingRule[] | undefined, customThemingRules: TokenStylingRule[], theme: ITheme): TokenStyle | undefined;
|
||||
|
||||
/**
|
||||
* JSON schema for an object to assign styling to token classifications
|
||||
*/
|
||||
getTokenStylingSchema(): IJSONSchema;
|
||||
}
|
||||
|
||||
class TokenClassificationRegistry implements ITokenClassificationRegistry {
|
||||
|
||||
private readonly _onDidChangeSchema = new Emitter<void>();
|
||||
readonly onDidChangeSchema: Event<void> = this._onDidChangeSchema.event;
|
||||
|
||||
private currentTypeNumber = 0;
|
||||
private currentModifierBit = 1;
|
||||
|
||||
private tokenTypeById: { [key: string]: TokenTypeOrModifierContribution };
|
||||
private tokenModifierById: { [key: string]: TokenTypeOrModifierContribution };
|
||||
|
||||
private tokenStylingDefaultRules: TokenStylingDefaultRule[] = [];
|
||||
|
||||
private tokenStylingSchema: IJSONSchema & { properties: IJSONSchemaMap } = {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
definitions: {
|
||||
style: {
|
||||
type: 'object',
|
||||
description: nls.localize('schema.token.settings', 'Colors and styles for the token.'),
|
||||
properties: {
|
||||
foreground: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.token.foreground', 'Foreground color for the token.'),
|
||||
format: 'color-hex',
|
||||
default: '#ff0000'
|
||||
},
|
||||
background: {
|
||||
type: 'string',
|
||||
deprecationMessage: nls.localize('schema.token.background.warning', 'Token background colors are currently not supported.')
|
||||
},
|
||||
fontStyle: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.token.fontStyle', 'Font style of the rule: \'italic\', \'bold\' or \'underline\', \'-italic\', \'-bold\' or \'-underline\'or a combination. The empty string unsets inherited settings.'),
|
||||
pattern: '^(\\s*(-?italic|-?bold|-?underline))*\\s*$',
|
||||
patternErrorMessage: nls.localize('schema.fontStyle.error', 'Font style must be \'italic\', \'bold\' or \'underline\' to set a style or \'-italic\', \'-bold\' or \'-underline\' to unset or a combination. The empty string unsets all styles.'),
|
||||
defaultSnippets: [{ label: nls.localize('schema.token.fontStyle.none', 'None (clear inherited style)'), bodyText: '""' }, { body: 'italic' }, { body: 'bold' }, { body: 'underline' }, { body: '-italic' }, { body: '-bold' }, { body: '-underline' }, { body: 'italic bold' }, { body: 'italic underline' }, { body: 'bold underline' }, { body: 'italic bold underline' }]
|
||||
}
|
||||
},
|
||||
additionalProperties: false,
|
||||
defaultSnippets: [{ body: { foreground: '${1:#FF0000}', fontStyle: '${2:bold}' } }]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.tokenTypeById = {};
|
||||
this.tokenModifierById = {};
|
||||
|
||||
this.tokenTypeById[TOKEN_TYPE_WILDCARD] = { num: TOKEN_TYPE_WILDCARD_NUM, id: TOKEN_TYPE_WILDCARD, description: '', deprecationMessage: undefined };
|
||||
}
|
||||
|
||||
public registerTokenType(id: string, description: string, deprecationMessage?: string): void {
|
||||
const num = this.currentTypeNumber++;
|
||||
let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, description, deprecationMessage };
|
||||
this.tokenTypeById[id] = tokenStyleContribution;
|
||||
|
||||
this.tokenStylingSchema.properties[id] = getStylingSchemeEntry(description, deprecationMessage);
|
||||
}
|
||||
|
||||
public registerTokenModifier(id: string, description: string, deprecationMessage?: string): void {
|
||||
const num = this.currentModifierBit;
|
||||
this.currentModifierBit = this.currentModifierBit * 2;
|
||||
let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, description, deprecationMessage };
|
||||
this.tokenModifierById[id] = tokenStyleContribution;
|
||||
|
||||
this.tokenStylingSchema.properties[`*.${id}`] = getStylingSchemeEntry(description, deprecationMessage);
|
||||
}
|
||||
|
||||
public getTokenClassification(type: string, modifiers: string[]): TokenClassification | undefined {
|
||||
const tokenTypeDesc = this.tokenTypeById[type];
|
||||
if (!tokenTypeDesc) {
|
||||
return undefined;
|
||||
}
|
||||
let allModifierBits = 0;
|
||||
for (const modifier of modifiers) {
|
||||
const tokenModifierDesc = this.tokenModifierById[modifier];
|
||||
if (tokenModifierDesc) {
|
||||
allModifierBits |= tokenModifierDesc.num;
|
||||
}
|
||||
}
|
||||
return { type: tokenTypeDesc.num, modifiers: allModifierBits };
|
||||
}
|
||||
|
||||
public getTokenClassificationFromString(str: TokenClassificationString): TokenClassification | undefined {
|
||||
const parts = str.split('.');
|
||||
const type = parts.shift();
|
||||
if (type) {
|
||||
return this.getTokenClassification(type, parts);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public getTokenStylingRule(classification: TokenClassification, value: TokenStyle): TokenStylingRule {
|
||||
return { classification, matchScore: getTokenStylingScore(classification), value };
|
||||
}
|
||||
|
||||
public registerTokenStyleDefault(classification: TokenClassification, defaults: TokenStyleDefaults): void {
|
||||
this.tokenStylingDefaultRules.push({ classification, matchScore: getTokenStylingScore(classification), defaults });
|
||||
}
|
||||
|
||||
public deregisterTokenType(id: string): void {
|
||||
delete this.tokenTypeById[id];
|
||||
delete this.tokenStylingSchema.properties[id];
|
||||
}
|
||||
|
||||
public deregisterTokenModifier(id: string): void {
|
||||
delete this.tokenModifierById[id];
|
||||
delete this.tokenStylingSchema.properties[`*.${id}`];
|
||||
}
|
||||
|
||||
public getTokenTypes(): TokenTypeOrModifierContribution[] {
|
||||
return Object.keys(this.tokenTypeById).map(id => this.tokenTypeById[id]);
|
||||
}
|
||||
|
||||
public getTokenModifiers(): TokenTypeOrModifierContribution[] {
|
||||
return Object.keys(this.tokenModifierById).map(id => this.tokenModifierById[id]);
|
||||
}
|
||||
|
||||
public resolveTokenStyle(classification: TokenClassification, themingRules: TokenStylingRule[] | undefined, customThemingRules: TokenStylingRule[], theme: ITheme): TokenStyle | undefined {
|
||||
let result: any = {
|
||||
foreground: undefined,
|
||||
bold: undefined,
|
||||
underline: undefined,
|
||||
italic: undefined
|
||||
};
|
||||
let score = {
|
||||
foreground: -1,
|
||||
bold: -1,
|
||||
underline: -1,
|
||||
italic: -1
|
||||
};
|
||||
|
||||
function _processStyle(matchScore: number, style: TokenStyle) {
|
||||
if (style.foreground && score.foreground <= matchScore) {
|
||||
score.foreground = matchScore;
|
||||
result.foreground = style.foreground;
|
||||
}
|
||||
for (let p of ['bold', 'underline', 'italic']) {
|
||||
const property = p as keyof TokenStyle;
|
||||
const info = style[property];
|
||||
if (info !== undefined) {
|
||||
if (score[property] <= matchScore) {
|
||||
score[property] = matchScore;
|
||||
result[property] = info;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (themingRules === undefined) {
|
||||
for (const rule of this.tokenStylingDefaultRules) {
|
||||
const matchScore = match(rule, classification);
|
||||
if (matchScore >= 0) {
|
||||
let style = theme.resolveScopes(rule.defaults.scopesToProbe);
|
||||
if (!style) {
|
||||
style = this.resolveTokenStyleValue(rule.defaults[theme.type], theme);
|
||||
}
|
||||
if (style) {
|
||||
_processStyle(matchScore, style);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const rule of themingRules) {
|
||||
const matchScore = match(rule, classification);
|
||||
if (matchScore >= 0) {
|
||||
_processStyle(matchScore, rule.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const rule of customThemingRules) {
|
||||
const matchScore = match(rule, classification);
|
||||
if (matchScore >= 0) {
|
||||
_processStyle(matchScore, rule.value);
|
||||
}
|
||||
}
|
||||
return TokenStyle.fromData(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenStyleValue Resolve a tokenStyleValue in the context of a theme
|
||||
*/
|
||||
private resolveTokenStyleValue(tokenStyleValue: TokenStyleValue | null, theme: ITheme): TokenStyle | undefined {
|
||||
if (tokenStyleValue === null) {
|
||||
return undefined;
|
||||
} else if (typeof tokenStyleValue === 'string') {
|
||||
const classification = this.getTokenClassificationFromString(tokenStyleValue);
|
||||
if (classification) {
|
||||
return theme.getTokenStyle(classification);
|
||||
}
|
||||
} else if (typeof tokenStyleValue === 'object') {
|
||||
return tokenStyleValue;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public getTokenStylingSchema(): IJSONSchema {
|
||||
return this.tokenStylingSchema;
|
||||
}
|
||||
|
||||
|
||||
public toString() {
|
||||
let sorter = (a: string, b: string) => {
|
||||
let cat1 = a.indexOf('.') === -1 ? 0 : 1;
|
||||
let cat2 = b.indexOf('.') === -1 ? 0 : 1;
|
||||
if (cat1 !== cat2) {
|
||||
return cat1 - cat2;
|
||||
}
|
||||
return a.localeCompare(b);
|
||||
};
|
||||
|
||||
return Object.keys(this.tokenTypeById).sort(sorter).map(k => `- \`${k}\`: ${this.tokenTypeById[k].description}`).join('\n');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function match(themeSelector: TokenStylingRule | TokenStylingDefaultRule, classification: TokenClassification): number {
|
||||
const selectorType = themeSelector.classification.type;
|
||||
if (selectorType !== TOKEN_TYPE_WILDCARD_NUM && selectorType !== classification.type) {
|
||||
return -1;
|
||||
}
|
||||
const selectorModifier = themeSelector.classification.modifiers;
|
||||
if ((classification.modifiers & selectorModifier) !== selectorModifier) {
|
||||
return -1;
|
||||
}
|
||||
return themeSelector.matchScore;
|
||||
}
|
||||
|
||||
|
||||
const tokenClassificationRegistry = new TokenClassificationRegistry();
|
||||
platform.Registry.add(Extensions.TokenClassificationContribution, tokenClassificationRegistry);
|
||||
|
||||
export function registerTokenType(id: string, description: string, scopesToProbe: ProbeScope[] = [], extendsTC: string | null = null, deprecationMessage?: string): string {
|
||||
tokenClassificationRegistry.registerTokenType(id, description, deprecationMessage);
|
||||
|
||||
if (scopesToProbe || extendsTC) {
|
||||
const classification = tokenClassificationRegistry.getTokenClassification(id, []);
|
||||
tokenClassificationRegistry.registerTokenStyleDefault(classification!, { scopesToProbe, light: extendsTC, dark: extendsTC, hc: extendsTC });
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
export function registerTokenModifier(id: string, description: string, deprecationMessage?: string): string {
|
||||
tokenClassificationRegistry.registerTokenModifier(id, description, deprecationMessage);
|
||||
return id;
|
||||
}
|
||||
|
||||
export function getTokenClassificationRegistry(): ITokenClassificationRegistry {
|
||||
return tokenClassificationRegistry;
|
||||
}
|
||||
|
||||
export const comments = registerTokenType('comments', nls.localize('comments', "Style for comments."), [['comment']]);
|
||||
export const strings = registerTokenType('strings', nls.localize('strings', "Style for strings."), [['string']]);
|
||||
export const keywords = registerTokenType('keywords', nls.localize('keywords', "Style for keywords."), [['keyword.control']]);
|
||||
export const numbers = registerTokenType('numbers', nls.localize('numbers', "Style for numbers."), [['constant.numeric']]);
|
||||
export const regexp = registerTokenType('regexp', nls.localize('regexp', "Style for expressions."), [['constant.regexp']]);
|
||||
export const operators = registerTokenType('operators', nls.localize('operator', "Style for operators."), [['keyword.operator']]);
|
||||
|
||||
export const namespaces = registerTokenType('namespaces', nls.localize('namespace', "Style for namespaces."), [['entity.name.namespace']]);
|
||||
|
||||
export const types = registerTokenType('types', nls.localize('types', "Style for types."), [['entity.name.type'], ['entity.name.class'], ['support.type'], ['support.class']]);
|
||||
export const structs = registerTokenType('structs', nls.localize('struct', "Style for structs."), [['storage.type.struct']], types);
|
||||
export const classes = registerTokenType('classes', nls.localize('class', "Style for classes."), [['entity.name.class']], types);
|
||||
export const interfaces = registerTokenType('interfaces', nls.localize('interface', "Style for interfaces."), undefined, types);
|
||||
export const enums = registerTokenType('enums', nls.localize('enum', "Style for enums."), undefined, types);
|
||||
export const parameterTypes = registerTokenType('parameterTypes', nls.localize('parameterType', "Style for parameter types."), undefined, types);
|
||||
|
||||
export const functions = registerTokenType('functions', nls.localize('functions', "Style for functions"), [['entity.name.function'], ['support.function']]);
|
||||
export const macros = registerTokenType('macros', nls.localize('macro', "Style for macros."), undefined, functions);
|
||||
|
||||
export const variables = registerTokenType('variables', nls.localize('variables', "Style for variables."), [['variable'], ['entity.name.variable']]);
|
||||
export const constants = registerTokenType('constants', nls.localize('constants', "Style for constants."), undefined, variables);
|
||||
export const parameters = registerTokenType('parameters', nls.localize('parameters', "Style for parameters."), undefined, variables);
|
||||
export const property = registerTokenType('properties', nls.localize('properties', "Style for properties."), undefined, variables);
|
||||
|
||||
export const labels = registerTokenType('labels', nls.localize('labels', "Style for labels. "), undefined);
|
||||
|
||||
export const m_declaration = registerTokenModifier('declaration', nls.localize('declaration', "Style for all symbol declarations."), undefined);
|
||||
export const m_documentation = registerTokenModifier('documentation', nls.localize('documentation', "Style to use for references in documentation."), undefined);
|
||||
export const m_member = registerTokenModifier('member', nls.localize('member', "Style to use for member functions, variables (fields) and types."), undefined);
|
||||
export const m_static = registerTokenModifier('static', nls.localize('static', "Style to use for symbols that are static."), undefined);
|
||||
export const m_abstract = registerTokenModifier('abstract', nls.localize('abstract', "Style to use for symbols that are abstract."), undefined);
|
||||
export const m_deprecated = registerTokenModifier('deprecated', nls.localize('deprecated', "Style to use for symbols that are deprecated."), undefined);
|
||||
export const m_modification = registerTokenModifier('modification', nls.localize('modification', "Style to use for write accesses."), undefined);
|
||||
export const m_async = registerTokenModifier('async', nls.localize('async', "Style to use for symbols that are async."), undefined);
|
||||
|
||||
function bitCount(u: number) {
|
||||
// https://blogs.msdn.microsoft.com/jeuge/2005/06/08/bit-fiddling-3/
|
||||
const uCount = u - ((u >> 1) & 0o33333333333) - ((u >> 2) & 0o11111111111);
|
||||
return ((uCount + (uCount >> 3)) & 0o30707070707) % 63;
|
||||
}
|
||||
|
||||
function getTokenStylingScore(classification: TokenClassification) {
|
||||
return bitCount(classification.modifiers) + ((classification.type !== TOKEN_TYPE_WILDCARD_NUM) ? 1 : 0);
|
||||
}
|
||||
|
||||
function getStylingSchemeEntry(description: string, deprecationMessage?: string): IJSONSchema {
|
||||
return {
|
||||
description,
|
||||
deprecationMessage,
|
||||
defaultSnippets: [{ body: '${1:#ff0000}' }],
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
format: 'color-hex'
|
||||
},
|
||||
{
|
||||
$ref: '#definitions/style'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
export const tokenStylingSchemaId = 'vscode://schemas/token-styling';
|
||||
|
||||
let schemaRegistry = platform.Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
|
||||
schemaRegistry.registerSchema(tokenStylingSchemaId, tokenClassificationRegistry.getTokenStylingSchema());
|
||||
|
||||
const delayer = new RunOnceScheduler(() => schemaRegistry.notifySchemaChanged(tokenStylingSchemaId), 200);
|
||||
tokenClassificationRegistry.onDidChangeSchema(() => {
|
||||
if (!delayer.isScheduled()) {
|
||||
delayer.schedule();
|
||||
}
|
||||
});
|
|
@ -6,6 +6,7 @@
|
|||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IThemeService, ITheme, DARK, IIconTheme } from 'vs/platform/theme/common/themeService';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { TokenStyle, TokenClassification, ProbeScope } from 'vs/platform/theme/common/tokenClassificationRegistry';
|
||||
|
||||
export class TestTheme implements ITheme {
|
||||
|
||||
|
@ -23,6 +24,14 @@ export class TestTheme implements ITheme {
|
|||
defines(color: string): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getTokenStyle(classification: TokenClassification, useDefault?: boolean | undefined): TokenStyle | undefined {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
resolveScopes(scopes: ProbeScope[]): TokenStyle | undefined {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
export class TestIconTheme implements IIconTheme {
|
||||
|
|
|
@ -130,7 +130,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
|
||||
// Other instances
|
||||
const extHostClipboard = new ExtHostClipboard(rpcProtocol);
|
||||
const extHostMessageService = new ExtHostMessageService(rpcProtocol);
|
||||
const extHostMessageService = new ExtHostMessageService(rpcProtocol, extHostLogService);
|
||||
const extHostDialogs = new ExtHostDialogs(rpcProtocol);
|
||||
const extHostStatusBar = new ExtHostStatusBar(rpcProtocol);
|
||||
const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments);
|
||||
|
|
|
@ -20,6 +20,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
|||
import { Barrier } from 'vs/base/common/async';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
function lookUp(tree: any, key: string) {
|
||||
if (key) {
|
||||
|
@ -45,16 +46,19 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
|
|||
readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly _proxy: MainThreadConfigurationShape;
|
||||
private readonly _logService: ILogService;
|
||||
private readonly _extHostWorkspace: ExtHostWorkspace;
|
||||
private readonly _barrier: Barrier;
|
||||
private _actual: ExtHostConfigProvider | null;
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostWorkspace extHostWorkspace: IExtHostWorkspace
|
||||
@IExtHostWorkspace extHostWorkspace: IExtHostWorkspace,
|
||||
@ILogService logService: ILogService,
|
||||
) {
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadConfiguration);
|
||||
this._extHostWorkspace = extHostWorkspace;
|
||||
this._logService = logService;
|
||||
this._barrier = new Barrier();
|
||||
this._actual = null;
|
||||
}
|
||||
|
@ -64,7 +68,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
|
|||
}
|
||||
|
||||
$initializeConfiguration(data: IConfigurationInitData): void {
|
||||
this._actual = new ExtHostConfigProvider(this._proxy, this._extHostWorkspace, data);
|
||||
this._actual = new ExtHostConfigProvider(this._proxy, this._extHostWorkspace, data, this._logService);
|
||||
this._barrier.open();
|
||||
}
|
||||
|
||||
|
@ -80,9 +84,11 @@ export class ExtHostConfigProvider {
|
|||
private readonly _extHostWorkspace: ExtHostWorkspace;
|
||||
private _configurationScopes: Map<string, ConfigurationScope | undefined>;
|
||||
private _configuration: Configuration;
|
||||
private _logService: ILogService;
|
||||
|
||||
constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace, data: IConfigurationInitData) {
|
||||
constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace, data: IConfigurationInitData, logService: ILogService) {
|
||||
this._proxy = proxy;
|
||||
this._logService = logService;
|
||||
this._extHostWorkspace = extHostWorkspace;
|
||||
this._configuration = ExtHostConfigProvider.parse(data);
|
||||
this._configurationScopes = this._toMap(data.configurationScopes);
|
||||
|
@ -236,13 +242,13 @@ export class ExtHostConfigProvider {
|
|||
const extensionIdText = extensionId ? `[${extensionId.value}] ` : '';
|
||||
if (ConfigurationScope.RESOURCE === scope) {
|
||||
if (resource === undefined) {
|
||||
console.warn(`${extensionIdText}Accessing a resource scoped configuration without providing a resource is not expected. To get the effective value for '${key}', provide the URI of a resource or 'null' for any resource.`);
|
||||
this._logService.warn(`${extensionIdText}Accessing a resource scoped configuration without providing a resource is not expected. To get the effective value for '${key}', provide the URI of a resource or 'null' for any resource.`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (ConfigurationScope.WINDOW === scope) {
|
||||
if (resource) {
|
||||
console.warn(`${extensionIdText}Accessing a window scoped configuration for a resource is not expected. To associate '${key}' to a resource, define its scope to 'resource' in configuration contributions in 'package.json'.`);
|
||||
this._logService.warn(`${extensionIdText}Accessing a window scoped configuration for a resource is not expected. To associate '${key}' to a resource, define its scope to 'resource' in configuration contributions in 'package.json'.`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -474,11 +474,11 @@ class NavigateTypeAdapter {
|
|||
|
||||
private readonly _symbolCache = new Map<number, vscode.SymbolInformation>();
|
||||
private readonly _resultCache = new Map<number, [number, number]>();
|
||||
private readonly _provider: vscode.WorkspaceSymbolProvider;
|
||||
|
||||
constructor(provider: vscode.WorkspaceSymbolProvider) {
|
||||
this._provider = provider;
|
||||
}
|
||||
constructor(
|
||||
private readonly _provider: vscode.WorkspaceSymbolProvider,
|
||||
private readonly _logService: ILogService
|
||||
) { }
|
||||
|
||||
provideWorkspaceSymbols(search: string, token: CancellationToken): Promise<extHostProtocol.IWorkspaceSymbolsDto> {
|
||||
const result: extHostProtocol.IWorkspaceSymbolsDto = extHostProtocol.IdObject.mixin({ symbols: [] });
|
||||
|
@ -490,7 +490,7 @@ class NavigateTypeAdapter {
|
|||
continue;
|
||||
}
|
||||
if (!item.name) {
|
||||
console.warn('INVALID SymbolInformation, lacks name', item);
|
||||
this._logService.warn('INVALID SymbolInformation, lacks name', item);
|
||||
continue;
|
||||
}
|
||||
const symbol = extHostProtocol.IdObject.mixin(typeConvert.WorkspaceSymbol.from(item));
|
||||
|
@ -538,7 +538,8 @@ class RenameAdapter {
|
|||
|
||||
constructor(
|
||||
private readonly _documents: ExtHostDocuments,
|
||||
private readonly _provider: vscode.RenameProvider
|
||||
private readonly _provider: vscode.RenameProvider,
|
||||
private readonly _logService: ILogService
|
||||
) { }
|
||||
|
||||
provideRenameEdits(resource: URI, position: IPosition, newName: string, token: CancellationToken): Promise<extHostProtocol.IWorkspaceEditDto | undefined> {
|
||||
|
@ -587,7 +588,7 @@ class RenameAdapter {
|
|||
return undefined;
|
||||
}
|
||||
if (range.start.line > pos.line || range.end.line < pos.line) {
|
||||
console.warn('INVALID rename location: position line must be within range start/end lines');
|
||||
this._logService.warn('INVALID rename location: position line must be within range start/end lines');
|
||||
return undefined;
|
||||
}
|
||||
return { range: typeConvert.Range.from(range), text };
|
||||
|
@ -618,18 +619,15 @@ class SuggestAdapter {
|
|||
return typeof provider.resolveCompletionItem === 'function';
|
||||
}
|
||||
|
||||
private _documents: ExtHostDocuments;
|
||||
private _commands: CommandsConverter;
|
||||
private _provider: vscode.CompletionItemProvider;
|
||||
|
||||
private _cache = new Cache<vscode.CompletionItem>('CompletionItem');
|
||||
private _disposables = new Map<number, DisposableStore>();
|
||||
|
||||
constructor(documents: ExtHostDocuments, commands: CommandsConverter, provider: vscode.CompletionItemProvider) {
|
||||
this._documents = documents;
|
||||
this._commands = commands;
|
||||
this._provider = provider;
|
||||
}
|
||||
constructor(
|
||||
private readonly _documents: ExtHostDocuments,
|
||||
private readonly _commands: CommandsConverter,
|
||||
private readonly _provider: vscode.CompletionItemProvider,
|
||||
private readonly _logService: ILogService
|
||||
) { }
|
||||
|
||||
provideCompletionItems(resource: URI, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<extHostProtocol.ISuggestResultDto | undefined> {
|
||||
|
||||
|
@ -712,7 +710,7 @@ class SuggestAdapter {
|
|||
|
||||
private _convertCompletionItem(item: vscode.CompletionItem, position: vscode.Position, id: extHostProtocol.ChainedCacheId): extHostProtocol.ISuggestDataDto | undefined {
|
||||
if (typeof item.label !== 'string' || item.label.length === 0) {
|
||||
console.warn('INVALID text edit -> must have at least a label');
|
||||
this._logService.warn('INVALID text edit -> must have at least a label');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -764,7 +762,7 @@ class SuggestAdapter {
|
|||
if (range) {
|
||||
if (Range.isRange(range)) {
|
||||
if (!SuggestAdapter._isValidRangeForCompletion(range, position)) {
|
||||
console.trace('INVALID range -> must be single line and on the same line');
|
||||
this._logService.trace('INVALID range -> must be single line and on the same line');
|
||||
return undefined;
|
||||
}
|
||||
result[extHostProtocol.ISuggestDataDtoField.range] = typeConvert.Range.from(range);
|
||||
|
@ -776,7 +774,7 @@ class SuggestAdapter {
|
|||
|| !range.inserting.start.isEqual(range.replacing.start)
|
||||
|| !range.replacing.contains(range.inserting)
|
||||
) {
|
||||
console.trace('INVALID range -> must be single line, on the same line, insert range must be a prefix of replace range');
|
||||
this._logService.trace('INVALID range -> must be single line, on the same line, insert range must be a prefix of replace range');
|
||||
return undefined;
|
||||
}
|
||||
result[extHostProtocol.ISuggestDataDtoField.range] = {
|
||||
|
@ -992,7 +990,8 @@ class SelectionRangeAdapter {
|
|||
|
||||
constructor(
|
||||
private readonly _documents: ExtHostDocuments,
|
||||
private readonly _provider: vscode.SelectionRangeProvider
|
||||
private readonly _provider: vscode.SelectionRangeProvider,
|
||||
private readonly _logService: ILogService
|
||||
) { }
|
||||
|
||||
provideSelectionRanges(resource: URI, pos: IPosition[], token: CancellationToken): Promise<modes.SelectionRange[][]> {
|
||||
|
@ -1004,7 +1003,7 @@ class SelectionRangeAdapter {
|
|||
return [];
|
||||
}
|
||||
if (allProviderRanges.length !== positions.length) {
|
||||
console.warn('BAD selection ranges, provider must return ranges for each position');
|
||||
this._logService.warn('BAD selection ranges, provider must return ranges for each position');
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -1413,7 +1412,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
|||
// --- navigate types
|
||||
|
||||
registerWorkspaceSymbolProvider(extension: IExtensionDescription, provider: vscode.WorkspaceSymbolProvider): vscode.Disposable {
|
||||
const handle = this._addNewAdapter(new NavigateTypeAdapter(provider), extension);
|
||||
const handle = this._addNewAdapter(new NavigateTypeAdapter(provider, this._logService), extension);
|
||||
this._proxy.$registerNavigateTypeSupport(handle);
|
||||
return this._createDisposable(handle);
|
||||
}
|
||||
|
@ -1433,7 +1432,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
|||
// --- rename
|
||||
|
||||
registerRenameProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable {
|
||||
const handle = this._addNewAdapter(new RenameAdapter(this._documents, provider), extension);
|
||||
const handle = this._addNewAdapter(new RenameAdapter(this._documents, provider, this._logService), extension);
|
||||
this._proxy.$registerRenameSupport(handle, this._transformDocumentSelector(selector), RenameAdapter.supportsResolving(provider));
|
||||
return this._createDisposable(handle);
|
||||
}
|
||||
|
@ -1449,7 +1448,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
|||
// --- suggestion
|
||||
|
||||
registerCompletionItemProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable {
|
||||
const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider), extension);
|
||||
const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider, this._logService), extension);
|
||||
this._proxy.$registerSuggestSupport(handle, this._transformDocumentSelector(selector), triggerCharacters, SuggestAdapter.supportsResolving(provider), extension.identifier);
|
||||
return this._createDisposable(handle);
|
||||
}
|
||||
|
@ -1533,7 +1532,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
|||
// --- smart select
|
||||
|
||||
registerSelectionRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.SelectionRangeProvider): vscode.Disposable {
|
||||
const handle = this._addNewAdapter(new SelectionRangeAdapter(this._documents, provider), extension);
|
||||
const handle = this._addNewAdapter(new SelectionRangeAdapter(this._documents, provider, this._logService), extension);
|
||||
this._proxy.$registerSelectionRangeProvider(handle, this._transformDocumentSelector(selector));
|
||||
return this._createDisposable(handle);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import Severity from 'vs/base/common/severity';
|
|||
import * as vscode from 'vscode';
|
||||
import { MainContext, MainThreadMessageServiceShape, MainThreadMessageOptions, IMainContext } from './extHost.protocol';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
function isMessageItem(item: any): item is vscode.MessageItem {
|
||||
return item && item.title;
|
||||
|
@ -16,7 +17,10 @@ export class ExtHostMessageService {
|
|||
|
||||
private _proxy: MainThreadMessageServiceShape;
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
@ILogService private readonly _logService: ILogService
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadMessageService);
|
||||
}
|
||||
|
||||
|
@ -45,7 +49,7 @@ export class ExtHostMessageService {
|
|||
let { title, isCloseAffordance } = command;
|
||||
commands.push({ title, isCloseAffordance: !!isCloseAffordance, handle });
|
||||
} else {
|
||||
console.warn('Invalid message item:', command);
|
||||
this._logService.warn('Invalid message item:', command);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData
|
|||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { platform } from 'vs/base/common/process';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
|
||||
interface LoadFunction {
|
||||
|
@ -41,7 +42,8 @@ export abstract class RequireInterceptor {
|
|||
@IInstantiationService private readonly _instaService: IInstantiationService,
|
||||
@IExtHostConfiguration private readonly _extHostConfiguration: IExtHostConfiguration,
|
||||
@IExtHostExtensionService private readonly _extHostExtensionService: IExtHostExtensionService,
|
||||
@IExtHostInitDataService private readonly _initData: IExtHostInitDataService
|
||||
@IExtHostInitDataService private readonly _initData: IExtHostInitDataService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
) {
|
||||
this._factories = new Map<string, INodeModuleFactory>();
|
||||
this._alternatives = [];
|
||||
|
@ -54,7 +56,7 @@ export abstract class RequireInterceptor {
|
|||
const configProvider = await this._extHostConfiguration.getConfigProvider();
|
||||
const extensionPaths = await this._extHostExtensionService.getExtensionPathIndex();
|
||||
|
||||
this.register(new VSCodeNodeModuleFactory(this._apiFactory, extensionPaths, this._extensionRegistry, configProvider));
|
||||
this.register(new VSCodeNodeModuleFactory(this._apiFactory, extensionPaths, this._extensionRegistry, configProvider, this._logService));
|
||||
this.register(this._instaService.createInstance(KeytarNodeModuleFactory));
|
||||
if (this._initData.remote.isRemote) {
|
||||
this.register(this._instaService.createInstance(OpenNodeModuleFactory, extensionPaths, this._initData.environment.appUriScheme));
|
||||
|
@ -91,7 +93,8 @@ class VSCodeNodeModuleFactory implements INodeModuleFactory {
|
|||
private readonly _apiFactory: IExtensionApiFactory,
|
||||
private readonly _extensionPaths: TernarySearchTree<IExtensionDescription>,
|
||||
private readonly _extensionRegistry: ExtensionDescriptionRegistry,
|
||||
private readonly _configProvider: ExtHostConfigProvider
|
||||
private readonly _configProvider: ExtHostConfigProvider,
|
||||
private readonly _logService: ILogService,
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -112,7 +115,7 @@ class VSCodeNodeModuleFactory implements INodeModuleFactory {
|
|||
if (!this._defaultApiImpl) {
|
||||
let extensionPathsPretty = '';
|
||||
this._extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.identifier.value}\n`);
|
||||
console.warn(`Could not identify extension for 'vscode' require call from ${parent.fsPath}. These are the extension path mappings: \n${extensionPathsPretty}`);
|
||||
this._logService.warn(`Could not identify extension for 'vscode' require call from ${parent.fsPath}. These are the extension path mappings: \n${extensionPathsPretty}`);
|
||||
this._defaultApiImpl = this._apiFactory(nullExtensionDescription, this._extensionRegistry, this._configProvider);
|
||||
}
|
||||
return this._defaultApiImpl;
|
||||
|
|
|
@ -16,6 +16,7 @@ import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/we
|
|||
import * as vscode from 'vscode';
|
||||
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewStateData } from './extHost.protocol';
|
||||
import { Disposable as VSCodeDisposable } from './extHostTypes';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
|
||||
type IconPath = URI | { light: URI, dark: URI };
|
||||
|
||||
|
@ -113,7 +114,8 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
|||
|
||||
readonly _onDidChangeViewStateEmitter = this._register(new Emitter<vscode.WebviewPanelOnDidChangeViewStateEvent>());
|
||||
public readonly onDidChangeViewState: Event<vscode.WebviewPanelOnDidChangeViewStateEvent> = this._onDidChangeViewStateEmitter.event;
|
||||
_capabilities: vscode.WebviewEditorCapabilities;
|
||||
|
||||
public _capabilities?: vscode.WebviewEditorCapabilities;
|
||||
|
||||
constructor(
|
||||
handle: WebviewPanelHandle,
|
||||
|
@ -245,7 +247,7 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
|||
}
|
||||
|
||||
_undoEdits(edits: string[]): void {
|
||||
this._capabilities.editingCapability?.undoEdits(edits);
|
||||
assertIsDefined(this._capabilities).editingCapability?.undoEdits(edits);
|
||||
}
|
||||
|
||||
private assertNotDisposed() {
|
||||
|
|
|
@ -11,6 +11,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
|
|||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export class ExtensionStoragePaths implements IExtensionStoragePaths {
|
||||
|
||||
|
@ -22,7 +23,10 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths {
|
|||
readonly whenReady: Promise<string | undefined>;
|
||||
private _value?: string;
|
||||
|
||||
constructor(@IExtHostInitDataService initData: IExtHostInitDataService) {
|
||||
constructor(
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
) {
|
||||
this._workspace = withNullAsUndefined(initData.workspace);
|
||||
this._environment = initData.environment;
|
||||
this.whenReady = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
|
||||
|
@ -69,7 +73,7 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths {
|
|||
return storagePath;
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this._logService.error(e);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,6 +112,8 @@ export abstract class BaseEditor extends Panel implements IEditor {
|
|||
this.createEditor(parent);
|
||||
}
|
||||
|
||||
onHide() { }
|
||||
|
||||
/**
|
||||
* Called to create the editor in the parent HTMLElement.
|
||||
*/
|
||||
|
|
|
@ -208,6 +208,7 @@ export class EditorControl extends Disposable {
|
|||
if (controlInstanceContainer) {
|
||||
this.parent.removeChild(controlInstanceContainer);
|
||||
hide(controlInstanceContainer);
|
||||
this._activeControl.onHide();
|
||||
}
|
||||
|
||||
// Indicate to editor control
|
||||
|
|
|
@ -240,6 +240,9 @@ export class ContributableViewsModel extends Disposable {
|
|||
private _onDidChangeViewState = this._register(new Emitter<IViewDescriptorRef>());
|
||||
protected readonly onDidChangeViewState: Event<IViewDescriptorRef> = this._onDidChangeViewState.event;
|
||||
|
||||
private _onDidChangeActiveViews = this._register(new Emitter<IViewDescriptor[]>());
|
||||
readonly onDidChangeActiveViews: Event<IViewDescriptor[]> = this._onDidChangeActiveViews.event;
|
||||
|
||||
constructor(
|
||||
container: ViewContainer,
|
||||
viewsService: IViewsService,
|
||||
|
@ -469,6 +472,8 @@ export class ContributableViewsModel extends Disposable {
|
|||
if (toAdd.length) {
|
||||
this._onDidAdd.fire(toAdd);
|
||||
}
|
||||
|
||||
this._onDidChangeActiveViews.fire(this.viewDescriptors);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import { DefaultPanelDndController } from 'vs/base/browser/ui/splitview/panelvie
|
|||
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { localize } from 'vs/nls';
|
||||
|
@ -112,7 +112,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
|
|||
}));
|
||||
|
||||
result.push(...viewToggleActions);
|
||||
const parentActions = super.getContextMenuActions();
|
||||
const parentActions = this.getViewletContextMenuActions();
|
||||
if (viewToggleActions.length && parentActions.length) {
|
||||
result.push(new Separator());
|
||||
}
|
||||
|
@ -121,6 +121,10 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
|
|||
return result;
|
||||
}
|
||||
|
||||
protected getViewletContextMenuActions() {
|
||||
return super.getContextMenuActions();
|
||||
}
|
||||
|
||||
setVisible(visible: boolean): void {
|
||||
super.setVisible(visible);
|
||||
this.panels.filter(view => view.isVisible() !== visible)
|
||||
|
@ -264,7 +268,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
|
|||
});
|
||||
}
|
||||
|
||||
private toggleViewVisibility(viewId: string): void {
|
||||
protected toggleViewVisibility(viewId: string): void {
|
||||
const visible = !this.viewsModel.isVisible(viewId);
|
||||
type ViewsToggleVisibilityClassification = {
|
||||
viewId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
|
@ -321,6 +325,109 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
|
|||
}
|
||||
}
|
||||
|
||||
export abstract class FilterViewContainerViewlet extends ViewContainerViewlet {
|
||||
private constantViewDescriptors: Map<string, IViewDescriptor> = new Map();
|
||||
private allViews: Map<string, Map<string, IViewDescriptor>> = new Map();
|
||||
private filterValue: string;
|
||||
|
||||
protected onDidChangeFilterValue: Emitter<string> = new Emitter();
|
||||
|
||||
constructor(
|
||||
viewletId: string,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService
|
||||
) {
|
||||
super(viewletId, `${viewletId}.state`, false, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
|
||||
this._register(this.onDidChangeFilterValue.event(newFilterValue => {
|
||||
this.filterValue = newFilterValue;
|
||||
this.onFilterChanged(newFilterValue);
|
||||
}));
|
||||
|
||||
this._register(this.viewsModel.onDidChangeActiveViews((viewDescriptors) => {
|
||||
viewDescriptors.forEach(descriptor => {
|
||||
let filterOnValue = this.getFilterOn(descriptor);
|
||||
if (!filterOnValue) {
|
||||
return;
|
||||
}
|
||||
if (!this.allViews.has(filterOnValue)) {
|
||||
this.allViews.set(filterOnValue, new Map());
|
||||
}
|
||||
this.allViews.get(filterOnValue)!.set(descriptor.id, descriptor);
|
||||
if (filterOnValue !== this.filterValue) {
|
||||
this.viewsModel.setVisible(descriptor.id, false);
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
protected addConstantViewDescriptors(constantViewDescriptors: IViewDescriptor[]) {
|
||||
constantViewDescriptors.forEach(viewDescriptor => this.constantViewDescriptors.set(viewDescriptor.id, viewDescriptor));
|
||||
}
|
||||
|
||||
protected abstract getFilterOn(viewDescriptor: IViewDescriptor): string | undefined;
|
||||
|
||||
private onFilterChanged(newFilterValue: string) {
|
||||
this.getViewsNotForTarget(newFilterValue).forEach(item => this.viewsModel.setVisible(item.id, false));
|
||||
this.getViewsForTarget(newFilterValue).forEach(item => this.viewsModel.setVisible(item.id, true));
|
||||
}
|
||||
|
||||
getContextMenuActions(): IAction[] {
|
||||
const result: IAction[] = [];
|
||||
let viewToggleActions: IAction[] = Array.from(this.constantViewDescriptors.values()).map(viewDescriptor => (<IAction>{
|
||||
id: `${viewDescriptor.id}.toggleVisibility`,
|
||||
label: viewDescriptor.name,
|
||||
checked: this.viewsModel.isVisible(viewDescriptor.id),
|
||||
enabled: viewDescriptor.canToggleVisibility,
|
||||
run: () => this.toggleViewVisibility(viewDescriptor.id)
|
||||
}));
|
||||
|
||||
result.push(...viewToggleActions);
|
||||
const parentActions = this.getViewletContextMenuActions();
|
||||
if (viewToggleActions.length && parentActions.length) {
|
||||
result.push(new Separator());
|
||||
}
|
||||
|
||||
result.push(...parentActions);
|
||||
return result;
|
||||
}
|
||||
|
||||
private getViewsForTarget(target: string): IViewDescriptor[] {
|
||||
return this.allViews.has(target) ? Array.from(this.allViews.get(target)!.values()) : [];
|
||||
}
|
||||
|
||||
private getViewsNotForTarget(target: string): IViewDescriptor[] {
|
||||
const iterable = this.allViews.keys();
|
||||
let key = iterable.next();
|
||||
let views: IViewDescriptor[] = [];
|
||||
while (!key.done) {
|
||||
if (key.value !== target) {
|
||||
views = views.concat(this.getViewsForTarget(key.value));
|
||||
}
|
||||
key = iterable.next();
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPanel[] {
|
||||
const panels: ViewletPanel[] = super.onDidAddViews(added);
|
||||
for (let i = 0; i < added.length; i++) {
|
||||
if (this.constantViewDescriptors.has(added[i].viewDescriptor.id)) {
|
||||
panels[i].setExpanded(false);
|
||||
}
|
||||
}
|
||||
return panels;
|
||||
}
|
||||
|
||||
abstract getTitle(): string;
|
||||
}
|
||||
|
||||
export class FileIconThemableWorkbenchTree extends WorkbenchTree {
|
||||
|
||||
constructor(
|
||||
|
|
|
@ -593,13 +593,13 @@ export const WINDOW_ACTIVE_BORDER = registerColor('window.activeBorder', {
|
|||
dark: null,
|
||||
light: null,
|
||||
hc: contrastBorder
|
||||
}, nls.localize('windowActiveBorder', "The color used for the border of the window when it is active. Only supported in the desktop client."));
|
||||
}, nls.localize('windowActiveBorder', "The color used for the border of the window when it is active. Only supported in the desktop client when using the custom title bar."));
|
||||
|
||||
export const WINDOW_INACTIVE_BORDER = registerColor('window.inactiveBorder', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: contrastBorder
|
||||
}, nls.localize('windowInactiveBorder', "The color used for the border of the window when it is inactive. Only supported in the desktop client."));
|
||||
}, nls.localize('windowInactiveBorder', "The color used for the border of the window when it is inactive. Only supported in the desktop client when using the custom title bar."));
|
||||
|
||||
/**
|
||||
* Base class for all themable workbench components.
|
||||
|
|
|
@ -247,6 +247,10 @@ export class SuggestEnabledInput extends Widget implements IThemable {
|
|||
}
|
||||
}
|
||||
|
||||
public onHide(): void {
|
||||
this.inputWidget.onHide();
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): void {
|
||||
this.inputWidget.layout(dimension);
|
||||
this.placeholderText.style.width = `${dimension.width}px`;
|
||||
|
|
|
@ -18,6 +18,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
|
|||
import { IWorkspaceProvider, IWorkspace } from 'vs/workbench/services/host/browser/browserHostService';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient implements IExtensionHostDebugService {
|
||||
|
||||
|
@ -25,7 +26,8 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i
|
|||
|
||||
constructor(
|
||||
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
||||
@ILogService logService: ILogService
|
||||
) {
|
||||
const connection = remoteAgentService.getConnection();
|
||||
let channel: IChannel;
|
||||
|
@ -34,7 +36,7 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i
|
|||
} else {
|
||||
channel = { call: async () => undefined, listen: () => Event.None } as any;
|
||||
// TODO@weinand TODO@isidorn fallback?
|
||||
console.warn('Extension Host Debugging not available due to missing connection.');
|
||||
logService.warn('Extension Host Debugging not available due to missing connection.');
|
||||
}
|
||||
|
||||
super(channel);
|
||||
|
@ -43,7 +45,7 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i
|
|||
this.workspaceProvider = environmentService.options.workspaceProvider;
|
||||
} else {
|
||||
this.workspaceProvider = { open: async () => undefined, workspace: undefined };
|
||||
console.warn('Extension Host Debugging not available due to missing workspace provider.');
|
||||
logService.warn('Extension Host Debugging not available due to missing workspace provider.');
|
||||
}
|
||||
|
||||
// Reload window on reload request
|
||||
|
|
|
@ -1093,6 +1093,9 @@ export class DebugModel implements IDebugModel {
|
|||
}
|
||||
|
||||
element.enabled = enable;
|
||||
if (enable) {
|
||||
this.breakpointsActivated = true;
|
||||
}
|
||||
|
||||
this._onDidChangeBreakpoints.fire({ changed: changed });
|
||||
}
|
||||
|
@ -1119,6 +1122,9 @@ export class DebugModel implements IDebugModel {
|
|||
}
|
||||
dbp.enabled = enable;
|
||||
});
|
||||
if (enable) {
|
||||
this.breakpointsActivated = true;
|
||||
}
|
||||
|
||||
this._onDidChangeBreakpoints.fire({ changed: changed });
|
||||
}
|
||||
|
|
|
@ -452,10 +452,8 @@ export class ExtensionEditor extends BaseEditor {
|
|||
private setSubText(extension: IExtension, reloadAction: ReloadAction, template: IExtensionEditorTemplate): void {
|
||||
hide(template.subtextContainer);
|
||||
|
||||
const ignoreAction = this.instantiationService.createInstance(IgnoreExtensionRecommendationAction);
|
||||
const undoIgnoreAction = this.instantiationService.createInstance(UndoIgnoreExtensionRecommendationAction);
|
||||
ignoreAction.extension = extension;
|
||||
undoIgnoreAction.extension = extension;
|
||||
const ignoreAction = this.instantiationService.createInstance(IgnoreExtensionRecommendationAction, extension);
|
||||
const undoIgnoreAction = this.instantiationService.createInstance(UndoIgnoreExtensionRecommendationAction, extension);
|
||||
ignoreAction.enabled = false;
|
||||
undoIgnoreAction.enabled = false;
|
||||
|
||||
|
|
|
@ -135,9 +135,9 @@ function getRelativeDateLabel(date: Date): string {
|
|||
}
|
||||
|
||||
export abstract class ExtensionAction extends Action implements IExtensionContainer {
|
||||
private _extension: IExtension;
|
||||
get extension(): IExtension { return this._extension; }
|
||||
set extension(extension: IExtension) { this._extension = extension; this.update(); }
|
||||
private _extension: IExtension | null = null;
|
||||
get extension(): IExtension | null { return this._extension; }
|
||||
set extension(extension: IExtension | null) { this._extension = extension; this.update(); }
|
||||
abstract update(): void;
|
||||
}
|
||||
|
||||
|
@ -150,7 +150,7 @@ export class InstallAction extends ExtensionAction {
|
|||
private static readonly InstallingClass = 'extension-action install installing';
|
||||
|
||||
|
||||
private _manifest: IExtensionManifest | null;
|
||||
private _manifest: IExtensionManifest | null = null;
|
||||
set manifest(manifest: IExtensionManifest) {
|
||||
this._manifest = manifest;
|
||||
this.updateLabel();
|
||||
|
@ -193,6 +193,9 @@ export class InstallAction extends ExtensionAction {
|
|||
}
|
||||
|
||||
private updateLabel(): void {
|
||||
if (!this.extension) {
|
||||
return;
|
||||
}
|
||||
if (this.extension.state === ExtensionState.Installing) {
|
||||
this.label = InstallAction.INSTALLING_LABEL;
|
||||
this.tooltip = InstallAction.INSTALLING_LABEL;
|
||||
|
@ -214,6 +217,9 @@ export class InstallAction extends ExtensionAction {
|
|||
}
|
||||
|
||||
async run(): Promise<any> {
|
||||
if (!this.extension) {
|
||||
return;
|
||||
}
|
||||
this.extensionsWorkbenchService.open(this.extension);
|
||||
|
||||
alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName));
|
||||
|
@ -303,7 +309,7 @@ export abstract class InstallInOtherServerAction extends ExtensionAction {
|
|||
// disabled by extension kind or it is a language pack extension
|
||||
&& (this.extension.enablementState === EnablementState.DisabledByExtensionKind || isLanguagePackExtension(this.extension.local.manifest))
|
||||
) {
|
||||
const extensionInOtherServer = this.extensionsWorkbenchService.installed.filter(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.server)[0];
|
||||
const extensionInOtherServer = this.extensionsWorkbenchService.installed.filter(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server === this.server)[0];
|
||||
if (extensionInOtherServer) {
|
||||
// Getting installed in other server
|
||||
if (extensionInOtherServer.state === ExtensionState.Installing && !extensionInOtherServer.local) {
|
||||
|
@ -320,6 +326,9 @@ export abstract class InstallInOtherServerAction extends ExtensionAction {
|
|||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
if (!this.extension) {
|
||||
return;
|
||||
}
|
||||
if (this.server) {
|
||||
this.extensionsWorkbenchService.open(this.extension);
|
||||
alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName));
|
||||
|
@ -412,11 +421,14 @@ export class UninstallAction extends ExtensionAction {
|
|||
this.enabled = true;
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
async run(): Promise<any> {
|
||||
if (!this.extension) {
|
||||
return;
|
||||
}
|
||||
alert(localize('uninstallExtensionStart', "Uninstalling extension {0} started.", this.extension.displayName));
|
||||
|
||||
return this.extensionsWorkbenchService.uninstall(this.extension).then(() => {
|
||||
alert(localize('uninstallExtensionComplete', "Please reload Visual Studio Code to complete the uninstallation of the extension {0}.", this.extension.displayName));
|
||||
alert(localize('uninstallExtensionComplete', "Please reload Visual Studio Code to complete the uninstallation of the extension {0}.", this.extension!.displayName));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -527,14 +539,17 @@ export class UpdateAction extends ExtensionAction {
|
|||
this.label = this.extension.outdated ? this.getUpdateLabel(this.extension.latestVersion) : this.getUpdateLabel();
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
async run(): Promise<any> {
|
||||
if (!this.extension) {
|
||||
return;
|
||||
}
|
||||
alert(localize('updateExtensionStart', "Updating extension {0} to version {1} started.", this.extension.displayName, this.extension.latestVersion));
|
||||
return this.install(this.extension);
|
||||
}
|
||||
|
||||
private install(extension: IExtension): Promise<void> {
|
||||
return this.extensionsWorkbenchService.install(extension).then(() => {
|
||||
alert(localize('updateExtensionComplete', "Updating extension {0} to version {1} completed.", this.extension.displayName, this.extension.latestVersion));
|
||||
alert(localize('updateExtensionComplete', "Updating extension {0} to version {1} completed.", extension.displayName, extension.latestVersion));
|
||||
}, err => {
|
||||
if (!extension.gallery) {
|
||||
return this.notificationService.error(err);
|
||||
|
@ -557,8 +572,6 @@ interface IExtensionActionViewItemOptions extends IActionViewItemOptions {
|
|||
|
||||
export class ExtensionActionViewItem extends ActionViewItem {
|
||||
|
||||
protected options: IExtensionActionViewItemOptions;
|
||||
|
||||
constructor(context: any, action: IAction, options: IExtensionActionViewItemOptions = {}) {
|
||||
super(context, action, options);
|
||||
}
|
||||
|
@ -566,14 +579,14 @@ export class ExtensionActionViewItem extends ActionViewItem {
|
|||
updateEnabled(): void {
|
||||
super.updateEnabled();
|
||||
|
||||
if (this.label && this.options.tabOnlyOnFocus && this.getAction().enabled && !this._hasFocus) {
|
||||
if (this.label && (<IExtensionActionViewItemOptions>this.options).tabOnlyOnFocus && this.getAction().enabled && !this._hasFocus) {
|
||||
DOM.removeTabIndexAndUpdateFocus(this.label);
|
||||
}
|
||||
}
|
||||
|
||||
private _hasFocus: boolean;
|
||||
private _hasFocus: boolean = false;
|
||||
setFocus(value: boolean): void {
|
||||
if (!this.options.tabOnlyOnFocus || this._hasFocus === value) {
|
||||
if (!(<IExtensionActionViewItemOptions>this.options).tabOnlyOnFocus || this._hasFocus === value) {
|
||||
return;
|
||||
}
|
||||
this._hasFocus = value;
|
||||
|
@ -600,7 +613,7 @@ export abstract class ExtensionDropDownAction extends ExtensionAction {
|
|||
super(id, label, cssClass, enabled);
|
||||
}
|
||||
|
||||
private _actionViewItem: DropDownMenuActionViewItem;
|
||||
private _actionViewItem: DropDownMenuActionViewItem | null = null;
|
||||
createActionViewItem(): DropDownMenuActionViewItem {
|
||||
this._actionViewItem = this.instantiationService.createInstance(DropDownMenuActionViewItem, this, this.tabOnlyOnFocus);
|
||||
return this._actionViewItem;
|
||||
|
@ -692,13 +705,14 @@ export class ManageExtensionAction extends ExtensionDropDownAction {
|
|||
groups.push([this.instantiationService.createInstance(UninstallAction)]);
|
||||
groups.push([this.instantiationService.createInstance(InstallAnotherVersionAction)]);
|
||||
|
||||
const extensionActions: ExtensionAction[] = [this.instantiationService.createInstance(ExtensionInfoAction)];
|
||||
if (this.extension.local && this.extension.local.manifest.contributes && this.extension.local.manifest.contributes.configuration) {
|
||||
extensionActions.push(this.instantiationService.createInstance(ExtensionSettingsAction));
|
||||
if (this.extension) {
|
||||
const extensionActions: ExtensionAction[] = [this.instantiationService.createInstance(ExtensionInfoAction)];
|
||||
if (this.extension.local && this.extension.local.manifest.contributes && this.extension.local.manifest.contributes.configuration) {
|
||||
extensionActions.push(this.instantiationService.createInstance(ExtensionSettingsAction));
|
||||
}
|
||||
groups.push(extensionActions);
|
||||
}
|
||||
|
||||
groups.push(extensionActions);
|
||||
|
||||
groups.forEach(group => group.forEach(extensionAction => extensionAction.extension = this.extension));
|
||||
|
||||
return groups;
|
||||
|
@ -742,7 +756,7 @@ export class InstallAnotherVersionAction extends ExtensionAction {
|
|||
}
|
||||
|
||||
update(): void {
|
||||
this.enabled = this.extension && !!this.extension.gallery;
|
||||
this.enabled = !!this.extension && !!this.extension.gallery;
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
|
@ -752,19 +766,19 @@ export class InstallAnotherVersionAction extends ExtensionAction {
|
|||
return this.quickInputService.pick(this.getVersionEntries(), { placeHolder: localize('selectVersion', "Select Version to Install"), matchOnDetail: true })
|
||||
.then(pick => {
|
||||
if (pick) {
|
||||
if (this.extension.version === pick.id) {
|
||||
if (this.extension!.version === pick.id) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
const promise: Promise<any> = pick.latest ? this.extensionsWorkbenchService.install(this.extension) : this.extensionsWorkbenchService.installVersion(this.extension, pick.id);
|
||||
const promise: Promise<any> = pick.latest ? this.extensionsWorkbenchService.install(this.extension!) : this.extensionsWorkbenchService.installVersion(this.extension!, pick.id);
|
||||
return promise
|
||||
.then(null, err => {
|
||||
if (!this.extension.gallery) {
|
||||
if (!this.extension!.gallery) {
|
||||
return this.notificationService.error(err);
|
||||
}
|
||||
|
||||
console.error(err);
|
||||
|
||||
return promptDownloadManually(this.extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", this.extension.identifier.id), err, this.instantiationService, this.notificationService, this.openerService, this.productService);
|
||||
return promptDownloadManually(this.extension!.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", this.extension!.identifier.id), err, this.instantiationService, this.notificationService, this.openerService, this.productService);
|
||||
});
|
||||
}
|
||||
return null;
|
||||
|
@ -772,8 +786,8 @@ export class InstallAnotherVersionAction extends ExtensionAction {
|
|||
}
|
||||
|
||||
private getVersionEntries(): Promise<(IQuickPickItem & { latest: boolean, id: string })[]> {
|
||||
return this.extensionGalleryService.getAllVersions(this.extension.gallery!, true)
|
||||
.then(allVersions => allVersions.map((v, i) => ({ id: v.version, label: v.version, description: `${getRelativeDateLabel(new Date(Date.parse(v.date)))}${v.version === this.extension.version ? ` (${localize('current', "Current")})` : ''}`, latest: i === 0 })));
|
||||
return this.extensionGalleryService.getAllVersions(this.extension!.gallery!, true)
|
||||
.then(allVersions => allVersions.map((v, i) => ({ id: v.version, label: v.version, description: `${getRelativeDateLabel(new Date(Date.parse(v.date)))}${v.version === this.extension!.version ? ` (${localize('current', "Current")})` : ''}`, latest: i === 0 })));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -793,7 +807,10 @@ export class ExtensionInfoAction extends ExtensionAction {
|
|||
this.enabled = !!this.extension;
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
async run(): Promise<any> {
|
||||
if (!this.extension) {
|
||||
return;
|
||||
}
|
||||
|
||||
const name = localize('extensionInfoName', 'Name: {0}', this.extension.displayName);
|
||||
const id = localize('extensionInfoId', 'Id: {0}', this.extension.identifier.id);
|
||||
|
@ -823,7 +840,11 @@ export class ExtensionSettingsAction extends ExtensionAction {
|
|||
update(): void {
|
||||
this.enabled = !!this.extension;
|
||||
}
|
||||
run(): Promise<any> {
|
||||
|
||||
async run(): Promise<any> {
|
||||
if (!this.extension) {
|
||||
return;
|
||||
}
|
||||
this.preferencesService.openSettings(false, `@ext:${this.extension.identifier.id}`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
@ -851,7 +872,10 @@ export class EnableForWorkspaceAction extends ExtensionAction {
|
|||
}
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
async run(): Promise<any> {
|
||||
if (!this.extension) {
|
||||
return;
|
||||
}
|
||||
return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.EnabledWorkspace);
|
||||
}
|
||||
}
|
||||
|
@ -878,7 +902,10 @@ export class EnableGloballyAction extends ExtensionAction {
|
|||
}
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
async run(): Promise<any> {
|
||||
if (!this.extension) {
|
||||
return;
|
||||
}
|
||||
return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.EnabledGlobally);
|
||||
}
|
||||
}
|
||||
|
@ -899,14 +926,17 @@ export class DisableForWorkspaceAction extends ExtensionAction {
|
|||
|
||||
update(): void {
|
||||
this.enabled = false;
|
||||
if (this.extension && this.extension.local && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension.identifier) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) {
|
||||
if (this.extension && this.extension.local && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) {
|
||||
this.enabled = this.extension.state === ExtensionState.Installed
|
||||
&& (this.extension.enablementState === EnablementState.EnabledGlobally || this.extension.enablementState === EnablementState.EnabledWorkspace)
|
||||
&& this.extensionEnablementService.canChangeEnablement(this.extension.local);
|
||||
}
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
async run(): Promise<any> {
|
||||
if (!this.extension) {
|
||||
return;
|
||||
}
|
||||
return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.DisabledWorkspace);
|
||||
}
|
||||
}
|
||||
|
@ -926,14 +956,17 @@ export class DisableGloballyAction extends ExtensionAction {
|
|||
|
||||
update(): void {
|
||||
this.enabled = false;
|
||||
if (this.extension && this.extension.local && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension.identifier))) {
|
||||
if (this.extension && this.extension.local && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))) {
|
||||
this.enabled = this.extension.state === ExtensionState.Installed
|
||||
&& (this.extension.enablementState === EnablementState.EnabledGlobally || this.extension.enablementState === EnablementState.EnabledWorkspace)
|
||||
&& this.extensionEnablementService.canChangeEnablement(this.extension.local);
|
||||
}
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
async run(): Promise<any> {
|
||||
if (!this.extension) {
|
||||
return;
|
||||
}
|
||||
return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.DisabledGlobally);
|
||||
}
|
||||
}
|
||||
|
@ -1190,11 +1223,11 @@ export class ReloadAction extends ExtensionAction {
|
|||
}
|
||||
|
||||
private computeReloadState(): void {
|
||||
if (!this._runningExtensions) {
|
||||
if (!this._runningExtensions || !this.extension) {
|
||||
return;
|
||||
}
|
||||
const isUninstalled = this.extension.state === ExtensionState.Uninstalled;
|
||||
const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension.identifier))[0];
|
||||
const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))[0];
|
||||
const isSameExtensionRunning = runningExtension && this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation);
|
||||
|
||||
if (isUninstalled) {
|
||||
|
@ -1242,7 +1275,7 @@ export class ReloadAction extends ExtensionAction {
|
|||
|
||||
const otherServer = this.extension.server ? this.extension.server === this.extensionManagementServerService.localExtensionManagementServer ? this.extensionManagementServerService.remoteExtensionManagementServer : this.extensionManagementServerService.localExtensionManagementServer : null;
|
||||
if (otherServer && this.extension.enablementState === EnablementState.DisabledByExtensionKind) {
|
||||
const extensionInOtherServer = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === otherServer)[0];
|
||||
const extensionInOtherServer = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server === otherServer)[0];
|
||||
// Same extension in other server exists and
|
||||
if (extensionInOtherServer && extensionInOtherServer.local && this.extensionEnablementService.isEnabled(extensionInOtherServer.local)) {
|
||||
this.enabled = true;
|
||||
|
@ -1300,7 +1333,7 @@ export class SetColorThemeAction extends ExtensionAction {
|
|||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
let extensionThemes = SetColorThemeAction.getColorThemes(this.colorThemes, this.extension);
|
||||
let extensionThemes = SetColorThemeAction.getColorThemes(this.colorThemes, this.extension!);
|
||||
const currentTheme = this.colorThemes.filter(t => t.settingsId === this.configurationService.getValue(COLOR_THEME_SETTING))[0];
|
||||
showCurrentTheme = showCurrentTheme || extensionThemes.some(t => t.id === currentTheme.id);
|
||||
if (showCurrentTheme) {
|
||||
|
@ -1366,7 +1399,7 @@ export class SetFileIconThemeAction extends ExtensionAction {
|
|||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
let extensionThemes = SetFileIconThemeAction.getFileIconThemes(this.fileIconThemes, this.extension);
|
||||
let extensionThemes = SetFileIconThemeAction.getFileIconThemes(this.fileIconThemes, this.extension!);
|
||||
const currentTheme = this.fileIconThemes.filter(t => t.settingsId === this.configurationService.getValue(ICON_THEME_SETTING))[0] || this.workbenchThemeService.getFileIconTheme();
|
||||
showCurrentTheme = showCurrentTheme || extensionThemes.some(t => t.id === currentTheme.id);
|
||||
if (showCurrentTheme) {
|
||||
|
@ -1719,9 +1752,8 @@ export class IgnoreExtensionRecommendationAction extends Action {
|
|||
|
||||
private static readonly Class = 'extension-action ignore';
|
||||
|
||||
extension: IExtension;
|
||||
|
||||
constructor(
|
||||
private readonly extension: IExtension,
|
||||
@IExtensionTipsService private readonly extensionsTipsService: IExtensionTipsService,
|
||||
) {
|
||||
super(IgnoreExtensionRecommendationAction.ID, 'Ignore Recommendation');
|
||||
|
@ -1743,9 +1775,8 @@ export class UndoIgnoreExtensionRecommendationAction extends Action {
|
|||
|
||||
private static readonly Class = 'extension-action undo-ignore';
|
||||
|
||||
extension: IExtension;
|
||||
|
||||
constructor(
|
||||
private readonly extension: IExtension,
|
||||
@IExtensionTipsService private readonly extensionsTipsService: IExtensionTipsService,
|
||||
) {
|
||||
super(UndoIgnoreExtensionRecommendationAction.ID, 'Undo');
|
||||
|
@ -2389,9 +2420,9 @@ export class StatusLabelAction extends Action implements IExtensionContainer {
|
|||
private status: ExtensionState | null = null;
|
||||
private enablementState: EnablementState | null = null;
|
||||
|
||||
private _extension: IExtension;
|
||||
get extension(): IExtension { return this._extension; }
|
||||
set extension(extension: IExtension) {
|
||||
private _extension: IExtension | null = null;
|
||||
get extension(): IExtension | null { return this._extension; }
|
||||
set extension(extension: IExtension | null) {
|
||||
if (!(this._extension && extension && areSameExtensions(this._extension.identifier, extension.identifier))) {
|
||||
// Different extension. Reset
|
||||
this.initialStatus = null;
|
||||
|
@ -2432,21 +2463,21 @@ export class StatusLabelAction extends Action implements IExtensionContainer {
|
|||
|
||||
const runningExtensions = await this.extensionService.getExtensions();
|
||||
const canAddExtension = () => {
|
||||
const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension.identifier))[0];
|
||||
if (this.extension.local) {
|
||||
if (runningExtension && this.extension.version === runningExtension.version) {
|
||||
const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))[0];
|
||||
if (this.extension!.local) {
|
||||
if (runningExtension && this.extension!.version === runningExtension.version) {
|
||||
return true;
|
||||
}
|
||||
return this.extensionService.canAddExtension(toExtensionDescription(this.extension.local));
|
||||
return this.extensionService.canAddExtension(toExtensionDescription(this.extension!.local));
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const canRemoveExtension = () => {
|
||||
if (this.extension.local) {
|
||||
if (runningExtensions.every(e => !(areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension.identifier) && this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(e.extensionLocation)))) {
|
||||
if (this.extension!.local) {
|
||||
if (runningExtensions.every(e => !(areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier) && this.extension!.server === this.extensionManagementServerService.getExtensionManagementServer(e.extensionLocation)))) {
|
||||
return true;
|
||||
}
|
||||
return this.extensionService.canRemoveExtension(toExtensionDescription(this.extension.local));
|
||||
return this.extensionService.canRemoveExtension(toExtensionDescription(this.extension!.local));
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
@ -2549,7 +2580,7 @@ export class ExtensionToolTipAction extends ExtensionAction {
|
|||
return this.warningAction.tooltip;
|
||||
}
|
||||
if (this.extension && this.extension.local && this.extension.state === ExtensionState.Installed && this._runningExtensions) {
|
||||
const isRunning = this._runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension.identifier));
|
||||
const isRunning = this._runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier));
|
||||
const isEnabled = this.extensionEnablementService.isEnabled(this.extension.local);
|
||||
|
||||
if (isEnabled && isRunning) {
|
||||
|
@ -2623,7 +2654,7 @@ export class SystemDisabledWarningAction extends ExtensionAction {
|
|||
return;
|
||||
}
|
||||
if (isLanguagePackExtension(this.extension.local.manifest)) {
|
||||
if (!this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server !== this.extension.server)) {
|
||||
if (!this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server !== this.extension!.server)) {
|
||||
this.class = `${SystemDisabledWarningAction.INFO_CLASS}`;
|
||||
this.tooltip = this.extension.server === this.extensionManagementServerService.localExtensionManagementServer
|
||||
? localize('Install language pack also in remote server', "Install the language pack extension on '{0}' to enable it also there.", this.extensionManagementServerService.remoteExtensionManagementServer.label)
|
||||
|
@ -2632,13 +2663,13 @@ export class SystemDisabledWarningAction extends ExtensionAction {
|
|||
return;
|
||||
}
|
||||
if (this.extension.enablementState === EnablementState.DisabledByExtensionKind) {
|
||||
if (!this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server !== this.extension.server)) {
|
||||
if (!this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server !== this.extension!.server)) {
|
||||
this.class = `${SystemDisabledWarningAction.WARNING_CLASS}`;
|
||||
const server = this.extensionManagementServerService.localExtensionManagementServer === this.extension.server ? this.extensionManagementServerService.remoteExtensionManagementServer : this.extensionManagementServerService.localExtensionManagementServer;
|
||||
this.tooltip = localize('Install in other server to enable', "Install the extension on '{0}' to enable.", server.label);
|
||||
return;
|
||||
}
|
||||
const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension.identifier))[0];
|
||||
const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))[0];
|
||||
const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation) : null;
|
||||
if (this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer) {
|
||||
this.class = `${SystemDisabledWarningAction.INFO_CLASS}`;
|
||||
|
|
|
@ -501,6 +501,7 @@ suite('ExtensionsTipsService Test', () => {
|
|||
const ignoredExtensionId = 'Some.Extension';
|
||||
instantiationService.stub(IStorageService, <Partial<IStorageService>>{
|
||||
get: (a: string, b: StorageScope, c?: boolean) => a === 'extensionsAssistant/ignored_recommendations' ? '["ms-vscode.vscode"]' : c,
|
||||
getBoolean: (a: string, b: StorageScope, c: boolean) => c,
|
||||
store: (...args: any[]) => {
|
||||
storageSetterTarget(...args);
|
||||
}
|
||||
|
@ -519,7 +520,7 @@ suite('ExtensionsTipsService Test', () => {
|
|||
|
||||
test('ExtensionTipsService: Get file based recommendations from storage (old format)', () => {
|
||||
const storedRecommendations = '["ms-vscode.csharp", "ms-python.python", "ms-vscode.vscode-typescript-tslint-plugin"]';
|
||||
instantiationService.stub(IStorageService, <Partial<IStorageService>>{ get: (a: string, b: StorageScope, c?: string) => a === 'extensionsAssistant/recommendations' ? storedRecommendations : c });
|
||||
instantiationService.stub(IStorageService, <Partial<IStorageService>>{ get: (a: string, b: StorageScope, c?: string) => a === 'extensionsAssistant/recommendations' ? storedRecommendations : c, getBoolean: (a: string, b: StorageScope, c: boolean) => c });
|
||||
|
||||
return setUpFolderWorkspace('myFolder', []).then(() => {
|
||||
testObject = instantiationService.createInstance(ExtensionTipsService);
|
||||
|
@ -538,7 +539,7 @@ suite('ExtensionsTipsService Test', () => {
|
|||
const now = Date.now();
|
||||
const tenDaysOld = 10 * milliSecondsInADay;
|
||||
const storedRecommendations = `{"ms-vscode.csharp": ${now}, "ms-python.python": ${now}, "ms-vscode.vscode-typescript-tslint-plugin": ${now}, "lukehoban.Go": ${tenDaysOld}}`;
|
||||
instantiationService.stub(IStorageService, <Partial<IStorageService>>{ get: (a: string, b: StorageScope, c?: string) => a === 'extensionsAssistant/recommendations' ? storedRecommendations : c });
|
||||
instantiationService.stub(IStorageService, <Partial<IStorageService>>{ get: (a: string, b: StorageScope, c?: string) => a === 'extensionsAssistant/recommendations' ? storedRecommendations : c, getBoolean: (a: string, b: StorageScope, c: boolean) => c });
|
||||
|
||||
return setUpFolderWorkspace('myFolder', []).then(() => {
|
||||
testObject = instantiationService.createInstance(ExtensionTipsService);
|
||||
|
|
|
@ -751,9 +751,9 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
|
|||
if (confirmDragAndDrop) {
|
||||
const confirmation = await this.dialogService.confirm({
|
||||
message: items.length > 1 && items.every(s => s.isRoot) ? localize('confirmRootsMove', "Are you sure you want to change the order of multiple root folders in your workspace?")
|
||||
: items.length > 1 ? getConfirmMessage(localize('confirmMultiMove', "Are you sure you want to move the following {0} files?", items.length), items.map(s => s.resource))
|
||||
: items.length > 1 ? getConfirmMessage(localize('confirmMultiMove', "Are you sure you want to move the following {0} files into '{1}'?", items.length, target.name), items.map(s => s.resource))
|
||||
: items[0].isRoot ? localize('confirmRootMove', "Are you sure you want to change the order of root folder '{0}' in your workspace?", items[0].name)
|
||||
: localize('confirmMove', "Are you sure you want to move '{0}'?", items[0].name),
|
||||
: localize('confirmMove', "Are you sure you want to move '{0}' into '{1}'?", items[0].name, target.name),
|
||||
checkbox: {
|
||||
label: localize('doNotAskAgain', "Do not ask me again")
|
||||
},
|
||||
|
|
|
@ -466,10 +466,14 @@ export class OutlinePanel extends ViewletPanel {
|
|||
}
|
||||
|
||||
const textModel = editor.getModel();
|
||||
const loadingMessage = oldModel && new TimeoutTimer(
|
||||
() => this._showMessage(localize('loading', "Loading document symbols for '{0}'...", basename(textModel.uri))),
|
||||
100
|
||||
);
|
||||
|
||||
let loadingMessage: IDisposable | undefined;
|
||||
if (!oldModel) {
|
||||
loadingMessage = new TimeoutTimer(
|
||||
() => this._showMessage(localize('loading', "Loading document symbols for '{0}'...", basename(textModel.uri))),
|
||||
100
|
||||
);
|
||||
}
|
||||
|
||||
const requestDelay = OutlineModel.getRequestDelay(textModel);
|
||||
this._progressBar.infinite().show(requestDelay);
|
||||
|
|
|
@ -82,7 +82,7 @@ export class KeybindingsSearchWidget extends SearchWidget {
|
|||
|
||||
stopRecordingKeys(): void {
|
||||
this._reset();
|
||||
this.recordDisposables.dispose();
|
||||
this.recordDisposables.clear();
|
||||
}
|
||||
|
||||
setInputValue(value: string): void {
|
||||
|
|
|
@ -65,22 +65,22 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
|||
private _onLayout: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onLayout: Event<void> = this._onLayout.event;
|
||||
|
||||
private keybindingsEditorModel: KeybindingsEditorModel;
|
||||
private keybindingsEditorModel: KeybindingsEditorModel | null = null;
|
||||
|
||||
private headerContainer: HTMLElement;
|
||||
private actionsContainer: HTMLElement;
|
||||
private searchWidget: KeybindingsSearchWidget;
|
||||
private headerContainer!: HTMLElement;
|
||||
private actionsContainer!: HTMLElement;
|
||||
private searchWidget!: KeybindingsSearchWidget;
|
||||
|
||||
private overlayContainer: HTMLElement;
|
||||
private defineKeybindingWidget: DefineKeybindingWidget;
|
||||
private overlayContainer!: HTMLElement;
|
||||
private defineKeybindingWidget!: DefineKeybindingWidget;
|
||||
|
||||
private columnItems: ColumnItem[] = [];
|
||||
private keybindingsListContainer: HTMLElement;
|
||||
private unAssignedKeybindingItemToRevealAndFocus: IKeybindingItemEntry | null;
|
||||
private listEntries: IListEntry[];
|
||||
private keybindingsList: WorkbenchList<IListEntry>;
|
||||
private keybindingsListContainer!: HTMLElement;
|
||||
private unAssignedKeybindingItemToRevealAndFocus: IKeybindingItemEntry | null = null;
|
||||
private listEntries: IListEntry[] = [];
|
||||
private keybindingsList!: WorkbenchList<IListEntry>;
|
||||
|
||||
private dimension: DOM.Dimension;
|
||||
private dimension: DOM.Dimension | null = null;
|
||||
private delayedFiltering: Delayer<void>;
|
||||
private latestEmptyFilters: string[] = [];
|
||||
private delayedFilterLogging: Delayer<void>;
|
||||
|
@ -88,11 +88,10 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
|||
private keybindingFocusContextKey: IContextKey<boolean>;
|
||||
private searchFocusContextKey: IContextKey<boolean>;
|
||||
|
||||
private actionBar: ActionBar;
|
||||
private sortByPrecedenceAction: Action;
|
||||
private recordKeysAction: Action;
|
||||
private readonly sortByPrecedenceAction: Action;
|
||||
private readonly recordKeysAction: Action;
|
||||
|
||||
private ariaLabelElement: HTMLElement;
|
||||
private ariaLabelElement!: HTMLElement;
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
|
@ -115,6 +114,16 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
|||
this.searchFocusContextKey = CONTEXT_KEYBINDINGS_SEARCH_FOCUS.bindTo(this.contextKeyService);
|
||||
this.keybindingFocusContextKey = CONTEXT_KEYBINDING_FOCUS.bindTo(this.contextKeyService);
|
||||
this.delayedFilterLogging = new Delayer<void>(1000);
|
||||
|
||||
const recordKeysActionKeybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS);
|
||||
const recordKeysActionLabel = localize('recordKeysLabel', "Record Keys");
|
||||
this.recordKeysAction = new Action(KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, recordKeysActionKeybinding ? localize('recordKeysLabelWithKeybinding', "{0} ({1})", recordKeysActionLabel, recordKeysActionKeybinding.getLabel()) : recordKeysActionLabel, 'codicon-record-keys');
|
||||
this.recordKeysAction.checked = false;
|
||||
|
||||
const sortByPrecedenceActionKeybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE);
|
||||
const sortByPrecedenceActionLabel = localize('sortByPrecedeneLabel', "Sort by Precedence");
|
||||
this.sortByPrecedenceAction = new Action('keybindings.editor.sortByPrecedence', sortByPrecedenceActionKeybinding ? localize('sortByPrecedeneLabelWithKeybinding', "{0} ({1})", sortByPrecedenceActionLabel, sortByPrecedenceActionKeybinding.getLabel()) : sortByPrecedenceActionLabel, 'codicon-sort-precedence');
|
||||
this.sortByPrecedenceAction.checked = false;
|
||||
}
|
||||
|
||||
createEditor(parent: HTMLElement): void {
|
||||
|
@ -298,7 +307,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
|||
this.overlayContainer.style.position = 'absolute';
|
||||
this.overlayContainer.style.zIndex = '10';
|
||||
this.defineKeybindingWidget = this._register(this.instantiationService.createInstance(DefineKeybindingWidget, this.overlayContainer));
|
||||
this._register(this.defineKeybindingWidget.onDidChange(keybindingStr => this.defineKeybindingWidget.printExisting(this.keybindingsEditorModel.fetch(`"${keybindingStr}"`).length)));
|
||||
this._register(this.defineKeybindingWidget.onDidChange(keybindingStr => this.defineKeybindingWidget.printExisting(this.keybindingsEditorModel!.fetch(`"${keybindingStr}"`).length)));
|
||||
this._register(this.defineKeybindingWidget.onShowExistingKeybidings(keybindingStr => this.searchWidget.setValue(`"${keybindingStr}"`)));
|
||||
this.hideOverlayContainer();
|
||||
}
|
||||
|
@ -337,10 +346,6 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
|||
this.actionsContainer = DOM.append(searchContainer, DOM.$('.keybindings-search-actions-container'));
|
||||
const recordingBadge = this.createRecordingBadge(this.actionsContainer);
|
||||
|
||||
const sortByPrecedenceActionKeybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE);
|
||||
const sortByPrecedenceActionLabel = localize('sortByPrecedeneLabel', "Sort by Precedence");
|
||||
this.sortByPrecedenceAction = new Action('keybindings.editor.sortByPrecedence', sortByPrecedenceActionKeybinding ? localize('sortByPrecedeneLabelWithKeybinding', "{0} ({1})", sortByPrecedenceActionLabel, sortByPrecedenceActionKeybinding.getLabel()) : sortByPrecedenceActionLabel, 'codicon-sort-precedence');
|
||||
this.sortByPrecedenceAction.checked = false;
|
||||
this._register(this.sortByPrecedenceAction.onDidChange(e => {
|
||||
if (e.checked !== undefined) {
|
||||
this.renderKeybindingsEntries(false);
|
||||
|
@ -348,10 +353,6 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
|||
this.updateSearchOptions();
|
||||
}));
|
||||
|
||||
const recordKeysActionKeybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS);
|
||||
const recordKeysActionLabel = localize('recordKeysLabel', "Record Keys");
|
||||
this.recordKeysAction = new Action(KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, recordKeysActionKeybinding ? localize('recordKeysLabelWithKeybinding', "{0} ({1})", recordKeysActionLabel, recordKeysActionKeybinding.getLabel()) : recordKeysActionLabel, 'codicon-record-keys');
|
||||
this.recordKeysAction.checked = false;
|
||||
this._register(this.recordKeysAction.onDidChange(e => {
|
||||
if (e.checked !== undefined) {
|
||||
DOM.toggleClass(recordingBadge, 'disabled', !e.checked);
|
||||
|
@ -370,7 +371,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
|||
}
|
||||
}));
|
||||
|
||||
this.actionBar = this._register(new ActionBar(this.actionsContainer, {
|
||||
const actionBar = this._register(new ActionBar(this.actionsContainer, {
|
||||
animated: false,
|
||||
actionViewItemProvider: (action: Action) => {
|
||||
if (action.id === this.sortByPrecedenceAction.id) {
|
||||
|
@ -383,7 +384,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
|||
}
|
||||
}));
|
||||
|
||||
this.actionBar.push([this.recordKeysAction, this.sortByPrecedenceAction, clearInputAction], { label: false, icon: true });
|
||||
actionBar.push([this.recordKeysAction, this.sortByPrecedenceAction, clearInputAction], { label: false, icon: true });
|
||||
}
|
||||
|
||||
private updateSearchOptions(): void {
|
||||
|
@ -563,6 +564,9 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
|||
}
|
||||
|
||||
private layoutKeybindingsList(): void {
|
||||
if (!this.dimension) {
|
||||
return;
|
||||
}
|
||||
let width = this.dimension.width - 27;
|
||||
for (const columnItem of this.columnItems) {
|
||||
if (columnItem.width && !columnItem.proportion) {
|
||||
|
@ -855,7 +859,7 @@ abstract class Column extends Disposable {
|
|||
|
||||
class ActionsColumn extends Column {
|
||||
|
||||
private actionBar: ActionBar;
|
||||
private readonly actionBar: ActionBar;
|
||||
readonly element: HTMLElement;
|
||||
|
||||
constructor(
|
||||
|
@ -864,13 +868,8 @@ class ActionsColumn extends Column {
|
|||
@IKeybindingService private keybindingsService: IKeybindingService
|
||||
) {
|
||||
super(keybindingsEditor);
|
||||
this.element = this.create(parent);
|
||||
}
|
||||
|
||||
create(parent: HTMLElement): HTMLElement {
|
||||
const actionsContainer = DOM.append(parent, $('.column.actions', { id: 'actions_' + ++Column.COUNTER }));
|
||||
this.actionBar = new ActionBar(actionsContainer, { animated: false });
|
||||
return actionsContainer;
|
||||
this.element = DOM.append(parent, $('.column.actions', { id: 'actions_' + ++Column.COUNTER }));
|
||||
this.actionBar = new ActionBar(this.element, { animated: false });
|
||||
}
|
||||
|
||||
render(keybindingItemEntry: IKeybindingItemEntry): void {
|
||||
|
@ -914,7 +913,7 @@ class ActionsColumn extends Column {
|
|||
|
||||
class CommandColumn extends Column {
|
||||
|
||||
private commandColumn: HTMLElement;
|
||||
private readonly commandColumn: HTMLElement;
|
||||
readonly element: HTMLElement;
|
||||
|
||||
constructor(
|
||||
|
@ -922,12 +921,7 @@ class CommandColumn extends Column {
|
|||
keybindingsEditor: IKeybindingsEditor,
|
||||
) {
|
||||
super(keybindingsEditor);
|
||||
this.element = this.create(parent);
|
||||
}
|
||||
|
||||
private create(parent: HTMLElement): HTMLElement {
|
||||
this.commandColumn = DOM.append(parent, $('.column.command', { id: 'command_' + ++Column.COUNTER }));
|
||||
return this.commandColumn;
|
||||
this.element = this.commandColumn = DOM.append(parent, $('.column.command', { id: 'command_' + ++Column.COUNTER }));
|
||||
}
|
||||
|
||||
render(keybindingItemEntry: IKeybindingItemEntry): void {
|
||||
|
@ -962,7 +956,7 @@ class CommandColumn extends Column {
|
|||
|
||||
class KeybindingColumn extends Column {
|
||||
|
||||
private keybindingLabel: HTMLElement;
|
||||
private readonly keybindingLabel: HTMLElement;
|
||||
readonly element: HTMLElement;
|
||||
|
||||
constructor(
|
||||
|
@ -970,13 +964,9 @@ class KeybindingColumn extends Column {
|
|||
keybindingsEditor: IKeybindingsEditor,
|
||||
) {
|
||||
super(keybindingsEditor);
|
||||
this.element = this.create(parent);
|
||||
}
|
||||
|
||||
private create(parent: HTMLElement): HTMLElement {
|
||||
const column = DOM.append(parent, $('.column.keybinding', { id: 'keybinding_' + ++Column.COUNTER }));
|
||||
this.keybindingLabel = DOM.append(column, $('div.keybinding-label'));
|
||||
return column;
|
||||
this.element = DOM.append(parent, $('.column.keybinding', { id: 'keybinding_' + ++Column.COUNTER }));
|
||||
this.keybindingLabel = DOM.append(this.element, $('div.keybinding-label'));
|
||||
}
|
||||
|
||||
render(keybindingItemEntry: IKeybindingItemEntry): void {
|
||||
|
@ -994,7 +984,7 @@ class KeybindingColumn extends Column {
|
|||
|
||||
class SourceColumn extends Column {
|
||||
|
||||
private sourceColumn: HTMLElement;
|
||||
private readonly sourceColumn: HTMLElement;
|
||||
readonly element: HTMLElement;
|
||||
|
||||
constructor(
|
||||
|
@ -1002,12 +992,7 @@ class SourceColumn extends Column {
|
|||
keybindingsEditor: IKeybindingsEditor,
|
||||
) {
|
||||
super(keybindingsEditor);
|
||||
this.element = this.create(parent);
|
||||
}
|
||||
|
||||
create(parent: HTMLElement): HTMLElement {
|
||||
this.sourceColumn = DOM.append(parent, $('.column.source', { id: 'source_' + ++Column.COUNTER }));
|
||||
return this.sourceColumn;
|
||||
this.element = this.sourceColumn = DOM.append(parent, $('.column.source', { id: 'source_' + ++Column.COUNTER }));
|
||||
}
|
||||
|
||||
render(keybindingItemEntry: IKeybindingItemEntry): void {
|
||||
|
@ -1024,8 +1009,8 @@ class SourceColumn extends Column {
|
|||
class WhenColumn extends Column {
|
||||
|
||||
readonly element: HTMLElement;
|
||||
private whenLabel: HTMLElement;
|
||||
private whenInput: InputBox;
|
||||
private readonly whenLabel: HTMLElement;
|
||||
private readonly whenInput: InputBox;
|
||||
private readonly renderDisposables = this._register(new DisposableStore());
|
||||
|
||||
private _onDidAccept: Emitter<void> = this._register(new Emitter<void>());
|
||||
|
@ -1041,14 +1026,11 @@ class WhenColumn extends Column {
|
|||
@IThemeService private readonly themeService: IThemeService
|
||||
) {
|
||||
super(keybindingsEditor);
|
||||
this.element = this.create(parent);
|
||||
}
|
||||
|
||||
private create(parent: HTMLElement): HTMLElement {
|
||||
const column = DOM.append(parent, $('.column.when', { id: 'when_' + ++Column.COUNTER }));
|
||||
this.element = DOM.append(parent, $('.column.when', { id: 'when_' + ++Column.COUNTER }));
|
||||
|
||||
this.whenLabel = DOM.append(column, $('div.when-label'));
|
||||
this.whenInput = new InputBox(column, this.contextViewService, {
|
||||
this.whenLabel = DOM.append(this.element, $('div.when-label'));
|
||||
this.whenInput = new InputBox(this.element, this.contextViewService, {
|
||||
validationOptions: {
|
||||
validation: (value) => {
|
||||
try {
|
||||
|
@ -1068,8 +1050,6 @@ class WhenColumn extends Column {
|
|||
this._register(attachInputBoxStyler(this.whenInput, this.themeService));
|
||||
this._register(DOM.addStandardDisposableListener(this.whenInput.inputElement, DOM.EventType.KEY_DOWN, e => this.onInputKeyDown(e)));
|
||||
this._register(DOM.addDisposableListener(this.whenInput.inputElement, DOM.EventType.BLUR, () => this.cancelEditing()));
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
private onInputKeyDown(e: IKeyboardEvent): void {
|
||||
|
|
|
@ -319,6 +319,10 @@ export class SettingsEditor2 extends BaseEditor {
|
|||
this.focusSearch();
|
||||
}
|
||||
|
||||
onHide(): void {
|
||||
this.searchWidget.onHide();
|
||||
}
|
||||
|
||||
focusSettings(): void {
|
||||
// Update ARIA global labels
|
||||
const labelElement = this.settingsAriaExtraLabelsContainer.querySelector('#settings_aria_more_actions_shortcut_label');
|
||||
|
|
106
src/vs/workbench/contrib/remote/browser/explorerViewItems.ts
Normal file
106
src/vs/workbench/contrib/remote/browser/explorerViewItems.ts
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
|
||||
import { IActionRunner, IAction, Action } from 'vs/base/common/actions';
|
||||
import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { selectBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox';
|
||||
import { IViewDescriptor } from 'vs/workbench/common/views';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { isStringArray } from 'vs/base/common/types';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
|
||||
export interface IRemoteSelectItem extends ISelectOptionItem {
|
||||
authority: string[];
|
||||
}
|
||||
|
||||
export class SwitchRemoteViewItem extends SelectActionViewItem {
|
||||
|
||||
actionRunner!: IActionRunner;
|
||||
|
||||
constructor(
|
||||
action: IAction,
|
||||
private readonly optionsItems: IRemoteSelectItem[],
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IContextViewService contextViewService: IContextViewService,
|
||||
@IRemoteExplorerService remoteExplorerService: IRemoteExplorerService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
super(null, action, optionsItems, 0, contextViewService, { ariaLabel: nls.localize('remotes', 'Switch Remote') });
|
||||
this._register(attachSelectBoxStyler(this.selectBox, themeService, {
|
||||
selectBackground: SIDE_BAR_BACKGROUND
|
||||
}));
|
||||
|
||||
this.setSelectionForConnection(optionsItems, environmentService, remoteExplorerService);
|
||||
}
|
||||
|
||||
private setSelectionForConnection(optionsItems: IRemoteSelectItem[], environmentService: IWorkbenchEnvironmentService, remoteExplorerService: IRemoteExplorerService) {
|
||||
// TODO: set from saved state
|
||||
if (this.optionsItems.length > 0) {
|
||||
const remoteAuthority = environmentService.configuration.remoteAuthority;
|
||||
let index = 0;
|
||||
if (remoteAuthority) {
|
||||
const actualRemoteAuthority = remoteAuthority.split('+')[0];
|
||||
for (let optionIterator = 0; (optionIterator < this.optionsItems.length) && (index === 0); optionIterator++) {
|
||||
for (let authorityIterator = 0; authorityIterator < optionsItems[optionIterator].authority.length; authorityIterator++) {
|
||||
if (optionsItems[optionIterator].authority[authorityIterator] === actualRemoteAuthority) {
|
||||
index = optionIterator;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.select(index);
|
||||
remoteExplorerService.targetType = optionsItems[index].authority[0];
|
||||
}
|
||||
}
|
||||
|
||||
render(container: HTMLElement) {
|
||||
super.render(container);
|
||||
dom.addClass(container, 'switch-remote');
|
||||
this._register(attachStylerCallback(this.themeService, { selectBorder }, colors => {
|
||||
container.style.border = colors.selectBorder ? `1px solid ${colors.selectBorder}` : '';
|
||||
}));
|
||||
}
|
||||
|
||||
protected getActionContext(_: string, index: number): any {
|
||||
return this.optionsItems[index];
|
||||
}
|
||||
|
||||
static createOptionItems(views: IViewDescriptor[]): IRemoteSelectItem[] {
|
||||
let options: IRemoteSelectItem[] = [];
|
||||
views.forEach(view => {
|
||||
if (view.group && startsWith(view.group, 'targets') && view.remoteAuthority) {
|
||||
options.push({ text: view.name, authority: isStringArray(view.remoteAuthority) ? view.remoteAuthority : [view.remoteAuthority] });
|
||||
}
|
||||
});
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
export class SwitchRemoteAction extends Action {
|
||||
|
||||
public static readonly ID = 'remote.explorer.switch';
|
||||
public static readonly LABEL = nls.localize('remote.explorer.switch', "Switch Remote");
|
||||
|
||||
constructor(
|
||||
id: string, label: string,
|
||||
@IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public async run(item: IRemoteSelectItem): Promise<any> {
|
||||
this.remoteExplorerService.targetType = item.authority[0];
|
||||
}
|
||||
}
|
|
@ -16,10 +16,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
|||
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 { FilterViewContainerViewlet } 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';
|
||||
|
@ -47,7 +46,10 @@ import Severity from 'vs/base/common/severity';
|
|||
import { ReloadWindowAction } from 'vs/workbench/browser/actions/windowActions';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { SwitchRemoteViewItem, SwitchRemoteAction } from 'vs/workbench/contrib/remote/browser/explorerViewItems';
|
||||
import { Action, IActionViewItem, IAction } from 'vs/base/common/actions';
|
||||
import { isStringArray } from 'vs/base/common/types';
|
||||
import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
|
||||
interface HelpInformation {
|
||||
extensionDescription: IExtensionDescription;
|
||||
|
@ -364,10 +366,9 @@ class HelpPanelDescriptor implements IViewDescriptor {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
export class RemoteViewlet extends ViewContainerViewlet implements IViewModel {
|
||||
export class RemoteViewlet extends FilterViewContainerViewlet implements IViewModel {
|
||||
private helpPanelDescriptor = new HelpPanelDescriptor(this);
|
||||
|
||||
private actions: IAction[] | undefined;
|
||||
helpInformations: HelpInformation[] = [];
|
||||
|
||||
constructor(
|
||||
|
@ -380,10 +381,10 @@ export class RemoteViewlet extends ViewContainerViewlet implements IViewModel {
|
|||
@IThemeService themeService: IThemeService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
@IWorkbenchEnvironmentService private environmentService: IWorkbenchEnvironmentService,
|
||||
@IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService
|
||||
) {
|
||||
super(VIEWLET_ID, `${VIEWLET_ID}.state`, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
|
||||
|
||||
super(VIEWLET_ID, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
|
||||
this.addConstantViewDescriptors([this.helpPanelDescriptor]);
|
||||
remoteHelpExtPoint.setHandler((extensions) => {
|
||||
let helpInformation: HelpInformation[] = [];
|
||||
for (let extension of extensions) {
|
||||
|
@ -399,6 +400,34 @@ export class RemoteViewlet extends ViewContainerViewlet implements IViewModel {
|
|||
viewsRegistry.deregisterViews([this.helpPanelDescriptor], VIEW_CONTAINER);
|
||||
}
|
||||
});
|
||||
|
||||
this._register(this.remoteExplorerService.onDidChangeTargetType(() => {
|
||||
this.onDidChangeFilterValue.fire(this.remoteExplorerService.targetType);
|
||||
}));
|
||||
}
|
||||
|
||||
protected getFilterOn(viewDescriptor: IViewDescriptor): string | undefined {
|
||||
return isStringArray(viewDescriptor.remoteAuthority) ? viewDescriptor.remoteAuthority[0] : viewDescriptor.remoteAuthority;
|
||||
}
|
||||
|
||||
public getActionViewItem(action: Action): IActionViewItem | undefined {
|
||||
if (action.id === SwitchRemoteAction.ID) {
|
||||
return this.instantiationService.createInstance(SwitchRemoteViewItem, action, SwitchRemoteViewItem.createOptionItems(Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).getViews(VIEW_CONTAINER)));
|
||||
}
|
||||
|
||||
return super.getActionViewItem(action);
|
||||
}
|
||||
|
||||
public getActions(): IAction[] {
|
||||
if (!this.actions) {
|
||||
this.actions = [
|
||||
this.instantiationService.createInstance(SwitchRemoteAction, SwitchRemoteAction.ID, SwitchRemoteAction.LABEL),
|
||||
];
|
||||
this.actions.forEach(a => {
|
||||
this._register(a);
|
||||
});
|
||||
}
|
||||
return this.actions;
|
||||
}
|
||||
|
||||
private _handleRemoteInfoExtensionPoint(extension: IExtensionPointUser<HelpInformation>, helpInformation: HelpInformation[]) {
|
||||
|
@ -419,38 +448,6 @@ export class RemoteViewlet extends ViewContainerViewlet implements IViewModel {
|
|||
});
|
||||
}
|
||||
|
||||
onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPanel[] {
|
||||
// too late, already added to the view model
|
||||
const result = super.onDidAddViews(added);
|
||||
|
||||
const remoteAuthority = this.environmentService.configuration.remoteAuthority;
|
||||
if (remoteAuthority) {
|
||||
const actualRemoteAuthority = remoteAuthority.split('+')[0];
|
||||
added.forEach((descriptor) => {
|
||||
const panel = this.getView(descriptor.viewDescriptor.id);
|
||||
if (!panel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const descriptorAuthority = descriptor.viewDescriptor.remoteAuthority;
|
||||
if (typeof descriptorAuthority === 'undefined') {
|
||||
panel.setExpanded(true);
|
||||
} else if (descriptor.viewDescriptor.id === HelpPanel.ID) {
|
||||
// Do nothing, keep the default behavior for Help
|
||||
} else {
|
||||
const descriptorAuthorityArr = Array.isArray(descriptorAuthority) ? descriptorAuthority : [descriptorAuthority];
|
||||
if (descriptorAuthorityArr.indexOf(actualRemoteAuthority) >= 0) {
|
||||
panel.setExpanded(true);
|
||||
} else {
|
||||
panel.setExpanded(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
const title = nls.localize('remote.explorer', "Remote Explorer");
|
||||
return title;
|
||||
|
|
|
@ -87,3 +87,22 @@
|
|||
.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')
|
||||
}
|
||||
|
||||
.monaco-workbench .part > .title > .title-actions .switch-remote {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 11px;
|
||||
margin-right: 0.3em;
|
||||
height: 20px;
|
||||
flex-shrink: 1;
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
.switch-remote > .monaco-select-box {
|
||||
border: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.monaco-workbench .part > .title > .title-actions .switch-remote > .monaco-select-box {
|
||||
padding-left: 3px;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ export const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry
|
|||
return -1000;
|
||||
}
|
||||
|
||||
matches = /^details@(\d+)$/.exec(group);
|
||||
matches = /^details(@(\d+))?$/.exec(group);
|
||||
|
||||
if (matches) {
|
||||
return -500;
|
||||
|
|
|
@ -333,7 +333,7 @@ CommandsRegistry.registerCommand({
|
|||
|
||||
const RevealInSideBarForSearchResultsCommand: ICommandAction = {
|
||||
id: Constants.RevealInSideBarForSearchResults,
|
||||
title: nls.localize('revealInSideBar', "Reveal in Explorer")
|
||||
title: nls.localize('revealInSideBar', "Reveal in Side Bar")
|
||||
};
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.SearchContext, {
|
||||
|
|
|
@ -19,7 +19,10 @@ function getMockTheme(type: ThemeType): ITheme {
|
|||
label: '',
|
||||
type: type,
|
||||
getColor: (colorId: ColorIdentifier): Color | undefined => themingRegistry.resolveDefaultColor(colorId, theme),
|
||||
defines: () => true
|
||||
defines: () => true,
|
||||
getTokenStyle: () => undefined,
|
||||
resolveScopes: () => undefined
|
||||
|
||||
};
|
||||
return theme;
|
||||
}
|
||||
|
@ -99,4 +102,4 @@ suite('Workbench - TerminalColorRegistry', () => {
|
|||
'#e5e5e5'
|
||||
], 'The dark terminal colors should be used when a dark theme is active');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -25,7 +25,9 @@ import { generateUuid } from 'vs/base/common/uuid';
|
|||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { editorBackground, editorForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
|
||||
export class TestCustomEditorsAction extends Action {
|
||||
const ENABLE = false;
|
||||
|
||||
class TestCustomEditorsAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openCustomEditor';
|
||||
static readonly LABEL = nls.localize('openCustomEditor', "Test Open Custom Editor");
|
||||
|
@ -45,7 +47,7 @@ export class TestCustomEditorsAction extends Action {
|
|||
}
|
||||
}
|
||||
|
||||
export class TestCustomEditor extends BaseEditor {
|
||||
class TestCustomEditor extends BaseEditor {
|
||||
|
||||
static ID = 'testCustomEditor';
|
||||
|
||||
|
@ -109,7 +111,7 @@ export class TestCustomEditor extends BaseEditor {
|
|||
layout(dimension: Dimension): void { }
|
||||
}
|
||||
|
||||
export class TestCustomEditorInput extends EditorInput {
|
||||
class TestCustomEditorInput extends EditorInput {
|
||||
private model: TestCustomEditorModel | undefined = undefined;
|
||||
private dirty = false;
|
||||
|
||||
|
@ -176,7 +178,7 @@ export class TestCustomEditorInput extends EditorInput {
|
|||
}
|
||||
}
|
||||
|
||||
export class TestCustomEditorModel extends EditorModel {
|
||||
class TestCustomEditorModel extends EditorModel {
|
||||
|
||||
public value: string = '';
|
||||
|
||||
|
@ -185,32 +187,34 @@ export class TestCustomEditorModel extends EditorModel {
|
|||
}
|
||||
}
|
||||
|
||||
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
||||
new EditorDescriptor(
|
||||
TestCustomEditor,
|
||||
TestCustomEditor.ID,
|
||||
nls.localize('testCustomEditor', "Test Custom Editor")
|
||||
),
|
||||
[
|
||||
new SyncDescriptor<EditorInput>(TestCustomEditorInput),
|
||||
]
|
||||
);
|
||||
if (ENABLE) {
|
||||
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
||||
new EditorDescriptor(
|
||||
TestCustomEditor,
|
||||
TestCustomEditor.ID,
|
||||
nls.localize('testCustomEditor', "Test Custom Editor")
|
||||
),
|
||||
[
|
||||
new SyncDescriptor<EditorInput>(TestCustomEditorInput),
|
||||
]
|
||||
);
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
|
||||
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(TestCustomEditorsAction, TestCustomEditorsAction.ID, TestCustomEditorsAction.LABEL), 'Test Open Custom Editor');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(TestCustomEditorsAction, TestCustomEditorsAction.ID, TestCustomEditorsAction.LABEL), 'Test Open Custom Editor');
|
||||
|
||||
class TestCustomEditorInputFactory implements IEditorInputFactory {
|
||||
class TestCustomEditorInputFactory implements IEditorInputFactory {
|
||||
|
||||
serialize(editorInput: TestCustomEditorInput): string {
|
||||
return JSON.stringify({
|
||||
resource: editorInput.resource.toString()
|
||||
});
|
||||
serialize(editorInput: TestCustomEditorInput): string {
|
||||
return JSON.stringify({
|
||||
resource: editorInput.resource.toString()
|
||||
});
|
||||
}
|
||||
|
||||
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): TestCustomEditorInput {
|
||||
return new TestCustomEditorInput(URI.parse(JSON.parse(serializedEditorInput).resource));
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): TestCustomEditorInput {
|
||||
return new TestCustomEditorInput(URI.parse(JSON.parse(serializedEditorInput).resource));
|
||||
}
|
||||
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(TestCustomEditor.ID, TestCustomEditorInputFactory);
|
||||
}
|
||||
|
||||
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(TestCustomEditor.ID, TestCustomEditorInputFactory);
|
||||
|
|
|
@ -158,7 +158,11 @@ export function isURLDomainTrusted(url: URI, trustedDomains: string[]) {
|
|||
}
|
||||
|
||||
if (url.authority === parsedTrustedDomain.authority) {
|
||||
return pathMatches(url.path, parsedTrustedDomain.path);
|
||||
if (pathMatches(url.path, parsedTrustedDomain.path)) {
|
||||
return true;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (trustedDomains[i].indexOf('*') !== -1) {
|
||||
|
|
|
@ -25,7 +25,6 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
|
|||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
|
||||
const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey<string>('authTokenStatus', AuthTokenStatus.Inactive);
|
||||
|
@ -51,7 +50,6 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
|||
@ITextFileService private readonly textFileService: ITextFileService,
|
||||
@IHistoryService private readonly historyService: IHistoryService,
|
||||
@IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService,
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
) {
|
||||
super();
|
||||
this.syncStatusContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService);
|
||||
|
@ -138,14 +136,11 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
|||
}
|
||||
|
||||
private async signIn(): Promise<void> {
|
||||
const token = await this.quickInputService.input({ placeHolder: localize('enter token', "Please provide the auth bearer token"), ignoreFocusLost: true, });
|
||||
if (token) {
|
||||
await this.authTokenService.updateToken(token);
|
||||
}
|
||||
return this.authTokenService.login();
|
||||
}
|
||||
|
||||
private async signOut(): Promise<void> {
|
||||
await this.authTokenService.deleteToken();
|
||||
await this.authTokenService.logout();
|
||||
}
|
||||
|
||||
private async continueSync(): Promise<void> {
|
||||
|
|
|
@ -83,7 +83,10 @@ function normalizeRequestPath(requestUri: URI) {
|
|||
|
||||
// Modern vscode-resources uris put the scheme of the requested resource as the authority
|
||||
if (requestUri.authority) {
|
||||
return URI.parse(requestUri.authority + ':' + requestUri.path);
|
||||
return URI.parse(`${requestUri.authority}:${requestUri.path}`).with({
|
||||
query: requestUri.query,
|
||||
fragment: requestUri.fragment
|
||||
});
|
||||
}
|
||||
|
||||
// Old style vscode-resource uris lose the scheme of the resource which means they are unable to
|
||||
|
|
|
@ -398,29 +398,23 @@ export class ElectronWindow extends Disposable {
|
|||
throw new Error('Prevented call to window.open(). Use IOpenerService instead!');
|
||||
};
|
||||
|
||||
// Handle internal open() calls
|
||||
this.openerService.registerOpener({
|
||||
open: async (resource: URI, options?: OpenOptions): Promise<boolean> => {
|
||||
|
||||
// If either the caller wants to open externally or the
|
||||
// scheme is one where we prefer to open externally
|
||||
// we handle this resource by delegating the opening to
|
||||
// the main process to prevent window focus issues.
|
||||
if (this.shouldOpenExternal(resource, options)) {
|
||||
const { resolved } = await this.openerService.resolveExternalUri(resource, options);
|
||||
const success = await this.electronService.openExternal(encodeURI(resolved.toString(true)));
|
||||
if (!success && resolved.scheme === Schemas.file) {
|
||||
// Handle external open() calls
|
||||
this.openerService.setExternalOpener({
|
||||
openExternal: async (href: string) => {
|
||||
const success = await this.electronService.openExternal(href);
|
||||
if (!success) {
|
||||
const fileCandidate = URI.parse(href);
|
||||
if (fileCandidate.scheme === Schemas.file) {
|
||||
// if opening failed, and this is a file, we can still try to reveal it
|
||||
await this.electronService.showItemInFolder(resolved.fsPath);
|
||||
await this.electronService.showItemInFolder(fileCandidate.fsPath);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // not handled by us
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Register external URI resolver
|
||||
this.openerService.registerExternalUriResolver({
|
||||
resolveExternalUri: async (uri: URI, options?: OpenOptions) => {
|
||||
if (options?.allowTunneling) {
|
||||
|
@ -440,12 +434,6 @@ export class ElectronWindow extends Disposable {
|
|||
});
|
||||
}
|
||||
|
||||
private shouldOpenExternal(resource: URI, options?: OpenOptions) {
|
||||
const scheme = resource.scheme.toLowerCase();
|
||||
const preferOpenExternal = (scheme === Schemas.mailto || scheme === Schemas.http || scheme === Schemas.https);
|
||||
return options?.openExternal || preferOpenExternal;
|
||||
}
|
||||
|
||||
private updateTouchbarMenu(): void {
|
||||
if (!isMacintosh) {
|
||||
return; // macOS only
|
||||
|
|
|
@ -3,12 +3,15 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth';
|
||||
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
const SERVICE_NAME = 'VS Code';
|
||||
const ACCOUNT = 'MyAccount';
|
||||
|
@ -21,10 +24,13 @@ export class AuthTokenService extends Disposable implements IAuthTokenService {
|
|||
private _onDidChangeStatus: Emitter<AuthTokenStatus> = this._register(new Emitter<AuthTokenStatus>());
|
||||
readonly onDidChangeStatus: Event<AuthTokenStatus> = this._onDidChangeStatus.event;
|
||||
|
||||
readonly _onDidGetCallback: Emitter<URI> = this._register(new Emitter<URI>());
|
||||
|
||||
constructor(
|
||||
@ICredentialsService private readonly credentialsService: ICredentialsService,
|
||||
@IProductService productService: IProductService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService
|
||||
) {
|
||||
super();
|
||||
if (productService.settingsSyncStoreUrl && configurationService.getValue('configurationSync.enableAuth')) {
|
||||
|
@ -37,29 +43,35 @@ export class AuthTokenService extends Disposable implements IAuthTokenService {
|
|||
}
|
||||
}
|
||||
|
||||
getToken(): Promise<string | null> {
|
||||
async getToken(): Promise<string | undefined> {
|
||||
if (this.status === AuthTokenStatus.Disabled) {
|
||||
throw new Error('Not enabled');
|
||||
}
|
||||
return this.credentialsService.getPassword(SERVICE_NAME, ACCOUNT);
|
||||
|
||||
const token = await this.credentialsService.getPassword(SERVICE_NAME, ACCOUNT);
|
||||
if (token) {
|
||||
return token;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
async updateToken(token: string): Promise<void> {
|
||||
if (this.status === AuthTokenStatus.Disabled) {
|
||||
throw new Error('Not enabled');
|
||||
async login(): Promise<void> {
|
||||
const token = await this.quickInputService.input({ placeHolder: localize('enter token', "Please provide the auth bearer token"), ignoreFocusLost: true, });
|
||||
if (token) {
|
||||
await this.credentialsService.setPassword(SERVICE_NAME, ACCOUNT, token);
|
||||
this.setStatus(AuthTokenStatus.Active);
|
||||
}
|
||||
await this.credentialsService.setPassword(SERVICE_NAME, ACCOUNT, token);
|
||||
this.setStatus(AuthTokenStatus.Active);
|
||||
}
|
||||
|
||||
async refreshToken(): Promise<void> {
|
||||
if (this.status === AuthTokenStatus.Disabled) {
|
||||
throw new Error('Not enabled');
|
||||
}
|
||||
await this.deleteToken();
|
||||
await this.logout();
|
||||
}
|
||||
|
||||
async deleteToken(): Promise<void> {
|
||||
async logout(): Promise<void> {
|
||||
if (this.status === AuthTokenStatus.Disabled) {
|
||||
throw new Error('Not enabled');
|
||||
}
|
||||
|
@ -75,4 +87,3 @@ export class AuthTokenService extends Disposable implements IAuthTokenService {
|
|||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,6 +9,8 @@ import { Emitter, Event } from 'vs/base/common/event';
|
|||
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth';
|
||||
import { IURLService } from 'vs/platform/url/common/url';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export class AuthTokenService extends Disposable implements IAuthTokenService {
|
||||
|
||||
|
@ -21,8 +23,11 @@ export class AuthTokenService extends Disposable implements IAuthTokenService {
|
|||
private _onDidChangeStatus: Emitter<AuthTokenStatus> = this._register(new Emitter<AuthTokenStatus>());
|
||||
readonly onDidChangeStatus: Event<AuthTokenStatus> = this._onDidChangeStatus.event;
|
||||
|
||||
readonly _onDidGetCallback: Emitter<URI> = this._register(new Emitter<URI>());
|
||||
|
||||
constructor(
|
||||
@ISharedProcessService sharedProcessService: ISharedProcessService
|
||||
@ISharedProcessService sharedProcessService: ISharedProcessService,
|
||||
@IURLService private readonly urlService: IURLService
|
||||
) {
|
||||
super();
|
||||
this.channel = sharedProcessService.getChannel('authToken');
|
||||
|
@ -30,22 +35,34 @@ export class AuthTokenService extends Disposable implements IAuthTokenService {
|
|||
this.updateStatus(status);
|
||||
this._register(this.channel.listen<AuthTokenStatus>('onDidChangeStatus')(status => this.updateStatus(status)));
|
||||
});
|
||||
|
||||
this.urlService.registerHandler(this);
|
||||
}
|
||||
|
||||
handleURL(uri: URI) {
|
||||
if (uri.authority === 'vscode.login') {
|
||||
this.channel.call('exchangeCodeForToken', uri);
|
||||
return Promise.resolve(true);
|
||||
} else {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
getToken(): Promise<string> {
|
||||
return this.channel.call('getToken');
|
||||
}
|
||||
|
||||
updateToken(token: string): Promise<void> {
|
||||
return this.channel.call('updateToken', [token]);
|
||||
login(): Promise<void> {
|
||||
const callbackUri = this.urlService.create({ authority: 'vscode.login ' });
|
||||
return this.channel.call('login', callbackUri);
|
||||
}
|
||||
|
||||
refreshToken(): Promise<void> {
|
||||
return this.channel.call('getToken');
|
||||
}
|
||||
|
||||
deleteToken(): Promise<void> {
|
||||
return this.channel.call('deleteToken');
|
||||
logout(): Promise<void> {
|
||||
return this.channel.call('logout');
|
||||
}
|
||||
|
||||
private async updateStatus(status: AuthTokenStatus): Promise<void> {
|
||||
|
|
|
@ -27,6 +27,10 @@ export class BrowserClipboardService implements IClipboardService {
|
|||
newTextarea.style.visibility = 'false';
|
||||
newTextarea.style.height = '1px';
|
||||
newTextarea.style.width = '1px';
|
||||
newTextarea.setAttribute('aria-hidden', 'true');
|
||||
newTextarea.style.position = 'absolute';
|
||||
newTextarea.style.top = '-1000';
|
||||
newTextarea.style.left = '-1000';
|
||||
document.body.appendChild(newTextarea);
|
||||
newTextarea.value = text;
|
||||
newTextarea.focus();
|
||||
|
|
|
@ -436,7 +436,7 @@ export class ConfigurationEditingService {
|
|||
}
|
||||
|
||||
if (target === EditableConfigurationTarget.WORKSPACE) {
|
||||
if (!operation.workspaceStandAloneConfigurationKey) {
|
||||
if (!operation.workspaceStandAloneConfigurationKey && !OVERRIDE_PROPERTY_PATTERN.test(operation.key)) {
|
||||
const configurationProperties = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
|
||||
if (configurationProperties[operation.key].scope === ConfigurationScope.APPLICATION) {
|
||||
return this.reject(ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_CONFIGURATION_APPLICATION, target, operation);
|
||||
|
@ -452,7 +452,7 @@ export class ConfigurationEditingService {
|
|||
return this.reject(ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_TARGET, target, operation);
|
||||
}
|
||||
|
||||
if (!operation.workspaceStandAloneConfigurationKey) {
|
||||
if (!operation.workspaceStandAloneConfigurationKey && !OVERRIDE_PROPERTY_PATTERN.test(operation.key)) {
|
||||
const configurationProperties = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
|
||||
if (configurationProperties[operation.key].scope !== ConfigurationScope.RESOURCE) {
|
||||
return this.reject(ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_CONFIGURATION, target, operation);
|
||||
|
|
|
@ -18,7 +18,7 @@ import * as uuid from 'vs/base/common/uuid';
|
|||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService';
|
||||
import { ConfigurationEditingService, ConfigurationEditingError, ConfigurationEditingErrorCode, EditableConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditingService';
|
||||
import { WORKSPACE_STANDALONE_CONFIGURATIONS } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { WORKSPACE_STANDALONE_CONFIGURATIONS, FOLDER_SETTINGS_PATH } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
|
@ -236,6 +236,41 @@ suite('ConfigurationEditingService', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('write overridable settings to user settings', () => {
|
||||
const key = '[language]';
|
||||
const value = { 'configurationEditing.service.testSetting': 'overridden value' };
|
||||
return testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key, value })
|
||||
.then(() => {
|
||||
const contents = fs.readFileSync(globalSettingsFile).toString('utf8');
|
||||
const parsed = json.parse(contents);
|
||||
assert.deepEqual(parsed[key], value);
|
||||
});
|
||||
});
|
||||
|
||||
test('write overridable settings to workspace settings', () => {
|
||||
const key = '[language]';
|
||||
const value = { 'configurationEditing.service.testSetting': 'overridden value' };
|
||||
return testObject.writeConfiguration(EditableConfigurationTarget.WORKSPACE, { key, value })
|
||||
.then(() => {
|
||||
const target = path.join(workspaceDir, FOLDER_SETTINGS_PATH);
|
||||
const contents = fs.readFileSync(target).toString('utf8');
|
||||
const parsed = json.parse(contents);
|
||||
assert.deepEqual(parsed[key], value);
|
||||
});
|
||||
});
|
||||
|
||||
test('write overridable settings to workspace folder settings', () => {
|
||||
const key = '[language]';
|
||||
const value = { 'configurationEditing.service.testSetting': 'overridden value' };
|
||||
const folderSettingsFile = path.join(workspaceDir, FOLDER_SETTINGS_PATH);
|
||||
return testObject.writeConfiguration(EditableConfigurationTarget.WORKSPACE_FOLDER, { key, value }, { scopes: { resource: URI.file(folderSettingsFile) } })
|
||||
.then(() => {
|
||||
const contents = fs.readFileSync(folderSettingsFile).toString('utf8');
|
||||
const parsed = json.parse(contents);
|
||||
assert.deepEqual(parsed[key], value);
|
||||
});
|
||||
});
|
||||
|
||||
test('write workspace standalone setting - empty file', () => {
|
||||
return testObject.writeConfiguration(EditableConfigurationTarget.WORKSPACE, { key: 'tasks.service.testSetting', value: 'value' })
|
||||
.then(() => {
|
||||
|
|
|
@ -39,13 +39,15 @@ function storageService(instantiationService: TestInstantiationService): IStorag
|
|||
|
||||
export class TestExtensionEnablementService extends ExtensionEnablementService {
|
||||
constructor(instantiationService: TestInstantiationService) {
|
||||
const extensionManagementService = instantiationService.get(IExtensionManagementService) || instantiationService.stub(IExtensionManagementService, { onDidInstallExtension: new Emitter<DidInstallExtensionEvent>().event, onDidUninstallExtension: new Emitter<DidUninstallExtensionEvent>().event } as IExtensionManagementService);
|
||||
const extensionManagementServerService = instantiationService.get(IExtensionManagementServerService) || instantiationService.stub(IExtensionManagementServerService, <IExtensionManagementServerService>{ localExtensionManagementServer: { extensionManagementService } });
|
||||
super(
|
||||
storageService(instantiationService),
|
||||
instantiationService.get(IWorkspaceContextService),
|
||||
instantiationService.get(IWorkbenchEnvironmentService) || instantiationService.stub(IWorkbenchEnvironmentService, { configuration: Object.create(null) } as IWorkbenchEnvironmentService),
|
||||
instantiationService.get(IExtensionManagementService) || instantiationService.stub(IExtensionManagementService,
|
||||
{ onDidInstallExtension: new Emitter<DidInstallExtensionEvent>().event, onDidUninstallExtension: new Emitter<DidUninstallExtensionEvent>().event } as IExtensionManagementService),
|
||||
instantiationService.get(IConfigurationService), instantiationService.get(IExtensionManagementServerService),
|
||||
extensionManagementService,
|
||||
instantiationService.get(IConfigurationService),
|
||||
extensionManagementServerService,
|
||||
productService
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
|||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader';
|
||||
|
||||
class ExtensionResourceLoaderService implements IExtensionResourceLoaderService {
|
||||
export class ExtensionResourceLoaderService implements IExtensionResourceLoaderService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
|
|
|
@ -343,9 +343,13 @@ function patches(originals: typeof http | typeof https, resolveProxy: ReturnType
|
|||
return original.apply(null, arguments as unknown as any[]);
|
||||
}
|
||||
|
||||
const optionsPatched = options.agent instanceof ProxyAgent;
|
||||
const originalAgent = options.agent;
|
||||
if (originalAgent === true) {
|
||||
throw new Error('Unexpected agent option: true');
|
||||
}
|
||||
const optionsPatched = originalAgent instanceof ProxyAgent;
|
||||
const config = onRequest && ((<any>options)._vscodeProxySupport || /* LS */ (<any>options)._vscodeSystemProxy) || proxySetting.config;
|
||||
const useProxySettings = !optionsPatched && (config === 'override' || config === 'on' && !options.agent);
|
||||
const useProxySettings = !optionsPatched && (config === 'override' || config === 'on' && originalAgent === undefined);
|
||||
const useSystemCertificates = !optionsPatched && certSetting.config && originals === https && !(options as https.RequestOptions).ca;
|
||||
|
||||
if (useProxySettings || useSystemCertificates) {
|
||||
|
@ -367,7 +371,7 @@ function patches(originals: typeof http | typeof https, resolveProxy: ReturnType
|
|||
options.agent = new ProxyAgent({
|
||||
resolveProxy: resolveProxy.bind(undefined, { useProxySettings, useSystemCertificates }),
|
||||
defaultPort: originals === https ? 443 : 80,
|
||||
originalAgent: options.agent
|
||||
originalAgent
|
||||
});
|
||||
return original(options, callback);
|
||||
}
|
||||
|
@ -469,7 +473,9 @@ async function readCaCertificates() {
|
|||
}
|
||||
|
||||
async function readWindowsCaCertificates() {
|
||||
const winCA = await import('vscode-windows-ca-certs');
|
||||
const winCA = await new Promise<any>((resolve, reject) => {
|
||||
require(['vscode-windows-ca-certs'], resolve, reject);
|
||||
});
|
||||
|
||||
let ders: any[] = [];
|
||||
const store = winCA();
|
||||
|
|
|
@ -11,7 +11,7 @@ import { Emitter, Event } from 'vs/base/common/event';
|
|||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { Keybinding, ResolvedKeybinding, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { KeybindingParser } from 'vs/base/common/keybindingParser';
|
||||
import { OS, OperatingSystem, isWeb } from 'vs/base/common/platform';
|
||||
import { OS, OperatingSystem } from 'vs/base/common/platform';
|
||||
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { Extensions as ConfigExtensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
|
@ -46,7 +46,7 @@ import { isArray } from 'vs/base/common/types';
|
|||
import { INavigatorWithKeyboard, IKeyboard } from 'vs/workbench/services/keybinding/browser/navigatorKeyboard';
|
||||
import { ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE } from 'vs/base/common/scanCode';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
import { BrowserFeatures } from 'vs/base/browser/canIUse';
|
||||
import { BrowserFeatures, KeyboardSupport } from 'vs/base/browser/canIUse';
|
||||
|
||||
interface ContributedKeyBinding {
|
||||
command: string;
|
||||
|
@ -241,7 +241,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService {
|
|||
this._register(browser.onDidChangeFullscreen(() => {
|
||||
const keyboard: IKeyboard | null = (<INavigatorWithKeyboard>navigator).keyboard;
|
||||
|
||||
if (!BrowserFeatures.fullKeyboard) {
|
||||
if (BrowserFeatures.keyboard === KeyboardSupport.None) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -352,15 +352,11 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService {
|
|||
}
|
||||
|
||||
private _assertBrowserConflicts(kb: Keybinding, commandId: string): boolean {
|
||||
if (!isWeb) {
|
||||
if (BrowserFeatures.keyboard === KeyboardSupport.Always) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (browser.isStandalone) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (browser.isFullscreen() && BrowserFeatures.fullKeyboard) {
|
||||
if (BrowserFeatures.keyboard === KeyboardSupport.FullScreen && browser.isFullscreen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -250,7 +250,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding
|
|||
|
||||
private parse(model: ITextModel): { result: IUserFriendlyKeybinding[], parseErrors: json.ParseError[] } {
|
||||
const parseErrors: json.ParseError[] = [];
|
||||
const result = json.parse(model.getValue(), parseErrors, { allowEmptyContent: true });
|
||||
const result = json.parse(model.getValue(), parseErrors, { allowTrailingComma: true, allowEmptyContent: true });
|
||||
return { result, parseErrors };
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
export const IRemoteExplorerService = createDecorator<IRemoteExplorerService>('remoteExplorerService');
|
||||
|
||||
export interface IRemoteExplorerService {
|
||||
_serviceBrand: undefined;
|
||||
onDidChangeTargetType: Event<string>;
|
||||
targetType: string;
|
||||
}
|
||||
|
||||
class RemoteExplorerService implements IRemoteExplorerService {
|
||||
public _serviceBrand: undefined;
|
||||
private _targetType: string = '';
|
||||
private _onDidChangeTargetType: Emitter<string> = new Emitter<string>();
|
||||
public onDidChangeTargetType: Event<string> = this._onDidChangeTargetType.event;
|
||||
|
||||
set targetType(name: string) {
|
||||
if (this._targetType !== name) {
|
||||
const oldTarget = this._targetType;
|
||||
this._targetType = name;
|
||||
this._onDidChangeTargetType.fire(oldTarget);
|
||||
}
|
||||
}
|
||||
get targetType(): string {
|
||||
return this._targetType;
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IRemoteExplorerService, RemoteExplorerService, true);
|
|
@ -22,7 +22,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
|
|||
import { ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { ITMSyntaxExtensionPoint, grammarsExtPoint } from 'vs/workbench/services/textMate/common/TMGrammars';
|
||||
import { ITextMateService } from 'vs/workbench/services/textMate/common/textMateService';
|
||||
import { ITokenColorizationRule, IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { ITextMateThemingRule, IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IGrammar, StackElement, IOnigLib, IRawTheme } from 'vscode-textmate';
|
||||
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
@ -257,7 +257,7 @@ export abstract class AbstractTextMateService extends Disposable implements ITex
|
|||
TokenizationRegistry.setColorMap(colorMap);
|
||||
}
|
||||
|
||||
private static equalsTokenRules(a: ITokenColorizationRule[] | null, b: ITokenColorizationRule[] | null): boolean {
|
||||
private static equalsTokenRules(a: ITextMateThemingRule[] | null, b: ITextMateThemingRule[] | null): boolean {
|
||||
if (!b || !a || b.length !== a.length) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import * as nls from 'vs/nls';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID, IColorCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID, IColorCustomizations, CUSTOM_EDITOR_TOKENSTYLES_SETTING, IExperimentalTokenStyleCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
|
@ -29,6 +29,7 @@ import * as resources from 'vs/base/common/resources';
|
|||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { textmateColorsSchemaId, registerColorThemeSchemas, textmateColorSettingsSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema';
|
||||
import { workbenchColorsSchemaId } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { tokenStylingSchemaId } from 'vs/platform/theme/common/tokenClassificationRegistry';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
|
@ -92,6 +93,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
|
|||
return this.configurationService.getValue<ITokenColorCustomizations>(CUSTOM_EDITOR_COLORS_SETTING) || {};
|
||||
}
|
||||
|
||||
private get tokenStylesCustomizations(): IExperimentalTokenStyleCustomizations {
|
||||
return this.configurationService.getValue<IExperimentalTokenStyleCustomizations>(CUSTOM_EDITOR_TOKENSTYLES_SETTING) || {};
|
||||
}
|
||||
|
||||
constructor(
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
|
@ -126,6 +131,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
|
|||
}
|
||||
themeData.setCustomColors(this.colorCustomizations);
|
||||
themeData.setCustomTokenColors(this.tokenColorCustomizations);
|
||||
themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations);
|
||||
this.updateDynamicCSSRules(themeData);
|
||||
this.applyTheme(themeData, undefined, true);
|
||||
|
||||
|
@ -154,18 +160,22 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
|
|||
|
||||
const themeSpecificWorkbenchColors: IJSONSchema = { properties: {} };
|
||||
const themeSpecificTokenColors: IJSONSchema = { properties: {} };
|
||||
const themeSpecificTokenStyling: IJSONSchema = { properties: {} };
|
||||
|
||||
const workbenchColors = { $ref: workbenchColorsSchemaId, additionalProperties: false };
|
||||
const tokenColors = { properties: tokenColorSchema.properties, additionalProperties: false };
|
||||
const tokenStyling = { $ref: tokenStylingSchemaId, additionalProperties: false };
|
||||
for (let t of event.themes) {
|
||||
// add theme specific color customization ("[Abyss]":{ ... })
|
||||
const themeId = `[${t.settingsId}]`;
|
||||
themeSpecificWorkbenchColors.properties![themeId] = workbenchColors;
|
||||
themeSpecificTokenColors.properties![themeId] = tokenColors;
|
||||
themeSpecificTokenStyling.properties![themeId] = tokenStyling;
|
||||
}
|
||||
|
||||
colorCustomizationsSchema.allOf![1] = themeSpecificWorkbenchColors;
|
||||
tokenColorCustomizationSchema.allOf![1] = themeSpecificTokenColors;
|
||||
experimentalTokenStylingCustomizationSchema.allOf![1] = themeSpecificTokenStyling;
|
||||
|
||||
configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration, tokenColorCustomizationConfiguration);
|
||||
|
||||
|
@ -308,6 +318,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
|
|||
this.currentColorTheme.setCustomTokenColors(this.tokenColorCustomizations);
|
||||
hasColorChanges = true;
|
||||
}
|
||||
if (e.affectsConfiguration(CUSTOM_EDITOR_TOKENSTYLES_SETTING)) {
|
||||
this.currentColorTheme.setCustomTokenStyleRules(this.tokenStylesCustomizations);
|
||||
hasColorChanges = true;
|
||||
}
|
||||
if (hasColorChanges) {
|
||||
this.updateDynamicCSSRules(this.currentColorTheme);
|
||||
this.onColorThemeChange.fire(this.currentColorTheme);
|
||||
|
@ -698,12 +712,18 @@ const tokenColorCustomizationSchema: IConfigurationPropertySchema = {
|
|||
default: {},
|
||||
allOf: [tokenColorSchema]
|
||||
};
|
||||
const experimentalTokenStylingCustomizationSchema: IConfigurationPropertySchema = {
|
||||
description: nls.localize('editorColorsTokenStyles', "Overrides token color and styles from the currently selected color theme."),
|
||||
default: {},
|
||||
allOf: [{ $ref: tokenStylingSchemaId }]
|
||||
};
|
||||
const tokenColorCustomizationConfiguration: IConfigurationNode = {
|
||||
id: 'editor',
|
||||
order: 7.2,
|
||||
type: 'object',
|
||||
properties: {
|
||||
[CUSTOM_EDITOR_COLORS_SETTING]: tokenColorCustomizationSchema
|
||||
[CUSTOM_EDITOR_COLORS_SETTING]: tokenColorCustomizationSchema,
|
||||
[CUSTOM_EDITOR_TOKENSTYLES_SETTING]: experimentalTokenStylingCustomizationSchema
|
||||
}
|
||||
};
|
||||
configurationRegistry.registerConfiguration(tokenColorCustomizationConfiguration);
|
||||
|
|
|
@ -6,22 +6,26 @@
|
|||
import { basename } from 'vs/base/common/path';
|
||||
import * as Json from 'vs/base/common/json';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { ExtensionData, ITokenColorCustomizations, ITokenColorizationRule, IColorTheme, IColorMap, IThemeExtensionPoint, VS_LIGHT_THEME, VS_HC_THEME, IColorCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { ExtensionData, ITokenColorCustomizations, ITextMateThemingRule, IColorTheme, IColorMap, IThemeExtensionPoint, VS_LIGHT_THEME, VS_HC_THEME, IColorCustomizations, IExperimentalTokenStyleCustomizations, ITokenColorizationSetting } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { convertSettings } from 'vs/workbench/services/themes/common/themeCompatibility';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { Extensions, IColorRegistry, ColorIdentifier, editorBackground, editorForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { Extensions as ColorRegistryExtensions, IColorRegistry, ColorIdentifier, editorBackground, editorForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ThemeType } from 'vs/platform/theme/common/themeService';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { parse as parsePList } from 'vs/workbench/services/themes/common/plistParser';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { TokenStyle, TokenClassification, ProbeScope, TokenStylingRule, getTokenClassificationRegistry } from 'vs/platform/theme/common/tokenClassificationRegistry';
|
||||
import { MatcherWithPriority, Matcher, createMatchers } from 'vs/workbench/services/themes/common/textMateScopeMatcher';
|
||||
import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader';
|
||||
|
||||
let colorRegistry = Registry.as<IColorRegistry>(Extensions.ColorContribution);
|
||||
let colorRegistry = Registry.as<IColorRegistry>(ColorRegistryExtensions.ColorContribution);
|
||||
|
||||
let tokenClassificationRegistry = getTokenClassificationRegistry();
|
||||
|
||||
const tokenGroupToScopesMap = {
|
||||
comments: ['comment', 'punctuation.definition.comment'],
|
||||
|
@ -33,6 +37,7 @@ const tokenGroupToScopesMap = {
|
|||
variables: ['variable', 'entity.name.variable']
|
||||
};
|
||||
|
||||
|
||||
export class ColorThemeData implements IColorTheme {
|
||||
|
||||
id: string;
|
||||
|
@ -44,11 +49,17 @@ export class ColorThemeData implements IColorTheme {
|
|||
watch?: boolean;
|
||||
extensionData?: ExtensionData;
|
||||
|
||||
private themeTokenColors: ITokenColorizationRule[] = [];
|
||||
private customTokenColors: ITokenColorizationRule[] = [];
|
||||
private themeTokenColors: ITextMateThemingRule[] = [];
|
||||
private customTokenColors: ITextMateThemingRule[] = [];
|
||||
private colorMap: IColorMap = {};
|
||||
private customColorMap: IColorMap = {};
|
||||
|
||||
private tokenStylingRules: TokenStylingRule[] | undefined = undefined;
|
||||
private customTokenStylingRules: TokenStylingRule[] = [];
|
||||
|
||||
private themeTokenScopeMatchers: Matcher<ProbeScope>[] | undefined;
|
||||
private customTokenScopeMatchers: Matcher<ProbeScope>[] | undefined;
|
||||
|
||||
private constructor(id: string, label: string, settingsId: string) {
|
||||
this.id = id;
|
||||
this.label = label;
|
||||
|
@ -56,8 +67,8 @@ export class ColorThemeData implements IColorTheme {
|
|||
this.isLoaded = false;
|
||||
}
|
||||
|
||||
get tokenColors(): ITokenColorizationRule[] {
|
||||
const result: ITokenColorizationRule[] = [];
|
||||
get tokenColors(): ITextMateThemingRule[] {
|
||||
const result: ITextMateThemingRule[] = [];
|
||||
|
||||
// the default rule (scope empty) is always the first rule. Ignore all other default rules.
|
||||
const foreground = this.getColor(editorForeground) || this.getDefault(editorForeground)!;
|
||||
|
@ -71,7 +82,7 @@ export class ColorThemeData implements IColorTheme {
|
|||
|
||||
let hasDefaultTokens = false;
|
||||
|
||||
function addRule(rule: ITokenColorizationRule) {
|
||||
function addRule(rule: ITextMateThemingRule) {
|
||||
if (rule.scope && rule.settings) {
|
||||
if (rule.scope === 'token.info-token') {
|
||||
hasDefaultTokens = true;
|
||||
|
@ -103,10 +114,57 @@ export class ColorThemeData implements IColorTheme {
|
|||
return color;
|
||||
}
|
||||
|
||||
public getTokenStyle(tokenClassification: TokenClassification, useDefault?: boolean): TokenStyle | undefined {
|
||||
// todo: cache results
|
||||
return tokenClassificationRegistry.resolveTokenStyle(tokenClassification, this.tokenStylingRules, this.customTokenStylingRules, this);
|
||||
}
|
||||
|
||||
public getDefault(colorId: ColorIdentifier): Color | undefined {
|
||||
return colorRegistry.resolveDefaultColor(colorId, this);
|
||||
}
|
||||
|
||||
public getDefaultTokenStyle(tokenClassification: TokenClassification): TokenStyle | undefined {
|
||||
return tokenClassificationRegistry.resolveTokenStyle(tokenClassification, undefined, [], this);
|
||||
}
|
||||
|
||||
public resolveScopes(scopes: ProbeScope[]): TokenStyle | undefined {
|
||||
|
||||
if (!this.themeTokenScopeMatchers) {
|
||||
this.themeTokenScopeMatchers = this.themeTokenColors.map(getScopeMatcher);
|
||||
}
|
||||
if (!this.customTokenScopeMatchers) {
|
||||
this.customTokenScopeMatchers = this.customTokenColors.map(getScopeMatcher);
|
||||
}
|
||||
|
||||
for (let scope of scopes) {
|
||||
let foreground: string | undefined = undefined;
|
||||
let fontStyle: string | undefined = undefined;
|
||||
let foregroundScore = -1;
|
||||
let fontStyleScore = -1;
|
||||
|
||||
function findTokenStyleForScopeInScopes(scopeMatchers: Matcher<ProbeScope>[], tokenColors: ITextMateThemingRule[]) {
|
||||
for (let i = 0; i < scopeMatchers.length; i++) {
|
||||
const score = scopeMatchers[i](scope);
|
||||
if (score >= 0) {
|
||||
const settings = tokenColors[i].settings;
|
||||
if (score >= foregroundScore && settings.foreground) {
|
||||
foreground = settings.foreground;
|
||||
}
|
||||
if (score >= fontStyleScore && types.isString(settings.fontStyle)) {
|
||||
fontStyle = settings.fontStyle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
findTokenStyleForScopeInScopes(this.themeTokenScopeMatchers, this.themeTokenColors);
|
||||
findTokenStyleForScopeInScopes(this.customTokenScopeMatchers, this.customTokenColors);
|
||||
if (foreground !== undefined || fontStyle !== undefined) {
|
||||
return getTokenStyle(foreground, fontStyle);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public defines(colorId: ColorIdentifier): boolean {
|
||||
return this.customColorMap.hasOwnProperty(colorId) || this.colorMap.hasOwnProperty(colorId);
|
||||
}
|
||||
|
@ -132,6 +190,7 @@ export class ColorThemeData implements IColorTheme {
|
|||
|
||||
public setCustomTokenColors(customTokenColors: ITokenColorCustomizations) {
|
||||
this.customTokenColors = [];
|
||||
this.customTokenScopeMatchers = undefined;
|
||||
// first add the non-theme specific settings
|
||||
this.addCustomTokenColors(customTokenColors);
|
||||
|
||||
|
@ -142,6 +201,16 @@ export class ColorThemeData implements IColorTheme {
|
|||
}
|
||||
}
|
||||
|
||||
public setCustomTokenStyleRules(tokenStylingRules: IExperimentalTokenStyleCustomizations) {
|
||||
this.tokenStylingRules = [];
|
||||
readCustomTokenStyleRules(tokenStylingRules, this.tokenStylingRules);
|
||||
|
||||
const themeSpecificColors = tokenStylingRules[`[${this.settingsId}]`] as IExperimentalTokenStyleCustomizations;
|
||||
if (types.isObject(themeSpecificColors)) {
|
||||
readCustomTokenStyleRules(themeSpecificColors, this.tokenStylingRules);
|
||||
}
|
||||
}
|
||||
|
||||
private addCustomTokenColors(customTokenColors: ITokenColorCustomizations) {
|
||||
// Put the general customizations such as comments, strings, etc. first so that
|
||||
// they can be overridden by specific customizations like "string.interpolated"
|
||||
|
@ -180,9 +249,18 @@ export class ColorThemeData implements IColorTheme {
|
|||
return Promise.resolve(undefined);
|
||||
}
|
||||
this.themeTokenColors = [];
|
||||
this.colorMap = {};
|
||||
return _loadColorTheme(extensionResourceLoaderService, this.location, this.themeTokenColors, this.colorMap).then(_ => {
|
||||
this.themeTokenScopeMatchers = undefined;
|
||||
|
||||
const result = {
|
||||
colors: {},
|
||||
textMateRules: [],
|
||||
stylingRules: undefined
|
||||
};
|
||||
return _loadColorTheme(extensionResourceLoaderService, this.location, result).then(_ => {
|
||||
this.isLoaded = true;
|
||||
this.tokenStylingRules = result.stylingRules;
|
||||
this.colorMap = result.colors;
|
||||
this.themeTokenColors = result.textMateRules;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -295,7 +373,7 @@ function toCSSSelector(extensionId: string, path: string) {
|
|||
return str;
|
||||
}
|
||||
|
||||
function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoaderService, themeLocation: URI, resultRules: ITokenColorizationRule[], resultColors: IColorMap): Promise<any> {
|
||||
function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoaderService, themeLocation: URI, result: { textMateRules: ITextMateThemingRule[], colors: IColorMap, stylingRules: TokenStylingRule[] | undefined }): Promise<any> {
|
||||
if (resources.extname(themeLocation) === '.json') {
|
||||
return extensionResourceLoaderService.readExtensionResource(themeLocation).then(content => {
|
||||
let errors: Json.ParseError[] = [];
|
||||
|
@ -307,11 +385,11 @@ function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoade
|
|||
}
|
||||
let includeCompletes: Promise<any> = Promise.resolve(null);
|
||||
if (contentValue.include) {
|
||||
includeCompletes = _loadColorTheme(extensionResourceLoaderService, resources.joinPath(resources.dirname(themeLocation), contentValue.include), resultRules, resultColors);
|
||||
includeCompletes = _loadColorTheme(extensionResourceLoaderService, resources.joinPath(resources.dirname(themeLocation), contentValue.include), result);
|
||||
}
|
||||
return includeCompletes.then(_ => {
|
||||
if (Array.isArray(contentValue.settings)) {
|
||||
convertSettings(contentValue.settings, resultRules, resultColors);
|
||||
convertSettings(contentValue.settings, result);
|
||||
return null;
|
||||
}
|
||||
let colors = contentValue.colors;
|
||||
|
@ -323,38 +401,42 @@ function _loadColorTheme(extensionResourceLoaderService: IExtensionResourceLoade
|
|||
for (let colorId in colors) {
|
||||
let colorHex = colors[colorId];
|
||||
if (typeof colorHex === 'string') { // ignore colors tht are null
|
||||
resultColors[colorId] = Color.fromHex(colors[colorId]);
|
||||
result.colors[colorId] = Color.fromHex(colors[colorId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
let tokenColors = contentValue.tokenColors;
|
||||
if (tokenColors) {
|
||||
if (Array.isArray(tokenColors)) {
|
||||
resultRules.push(...tokenColors);
|
||||
result.textMateRules.push(...tokenColors);
|
||||
return null;
|
||||
} else if (typeof tokenColors === 'string') {
|
||||
return _loadSyntaxTokens(extensionResourceLoaderService, resources.joinPath(resources.dirname(themeLocation), tokenColors), resultRules, {});
|
||||
return _loadSyntaxTokens(extensionResourceLoaderService, resources.joinPath(resources.dirname(themeLocation), tokenColors), result);
|
||||
} else {
|
||||
return Promise.reject(new Error(nls.localize({ key: 'error.invalidformat.tokenColors', comment: ['{0} will be replaced by a path. Values in quotes should not be translated.'] }, "Problem parsing color theme file: {0}. Property 'tokenColors' should be either an array specifying colors or a path to a TextMate theme file", themeLocation.toString())));
|
||||
}
|
||||
}
|
||||
let tokenStylingRules = contentValue.tokenStylingRules;
|
||||
if (tokenStylingRules && typeof tokenStylingRules === 'object') {
|
||||
result.stylingRules = readCustomTokenStyleRules(tokenStylingRules, result.stylingRules);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return _loadSyntaxTokens(extensionResourceLoaderService, themeLocation, resultRules, resultColors);
|
||||
return _loadSyntaxTokens(extensionResourceLoaderService, themeLocation, result);
|
||||
}
|
||||
}
|
||||
|
||||
function _loadSyntaxTokens(extensionResourceLoaderService: IExtensionResourceLoaderService, themeLocation: URI, resultRules: ITokenColorizationRule[], resultColors: IColorMap): Promise<any> {
|
||||
function _loadSyntaxTokens(extensionResourceLoaderService: IExtensionResourceLoaderService, themeLocation: URI, result: { textMateRules: ITextMateThemingRule[], colors: IColorMap }): Promise<any> {
|
||||
return extensionResourceLoaderService.readExtensionResource(themeLocation).then(content => {
|
||||
try {
|
||||
let contentValue = parsePList(content);
|
||||
let settings: ITokenColorizationRule[] = contentValue.settings;
|
||||
let settings: ITextMateThemingRule[] = contentValue.settings;
|
||||
if (!Array.isArray(settings)) {
|
||||
return Promise.reject(new Error(nls.localize('error.plist.invalidformat', "Problem parsing tmTheme file: {0}. 'settings' is not array.")));
|
||||
}
|
||||
convertSettings(settings, resultRules, resultColors);
|
||||
convertSettings(settings, result);
|
||||
return Promise.resolve(null);
|
||||
} catch (e) {
|
||||
return Promise.reject(new Error(nls.localize('error.cannotparse', "Problems parsing tmTheme file: {0}", e.message)));
|
||||
|
@ -364,7 +446,7 @@ function _loadSyntaxTokens(extensionResourceLoaderService: IExtensionResourceLoa
|
|||
});
|
||||
}
|
||||
|
||||
let defaultThemeColors: { [baseTheme: string]: ITokenColorizationRule[] } = {
|
||||
let defaultThemeColors: { [baseTheme: string]: ITextMateThemingRule[] } = {
|
||||
'light': [
|
||||
{ scope: 'token.info-token', settings: { foreground: '#316bcd' } },
|
||||
{ scope: 'token.warn-token', settings: { foreground: '#cd9731' } },
|
||||
|
@ -384,3 +466,124 @@ let defaultThemeColors: { [baseTheme: string]: ITokenColorizationRule[] } = {
|
|||
{ scope: 'token.debug-token', settings: { foreground: '#b267e6' } }
|
||||
],
|
||||
};
|
||||
|
||||
const noMatch = (_scope: ProbeScope) => -1;
|
||||
|
||||
function nameMatcher(identifers: string[], scope: ProbeScope): number {
|
||||
function findInIdents(s: string, lastIndent: number): number {
|
||||
for (let i = lastIndent - 1; i >= 0; i--) {
|
||||
if (scopesAreMatching(s, identifers[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (scope.length < identifers.length) {
|
||||
return -1;
|
||||
}
|
||||
let lastScopeIndex = scope.length - 1;
|
||||
let lastIdentifierIndex = findInIdents(scope[lastScopeIndex--], identifers.length);
|
||||
if (lastIdentifierIndex >= 0) {
|
||||
const score = (lastIdentifierIndex + 1) * 0x10000 + scope.length;
|
||||
while (lastScopeIndex >= 0) {
|
||||
lastIdentifierIndex = findInIdents(scope[lastScopeIndex--], lastIdentifierIndex);
|
||||
if (lastIdentifierIndex === -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return score;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
function scopesAreMatching(thisScopeName: string, scopeName: string): boolean {
|
||||
if (!thisScopeName) {
|
||||
return false;
|
||||
}
|
||||
if (thisScopeName === scopeName) {
|
||||
return true;
|
||||
}
|
||||
const len = scopeName.length;
|
||||
return thisScopeName.length > len && thisScopeName.substr(0, len) === scopeName && thisScopeName[len] === '.';
|
||||
}
|
||||
|
||||
function getScopeMatcher(rule: ITextMateThemingRule): Matcher<ProbeScope> {
|
||||
const ruleScope = rule.scope;
|
||||
if (!ruleScope || !rule.settings) {
|
||||
return noMatch;
|
||||
}
|
||||
const matchers: MatcherWithPriority<ProbeScope>[] = [];
|
||||
if (Array.isArray(ruleScope)) {
|
||||
for (let rs of ruleScope) {
|
||||
createMatchers(rs, nameMatcher, matchers);
|
||||
}
|
||||
} else {
|
||||
createMatchers(ruleScope, nameMatcher, matchers);
|
||||
}
|
||||
|
||||
if (matchers.length === 0) {
|
||||
return noMatch;
|
||||
}
|
||||
return (scope: ProbeScope) => {
|
||||
let max = matchers[0].matcher(scope);
|
||||
for (let i = 1; i < matchers.length; i++) {
|
||||
max = Math.max(max, matchers[i].matcher(scope));
|
||||
}
|
||||
return max;
|
||||
};
|
||||
}
|
||||
|
||||
function getTokenStyle(foreground: string | undefined, fontStyle: string | undefined): TokenStyle {
|
||||
let foregroundColor = undefined;
|
||||
if (foreground !== undefined) {
|
||||
foregroundColor = Color.fromHex(foreground);
|
||||
}
|
||||
let bold, underline, italic;
|
||||
if (fontStyle !== undefined) {
|
||||
fontStyle = fontStyle.trim();
|
||||
if (fontStyle.length === 0) {
|
||||
bold = italic = underline = false;
|
||||
} else {
|
||||
const expression = /-?italic|-?bold|-?underline/g;
|
||||
let match;
|
||||
while ((match = expression.exec(fontStyle))) {
|
||||
switch (match[0]) {
|
||||
case 'bold': bold = true; break;
|
||||
case '-bold': bold = false; break;
|
||||
case 'italic': italic = true; break;
|
||||
case '-italic': italic = false; break;
|
||||
case 'underline': underline = true; break;
|
||||
case '-underline': underline = false; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new TokenStyle(foregroundColor, bold, underline, italic);
|
||||
|
||||
}
|
||||
|
||||
function readCustomTokenStyleRules(tokenStylingRuleSection: IExperimentalTokenStyleCustomizations, result: TokenStylingRule[] = []) {
|
||||
for (let key in tokenStylingRuleSection) {
|
||||
if (key[0] !== '[') {
|
||||
const classification = tokenClassificationRegistry.getTokenClassificationFromString(key);
|
||||
if (classification) {
|
||||
const settings = tokenStylingRuleSection[key];
|
||||
let style: TokenStyle | undefined;
|
||||
if (typeof settings === 'string') {
|
||||
style = getTokenStyle(settings, undefined);
|
||||
} else if (isTokenColorizationSetting(settings)) {
|
||||
style = getTokenStyle(settings.foreground, settings.fontStyle);
|
||||
}
|
||||
if (style) {
|
||||
result.push(tokenClassificationRegistry.getTokenStylingRule(classification, style));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function isTokenColorizationSetting(style: any): style is ITokenColorizationSetting {
|
||||
return style && (style.foreground || style.fontStyle);
|
||||
}
|
||||
|
|
134
src/vs/workbench/services/themes/common/textMateScopeMatcher.ts
Normal file
134
src/vs/workbench/services/themes/common/textMateScopeMatcher.ts
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
export interface MatcherWithPriority<T> {
|
||||
matcher: Matcher<T>;
|
||||
priority: -1 | 0 | 1;
|
||||
}
|
||||
|
||||
export interface Matcher<T> {
|
||||
(matcherInput: T): number;
|
||||
}
|
||||
|
||||
export function createMatchers<T>(selector: string, matchesName: (names: string[], matcherInput: T) => number, results: MatcherWithPriority<T>[]): void {
|
||||
const tokenizer = newTokenizer(selector);
|
||||
let token = tokenizer.next();
|
||||
while (token !== null) {
|
||||
let priority: -1 | 0 | 1 = 0;
|
||||
if (token.length === 2 && token.charAt(1) === ':') {
|
||||
switch (token.charAt(0)) {
|
||||
case 'R': priority = 1; break;
|
||||
case 'L': priority = -1; break;
|
||||
default:
|
||||
console.log(`Unknown priority ${token} in scope selector`);
|
||||
}
|
||||
token = tokenizer.next();
|
||||
}
|
||||
let matcher = parseConjunction();
|
||||
if (matcher) {
|
||||
results.push({ matcher, priority });
|
||||
}
|
||||
if (token !== ',') {
|
||||
break;
|
||||
}
|
||||
token = tokenizer.next();
|
||||
}
|
||||
|
||||
function parseOperand(): Matcher<T> | null {
|
||||
if (token === '-') {
|
||||
token = tokenizer.next();
|
||||
const expressionToNegate = parseOperand();
|
||||
if (!expressionToNegate) {
|
||||
return null;
|
||||
}
|
||||
return matcherInput => {
|
||||
const score = expressionToNegate(matcherInput);
|
||||
return score < 0 ? 0 : -1;
|
||||
};
|
||||
}
|
||||
if (token === '(') {
|
||||
token = tokenizer.next();
|
||||
const expressionInParents = parseInnerExpression();
|
||||
if (token === ')') {
|
||||
token = tokenizer.next();
|
||||
}
|
||||
return expressionInParents;
|
||||
}
|
||||
if (isIdentifier(token)) {
|
||||
const identifiers: string[] = [];
|
||||
do {
|
||||
identifiers.push(token);
|
||||
token = tokenizer.next();
|
||||
} while (isIdentifier(token));
|
||||
return matcherInput => matchesName(identifiers, matcherInput);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function parseConjunction(): Matcher<T> | null {
|
||||
let matcher = parseOperand();
|
||||
if (!matcher) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const matchers: Matcher<T>[] = [];
|
||||
while (matcher) {
|
||||
matchers.push(matcher);
|
||||
matcher = parseOperand();
|
||||
}
|
||||
return matcherInput => { // and
|
||||
let min = matchers[0](matcherInput);
|
||||
for (let i = 1; min >= 0 && i < matchers.length; i++) {
|
||||
min = Math.min(min, matchers[i](matcherInput));
|
||||
}
|
||||
return min;
|
||||
};
|
||||
}
|
||||
function parseInnerExpression(): Matcher<T> | null {
|
||||
let matcher = parseConjunction();
|
||||
if (!matcher) {
|
||||
return null;
|
||||
}
|
||||
const matchers: Matcher<T>[] = [];
|
||||
while (matcher) {
|
||||
matchers.push(matcher);
|
||||
if (token === '|' || token === ',') {
|
||||
do {
|
||||
token = tokenizer.next();
|
||||
} while (token === '|' || token === ','); // ignore subsequent commas
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
matcher = parseConjunction();
|
||||
}
|
||||
return matcherInput => { // or
|
||||
let max = matchers[0](matcherInput);
|
||||
for (let i = 1; i < matchers.length; i++) {
|
||||
max = Math.max(max, matchers[i](matcherInput));
|
||||
}
|
||||
return max;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function isIdentifier(token: string | null): token is string {
|
||||
return !!token && !!token.match(/[\w\.:]+/);
|
||||
}
|
||||
|
||||
function newTokenizer(input: string): { next: () => string | null } {
|
||||
let regex = /([LR]:|[\w\.:][\w\.:\-]*|[\,\|\-\(\)])/g;
|
||||
let match = regex.exec(input);
|
||||
return {
|
||||
next: () => {
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
const res = match[0];
|
||||
match = regex.exec(input);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ITokenColorizationRule, IColorMap } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { ITextMateThemingRule, IColorMap } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import * as colorRegistry from 'vs/platform/theme/common/colorRegistry';
|
||||
|
||||
|
@ -18,9 +18,9 @@ function addSettingMapping(settingId: string, colorId: string) {
|
|||
colorIds.push(colorId);
|
||||
}
|
||||
|
||||
export function convertSettings(oldSettings: ITokenColorizationRule[], resultRules: ITokenColorizationRule[], resultColors: IColorMap): void {
|
||||
export function convertSettings(oldSettings: ITextMateThemingRule[], result: { textMateRules: ITextMateThemingRule[], colors: IColorMap }): void {
|
||||
for (let rule of oldSettings) {
|
||||
resultRules.push(rule);
|
||||
result.textMateRules.push(rule);
|
||||
if (!rule.scope) {
|
||||
let settings = rule.settings;
|
||||
if (!settings) {
|
||||
|
@ -34,7 +34,7 @@ export function convertSettings(oldSettings: ITokenColorizationRule[], resultRul
|
|||
if (typeof colorHex === 'string') {
|
||||
let color = Color.fromHex(colorHex);
|
||||
for (let colorId of mappings) {
|
||||
resultColors[colorId] = color;
|
||||
result.colors[colorId] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ export const DETECT_HC_SETTING = 'window.autoDetectHighContrast';
|
|||
export const ICON_THEME_SETTING = 'workbench.iconTheme';
|
||||
export const CUSTOM_WORKBENCH_COLORS_SETTING = 'workbench.colorCustomizations';
|
||||
export const CUSTOM_EDITOR_COLORS_SETTING = 'editor.tokenColorCustomizations';
|
||||
export const CUSTOM_EDITOR_TOKENSTYLES_SETTING = 'editor.tokenColorCustomizationsExperimental';
|
||||
|
||||
export interface IColorTheme extends ITheme {
|
||||
readonly id: string;
|
||||
|
@ -30,7 +31,7 @@ export interface IColorTheme extends ITheme {
|
|||
readonly extensionData?: ExtensionData;
|
||||
readonly description?: string;
|
||||
readonly isLoaded: boolean;
|
||||
readonly tokenColors: ITokenColorizationRule[];
|
||||
readonly tokenColors: ITextMateThemingRule[];
|
||||
}
|
||||
|
||||
export interface IColorMap {
|
||||
|
@ -69,7 +70,7 @@ export interface IColorCustomizations {
|
|||
}
|
||||
|
||||
export interface ITokenColorCustomizations {
|
||||
[groupIdOrThemeSettingsId: string]: string | ITokenColorizationSetting | ITokenColorCustomizations | undefined | ITokenColorizationRule[];
|
||||
[groupIdOrThemeSettingsId: string]: string | ITokenColorizationSetting | ITokenColorCustomizations | undefined | ITextMateThemingRule[];
|
||||
comments?: string | ITokenColorizationSetting;
|
||||
strings?: string | ITokenColorizationSetting;
|
||||
numbers?: string | ITokenColorizationSetting;
|
||||
|
@ -77,10 +78,14 @@ export interface ITokenColorCustomizations {
|
|||
types?: string | ITokenColorizationSetting;
|
||||
functions?: string | ITokenColorizationSetting;
|
||||
variables?: string | ITokenColorizationSetting;
|
||||
textMateRules?: ITokenColorizationRule[];
|
||||
textMateRules?: ITextMateThemingRule[];
|
||||
}
|
||||
|
||||
export interface ITokenColorizationRule {
|
||||
export interface IExperimentalTokenStyleCustomizations {
|
||||
[styleRuleOrThemeSettingsId: string]: string | ITokenColorizationSetting | IExperimentalTokenStyleCustomizations | undefined;
|
||||
}
|
||||
|
||||
export interface ITextMateThemingRule {
|
||||
name?: string;
|
||||
scope?: string | string[];
|
||||
settings: ITokenColorizationSetting;
|
||||
|
@ -89,7 +94,7 @@ export interface ITokenColorizationRule {
|
|||
export interface ITokenColorizationSetting {
|
||||
foreground?: string;
|
||||
background?: string;
|
||||
fontStyle?: string; // italic, underline, bold
|
||||
fontStyle?: string; /* [italic|underline|bold] */
|
||||
}
|
||||
|
||||
export interface ExtensionData {
|
||||
|
@ -106,4 +111,4 @@ export interface IThemeExtensionPoint {
|
|||
path: string;
|
||||
uiTheme?: typeof VS_LIGHT_THEME | typeof VS_DARK_THEME | typeof VS_HC_THEME;
|
||||
_watch: boolean; // unsupported options to watch location
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ColorThemeData } from 'vs/workbench/services/themes/common/colorThemeData';
|
||||
import * as assert from 'assert';
|
||||
import { ITokenColorCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { TokenStyle, comments, variables, types, functions, keywords, numbers, strings, getTokenClassificationRegistry } from 'vs/platform/theme/common/tokenClassificationRegistry';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { isString } from 'vs/base/common/types';
|
||||
import { FileService } from 'vs/platform/files/common/fileService';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { ExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/electron-browser/extensionResourceLoaderService';
|
||||
|
||||
let tokenClassificationRegistry = getTokenClassificationRegistry();
|
||||
|
||||
const undefinedStyle = { bold: undefined, underline: undefined, italic: undefined };
|
||||
const unsetStyle = { bold: false, underline: false, italic: false };
|
||||
|
||||
function ts(foreground: string | undefined, styleFlags: { bold?: boolean; underline?: boolean; italic?: boolean } | undefined): TokenStyle {
|
||||
const foregroundColor = isString(foreground) ? Color.fromHex(foreground) : undefined;
|
||||
return new TokenStyle(foregroundColor, styleFlags && styleFlags.bold, styleFlags && styleFlags.underline, styleFlags && styleFlags.italic);
|
||||
}
|
||||
|
||||
function tokenStyleAsString(ts: TokenStyle | undefined | null) {
|
||||
if (!ts) {
|
||||
return 'tokenstyle-undefined';
|
||||
}
|
||||
let str = ts.foreground ? ts.foreground.toString() : 'no-foreground';
|
||||
if (ts.bold !== undefined) {
|
||||
str += ts.bold ? '+B' : '-B';
|
||||
}
|
||||
if (ts.underline !== undefined) {
|
||||
str += ts.underline ? '+U' : '-U';
|
||||
}
|
||||
if (ts.italic !== undefined) {
|
||||
str += ts.italic ? '+I' : '-I';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function assertTokenStyle(actual: TokenStyle | undefined | null, expected: TokenStyle | undefined | null, message?: string) {
|
||||
assert.equal(tokenStyleAsString(actual), tokenStyleAsString(expected), message);
|
||||
}
|
||||
|
||||
function assertTokenStyles(themeData: ColorThemeData, expected: { [qualifiedClassifier: string]: TokenStyle }) {
|
||||
for (let qualifiedClassifier in expected) {
|
||||
const classification = tokenClassificationRegistry.getTokenClassificationFromString(qualifiedClassifier);
|
||||
assert.ok(classification, 'Classification not found');
|
||||
|
||||
const tokenStyle = themeData.getTokenStyle(classification!);
|
||||
assertTokenStyle(tokenStyle, expected[qualifiedClassifier], qualifiedClassifier);
|
||||
}
|
||||
}
|
||||
|
||||
suite('Themes - TokenStyleResolving', () => {
|
||||
|
||||
|
||||
const fileService = new FileService(new NullLogService());
|
||||
const extensionResourceLoaderService = new ExtensionResourceLoaderService(fileService);
|
||||
|
||||
const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService());
|
||||
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
|
||||
|
||||
|
||||
test('color defaults - monokai', async () => {
|
||||
const themeData = ColorThemeData.createUnloadedTheme('foo');
|
||||
const themeLocation = getPathFromAmdModule(require, '../../../../../../../extensions/theme-monokai/themes/monokai-color-theme.json');
|
||||
themeData.location = URI.file(themeLocation);
|
||||
await themeData.ensureLoaded(extensionResourceLoaderService);
|
||||
|
||||
assert.equal(themeData.isLoaded, true);
|
||||
|
||||
assertTokenStyles(themeData, {
|
||||
[comments]: ts('#88846f', undefinedStyle),
|
||||
[variables]: ts('#F8F8F2', unsetStyle),
|
||||
[types]: ts('#A6E22E', { underline: true }),
|
||||
[functions]: ts('#A6E22E', unsetStyle),
|
||||
[strings]: ts('#E6DB74', undefinedStyle),
|
||||
[numbers]: ts('#AE81FF', undefinedStyle),
|
||||
[keywords]: ts('#F92672', undefinedStyle)
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('color defaults - dark+', async () => {
|
||||
const themeData = ColorThemeData.createUnloadedTheme('foo');
|
||||
const themeLocation = getPathFromAmdModule(require, '../../../../../../../extensions/theme-defaults/themes/dark_plus.json');
|
||||
themeData.location = URI.file(themeLocation);
|
||||
await themeData.ensureLoaded(extensionResourceLoaderService);
|
||||
|
||||
assert.equal(themeData.isLoaded, true);
|
||||
|
||||
assertTokenStyles(themeData, {
|
||||
[comments]: ts('#6A9955', undefinedStyle),
|
||||
[variables]: ts('#9CDCFE', undefinedStyle),
|
||||
[types]: ts('#4EC9B0', undefinedStyle),
|
||||
[functions]: ts('#DCDCAA', undefinedStyle),
|
||||
[strings]: ts('#CE9178', undefinedStyle),
|
||||
[numbers]: ts('#B5CEA8', undefinedStyle),
|
||||
[keywords]: ts('#C586C0', undefinedStyle)
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('color defaults - light vs', async () => {
|
||||
const themeData = ColorThemeData.createUnloadedTheme('foo');
|
||||
const themeLocation = getPathFromAmdModule(require, '../../../../../../../extensions/theme-defaults/themes/light_vs.json');
|
||||
themeData.location = URI.file(themeLocation);
|
||||
await themeData.ensureLoaded(extensionResourceLoaderService);
|
||||
|
||||
assert.equal(themeData.isLoaded, true);
|
||||
|
||||
assertTokenStyles(themeData, {
|
||||
[comments]: ts('#008000', undefinedStyle),
|
||||
[variables]: ts(undefined, undefinedStyle),
|
||||
[types]: ts(undefined, undefinedStyle),
|
||||
[functions]: ts(undefined, undefinedStyle),
|
||||
[strings]: ts('#a31515', undefinedStyle),
|
||||
[numbers]: ts('#09885a', undefinedStyle),
|
||||
[keywords]: ts('#0000ff', undefinedStyle)
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('color defaults - hc', async () => {
|
||||
const themeData = ColorThemeData.createUnloadedTheme('foo');
|
||||
const themeLocation = getPathFromAmdModule(require, '../../../../../../../extensions/theme-defaults/themes/hc_black.json');
|
||||
themeData.location = URI.file(themeLocation);
|
||||
await themeData.ensureLoaded(extensionResourceLoaderService);
|
||||
|
||||
assert.equal(themeData.isLoaded, true);
|
||||
|
||||
assertTokenStyles(themeData, {
|
||||
[comments]: ts('#7ca668', undefinedStyle),
|
||||
[variables]: ts('#9CDCFE', undefinedStyle),
|
||||
[types]: ts('#4EC9B0', undefinedStyle),
|
||||
[functions]: ts('#DCDCAA', undefinedStyle),
|
||||
[strings]: ts('#ce9178', undefinedStyle),
|
||||
[numbers]: ts('#b5cea8', undefinedStyle),
|
||||
[keywords]: ts('#C586C0', undefinedStyle)
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('color defaults - kimbie dark', async () => {
|
||||
const themeData = ColorThemeData.createUnloadedTheme('foo');
|
||||
const themeLocation = getPathFromAmdModule(require, '../../../../../../../extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json');
|
||||
themeData.location = URI.file(themeLocation);
|
||||
await themeData.ensureLoaded(extensionResourceLoaderService);
|
||||
|
||||
assert.equal(themeData.isLoaded, true);
|
||||
|
||||
assertTokenStyles(themeData, {
|
||||
[comments]: ts('#a57a4c', undefinedStyle),
|
||||
[variables]: ts('#dc3958', undefinedStyle),
|
||||
[types]: ts('#f06431', undefinedStyle),
|
||||
[functions]: ts('#8ab1b0', undefinedStyle),
|
||||
[strings]: ts('#889b4a', undefinedStyle),
|
||||
[numbers]: ts('#f79a32', undefinedStyle),
|
||||
[keywords]: ts('#98676a', undefinedStyle)
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('color defaults - abyss', async () => {
|
||||
const themeData = ColorThemeData.createUnloadedTheme('foo');
|
||||
const themeLocation = getPathFromAmdModule(require, '../../../../../../../extensions/theme-abyss/themes/abyss-color-theme.json');
|
||||
themeData.location = URI.file(themeLocation);
|
||||
await themeData.ensureLoaded(extensionResourceLoaderService);
|
||||
|
||||
assert.equal(themeData.isLoaded, true);
|
||||
|
||||
assertTokenStyles(themeData, {
|
||||
[comments]: ts('#384887', undefinedStyle),
|
||||
[variables]: ts(undefined, unsetStyle),
|
||||
[types]: ts('#ffeebb', { underline: true }),
|
||||
[functions]: ts('#ddbb88', unsetStyle),
|
||||
[strings]: ts('#22aa44', undefinedStyle),
|
||||
[numbers]: ts('#f280d0', undefinedStyle),
|
||||
[keywords]: ts('#225588', undefinedStyle)
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('resolveScopes', async () => {
|
||||
const themeData = ColorThemeData.createLoadedEmptyTheme('test', 'test');
|
||||
|
||||
const customTokenColors: ITokenColorCustomizations = {
|
||||
textMateRules: [
|
||||
{
|
||||
scope: 'variable',
|
||||
settings: {
|
||||
fontStyle: '',
|
||||
foreground: '#F8F8F2'
|
||||
}
|
||||
},
|
||||
{
|
||||
scope: 'keyword.operator',
|
||||
settings: {
|
||||
fontStyle: 'italic bold underline',
|
||||
foreground: '#F92672'
|
||||
}
|
||||
},
|
||||
{
|
||||
scope: 'storage',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
foreground: '#F92672'
|
||||
}
|
||||
},
|
||||
{
|
||||
scope: ['storage.type', 'meta.structure.dictionary.json string.quoted.double.json'],
|
||||
settings: {
|
||||
foreground: '#66D9EF'
|
||||
}
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.type, entity.name.class, entity.name.namespace, entity.name.scope-resolution',
|
||||
settings: {
|
||||
fontStyle: 'underline',
|
||||
foreground: '#A6E22E'
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
themeData.setCustomTokenColors(customTokenColors);
|
||||
|
||||
let tokenStyle;
|
||||
let defaultTokenStyle = undefined;
|
||||
|
||||
tokenStyle = themeData.resolveScopes([['variable']]);
|
||||
assertTokenStyle(tokenStyle, ts('#F8F8F2', unsetStyle), 'variable');
|
||||
|
||||
tokenStyle = themeData.resolveScopes([['keyword.operator']]);
|
||||
assertTokenStyle(tokenStyle, ts('#F92672', { italic: true, bold: true, underline: true }), 'keyword');
|
||||
|
||||
tokenStyle = themeData.resolveScopes([['keyword']]);
|
||||
assertTokenStyle(tokenStyle, defaultTokenStyle, 'keyword');
|
||||
|
||||
tokenStyle = themeData.resolveScopes([['keyword.operator']]);
|
||||
assertTokenStyle(tokenStyle, ts('#F92672', { italic: true, bold: true, underline: true }), 'keyword.operator');
|
||||
|
||||
tokenStyle = themeData.resolveScopes([['keyword.operators']]);
|
||||
assertTokenStyle(tokenStyle, defaultTokenStyle, 'keyword.operators');
|
||||
|
||||
tokenStyle = themeData.resolveScopes([['storage']]);
|
||||
assertTokenStyle(tokenStyle, ts('#F92672', { italic: true }), 'storage');
|
||||
|
||||
tokenStyle = themeData.resolveScopes([['storage.type']]);
|
||||
assertTokenStyle(tokenStyle, ts('#66D9EF', { italic: true }), 'storage.type');
|
||||
|
||||
tokenStyle = themeData.resolveScopes([['entity.name.class']]);
|
||||
assertTokenStyle(tokenStyle, ts('#A6E22E', { underline: true }), 'entity.name.class');
|
||||
|
||||
tokenStyle = themeData.resolveScopes([['meta.structure.dictionary.json', 'string.quoted.double.json']]);
|
||||
assertTokenStyle(tokenStyle, ts('#66D9EF', undefined), 'json property');
|
||||
|
||||
tokenStyle = themeData.resolveScopes([['keyword'], ['storage.type'], ['entity.name.class']]);
|
||||
assertTokenStyle(tokenStyle, ts('#66D9EF', { italic: true }), 'storage.type');
|
||||
|
||||
});
|
||||
|
||||
test('rule matching', async () => {
|
||||
const themeData = ColorThemeData.createLoadedEmptyTheme('test', 'test');
|
||||
themeData.setCustomColors({ 'editor.foreground': '#000000' });
|
||||
themeData.setCustomTokenStyleRules({
|
||||
'types': '#ff0000',
|
||||
'classes': { foreground: '#0000ff', fontStyle: 'italic' },
|
||||
'*.static': { fontStyle: 'bold' },
|
||||
'*.declaration': { fontStyle: 'italic' },
|
||||
'*.async.static': { fontStyle: 'italic underline' },
|
||||
'*.async': { foreground: '#000fff', fontStyle: '-italic underline' }
|
||||
});
|
||||
|
||||
assertTokenStyles(themeData, {
|
||||
'types': ts('#ff0000', undefinedStyle),
|
||||
'types.static': ts('#ff0000', { bold: true }),
|
||||
'types.static.declaration': ts('#ff0000', { bold: true, italic: true }),
|
||||
'classes': ts('#0000ff', { italic: true }),
|
||||
'classes.static.declaration': ts('#0000ff', { bold: true, italic: true }),
|
||||
'classes.declaration': ts('#0000ff', { italic: true }),
|
||||
'classes.declaration.async': ts('#000fff', { underline: true, italic: false }),
|
||||
'classes.declaration.async.static': ts('#000fff', { italic: true, underline: true, bold: true }),
|
||||
});
|
||||
|
||||
});
|
||||
});
|
|
@ -70,5 +70,7 @@ suite('Link protection domain matching', () => {
|
|||
|
||||
linkNotAllowedByRules('https://a.x.org/bar', ['https://*.x.org/foo']);
|
||||
linkNotAllowedByRules('https://a.b.x.org/bar', ['https://*.x.org/foo']);
|
||||
|
||||
linkAllowedByRules('https://github.com', ['https://github.com/foo/bar', 'https://github.com']);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -35,7 +35,7 @@ suite('ExtHostConfiguration', function () {
|
|||
if (!shape) {
|
||||
shape = new class extends mock<MainThreadConfigurationShape>() { };
|
||||
}
|
||||
return new ExtHostConfigProvider(shape, createExtHostWorkspace(), createConfigurationData(contents));
|
||||
return new ExtHostConfigProvider(shape, createExtHostWorkspace(), createConfigurationData(contents), new NullLogService());
|
||||
}
|
||||
|
||||
function createConfigurationData(contents: any): IConfigurationInitData {
|
||||
|
@ -283,7 +283,8 @@ suite('ExtHostConfiguration', function () {
|
|||
workspace: new ConfigurationModel({}, []),
|
||||
folders: [],
|
||||
configurationScopes: []
|
||||
}
|
||||
},
|
||||
new NullLogService()
|
||||
);
|
||||
|
||||
let actual = testObject.getConfiguration().inspect('editor.wordWrap')!;
|
||||
|
@ -331,7 +332,8 @@ suite('ExtHostConfiguration', function () {
|
|||
workspace,
|
||||
folders,
|
||||
configurationScopes: []
|
||||
}
|
||||
},
|
||||
new NullLogService()
|
||||
);
|
||||
|
||||
let actual1 = testObject.getConfiguration().inspect('editor.wordWrap')!;
|
||||
|
@ -407,7 +409,8 @@ suite('ExtHostConfiguration', function () {
|
|||
workspace,
|
||||
folders,
|
||||
configurationScopes: []
|
||||
}
|
||||
},
|
||||
new NullLogService()
|
||||
);
|
||||
|
||||
let actual1 = testObject.getConfiguration().inspect('editor.wordWrap')!;
|
||||
|
@ -607,7 +610,8 @@ suite('ExtHostConfiguration', function () {
|
|||
'config': false,
|
||||
'updatedconfig': false
|
||||
}
|
||||
})
|
||||
}),
|
||||
new NullLogService()
|
||||
);
|
||||
|
||||
const newConfigData = createConfigurationData({
|
||||
|
|
|
@ -81,6 +81,7 @@ import 'vs/workbench/services/notification/common/notificationService';
|
|||
import 'vs/workbench/services/extensions/common/staticExtensions';
|
||||
import 'vs/workbench/services/userDataSync/common/settingsMergeService';
|
||||
import 'vs/workbench/services/path/common/remotePathService';
|
||||
import 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
|
||||
|
|
|
@ -64,7 +64,7 @@ import { NoOpTunnelService } from 'vs/platform/remote/common/tunnelService';
|
|||
import { ILoggerService } from 'vs/platform/log/common/log';
|
||||
import { FileLoggerService } from 'vs/platform/log/common/fileLogService';
|
||||
import { IAuthTokenService } from 'vs/platform/auth/common/auth';
|
||||
import { AuthTokenService } from 'vs/platform/auth/common/authTokenService';
|
||||
import { AuthTokenService } from 'vs/workbench/services/authToken/browser/authTokenService';
|
||||
import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
|
||||
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
|
||||
|
|
15
yarn.lock
15
yarn.lock
|
@ -133,6 +133,13 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/http-proxy-agent@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/http-proxy-agent/-/http-proxy-agent-2.0.1.tgz#2f95077f6bfe7adc39cc0f0042da85997ae77fc7"
|
||||
integrity sha512-dgsgbsgI3t+ZkdzF9H19uBaLsurIZJJjJsVpj4mCLp8B6YghQ7jVwyqhaL0PcVtuC3nOi0ZBhAi2Dd9jCUwdFA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/iconv-lite@0.0.1":
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/iconv-lite/-/iconv-lite-0.0.1.tgz#aa3b8bda2be512b1ae0a057b942e869c370a5569"
|
||||
|
@ -9033,10 +9040,10 @@ vscode-nsfw@1.2.8:
|
|||
lodash.isundefined "^3.0.1"
|
||||
nan "^2.10.0"
|
||||
|
||||
vscode-proxy-agent@^0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-proxy-agent/-/vscode-proxy-agent-0.5.1.tgz#7fd15e157c02176a0dca9f87840ad0991a62ca57"
|
||||
integrity sha512-Nnkc7gBk9iAbbZURYZm3p/wvDvRVwDvdzEvDqF1Jh40p6przwQU/JTlV1XLrmd4cCwh6TOAWD81Z3cq+K7Xdmw==
|
||||
vscode-proxy-agent@^0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-proxy-agent/-/vscode-proxy-agent-0.5.2.tgz#0c90d24d353957b841d741da7b2701e3f0a044c4"
|
||||
integrity sha512-1cCNPxrWIrmUwS+1XGaXxkh3G1y7z2fpXl1sT74OZvELaryQWYb3NMxMLJJ4Q/CpPLEyuhp/bAN7nzHxxFcQ5Q==
|
||||
dependencies:
|
||||
debug "^3.1.0"
|
||||
http-proxy-agent "^2.1.0"
|
||||
|
|
Loading…
Reference in a new issue