Skip to content

Commit 6f6f620

Browse files
author
Roberto Sora
authored
Merge pull request #100 from bcmi-labs/debugger-cli
Debugger implementation based on arduino-cli debug command
2 parents 2f9ef76 + b055bd9 commit 6f6f620

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+3640
-510
lines changed

.vscode/launch.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@
1010
"name": "Attach by Process ID",
1111
"processId": "${command:PickProcess}"
1212
},
13+
{
14+
"type": "node",
15+
"request": "launch",
16+
"name": "Launch Electron Packager",
17+
"program": "${workspaceRoot}/electron/packager/index.js",
18+
"cwd": "${workspaceFolder}/electron/packager"
19+
},
1320
{
1421
"type": "node",
1522
"request": "launch",
@@ -99,13 +106,6 @@
99106
"smartStep": true,
100107
"internalConsoleOptions": "openOnSessionStart",
101108
"outputCapture": "std"
102-
},
103-
{
104-
"type": "node",
105-
"request": "launch",
106-
"name": "Packager",
107-
"program": "${workspaceRoot}/electron/packager/index.js",
108-
"cwd": "${workspaceFolder}/electron/packager"
109109
}
110110
]
111111
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"name": "arduino-debugger-extension",
3+
"version": "0.0.4",
4+
"description": "An extension for debugging Arduino programs",
5+
"license": "MIT",
6+
"engines": {
7+
"node": ">=10.10.0"
8+
},
9+
"dependencies": {
10+
"@theia/debug": "next",
11+
"arduino-ide-extension": "0.0.4",
12+
"cdt-gdb-adapter": "^0.0.14",
13+
"vscode-debugadapter": "^1.26.0",
14+
"vscode-debugprotocol": "^1.26.0"
15+
},
16+
"scripts": {
17+
"prepare": "yarn run clean && yarn run build",
18+
"clean": "rimraf lib",
19+
"lint": "tslint -c ./tslint.json --project ./tsconfig.json",
20+
"build": "tsc && yarn lint",
21+
"watch": "tsc -w"
22+
},
23+
"devDependencies": {
24+
"rimraf": "^2.6.1",
25+
"tslint": "^5.5.0",
26+
"typescript": "3.5.1"
27+
},
28+
"files": [
29+
"lib",
30+
"src",
31+
"build",
32+
"data"
33+
],
34+
"theiaExtensions": [
35+
{
36+
"backend": "lib/node/backend-module",
37+
"frontend": "lib/browser/frontend-module"
38+
}
39+
]
40+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { DebugConfigurationManager } from "@theia/debug/lib/browser/debug-configuration-manager";
2+
import { injectable } from "inversify";
3+
4+
@injectable()
5+
export class ArduinoDebugConfigurationManager extends DebugConfigurationManager {
6+
7+
get defaultDebugger(): Promise<string | undefined> {
8+
return this.debug.getDebuggersForLanguage('ino').then(debuggers => {
9+
if (debuggers.length === 0)
10+
return undefined;
11+
return debuggers[0].type;
12+
});
13+
}
14+
15+
protected async selectDebugType(): Promise<string | undefined> {
16+
const widget = this.editorManager.currentEditor;
17+
if (!widget) {
18+
return this.defaultDebugger;
19+
}
20+
const { languageId } = widget.editor.document;
21+
const debuggers = await this.debug.getDebuggersForLanguage(languageId);
22+
if (debuggers.length === 0) {
23+
return this.defaultDebugger;
24+
}
25+
return this.quickPick.show(debuggers.map(
26+
({ label, type }) => ({ label, value: type }),
27+
{ placeholder: 'Select Environment' })
28+
);
29+
}
30+
31+
async createDefaultConfiguration(): Promise<void> {
32+
const { model } = this;
33+
if (model) {
34+
await this.doCreate(model);
35+
await this.updateModels();
36+
}
37+
}
38+
39+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import { injectable, inject } from 'inversify';
2+
import { MenuModelRegistry, Path, MessageService, Command, CommandRegistry } from '@theia/core';
3+
import { KeybindingRegistry } from '@theia/core/lib/browser';
4+
import { TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
5+
import { DebugFrontendApplicationContribution, DebugCommands } from '@theia/debug/lib/browser/debug-frontend-application-contribution';
6+
import { DebugSessionOptions } from "@theia/debug/lib/browser/debug-session-options";
7+
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
8+
import { FileSystem } from '@theia/filesystem/lib/common';
9+
import URI from '@theia/core/lib/common/uri';
10+
import { EditorManager } from '@theia/editor/lib/browser';
11+
import { EditorMode } from "arduino-ide-extension/lib/browser/editor-mode";
12+
import { SketchesService } from 'arduino-ide-extension/lib/common/protocol/sketches-service';
13+
import { ArduinoToolbar } from 'arduino-ide-extension/lib/browser/toolbar/arduino-toolbar';
14+
import { ArduinoDebugConfigurationManager } from './arduino-debug-configuration-manager';
15+
16+
export namespace ArduinoDebugCommands {
17+
export const START_DEBUG: Command = {
18+
id: 'arduino-start-debug',
19+
label: 'Start Debugging'
20+
}
21+
}
22+
23+
@injectable()
24+
export class ArduinoDebugFrontendApplicationContribution extends DebugFrontendApplicationContribution {
25+
26+
@inject(EditorMode)
27+
protected readonly editorMode: EditorMode;
28+
29+
@inject(WorkspaceService)
30+
protected readonly workspaceService: WorkspaceService;
31+
32+
@inject(SketchesService)
33+
protected readonly sketchesService: SketchesService;
34+
35+
@inject(FileSystem)
36+
protected readonly fileSystem: FileSystem;
37+
38+
@inject(EditorManager)
39+
protected readonly editorManager: EditorManager;
40+
41+
@inject(MessageService)
42+
protected readonly messageService: MessageService;
43+
44+
async start(noDebug?: boolean, debugSessionOptions?: DebugSessionOptions): Promise<void> {
45+
const configurations = this.configurations as ArduinoDebugConfigurationManager;
46+
let current = debugSessionOptions ? debugSessionOptions : configurations.current;
47+
// If no configurations are currently present, create them
48+
if (!current) {
49+
await configurations.createDefaultConfiguration();
50+
current = configurations.current;
51+
}
52+
if (current) {
53+
if (noDebug !== undefined) {
54+
current = {
55+
...current,
56+
configuration: {
57+
...current.configuration,
58+
noDebug
59+
}
60+
};
61+
}
62+
if (current.configuration.type === 'arduino') {
63+
const wsStat = this.workspaceService.workspace;
64+
let sketchFileURI: URI | undefined;
65+
if (wsStat && await this.sketchesService.isSketchFolder(wsStat.uri)) {
66+
const wsPath = new Path(wsStat.uri);
67+
const sketchFilePath = wsPath.join(wsPath.name + '.ino').toString();
68+
sketchFileURI = new URI(sketchFilePath);
69+
} else if (this.editorManager.currentEditor) {
70+
const editorURI = this.editorManager.currentEditor.getResourceUri();
71+
if (editorURI && editorURI.path && editorURI.path.ext === '.ino') {
72+
sketchFileURI = editorURI;
73+
}
74+
}
75+
if (sketchFileURI) {
76+
await this.editorManager.open(sketchFileURI);
77+
await this.manager.start(current);
78+
} else {
79+
this.messageService.error('Please open a sketch file to start debugging.')
80+
}
81+
} else {
82+
await this.manager.start(current);
83+
}
84+
}
85+
}
86+
87+
initializeLayout(): Promise<void> {
88+
if (this.editorMode.proMode) {
89+
return super.initializeLayout();
90+
}
91+
return Promise.resolve();
92+
}
93+
94+
registerMenus(menus: MenuModelRegistry): void {
95+
if (this.editorMode.proMode) {
96+
super.registerMenus(menus);
97+
menus.unregisterMenuAction(DebugCommands.START_NO_DEBUG);
98+
}
99+
}
100+
101+
registerKeybindings(keybindings: KeybindingRegistry): void {
102+
if (this.editorMode.proMode) {
103+
super.registerKeybindings(keybindings);
104+
keybindings.unregisterKeybinding({
105+
command: DebugCommands.START_NO_DEBUG.id,
106+
keybinding: 'ctrl+f5'
107+
});
108+
}
109+
}
110+
111+
registerToolbarItems(toolbar: TabBarToolbarRegistry): void {
112+
super.registerToolbarItems(toolbar);
113+
toolbar.registerItem({
114+
id: ArduinoDebugCommands.START_DEBUG.id,
115+
command: ArduinoDebugCommands.START_DEBUG.id,
116+
tooltip: 'Start Debugging',
117+
priority: 1
118+
});
119+
}
120+
121+
registerCommands(registry: CommandRegistry): void {
122+
super.registerCommands(registry);
123+
registry.registerCommand(ArduinoDebugCommands.START_DEBUG, {
124+
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left',
125+
isEnabled: widget => ArduinoToolbar.is(widget) && widget.side === 'left',
126+
execute: () => {
127+
registry.executeCommand(DebugCommands.START.id);
128+
}
129+
});
130+
}
131+
132+
133+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { DebugSessionManager } from "@theia/debug/lib/browser/debug-session-manager";
2+
import { DebugSessionOptions } from "@theia/debug/lib/browser/debug-session-options";
3+
4+
export class ArduinoDebugSessionManager extends DebugSessionManager {
5+
6+
start(options: DebugSessionOptions) {
7+
if (options.configuration.type === 'arduino' && this.sessions.find(s => s.configuration.type === 'arduino')) {
8+
this.messageService.info('A debug session is already running. You must stop the running session before starting a new one.')
9+
return Promise.resolve(undefined);
10+
}
11+
return super.start(options);
12+
}
13+
14+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
2+
import { VariableContribution, VariableRegistry, Variable } from '@theia/variable-resolver/lib/browser';
3+
import { injectable, inject } from 'inversify';
4+
import { MessageService } from '@theia/core/lib/common/message-service';
5+
import { BoardsServiceClientImpl } from 'arduino-ide-extension/lib/browser/boards/boards-service-client-impl';
6+
7+
@injectable()
8+
export class ArduinoVariableResolver implements VariableContribution {
9+
10+
@inject(BoardsServiceClientImpl)
11+
protected readonly boardsServiceClient: BoardsServiceClientImpl;
12+
13+
@inject(MessageService)
14+
protected readonly messageService: MessageService
15+
16+
registerVariables(variables: VariableRegistry): void {
17+
variables.registerVariable(<Variable>{
18+
name: 'fqbn',
19+
description: 'Qualified name of the selected board',
20+
resolve: this.resolveFqbn.bind(this),
21+
});
22+
variables.registerVariable({
23+
name: 'port',
24+
description: 'Selected upload port',
25+
resolve: this.resolvePort.bind(this)
26+
});
27+
}
28+
29+
protected async resolveFqbn(): Promise<string | undefined> {
30+
const { boardsConfig } = this.boardsServiceClient;
31+
if (!boardsConfig || !boardsConfig.selectedBoard) {
32+
this.messageService.error('No board selected. Please select a board for debugging.');
33+
return undefined;
34+
}
35+
return boardsConfig.selectedBoard.fqbn;
36+
}
37+
38+
protected async resolvePort(): Promise<string | undefined> {
39+
const { boardsConfig } = this.boardsServiceClient;
40+
if (!boardsConfig || !boardsConfig.selectedPort) {
41+
return undefined;
42+
}
43+
return boardsConfig.selectedPort.address;
44+
}
45+
46+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { ContainerModule } from 'inversify';
2+
import { VariableContribution } from '@theia/variable-resolver/lib/browser';
3+
import { ArduinoVariableResolver } from './arduino-variable-resolver';
4+
import { DebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
5+
import { DebugFrontendApplicationContribution } from '@theia/debug/lib/browser/debug-frontend-application-contribution';
6+
import { DebugConfigurationManager } from '@theia/debug/lib/browser/debug-configuration-manager';
7+
import { ArduinoDebugConfigurationManager } from './arduino-debug-configuration-manager';
8+
import { ArduinoDebugFrontendApplicationContribution } from './arduino-debug-frontend-application-contribution';
9+
import { ArduinoDebugSessionManager } from './arduino-debug-session-manager';
10+
11+
import '../../src/browser/style/index.css';
12+
13+
14+
export default new ContainerModule((bind, unbind, isBound, rebind) => {
15+
bind(ArduinoVariableResolver).toSelf().inSingletonScope();
16+
bind(VariableContribution).toService(ArduinoVariableResolver);
17+
rebind(DebugSessionManager).to(ArduinoDebugSessionManager).inSingletonScope();
18+
rebind(DebugConfigurationManager).to(ArduinoDebugConfigurationManager).inSingletonScope();
19+
rebind(DebugFrontendApplicationContribution).to(ArduinoDebugFrontendApplicationContribution);
20+
});
Lines changed: 4 additions & 0 deletions
Loading
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.arduino-start-debug-icon {
2+
-webkit-mask: url('debug-dark.svg') 50%;
3+
mask: url('debug-dark.svg') 50%;
4+
-webkit-mask-size: 100%;
5+
mask-size: 100%;
6+
-webkit-mask-repeat: no-repeat;
7+
mask-repeat: no-repeat;
8+
display: flex;
9+
justify-content: center;
10+
align-items: center;
11+
color: var(--theia-ui-button-font-color);
12+
}
13+
14+
.arduino-start-debug {
15+
border-radius: 12px;
16+
}

0 commit comments

Comments
 (0)