From d336de044dd4b0a53c38f54d991c271a172f60c3 Mon Sep 17 00:00:00 2001 From: Yaohai Zheng Date: Wed, 4 Jan 2017 16:49:38 +0800 Subject: [PATCH] Add arduino integration. --- package.json | 9 ++++++- snippets/arduino.json | 16 +++++++++++++ src/arduino/arduino.ts | 52 ++++++++++++++++++++++++++++++++++------- src/arduino/settings.ts | 38 +++++++++++++++--------------- src/common/util.ts | 38 ++++++++++++++++++++++++++++++ src/extension.ts | 25 ++++---------------- src/helper/fsHelper.ts | 11 --------- test/extension.test.ts | 18 +++++++------- test/index.ts | 8 +++---- tslint.json | 10 +++++++- 10 files changed, 152 insertions(+), 73 deletions(-) create mode 100644 snippets/arduino.json create mode 100644 src/common/util.ts delete mode 100644 src/helper/fsHelper.ts diff --git a/package.json b/package.json index 85eb959d..3edc64d7 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,17 @@ ], "activationEvents": [ "onCommand:extension.verifyArduino", - "onCommand:extension.uploadArduino" + "onCommand:extension.uploadArduino", + "workspaceContains:device.json" ], "main": "./out/src/extension", "contributes": { + "snippets": [ + { + "language": "cpp", + "path": "./snippets/arduino.json" + } + ], "commands": [ { "command": "extension.verifyArduino", diff --git a/snippets/arduino.json b/snippets/arduino.json new file mode 100644 index 00000000..4f0ecd2c --- /dev/null +++ b/snippets/arduino.json @@ -0,0 +1,16 @@ +{ + "loop": { + "prefix": "loop", + "body": [ + "void loop()", + "{\n}" + ] + }, + "setup": { + "prefix": "setup", + "body": [ + "void setup()", + "{\n}" + ] + } +} \ No newline at end of file diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index f22262a3..3056dfcd 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -1,14 +1,50 @@ -'use strict'; +"use strict"; -import vscode = require('vscode'); -import settings = require('./settings'); +import os = require("os"); +import fs = require("fs"); +import path = require("path"); +import vscode = require("vscode"); +import settings = require("./settings"); +import * as util from "../common/util"; + +export const outputChannel = vscode.window.createOutputChannel("Arduino"); + +export function upload(arduinoConfig: settings.IArduinoSettings) { + return loadProjectConfig() + .then((projectConfig: any) => { + const boardDescriptor = getBoardDescriptor(projectConfig); + const appPath = path.join(vscode.workspace.rootPath, projectConfig.appPath || "app.ino"); + outputChannel.show(true); + return util.spawn(arduinoConfig.arduinoPath, + outputChannel, + ["--upload", "--board", boardDescriptor, "--port", projectConfig.port, appPath]); + }); +} export function verify(arduinoConfig: settings.IArduinoSettings) { - vscode.window.showInformationMessage(arduinoConfig.arduinoPath); - return 'verify'; + return loadProjectConfig() + .then((projectConfig: any) => { + const boardDescriptor = getBoardDescriptor(projectConfig); + const appPath = path.join(vscode.workspace.rootPath, projectConfig.appPath || "app.ino"); + outputChannel.show(true); + return util.spawn(arduinoConfig.arduinoPath, + outputChannel, + ["--verify", "--board", boardDescriptor, "--port", projectConfig.port, appPath]); + }); } -export function upload(arduinoConfig: settings.IArduinoSettings) { - vscode.window.showInformationMessage(arduinoConfig.arduinoPath); - return 'upload'; +function loadProjectConfig(): Thenable { + return vscode.workspace.findFiles("device.json", null, 1) + .then((files) => { + const configFile = files[0]; + return JSON.parse(fs.readFileSync(configFile.fsPath, "utf8")); + }); +} + +function getBoardDescriptor(projectConfig: any): string { + let boardDescriptor = `${projectConfig.package}:${projectConfig.arch}:${projectConfig.board}`; + if (projectConfig.parameters) { + boardDescriptor = `${boardDescriptor}:${projectConfig.parameters}`; + } + return boardDescriptor; } diff --git a/src/arduino/settings.ts b/src/arduino/settings.ts index cd916d67..ab88d2a1 100644 --- a/src/arduino/settings.ts +++ b/src/arduino/settings.ts @@ -1,8 +1,8 @@ -'use strict'; +"use strict"; -import * as vscode from 'vscode'; -import * as path from 'path'; -import * as fsHelper from '../helper/fsHelper'; +import * as path from "path"; +import * as vscode from "vscode"; +import * as util from "../common/util"; export const IS_WINDOWS = /^win/.test(process.platform); @@ -11,22 +11,23 @@ export interface IArduinoSettings { } export class ArduinoSettings implements IArduinoSettings { + public static getIntance(): ArduinoSettings { + return ArduinoSettings.arduinoSettings; + } + private static arduinoSettings: ArduinoSettings = new ArduinoSettings(); + + private _arduinoPath: string; + constructor() { this.initializeSettings(); } - public static getIntance(): ArduinoSettings { - return ArduinoSettings.arduinoSettings; - } - private initializeSettings() { - let arduinoConfig = vscode.workspace.getConfiguration('arduino'); - this.arduinoPath = arduinoConfig.get('arduinoPath'); + let arduinoConfig = vscode.workspace.getConfiguration("arduino"); + this.arduinoPath = arduinoConfig.get("arduinoPath"); } - private _arduinoPath: string; - public get arduinoPath(): string { return this._arduinoPath; } @@ -37,25 +38,24 @@ export class ArduinoSettings implements IArduinoSettings { } try { this._arduinoPath = getArduinoExecutable(value); - } - catch (ex) { + } catch (ex) { this._arduinoPath = value; } } } function getArduinoExecutable(arduinoPath: string): string { - if (arduinoPath === 'arduino' || fsHelper.fileExists(arduinoPath)) { + if (arduinoPath === "arduino" || util.fileExists(arduinoPath)) { return arduinoPath; } if (IS_WINDOWS) { - if (fsHelper.fileExists(path.join(arduinoPath, 'arduino.exe'))) { - return path.join(arduinoPath, 'arduino.exe'); + if (util.fileExists(path.join(arduinoPath, "arduino.exe"))) { + return path.join(arduinoPath, "arduino.exe"); } } else { - if (fsHelper.fileExists(path.join(arduinoPath, 'arduino'))) { - return path.join(arduinoPath, 'arduino'); + if (util.fileExists(path.join(arduinoPath, "arduino"))) { + return path.join(arduinoPath, "arduino"); } } return arduinoPath; diff --git a/src/common/util.ts b/src/common/util.ts new file mode 100644 index 00000000..5d7f50d3 --- /dev/null +++ b/src/common/util.ts @@ -0,0 +1,38 @@ +"use strict"; + +import fs = require("fs"); +import path = require("path"); +import vscode = require("vscode"); +import childProcess = require("child_process"); + +export function fileExists(filePath: string): boolean { + try { + return fs.statSync(filePath).isFile(); + } catch (e) { + return false; + } +} + +export function spawn(command: string, outputChannel: vscode.OutputChannel, args: T[] = [], options: any = {}): Thenable { + return new Promise((resolve, reject) => { + let stdout = ""; + let stderr = ""; + options.cwd = options.cwd || path.resolve(path.join(__dirname, "..")); + const child = childProcess.spawn(command, args, options); + + if (outputChannel) { + child.stdout.on("data", (data) => { outputChannel.append(data.toString()); }); + child.stderr.on("data", (data) => { outputChannel.append(data.toString()); }); + } + + child.on("error", (error) => reject({ error, stderr, stdout })); + + child.on("exit", (code) => { + if (code === 0) { + resolve({ code, stdout, stderr }); + } else { + reject({ code, stdout, stderr }); + } + }); + }); +} diff --git a/src/extension.ts b/src/extension.ts index 68ce3fcf..ed617340 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,24 +1,9 @@ -'use strict'; +import vscode = require("vscode"); +import { outputChannel, upload, verify } from "./arduino/arduino"; +import * as settings from "./arduino/settings"; -import vscode = require('vscode'); -import * as settings from './arduino/settings'; -import { verify, upload } from './arduino/arduino'; - -// this method is called when your extension is activated -// your extension is activated the very first time the command is executed export function activate(context: vscode.ExtensionContext) { - let arduinoSettings = settings.ArduinoSettings.getIntance(); - - vscode.commands.registerCommand('extension.verifyArduino', () => { - return verify(arduinoSettings); - }); - - vscode.commands.registerCommand('extension.uploadArduino', () => { - return upload(arduinoSettings); - }); + vscode.commands.registerCommand("extension.verifyArduino", () => verify(arduinoSettings)); + vscode.commands.registerCommand("extension.uploadArduino", () => upload(arduinoSettings)); } - -// this method is called when your extension is deactivated -export function deactivate() { -} \ No newline at end of file diff --git a/src/helper/fsHelper.ts b/src/helper/fsHelper.ts deleted file mode 100644 index 3d77c1d7..00000000 --- a/src/helper/fsHelper.ts +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -import fs = require('fs'); - -export function fileExists(filePath: string): boolean { - try { - return fs.statSync(filePath).isFile(); - } catch (e) { - return false; - } -} \ No newline at end of file diff --git a/test/extension.test.ts b/test/extension.test.ts index 92e40ecb..0ed6a80f 100644 --- a/test/extension.test.ts +++ b/test/extension.test.ts @@ -2,21 +2,21 @@ // Note: This example test is leveraging the Mocha test framework. // Please refer to their documentation on https://mochajs.org/ for help. // -import * as assert from 'assert'; -import * as vscode from 'vscode'; +import * as assert from "assert"; +import * as vscode from "vscode"; // Defines a Mocha test suite to group tests of similar kind together suite("Arduino Extension Tests", () => { - test('Verify command', (done) => { - vscode.commands.executeCommand('extension.verifyArduino') - .then(result => assert(result === 'verify', 'Failed to verify Arduino app')) + test("Verify command", (done) => { + vscode.commands.executeCommand("extension.verifyArduino") + .then((result) => assert(result === "verify", "Failed to verify Arduino app")) .then(done, done); }); - test('Upload Arduino command', (done) => { - vscode.commands.executeCommand('extension.uploadArduino') - .then(result => assert(result === 'upload', 'Failed to upload arduino app')) + test("Upload Arduino command", (done) => { + vscode.commands.executeCommand("extension.uploadArduino") + .then((result) => assert(result === "upload", "Failed to upload arduino app")) .then(done, done); }); -}); \ No newline at end of file +}); diff --git a/test/index.ts b/test/index.ts index 50bae456..09370a86 100644 --- a/test/index.ts +++ b/test/index.ts @@ -10,13 +10,13 @@ // to report the results back to the caller. When the tests are finished, return // a possible error to the callback or null if none. -var testRunner = require('vscode/lib/testrunner'); +import testRunner = require("vscode/lib/testrunner"); // You can directly control Mocha options by uncommenting the following lines // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info testRunner.configure({ - ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) - useColors: true // colored output from test results + ui: "tdd", // the TDD UI is being used in extension.test.ts (suite, test, etc.) + useColors: true, // colored output from test results }); -module.exports = testRunner; \ No newline at end of file +module.exports = testRunner; diff --git a/tslint.json b/tslint.json index 1f87c2f2..6f4be8c8 100644 --- a/tslint.json +++ b/tslint.json @@ -1,5 +1,10 @@ { + "extends": "tslint:recommended", "rules": { + "variable-name": [ + true, + "allow-leading-underscore" + ], "no-unused-expression": true, "no-duplicate-variable": true, "curly": true, @@ -7,6 +12,9 @@ "semicolon": [ "always" ], - "triple-equals": true + "triple-equals": true, + "max-line-length": [ + 150 + ] } } \ No newline at end of file