mirror of https://github.com/opensumi/core
fix: debugConsole context menus excute (#2394)
* fix: debug console context menus * fix: improve debug console output style * test: add debugConsole contextMenu test
This commit is contained in:
parent
75ac54e8fe
commit
62c02aafb0
|
@ -111,7 +111,7 @@
|
||||||
"name": "Playwright Current File",
|
"name": "Playwright Current File",
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/tools/playwright/node_modules/.bin/playwright",
|
"program": "${workspaceFolder}/node_modules/.bin/playwright",
|
||||||
"args": ["test", "--debug", "--config=./configs/playwright.debug.config.ts", "${fileBasenameNoExtension}"],
|
"args": ["test", "--debug", "--config=./configs/playwright.debug.config.ts", "${fileBasenameNoExtension}"],
|
||||||
"cwd": "${workspaceFolder}/tools/playwright",
|
"cwd": "${workspaceFolder}/tools/playwright",
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
export const MARKER_CONTAINER_ID = 'markers';
|
export const MARKER_CONTAINER_ID = 'markers';
|
||||||
export const OUTPUT_CONTAINER_ID = 'output';
|
export const OUTPUT_CONTAINER_ID = 'output';
|
||||||
export const TERMINAL_CONTAINER_ID = 'terminal';
|
export const TERMINAL_CONTAINER_ID = 'terminal';
|
||||||
export const DEBUG_CONSOLE_CONTAINER_ID = 'debug-console-container';
|
export const DEBUG_CONSOLE_CONTAINER_ID = 'debug-console';
|
||||||
|
|
||||||
// Left Panel 的 Container
|
// Left Panel 的 Container
|
||||||
export const DEBUG_CONTAINER_ID = 'debug';
|
export const DEBUG_CONTAINER_ID = 'debug';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import throttle from 'lodash/throttle';
|
import throttle from 'lodash/throttle';
|
||||||
|
|
||||||
import { Autowired, Injectable, Optional } from '@opensumi/di';
|
import { Autowired, Injectable, Optional } from '@opensumi/di';
|
||||||
import { DisposableCollection, Emitter, Event, MessageType, ILogger } from '@opensumi/ide-core-common';
|
import { DisposableCollection, Emitter, Event, MessageType, ILogger, localize } from '@opensumi/ide-core-common';
|
||||||
import { IThemeService } from '@opensumi/ide-theme';
|
import { IThemeService } from '@opensumi/ide-theme';
|
||||||
import { DebugProtocol } from '@opensumi/vscode-debugprotocol/lib/debugProtocol';
|
import { DebugProtocol } from '@opensumi/vscode-debugprotocol/lib/debugProtocol';
|
||||||
|
|
||||||
|
@ -68,6 +68,8 @@ export class DebugConsoleSession implements IDebugConsoleSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async logOutput(session: DebugSession, event: DebugProtocol.OutputEvent): Promise<void> {
|
protected async logOutput(session: DebugSession, event: DebugProtocol.OutputEvent): Promise<void> {
|
||||||
|
// [2J is the ansi escape sequence for clearing the display http://ascii-table.com/ansi-escape-sequences.php
|
||||||
|
const clearAnsiSequence = '\u001b[2J';
|
||||||
const body = event.body;
|
const body = event.body;
|
||||||
const { category, variablesReference, source, line } = body;
|
const { category, variablesReference, source, line } = body;
|
||||||
if (!this.treeModel) {
|
if (!this.treeModel) {
|
||||||
|
@ -76,7 +78,9 @@ export class DebugConsoleSession implements IDebugConsoleSession {
|
||||||
const severity =
|
const severity =
|
||||||
category === 'stderr'
|
category === 'stderr'
|
||||||
? MessageType.Error
|
? MessageType.Error
|
||||||
: event.body.category === 'console'
|
: category === 'stdout'
|
||||||
|
? MessageType.Info
|
||||||
|
: category === 'console'
|
||||||
? MessageType.Warning
|
? MessageType.Warning
|
||||||
: MessageType.Info;
|
: MessageType.Info;
|
||||||
if (category === 'telemetry') {
|
if (category === 'telemetry') {
|
||||||
|
@ -95,11 +99,23 @@ export class DebugConsoleSession implements IDebugConsoleSession {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (typeof body.output === 'string') {
|
} else if (typeof body.output === 'string') {
|
||||||
|
let output = body.output;
|
||||||
|
if (output.indexOf(clearAnsiSequence) >= 0) {
|
||||||
|
this.clearConsole();
|
||||||
|
await this.insertItemWithAnsi(localize('debug.console.consoleCleare'), MessageType.Info);
|
||||||
|
output = output.substring(output.lastIndexOf(clearAnsiSequence) + clearAnsiSequence.length);
|
||||||
|
}
|
||||||
const previousItem = this.getLastItem();
|
const previousItem = this.getLastItem();
|
||||||
/**
|
/**
|
||||||
* 如果上一条 output 结尾没有换行符则应该与下一条 output 拼接在一起
|
* 如果上一次输出结尾没有换行符并且输出类型(MessageType)一致
|
||||||
|
* 则将接下来的输出拼接至上一次输出后
|
||||||
*/
|
*/
|
||||||
if (previousItem && !previousItem.description.endsWith('\n') && !previousItem.description.endsWith('\r\n')) {
|
if (
|
||||||
|
previousItem &&
|
||||||
|
!previousItem.description.endsWith('\n') &&
|
||||||
|
!previousItem.description.endsWith('\r\n') &&
|
||||||
|
(previousItem as AnsiConsoleNode).severity === severity
|
||||||
|
) {
|
||||||
this.treeModel.root.unlinkItem(previousItem);
|
this.treeModel.root.unlinkItem(previousItem);
|
||||||
await this.insertItemWithAnsi(previousItem.description + body.output, severity, source, line);
|
await this.insertItemWithAnsi(previousItem.description + body.output, severity, source, line);
|
||||||
} else {
|
} else {
|
||||||
|
@ -110,6 +126,16 @@ export class DebugConsoleSession implements IDebugConsoleSession {
|
||||||
this.fireDidChange();
|
this.fireDidChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async clearConsole() {
|
||||||
|
const items = this.treeModel.root.flattenedBranch?.map((id) => this.treeModel.root.getTreeNodeById(id));
|
||||||
|
if (!items) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const item of items) {
|
||||||
|
this.treeModel.root.unlinkItem(item as AnsiConsoleNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async insertItemWithAnsi(
|
private async insertItemWithAnsi(
|
||||||
output: string,
|
output: string,
|
||||||
severity?: MessageType,
|
severity?: MessageType,
|
||||||
|
|
|
@ -21,7 +21,7 @@ import {
|
||||||
} from '@opensumi/ide-core-browser';
|
} from '@opensumi/ide-core-browser';
|
||||||
import { AbstractContextMenuService, MenuId, ICtxMenuRenderer } from '@opensumi/ide-core-browser/lib/menu/next';
|
import { AbstractContextMenuService, MenuId, ICtxMenuRenderer } from '@opensumi/ide-core-browser/lib/menu/next';
|
||||||
|
|
||||||
import { IDebugSessionManager } from '../../../common';
|
import { IDebugConsoleModelService, IDebugSessionManager } from '../../../common';
|
||||||
import { LinkDetector } from '../../debug-link-detector';
|
import { LinkDetector } from '../../debug-link-detector';
|
||||||
import { DebugSession } from '../../debug-session';
|
import { DebugSession } from '../../debug-session';
|
||||||
import { DidChangeActiveDebugSession } from '../../debug-session-manager';
|
import { DidChangeActiveDebugSession } from '../../debug-session-manager';
|
||||||
|
@ -50,7 +50,7 @@ export interface IDebugConsoleModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DebugConsoleModelService {
|
export class DebugConsoleModelService implements IDebugConsoleModelService {
|
||||||
private static DEFAULT_REFRESH_DELAY = 200;
|
private static DEFAULT_REFRESH_DELAY = 200;
|
||||||
|
|
||||||
@Autowired(INJECTOR_TOKEN)
|
@Autowired(INJECTOR_TOKEN)
|
||||||
|
@ -164,16 +164,16 @@ export class DebugConsoleModelService {
|
||||||
return this.onDidRefreshedEmitter.event;
|
return this.onDidRefreshedEmitter.event;
|
||||||
}
|
}
|
||||||
|
|
||||||
clear = () => {
|
clear() {
|
||||||
// 重新初始化Console中渲染的TreeModel
|
// 重新初始化Console中渲染的TreeModel
|
||||||
this.initTreeModel(this.manager.currentSession, true);
|
this.initTreeModel(this.manager.currentSession, true);
|
||||||
};
|
}
|
||||||
|
|
||||||
collapseAll = () => {
|
collapseAll() {
|
||||||
this.treeModel?.root.collapsedAll();
|
this.treeModel?.root.collapsedAll();
|
||||||
};
|
}
|
||||||
|
|
||||||
copyAll = () => {
|
copyAll() {
|
||||||
let text = '';
|
let text = '';
|
||||||
if (!this.treeModel?.root || !this.treeModel.root.children) {
|
if (!this.treeModel?.root || !this.treeModel.root.children) {
|
||||||
return;
|
return;
|
||||||
|
@ -182,15 +182,18 @@ export class DebugConsoleModelService {
|
||||||
text += this.getValidText(child as DebugConsoleNode) + '\n';
|
text += this.getValidText(child as DebugConsoleNode) + '\n';
|
||||||
}
|
}
|
||||||
this.clipboardService.writeText(text.slice(0, -'\n'.length));
|
this.clipboardService.writeText(text.slice(0, -'\n'.length));
|
||||||
};
|
}
|
||||||
|
|
||||||
copy = (node: DebugConsoleNode) => {
|
copy(node: DebugConsoleNode) {
|
||||||
if (node) {
|
if (node) {
|
||||||
this.clipboardService.writeText(this.getValidText(node));
|
this.clipboardService.writeText(this.getValidText(node));
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
private getValidText(node: DebugConsoleNode) {
|
private getValidText(node: DebugConsoleNode) {
|
||||||
|
if (node.description.endsWith('\n')) {
|
||||||
|
return node.description.slice(0, -'\n'.length);
|
||||||
|
}
|
||||||
return node.description;
|
return node.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import {
|
||||||
IDebugSessionManager,
|
IDebugSessionManager,
|
||||||
CONTEXT_IN_DEBUG_MODE_KEY,
|
CONTEXT_IN_DEBUG_MODE_KEY,
|
||||||
DebugState,
|
DebugState,
|
||||||
|
IDebugConsoleModelService,
|
||||||
} from '../../../common';
|
} from '../../../common';
|
||||||
import { DebugSessionManager } from '../../debug-session-manager';
|
import { DebugSessionManager } from '../../debug-session-manager';
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ const consoleInputMonacoOptions: monaco.editor.IEditorOptions = {
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DebugConsoleService implements IHistoryNavigationWidget {
|
export class DebugConsoleService implements IHistoryNavigationWidget {
|
||||||
@Autowired(DebugConsoleModelService)
|
@Autowired(IDebugConsoleModelService)
|
||||||
protected readonly debugConsoleModelService: DebugConsoleModelService;
|
protected readonly debugConsoleModelService: DebugConsoleModelService;
|
||||||
|
|
||||||
@Autowired(IMainLayoutService)
|
@Autowired(IMainLayoutService)
|
||||||
|
@ -115,7 +116,7 @@ export class DebugConsoleService implements IHistoryNavigationWidget {
|
||||||
return bottomPanelHandler && bottomPanelHandler.isVisible;
|
return bottomPanelHandler && bottomPanelHandler.isVisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get consoleModel(): DebugConsoleModelService {
|
public get consoleModel() {
|
||||||
return this.debugConsoleModelService;
|
return this.debugConsoleModelService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,10 @@ export interface IDebugConsoleSession extends ITree {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IDebugConsoleModelService {
|
export interface IDebugConsoleModelService {
|
||||||
debugConsoleSession: IDebugConsoleSession;
|
debugConsoleSession?: IDebugConsoleSession;
|
||||||
clear(): void;
|
clear(): void;
|
||||||
copyAll(): void;
|
copyAll(): void;
|
||||||
collapseAll(): void;
|
collapseAll(): void;
|
||||||
copy(node: ITreeNode): void;
|
copy(node: ITreeNode): void;
|
||||||
|
execute(value: string): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { DebugConfigurationManager } from '@opensumi/ide-debug/lib/browser/debug
|
||||||
import { DebugPreferences } from '@opensumi/ide-debug/lib/browser/debug-preferences';
|
import { DebugPreferences } from '@opensumi/ide-debug/lib/browser/debug-preferences';
|
||||||
import { DebugSessionContributionRegistry } from '@opensumi/ide-debug/lib/browser/debug-session-contribution';
|
import { DebugSessionContributionRegistry } from '@opensumi/ide-debug/lib/browser/debug-session-contribution';
|
||||||
import { DebugSessionManager } from '@opensumi/ide-debug/lib/browser/debug-session-manager';
|
import { DebugSessionManager } from '@opensumi/ide-debug/lib/browser/debug-session-manager';
|
||||||
|
import { DebugConsoleModelService } from '@opensumi/ide-debug/lib/browser/view/console/debug-console-tree.model.service';
|
||||||
import { IDebugService, IDebugServer } from '@opensumi/ide-debug/lib/common/debug-service';
|
import { IDebugService, IDebugServer } from '@opensumi/ide-debug/lib/common/debug-service';
|
||||||
import { IDebugSessionManager, IDebugSessionOptions } from '@opensumi/ide-debug/lib/common/debug-session';
|
import { IDebugSessionManager, IDebugSessionOptions } from '@opensumi/ide-debug/lib/common/debug-session';
|
||||||
import { WorkbenchEditorService } from '@opensumi/ide-editor';
|
import { WorkbenchEditorService } from '@opensumi/ide-editor';
|
||||||
|
@ -98,7 +99,7 @@ export class MainThreadDebug implements IMainThreadDebug {
|
||||||
protected readonly terminalService: ITerminalApiService;
|
protected readonly terminalService: ITerminalApiService;
|
||||||
|
|
||||||
@Autowired(IDebugConsoleModelService)
|
@Autowired(IDebugConsoleModelService)
|
||||||
protected readonly debugConsoleModelService: IDebugConsoleModelService;
|
protected readonly debugConsoleModelService: DebugConsoleModelService;
|
||||||
|
|
||||||
@Autowired(OutputService)
|
@Autowired(OutputService)
|
||||||
protected readonly outputService: OutputService;
|
protected readonly outputService: OutputService;
|
||||||
|
|
|
@ -309,6 +309,7 @@ export const localizationBundle = {
|
||||||
'debug.console.followLink': '{0} + click to follow link',
|
'debug.console.followLink': '{0} + click to follow link',
|
||||||
'debug.console.input.placeholder': 'Please start a debug session to evaluate expressions',
|
'debug.console.input.placeholder': 'Please start a debug session to evaluate expressions',
|
||||||
'debug.console.errorMessage': 'Debug session initialization failed. See console for details.',
|
'debug.console.errorMessage': 'Debug session initialization failed. See console for details.',
|
||||||
|
'debug.console.consoleCleared': 'Console was cleared',
|
||||||
'debug.notSupported.type': 'Debug type "{0}" is not supported, please check your launch config.',
|
'debug.notSupported.type': 'Debug type "{0}" is not supported, please check your launch config.',
|
||||||
'debug.notSupported.any': 'Debug is not supported, please check your launch config.',
|
'debug.notSupported.any': 'Debug is not supported, please check your launch config.',
|
||||||
|
|
||||||
|
|
|
@ -269,6 +269,7 @@ export const localizationBundle = {
|
||||||
'debug.console.followLink': '按住 {0} 并单击可访问链接',
|
'debug.console.followLink': '按住 {0} 并单击可访问链接',
|
||||||
'debug.console.errorMessage': '调试进程初始化异常,请打开控制面板查看错误日志',
|
'debug.console.errorMessage': '调试进程初始化异常,请打开控制面板查看错误日志',
|
||||||
'debug.console.input.placeholder': '请发起调试会话来对表达式求值',
|
'debug.console.input.placeholder': '请发起调试会话来对表达式求值',
|
||||||
|
'debug.console.consoleCleared': '输出已清理',
|
||||||
'debug.notSupported.type': '调试类型 "{0}" 不是支持的调试类型,请检查配置或安装对应调试插件',
|
'debug.notSupported.type': '调试类型 "{0}" 不是支持的调试类型,请检查配置或安装对应调试插件',
|
||||||
'debug.notSupported.any': '当前调试配置不支持,请检查配置或安装对应调试插件',
|
'debug.notSupported.any': '当前调试配置不支持,请检查配置或安装对应调试插件',
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { OpenSumiApp } from './app';
|
||||||
|
import { OpenSumiContextMenu } from './context-menu';
|
||||||
|
import { OpenSumiPanel } from './panel';
|
||||||
|
|
||||||
|
export class OpenSumiDebugConsoleView extends OpenSumiPanel {
|
||||||
|
constructor(app: OpenSumiApp) {
|
||||||
|
super(app, 'DEBUG-CONSOLE');
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOutputContainer() {
|
||||||
|
return this.view?.$('[class*="debug_console_output__"]');
|
||||||
|
}
|
||||||
|
|
||||||
|
async openConsoleContextMenu() {
|
||||||
|
const view = await this.getOutputContainer();
|
||||||
|
if (!view) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return OpenSumiContextMenu.open(this.app, async () => view);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ import path from 'path';
|
||||||
import { expect } from '@playwright/test';
|
import { expect } from '@playwright/test';
|
||||||
|
|
||||||
import { OpenSumiApp } from '../app';
|
import { OpenSumiApp } from '../app';
|
||||||
|
import { OpenSumiDebugConsoleView } from '../debug-console-view';
|
||||||
import { OpenSumiDebugView } from '../debug-view';
|
import { OpenSumiDebugView } from '../debug-view';
|
||||||
import { OpenSumiExplorerView } from '../explorer-view';
|
import { OpenSumiExplorerView } from '../explorer-view';
|
||||||
import { OpenSumiTerminalView } from '../terminal-view';
|
import { OpenSumiTerminalView } from '../terminal-view';
|
||||||
|
@ -80,6 +81,41 @@ test.describe('OpenSumi Debug', () => {
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('ContextMenu on DebugConsole should be work', async () => {
|
||||||
|
editor = await app.openEditor(OpenSumiTextEditor, explorer, 'index.js', false);
|
||||||
|
await app.page.waitForTimeout(1000);
|
||||||
|
|
||||||
|
debugView = await app.open(OpenSumiDebugView);
|
||||||
|
const glyphMarginModel = await editor.getGlyphMarginModel();
|
||||||
|
const glyphOverlay = await glyphMarginModel.getOverlay(6);
|
||||||
|
expect(glyphOverlay).toBeDefined();
|
||||||
|
if (!glyphOverlay) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const isClicked = await glyphMarginModel.hasBreakpoint(glyphOverlay);
|
||||||
|
if (!isClicked) {
|
||||||
|
await glyphOverlay?.click({ position: { x: 9, y: 9 }, force: true });
|
||||||
|
await app.page.waitForTimeout(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
await debugView.start();
|
||||||
|
await app.page.waitForTimeout(2000);
|
||||||
|
|
||||||
|
const debugConsole = await app.open(OpenSumiDebugConsoleView);
|
||||||
|
const contextMenu = await debugConsole.openConsoleContextMenu();
|
||||||
|
await app.page.waitForTimeout(200);
|
||||||
|
expect(await contextMenu?.isOpen()).toBeTruthy();
|
||||||
|
const copyAll = await contextMenu?.menuItemByName('Copy All');
|
||||||
|
await copyAll?.click();
|
||||||
|
await app.page.waitForTimeout(1000);
|
||||||
|
const text = (await page.evaluate('navigator.clipboard.readText()')) as string;
|
||||||
|
expect(text.includes('Debugger attached.')).toBeTruthy();
|
||||||
|
|
||||||
|
await editor.close();
|
||||||
|
await debugView.stop();
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
});
|
||||||
|
|
||||||
test('Run Debug by Javascript Debug Terminal', async () => {
|
test('Run Debug by Javascript Debug Terminal', async () => {
|
||||||
await explorer.open();
|
await explorer.open();
|
||||||
editor = await app.openEditor(OpenSumiTextEditor, explorer, 'index.js', false);
|
editor = await app.openEditor(OpenSumiTextEditor, explorer, 'index.js', false);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Launch Program",
|
"name": "Launch Program",
|
||||||
"program": "${workspaceFolder}/index.js",
|
"program": "${workspaceFolder}/index.js",
|
||||||
|
"outputCapture": "std",
|
||||||
"skipFiles": ["<node_internals>/**"]
|
"skipFiles": ["<node_internals>/**"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue