mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-09 10:28:32 +00:00
chore: updated to Theia 1.37.0
- Updated `@theia/*` to `1.37.0`. - Fixed all `yarn audit` security vulnerabilities. - Updated to `electron@23.2.4`: - `contextIsolation` is `true`, - `nodeIntegration` is `false`, and the - `webpack` target is moved from `electron-renderer` to `web`. - Updated to `typescript@4.9.3`. - Updated the `eslint` plugins. - Added the new `Light High Contrast` theme to the IDE2. - High contrast themes use Theia APIs for style adjustments. - Support for ESM modules: `"moduleResolution": "node16"`. - Node.js >= 16.14 is required. - VISX langage packs were bumped to `1.70.0`. - Removed undesired editor context menu items. (Closes #1394) Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
@@ -20,8 +20,7 @@ import { ArduinoDaemon } from '../../../common/protocol';
|
||||
import { assertUnreachable } from '../../../common/utils';
|
||||
import { CreateFeatures } from '../../create/create-features';
|
||||
import { NotificationCenter } from '../../notification-center';
|
||||
import debounce = require('lodash.debounce');
|
||||
import isOnline = require('is-online');
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
@injectable()
|
||||
export class IsOnline implements FrontendApplicationContribution {
|
||||
@@ -30,17 +29,19 @@ export class IsOnline implements FrontendApplicationContribution {
|
||||
private stopped = false;
|
||||
|
||||
onStart(): void {
|
||||
const checkOnline = async () => {
|
||||
if (!this.stopped) {
|
||||
try {
|
||||
const online = await isOnline();
|
||||
this.setOnline(online);
|
||||
} finally {
|
||||
window.setTimeout(() => checkOnline(), 6_000); // 6 seconds poll interval
|
||||
import('is-online').then((module) => {
|
||||
const checkOnline = async () => {
|
||||
if (!this.stopped) {
|
||||
try {
|
||||
const online = await module.default();
|
||||
this.setOnline(online);
|
||||
} finally {
|
||||
window.setTimeout(() => checkOnline(), 6_000); // 6 seconds poll interval
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
checkOnline();
|
||||
};
|
||||
checkOnline();
|
||||
});
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
@@ -56,7 +57,7 @@ export class IsOnline implements FrontendApplicationContribution {
|
||||
return this.onDidChangeOnlineEmitter.event;
|
||||
}
|
||||
|
||||
private setOnline(online: boolean) {
|
||||
private setOnline(online: boolean): void {
|
||||
const oldOnline = this._online;
|
||||
this._online = online;
|
||||
if (!this.stopped && this._online !== oldOnline) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
TabBarRenderer as TheiaTabBarRenderer,
|
||||
ToolbarAwareTabBar as TheiaToolbarAwareTabBar,
|
||||
} from '@theia/core/lib/browser/shell/tab-bars';
|
||||
import debounce = require('lodash.debounce');
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
export class TabBarRenderer extends TheiaTabBarRenderer {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
@@ -25,8 +25,8 @@ const builtInThemeIds = new Set(
|
||||
[
|
||||
ArduinoThemes.light,
|
||||
ArduinoThemes.dark,
|
||||
BuiltinThemeProvider.hcLightTheme,
|
||||
BuiltinThemeProvider.hcTheme,
|
||||
// TODO: add the HC light theme after Theia 1.36
|
||||
].map(({ id }) => id)
|
||||
);
|
||||
const deprecatedThemeIds = new Set(
|
||||
@@ -37,7 +37,14 @@ const deprecatedThemeIds = new Set(
|
||||
|
||||
export const lightThemeLabel = nls.localize('arduino/theme/light', 'Light');
|
||||
export const darkThemeLabel = nls.localize('arduino/theme/dark', 'Dark');
|
||||
export const hcThemeLabel = nls.localize('arduino/theme/hc', 'High Contrast');
|
||||
export const hcLightThemeLabel = nls.localize(
|
||||
'arduino/theme/hcLight',
|
||||
'Light High Contrast'
|
||||
);
|
||||
export const hcThemeLabel = nls.localize(
|
||||
'arduino/theme/hc',
|
||||
'Dark High Contrast'
|
||||
);
|
||||
export function userThemeLabel(theme: Theme): string {
|
||||
return nls.localize('arduino/theme/user', '{0} (user)', theme.label);
|
||||
}
|
||||
@@ -57,6 +64,8 @@ export function themeLabelForSettings(theme: Theme): string {
|
||||
return darkThemeLabel;
|
||||
case BuiltinThemeProvider.hcTheme.id:
|
||||
return hcThemeLabel;
|
||||
case BuiltinThemeProvider.hcLightTheme.id:
|
||||
return hcLightThemeLabel;
|
||||
case BuiltinThemeProvider.lightTheme.id: // fall-through
|
||||
case BuiltinThemeProvider.darkTheme.id:
|
||||
return deprecatedThemeLabel(theme);
|
||||
@@ -73,6 +82,8 @@ export function compatibleBuiltInTheme(theme: Theme): Theme {
|
||||
return ArduinoThemes.dark;
|
||||
case 'hc':
|
||||
return BuiltinThemeProvider.hcTheme;
|
||||
case 'hcLight':
|
||||
return BuiltinThemeProvider.hcLightTheme;
|
||||
default: {
|
||||
console.warn(
|
||||
`Unhandled theme type: ${theme.type}. Theme ID: ${theme.id}, label: ${theme.label}`
|
||||
@@ -91,7 +102,7 @@ interface ThemeProvider {
|
||||
/**
|
||||
* Returns with a list of built-in themes officially supported by IDE2 (https://github.com/arduino/arduino-ide/issues/1283).
|
||||
* The themes in the array follow the following order:
|
||||
* - built-in themes first (in `Light`, `Dark`, `High Contrast`), // TODO -> High Contrast will be split up to HC Dark and HC Light after the Theia version uplift
|
||||
* - built-in themes first (in `Light`, `Dark`, `Light High Contrast`, and `Dark High Contrast`),
|
||||
* - followed by user installed (VSIX) themes grouped by theme type, then alphabetical order,
|
||||
* - if the `currentTheme` is either Light (Theia) or Dark (Theia), the last item of the array will be the selected theme with `(deprecated)` suffix.
|
||||
*/
|
||||
@@ -171,7 +182,8 @@ const arduinoThemeTypeOrder: Record<ArduinoThemeType, number> = {
|
||||
const themeTypeOrder: Record<ThemeType, number> = {
|
||||
light: 0,
|
||||
dark: 1,
|
||||
hc: 2,
|
||||
hcLight: 2,
|
||||
hc: 3,
|
||||
};
|
||||
|
||||
export function arduinoThemeTypeOf(theme: Theme | string): ArduinoThemeType {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { StartupTask } from '../../../electron-common/startup-task';
|
||||
import type { StartupTasks } from '../../../electron-common/startup-task';
|
||||
|
||||
export const WindowServiceExt = Symbol('WindowServiceExt');
|
||||
export interface WindowServiceExt {
|
||||
@@ -6,5 +6,6 @@ export interface WindowServiceExt {
|
||||
* Returns with a promise that resolves to `true` if the current window is the first window.
|
||||
*/
|
||||
isFirstWindow(): Promise<boolean>;
|
||||
reload(options?: StartupTask.Owner): void;
|
||||
reload(tasks?: StartupTasks): void;
|
||||
close(): void;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
import { NavigatableWidget } from '@theia/core/lib/browser/navigatable-types';
|
||||
@@ -118,10 +117,8 @@ export class WindowTitleUpdater extends TheiaWindowTitleUpdater {
|
||||
if (widget instanceof EditorWidget) {
|
||||
const { uri } = widget.editor;
|
||||
const filename = uri.path.toString();
|
||||
// Do not necessarily require the current window if not needed. It's a synchronous, blocking call.
|
||||
if (this.previousRepresentedFilename !== filename) {
|
||||
const currentWindow = remote.getCurrentWindow();
|
||||
currentWindow.setRepresentedFilename(uri.path.toString());
|
||||
window.electronArduino.setRepresentedFilename(uri.path.fsPath());
|
||||
this.previousRepresentedFilename = filename;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { DebugAction as TheiaDebugAction } from '@theia/debug/lib/browser/view/debug-action';
|
||||
import {
|
||||
codiconArray,
|
||||
DISABLED_CLASS,
|
||||
} from '@theia/core/lib/browser/widgets/widget';
|
||||
|
||||
// customized debug action to show the contributed command's label when there is no icon
|
||||
export class DebugAction extends TheiaDebugAction {
|
||||
override render(): React.ReactNode {
|
||||
const { enabled, label, iconClass } = this.props;
|
||||
const classNames = ['debug-action', ...codiconArray(iconClass, true)];
|
||||
if (enabled === false) {
|
||||
classNames.push(DISABLED_CLASS);
|
||||
}
|
||||
return (
|
||||
<span
|
||||
tabIndex={0}
|
||||
className={classNames.join(' ')}
|
||||
title={label}
|
||||
onClick={this.props.run}
|
||||
ref={this.setRef}
|
||||
>
|
||||
{!iconClass ||
|
||||
(iconClass.match(/plugin-icon-\d+/) && <div>{label}</div>)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import debounce = require('p-debounce');
|
||||
import debounce from 'p-debounce';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
|
||||
@@ -43,7 +43,8 @@ export class DefaultDebugSessionFactory extends TheiaDefaultDebugSessionFactory
|
||||
this.messages,
|
||||
this.fileService,
|
||||
this.debugContributionProvider,
|
||||
this.workspaceService
|
||||
this.workspaceService,
|
||||
2_000
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
import type { ContextKey } from '@theia/core/lib/browser/context-key-service';
|
||||
import { injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
DebugSession,
|
||||
DebugState,
|
||||
} from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
|
||||
import type { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
||||
|
||||
function debugStateLabel(state: DebugState): string {
|
||||
switch (state) {
|
||||
case DebugState.Initializing:
|
||||
return 'initializing';
|
||||
case DebugState.Stopped:
|
||||
return 'stopped';
|
||||
case DebugState.Running:
|
||||
return 'running';
|
||||
default:
|
||||
return 'inactive';
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class DebugSessionManager extends TheiaDebugSessionManager {
|
||||
protected debugStateKey: ContextKey<string>;
|
||||
|
||||
@postConstruct()
|
||||
protected override init(): void {
|
||||
this.debugStateKey = this.contextKeyService.createKey<string>(
|
||||
'debugState',
|
||||
debugStateLabel(this.state)
|
||||
);
|
||||
super.init();
|
||||
}
|
||||
|
||||
protected override fireDidChange(current: DebugSession | undefined): void {
|
||||
this.debugTypeKey.set(current?.configuration.type);
|
||||
this.inDebugModeKey.set(this.inDebugMode);
|
||||
this.debugStateKey.set(debugStateLabel(this.state));
|
||||
this.onDidChangeEmitter.fire(current);
|
||||
}
|
||||
|
||||
protected override async doStart(
|
||||
sessionId: string,
|
||||
options: DebugConfigurationSessionOptions
|
||||
): Promise<DebugSession> {
|
||||
const parentSession =
|
||||
options.configuration.parentSession &&
|
||||
this._sessions.get(options.configuration.parentSession.id);
|
||||
const contrib = this.sessionContributionRegistry.get(
|
||||
options.configuration.type
|
||||
);
|
||||
const sessionFactory = contrib
|
||||
? contrib.debugSessionFactory()
|
||||
: this.debugSessionFactory;
|
||||
const session = sessionFactory.get(sessionId, options, parentSession);
|
||||
this._sessions.set(sessionId, session);
|
||||
|
||||
this.debugTypeKey.set(session.configuration.type);
|
||||
// this.onDidCreateDebugSessionEmitter.fire(session); // defer the didCreate event after start https://github.com/eclipse-theia/theia/issues/11916
|
||||
|
||||
let state = DebugState.Inactive;
|
||||
session.onDidChange(() => {
|
||||
if (state !== session.state) {
|
||||
state = session.state;
|
||||
if (state === DebugState.Stopped) {
|
||||
this.onDidStopDebugSessionEmitter.fire(session);
|
||||
}
|
||||
}
|
||||
this.updateCurrentSession(session);
|
||||
});
|
||||
session.onDidChangeBreakpoints((uri) =>
|
||||
this.fireDidChangeBreakpoints({ session, uri })
|
||||
);
|
||||
session.on('terminated', async (event) => {
|
||||
const restart = event.body && event.body.restart;
|
||||
if (restart) {
|
||||
// postDebugTask isn't run in case of auto restart as well as preLaunchTask
|
||||
this.doRestart(session, !!restart);
|
||||
} else {
|
||||
await session.disconnect(false, () =>
|
||||
this.debug.terminateDebugSession(session.id)
|
||||
);
|
||||
await this.runTask(
|
||||
session.options.workspaceFolderUri,
|
||||
session.configuration.postDebugTask
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
session.on('exited', async (event) => {
|
||||
await session.disconnect(false, () =>
|
||||
this.debug.terminateDebugSession(session.id)
|
||||
);
|
||||
});
|
||||
|
||||
session.onDispose(() => this.cleanup(session));
|
||||
session
|
||||
.start()
|
||||
.then(() => {
|
||||
this.onDidCreateDebugSessionEmitter.fire(session); // now fire the didCreate event
|
||||
this.onDidStartDebugSessionEmitter.fire(session);
|
||||
})
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
.catch((e) => {
|
||||
session.stop(false, () => {
|
||||
this.debug.terminateDebugSession(session.id);
|
||||
});
|
||||
});
|
||||
session.onDidCustomEvent(({ event, body }) =>
|
||||
this.onDidReceiveDebugSessionCustomEventEmitter.fire({
|
||||
event,
|
||||
body,
|
||||
session,
|
||||
})
|
||||
);
|
||||
return session;
|
||||
}
|
||||
}
|
||||
@@ -1,231 +1,12 @@
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { Mutable } from '@theia/core/lib/common/types';
|
||||
import { URI } from '@theia/core/lib/common/uri';
|
||||
import { DebugSession as TheiaDebugSession } from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugFunctionBreakpoint } from '@theia/debug/lib/browser/model/debug-function-breakpoint';
|
||||
import { DebugSourceBreakpoint } from '@theia/debug/lib/browser/model/debug-source-breakpoint';
|
||||
import {
|
||||
DebugThreadData,
|
||||
StoppedDetails,
|
||||
} from '@theia/debug/lib/browser/model/debug-thread';
|
||||
import { DebugProtocol } from '@vscode/debugprotocol';
|
||||
import { DebugThread } from './debug-thread';
|
||||
|
||||
export class DebugSession extends TheiaDebugSession {
|
||||
/**
|
||||
* The `send('initialize')` request resolves later than `on('initialized')` emits the event.
|
||||
* Hence, the `configure` would use the empty object `capabilities`.
|
||||
* Using the empty `capabilities` could result in missing exception breakpoint filters, as
|
||||
* always `capabilities.exceptionBreakpointFilters` is falsy. This deferred promise works
|
||||
* around this timing issue.
|
||||
* See: https://github.com/eclipse-theia/theia/issues/11886.
|
||||
*/
|
||||
protected didReceiveCapabilities = new Deferred();
|
||||
|
||||
protected override async initialize(): Promise<void> {
|
||||
const clientName = FrontendApplicationConfigProvider.get().applicationName;
|
||||
try {
|
||||
const response = await this.connection.sendRequest('initialize', {
|
||||
clientID: clientName.toLocaleLowerCase().replace(/ /g, '_'),
|
||||
clientName,
|
||||
adapterID: this.configuration.type,
|
||||
locale: 'en-US',
|
||||
linesStartAt1: true,
|
||||
columnsStartAt1: true,
|
||||
pathFormat: 'path',
|
||||
supportsVariableType: false,
|
||||
supportsVariablePaging: false,
|
||||
supportsRunInTerminalRequest: true,
|
||||
});
|
||||
this.updateCapabilities(response?.body || {});
|
||||
this.didReceiveCapabilities.resolve();
|
||||
} catch (err) {
|
||||
this.didReceiveCapabilities.reject(err);
|
||||
throw err;
|
||||
}
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
protected override handleDisconnectError(err: unknown): void {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
protected override async configure(): Promise<void> {
|
||||
await this.didReceiveCapabilities.promise;
|
||||
return super.configure();
|
||||
}
|
||||
|
||||
override async stop(isRestart: boolean, callback: () => void): Promise<void> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const _this = this as any;
|
||||
if (!_this.isStopping) {
|
||||
_this.isStopping = true;
|
||||
if (this.configuration.lifecycleManagedByParent && this.parentSession) {
|
||||
await this.parentSession.stop(isRestart, callback);
|
||||
} else {
|
||||
if (this.canTerminate()) {
|
||||
const terminated = this.waitFor('terminated', 5000);
|
||||
try {
|
||||
await this.connection.sendRequest(
|
||||
'terminate',
|
||||
{ restart: isRestart },
|
||||
5000
|
||||
);
|
||||
await terminated;
|
||||
} catch (e) {
|
||||
console.error('Did not receive terminated event in time', e);
|
||||
}
|
||||
} else {
|
||||
const terminateDebuggee =
|
||||
this.initialized && this.capabilities.supportTerminateDebuggee;
|
||||
// Related https://github.com/microsoft/vscode/issues/165138
|
||||
try {
|
||||
await this.sendRequest(
|
||||
'disconnect',
|
||||
{ restart: isRestart, terminateDebuggee },
|
||||
2000
|
||||
);
|
||||
} catch (err) {
|
||||
if (
|
||||
'message' in err &&
|
||||
typeof err.message === 'string' &&
|
||||
err.message.test(err.message)
|
||||
) {
|
||||
// VS Code ignores errors when sending the `disconnect` request.
|
||||
// Debug adapter might not send the `disconnected` event as a response.
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async sendFunctionBreakpoints(
|
||||
affectedUri: URI
|
||||
): Promise<void> {
|
||||
const all = this.breakpoints
|
||||
.getFunctionBreakpoints()
|
||||
.map(
|
||||
(origin) =>
|
||||
new DebugFunctionBreakpoint(origin, this.asDebugBreakpointOptions())
|
||||
);
|
||||
const enabled = all.filter((b) => b.enabled);
|
||||
if (this.capabilities.supportsFunctionBreakpoints) {
|
||||
try {
|
||||
const response = await this.sendRequest('setFunctionBreakpoints', {
|
||||
breakpoints: enabled.map((b) => b.origin.raw),
|
||||
});
|
||||
// Apparently, `body` and `breakpoints` can be missing.
|
||||
// https://github.com/eclipse-theia/theia/issues/11885
|
||||
// https://github.com/microsoft/vscode/blob/80004351ccf0884b58359f7c8c801c91bb827d83/src/vs/workbench/contrib/debug/browser/debugSession.ts#L448-L449
|
||||
if (response && response.body) {
|
||||
response.body.breakpoints.forEach((raw, index) => {
|
||||
// node debug adapter returns more breakpoints sometimes
|
||||
if (enabled[index]) {
|
||||
enabled[index].update({ raw });
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// could be error or promise rejection of DebugProtocol.SetFunctionBreakpoints
|
||||
if (error instanceof Error) {
|
||||
console.error(`Error setting breakpoints: ${error.message}`);
|
||||
} else {
|
||||
// handle adapters that send failed DebugProtocol.SetFunctionBreakpoints for invalid breakpoints
|
||||
const genericMessage =
|
||||
'Function breakpoint not valid for current debug session';
|
||||
const message = error.message ? `${error.message}` : genericMessage;
|
||||
console.warn(
|
||||
`Could not handle function breakpoints: ${message}, disabling...`
|
||||
);
|
||||
enabled.forEach((b) =>
|
||||
b.update({
|
||||
raw: {
|
||||
verified: false,
|
||||
message,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setBreakpoints(affectedUri, all);
|
||||
}
|
||||
|
||||
protected override async sendSourceBreakpoints(
|
||||
affectedUri: URI,
|
||||
sourceModified?: boolean
|
||||
): Promise<void> {
|
||||
const source = await this.toSource(affectedUri);
|
||||
const all = this.breakpoints
|
||||
.findMarkers({ uri: affectedUri })
|
||||
.map(
|
||||
({ data }) =>
|
||||
new DebugSourceBreakpoint(data, this.asDebugBreakpointOptions())
|
||||
);
|
||||
const enabled = all.filter((b) => b.enabled);
|
||||
try {
|
||||
const breakpoints = enabled.map(({ origin }) => origin.raw);
|
||||
const response = await this.sendRequest('setBreakpoints', {
|
||||
source: source.raw,
|
||||
sourceModified,
|
||||
breakpoints,
|
||||
lines: breakpoints.map(({ line }) => line),
|
||||
});
|
||||
response.body.breakpoints.forEach((raw, index) => {
|
||||
// node debug adapter returns more breakpoints sometimes
|
||||
if (enabled[index]) {
|
||||
enabled[index].update({ raw });
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
// could be error or promise rejection of DebugProtocol.SetBreakpointsResponse
|
||||
if (error instanceof Error) {
|
||||
console.error(`Error setting breakpoints: ${error.message}`);
|
||||
} else {
|
||||
// handle adapters that send failed DebugProtocol.SetBreakpointsResponse for invalid breakpoints
|
||||
const genericMessage = 'Breakpoint not valid for current debug session';
|
||||
const message = error.message ? `${error.message}` : genericMessage;
|
||||
console.warn(
|
||||
`Could not handle breakpoints for ${affectedUri}: ${message}, disabling...`
|
||||
);
|
||||
enabled.forEach((b) =>
|
||||
b.update({
|
||||
raw: {
|
||||
verified: false,
|
||||
message,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
this.setSourceBreakpoints(affectedUri, all);
|
||||
}
|
||||
|
||||
protected override doUpdateThreads(
|
||||
threads: DebugProtocol.Thread[],
|
||||
stoppedDetails?: StoppedDetails
|
||||
): void {
|
||||
const existing = this._threads;
|
||||
this._threads = new Map();
|
||||
for (const raw of threads) {
|
||||
const id = raw.id;
|
||||
const thread = existing.get(id) || new DebugThread(this); // patched debug thread
|
||||
this._threads.set(id, thread);
|
||||
const data: Partial<Mutable<DebugThreadData>> = { raw };
|
||||
if (stoppedDetails) {
|
||||
if (stoppedDetails.threadId === id) {
|
||||
data.stoppedDetails = stoppedDetails;
|
||||
} else if (stoppedDetails.allThreadsStopped) {
|
||||
data.stoppedDetails = {
|
||||
// When a debug adapter notifies us that all threads are stopped,
|
||||
// we do not know why the others are stopped, so we should default
|
||||
// to something generic.
|
||||
reason: '',
|
||||
};
|
||||
}
|
||||
}
|
||||
thread.update(data);
|
||||
}
|
||||
this.updateCurrentThread(stoppedDetails);
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
protected override handleTerminateError(err: unknown): void {
|
||||
// NOOP
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { WidgetOpenerOptions } from '@theia/core/lib/browser/widget-open-handler';
|
||||
import { Range } from '@theia/core/shared/vscode-languageserver-types';
|
||||
import { DebugStackFrame as TheiaDebugStackFrame } from '@theia/debug/lib/browser/model/debug-stack-frame';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
|
||||
|
||||
export class DebugStackFrame extends TheiaDebugStackFrame {
|
||||
override async open(
|
||||
options: WidgetOpenerOptions = {
|
||||
mode: 'reveal',
|
||||
}
|
||||
): Promise<EditorWidget | undefined> {
|
||||
if (!this.source) {
|
||||
return undefined;
|
||||
}
|
||||
const { line, column, endLine, endColumn, source } = this.raw;
|
||||
if (!source) {
|
||||
return undefined;
|
||||
}
|
||||
// create selection based on VS Code
|
||||
// https://github.com/eclipse-theia/theia/issues/11880
|
||||
const selection = Range.create(
|
||||
line,
|
||||
column,
|
||||
endLine || line,
|
||||
endColumn || column
|
||||
);
|
||||
this.source.open({
|
||||
...options,
|
||||
selection,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { DebugStackFrame as TheiaDebugStackFrame } from '@theia/debug/lib/browser/model/debug-stack-frame';
|
||||
import { DebugThread as TheiaDebugThread } from '@theia/debug/lib/browser/model/debug-thread';
|
||||
import { DebugProtocol } from '@vscode/debugprotocol';
|
||||
import { DebugStackFrame } from './debug-stack-frame';
|
||||
|
||||
export class DebugThread extends TheiaDebugThread {
|
||||
protected override doUpdateFrames(
|
||||
frames: DebugProtocol.StackFrame[]
|
||||
): TheiaDebugStackFrame[] {
|
||||
const result = new Set<TheiaDebugStackFrame>();
|
||||
for (const raw of frames) {
|
||||
const id = raw.id;
|
||||
const frame =
|
||||
this._frames.get(id) || new DebugStackFrame(this, this.session); // patched debug stack frame
|
||||
this._frames.set(id, frame);
|
||||
frame.update({ raw });
|
||||
result.add(frame);
|
||||
}
|
||||
this.updateCurrentFrame();
|
||||
return [...result.values()];
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import {
|
||||
ActionMenuNode,
|
||||
CompositeMenuNode,
|
||||
MenuModelRegistry,
|
||||
} from '@theia/core/lib/common/menu';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { DebugState } from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugAction } from './debug-action';
|
||||
import { DebugToolBar as TheiaDebugToolbar } from '@theia/debug/lib/browser/view/debug-toolbar-widget';
|
||||
|
||||
@injectable()
|
||||
export class DebugToolbar extends TheiaDebugToolbar {
|
||||
@inject(CommandRegistry) private readonly commandRegistry: CommandRegistry;
|
||||
@inject(MenuModelRegistry)
|
||||
private readonly menuModelRegistry: MenuModelRegistry;
|
||||
@inject(ContextKeyService)
|
||||
private readonly contextKeyService: ContextKeyService;
|
||||
|
||||
protected override render(): React.ReactNode {
|
||||
const { state } = this.model;
|
||||
return (
|
||||
<React.Fragment>
|
||||
{this.renderContributedCommands()}
|
||||
{this.renderContinue()}
|
||||
<DebugAction
|
||||
enabled={state === DebugState.Stopped}
|
||||
run={this.stepOver}
|
||||
label={nls.localizeByDefault('Step Over')}
|
||||
iconClass="debug-step-over"
|
||||
ref={this.setStepRef}
|
||||
/>
|
||||
<DebugAction
|
||||
enabled={state === DebugState.Stopped}
|
||||
run={this.stepIn}
|
||||
label={nls.localizeByDefault('Step Into')}
|
||||
iconClass="debug-step-into"
|
||||
/>
|
||||
<DebugAction
|
||||
enabled={state === DebugState.Stopped}
|
||||
run={this.stepOut}
|
||||
label={nls.localizeByDefault('Step Out')}
|
||||
iconClass="debug-step-out"
|
||||
/>
|
||||
<DebugAction
|
||||
enabled={state !== DebugState.Inactive}
|
||||
run={this.restart}
|
||||
label={nls.localizeByDefault('Restart')}
|
||||
iconClass="debug-restart"
|
||||
/>
|
||||
{this.renderStart()}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
private renderContributedCommands(): React.ReactNode {
|
||||
return this.menuModelRegistry
|
||||
.getMenu(TheiaDebugToolbar.MENU)
|
||||
.children.filter((node) => node instanceof CompositeMenuNode)
|
||||
.map((node) => (node as CompositeMenuNode).children)
|
||||
.reduce((acc, curr) => acc.concat(curr), [])
|
||||
.filter((node) => node instanceof ActionMenuNode)
|
||||
.map((node) => this.debugAction(node as ActionMenuNode));
|
||||
}
|
||||
|
||||
private debugAction(node: ActionMenuNode): React.ReactNode {
|
||||
const { label, command, when, icon: iconClass = '' } = node;
|
||||
const run = () => this.commandRegistry.executeCommand(command);
|
||||
const enabled = when ? this.contextKeyService.match(when) : true;
|
||||
return (
|
||||
enabled && (
|
||||
<DebugAction
|
||||
key={command}
|
||||
enabled={enabled}
|
||||
label={label}
|
||||
iconClass={iconClass}
|
||||
run={run}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
import { ResourceSaveOptions } from '@theia/core/lib/common/resource';
|
||||
import { Readable } from '@theia/core/lib/common/stream';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
@@ -11,14 +9,13 @@ import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import {
|
||||
FileOperationError,
|
||||
FileOperationResult,
|
||||
FileStat,
|
||||
} from '@theia/filesystem/lib/common/files';
|
||||
import * as PQueue from 'p-queue';
|
||||
import PQueue from 'p-queue';
|
||||
|
||||
@injectable()
|
||||
export class FileResourceResolver extends TheiaFileResourceResolver {
|
||||
override async resolve(uri: URI): Promise<WriteQueuedFileResource> {
|
||||
let stat: FileStat | undefined;
|
||||
let stat;
|
||||
try {
|
||||
stat = await this.fileService.resolve(uri);
|
||||
} catch (e) {
|
||||
@@ -37,6 +34,7 @@ export class FileResourceResolver extends TheiaFileResourceResolver {
|
||||
);
|
||||
}
|
||||
return new WriteQueuedFileResource(uri, this.fileService, {
|
||||
isReadonly: stat?.isReadonly ?? false,
|
||||
shouldOverwrite: () => this.shouldOverwrite(uri),
|
||||
shouldOpenAsText: (error) => this.shouldOpenAsText(uri, error),
|
||||
});
|
||||
@@ -52,23 +50,32 @@ class WriteQueuedFileResource extends FileResource {
|
||||
options: FileResourceOptions
|
||||
) {
|
||||
super(uri, fileService, options);
|
||||
const originalDoWrite = this['doWrite'];
|
||||
this['doWrite'] = (content, options) =>
|
||||
this.writeQueue.add(() => originalDoWrite.bind(this)(content, options));
|
||||
const originalSaveStream = this['saveStream'];
|
||||
if (originalSaveStream) {
|
||||
this['saveStream'] = (content, options) =>
|
||||
this.writeQueue.add(() =>
|
||||
originalSaveStream.bind(this)(content, options)
|
||||
);
|
||||
}
|
||||
const originalSaveContents = this['saveContents'];
|
||||
if (originalSaveContents) {
|
||||
this['saveContents'] = (content, options) =>
|
||||
this.writeQueue.add(() =>
|
||||
originalSaveContents.bind(this)(content, options)
|
||||
);
|
||||
}
|
||||
const originalSaveContentChanges = this['saveContentChanges'];
|
||||
if (originalSaveContentChanges) {
|
||||
this['saveContentChanges'] = (changes, options) => {
|
||||
return this.writeQueue.add(() =>
|
||||
this['saveContentChanges'] = (changes, options) =>
|
||||
this.writeQueue.add(() =>
|
||||
originalSaveContentChanges.bind(this)(changes, options)
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected override async doWrite(
|
||||
content: string | Readable<string>,
|
||||
options?: ResourceSaveOptions
|
||||
): Promise<void> {
|
||||
return this.writeQueue.add(() => super.doWrite(content, options));
|
||||
}
|
||||
|
||||
protected override async isInSync(): Promise<boolean> {
|
||||
// Let all the write operations finish to update the version (mtime) before checking whether the resource is in sync.
|
||||
// https://github.com/eclipse-theia/theia/issues/12327
|
||||
|
||||
@@ -8,7 +8,7 @@ import URI from '@theia/core/lib/common/uri';
|
||||
import { Marker } from '@theia/markers/lib/common/marker';
|
||||
import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browser/problem/problem-manager';
|
||||
import { ConfigServiceClient } from '../../config/config-service-client';
|
||||
import debounce = require('lodash.debounce');
|
||||
import debounce from 'lodash.debounce';
|
||||
import {
|
||||
ARDUINO_CLOUD_FOLDER,
|
||||
REMOTE_SKETCHBOOK_FOLDER,
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { MenuModelRegistry } from '@theia/core/lib/common/menu/menu-model-registry';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { MonacoEditorMenuContribution as TheiaMonacoEditorMenuContribution } from '@theia/monaco/lib/browser/monaco-menu';
|
||||
|
||||
@injectable()
|
||||
export class MonacoEditorMenuContribution extends TheiaMonacoEditorMenuContribution {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
super.registerMenus(registry);
|
||||
// https://github.com/arduino/arduino-ide/issues/1394
|
||||
registry.unregisterMenuAction('editor.action.refactor'); // Refactor...
|
||||
registry.unregisterMenuAction('editor.action.sourceAction'); // Source Action...
|
||||
// https://github.com/arduino/arduino-ide/pull/2027#pullrequestreview-1414246614
|
||||
// Root editor context menu
|
||||
registry.unregisterMenuAction('editor.action.revealDeclaration'); // Go to Declaration
|
||||
registry.unregisterMenuAction('editor.action.goToTypeDefinition'); // Go to Type Definition
|
||||
registry.unregisterMenuAction('editor.action.goToImplementation'); // Go to Implementations
|
||||
registry.unregisterMenuAction('editor.action.goToReferences'); // Go to References
|
||||
// Peek submenu
|
||||
registry.unregisterMenuAction('editor.action.peekDeclaration'); // Peek Declaration
|
||||
registry.unregisterMenuAction('editor.action.peekTypeDefinition'); // Peek Type Definition
|
||||
registry.unregisterMenuAction('editor.action.peekImplementation'); // Peek Implementation
|
||||
registry.unregisterMenuAction('editor.action.referenceSearch.trigger'); // Peek References
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import { DebuggerDescription } from '@theia/debug/lib/common/debug-service';
|
||||
import { DebugMainImpl as TheiaDebugMainImpl } from '@theia/plugin-ext/lib/main/browser/debug/debug-main';
|
||||
import { PluginDebugAdapterContribution } from '@theia/plugin-ext/lib/main/browser/debug/plugin-debug-adapter-contribution';
|
||||
import { PluginDebugSessionFactory } from './plugin-debug-session-factory';
|
||||
|
||||
export class DebugMainImpl extends TheiaDebugMainImpl {
|
||||
override async $registerDebuggerContribution(
|
||||
description: DebuggerDescription
|
||||
): Promise<void> {
|
||||
const debugType = description.type;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const _this = <any>this;
|
||||
const terminalOptionsExt = await _this.debugExt.$getTerminalCreationOptions(
|
||||
debugType
|
||||
);
|
||||
|
||||
if (_this.toDispose.disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const debugSessionFactory = new PluginDebugSessionFactory(
|
||||
_this.terminalService,
|
||||
_this.editorManager,
|
||||
_this.breakpointsManager,
|
||||
_this.labelProvider,
|
||||
_this.messages,
|
||||
_this.outputChannelManager,
|
||||
_this.debugPreferences,
|
||||
async (sessionId: string) => {
|
||||
const connection = await _this.connectionMain.ensureConnection(
|
||||
sessionId
|
||||
);
|
||||
return connection;
|
||||
},
|
||||
_this.fileService,
|
||||
terminalOptionsExt,
|
||||
_this.debugContributionProvider,
|
||||
_this.workspaceService
|
||||
);
|
||||
|
||||
const toDispose = new DisposableCollection(
|
||||
Disposable.create(() => _this.debuggerContributions.delete(debugType))
|
||||
);
|
||||
_this.debuggerContributions.set(debugType, toDispose);
|
||||
toDispose.pushAll([
|
||||
_this.pluginDebugService.registerDebugAdapterContribution(
|
||||
new PluginDebugAdapterContribution(
|
||||
description,
|
||||
_this.debugExt,
|
||||
_this.pluginService
|
||||
)
|
||||
),
|
||||
_this.sessionContributionRegistrator.registerDebugSessionContribution({
|
||||
debugType: description.type,
|
||||
debugSessionFactory: () => debugSessionFactory,
|
||||
}),
|
||||
]);
|
||||
_this.toDispose.push(
|
||||
Disposable.create(() => this.$unregisterDebuggerConfiguration(debugType))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,7 @@
|
||||
import { Emitter, Event, JsonRpcProxy } from '@theia/core';
|
||||
import { injectable, interfaces } from '@theia/core/shared/inversify';
|
||||
import { HostedPluginServer } from '@theia/plugin-ext/lib/common/plugin-protocol';
|
||||
import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol';
|
||||
import {
|
||||
HostedPluginSupport as TheiaHostedPluginSupport,
|
||||
PluginHost,
|
||||
} from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
import { PluginWorker } from '@theia/plugin-ext/lib/hosted/browser/plugin-worker';
|
||||
import { setUpPluginApi } from '@theia/plugin-ext/lib/main/browser/main-context';
|
||||
import { PLUGIN_RPC_CONTEXT } from '@theia/plugin-ext/lib/common/plugin-api-rpc';
|
||||
import { DebugMainImpl } from './debug-main';
|
||||
import { ConnectionImpl } from '@theia/plugin-ext/lib/common/connection';
|
||||
import { HostedPluginSupport as TheiaHostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
|
||||
@injectable()
|
||||
export class HostedPluginSupport extends TheiaHostedPluginSupport {
|
||||
@@ -41,26 +32,4 @@ export class HostedPluginSupport extends TheiaHostedPluginSupport {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (this as any).server;
|
||||
}
|
||||
|
||||
// to patch the VS Code extension based debugger
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
protected override initRpc(host: PluginHost, pluginId: string): RPCProtocol {
|
||||
const rpc =
|
||||
host === 'frontend' ? new PluginWorker().rpc : this.createServerRpc(host);
|
||||
setUpPluginApi(rpc, this.container);
|
||||
this.patchDebugMain(rpc);
|
||||
this.mainPluginApiProviders
|
||||
.getContributions()
|
||||
.forEach((p) => p.initialize(rpc, this.container));
|
||||
return rpc;
|
||||
}
|
||||
|
||||
private patchDebugMain(rpc: RPCProtocol): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const connectionMain = (rpc as any).locals.get(
|
||||
PLUGIN_RPC_CONTEXT.CONNECTION_MAIN.id
|
||||
) as ConnectionImpl;
|
||||
const debugMain = new DebugMainImpl(rpc, connectionMain, this.container);
|
||||
rpc.set(PLUGIN_RPC_CONTEXT.DEBUG_MAIN, debugMain);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import { DebugSession } from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugSessionConnection } from '@theia/debug/lib/browser/debug-session-connection';
|
||||
import { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
||||
import { PluginDebugSessionFactory as TheiaPluginDebugSessionFactory } from '@theia/plugin-ext/lib/main/browser/debug/plugin-debug-session-factory';
|
||||
import { PluginDebugSession } from './plugin-debug-session';
|
||||
|
||||
export class PluginDebugSessionFactory extends TheiaPluginDebugSessionFactory {
|
||||
override get(
|
||||
sessionId: string,
|
||||
options: DebugConfigurationSessionOptions,
|
||||
parentSession?: DebugSession
|
||||
): DebugSession {
|
||||
const connection = new DebugSessionConnection(
|
||||
sessionId,
|
||||
this.connectionFactory,
|
||||
this.getTraceOutputChannel()
|
||||
);
|
||||
|
||||
return new PluginDebugSession(
|
||||
sessionId,
|
||||
options,
|
||||
parentSession,
|
||||
connection,
|
||||
this.terminalService,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
this.editorManager as any,
|
||||
this.breakpoints,
|
||||
this.labelProvider,
|
||||
this.messages,
|
||||
this.fileService,
|
||||
this.terminalOptionsExt,
|
||||
this.debugContributionProvider,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
this.workspaceService as any
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import { ContributionProvider, MessageClient } from '@theia/core';
|
||||
import { LabelProvider } from '@theia/core/lib/browser';
|
||||
import { BreakpointManager } from '@theia/debug/lib/browser/breakpoint/breakpoint-manager';
|
||||
import { DebugContribution } from '@theia/debug/lib/browser/debug-contribution';
|
||||
import { DebugSession as TheiaDebugSession } from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugSessionConnection } from '@theia/debug/lib/browser/debug-session-connection';
|
||||
import { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { TerminalOptionsExt } from '@theia/plugin-ext';
|
||||
import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
|
||||
import {
|
||||
TerminalWidget,
|
||||
TerminalWidgetOptions,
|
||||
} from '@theia/terminal/lib/browser/base/terminal-widget';
|
||||
import { DebugSession } from '../debug/debug-session';
|
||||
import { EditorManager } from '../editor/editor-manager';
|
||||
import { WorkspaceService } from '../workspace/workspace-service';
|
||||
|
||||
// This class extends the patched debug session, and not the default debug session from Theia
|
||||
export class PluginDebugSession extends DebugSession {
|
||||
constructor(
|
||||
override readonly id: string,
|
||||
override readonly options: DebugConfigurationSessionOptions,
|
||||
override readonly parentSession: TheiaDebugSession | undefined,
|
||||
protected override readonly connection: DebugSessionConnection,
|
||||
protected override readonly terminalServer: TerminalService,
|
||||
protected override readonly editorManager: EditorManager,
|
||||
protected override readonly breakpoints: BreakpointManager,
|
||||
protected override readonly labelProvider: LabelProvider,
|
||||
protected override readonly messages: MessageClient,
|
||||
protected override readonly fileService: FileService,
|
||||
protected readonly terminalOptionsExt: TerminalOptionsExt | undefined,
|
||||
protected override readonly debugContributionProvider: ContributionProvider<DebugContribution>,
|
||||
protected override readonly workspaceService: WorkspaceService
|
||||
) {
|
||||
super(
|
||||
id,
|
||||
options,
|
||||
parentSession,
|
||||
connection,
|
||||
terminalServer,
|
||||
editorManager,
|
||||
breakpoints,
|
||||
labelProvider,
|
||||
messages,
|
||||
fileService,
|
||||
debugContributionProvider,
|
||||
workspaceService
|
||||
);
|
||||
}
|
||||
|
||||
protected override async doCreateTerminal(
|
||||
terminalWidgetOptions: TerminalWidgetOptions
|
||||
): Promise<TerminalWidget> {
|
||||
terminalWidgetOptions = Object.assign(
|
||||
{},
|
||||
terminalWidgetOptions,
|
||||
this.terminalOptionsExt
|
||||
);
|
||||
return super.doCreateTerminal(terminalWidgetOptions);
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
import { MenuPath } from '@theia/core';
|
||||
import { TAB_BAR_TOOLBAR_CONTEXT_MENU } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
import { injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import { DebugToolBar } from '@theia/debug/lib/browser/view/debug-toolbar-widget';
|
||||
import { DebugVariablesWidget } from '@theia/debug/lib/browser/view/debug-variables-widget';
|
||||
import {
|
||||
ArgumentAdapter,
|
||||
PluginMenuCommandAdapter as TheiaPluginMenuCommandAdapter,
|
||||
} from '@theia/plugin-ext/lib/main/browser/menus/plugin-menu-command-adapter';
|
||||
import {
|
||||
codeToTheiaMappings,
|
||||
ContributionPoint,
|
||||
} from '@theia/plugin-ext/lib/main/browser/menus/vscode-theia-menu-mappings';
|
||||
|
||||
function patch(
|
||||
toPatch: typeof codeToTheiaMappings,
|
||||
key: string,
|
||||
value: MenuPath[]
|
||||
): void {
|
||||
const loose = toPatch as Map<string, MenuPath[]>;
|
||||
if (!loose.has(key)) {
|
||||
loose.set(key, value);
|
||||
}
|
||||
}
|
||||
// mappings is a const and cannot be customized with DI
|
||||
patch(codeToTheiaMappings, 'debug/variables/context', [
|
||||
DebugVariablesWidget.CONTEXT_MENU,
|
||||
]);
|
||||
patch(codeToTheiaMappings, 'debug/toolBar', [DebugToolBar.MENU]);
|
||||
|
||||
@injectable()
|
||||
export class PluginMenuCommandAdapter extends TheiaPluginMenuCommandAdapter {
|
||||
@postConstruct()
|
||||
protected override init(): void {
|
||||
const toCommentArgs: ArgumentAdapter = (...args) =>
|
||||
this.toCommentArgs(...args);
|
||||
const firstArgOnly: ArgumentAdapter = (...args) => [args[0]];
|
||||
const noArgs: ArgumentAdapter = () => [];
|
||||
const toScmArgs: ArgumentAdapter = (...args) => this.toScmArgs(...args);
|
||||
const selectedResource = () => this.getSelectedResources();
|
||||
const widgetURI: ArgumentAdapter = (widget) =>
|
||||
this.codeEditorUtil.is(widget)
|
||||
? [this.codeEditorUtil.getResourceUri(widget)]
|
||||
: [];
|
||||
(<Array<[ContributionPoint, ArgumentAdapter | undefined]>>[
|
||||
['comments/comment/context', toCommentArgs],
|
||||
['comments/comment/title', toCommentArgs],
|
||||
['comments/commentThread/context', toCommentArgs],
|
||||
['debug/callstack/context', firstArgOnly],
|
||||
['debug/variables/context', firstArgOnly],
|
||||
['debug/toolBar', noArgs],
|
||||
['editor/context', selectedResource],
|
||||
['editor/title', widgetURI],
|
||||
['editor/title/context', selectedResource],
|
||||
['explorer/context', selectedResource],
|
||||
['scm/resourceFolder/context', toScmArgs],
|
||||
['scm/resourceGroup/context', toScmArgs],
|
||||
['scm/resourceState/context', toScmArgs],
|
||||
['scm/title', () => this.toScmArg(this.scmService.selectedRepository)],
|
||||
['timeline/item/context', (...args) => this.toTimelineArgs(...args)],
|
||||
['view/item/context', (...args) => this.toTreeArgs(...args)],
|
||||
['view/title', noArgs],
|
||||
]).forEach(([contributionPoint, adapter]) => {
|
||||
if (adapter) {
|
||||
const paths = codeToTheiaMappings.get(contributionPoint);
|
||||
if (paths) {
|
||||
paths.forEach((path) => this.addArgumentAdapter(path, adapter));
|
||||
}
|
||||
}
|
||||
});
|
||||
this.addArgumentAdapter(TAB_BAR_TOOLBAR_CONTEXT_MENU, widgetURI);
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,9 @@ import {
|
||||
SketchesService,
|
||||
} from '../../../common/protocol/sketches-service';
|
||||
import {
|
||||
StartupTask,
|
||||
StartupTaskProvider,
|
||||
hasStartupTasks,
|
||||
StartupTask,
|
||||
} from '../../../electron-common/startup-task';
|
||||
import { WindowServiceExt } from '../core/window-service-ext';
|
||||
|
||||
@@ -128,7 +129,7 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
||||
.getContributions()
|
||||
.map((contribution) => contribution.tasks())
|
||||
.reduce((prev, curr) => prev.concat(curr), []);
|
||||
if (StartupTask.has(options)) {
|
||||
if (hasStartupTasks(options)) {
|
||||
tasks.push(...options.tasks);
|
||||
}
|
||||
return tasks;
|
||||
|
||||
Reference in New Issue
Block a user