Skip to content
This repository was archived by the owner on Oct 1, 2024. It is now read-only.

Commit b68c65c

Browse files
Merge branch 'master' into master
2 parents 681871e + 3f00670 commit b68c65c

File tree

9 files changed

+152
-29
lines changed

9 files changed

+152
-29
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Welcome to **Arduino** <sup>preview</sup> for Visual Studio Code! The Arduino ex
1111
* Built-in serial monitor
1212
* Snippets for sketches
1313
* Automatic Arduino project scaffolding
14-
* Commond Palette (F1) integration of frequently used commands (e.g. Verify, Upload...)
14+
* Command Palette (F1) integration of frequently used commands (e.g. Verify, Upload...)
1515

1616
## Prerequisites
1717
Arduino IDE is required. Please install it from [here](https://www.arduino.cc/en/main/software#download).

snippets/sample.ino

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
void setup()
2+
{
3+
4+
}
5+
6+
void loop()
7+
{
8+
9+
}

src/arduino/arduino.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { ExampleManager } from "./exampleManager";
2020
import { LibraryManager } from "./libraryManager";
2121

2222
import { arduinoChannel } from "../common/outputChannel";
23+
import { SerialMonitor } from "../serialmonitor/serialMonitor";
2324

2425
/**
2526
* Represent an Arduino application based on the official Arduino IDE.
@@ -95,7 +96,7 @@ export class ArduinoApp {
9596
if (!boardDescriptor) {
9697
return;
9798
}
98-
if (!dc.sketch) {
99+
if (!dc.sketch || !util.fileExistsSync(path.join(vscode.workspace.rootPath, dc.sketch))) {
99100
await this.getMainSketch(dc);
100101
}
101102
if (!dc.port) {
@@ -106,15 +107,20 @@ export class ArduinoApp {
106107
arduinoChannel.show();
107108
arduinoChannel.start(`Upload sketch - ${dc.sketch}`);
108109

109-
await vscode.commands.executeCommand("arduino.closeSerialMonitor", dc.port);
110+
const serialMonitor = SerialMonitor.getIntance();
111+
112+
const needRestore = await serialMonitor.closeSerialMonitor(dc.port);
110113
await vscode.workspace.saveAll(false);
111114

112115
const appPath = path.join(vscode.workspace.rootPath, dc.sketch);
113116
const args = ["--upload", "--board", boardDescriptor, "--port", dc.port, appPath];
114117
if (this._settings.logLevel === "verbose") {
115118
args.push("--verbose");
116119
}
117-
await util.spawn(this._settings.commandPath, arduinoChannel.channel, args).then((result) => {
120+
await util.spawn(this._settings.commandPath, arduinoChannel.channel, args).then(async (result) => {
121+
if (needRestore) {
122+
await serialMonitor.openSerialMonitor();
123+
}
118124
arduinoChannel.end(`Uploaded the sketch: ${dc.sketch}${os.EOL}`);
119125
}, (reason) => {
120126
arduinoChannel.error(`Exit with code=${reason.code}${os.EOL}`);
@@ -128,7 +134,7 @@ export class ArduinoApp {
128134
return;
129135
}
130136

131-
if (!dc.sketch) {
137+
if (!dc.sketch || !util.fileExistsSync(path.join(vscode.workspace.rootPath, dc.sketch))) {
132138
await this.getMainSketch(dc);
133139
}
134140

src/arduino/boardManager.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import * as url from "url";
1010
import * as vscode from "vscode";
1111
import * as util from "../common/util";
1212

13+
import { arduinoChannel } from "../common/outputChannel";
1314
import { DeviceContext } from "../deviceContext";
1415
import { ArduinoApp } from "./arduino";
1516
import { Board, parseBoardDescriptor } from "./board";
@@ -174,7 +175,15 @@ export class BoardManager {
174175
if (!packageContent) {
175176
return;
176177
}
177-
const rawModel = JSON.parse(packageContent);
178+
179+
let rawModel = null;
180+
try {
181+
rawModel = JSON.parse(packageContent);
182+
} catch (ex) {
183+
arduinoChannel.error(`Invalid json file "${path.join(this._settings.packagePath, indexFileName)}".
184+
Suggest to remove it manually and allow boardmanager to re-download it.`);
185+
return ;
186+
}
178187

179188
this._packages.concat(rawModel.packages);
180189

@@ -202,7 +211,7 @@ export class BoardManager {
202211
public updateInstalledPlatforms(pkgName: string, arch: string) {
203212
const archPath = path.join(this._settings.packagePath, "packages", pkgName, "hardware", arch);
204213

205-
const allVersion = util.filterJunk(fs.readdirSync(archPath));
214+
const allVersion = util.filterJunk(util.readdirSync(archPath, true));
206215
if (allVersion && allVersion.length) {
207216
const newPlatform = {
208217
packageName: pkgName,
@@ -310,9 +319,9 @@ export class BoardManager {
310319
if (!util.directoryExistsSync(archPath)) {
311320
continue;
312321
}
313-
const architectures = util.filterJunk(fs.readdirSync(archPath));
322+
const architectures = util.filterJunk(util.readdirSync(archPath, true));
314323
architectures.forEach((architecture) => {
315-
const allVersion = util.filterJunk(fs.readdirSync(path.join(archPath, architecture)));
324+
const allVersion = util.filterJunk(util.readdirSync(path.join(archPath, architecture), true));
316325
if (allVersion && allVersion.length) {
317326
manuallyInstalled.push({
318327
packageName,

src/deviceContext.ts

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*-------------------------------------------------------------------------------------------*/
55

66
import * as fs from "fs";
7+
import * as os from "os";
78
import * as path from "path";
89
import * as vscode from "vscode";
910
import * as constants from "./common/constants";
@@ -64,6 +65,8 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable {
6465

6566
private _arduinoApp: ArduinoApp;
6667

68+
private _extensionPath: string;
69+
6770
private _watcher: vscode.FileSystemWatcher;
6871

6972
/**
@@ -92,6 +95,14 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable {
9295
this._arduinoApp = value;
9396
}
9497

98+
public get extensionPath(): string {
99+
return this._extensionPath;
100+
}
101+
102+
public set extensionPath(value: string) {
103+
this._extensionPath = value;
104+
}
105+
95106
/**
96107
* TODO: Current we use the Arduino default settings. For future release, this dependency might be removed
97108
* and the setting only depends on device.json.
@@ -177,16 +188,44 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable {
177188
vscode.window.showInformationMessage("Arduino.json is already generated.");
178189
return;
179190
} else {
180-
await vscode.commands.executeCommand("arduino.changeBoardType");
181191
await this.resolveMainSketch();
182-
vscode.window.showInformationMessage("The workspace is initialized with the Arduino extension support.");
192+
if (this.sketch) {
193+
await vscode.commands.executeCommand("arduino.changeBoardType");
194+
vscode.window.showInformationMessage("The workspace is initialized with the Arduino extension support.");
195+
} else {
196+
vscode.window.showInformationMessage("No *.ino sketch file was found or selected, so skip initialize command.");
197+
}
183198
}
184199
}
185200

186201
public async resolveMainSketch() {
187202
return await vscode.workspace.findFiles("**/*.ino", null)
188203
.then(async (fileUris) => {
189-
if (fileUris.length === 1) {
204+
if (fileUris.length === 0) {
205+
let newSketchFileName = await vscode.window.showInputBox({
206+
value: "app.ino",
207+
prompt: "No .ino file was found on workspace, initialize sketch first",
208+
placeHolder: "Input the sketch file name",
209+
validateInput: (value) => {
210+
if (value && /^\w+\.((ino)|(cpp)|c)$/.test(value.trim())) {
211+
return null;
212+
} else {
213+
return "Invalid sketch file name. Should be *.ino/*.cpp/*.c";
214+
}
215+
},
216+
});
217+
newSketchFileName = (newSketchFileName && newSketchFileName.trim()) || "";
218+
if (newSketchFileName) {
219+
const snippets = fs.readFileSync(path.join(this.extensionPath, "snippets", "sample.ino"));
220+
fs.writeFileSync(path.join(vscode.workspace.rootPath, newSketchFileName), snippets);
221+
this.sketch = newSketchFileName;
222+
// Open the new sketch file.
223+
const textDocument = await vscode.workspace.openTextDocument(path.join(vscode.workspace.rootPath, newSketchFileName));
224+
vscode.window.showTextDocument(textDocument, vscode.ViewColumn.One, true);
225+
} else {
226+
this._sketch = undefined;
227+
}
228+
} else if (fileUris.length === 1) {
190229
this.sketch = path.relative(vscode.workspace.rootPath, fileUris[0].fsPath);
191230
} else if (fileUris.length > 1) {
192231
const chosen = await vscode.window.showQuickPick(<vscode.QuickPickItem[]>fileUris.map((fileUri): vscode.QuickPickItem => {

src/extension.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Copyright (C) Microsoft Corporation. All rights reserved.
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*-------------------------------------------------------------------------------------------*/
5+
import * as path from "path";
56
import * as Uuid from "uuid/v4";
67
import * as vscode from "vscode";
78

@@ -18,10 +19,24 @@ import * as Logger from "./logger/logger";
1819
import { SerialMonitor } from "./serialmonitor/serialMonitor";
1920
import { UsbDetector } from "./serialmonitor/usbDetector";
2021

22+
let usbDetector: UsbDetector = null;
23+
2124
export async function activate(context: vscode.ExtensionContext) {
2225
Logger.configure(context);
2326
const activeGuid = Uuid().replace(/\-/g, "");
2427
Logger.traceUserData("start-activate-extension", { correlationId: activeGuid });
28+
// Show a warning message if the working file is not under the workspace folder.
29+
// People should know the extension might not work appropriately, they should look for the doc to get started.
30+
const openEditor = vscode.window.activeTextEditor;
31+
if (openEditor && openEditor.document.fileName.endsWith(".ino")) {
32+
const workingFile = path.normalize(openEditor.document.fileName);
33+
const workspaceFolder = (vscode.workspace && vscode.workspace.rootPath) || "";
34+
if (!workspaceFolder || workingFile.indexOf(path.normalize(workspaceFolder)) < 0) {
35+
vscode.window.showWarningMessage(`The working file "${workingFile}" is not under the workspace folder, ` +
36+
"the arduino extension might not work appropriately.");
37+
}
38+
}
39+
2540
const arduinoSettings = new ArduinoSettings();
2641
await arduinoSettings.initialize();
2742
const arduinoApp = new ArduinoApp(arduinoSettings);
@@ -30,6 +45,7 @@ export async function activate(context: vscode.ExtensionContext) {
3045
// TODO: After use the device.json config, should remove the dependency on the ArduinoApp object.
3146
const deviceContext = DeviceContext.getIntance();
3247
deviceContext.arduinoApp = arduinoApp;
48+
deviceContext.extensionPath = context.extensionPath;
3349
await deviceContext.loadContext();
3450
context.subscriptions.push(deviceContext);
3551

@@ -109,7 +125,7 @@ export async function activate(context: vscode.ExtensionContext) {
109125
context.subscriptions.push(registerCommand("arduino.addLibPath", (path) => arduinoApp.addLibPath(path)));
110126

111127
// serial monitor commands
112-
const serialMonitor = new SerialMonitor();
128+
const serialMonitor = SerialMonitor.getIntance();
113129
context.subscriptions.push(serialMonitor);
114130
context.subscriptions.push(registerCommand("arduino.selectSerialPort", () => serialMonitor.selectSerialPort(null, null)));
115131
context.subscriptions.push(registerCommand("arduino.openSerialMonitor", () => serialMonitor.openSerialMonitor()));
@@ -120,7 +136,7 @@ export async function activate(context: vscode.ExtensionContext) {
120136
const completionProvider = new CompletionProvider(arduinoApp);
121137
context.subscriptions.push(vscode.languages.registerCompletionItemProvider(ARDUINO_MODE, completionProvider, "<", '"', "."));
122138

123-
const usbDetector = new UsbDetector(arduinoApp, boardManager, serialMonitor, context.extensionPath);
139+
usbDetector = new UsbDetector(arduinoApp, boardManager, context.extensionPath);
124140
usbDetector.startListening();
125141

126142
const updateStatusBar = () => {
@@ -142,6 +158,11 @@ export async function activate(context: vscode.ExtensionContext) {
142158
Logger.traceUserData("end-activate-extension", { correlationId: activeGuid });
143159
}
144160

145-
export function deactivate() {
161+
export async function deactivate() {
162+
const monitor = SerialMonitor.getIntance();
163+
await monitor.closeSerialMonitor(null, false);
164+
if (usbDetector) {
165+
usbDetector.stopListening();
166+
}
146167
Logger.traceUserData("deactivate-extension");
147168
}

src/serialmonitor/serialMonitor.ts

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ export class SerialMonitor implements vscode.Disposable {
2626
return [300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 250000];
2727
}
2828

29+
public static getIntance(): SerialMonitor {
30+
if (SerialMonitor._serailMonitor === null) {
31+
SerialMonitor._serailMonitor = new SerialMonitor();
32+
}
33+
return SerialMonitor._serailMonitor;
34+
}
35+
36+
private static _serailMonitor: SerialMonitor = null;
37+
2938
private _currentPort: string;
3039

3140
private _currentBaudRate: number;
@@ -40,7 +49,7 @@ export class SerialMonitor implements vscode.Disposable {
4049

4150
private _outputChannel: vscode.OutputChannel;
4251

43-
constructor() {
52+
private constructor() {
4453
this._outputChannel = vscode.window.createOutputChannel(SerialMonitor.SERIAL_MONITOR);
4554
this._currentBaudRate = SerialMonitor.DEFAULT_BAUD_RATE;
4655
this._portsStatusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 2);
@@ -51,7 +60,7 @@ export class SerialMonitor implements vscode.Disposable {
5160
this._openPortStatusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 3);
5261
this._openPortStatusBar.command = "arduino.openSerialMonitor";
5362
this._openPortStatusBar.text = `$(plug)`;
54-
this._openPortStatusBar.tooltip = "Open Port";
63+
this._openPortStatusBar.tooltip = "Open Serial Monitor";
5564
this._openPortStatusBar.show();
5665

5766
this._baudRateStatusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 4);
@@ -96,14 +105,24 @@ export class SerialMonitor implements vscode.Disposable {
96105
};
97106
}).sort((a, b): number => {
98107
return a.label === b.label ? 0 : (a.label > b.label ? 1 : -1);
99-
}));
108+
}), { placeHolder: "Select a serial port" });
100109
if (chosen && chosen.label) {
101110
this.updatePortListStatus(chosen.label);
102111
}
103112
}
104113
}
105114

106115
public async openSerialMonitor() {
116+
if (!this._currentPort) {
117+
const ans = await vscode.window.showInformationMessage("No serial port was selected, please select a serial port first", "Yes", "No");
118+
if (ans === "Yes") {
119+
await this.selectSerialPort(null, null);
120+
}
121+
if (!this._currentPort) {
122+
return ;
123+
}
124+
}
125+
107126
if (this._serialPortCtrl) {
108127
if (this._currentPort !== this._serialPortCtrl.currentPort) {
109128
await this._serialPortCtrl.changePort(this._currentPort);
@@ -114,6 +133,12 @@ export class SerialMonitor implements vscode.Disposable {
114133
} else {
115134
this._serialPortCtrl = new SerialPortCtrl(this._currentPort, this._currentBaudRate, this._outputChannel);
116135
}
136+
137+
if (!this._serialPortCtrl.currentPort) {
138+
Logger.traceError("openSerialMonitorError", new Error(`Failed to open serial port ${this._currentPort}`));
139+
return ;
140+
}
141+
117142
try {
118143
await this._serialPortCtrl.open();
119144
this.updatePortStatus(true);
@@ -157,16 +182,18 @@ export class SerialMonitor implements vscode.Disposable {
157182
this._baudRateStatusBar.text = chosen;
158183
}
159184

160-
public async closeSerialMonitor(port: string) {
185+
public async closeSerialMonitor(port: string, showWarning: boolean = true): Promise<boolean> {
161186
if (this._serialPortCtrl) {
162-
if (port && port !== this._currentPort) {
187+
if (port && port !== this._serialPortCtrl.currentPort) {
163188
// Port is not opened
164-
return;
189+
return false;
165190
}
166-
await this._serialPortCtrl.stop();
191+
const result = await this._serialPortCtrl.stop();
167192
this.updatePortStatus(false);
168-
} else if (!port) {
193+
return result;
194+
} else if (!port && showWarning) {
169195
Logger.notifyUserWarning("closeSerialMonitorError", new Error(constants.messages.SERIAL_PORT_NOT_STARTED));
196+
return false;
170197
}
171198
}
172199

src/serialmonitor/serialportctrl.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export class SerialPortCtrl {
129129
public stop(): Promise<any> {
130130
return new Promise((resolve, reject) => {
131131
if (!this._currentSerialPort || !this.isActive) {
132-
resolve();
132+
resolve(false);
133133
return;
134134
}
135135
this._currentSerialPort.close((err) => {
@@ -140,7 +140,7 @@ export class SerialPortCtrl {
140140
if (err) {
141141
reject(err);
142142
} else {
143-
resolve();
143+
resolve(true);
144144
}
145145
});
146146
});

0 commit comments

Comments
 (0)