mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-30 16:50:30 -04:00
Merge from vscode e558dc6ea73a75bd69d7a0b485f0e7e4194c66bf (#6864)
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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[]> {
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -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[]> {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
44
src/vs/workbench/contrib/logs/common/logsDataCleaner.ts
Normal file
44
src/vs/workbench/contrib/logs/common/logsDataCleaner.ts
Normal 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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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})`;
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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 = '../../../../../';
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}));
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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[] {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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() { }
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user