├── .DS_Store
├── .gitignore
├── .vscode
├── launch.json
├── settings.json
└── tasks.json
├── .vscodeignore
├── CHANGELOG.md
├── README.md
├── images
├── arduino-logo.svg
├── compile.svg
└── upload.svg
├── package.json
├── src
├── arduino
│ └── index.ts
├── config
│ └── index.ts
└── extension.ts
├── test
├── extension.test.ts
└── index.ts
├── tsconfig.json
└── vsc-extension-quickstart.md
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steve3d/arduino-vscode/7ee39fe9fa8ee3a2f35eea16e33495d03332befa/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | out
2 | node_modules
3 | *.vsix
4 | .vscode-test/
5 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | // A launch configuration that compiles the extension and then opens it inside a new window
2 | {
3 | "version": "0.1.0",
4 | "configurations": [
5 | {
6 | "name": "Launch Extension",
7 | "type": "extensionHost",
8 | "request": "launch",
9 | "runtimeExecutable": "${execPath}",
10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
11 | "stopOnEntry": false,
12 | "sourceMaps": true,
13 | "outFiles": [ "${workspaceRoot}/out/src/**/*.js" ],
14 | "preLaunchTask": "npm"
15 | },
16 | {
17 | "name": "Launch Tests",
18 | "type": "extensionHost",
19 | "request": "launch",
20 | "runtimeExecutable": "${execPath}",
21 | "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ],
22 | "stopOnEntry": false,
23 | "sourceMaps": true,
24 | "outFiles": [ "${workspaceRoot}/out/test/**/*.js" ],
25 | "preLaunchTask": "npm"
26 | }
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "files.exclude": {
4 | "out": false // set this to true to hide the "out" folder with the compiled JS files
5 | },
6 | "search.exclude": {
7 | "out": true // set this to false to include "out" folder in search results
8 | },
9 | "typescript.tsdk": "./node_modules/typescript/lib", // we want to use the TS server from our node_modules folder to control its version
10 | "vsicons.presets.angular": false,
11 | "files.trimTrailingWhitespace": true
12 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | // Available variables which can be used inside of strings.
2 | // ${workspaceRoot}: the root folder of the team
3 | // ${file}: the current opened file
4 | // ${fileBasename}: the current opened file's basename
5 | // ${fileDirname}: the current opened file's dirname
6 | // ${fileExtname}: the current opened file's extension
7 | // ${cwd}: the current working directory of the spawned process
8 |
9 | // A task runner that calls a custom npm script that compiles the extension.
10 | {
11 | "version": "0.1.0",
12 |
13 | // we want to run npm
14 | "command": "npm",
15 |
16 | // the command is a shell script
17 | "isShellCommand": true,
18 |
19 | // show the output window only if unrecognized errors occur.
20 | "showOutput": "silent",
21 |
22 | // we run the custom script "compile" as defined in package.json
23 | "args": ["run", "compile", "--loglevel", "silent"],
24 |
25 | // The tsc compiler is started in watching mode
26 | "isWatching": true,
27 |
28 | // use the standard tsc in watch mode problem matcher to find compile problems in the output.
29 | "problemMatcher": "$tsc-watch"
30 | }
--------------------------------------------------------------------------------
/.vscodeignore:
--------------------------------------------------------------------------------
1 | .vscode/**
2 | .vscode-test/**
3 | out/test/**
4 | test/**
5 | src/**
6 | **/*.map
7 | .gitignore
8 | tsconfig.json
9 | vsc-extension-quickstart.md
10 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to the "arduino-vscode" extension will be documented in this file.
3 |
4 | ## 0.2.2 - 2017-03-21
5 | - Display size summary after building. #15
6 | - and some improvement for setting documentation
7 |
8 | ## 0.2.1 - 2017-02-14
9 | - bug fix
10 |
11 | ## 0.2.0 - 2017-02-06
12 | - support custom uploader
13 |
14 | ## 0.1.4 - 2017-02-04
15 | - make the verbose option the real verbose.
16 |
17 | ## 0.1.3 - 2017-02-04
18 | - added a verbose option, when build/upload, the build/upload flags will also shown in the output
19 | - make sure the file flag in build/upload is placed as last flag.
20 |
21 | ## 0.1.2 - 2017-01-23
22 | - fix a bug of update configuration of serialPort
23 |
24 | ## 0.1.1 - 2017-01-14
25 | - add a extension logo
26 |
27 | ## 0.1.0 - 2017-01-14
28 | - added linux support
29 | - add diagnostic support for failed build
30 | - rewrite the Config class
31 | - automatically use the default installlation of Arduino IDE
32 | - only need to set the serial port setting if Arduino IDE installed with default folder on macOS and Windows
33 |
34 | ## [Unreleased]
35 | - Initial release
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Arduino Support for Visual Studio Code (abandoned)
2 |
3 |
4 | The Arduino extension makes it easy to build and upload Arduino sketch from Visual Studio Code.
5 |
6 | * C++ intellisense with cpptools
7 | * Build/Compile sketch with VSCode
8 | * Upload sketch
9 | * Custom uploader support
10 |
11 | ## Features
12 |
13 | Integrate the Arduino IDE for VSCode, with this extension, you can edit sketch with intellisense and use the VSCode to compile and upload to your board.
14 |
15 | ## Requirements
16 |
17 | * Recent version of Arduino IDE (at least v1.8.x)
18 | * ms-vscode.cpptools extension for C++ intellisense to work
19 | * An Arduino compatiable board to develop. :)
20 | * Manually associate the .ino file to C++ if needed.
21 |
22 | ## Extension Settings
23 |
24 | This extension contributes the following settings:
25 |
26 | * `arduino.idePath`: Specify where the Arduino IDE is (Note: Absolute path only).
27 | * `arduino.libraryPath`: Specifies the serial port board are connected (Note: Absolute path only).
28 | * `arduino.fqbn`: Specifies the board type to use (fully qualified board name).
29 | * `arduino.serialPort`: Specifies the serial port borad are connected. (Windows: COMx, macOS: /dev/cu./dev/cu.usbmodemxxxx, Linux: /dev/ttyUSBxx)
30 | * `arduino.warnPercentage`: set to `blah` to do something
31 | * `arduino.compileOptions`: Additional options for compile the sketch
32 | * `arduino.uploader`: Custom uploader for extra board types
33 | * `arduino.uploadOptions`: Additional options for avrdude upload the compiled sketch
34 | * `arduino.partno`: Specify AVR device (Upload only)
35 | * `arduino.programmer`: Specify programmer type (Upload only)
36 | * `arduino.baudrate`: Override RS-232 baud rate (Upload only)
37 | * `arduino.verbose`: Use verbose output when build and upload.
38 |
39 | Please note, every path here should be absolute path, relative path won't work.
40 |
41 | ## Custom uploader support
42 |
43 | If you need to develop on a custom board like NodeMCU, you need to specify the uploader `esptool` to `arduino.uploader` option.
44 | Once you set the uploader, the extension will use the `arduino.uploadOptions` with this uploader. And there are some replacement arguments for the `uploadOptions`:
45 |
46 | - `$BAUDRATE` will be replaced to the baudrate option
47 | - `$SERIALPORT` will be replaced to the serial port option
48 | - `$TARGET` will be replaced to the compiled object, (Note, this option contains no `bin` or `hex` extension).
49 |
50 | For example:
51 |
52 | ```
53 | "arduino.fqbn": "esp8266:esp8266:nodemcu:CpuFrequency=80,UploadSpeed=115200,FlashSize=4M3M",
54 | "arduino.uploader" : "/Users/steve/Library/Arduino15/packages/esp8266/tools/esptool/0.4.9/esptool",
55 | "arduino.uploadOptions": "-vv -cd ck -cb $BAUDRATE -cp $SERIALPORT -ca 0x00000 -cf $TARGET.bin",
56 | "arduino.compileOptions": "-hardware /Users/steve/Library/Arduino15/packages -tools /Users/steve/Library/Arduino15/packages -prefs=runtime.tools.esptool.path=/Users/steve/Library/Arduino15/packages/esp8266/tools/esptool/0.4.9 -prefs=runtime.tools.xtensa-lx106-elf-gcc.path=/Users/steve/Library/Arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2 -prefs=runtime.tools.mkspiffs.path=/Users/steve/Library/Arduino15/packages/esp8266/tools/mkspiffs/0.1.2"
57 | ```
58 |
59 | You can get these options by enabling verbose output when you compile/upload in Arduino IDE.
60 |
61 | ## Known Issues
62 |
63 | Linux support not tested, but it should work.
64 | Custom uploader not tested, because I don't have a board that requires or implements a custom uploader to test.
65 |
66 | ## Change log
67 |
68 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
69 |
70 | -----------------------------------------------------------------------------------------------------------
71 |
72 | **Enjoy!**
73 |
--------------------------------------------------------------------------------
/images/arduino-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 |
22 | image/svg+xml
23 |
25 |
26 |
27 |
28 |
30 |
50 |
56 |
59 |
60 |
65 |
68 |
72 |
73 |
78 |
79 |
80 |
81 |
88 |
91 |
95 |
96 |
97 |
107 |
111 |
115 |
119 |
123 |
127 |
131 |
135 |
139 |
140 |
141 |
146 |
150 |
154 |
155 |
158 |
159 |
160 |
--------------------------------------------------------------------------------
/images/compile.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/images/upload.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "arduino-vscode",
3 | "displayName": "Arduino",
4 | "description": "Arduino Support for VSCode",
5 | "version": "0.2.2",
6 | "publisher": "steveyin",
7 | "license": "LGPL-3.0",
8 | "icon": "images/arduino-logo.svg",
9 | "preview": true,
10 | "homepage": "https://github.com/steve3d/arduino-vscode/blob/master/README.md",
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/steve3d/arduino-vscode.git"
14 | },
15 | "engines": {
16 | "vscode": "^1.5.0"
17 | },
18 | "extensionDependencies": [
19 | "ms-vscode.cpptools"
20 | ],
21 | "categories": [
22 | "Other"
23 | ],
24 | "activationEvents": [
25 | "onCommand:extension.build",
26 | "onCommand:extension.rebuild",
27 | "onCommand:extension.clean",
28 | "onCommand:extension.upload",
29 | "onCommand:extension.buildAndUpload",
30 | "onCommand:extension.rebuildAndUpload",
31 | "onCommand:extension.initialize"
32 | ],
33 | "main": "./out/src/extension",
34 | "contributes": {
35 | "configuration": {
36 | "type": "object",
37 | "title": "Arduino configuration",
38 | "properties": {
39 | "arduino.idePath": {
40 | "type": ["string"],
41 | "default": null,
42 | "description": "Specify where the Arduino IDE is (Absolute path only)."
43 | },
44 | "arduino.libraryPath": {
45 | "type": ["string"],
46 | "default": null,
47 | "description": "Specifies the path to your library directory. Default is used path to /Documents/Arduino/libraries (Absolute path only)."
48 | },
49 | "arduino.fqbn": {
50 | "type": ["string"],
51 | "default": "arduino:avr:uno",
52 | "description": "Specifies the board type to use (fully qualified board name). \nYou can find this value in Arduino IDE output with checked \"verbose\" option in preferences or in boards.txt file in Arduino IDE directory. \n\nFormat: {packageID}:{platformID}:{boardID}:{boardOptions}. \nPlatform ID is usually name of directory with board.txt and package ID is usually name of directory contains parent of board.txt. \nBoard ID is prefix with board name from board.txt (part before first dot). \nIf board has different versions then you must specify BoardOptions. Usualy you must provide CPU variant in format cpu=version where version is the part from board.txt after part \"cpu\". \n\nFor example for Arduino Pro Mini (Atmega328p, 8MHz) we have in board.txt entry: pro.menu.cpu.8MHzatmega168=ATmega168 (3.3V, 8 MHz). Then full qualified name is: arduino:avr:pro:cpu=8MHzatmega328"
53 | },
54 | "arduino.serialPort": {
55 | "type": ["string"],
56 | "default": null,
57 | "description": "Specifies the serial port board are connected. (Windows: COMx, macOS: /dev/cu./dev/cu.usbmodemxxxx, Linux: /dev/ttyUSBxx)"
58 | },
59 | "arduino.warnMode": {
60 | "type":["string"],
61 | "default": "none",
62 | "description": "Specifies warnig verbosity. Possible values: \"none\", \"default\", \"more\", \"all\"."
63 | },
64 | "arduino.warnPercentage": {
65 | "type": ["number"],
66 | "default": 75,
67 | "description": ""
68 | },
69 | "arduino.compileOptions": {
70 | "type": ["string"],
71 | "default": "",
72 | "description": "Additional options for compile"
73 | },
74 | "arduino.uploader": {
75 | "type": ["string"],
76 | "default": null,
77 | "description": "Custom uploader for extra board types (Note: Set full upload options with this)"
78 | },
79 | "arduino.uploadOptions": {
80 | "type": ["string"],
81 | "default": "-D -q",
82 | "description": "Additional options for avrdude upload"
83 | },
84 | "arduino.partno": {
85 | "type": ["string"],
86 | "default": "atmega328p",
87 | "description": "Upload: Specify AVR device"
88 | },
89 | "arduino.programmer": {
90 | "type": ["string"],
91 | "default": "arduino",
92 | "description": "Upload: Specify programmer type"
93 | },
94 | "arduino.baudrate": {
95 | "type": ["number"],
96 | "default": 115200,
97 | "description": "Upload: Override RS-232 baud rate"
98 | },
99 | "arduino.verbose": {
100 | "type": ["boolean"],
101 | "default": false,
102 | "description": "Use verbose output in build and upload showing the build/upload flags."
103 | }
104 | }
105 | },
106 | "commands": [
107 | {
108 | "command": "extension.build",
109 | "title": "Arduino: Build",
110 | "icon": "./images/compile.svg"
111 | },
112 | {
113 | "command": "extension.rebuild",
114 | "title": "Arduino: Rebuild"
115 | },
116 | {
117 | "command": "extension.clean",
118 | "title": "Arduino: Clean"
119 | },
120 | {
121 | "command": "extension.upload",
122 | "title": "Arduino: Upload",
123 | "icon": "./images/upload.svg"
124 | },
125 | {
126 | "command": "extension.buildAndUpload",
127 | "title": "Arduino: Build & Upload"
128 | },
129 | {
130 | "command": "extension.rebuildAndUpload",
131 | "title": "Arduino: Rebuild & Upload"
132 | },
133 | {
134 | "command": "extension.initialize",
135 | "title": "Arduino: Initialize C++ Intellisense"
136 | }
137 | ],
138 | "menus": {
139 | "editor/title": [
140 | {
141 | "when": "resourceLangId == cpp",
142 | "command": "extension.build",
143 | "group": "navigation"
144 | },
145 | {
146 | "when": "resourceLangId == cpp",
147 | "command": "extension.upload",
148 | "group": "navigation"
149 | }
150 | ]
151 | }
152 | },
153 | "scripts": {
154 | "vscode:prepublish": "tsc -p ./",
155 | "compile": "tsc -watch -p ./",
156 | "postinstall": "node ./node_modules/vscode/bin/install",
157 | "test": "node ./node_modules/vscode/bin/test"
158 | },
159 | "devDependencies": {
160 | "typescript": "^2.0.3",
161 | "vscode": "^1.0.0",
162 | "mocha": "^2.3.3",
163 | "@types/node": "^6.0.40",
164 | "@types/mocha": "^2.2.32"
165 | }
166 | }
--------------------------------------------------------------------------------
/src/arduino/index.ts:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as vscode from 'vscode'
4 | import * as fs from 'fs';
5 | import * as path from 'path'
6 | import * as child_process from 'child_process'
7 | import * as os from 'os';
8 | import { EventEmitter } from "events";
9 | import { ConfigUtil } from '../config'
10 |
11 | export class ArduinoVS {
12 | private config: ConfigUtil;
13 | private updateSettings = `Please update workspace settings, Can't continue without Arduino IDE and the serial port.`;
14 | private builtEvent = new EventEmitter();
15 | private building = false;
16 | private uploading = false;
17 | private diagnostics: vscode.DiagnosticCollection;
18 | private errors: Object;
19 |
20 | constructor(private output: vscode.OutputChannel) {
21 | this.config = new ConfigUtil();
22 |
23 | this.diagnostics = vscode.languages.createDiagnosticCollection();
24 | }
25 |
26 | get needRebuild(): boolean {
27 | if (vscode.window.activeTextEditor.document.isDirty)
28 | return true;
29 |
30 | let hexPath = this.config.hexPath;
31 | if (!fs.existsSync(hexPath))
32 | return true;
33 |
34 | let hexStat = fs.lstatSync(hexPath);
35 | let sourceStat = fs.lstatSync(vscode.window.activeTextEditor.document.fileName);
36 |
37 | return sourceStat.mtime > hexStat.mtime;
38 | }
39 |
40 | ouputMessage(data: string) {
41 | this.output.append(data);
42 | }
43 |
44 | initialize() {
45 | if (!this.config.hasIdePath || !this.config.hasSerialPort) {
46 | vscode.window.showErrorMessage(this.updateSettings);
47 | return;
48 | }
49 |
50 | let cppJsonFile = path.join(vscode.workspace.rootPath, '.vscode/c_cpp_properties.json');
51 | fs.writeFile(cppJsonFile, JSON.stringify(this.config.cppConfig, null, 4),
52 | () => vscode.window.showInformationMessage("Successfully updated C++ Intellisense settings."));
53 | }
54 |
55 | showProgress(data: string, item: vscode.StatusBarItem, status: string) {
56 | let output = data.split('\n');
57 | output.forEach(line => {
58 | if(line == '')
59 | return;
60 |
61 | let parts = line.split(' ||| ');
62 | if(parts.length == 3) {
63 | let value = parts[2].substr(1, parts[2].length-2);
64 | if(line.includes('Progress'))
65 | item.text = status + value + '%';
66 | else {
67 | let values = value.split(' ');
68 | if(values.length == 3)
69 | this.output.append(`Sketch uses ${values[0]} bytes (${values[2]}%) of program storage space. Maximum is ${values[1]} bytes.\n`)
70 | else if(values.length == 4)
71 | this.output.append(`Global variables use ${values[0]} bytes (${values[2]}%) of dynamic memory, leaving ${values[3]} bytes for local variables. Maximum is ${values[1]} bytes.\n`)
72 | else
73 | this.output.append(line + '\n');
74 | }
75 |
76 | } else
77 | this.output.append(line + '\n');
78 | })
79 | }
80 |
81 | addDiagnostic(status: RegExpMatchArray) {
82 | if(status == null)
83 | return;
84 |
85 | if(!this.errors.hasOwnProperty(status[1]))
86 | this.errors[status[1]] = [];
87 | let line = parseInt(status[2], 10)-1;
88 | let column = parseInt(status[3], 10);
89 | let diag = new vscode.Diagnostic(
90 | new vscode.Range(line, column, line, column),
91 | status[5],
92 | status[4] == 'error' ? vscode.DiagnosticSeverity.Error : vscode.DiagnosticSeverity.Warning);
93 |
94 | this.errors[status[1]].push(diag);
95 | }
96 |
97 | build() {
98 | if(this.building) {
99 | vscode.window.showErrorMessage('Building in progress, please wait a moment.');
100 | return;
101 | }
102 |
103 | let document = vscode.window.activeTextEditor.document;
104 |
105 | if (!this.config.hasIdePath) {
106 | vscode.window.showErrorMessage('Can not build anything without Arduino IDE, please update settings.');
107 | return;
108 | }
109 |
110 | if (document.isUntitled) {
111 | vscode.window.showInformationMessage('Please save the file first!');
112 | }
113 |
114 | if (document.isDirty) {
115 | document.save()
116 | .then((success) => console.log('save status:', success));
117 | }
118 |
119 | this.errors = {};
120 | this.diagnostics.clear();
121 | this.output.clear();
122 | this.output.append('============== Begin to compile. ==============\n')
123 | if(this.config.verbose) {
124 | this.output.append(this.config.builder + ' ');
125 | this.output.append(this.config.buildArgs.join(' ') + '\n');
126 | }
127 | this.building = true;
128 | let spawn = child_process.spawn(this.config.builder, this.config.buildArgs);
129 | let statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
130 | let status = 'Compiling ';
131 | statusBarItem.text = status + '0%';
132 | statusBarItem.show();
133 |
134 | spawn.stdout.on('data', data => this.showProgress(String.fromCharCode.apply(null, data),
135 | statusBarItem,
136 | status));
137 |
138 | spawn.stderr.on('data', data => {
139 | let error:string = String.fromCharCode.apply(null, data);
140 |
141 | error.split('\n')
142 | .forEach(line => this.addDiagnostic(line.match(/(.*):(\d+):(\d+):\s+(warning|error):\s+(.*)/)));
143 |
144 | this.output.append(error);
145 | });
146 |
147 | spawn.on('close', (result) => {
148 | // If build successed then we display summary output size.
149 | if (result == 0)
150 | this.summarySize((summaryResult) => this.onBuildFinished(result, statusBarItem));
151 | else
152 | this.onBuildFinished(result, statusBarItem);
153 | });
154 | }
155 |
156 | onBuildFinished(result: number, statusBarItem: vscode.StatusBarItem) {
157 | this.builtEvent.emit('build', result);
158 | statusBarItem.dispose();
159 | this.building = false;
160 | if(result)
161 | this.output.show(true);
162 |
163 | let items = [];
164 | for (let key in this.errors) {
165 | if (this.errors.hasOwnProperty(key)) {
166 | items.push([vscode.Uri.file(key), this.errors[key]]);
167 | }
168 | }
169 |
170 | this.diagnostics.set(items);
171 |
172 | if(result)
173 | vscode.window.showErrorMessage('Build failed.');
174 | }
175 |
176 | deleteFolderRecursive(path) {
177 | var files = [];
178 | if (fs.existsSync(path)) {
179 | files = fs.readdirSync(path);
180 | files.forEach((file) => {
181 | var curPath = path + "/" + file;
182 | if (fs.lstatSync(curPath).isDirectory()) { // recurse
183 | this.deleteFolderRecursive(curPath);
184 | } else { // delete file
185 | fs.unlinkSync(curPath);
186 | }
187 | });
188 |
189 | fs.rmdirSync(path);
190 | }
191 | };
192 |
193 | clean(info = true) {
194 | if (fs.existsSync(this.config.buildPath)) {
195 | this.deleteFolderRecursive(this.config.buildPath);
196 | if(info)
197 | vscode.window.showInformationMessage('Intermediate ouput folder cleaned.');
198 | } else if(info)
199 | vscode.window.showInformationMessage('Nothing need to clean.');
200 | }
201 |
202 | rebuild() {
203 | this.clean(false);
204 | this.build();
205 | }
206 |
207 | upload() {
208 | if(this.uploading || this.building) {
209 | vscode.window.showErrorMessage('Building or uploading in progress. Please wait');
210 | return;
211 | }
212 |
213 | if(!this.config.hasSerialPort) {
214 | vscode.window.showErrorMessage('Can not upload without serial port specified, please update settings.');
215 | return;
216 | }
217 |
218 | if (this.needRebuild) {
219 | this.build();
220 | this.builtEvent.on('build', result => {
221 | if(result == 0)
222 | this._realUpload();
223 | })
224 | } else
225 | this._realUpload();
226 | }
227 |
228 | private _realUpload() {
229 | this.uploading = true;
230 | let spawn = child_process.spawn(this.config.avrdude, this.config.uploadArgs);
231 |
232 | this.output.append('\n============== Begin to upload. ==============\n');
233 | if(this.config.verbose) {
234 | this.output.append(this.config.avrdude + ' ');
235 | this.output.append(this.config.uploadArgs.join(' ') + '\n');
236 | }
237 |
238 | spawn.stdout.on('data', data => this.output.append(String.fromCharCode.apply(null, data)));
239 | spawn.stderr.on('data', data => this.output.append(String.fromCharCode.apply(null, data)));
240 | spawn.on('close', (result) => {
241 | this.uploading = false;
242 | this.output.append(`\nUpload ${result ? 'failed' : 'success'}.\n`);
243 | if(result)
244 | this.output.show(true);
245 | });
246 |
247 | this.builtEvent.removeAllListeners();
248 | }
249 |
250 | buildAndUpload() {
251 | this.build();
252 |
253 | this.builtEvent.on('build', result => {
254 | console.log('build result', result);
255 | if(!result)
256 | this.upload();
257 | else
258 | vscode.window.showErrorMessage('Build failed, Can not upload.');
259 |
260 | });
261 | }
262 |
263 | rebuildAndUpload() {
264 | this.rebuild();
265 |
266 | this.builtEvent.on('build', result => {
267 | if(!result)
268 | this.upload();
269 | else
270 | vscode.window.showErrorMessage('Build failed, Can not upload.');
271 |
272 | });
273 | }
274 |
275 | summarySize(onEnd: (resultCode: number) => void): void {
276 | let spawn = child_process.spawn(this.config.avrsize, this.config.sizeArgs);
277 |
278 | // Add empty line
279 | this.output.appendLine('');
280 |
281 | spawn.stdout.on('data', data => {
282 | let msg: string = String.fromCharCode.apply(null, data);
283 | this.output.append(msg);
284 | });
285 |
286 | spawn.stderr.on('data', data => {
287 | let error:string = String.fromCharCode.apply(null, data);
288 |
289 | error.split('\n')
290 | .forEach(line => this.addDiagnostic(line.match(/(.*):(\d+):(\d+):\s+(warning|error):\s+(.*)/)));
291 |
292 | this.output.append(error);
293 | });
294 |
295 | spawn.on('close', (result) => onEnd(result));
296 | }
297 | }
--------------------------------------------------------------------------------
/src/config/index.ts:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as vscode from 'vscode';
4 | import * as os from 'os';
5 | import * as path from 'path';
6 | import * as fs from 'fs';
7 | import * as process from 'process';
8 |
9 |
10 | export class ConfigUtil {
11 |
12 | private _convertSeprator = false;
13 | private _idePath: string;
14 | private _libraryPath: string;
15 | private _serialPort: string;
16 | private _fqbn: string;
17 | private _warnMode: string;
18 | private _warnPercentage: number;
19 | private _partno: string;
20 | private _programmer: string;
21 | private _baudrate: number;
22 | private _compileOptions: string[];
23 | private _uploader: string = null;
24 | private _uploadOptions: string[];
25 | private _verbose: boolean;
26 |
27 | constructor() {
28 | if(os.type() == 'Windows_NT')
29 | this._convertSeprator = true;
30 | this._updateSettings();
31 | vscode.workspace.onDidChangeConfiguration((e) => this._updateSettings());
32 | }
33 |
34 | private _updateSettings() {
35 | let config = vscode.workspace.getConfiguration('arduino');
36 | this._fqbn = config.get('fqbn');
37 | this._warnMode = config.get('warnMode');
38 | this._warnPercentage = config.get('warnPercentage');
39 | this._partno = config.get('partno');
40 | this._programmer = config.get('programmer');
41 | this._baudrate = config.get('baudrate');
42 | this._serialPort = config.get('serialPort');
43 | this._verbose = config.get('verbose');
44 |
45 | this._compileOptions = config.get('compileOptions', '').split(' ').filter(x => x != '');
46 |
47 | this._uploader = config.get('uploader');
48 | this._uploadOptions = config.get('uploadOptions', '').split(' ').filter(x => x != '');
49 |
50 | this._updateIdePath(config);
51 | this._updateLibraryPath(config);
52 | }
53 |
54 | private _updateIdePath(config: vscode.WorkspaceConfiguration) {
55 | this._idePath = config.get('idePath');
56 |
57 | switch(os.type()) {
58 | case 'Windows_NT':
59 | if(this._idePath == null)
60 | this._idePath = 'C:\\Program Files (x86)\\Arduino';
61 |
62 | if(!fs.existsSync(path.join(this._idePath, 'arduino-builder.exe')))
63 | this._idePath = null
64 | break;
65 | case 'Linux':
66 | if(!fs.existsSync(path.join(this._idePath, 'arduino-builder')))
67 | this._idePath = null
68 | break;
69 | case 'Darwin':
70 | if(this._idePath == null)
71 | this._idePath = '/Applications/Arduino.app'
72 |
73 | if(fs.existsSync(path.join(this._idePath, 'Contents/Java/arduino-builder')))
74 | this._idePath = path.join(this._idePath, 'Contents/Java');
75 | else
76 | this._idePath = null;
77 |
78 | break;
79 | default:
80 | this._idePath = null;
81 | }
82 |
83 | // custom uploader must also exists
84 | if(this._uploader && !fs.existsSync(this._uploader))
85 | this._uploader = null;
86 | }
87 |
88 | private _updateLibraryPath(config: vscode.WorkspaceConfiguration) {
89 | this._libraryPath = config.get('libraryPath');
90 |
91 | if(this._libraryPath == null) {
92 | this._libraryPath = path.join(os.homedir(), 'Documents/Arduino/libraries');
93 | if(!fs.existsSync(this._libraryPath))
94 | this._libraryPath = os.homedir();
95 | }
96 | }
97 |
98 | get hasIdePath(): boolean {
99 | return this._idePath != null;
100 | }
101 |
102 | get hasSerialPort(): boolean {
103 | return this._serialPort != null;
104 | }
105 |
106 | get basename(): string {
107 | return path.basename(vscode.window.activeTextEditor.document.fileName, '.ino')
108 | }
109 |
110 | get filename(): string {
111 | return path.basename(vscode.window.activeTextEditor.document.fileName);
112 | }
113 |
114 | get hexPath(): string {
115 | return path.join(vscode.workspace.rootPath, `.build/${this.basename}/${this.filename}`);
116 | }
117 |
118 | get buildPath(): string {
119 | let document = vscode.window.activeTextEditor.document;
120 |
121 | let buildPath = path.join(vscode.workspace.rootPath, `.build`);
122 |
123 | if (!fs.existsSync(buildPath))
124 | fs.mkdirSync(buildPath);
125 |
126 | buildPath = path.join(buildPath, this.basename);
127 | if (!fs.existsSync(buildPath))
128 | fs.mkdirSync(buildPath);
129 |
130 | return buildPath;
131 | }
132 |
133 | get cppConfig(): any {
134 | let includes = [
135 | path.join(this._idePath, 'hardware/arduino/avr/cores/arduino'),
136 | path.join(this._idePath, 'hardware/arduino/avr/libraries'),
137 | path.join(this._idePath, 'hardware/arduino/avr/variants/standard'),
138 | path.join(this._idePath, 'libraries')
139 | ];
140 |
141 | if (this._libraryPath) {
142 | includes.push(this._libraryPath);
143 | // libs path
144 | fs.readdirSync(this._libraryPath)
145 | .map(item => path.join(this._libraryPath, item))
146 | .filter(item => fs.lstatSync(item).isDirectory())
147 | .forEach(item => includes.push(item));
148 | }
149 |
150 | if(this._convertSeprator)
151 | includes = includes.map(x => x.replace(/\//g, '\\'));
152 |
153 | return {
154 | configurations: [
155 | {
156 | name: 'Arduino',
157 | includePath: includes,
158 | browse: {
159 | 'limitSymbolsToIncludedHeaders': true,
160 | 'databaseFilename': ''
161 | }
162 | }
163 | ]
164 | };
165 | }
166 |
167 | get buildArgs(): string[] {
168 | let args = [
169 | '-compile',
170 | '-logger', this._verbose ? 'human' : 'machine',
171 | '-hardware', `${this._idePath}/hardware`,
172 | '-tools', `${this._idePath}/tools-builder`,
173 | '-tools', `${this._idePath}/hardware/tools/avr`,
174 | '-built-in-libraries', `${this._idePath}/libraries`,
175 | '-libraries', `${this._libraryPath}`,
176 | `-fqbn=${this._fqbn}`,
177 | '-build-path', this.buildPath,
178 | `-warnings=${this._warnMode}`,
179 | `-prefs=build.warn_data_percentage=${this._warnPercentage}`,
180 | `-prefs=runtime.tools.avr-gcc.path=${this._idePath}/hardware/tools/avr`,
181 | `-prefs=runtime.tools.avrdude.path=${this._idePath}/hardware/tools/avr`,
182 | `-prefs=runtime.tools.arduinoOTA.path=${this._idePath}/hardware/tools/avr`,
183 | ].concat(this._compileOptions);
184 |
185 | if(this._verbose)
186 | args.push('-verbose');
187 |
188 | args.push(vscode.window.activeTextEditor.document.fileName);
189 |
190 | return this._convertSeprator ? args.map(x => x.replace(/\//g, '\\')) : args;
191 | }
192 |
193 | get uploadArgs(): string[] {
194 | let args: string[];
195 | if(this._uploader) {
196 | args = this._uploadOptions
197 | .map(x => x.replace(/\$(TARGET|BAUDRATE|SERIALPORT)/,
198 | (match, p1) => {
199 | switch(match) {
200 | case '$TARGET':
201 | return this.hexPath;
202 | case '$BAUDRATE':
203 | return this._baudrate.toString();
204 | case '$SERIALPORT':
205 | return this._serialPort;
206 | }
207 |
208 | return match;
209 | } ));
210 | } else {
211 | args = [`-C${this._idePath}/hardware/tools/avr/etc/avrdude.conf`,
212 | `-p${this._partno}`,
213 | `-c${this._programmer}`,
214 | `-P${this._serialPort}`,
215 | `-b${this._baudrate}`,
216 | ].concat(this._uploadOptions,
217 | `-Uflash:w:${this.hexPath}.hex:i`);
218 | }
219 |
220 | return this._convertSeprator ? args.map(x => x.replace(/\//g, '\\')) : args;
221 | }
222 |
223 | get sizeArgs(): string[] {
224 | let args: string[];
225 | args = ['-C', // Used -C option, because the output is immediately in human readable format
226 | `--mcu=${this._partno}`,
227 | `${this.hexPath}.elf`];
228 |
229 | return this._convertSeprator ? args.map(x => x.replace(/\//g, '\\')) : args;
230 | }
231 |
232 | get builder(): string {
233 | let builder = path.join(this._idePath, 'arduino-builder');
234 |
235 | if(this._convertSeprator)
236 | builder = builder.replace(/\//g, '\\') + '.exe';
237 |
238 | return builder;
239 | }
240 |
241 | get avrdude(): string {
242 | if(this._uploader)
243 | return this._uploader;
244 |
245 | let avrdude = path.join(this._idePath, 'hardware/tools/avr/bin/avrdude');
246 |
247 | if(this._convertSeprator)
248 | avrdude = avrdude.replace(/\\/g, '\\') + '.exe';
249 |
250 | return avrdude;
251 | }
252 |
253 | get avrsize(): string {
254 | let avrsize = path.join(this._idePath, 'hardware/tools/avr/bin/avr-size');
255 |
256 | if (this._convertSeprator)
257 | avrsize = avrsize.replace(/\\/g, '\\') + '.exe';
258 |
259 | return avrsize;
260 | }
261 |
262 | get verbose(): boolean {
263 | return this._verbose;
264 | }
265 | }
266 |
--------------------------------------------------------------------------------
/src/extension.ts:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | // The module 'vscode' contains the VS Code extensibility API
3 | // Import the module and reference it with the alias vscode in your code below
4 | import * as vscode from 'vscode';
5 | import { ArduinoVS } from './arduino'
6 |
7 | // this method is called when your extension is activated
8 | // your extension is activated the very first time the command is executed
9 | export function activate(context: vscode.ExtensionContext) {
10 |
11 | // Use the console to output diagnostic information (console.log) and errors (console.error)
12 | // This line of code will only be executed once when your extension is activated
13 | console.log('ArduinoVS actived!');
14 |
15 | let output = vscode.window.createOutputChannel('Arduino');
16 |
17 | let arduino = new ArduinoVS(output);
18 |
19 | context.subscriptions.push(vscode.commands.registerTextEditorCommand('extension.build', () => arduino.build()));
20 | context.subscriptions.push(vscode.commands.registerTextEditorCommand('extension.rebuild', () => arduino.rebuild()));
21 | context.subscriptions.push(vscode.commands.registerTextEditorCommand('extension.clean', () => arduino.clean()));
22 | context.subscriptions.push(vscode.commands.registerTextEditorCommand('extension.upload', () => arduino.upload()));
23 | context.subscriptions.push(vscode.commands.registerTextEditorCommand('extension.buildAndUpload', () => arduino.buildAndUpload()));
24 | context.subscriptions.push(vscode.commands.registerTextEditorCommand('extension.rebuildAndUpload', () => arduino.rebuildAndUpload()));
25 | context.subscriptions.push(vscode.commands.registerTextEditorCommand('extension.initialize', () => arduino.initialize()));
26 | }
27 |
28 | // this method is called when your extension is deactivated
29 | export function deactivate() {
30 | }
--------------------------------------------------------------------------------
/test/extension.test.ts:
--------------------------------------------------------------------------------
1 | //
2 | // Note: This example test is leveraging the Mocha test framework.
3 | // Please refer to their documentation on https://mochajs.org/ for help.
4 | //
5 |
6 | // The module 'assert' provides assertion methods from node
7 | import * as assert from 'assert';
8 |
9 | // You can import and use all API from the 'vscode' module
10 | // as well as import your extension to test it
11 | import * as vscode from 'vscode';
12 | import * as myExtension from '../src/extension';
13 |
14 | // Defines a Mocha test suite to group tests of similar kind together
15 | suite("Extension Tests", () => {
16 |
17 | // Defines a Mocha unit test
18 | test("Something 1", () => {
19 | assert.equal(-1, [1, 2, 3].indexOf(5));
20 | assert.equal(-1, [1, 2, 3].indexOf(0));
21 | });
22 | });
--------------------------------------------------------------------------------
/test/index.ts:
--------------------------------------------------------------------------------
1 | //
2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
3 | //
4 | // This file is providing the test runner to use when running extension tests.
5 | // By default the test runner in use is Mocha based.
6 | //
7 | // You can provide your own test runner if you want to override it by exporting
8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension
9 | // host can call to run the tests. The test runner is expected to use console.log
10 | // to report the results back to the caller. When the tests are finished, return
11 | // a possible error to the callback or null if none.
12 |
13 | var testRunner = require('vscode/lib/testrunner');
14 |
15 | // You can directly control Mocha options by uncommenting the following lines
16 | // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
17 | testRunner.configure({
18 | ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
19 | useColors: true // colored output from test results
20 | });
21 |
22 | module.exports = testRunner;
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es6",
5 | "outDir": "out",
6 | "lib": [
7 | "es6"
8 | ],
9 | "sourceMap": true,
10 | "rootDir": "."
11 | },
12 | "exclude": [
13 | "node_modules",
14 | ".vscode-test"
15 | ]
16 | }
--------------------------------------------------------------------------------
/vsc-extension-quickstart.md:
--------------------------------------------------------------------------------
1 | # Welcome to your first VS Code Extension
2 |
3 | ## What's in the folder
4 | * This folder contains all of the files necessary for your extension
5 | * `package.json` - this is the manifest file in which you declare your extension and command.
6 | The sample plugin registers a command and defines its title and command name. With this information
7 | VS Code can show the command in the command palette. It doesn’t yet need to load the plugin.
8 | * `src/extension.ts` - this is the main file where you will provide the implementation of your command.
9 | The file exports one function, `activate`, which is called the very first time your extension is
10 | activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
11 | We pass the function containing the implementation of the command as the second parameter to
12 | `registerCommand`.
13 |
14 | ## Get up and running straight away
15 | * press `F5` to open a new window with your extension loaded
16 | * run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`
17 | * set breakpoints in your code inside `src/extension.ts` to debug your extension
18 | * find output from your extension in the debug console
19 |
20 | ## Make changes
21 | * you can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`
22 | * you can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes
23 |
24 | ## Explore the API
25 | * you can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts`
26 |
27 | ## Run tests
28 | * open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests`
29 | * press `F5` to run the tests in a new window with your extension loaded
30 | * see the output of the test result in the debug console
31 | * make changes to `test/extension.test.ts` or create new test files inside the `test` folder
32 | * by convention, the test runner will only consider files matching the name pattern `**.test.ts`
33 | * you can create folders inside the `test` folder to structure your tests any way you want
--------------------------------------------------------------------------------