Merge from vscode e558dc6ea73a75bd69d7a0b485f0e7e4194c66bf (#6864)

This commit is contained in:
Anthony Dresser
2019-08-21 20:44:59 -07:00
committed by GitHub
parent d2ae0f0154
commit 985bfae8a0
107 changed files with 2260 additions and 814 deletions

View File

@@ -330,6 +330,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
return {
label: data.a,
kind: data.b,
kindModifier: data.n ? modes.CompletionItemKindModifier.Deprecated : undefined,
detail: data.c,
documentation: data.d,
sortText: data.e,
@@ -366,7 +367,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
};
if (supportsResolveDetails) {
provider.resolveCompletionItem = (model, position, suggestion, token) => {
return this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion._id, token).then(result => {
return this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion._id!, token).then(result => {
if (!result) {
return suggestion;
}

View File

@@ -3,20 +3,46 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ILogService } from 'vs/platform/log/common/log';
import { Disposable } from 'vs/base/common/lifecycle';
import { IExtHostContext, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IExtHostContext, ExtHostContext, MainThreadLogShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { UriComponents, URI } from 'vs/base/common/uri';
import { FileLogService } from 'vs/platform/log/common/fileLogService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { basename } from 'vs/base/common/path';
@extHostCustomer
export class MainThreadLogService extends Disposable {
@extHostNamedCustomer(MainContext.MainThreadLog)
export class MainThreadLogService implements MainThreadLogShape {
private readonly _loggers = new Map<string, FileLogService>();
private readonly _logListener: IDisposable;
constructor(
extHostContext: IExtHostContext,
@ILogService logService: ILogService,
@ILogService private readonly _logService: ILogService,
@IInstantiationService private readonly _instaService: IInstantiationService,
) {
super();
this._register(logService.onDidChangeLogLevel(level => extHostContext.getProxy(ExtHostContext.ExtHostLogService).$setLevel(level)));
const proxy = extHostContext.getProxy(ExtHostContext.ExtHostLogService);
this._logListener = _logService.onDidChangeLogLevel(level => {
proxy.$setLevel(level);
this._loggers.forEach(value => value.setLevel(level));
});
}
}
dispose(): void {
this._logListener.dispose();
this._loggers.forEach(value => value.dispose());
this._loggers.clear();
}
$log(file: UriComponents, level: LogLevel, message: any[]): void {
const uri = URI.revive(file);
let logger = this._loggers.get(uri.toString());
if (!logger) {
logger = this._instaService.createInstance(FileLogService, basename(file.path), URI.revive(file), this._logService.getLevel());
this._loggers.set(uri.toString(), logger);
}
logger.log(level, message);
}
}

View File

@@ -292,7 +292,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
viewStates[handle] = {
visible: input === group.activeEditor,
active: input === activeInput,
position: editorGroupToViewColumn(this._editorGroupService, group.id || 0),
position: editorGroupToViewColumn(this._editorGroupService, group.id),
};
}
}
@@ -304,10 +304,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
}
private onDidClickLink(handle: WebviewPanelHandle, link: URI): void {
if (!link) {
return;
}
const webview = this.getWebviewEditorInput(handle);
if (this.isSupportedLink(webview, link)) {
this._openerService.open(link);

View File

@@ -939,6 +939,7 @@ export interface ISuggestDataDto {
k/* commitCharacters */?: string[];
l/* additionalTextEdits */?: ISingleEditOperation[];
m/* command */?: modes.Command;
n/* deprecated */?: boolean;
// not-standard
x?: ChainedCacheId;
}
@@ -1287,6 +1288,10 @@ export interface ExtHostLogServiceShape {
$setLevel(level: LogLevel): void;
}
export interface MainThreadLogShape {
$log(file: UriComponents, level: LogLevel, args: any[]): void;
}
export interface ExtHostOutputServiceShape {
$setVisibleChannel(channelId: string | null): void;
}
@@ -1329,6 +1334,7 @@ export const MainContext = {
MainThreadKeytar: createMainId<MainThreadKeytarShape>('MainThreadKeytar'),
MainThreadLanguageFeatures: createMainId<MainThreadLanguageFeaturesShape>('MainThreadLanguageFeatures'),
MainThreadLanguages: createMainId<MainThreadLanguagesShape>('MainThreadLanguages'),
MainThreadLog: createMainId<MainThreadLogShape>('MainThread'),
MainThreadMessageService: createMainId<MainThreadMessageServiceShape>('MainThreadMessageService'),
MainThreadOutputService: createMainId<MainThreadOutputServiceShape>('MainThreadOutputService'),
MainThreadProgress: createMainId<MainThreadProgressShape>('MainThreadProgress'),

View File

@@ -736,6 +736,7 @@ class SuggestAdapter {
k: item.commitCharacters,
l: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from),
m: this._commands.toInternal(item.command, disposables),
n: item.deprecated
};
// 'insertText'-logic

View File

@@ -8,7 +8,6 @@ import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHost
import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { endsWith, startsWith } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { joinPath } from 'vs/base/common/resources';
import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor';
@@ -128,7 +127,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
const next = joinPath(parent, '..', ensureSuffix(mod, '.js'));
moduleStack.push(next);
const trap = ExportsTrap.Instance.add(next.toString());
importScripts(asDomUri(next).toString(true));
importScripts(next.toString(true));
moduleStack.pop();
return trap.claim();
@@ -139,7 +138,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
module = module.with({ path: ensureSuffix(module.path, '.js') });
moduleStack.push(module);
const trap = ExportsTrap.Instance.add(module.toString());
importScripts(asDomUri(module).toString(true));
importScripts(module.toString(true));
moduleStack.pop();
return Promise.resolve<T>(trap.claim());
@@ -153,16 +152,6 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
}
}
// todo@joh this is a copy of `dom.ts#asDomUri`
function asDomUri(uri: URI): URI {
if (Schemas.vscodeRemote === uri.scheme) {
// rewrite vscode-remote-uris to uris of the window location
// so that they can be intercepted by the service worker
return URI.parse(window.location.href).with({ path: '/vscode-remote', query: JSON.stringify(uri) });
}
return uri;
}
function ensureSuffix(path: string, suffix: string): string {
return endsWith(path, suffix) ? path : path + suffix;
}

View File

@@ -4,24 +4,33 @@
*--------------------------------------------------------------------------------------------*/
import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log';
import { ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostLogServiceShape, MainThreadLogShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput';
import * as vscode from 'vscode';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { joinPath } from 'vs/base/common/resources';
import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions';
import { UriComponents } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
export class ExtHostLogService extends AbstractLogService implements ILogService, ExtHostLogServiceShape {
_serviceBrand: any;
private readonly _logChannel: vscode.OutputChannel;
private readonly _proxy: MainThreadLogShape;
private readonly _logFile: UriComponents;
constructor(
@IExtHostRpcService rpc: IExtHostRpcService,
@IExtHostInitDataService initData: IExtHostInitDataService,
@IExtHostOutputService extHostOutputService: IExtHostOutputService
) {
super();
const logFile = joinPath(initData.logsLocation, `${ExtensionHostLogFileName}.log`);
this._proxy = rpc.getProxy(MainContext.MainThreadLog);
this._logFile = logFile.toJSON();
this.setLevel(initData.logLevel);
this._logChannel = extHostOutputService.createOutputChannel('Log (Worker Extension Host)');
extHostOutputService.createOutputChannelFromLogFile(localize('name', "Worker Extension Host"), logFile);
}
$setLevel(level: LogLevel): void {
@@ -30,55 +39,37 @@ export class ExtHostLogService extends AbstractLogService implements ILogService
trace(_message: string, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Trace) {
this._logChannel.appendLine(this._format(arguments));
this._proxy.$log(this._logFile, LogLevel.Trace, Array.from(arguments));
}
}
debug(_message: string, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Debug) {
this._logChannel.appendLine(this._format(arguments));
this._proxy.$log(this._logFile, LogLevel.Debug, Array.from(arguments));
}
}
info(_message: string, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Info) {
this._logChannel.appendLine(this._format(arguments));
this._proxy.$log(this._logFile, LogLevel.Info, Array.from(arguments));
}
}
warn(_message: string, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Warning) {
this._logChannel.appendLine(this._format(arguments));
this._proxy.$log(this._logFile, LogLevel.Warning, Array.from(arguments));
}
}
error(_message: string | Error, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Error) {
this._logChannel.appendLine(this._format(arguments));
this._proxy.$log(this._logFile, LogLevel.Error, Array.from(arguments));
}
}
critical(_message: string | Error, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Critical) {
this._logChannel.appendLine(String(arguments));
this._proxy.$log(this._logFile, LogLevel.Critical, Array.from(arguments));
}
}
private _format(args: any): string {
let result = '';
for (let i = 0; i < args.length; i++) {
let a = args[i];
if (typeof a === 'object') {
try {
a = JSON.stringify(a);
} catch (e) { }
}
result += (i > 0 ? ' ' : '') + a;
}
return result;
}
}

View File

@@ -8,7 +8,6 @@ import { domContentLoaded, addDisposableListener, EventType, addClass } from 'vs
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { ILogService } from 'vs/platform/log/common/log';
import { Disposable } from 'vs/base/common/lifecycle';
import { SimpleLogService } from 'vs/workbench/browser/web.simpleservices';
import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { Workbench } from 'vs/workbench/browser/workbench';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
@@ -42,6 +41,10 @@ import { getThemeTypeSelector, DARK, HIGH_CONTRAST, LIGHT } from 'vs/platform/th
import { InMemoryUserDataProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider';
import { registerWindowDriver } from 'vs/platform/driver/browser/driver';
import { StaticExtensionsService, IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions';
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
import { FileLogService } from 'vs/platform/log/common/fileLogService';
import { toLocalISOString } from 'vs/base/common/date';
import { INDEXEDDB_LOG_SCHEME, IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedDBLogProvider';
class CodeRendererMain extends Disposable {
@@ -117,13 +120,14 @@ class CodeRendererMain extends Disposable {
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Log
const logService = new SimpleLogService();
const logsPath = URI.file(toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')).with({ scheme: INDEXEDDB_LOG_SCHEME });
const logService = new BufferLogService();
serviceCollection.set(ILogService, logService);
const payload = await this.resolveWorkspaceInitializationPayload();
// Environment
const environmentService = new BrowserWorkbenchEnvironmentService(payload.id, this.configuration);
const environmentService = new BrowserWorkbenchEnvironmentService({ workspaceId: payload.id, logsPath, ...this.configuration });
serviceCollection.set(IWorkbenchEnvironmentService, environmentService);
// Product
@@ -146,6 +150,10 @@ class CodeRendererMain extends Disposable {
const fileService = this._register(new FileService(logService));
serviceCollection.set(IFileService, fileService);
// Logger
fileService.registerProvider(INDEXEDDB_LOG_SCHEME, new IndexedDBLogProvider());
logService.logger = new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService);
// Static Extensions
const staticExtensions = new StaticExtensionsService(this.configuration.staticExtensions || []);
serviceCollection.set(IStaticExtensionsService, staticExtensions);

View File

@@ -8,10 +8,9 @@ import * as browser from 'vs/base/browser/browser';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Event } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionTipsService, ExtensionRecommendationReason, IExtensionRecommendation } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IURLHandler, IURLService } from 'vs/platform/url/common/url';
import { ConsoleLogService, ILogService } from 'vs/platform/log/common/log';
import { ILogService } from 'vs/platform/log/common/log';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IUpdateService, State } from 'vs/platform/update/common/update';
@@ -31,7 +30,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { toStoreData, restoreRecentlyOpened } from 'vs/platform/history/common/historyStorage';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IProductService } from 'vs/platform/product/common/product';
import Severity from 'vs/base/common/severity';
@@ -40,55 +38,6 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
// tslint:disable-next-line: import-patterns
import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats';
//#region Extension Tips
export class SimpleExtensionTipsService implements IExtensionTipsService {
_serviceBrand: any;
onRecommendationChange = Event.None;
getAllRecommendationsWithReason(): { [id: string]: { reasonId: ExtensionRecommendationReason; reasonText: string; }; } {
return Object.create(null);
}
getFileBasedRecommendations(): IExtensionRecommendation[] {
return [];
}
getOtherRecommendations(): Promise<IExtensionRecommendation[]> {
return Promise.resolve([]);
}
getWorkspaceRecommendations(): Promise<IExtensionRecommendation[]> {
return Promise.resolve([]);
}
getKeymapRecommendations(): IExtensionRecommendation[] {
return [];
}
toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void {
}
getAllIgnoredRecommendations(): { global: string[]; workspace: string[]; } {
return { global: [], workspace: [] };
}
// {{SQL CARBON EDIT}}
getRecommendedExtensionsByScenario(scenarioType: string): Promise<IExtensionRecommendation[]> {
return Promise.resolve([]);
}
promptRecommendedExtensionsByScenario(scenarioType: string): void {
return;
}
// {{SQL CARBON EDIT}} - End
}
registerSingleton(IExtensionTipsService, SimpleExtensionTipsService, true);
//#endregion
//#region Extension URL Handler
export const IExtensionUrlHandler = createDecorator<IExtensionUrlHandler>('inactiveExtensionUrlHandler');
@@ -112,12 +61,6 @@ registerSingleton(IExtensionUrlHandler, SimpleExtensionURLHandler, true);
//#endregion
//#region Log
export class SimpleLogService extends ConsoleLogService { }
//#endregion
//#region Update
export class SimpleUpdateService implements IUpdateService {
@@ -192,7 +135,6 @@ export class SimpleWindowService extends Disposable implements IWindowService {
@IStorageService private readonly storageService: IStorageService,
@IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService,
@ILogService private readonly logService: ILogService,
@IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService
) {
super();
@@ -388,7 +330,7 @@ export class SimpleWindowService extends Disposable implements IWindowService {
for (let i = 0; i < _uris.length; i++) {
const uri = _uris[i];
if ('folderUri' in uri) {
const newAddress = `${document.location.origin}/?folder=${uri.folderUri.path}${this.workbenchEnvironmentService.configuration.connectionToken ? `&tkn=${this.workbenchEnvironmentService.configuration.connectionToken}` : ''}`;
const newAddress = `${document.location.origin}/?folder=${uri.folderUri.path}`;
if (openFolderInNewWindow) {
window.open(newAddress);
} else {
@@ -475,7 +417,6 @@ export class SimpleWindowsService implements IWindowsService {
readonly onRecentlyOpenedChange: Event<void> = Event.None;
constructor(
@IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService,
@IDialogService private readonly dialogService: IDialogService,
@IProductService private readonly productService: IProductService,
@IClipboardService private readonly clipboardService: IClipboardService
@@ -667,11 +608,6 @@ export class SimpleWindowsService implements IWindowsService {
addQueryParameter('ibe', ibe);
}
// add connection token
if (this.workbenchEnvironmentService.configuration.connectionToken) {
addQueryParameter('tkn', this.workbenchEnvironmentService.configuration.connectionToken);
}
window.open(newAddress);
return Promise.resolve();

View File

@@ -9,7 +9,7 @@ import * as dom from 'vs/base/browser/dom';
import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput';
import { Widget } from 'vs/base/browser/ui/widget';
import { Delayer } from 'vs/base/common/async';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { KeyCode } from 'vs/base/common/keyCodes';
import { FindReplaceState } from 'vs/editor/contrib/find/findState';
import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
import { SimpleButton } from 'vs/editor/contrib/find/findWidget';
@@ -42,8 +42,7 @@ export abstract class SimpleFindWidget extends Widget {
@IContextViewService private readonly _contextViewService: IContextViewService,
@IContextKeyService contextKeyService: IContextKeyService,
private readonly _state: FindReplaceState = new FindReplaceState(),
showOptionButtons?: boolean,
private readonly _invertDefaultDirection: boolean = false
showOptionButtons?: boolean
) {
super();
@@ -94,20 +93,6 @@ export abstract class SimpleFindWidget extends Widget {
this.findFirst();
}));
this._register(this._findInput.onKeyDown((e) => {
if (e.equals(KeyCode.Enter)) {
this.find(this._invertDefaultDirection);
e.preventDefault();
return;
}
if (e.equals(KeyMod.Shift | KeyCode.Enter)) {
this.find(!this._invertDefaultDirection);
e.preventDefault();
return;
}
}));
this.prevBtn = this._register(new SimpleButton({
label: NLS_PREVIOUS_MATCH_BTN_LABEL,
className: 'previous',

View File

@@ -593,13 +593,13 @@ class CallStackDataSource implements IAsyncDataSource<IDebugModel, CallStackItem
return isDebugModel(element) || isDebugSession(element) || (element instanceof Thread && element.stopped);
}
getChildren(element: IDebugModel | CallStackItem): Promise<CallStackItem[]> {
async getChildren(element: IDebugModel | CallStackItem): Promise<CallStackItem[]> {
if (isDebugModel(element)) {
const sessions = element.getSessions();
if (sessions.length === 0) {
return Promise.resolve([]);
}
if (sessions.length > 1 || this.debugService.getViewModel().isMultiSessionView()) {
if (sessions.length > 1) {
return Promise.resolve(sessions.filter(s => !s.parentSession));
}
@@ -609,9 +609,10 @@ class CallStackDataSource implements IAsyncDataSource<IDebugModel, CallStackItem
} else if (isDebugSession(element)) {
const childSessions = this.debugService.getModel().getSessions().filter(s => s.parentSession === element);
const threads: CallStackItem[] = element.getAllThreads();
if (threads.length === 1 && childSessions.length === 0) {
if (threads.length === 1) {
// Do not show thread when there is only one to be compact.
return this.getThreadChildren(<Thread>threads[0]);
const children = await this.getThreadChildren(<Thread>threads[0]);
return children.concat(childSessions);
}
return Promise.resolve(threads.concat(childSessions));

View File

@@ -245,6 +245,9 @@ export class DebugHoverWidget implements IContentWidget {
this.layoutTreeAndContainer();
this.editor.layoutContentWidget(this);
this.scrollbar.scanDomNode();
this.tree.scrollTop = 0;
this.tree.scrollLeft = 0;
if (focus) {
this.editor.render();
this.tree.domFocus();

View File

@@ -479,11 +479,11 @@ export class DebugService implements IDebugService {
});
}
private launchOrAttachToSession(session: IDebugSession, focus = true): Promise<void> {
private launchOrAttachToSession(session: IDebugSession, forceFocus = false): Promise<void> {
const dbgr = this.configurationManager.getDebugger(session.configuration.type);
return session.initialize(dbgr!).then(() => {
return session.launchOrAttach(session.configuration).then(() => {
if (focus) {
if (forceFocus || !this.viewModel.focusedSession) {
this.focusStackFrame(undefined, undefined, session);
}
});
@@ -572,7 +572,7 @@ export class DebugService implements IDebugService {
return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? this.extensionHostDebugService.reload(session.getId()) : undefined);
}
const shouldFocus = this.viewModel.focusedSession && session.getId() === this.viewModel.focusedSession.getId();
const shouldFocus = !!this.viewModel.focusedSession && session.getId() === this.viewModel.focusedSession.getId();
// If the restart is automatic -> disconnect, otherwise -> terminate #55064
return (isAutoRestart ? session.disconnect(true) : session.terminate(true)).then(() => {

View File

@@ -131,6 +131,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution {
private registerListeners(): void {
this._register(this.debugService.onDidChangeState(() => this.updateScheduler.schedule()));
this._register(this.debugService.getViewModel().onDidFocusSession(() => this.updateScheduler.schedule()));
this._register(this.debugService.onDidNewSession(() => this.updateScheduler.schedule()));
this._register(this.configurationService.onDidChangeConfiguration(e => this.onDidConfigurationChange(e)));
this._register(this.actionBar.actionRunner.onDidRun((e: IRunEvent) => {
// check for error

View File

@@ -50,7 +50,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry';
import { isUndefined } from 'vs/base/common/types';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/browser/webview';
import { IWebviewService, Webview, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/webview/browser/webview';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { renderDashboardContributions } from 'sql/workbench/parts/extensions/browser/contributionRenders'; // {{SQL CARBON EDIT}}
import { generateUuid } from 'vs/base/common/uuid';
@@ -536,6 +536,12 @@ export class ExtensionEditor extends BaseEditor {
}
}
runFindAction(previous: boolean): void {
if (this.activeElement && (<Webview>this.activeElement).runFindAction) {
(<Webview>this.activeElement).runFindAction(previous);
}
}
private onNavbarChange(extension: IExtension, { id, focus }: { id: string | null, focus: boolean }, template: IExtensionEditorTemplate): void {
if (this.editorLoadComplete) {
/* __GDPR__
@@ -1326,28 +1332,66 @@ export class ExtensionEditor extends BaseEditor {
}
}
const contextKeyExpr = ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', ExtensionEditor.ID), ContextKeyExpr.not('editorFocus'));
class ShowExtensionEditorFindCommand extends Command {
public runCommand(accessor: ServicesAccessor, args: any): void {
const extensionEditor = this.getExtensionEditor(accessor);
const extensionEditor = getExtensionEditor(accessor);
if (extensionEditor) {
extensionEditor.showFind();
}
}
private getExtensionEditor(accessor: ServicesAccessor): ExtensionEditor | null {
const activeControl = accessor.get(IEditorService).activeControl as ExtensionEditor;
if (activeControl instanceof ExtensionEditor) {
return activeControl;
}
return null;
}
}
const showCommand = new ShowExtensionEditorFindCommand({
(new ShowExtensionEditorFindCommand({
id: 'editor.action.extensioneditor.showfind',
precondition: ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', ExtensionEditor.ID), ContextKeyExpr.not('editorFocus')),
precondition: contextKeyExpr,
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_F,
weight: KeybindingWeight.EditorContrib
}
});
showCommand.register();
})).register();
class StartExtensionEditorFindNextCommand extends Command {
public runCommand(accessor: ServicesAccessor, args: any): void {
const extensionEditor = getExtensionEditor(accessor);
if (extensionEditor) {
extensionEditor.runFindAction(false);
}
}
}
(new StartExtensionEditorFindNextCommand({
id: 'editor.action.extensioneditor.findNext',
precondition: ContextKeyExpr.and(
contextKeyExpr,
KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED),
kbOpts: {
primary: KeyCode.Enter,
weight: KeybindingWeight.EditorContrib
}
})).register();
class StartExtensionEditorFindPreviousCommand extends Command {
public runCommand(accessor: ServicesAccessor, args: any): void {
const extensionEditor = getExtensionEditor(accessor);
if (extensionEditor) {
extensionEditor.runFindAction(true);
}
}
}
(new StartExtensionEditorFindPreviousCommand({
id: 'editor.action.extensioneditor.findPrevious',
precondition: ContextKeyExpr.and(
contextKeyExpr,
KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED),
kbOpts: {
primary: KeyMod.Shift | KeyCode.Enter,
weight: KeybindingWeight.EditorContrib
}
})).register();
function getExtensionEditor(accessor: ServicesAccessor): ExtensionEditor | null {
const activeControl = accessor.get(IEditorService).activeControl as ExtensionEditor;
if (activeControl instanceof ExtensionEditor) {
return activeControl;
}
return null;
}

View File

@@ -1,11 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService';
// Singletons
registerSingleton(IExtensionTipsService, ExtensionTipsService);

View File

@@ -3100,7 +3100,6 @@ export class InstallLocalExtensionsInRemoteAction extends Action {
private extensions: IExtension[] | undefined = undefined;
constructor(
private readonly selectAndInstall: boolean,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
@@ -3122,9 +3121,7 @@ export class InstallLocalExtensionsInRemoteAction extends Action {
get label(): string {
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
return this.selectAndInstall ?
localize('select and install local extensions', "Install Local Extensions in {0}...", this.extensionManagementServerService.remoteExtensionManagementServer.label)
: localize('install local extensions', "Install Local Extensions in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label);
return localize('select and install local extensions', "Install Local Extensions in {0}...", this.extensionManagementServerService.remoteExtensionManagementServer.label);
}
return '';
}
@@ -3140,12 +3137,7 @@ export class InstallLocalExtensionsInRemoteAction extends Action {
}
async run(): Promise<void> {
if (this.selectAndInstall) {
return this.selectAndInstallLocalExtensions();
} else {
const extensionsToInstall = await this.queryExtensionsToInstall();
return this.installLocalExtensions(extensionsToInstall);
}
return this.selectAndInstallLocalExtensions();
}
private async queryExtensionsToInstall(): Promise<IExtension[]> {

View File

@@ -961,7 +961,7 @@ export class ServerExtensionsView extends ExtensionsListView {
getActions(): IAction[] {
if (this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManagementServerService.localExtensionManagementServer === this.server) {
const installLocalExtensionsInRemoteAction = this._register(this.instantiationService.createInstance(InstallLocalExtensionsInRemoteAction, false));
const installLocalExtensionsInRemoteAction = this._register(this.instantiationService.createInstance(InstallLocalExtensionsInRemoteAction));
installLocalExtensionsInRemoteAction.class = 'octicon octicon-cloud-download';
return [installLocalExtensionsInRemoteAction];
}

View File

@@ -22,7 +22,7 @@ export class RemoteExtensionsInstaller extends Disposable implements IWorkbenchC
) {
super();
if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
const installLocalExtensionsInRemoteAction = instantiationService.createInstance(InstallLocalExtensionsInRemoteAction, true);
const installLocalExtensionsInRemoteAction = instantiationService.createInstance(InstallLocalExtensionsInRemoteAction);
CommandsRegistry.registerCommand('workbench.extensions.installLocalExtensions', () => installLocalExtensionsInRemoteAction.run());
let disposable = Disposable.None;
const appendMenuItem = () => {

View File

@@ -11,6 +11,7 @@ import { IDecorationsProvider, IDecorationData } from 'vs/workbench/services/dec
import { listInvalidItemForeground } from 'vs/platform/theme/common/colorRegistry';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IExplorerService } from 'vs/workbench/contrib/files/common/files';
import { explorerRootErrorEmitter } from 'vs/workbench/contrib/files/browser/views/explorerViewer';
export class ExplorerDecorationsProvider implements IDecorationsProvider {
readonly label: string = localize('label', "Explorer");
@@ -30,6 +31,9 @@ export class ExplorerDecorationsProvider implements IDecorationsProvider {
this._onDidChange.fire([change.item.resource]);
}
}));
this.toDispose.add(explorerRootErrorEmitter.event((resource => {
this._onDidChange.fire([resource]);
})));
}
get onDidChange(): Event<URI[]> {

View File

@@ -50,6 +50,7 @@ import { first } from 'vs/base/common/arrays';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
import { dispose } from 'vs/base/common/lifecycle';
import { timeout } from 'vs/base/common/async';
export class ExplorerView extends ViewletPanel {
static readonly ID: string = 'workbench.explorer.fileView';
@@ -522,6 +523,8 @@ export class ExplorerView extends ViewletPanel {
while (item && item.resource.toString() !== resource.toString()) {
await this.tree.expand(item);
// Tree returns too early from the expand, need to wait for next tick #77106
await timeout(0);
item = first(values(item.children), i => isEqualOrParent(resource, i.resource));
}

View File

@@ -46,6 +46,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces';
import { findValidPasteFileTarget } from 'vs/workbench/contrib/files/browser/fileActions';
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
import { Emitter } from 'vs/base/common/event';
export class ExplorerDelegate implements IListVirtualDelegate<ExplorerItem> {
@@ -60,6 +61,7 @@ export class ExplorerDelegate implements IListVirtualDelegate<ExplorerItem> {
}
}
export const explorerRootErrorEmitter = new Emitter<URI>();
export class ExplorerDataSource implements IAsyncDataSource<ExplorerItem | ExplorerItem[], ExplorerItem> {
constructor(
@@ -87,8 +89,9 @@ export class ExplorerDataSource implements IAsyncDataSource<ExplorerItem | Explo
// Single folder create a dummy explorer item to show error
const placeholder = new ExplorerItem(element.resource, undefined, false);
placeholder.isError = true;
return [placeholder];
} else {
explorerRootErrorEmitter.fire(element.resource);
}
} else {
// Do not show error for roots since we already use an explorer decoration to notify user

View File

@@ -247,9 +247,14 @@ export class ExplorerItem {
// Resolve metadata only when the mtime is needed since this can be expensive
// Mtime is only used when the sort order is 'modified'
const resolveMetadata = explorerService.sortOrder === 'modified';
const stat = await fileService.resolve(this.resource, { resolveSingleChildDescendants: true, resolveMetadata });
const resolved = ExplorerItem.create(stat, this);
ExplorerItem.mergeLocalWithDisk(resolved, this);
try {
const stat = await fileService.resolve(this.resource, { resolveSingleChildDescendants: true, resolveMetadata });
const resolved = ExplorerItem.create(stat, this);
ExplorerItem.mergeLocalWithDisk(resolved, this);
} catch (e) {
this.isError = true;
throw e;
}
this._isDirectoryResolved = true;
}

View File

@@ -4,11 +4,90 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { join } from 'vs/base/common/path';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { SetLogLevelAction } from 'vs/workbench/contrib/logs/common/logsActions';
import { SetLogLevelAction, OpenLogsFolderAction, OpenSessionLogFileAction } from 'vs/workbench/contrib/logs/common/logsActions';
import * as Constants from 'vs/workbench/contrib/logs/common/logConstants';
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IFileService, FileChangeType } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { IOutputChannelRegistry, Extensions as OutputExt } from 'vs/workbench/contrib/output/common/output';
import { Disposable } from 'vs/base/common/lifecycle';
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
import { dirname } from 'vs/base/common/resources';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { isWeb } from 'vs/base/common/platform';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { LogsDataCleaner } from 'vs/workbench/contrib/logs/common/logsDataCleaner';
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
const devCategory = nls.localize('developer', "Developer");
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SetLogLevelAction, SetLogLevelAction.ID, SetLogLevelAction.LABEL), 'Developer: Set Log Level...', devCategory);
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SetLogLevelAction, SetLogLevelAction.ID, SetLogLevelAction.LABEL), 'Developer: Set Log Level...', devCategory);
class LogOutputChannels extends Disposable implements IWorkbenchContribution {
constructor(
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@ILogService private readonly logService: ILogService,
@IFileService private readonly fileService: IFileService,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
super();
if (isWeb) {
this.registerWebContributions();
} else {
this.registerNativeContributions();
}
}
private registerWebContributions(): void {
Registry.as<IOutputChannelRegistry>(OutputExt.OutputChannels).registerChannel({ id: Constants.rendererLogChannelId, label: nls.localize('rendererLog', "Window"), file: this.environmentService.logFile, log: true });
this.instantiationService.createInstance(LogsDataCleaner);
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
const devCategory = nls.localize('developer', "Developer");
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenSessionLogFileAction, OpenSessionLogFileAction.ID, OpenSessionLogFileAction.LABEL), 'Developer: Open Log File (Session)...', devCategory);
}
private registerNativeContributions(): void {
this.registerLogChannel(Constants.mainLogChannelId, nls.localize('mainLog', "Main"), URI.file(join(this.environmentService.logsPath, `main.log`)));
this.registerLogChannel(Constants.sharedLogChannelId, nls.localize('sharedLog', "Shared"), URI.file(join(this.environmentService.logsPath, `sharedprocess.log`)));
this.registerLogChannel(Constants.rendererLogChannelId, nls.localize('rendererLog', "Window"), this.environmentService.logFile);
const registerTelemetryChannel = (level: LogLevel) => {
if (level === LogLevel.Trace && !Registry.as<IOutputChannelRegistry>(OutputExt.OutputChannels).getChannel(Constants.telemetryLogChannelId)) {
this.registerLogChannel(Constants.telemetryLogChannelId, nls.localize('telemetryLog', "Telemetry"), URI.file(join(this.environmentService.logsPath, `telemetry.log`)));
}
};
registerTelemetryChannel(this.logService.getLevel());
this.logService.onDidChangeLogLevel(registerTelemetryChannel);
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
const devCategory = nls.localize('developer', "Developer");
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLogsFolderAction, OpenLogsFolderAction.ID, OpenLogsFolderAction.LABEL), 'Developer: Open Logs Folder', devCategory);
}
private async registerLogChannel(id: string, label: string, file: URI): Promise<void> {
const outputChannelRegistry = Registry.as<IOutputChannelRegistry>(OutputExt.OutputChannels);
const exists = await this.fileService.exists(file);
if (exists) {
outputChannelRegistry.registerChannel({ id, label, file, log: true });
return;
}
const watcher = this.fileService.watch(dirname(file));
const disposable = this.fileService.onFileChanges(e => {
if (e.contains(file, FileChangeType.ADDED)) {
watcher.dispose();
disposable.dispose();
outputChannelRegistry.registerChannel({ id, label, file, log: true });
}
});
}
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogOutputChannels, LifecyclePhase.Restored);

View File

@@ -9,8 +9,12 @@ import { join } from 'vs/base/common/path';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { ILogService, LogLevel, DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { dirname, basename, isEqual } from 'vs/base/common/resources';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
export class OpenLogsFolderAction extends Action {
@@ -73,3 +77,68 @@ export class SetLogLevelAction extends Action {
return undefined;
}
}
export class OpenSessionLogFileAction extends Action {
static ID = 'workbench.action.openSessionLogFile';
static LABEL = nls.localize('openSessionLogFile', "Open Log File (Session)...");
constructor(id: string, label: string,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IFileService private readonly fileService: IFileService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IEditorService private readonly editorService: IEditorService,
) {
super(id, label);
}
async run(): Promise<void> {
const sessionResult = await this.quickInputService.pick(
this.getSessions().then(sessions => sessions.map((s, index) => (<IQuickPickItem>{
id: s.toString(),
label: basename(s),
description: index === 0 ? nls.localize('current', "Current") : undefined
}))),
{
canPickMany: false,
placeHolder: nls.localize('sessions placeholder', "Select Session")
});
if (sessionResult) {
const logFileResult = await this.quickInputService.pick(
this.getLogFiles(URI.parse(sessionResult.id!)).then(logFiles => logFiles.map(s => (<IQuickPickItem>{
id: s.toString(),
label: basename(s)
}))),
{
canPickMany: false,
placeHolder: nls.localize('log placeholder', "Select Log file")
});
if (logFileResult) {
return this.editorService.openEditor({ resource: URI.parse(logFileResult.id!) }).then(() => undefined);
}
}
}
private async getSessions(): Promise<URI[]> {
const logsPath = URI.file(this.environmentService.logsPath).with({ scheme: this.environmentService.logFile.scheme });
const result: URI[] = [logsPath];
const stat = await this.fileService.resolve(dirname(logsPath));
if (stat.children) {
result.push(...stat.children
.filter(stat => !isEqual(stat.resource, logsPath) && stat.isDirectory && /^\d{8}T\d{6}$/.test(stat.name))
.sort()
.reverse()
.map(d => d.resource));
}
return result;
}
private async getLogFiles(session: URI): Promise<URI[]> {
const stat = await this.fileService.resolve(session);
if (stat.children) {
return stat.children.filter(stat => !stat.isDirectory).map(stat => stat.resource);
}
return [];
}
}

View File

@@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { IFileService } from 'vs/platform/files/common/files';
import { basename, dirname } from 'vs/base/common/resources';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { URI } from 'vs/base/common/uri';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
export class LogsDataCleaner extends Disposable {
constructor(
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IFileService private readonly fileService: IFileService,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
) {
super();
this.cleanUpOldLogsSoon();
}
private cleanUpOldLogsSoon(): void {
let handle: NodeJS.Timeout | undefined = setTimeout(async () => {
handle = undefined;
const logsPath = URI.file(this.environmentService.logsPath).with({ scheme: this.environmentService.logFile.scheme });
const stat = await this.fileService.resolve(dirname(logsPath));
if (stat.children) {
const currentLog = basename(logsPath);
const allSessions = stat.children.filter(stat => stat.isDirectory && /^\d{8}T\d{6}$/.test(stat.name));
const oldSessions = allSessions.sort().filter((d, i) => d.name !== currentLog);
const toDelete = oldSessions.slice(0, Math.max(0, oldSessions.length - 49));
Promise.all(toDelete.map(stat => this.fileService.del(stat.resource, { recursive: true })));
}
}, 10 * 1000);
this.lifecycleService.onWillShutdown(() => {
if (handle) {
clearTimeout(handle);
handle = undefined;
}
});
}
}

View File

@@ -1,68 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { join } from 'vs/base/common/path';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/contrib/output/common/output';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import * as Constants from 'vs/workbench/contrib/logs/common/logConstants';
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { OpenLogsFolderAction } from 'vs/workbench/contrib/logs/common/logsActions';
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
import { IFileService, FileChangeType } from 'vs/platform/files/common/files';
import { dirname } from 'vs/base/common/resources';
class LogOutputChannels extends Disposable implements IWorkbenchContribution {
constructor(
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@ILogService logService: ILogService,
@IFileService private readonly fileService: IFileService
) {
super();
this.registerLogChannel(Constants.mainLogChannelId, nls.localize('mainLog', "Main"), URI.file(join(environmentService.logsPath, `main.log`)));
this.registerLogChannel(Constants.sharedLogChannelId, nls.localize('sharedLog', "Shared"), URI.file(join(environmentService.logsPath, `sharedprocess.log`)));
this.registerLogChannel(Constants.rendererLogChannelId, nls.localize('rendererLog', "Window"), URI.file(join(environmentService.logsPath, `renderer${environmentService.configuration.windowId}.log`)));
const registerTelemetryChannel = (level: LogLevel) => {
if (level === LogLevel.Trace && !Registry.as<IOutputChannelRegistry>(OutputExt.OutputChannels).getChannel(Constants.telemetryLogChannelId)) {
this.registerLogChannel(Constants.telemetryLogChannelId, nls.localize('telemetryLog', "Telemetry"), URI.file(join(environmentService.logsPath, `telemetry.log`)));
}
};
registerTelemetryChannel(logService.getLevel());
logService.onDidChangeLogLevel(registerTelemetryChannel);
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
const devCategory = nls.localize('developer', "Developer");
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLogsFolderAction, OpenLogsFolderAction.ID, OpenLogsFolderAction.LABEL), 'Developer: Open Logs Folder', devCategory);
}
private async registerLogChannel(id: string, label: string, file: URI): Promise<void> {
const outputChannelRegistry = Registry.as<IOutputChannelRegistry>(OutputExt.OutputChannels);
const exists = await this.fileService.exists(file);
if (exists) {
outputChannelRegistry.registerChannel({ id, label, file, log: true });
return;
}
const watcher = this.fileService.watch(dirname(file));
const disposable = this.fileService.onFileChanges(e => {
if (e.contains(file, FileChangeType.ADDED)) {
watcher.dispose();
disposable.dispose();
outputChannelRegistry.registerChannel({ id, label, file, log: true });
}
});
}
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogOutputChannels, LifecyclePhase.Restored);

View File

@@ -109,7 +109,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
) {
super(KeybindingsEditor.ID, telemetryService, themeService, storageService);
this.delayedFiltering = new Delayer<void>(300);
this._register(keybindingsService.onDidUpdateKeybindings(() => this.render(true)));
this._register(keybindingsService.onDidUpdateKeybindings(() => this.render(!!this.keybindingFocusContextKey.get())));
this.keybindingsEditorContextKey = CONTEXT_KEYBINDINGS_EDITOR.bindTo(this.contextKeyService);
this.searchFocusContextKey = CONTEXT_KEYBINDINGS_SEARCH_FOCUS.bindTo(this.contextKeyService);
@@ -537,7 +537,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
}
this.unAssignedKeybindingItemToRevealAndFocus = null;
} else if (currentSelectedIndex !== -1 && currentSelectedIndex < this.listEntries.length) {
this.selectEntry(currentSelectedIndex);
this.selectEntry(currentSelectedIndex, preserveFocus);
} else if (this.editorService.activeControl === this && !preserveFocus) {
this.focus();
}
@@ -597,11 +597,13 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
return -1;
}
private selectEntry(keybindingItemEntry: IKeybindingItemEntry | number): void {
private selectEntry(keybindingItemEntry: IKeybindingItemEntry | number, focus: boolean = true): void {
const index = typeof keybindingItemEntry === 'number' ? keybindingItemEntry : this.getIndexOf(keybindingItemEntry);
if (index !== -1) {
this.keybindingsList.getHTMLElement().focus();
this.keybindingsList.setFocus([index]);
if (focus) {
this.keybindingsList.getHTMLElement().focus();
this.keybindingsList.setFocus([index]);
}
this.keybindingsList.setSelection([index]);
}
}

View File

@@ -115,6 +115,7 @@ export class TOCRenderer implements ITreeRenderer<SettingsTreeGroupElement, neve
const label = element.label;
template.labelElement.textContent = label;
template.labelElement.title = label;
if (count) {
template.countElement.textContent = ` (${count})`;

View File

@@ -34,7 +34,7 @@ self.addEventListener('activate', event => {
//#region --- fetching/caching
const _cacheName = 'vscode-extension-resources';
const _resourcePrefix = '/vscode-remote';
const _resourcePrefix = '/vscode-remote-resource';
const _pendingFetch = new Map<string, Function>();
self.addEventListener('message', event => {

View File

@@ -10,7 +10,7 @@
// statement.
// trigger service worker updates
const _tag = '52278406-3ca9-48af-a8fb-8495add5bb4e';
const _tag = '23549971-9b8d-41bb-92ae-d7f6a68c9702';
// loader world
const baseUrl = '../../../../../';

View File

@@ -530,7 +530,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNe
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find next', category);
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID, FindNext.LABEL, {
primary: KeyCode.F3,
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] }
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3, KeyMod.Shift | KeyCode.Enter] }
}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find next');
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID_TERMINAL_FOCUS, FindPrevious.LABEL, {
primary: KeyMod.Shift | KeyCode.F3,
@@ -538,7 +538,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, Fi
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find previous', category);
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID, FindPrevious.LABEL, {
primary: KeyMod.Shift | KeyCode.F3,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] },
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3, KeyCode.Enter] },
}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find previous');

View File

@@ -19,7 +19,7 @@ export class TerminalFindWidget extends SimpleFindWidget {
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@ITerminalService private readonly _terminalService: ITerminalService
) {
super(_contextViewService, _contextKeyService, findState, true, true);
super(_contextViewService, _contextKeyService, findState, true);
this._register(findState.onFindReplaceStateChange(() => {
this.show();
}));

View File

@@ -26,6 +26,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
export class TerminalService extends CommonTerminalService implements ITerminalService {
private _configHelper: IBrowserTerminalConfigHelper;
private _terminalContainer: HTMLElement | undefined;
public get configHelper(): ITerminalConfigHelper { return this._configHelper; }

View File

@@ -35,7 +35,6 @@ export abstract class TerminalService implements ITerminalService {
protected _isShuttingDown: boolean;
protected _terminalFocusContextKey: IContextKey<boolean>;
protected _findWidgetVisible: IContextKey<boolean>;
protected _terminalContainer: HTMLElement | undefined;
protected _terminalTabs: ITerminalTab[] = [];
protected _backgroundedTerminalInstances: ITerminalInstance[] = [];
protected get _terminalInstances(): ITerminalInstance[] {

View File

@@ -14,17 +14,24 @@ import { Action } from 'vs/base/common/actions';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IProductService } from 'vs/platform/product/common/product';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { equalsIgnoreCase } from 'vs/base/common/strings';
import { Schemas } from 'vs/base/common/network';
import Severity from 'vs/base/common/severity';
export class OpenUrlAction extends Action {
static readonly ID = 'workbench.action.url.openUrl';
static readonly LABEL = localize('openUrl', "Open URL");
static readonly LABEL = localize('openUrl', 'Open URL');
constructor(
id: string,
label: string,
@IURLService private readonly urlService: IURLService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IQuickInputService private readonly quickInputService: IQuickInputService
) {
super(id, label);
}
@@ -45,7 +52,7 @@ Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions).registe
const VSCODE_DOMAIN = 'https://code.visualstudio.com';
const configureTrustedDomainsHandler = (
const configureTrustedDomainsHandler = async (
quickInputService: IQuickInputService,
storageService: IStorageService,
domainToConfigure?: string
@@ -66,7 +73,7 @@ const configureTrustedDomainsHandler = (
type: 'item',
label: d,
id: d,
picked: true,
picked: true
};
});
@@ -91,23 +98,24 @@ const configureTrustedDomainsHandler = (
specialQuickPickItems.push(<IQuickPickItem>domainToConfigureItem);
}
const quickPickItems: (IQuickPickItem | IQuickPickSeparator)[] = domainQuickPickItems.length === 0
? specialQuickPickItems
: [...specialQuickPickItems, { type: 'separator' }, ...domainQuickPickItems];
const quickPickItems: (IQuickPickItem | IQuickPickSeparator)[] =
domainQuickPickItems.length === 0
? specialQuickPickItems
: [...specialQuickPickItems, { type: 'separator' }, ...domainQuickPickItems];
return quickInputService.pick(quickPickItems, {
const pickedResult = await quickInputService.pick(quickPickItems, {
canPickMany: true,
activeItem: domainToConfigureItem
}).then(result => {
if (result) {
const pickedDomains = result.map(r => r.id);
storageService.store('http.trustedDomains', JSON.stringify(pickedDomains), StorageScope.GLOBAL);
return pickedDomains;
}
return [];
});
if (pickedResult) {
const pickedDomains: string[] = pickedResult.map(r => r.id!);
storageService.store('http.trustedDomains', JSON.stringify(pickedDomains), StorageScope.GLOBAL);
return pickedDomains;
}
return [];
};
const configureTrustedDomainCommand = {
@@ -131,3 +139,93 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
title: configureTrustedDomainCommand.description.description
}
});
class OpenerValidatorContributions implements IWorkbenchContribution {
constructor(
@IOpenerService private readonly _openerService: IOpenerService,
@IStorageService private readonly _storageService: IStorageService,
@IDialogService private readonly _dialogService: IDialogService,
@IProductService private readonly _productService: IProductService,
@IQuickInputService private readonly _quickInputService: IQuickInputService
) {
this._openerService.registerValidator({ shouldOpen: r => this.validateLink(r) });
}
async validateLink(resource: URI): Promise<boolean> {
const { scheme, authority } = resource;
if (!equalsIgnoreCase(scheme, Schemas.http) && !equalsIgnoreCase(scheme, Schemas.https)) {
return true;
}
let trustedDomains: string[] = [VSCODE_DOMAIN];
try {
const trustedDomainsSrc = this._storageService.get('http.trustedDomains', StorageScope.GLOBAL);
if (trustedDomainsSrc) {
trustedDomains = JSON.parse(trustedDomainsSrc);
}
} catch (err) { }
const domainToOpen = `${scheme}://${authority}`;
if (isDomainTrusted(domainToOpen, trustedDomains)) {
return true;
} else {
const choice = await this._dialogService.show(
Severity.Info,
localize(
'openExternalLinkAt',
'Do you want {0} to open the external website?\n{1}',
this._productService.nameShort,
resource.toString(true)
),
[
localize('openLink', 'Open Link'),
localize('cancel', 'Cancel'),
localize('configureTrustedDomains', 'Configure Trusted Domains')
],
{
cancelId: 1
}
);
// Open Link
if (choice === 0) {
return true;
}
// Configure Trusted Domains
else if (choice === 2) {
const pickedDomains = await configureTrustedDomainsHandler(this._quickInputService, this._storageService, domainToOpen);
if (pickedDomains.indexOf(domainToOpen) !== -1) {
return true;
}
return false;
}
return false;
}
}
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(
OpenerValidatorContributions,
LifecyclePhase.Restored
);
/**
* Check whether a domain like https://www.microsoft.com matches
* the list of trusted domains.
*/
function isDomainTrusted(domain: string, trustedDomains: string[]) {
for (let i = 0; i < trustedDomains.length; i++) {
if (trustedDomains[i] === '*') {
return true;
}
if (trustedDomains[i] === domain) {
return true;
}
}
return false;
}

View File

@@ -153,6 +153,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewEd
reload(): void { this.withWebview(webview => webview.reload()); }
showFind(): void { this.withWebview(webview => webview.showFind()); }
hideFind(): void { this.withWebview(webview => webview.hideFind()); }
runFindAction(previous: boolean): void { this.withWebview(webview => webview.runFindAction(previous)); }
public getInnerWebview() {
return this._webview.value;

View File

@@ -15,8 +15,8 @@ import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } fro
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor';
import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory';
import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/browser/webview';
import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand } from '../browser/webviewCommands';
import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategory, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/webview/browser/webview';
import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand, WebViewEditorFindNextCommand, WebViewEditorFindPreviousCommand } from '../browser/webviewCommands';
import { WebviewEditor } from '../browser/webviewEditor';
import { WebviewEditorInput } from '../browser/webviewEditorInput';
import { IWebviewEditorService, WebviewEditorService } from '../browser/webviewEditorService';
@@ -58,6 +58,28 @@ function registerWebViewCommands(editorId: string): void {
weight: KeybindingWeight.EditorContrib
}
})).register();
(new WebViewEditorFindNextCommand({
id: WebViewEditorFindNextCommand.ID,
precondition: ContextKeyExpr.and(
contextKeyExpr,
KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED),
kbOpts: {
primary: KeyCode.Enter,
weight: KeybindingWeight.EditorContrib
}
})).register();
(new WebViewEditorFindPreviousCommand({
id: WebViewEditorFindPreviousCommand.ID,
precondition: ContextKeyExpr.and(
contextKeyExpr,
KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED),
kbOpts: {
primary: KeyMod.Shift | KeyCode.Enter,
weight: KeybindingWeight.EditorContrib
}
})).register();
}
registerWebViewCommands(WebviewEditor.ID);

View File

@@ -16,6 +16,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
* Set when the find widget in a webview is visible.
*/
export const KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE = new RawContextKey<boolean>('webviewFindWidgetVisible', false);
export const KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED = new RawContextKey<boolean>('webviewFindWidgetFocused', false);
export const IWebviewService = createDecorator<IWebviewService>('webviewService');
@@ -83,6 +84,7 @@ export interface Webview extends IDisposable {
showFind(): void;
hideFind(): void;
runFindAction(previous: boolean): void;
}
export interface WebviewElement extends Webview {

View File

@@ -32,6 +32,27 @@ export class HideWebViewEditorFindCommand extends Command {
}
}
export class WebViewEditorFindNextCommand extends Command {
public static readonly ID = 'editor.action.webvieweditor.findNext';
public runCommand(accessor: ServicesAccessor, args: any): void {
const webViewEditor = getActiveWebviewEditor(accessor);
if (webViewEditor) {
webViewEditor.find(false);
}
}
}
export class WebViewEditorFindPreviousCommand extends Command {
public static readonly ID = 'editor.action.webvieweditor.findPrevious';
public runCommand(accessor: ServicesAccessor, args: any): void {
const webViewEditor = getActiveWebviewEditor(accessor);
if (webViewEditor) {
webViewEditor.find(true);
}
}
}
export class ReloadWebviewAction extends Action {
static readonly ID = 'workbench.action.webview.reloadWebviewAction';
static readonly LABEL = nls.localize('refreshWebviewLabel', "Reload Webviews");
@@ -62,4 +83,4 @@ function getActiveWebviewEditor(accessor: ServicesAccessor): WebviewEditor | nul
const editorService = accessor.get(IEditorService);
const activeControl = editorService.activeControl as WebviewEditor;
return activeControl.isWebviewEditor ? activeControl : null;
}
}

View File

@@ -25,7 +25,6 @@ export class WebviewEditor extends BaseEditor {
private readonly _scopedContextKeyService = this._register(new MutableDisposable<IContextKeyService>());
private _findWidgetVisible: IContextKey<boolean>;
private _editorFrame?: HTMLElement;
private _content?: HTMLElement;
@@ -79,6 +78,12 @@ export class WebviewEditor extends BaseEditor {
this.withWebview(webview => webview.hideFind());
}
public find(previous: boolean) {
this.withWebview(webview => {
webview.runFindAction(previous);
});
}
public reload() {
this.withWebview(webview => webview.reload());
}

View File

@@ -268,6 +268,10 @@ export class IFrameWebview extends Disposable implements Webview {
throw new Error('Method not implemented.');
}
runFindAction(previous: boolean): void {
throw new Error('Method not implemented.');
}
public set state(state: string | undefined) {
this.content = {
html: this.content.html,

View File

@@ -4,8 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { SimpleFindWidget } from 'vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/webview/browser/webview';
export interface WebviewFindDelegate {
find(value: string, previous: boolean): void;
@@ -15,6 +16,7 @@ export interface WebviewFindDelegate {
}
export class WebviewFindWidget extends SimpleFindWidget {
protected _findWidgetFocused: IContextKey<boolean>;
constructor(
private readonly _delegate: WebviewFindDelegate,
@@ -22,6 +24,7 @@ export class WebviewFindWidget extends SimpleFindWidget {
@IContextKeyService contextKeyService: IContextKeyService
) {
super(contextViewService, contextKeyService);
this._findWidgetFocused = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED.bindTo(contextKeyService);
}
public find(previous: boolean) {
@@ -47,9 +50,13 @@ export class WebviewFindWidget extends SimpleFindWidget {
return false;
}
protected onFocusTrackerFocus() { }
protected onFocusTrackerFocus() {
this._findWidgetFocused.set(true);
}
protected onFocusTrackerBlur() { }
protected onFocusTrackerBlur() {
this._findWidgetFocused.reset();
}
protected onFindInputFocusTrackerFocus() { }

View File

@@ -319,7 +319,7 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview {
return;
case 'did-click-link':
let [uri] = event.args;
const [uri] = event.args;
this._onDidClickLink.fire(URI.parse(uri));
return;
@@ -334,12 +334,10 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview {
clientY: rawEvent.clientY + bounds.top,
}));
return;
}
catch (TypeError) {
} catch {
// CustomEvent was treated as MouseEvent so don't do anything - https://github.com/microsoft/vscode/issues/78915
return;
}
}
case 'did-set-content':
@@ -640,6 +638,12 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview {
}
}
public runFindAction(previous: boolean) {
if (this._webviewFindWidget) {
this._webviewFindWidget.find(previous);
}
}
public reload() {
this.doUpdateContent();
}

View File

@@ -5,7 +5,6 @@ The core editor in VS Code is packed with features. This page highlights a numb
* [IntelliSense](#intellisense) - get code assistance and parameter suggestions for your code and external modules.
* [Line Actions](#line-actions) - quickly move lines around to re-order your code.
* [Rename Refactoring](#rename-refactoring) - quickly rename symbols across your code base.
* [Refactoring via Extraction](#refactoring-via-extraction) - quickly extract common code into a separate function or constant.
* [Formatting](#formatting) - keep your code looking great with inbuilt document & selection formatting.
* [Code Folding](#code-folding) - focus on the most relevant parts of your code by folding other areas.
* [Errors and Warnings](#errors-and-warnings) - see errors and warning as you type.
@@ -90,21 +89,6 @@ function Book(title, author) {
> **JSDoc Tip:** VS Code's IntelliSense uses JSDoc comments to provide richer suggestions. The types and documentation from JSDoc comments show up when you hover over a reference to `Book` or in IntelliSense when you create a new instance of `Book`.
### Refactoring via Extraction
Sometimes you want to refactor already written code into a separate function or constant to reuse it later. Select the lines you want to refactor out and press kb(editor.action.quickFix) or click the little light bulb and choose one of the respective `Extract to...` options. Try it by selecting the code inside the `if`-clause on line 3 or any other common code you want to refactor out.
```js
function findFirstEvenNumber(arr) {
for (const el of arr) {
if (typeof el === 'number' && el % 2 === 0) {
return el;
}
}
return null;
}
```
### Formatting
Keeping your code looking great is hard without a good formatter. Luckily it's easy to format content, either for the entire document with kb(editor.action.formatDocument) or for the current selection with kb(editor.action.formatSelection). Both of these options are also available through the right-click context menu.

View File

@@ -275,7 +275,7 @@ export class EditorService extends Disposable implements EditorServiceImpl {
// Respect option to reveal an editor if it is already visible in any group
if (options && options.revealIfVisible) {
for (const group of groupsByLastActive) {
if (input.matches(group.activeEditor)) {
if (group.isActive(input)) {
targetGroup = group;
break;
}
@@ -283,11 +283,16 @@ export class EditorService extends Disposable implements EditorServiceImpl {
}
// Respect option to reveal an editor if it is open (not necessarily visible)
if ((options && options.revealIfOpened) || this.configurationService.getValue<boolean>('workbench.editor.revealIfOpen')) {
for (const group of groupsByLastActive) {
if (group.isOpened(input)) {
targetGroup = group;
break;
if (!targetGroup) {
if ((options && options.revealIfOpened) || this.configurationService.getValue<boolean>('workbench.editor.revealIfOpen')) {
for (const group of groupsByLastActive) {
if (group.isOpened(input) && group.isActive(input)) {
targetGroup = group;
break;
}
if (group.isOpened(input) && !targetGroup) {
targetGroup = group;
}
}
}
}

View File

@@ -62,11 +62,9 @@ export class BrowserWindowConfiguration implements IWindowConfiguration {
termProgram?: string;
}
export interface IBrowserWindowConfiguration {
interface IBrowserWorkbenchEnvironemntConstructionOptions extends IWorkbenchConstructionOptions {
workspaceId: string;
remoteAuthority?: string;
webviewEndpoint?: string;
connectionToken?: string;
logsPath: URI;
}
export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironmentService {
@@ -75,8 +73,10 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
readonly configuration: IWindowConfiguration = new BrowserWindowConfiguration();
constructor(workspaceId: string, public readonly options: IWorkbenchConstructionOptions) {
constructor(readonly options: IBrowserWorkbenchEnvironemntConstructionOptions) {
this.args = { _: [] };
this.logsPath = options.logsPath.path;
this.logFile = joinPath(options.logsPath, 'window.log');
this.appRoot = '/web/';
this.appNameLong = 'Visual Studio Code - Web';
@@ -88,10 +88,8 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
this.keyboardLayoutResource = joinPath(this.userRoamingDataHome, 'keyboardLayout.json');
this.localeResource = joinPath(this.userRoamingDataHome, 'locale.json');
this.backupHome = joinPath(this.userRoamingDataHome, BACKUPS);
this.configuration.backupWorkspaceResource = joinPath(this.backupHome, workspaceId);
this.configuration.connectionToken = options.connectionToken || this.getConnectionTokenFromLocation();
this.logsPath = '/web/logs';
this.configuration.backupWorkspaceResource = joinPath(this.backupHome, options.workspaceId);
this.configuration.connectionToken = options.connectionToken || getCookieValue('vscode-tkn');
this.debugExtensionHost = {
port: null,
@@ -183,6 +181,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
driverVerbose: boolean;
webviewEndpoint?: string;
galleryMachineIdResource?: URI;
readonly logFile: URI;
get webviewResourceRoot(): string {
return this.webviewEndpoint ? this.webviewEndpoint + '/vscode-resource{{resource}}' : 'vscode-resource:{{resource}}';
@@ -191,21 +190,12 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
get webviewCspSource(): string {
return this.webviewEndpoint ? this.webviewEndpoint : 'vscode-resource:';
}
private getConnectionTokenFromLocation(): string | undefined {
// TODO: Check with @alexd where the token will be: search or hash?
let connectionToken: string | undefined = undefined;
if (document.location.search) {
connectionToken = this.getConnectionToken(document.location.search);
}
if (!connectionToken && document.location.hash) {
connectionToken = this.getConnectionToken(document.location.hash);
}
return connectionToken;
}
private getConnectionToken(str: string): string | undefined {
const m = str.match(/[#&?]tkn=([^&]+)/);
return m ? m[1] : undefined;
}
}
/**
* See https://stackoverflow.com/a/25490531
*/
function getCookieValue(name: string): string | undefined {
const m = document.cookie.match('(^|[^;]+)\\s*' + name + '\\s*=\\s*([^;]+)');
return m ? m.pop() : undefined;
}

View File

@@ -7,6 +7,7 @@ import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/co
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api';
import { URI } from 'vs/base/common/uri';
export const IWorkbenchEnvironmentService = createDecorator<IWorkbenchEnvironmentService>('environmentService');
@@ -16,5 +17,7 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService {
readonly configuration: IWindowConfiguration;
readonly logFile: URI;
readonly options?: IWorkbenchConstructionOptions;
}

View File

@@ -11,6 +11,7 @@ import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { toBackupWorkspaceResource } from 'vs/workbench/services/backup/common/backup';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { join } from 'vs/base/common/path';
export class WorkbenchEnvironmentService extends EnvironmentService implements IWorkbenchEnvironmentService {
@@ -31,4 +32,7 @@ export class WorkbenchEnvironmentService extends EnvironmentService implements I
@memoize
get userRoamingDataHome(): URI { return this.appSettingsHome.with({ scheme: Schemas.userData }); }
@memoize
get logFile(): URI { return URI.file(join(this.logsPath, `renderer${this.configuration.windowId}.log`)); }
}

View File

@@ -86,7 +86,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
const result: ExtensionHostProcessManager[] = [];
const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => isWebExtension(ext, this._configService)));
const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.parse('empty:value')); //todo@joh
const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.file(this._environmentService.logsPath).with({ scheme: this._environmentService.logFile.scheme }));
const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, null, initialActivationEvents);
result.push(webHostProcessManager);

View File

@@ -176,6 +176,7 @@ export class BrowserKeyboardMapperFactoryBase {
}
setActiveKeyMapping(keymap: IKeyboardMapping | null) {
let keymapUpdated = false;
let matchedKeyboardLayout = this.getMatchedKeymapInfo(keymap);
if (matchedKeyboardLayout) {
// let score = matchedKeyboardLayout.score;
@@ -209,18 +210,21 @@ export class BrowserKeyboardMapperFactoryBase {
if (!this._activeKeymapInfo) {
this._activeKeymapInfo = matchedKeyboardLayout.result;
keymapUpdated = true;
} else if (keymap) {
if (matchedKeyboardLayout.result.getScore(keymap) > this._activeKeymapInfo.getScore(keymap)) {
this._activeKeymapInfo = matchedKeyboardLayout.result;
keymapUpdated = true;
}
}
}
if (!this._activeKeymapInfo) {
this._activeKeymapInfo = this.getUSStandardLayout();
keymapUpdated = true;
}
if (!this._activeKeymapInfo) {
if (!this._activeKeymapInfo || !keymapUpdated) {
return;
}

View File

@@ -40,12 +40,12 @@ suite('keyboard layout loader', () => {
let commandService = instantiationService.stub(ICommandService, {});
let instance = new TestKeyboardMapperFactory(notitifcationService, storageService, commandService);
test.skip('load default US keyboard layout', () => {
test('load default US keyboard layout', () => {
assert.notEqual(instance.activeKeyboardLayout, null);
assert.equal(instance.activeKeyboardLayout!.isUSStandard, true);
});
test.skip('isKeyMappingActive', () => {
test('isKeyMappingActive', () => {
instance.setUSKeyboardLayout();
assert.equal(instance.isKeyMappingActive({
KeyA: {
value: 'a',

View File

@@ -0,0 +1,98 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { KeyValueLogProvider } from 'vs/workbench/services/log/common/keyValueLogProvider';
export const INDEXEDDB_LOG_SCHEME = 'vscode-logs-indexedbd';
export const INDEXEDDB_LOGS_DB = 'vscode-logs-db';
export const INDEXEDDB_LOGS_OBJECT_STORE = 'vscode-logs-store';
export class IndexedDBLogProvider extends KeyValueLogProvider {
private readonly database: Promise<IDBDatabase>;
constructor(
) {
super(INDEXEDDB_LOG_SCHEME);
this.database = this.openDatabase(1);
}
private openDatabase(version: number): Promise<IDBDatabase> {
return new Promise((c, e) => {
const request = window.indexedDB.open(INDEXEDDB_LOGS_DB, version);
request.onerror = (err) => e(request.error);
request.onsuccess = () => {
const db = request.result;
if (db.objectStoreNames.contains(INDEXEDDB_LOGS_OBJECT_STORE)) {
c(db);
}
};
request.onupgradeneeded = () => {
const db = request.result;
if (!db.objectStoreNames.contains(INDEXEDDB_LOGS_OBJECT_STORE)) {
db.createObjectStore(INDEXEDDB_LOGS_OBJECT_STORE);
}
c(db);
};
});
}
protected async getAllKeys(): Promise<string[]> {
return new Promise(async (c, e) => {
const db = await this.database;
const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]);
const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE);
const request = objectStore.getAllKeys();
request.onerror = () => e(request.error);
request.onsuccess = () => c(<string[]>request.result);
});
}
protected hasKey(key: string): Promise<boolean> {
return new Promise<boolean>(async (c, e) => {
const db = await this.database;
const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]);
const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE);
const request = objectStore.getKey(key);
request.onerror = () => e(request.error);
request.onsuccess = () => {
c(!!request.result);
};
});
}
protected getValue(key: string): Promise<string> {
return new Promise(async (c, e) => {
const db = await this.database;
const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]);
const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE);
const request = objectStore.get(key);
request.onerror = () => e(request.error);
request.onsuccess = () => c(request.result || '');
});
}
protected setValue(key: string, value: string): Promise<void> {
return new Promise(async (c, e) => {
const db = await this.database;
const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE], 'readwrite');
const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE);
const request = objectStore.put(value, key);
request.onerror = () => e(request.error);
request.onsuccess = () => c();
});
}
protected deleteKey(key: string): Promise<void> {
return new Promise(async (c, e) => {
const db = await this.database;
const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE], 'readwrite');
const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE);
const request = objectStore.delete(key);
request.onerror = () => e(request.error);
request.onsuccess = () => c();
});
}
}

View File

@@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { KeyValueLogProvider } from 'vs/workbench/services/log/common/keyValueLogProvider';
import { keys } from 'vs/base/common/map';
export const INMEMORY_LOG_SCHEME = 'vscode-logs-inmemory';
export class InMemoryLogProvider extends KeyValueLogProvider {
private readonly logs: Map<string, string> = new Map<string, string>();
constructor(
) {
super(INMEMORY_LOG_SCHEME);
}
protected async getAllKeys(): Promise<string[]> {
return keys(this.logs);
}
protected async hasKey(key: string): Promise<boolean> {
return this.logs.has(key);
}
protected async getValue(key: string): Promise<string> {
return this.logs.get(key) || '';
}
protected async setValue(key: string, value: string): Promise<void> {
this.logs.set(key, value);
}
protected async deleteKey(key: string): Promise<void> {
this.logs.delete(key);
}
}

View File

@@ -0,0 +1,129 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { IFileSystemProviderWithFileReadWriteCapability, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileDeleteOptions, FileWriteOptions, FileChangeType, FileSystemProviderErrorCode } from 'vs/platform/files/common/files';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { VSBuffer } from 'vs/base/common/buffer';
import { FileSystemError } from 'vs/workbench/api/common/extHostTypes';
import { isEqualOrParent, joinPath, relativePath } from 'vs/base/common/resources';
import { values } from 'vs/base/common/map';
export abstract class KeyValueLogProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability {
readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite;
readonly onDidChangeCapabilities: Event<void> = Event.None;
private readonly _onDidChangeFile: Emitter<IFileChange[]> = this._register(new Emitter<IFileChange[]>());
readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChangeFile.event;
private readonly versions: Map<string, number> = new Map<string, number>();
constructor(private readonly scheme: string) {
super();
}
watch(resource: URI, opts: IWatchOptions): IDisposable {
return Disposable.None;
}
async mkdir(resource: URI): Promise<void> {
}
async stat(resource: URI): Promise<IStat> {
try {
const content = await this.readFile(resource);
return {
type: FileType.File,
ctime: 0,
mtime: this.versions.get(resource.toString()) || 0,
size: content.byteLength
};
} catch (e) {
}
const files = await this.readdir(resource);
if (files.length) {
return {
type: FileType.Directory,
ctime: 0,
mtime: 0,
size: 0
};
}
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound));
}
async readdir(resource: URI): Promise<[string, FileType][]> {
const hasKey = await this.hasKey(resource.path);
if (hasKey) {
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotADirectory));
}
const keys = await this.getAllKeys();
const files: Map<string, [string, FileType]> = new Map<string, [string, FileType]>();
for (const key of keys) {
const keyResource = this.toResource(key);
if (isEqualOrParent(keyResource, resource, false)) {
const path = relativePath(resource, keyResource, false);
if (path) {
const keySegments = path.split('/');
files.set(keySegments[0], [keySegments[0], keySegments.length === 1 ? FileType.File : FileType.Directory]);
}
}
}
return values(files);
}
async readFile(resource: URI): Promise<Uint8Array> {
const hasKey = await this.hasKey(resource.path);
if (!hasKey) {
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound));
}
const value = await this.getValue(resource.path);
return VSBuffer.fromString(value).buffer;
}
async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
const hasKey = await this.hasKey(resource.path);
if (!hasKey) {
const files = await this.readdir(resource);
if (files.length) {
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileIsADirectory));
}
}
await this.setValue(resource.path, VSBuffer.wrap(content).toString());
this.versions.set(resource.toString(), (this.versions.get(resource.toString()) || 0) + 1);
this._onDidChangeFile.fire([{ resource, type: FileChangeType.UPDATED }]);
}
async delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
const hasKey = await this.hasKey(resource.path);
if (hasKey) {
await this.deleteKey(resource.path);
this.versions.delete(resource.path);
this._onDidChangeFile.fire([{ resource, type: FileChangeType.DELETED }]);
return;
}
if (opts.recursive) {
const files = await this.readdir(resource);
await Promise.all(files.map(([key]) => this.delete(joinPath(resource, key), opts)));
}
}
rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> {
return Promise.reject(new Error('Not Supported'));
}
private toResource(key: string): URI {
return URI.file(key).with({ scheme: this.scheme });
}
protected abstract getAllKeys(): Promise<string[]>;
protected abstract hasKey(key: string): Promise<boolean>;
protected abstract getValue(key: string): Promise<string>;
protected abstract setValue(key: string, value: string): Promise<void>;
protected abstract deleteKey(key: string): Promise<void>;
}

View File

@@ -47,7 +47,7 @@ suite('FileUserDataProvider', () => {
userDataResource = URI.file(userDataPath).with({ scheme: Schemas.userData });
await Promise.all([pfs.mkdirp(userDataPath), pfs.mkdirp(backupsPath)]);
const environmentService = new BrowserWorkbenchEnvironmentService('workspaceId', { remoteAuthority: 'remote' });
const environmentService = new BrowserWorkbenchEnvironmentService({ remoteAuthority: 'remote', workspaceId: 'workspaceId', logsPath: URI.file('logFile') });
environmentService.userRoamingDataHome = userDataResource;
const userDataFileSystemProvider = new FileUserDataProvider(URI.file(userDataPath), URI.file(backupsPath), diskFileSystemProvider, environmentService);
@@ -321,7 +321,7 @@ suite('FileUserDataProvider - Watching', () => {
localUserDataResource = URI.file(userDataPath);
userDataResource = localUserDataResource.with({ scheme: Schemas.userData });
const environmentService = new BrowserWorkbenchEnvironmentService('workspaceId', { remoteAuthority: 'remote' });
const environmentService = new BrowserWorkbenchEnvironmentService({ remoteAuthority: 'remote', workspaceId: 'workspaceId', logsPath: URI.file('logFile') });
environmentService.userRoamingDataHome = userDataResource;
const userDataFileSystemProvider = new FileUserDataProvider(localUserDataResource, localBackupsResource, new TestFileSystemProvider(fileEventEmitter.event), environmentService);

View File

@@ -201,9 +201,6 @@ registerSingleton(IAdsTelemetryService, AdsTelemetryService);
// Localizations
import 'vs/workbench/contrib/localizations/browser/localizations.contribution';
// Logs
import 'vs/workbench/contrib/logs/electron-browser/logs.contribution';
// Stats
import 'vs/workbench/contrib/stats/electron-browser/workspaceStatsService';
import 'vs/workbench/contrib/stats/electron-browser/stats.contribution';