├── .github └── FUNDING.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── README.md ├── README.old.md ├── images ├── animation.gif ├── animation1.gif ├── animation2.gif ├── animation3.gif ├── animation4.gif ├── debug-bar-continue.png ├── debug-bar.png ├── demo.gif ├── editor.png ├── infer-animate.gif ├── install1.png ├── install2.gif ├── install3.png ├── install4.png ├── open-workspace.png ├── pi-spi-pin.jpg ├── pi-spi-pinetime-small.jpg ├── pinetime-antenna.png ├── pinetime-pi-pinout-spi.png ├── pinetime-swd3.jpg ├── rls-fail.png ├── run1.png ├── run2.png ├── run3.png ├── rust-source-files.png ├── stlink.jpg ├── trash-close.png ├── typeless-rust.png ├── visual-rust-icon.png ├── visual-rust-map.png ├── vs-build-tools.png ├── vscode-debug-windows.png ├── vscode-flow.jpg └── vsstudio-build-tools.png ├── media ├── README.md ├── dep.svg └── vscode │ ├── README.md │ ├── message.js │ ├── modal.js │ ├── storage.js │ └── style.css ├── package-lock.json ├── package.json ├── resources ├── README.md ├── dark │ ├── boolean.svg │ ├── dependency.svg │ ├── document.svg │ ├── edit.svg │ ├── folder.svg │ ├── number.svg │ ├── refresh.svg │ └── string.svg ├── light │ ├── boolean.svg │ ├── dependency.svg │ ├── document.svg │ ├── edit.svg │ ├── folder.svg │ ├── number.svg │ ├── refresh.svg │ └── string.svg ├── package-explorer.png ├── replay.log └── template.rs ├── src ├── declarations.ts ├── decorate.ts ├── extension.ts ├── replay.ts ├── test │ ├── runTest.ts │ └── suite │ │ ├── extension.test.ts │ │ └── index.ts └── web.ts ├── tsconfig.json ├── tslint.json ├── vsc-extension-quickstart.md └── workspace.code-workspace /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [lupyuen] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['paypal.me/lupyuen'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "ms-vscode.vscode-typescript-tslint-plugin" 6 | ] 7 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/out/**/*.js" 18 | ], 19 | "preLaunchTask": "npm: watch" 20 | }, 21 | { 22 | "name": "Extension Tests", 23 | "type": "extensionHost", 24 | "request": "launch", 25 | "runtimeExecutable": "${execPath}", 26 | "args": [ 27 | "--extensionDevelopmentPath=${workspaceFolder}", 28 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 29 | ], 30 | "outFiles": [ 31 | "${workspaceFolder}/out/test/**/*.js" 32 | ], 33 | "preLaunchTask": "npm: watch" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /.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 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Package Extension", 8 | "type": "shell", 9 | "command": "vsce package" 10 | }, 11 | { 12 | "type": "npm", 13 | "script": "watch", 14 | "problemMatcher": "$tsc-watch", 15 | "isBackground": true, 16 | "presentation": { 17 | "reveal": "never" 18 | }, 19 | "group": { 20 | "kind": "build", 21 | "isDefault": true 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/tslint.json 9 | **/*.map 10 | **/*.ts -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the "visual-embedded-rust" extension will be documented in this file. 4 | 5 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 6 | 7 | ## [Unreleased] 8 | 9 | - Initial release -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | UPDATE: This code in this article has been archived in the [pre-lvgl branch of pinetime-rust-mynewt](https://github.com/lupyuen/pinetime-rust-mynewt/tree/pre-lvgl). The [pinetime-rust-mynewt firmware](https://github.com/lupyuen/pinetime-rust-mynewt) has been revamped to support [Rust Watch Faces on LVGL](https://lupyuen.github.io/pinetime-rust-mynewt/articles/watchface). [Check out the updates](https://lupyuen.github.io/pinetime-rust-mynewt/articles/watchface) 2 | 3 | # visual-embedded-rust 4 | 5 | - Create and edit Embedded Rust programs visually by dragging and dropping blocks 6 | 7 | - Generates Embedded Rust firmware code for [__PineTime Smart Watch__](https://wiki.pine64.org/index.php/PineTime) hosted on [__Apache Mynewt__](https://mynewt.apache.org/) realtime operating system, with [__druid UI Framework__](https://github.com/lupyuen/druid-embedded) 8 | 9 | - Hardware Required: PineTime Smart Watch and Raspberry Pi (preferably Pi 4 with Raspbian) 10 | 11 | ![Demo](images/demo.gif) 12 | 13 | ![Visual Embedded Rust](images/animation.gif) 14 | 15 | # Connect PineTime to Raspberry Pi 16 | 17 | 1️⃣ Carefully pry open the PineTime casing. Use tweezers to pivot the shiny battery gently to the side. Be careful not to break the red and black wires that connect the battery to the watch! 18 | 19 | 2️⃣ Just above the battery we see 4 shiny rings. This is the __[Serial Wire Debug](https://en.wikipedia.org/wiki/JTAG#Serial_Wire_Debug) (SWD)__ Port for PineTime. We’ll use this port to flash our firmware to PineTime. The 4 pins (from left to right) are SWDIO (Data I/O), SWDCLK (Clock), 3.3V, GND. 20 | 21 | 🛈 [_What is “flash memory” / “flashing” / “firmware”? Read this_](https://gist.github.com/lupyuen/41fffaddade277d27c48697bca21d837) 22 | 23 | ![SWD Port on PineTime](images/pinetime-swd3.jpg) 24 | 25 | In the above photo, the SWD pins from left to right are… 26 | 27 | 1. __SWDIO__ (Yellow) 28 | 29 | 1. __SWDCLK__ (Blue) 30 | 31 | 1. __3.3V__ (Red) 32 | 33 | 1. __GND__ (Black) 34 | 35 | The exposed copper wire at the top centre of the photo is the Bluetooth antenna. Bend it upwards so that it doesn’t come into contact with anything. 36 | 37 | ![Bend PineTime antenna](images/pinetime-antenna.png) 38 | 39 | 3️⃣ At lower right we see a pad marked 5V. We’ll connect this pad to Raspberry Pi to charge the battery. If charging of the battery is not needed during development, we may leave5V disconnected. 40 | 41 | 4️⃣ Connect the SWD Port and the 5V Pad (optional) to the Raspberry Pi with [__Solid-Core Wire (22 AWG)__](https://www.adafruit.com/product/288) and [__Female-To-Female Jumper Cables__](https://www.adafruit.com/product/1951)… 42 | 43 | | PineTime | Raspberry Pi | Wire Colour | 44 | | :--- | :--- | :--- | 45 | | `SWDIO` | `Header Pin 19 (MOSI)` | Yellow | 46 | | `SWDCLK` | `Header Pin 23 (SCLK)` | Blue | 47 | | `3.3V` | `3.3V` | Red | 48 | | `GND` | `GND` | Black | 49 | | `5V` | `5V` | Green (Optional) | 50 | 51 | ![Raspberry Pi Pinout](images/pinetime-pi-pinout-spi.png) 52 | 53 | _Based on https://pinout.xyz/_ 54 | 55 | 5️⃣ We may use Raspberry Pi Zero, 1, 2, 3 or 4. 56 | 57 | ![Raspberry Pi Pinout](images/pi-spi-pin.jpg) 58 | 59 | 6️⃣ The PineTime touchscreen needs to be accessible during development, so I mounted PineTime on a [$2 clear box cover from Daiso](https://www.daisojapan.com/p-30955-clear-box-28-x-47-x-19-x-in-12pks.aspx) with Blu Tack and sticky tape. 60 | 61 | ![Connecting PineTime to Raspberry Pi](images/pi-spi-pinetime-small.jpg) 62 | 63 | # Remove PineTime Flash Protection 64 | 65 | PineTime is shipped with preloaded demo firmware. We need to erase the demo firmware and unprotect PineTime’s flash memory so that we may flash our own firmware. 66 | 67 | 🛈 [_What is “flash protection”? Read this_](https://gist.github.com/lupyuen/3ee440542853e1e637582c4efa1b240a) 68 | 69 | 1️⃣ Power on the Raspberry Pi. Open a command prompt and enter the following… 70 | 71 | ```bash 72 | sudo raspi-config 73 | ``` 74 | 75 | Select `Interfacing Options → SPI → Yes` 76 | 77 | Select `Finish` 78 | 79 | At the command prompt, enter the following… 80 | 81 | ```bash 82 | # Remove folders ~/pinetime-rust-mynewt and ~/openocd-spi (if they exist) 83 | rm -rf ~/pinetime-rust-mynewt 84 | rm -rf ~/openocd-spi 85 | 86 | # Download and extract "pinetime-rust-mynewt" folder containing our prebuilt firmware, source files and flashing scripts 87 | sudo apt install -y wget p7zip-full 88 | cd ~ 89 | wget https://github.com/lupyuen/pinetime-rust-mynewt/releases/download/v3.0.3/pinetime-rust-mynewt.7z 90 | 7z x pinetime-rust-mynewt.7z 91 | rm pinetime-rust-mynewt.7z 92 | 93 | # Install build tools for PineTime: VSCode, Rust, gcc, gdb, openocd-spi, newt 94 | cd ~/pinetime-rust-mynewt 95 | scripts/install-pi.sh 96 | 97 | # Latest nightly-2020-04-20 fails with asm error, so we use nightly-2020-02-16 98 | source $HOME/.cargo/env 99 | rustup default nightly-2020-02-16 100 | rustup update 101 | rustup target add thumbv7em-none-eabihf 102 | ``` 103 | 104 | 2️⃣ At the `Welcome to Rust!` prompt, press Enter to select the default option: 105 | 106 | `1) Proceed with installation (default)` 107 | 108 | If you see this error… 109 | 110 | ``` 111 | Cloning into 'openocd-spi/jimtcl'... 112 | fatal: unable to access 'http://repo.or.cz/r/jimtcl.git/': Recv failure: Connection reset by peer 113 | fatal: clone of 'http://repo.or.cz/r/jimtcl.git' into submodule path '/private/tmp/aa/openocd-spi/jimtcl' failed 114 | ``` 115 | 116 | It means that the sub-repository for one of the dependencies jimtcl is temporarily down. You may download the pre-built `openocd-spi` binaries [from this link](https://github.com/lupyuen/pinetime-rust-mynewt/releases/download/openocd-spi2/openocd-spi.7z). Then copy the executable openocd-spi/src/openocd to pinetime-rust-mynewt/openocd/bin/openocd 117 | 118 | 3️⃣ When the installation has completed, enter the following at the command prompt… 119 | 120 | ```bash 121 | # Remove flash protection from PineTime and erase demo firmware 122 | cd ~/pinetime-rust-mynewt 123 | scripts/nrf52-pi/flash-unprotect.sh 124 | ``` 125 | 126 | 4️⃣ We should see `Shut Down And Power Off Your Raspberry Pi`… 127 | 128 | ![Unprotect Flash ROM](https://lupyuen.github.io/images/flash-unprotect.png) 129 | 130 | If we see `Clock Speed` and nothing else after that… 131 | 132 | ``` 133 | Info : BCM2835 SPI SWD driver 134 | Info : SWD only mode enabled 135 | Info : clock speed 31200 kHz 136 | Info : SWD DPIDR 0x2ba01477 137 | ``` 138 | 139 | Then the connection to the SWD Port is probably loose, please check the pins. 140 | 141 | If we don't see this `DPIDR` number, or if we see a different `DPIDR` number... 142 | 143 | ``` 144 | SWD DPIDR 0x2ba01477 145 | ``` 146 | 147 | Then the connection to the SWD Port is most likely loose, please check the pins. 148 | 149 | Also enter `sudo raspi-config` and confirm that the SPI port has been enabled. 150 | 151 | If we see this instead… 152 | 153 | ``` 154 | openocd/bin/openocd: cannot execute binary file: Exec format error 155 | ``` 156 | 157 | Then `install-pi.sh` probably didn’t run correctly. To fix this, copy the `openocd` executable like this… 158 | 159 | ```bash 160 | cp $HOME/openocd-spi/src/openocd $HOME/pinetime-rust-mynewt/openocd/bin/openocd 161 | ``` 162 | 163 | 5️⃣ Shut down and power off your Raspberry Pi. Wait 30 seconds for the red and green LEDs on your Pi to turn off. Power on your Pi. Enter the same commands at a command prompt… 164 | 165 | ```bash 166 | # Remove flash protection from PineTime and erase demo firmware 167 | cd ~/pinetime-rust-mynewt 168 | scripts/nrf52-pi/flash-unprotect.sh 169 | ``` 170 | 171 | 6️⃣ We should see `Flash Is Already Unprotected`… 172 | 173 | PineTime’s demo firmware has been erased and the flash protection has been removed. 174 | 175 | 🛈 [_What is OpenOCD? Why Raspberry Pi and not ROCK64 or Nvidia Jetson Nano? Read this_](https://gist.github.com/lupyuen/18e66c3e81e11050a10d1192c5b84bb0) 176 | 177 | # Edit The Visual Rust Application 178 | 179 | We shall be using VSCode with the Visual Embedded Rust Extension to edit our Visual Rust application graphically. 180 | 181 | 🛈 [_What is VSCode? Is it related to Visual Studio? How is Microsoft involved? Read this_](https://gist.github.com/lupyuen/08e383845d68d3337747e8eb59d0f624) 182 | 183 | 1️⃣ Launch VSCode by clicking the Raspberry Pi Menu (top left corner) → Programming → Code OSS Headmelted 184 | 185 | In VSCode, click `File → Open Folder` 186 | 187 | Under `Home`, select the folder `pinetime-rust-mynewt` and click OK 188 | 189 | When prompted to open the workspace, click Open Workspace 190 | 191 | When prompted to install Extension Recommendations, click `Install All` 192 | 193 | Ignore the message `Unable To Watch For File Changes`. Close the message when it appears. 194 | 195 | 2️⃣ Install the `Visual Embedded Rust` Extension... 196 | 197 | Click `View → Extensions` 198 | 199 | Search for `Visual Embedded Rust` 200 | 201 | Install the extension 202 | 203 | 3️⃣ Enable the Visual Rust application... 204 | 205 | Browse to `rust/app/Cargo.toml` 206 | 207 | Modify the file such that `visual_app` is uncommented and the other options are commented out... 208 | 209 | ```yaml 210 | default = [ # Select the conditional compiled features 211 | # "display_app", # Disable graphics display app 212 | # "ui_app", # Disable druid UI app 213 | "visual_app", # Enable Visual Rust app 214 | # "use_float", # Disable floating-point for GPS geolocation 215 | ] 216 | ``` 217 | 218 | 4️⃣ Edit the Visual Rust application... 219 | 220 | Browse to [`rust/app/src/visual.rs`](https://github.com/lupyuen/pinetime-rust-mynewt/blob/master/rust/app/src/visual.rs) 221 | 222 | Click `Visual Editor` at top right 223 | 224 | ![Click Visual Editor](images/install3.png) 225 | 226 | Use the Visual Editor to edit the Visual Rust application 227 | 228 | ![Editing the Visual Rust application](images/animation.gif) 229 | 230 | 5️⃣ After editing, save the [`visual.rs`](https://github.com/lupyuen/pinetime-rust-mynewt/blob/master/rust/app/src/visual.rs) source file to save the visual program. Don't edit the Rust source file manually, always use the Visual Editor. 231 | 232 | [_Rust Source Code generated from Visual Rust application_](https://github.com/lupyuen/pinetime-rust-mynewt/blob/master/rust/app/src/visual.rs) 233 | 234 | The Visual Rust application shows a button that increments a counter... 235 | 236 | ![Demo](images/demo.gif) 237 | 238 | Let's look at the blocks in the Visual Rust application... 239 | 240 | # On Start 241 | 242 | ![On Start Block](images/animation1.gif) 243 | 244 | Upon starting the Watch App, we initialise the `count` variable to 0. 245 | 246 | This block generates the following Rust code... 247 | 248 | ```rust 249 | /// Application State 250 | #[infer_type] // Infer the missing types 251 | #[derive(Clone, Data, Default)] 252 | struct State { 253 | count: _, 254 | } 255 | 256 | /// Will be run upon startup to launch the app 257 | #[infer_type] // Infer the missing types 258 | pub fn on_start() -> MynewtResult<()> { 259 | console::print("on_start\n"); 260 | // Build a new window 261 | let main_window = WindowDesc::new(ui_builder); 262 | // Create application state 263 | let mut state = State::default(); 264 | state.count = 0; 265 | 266 | // Launch the window with the application state 267 | AppLauncher::with_window(main_window) 268 | .use_simple_logger() 269 | .launch(state) 270 | .expect("launch failed"); 271 | // Return success to `main()` function 272 | Ok(()) 273 | } 274 | ``` 275 | 276 | # Create App 277 | 278 | ![Create App Block](images/animation2.gif) 279 | 280 | We create a Watch App with two Widgets... 281 | 282 | 1. A Label named `my_label` surrounded by padding of 5 pixels 283 | 284 | 1. A Button named `my_button` with the title `Press Me`, surrounded by padding of 5 pixels 285 | 286 | This block generates the following Rust code... 287 | 288 | ```rust 289 | /// Build the UI for the window 290 | #[infer_type] // Infer the missing types 291 | fn ui_builder() -> impl Widget { // `State` is the Application State 292 | console::print("Rust UI builder\n"); console::flush(); 293 | // Create a line of text 294 | // Call `on_my_label_show` to get label text 295 | let my_label_text = LocalizedString::new("hello-counter") 296 | .with_arg("count", on_my_label_show); 297 | // Create a label widget `my_label` 298 | let my_label = Label::new(my_label_text); 299 | // Create a button widget `my_button` 300 | // Call `on_my_button_press` when pressed 301 | let my_button = Button::new("Press Me", on_my_button_press); 302 | 303 | // Create a column 304 | let mut col = Column::new(); 305 | // Add the label widget to the column, centered with padding 306 | col.add_child( 307 | Align::centered( 308 | Padding::new(5.0, 309 | my_label 310 | ) 311 | ), 312 | 1.0 313 | ); 314 | // Add the button widget to the column, with padding 315 | col.add_child( 316 | Padding::new(5.0, 317 | my_button 318 | ), 319 | 1.0 320 | ); 321 | // Return the column containing the widgets 322 | col 323 | } // ; 324 | ``` 325 | 326 | # On Label Show 327 | 328 | ![On Label Show Block](images/animation3.gif) 329 | 330 | This block is called to generate the text that will be shown on the label `my_label`. 331 | 332 | We return the variable `count` for display on the label. 333 | 334 | This block generates the following Rust code... 335 | 336 | ```rust 337 | /// Callback function that will be called to create the formatted text for the label `my_label` 338 | #[infer_type] // Infer the missing types 339 | fn on_my_label_show(state: _, env: _) -> ArgValue { 340 | console::print("on_my_label_show\n"); 341 | state.count.into() 342 | } 343 | ``` 344 | 345 | # On Button Press 346 | 347 | ![On Button Press Block](images/animation4.gif) 348 | 349 | This block is called to when the button `my_button` is pressed. 350 | 351 | We increment the variable `count` by 1. 352 | 353 | This block generates the following Rust code... 354 | 355 | ```rust 356 | /// Callback function that will be called when the button `my_button` is pressed 357 | #[infer_type] // Infer the missing types 358 | fn on_my_button_press(ctx: _, state: _, env: _) { 359 | console::print("on_my_button_press\n"); 360 | state.count = state.count + 1; 361 | } 362 | ``` 363 | 364 | # Build And Flash The Firmware 365 | 366 | We’ll be flashing the PineTime firmware that’s based on open-source [__Apache Mynewt embedded operating system__](https://mynewt.apache.org/). Mynewt OS contains two components that we shall flash to PineTime… 367 | 368 | __Mynewt Bootloader__: This is the C code that’s run whenever we power on PineTime. The Bootloader executes the Mynewt Application upon startup. 369 | 370 | __Mynewt Application__: Contains a Rust application that controls the PineTime functions, and low-level system functions written in C. 371 | 372 | The Bootloader and Application firmware image files may be found at these locations… 373 | 374 | | Mynewt Component | Flash Memory Address | Location of Firmware Image | 375 | | :--- | :--- | :--- | 376 | | Bootloader | `0x0` | `~/pinetime-rust-mynewt/bin/targets/nrf52_boot/app/apps/boot_stub/boot_stub.elf.bin` | 377 | | Application | `0x8000` | `~/pinetime-rust-mynewt/bin/targets/nrf52_my_sensor/app/apps/my_sensor_app/my_sensor_app.img` | 378 | 379 | _From https://github.com/lupyuen/pinetime-rust-mynewt/blob/master/hw/bsp/nrf52/bsp.yml_ 380 | 381 | 🛈 [_What is a Bootloader? Read this_](https://gist.github.com/lupyuen/93ba71e0ae5e746e7a68e4513e0a54d8) 382 | 383 | 1️⃣ At the lower left corner, there is a panel `Task Runner`. Click the panel to display the build and flash tasks. 384 | 385 | 2️⃣ In the Task Runner, click `[1] Build Bootloader` 386 | 387 | When the Terminal Panel appears, right-click the `Terminal` tab, select `Move Panel Right` 388 | 389 | After the building the Bootloader, we should see `Done` 390 | 391 | Ignore the message `There Are Task Errors` 392 | 393 | The Bootloader only needs to be built once. 394 | 395 | 3️⃣ In the Task Runner, click `[2] Build Application` 396 | 397 | After the building the Application, we should see `Done` 398 | 399 | If you see the message `Undefined Reference To Main`, click `[2] Build Application` again and it should succeed. 400 | 401 | The Application needs to be rebuilt whenever a source file has been changed. 402 | 403 | 4️⃣ In the Task Runner, click `[3] Image Application` 404 | 405 | After the creating the Firmware Image, we should see `Done` 406 | 407 | 5️⃣ In the Task Runner, click `[4] Flash Bootloader` 408 | 409 | After flashing the Bootloader Firmware to PineTime, we should see `Done` 410 | 411 | 412 | ``` 413 | Flashing Bootloader... 414 | target halted due to debug-request, current mode: Thread 415 | xPSR: 0x01000000 pc: 0x000000d8 msp: 0x20010000 416 | Enabled ARM Semihosting to show debug output 417 | ** Programming Started ** 418 | Info : nRF52832-QFAA(build code: E1) 512kB Flash, 64kB RAM 419 | Warn : Adding extra erase range, 0x00000b78 .. 0x00000fff 420 | ** Programming Finished ** 421 | ** Verify Started ** 422 | ** Verified OK ** 423 | 424 | Restarting... 425 | target halted due to debug-request, current mode: Thread 426 | xPSR: 0x01000000 pc: 0x000000d8 msp: 0x20010000, semihosting 427 | 428 | **** Done! 429 | ``` 430 | 431 | _From https://github.com/lupyuen/pinetime-rust-mynewt/blob/master/logs/load-bootloader-pi.log_ 432 | 433 | The Bootloader only needs to be flashed once. 434 | 435 | 6️⃣ In the Task Runner, click `[5] Flash Application` 436 | 437 | After the flashing the Application Firmware to PineTime, we should see `Done! Press Ctrl-C To Exit`… 438 | 439 | ``` 440 | Flashing Application... 441 | target halted due to debug-request, current mode: Thread 442 | xPSR: 0x01000000 pc: 0x000000d8 msp: 0x20010000 443 | Enabled ARM Semihosting to show debug output 444 | ** Programming Started ** 445 | Info : nRF52832-QFAA(build code: E1) 512kB Flash, 64kB RAM 446 | Warn : Adding extra erase range, 0x0003e820 .. 0x0003efff 447 | ** Programming Finished ** 448 | ** Verify Started ** 449 | ** Verified OK ** 450 | 451 | Restarting... 452 | target halted due to debug-request, current mode: Thread 453 | xPSR: 0x01000000 pc: 0x000000d8 msp: 0x20010000, semihosting 454 | Enabled ARM Semihosting to show debug output 455 | 456 | **** Done! Press Ctrl-C to exit... 457 | ``` 458 | 459 | _From https://github.com/lupyuen/pinetime-rust-mynewt/blob/master/logs/load-application-pi.log_ 460 | 461 | 7️⃣ Our Visual Rust application starts running on PineTime 462 | 463 | ![Demo](images/demo.gif) 464 | 465 | 8️⃣ Click the Trash icon 🗑 near the top right to terminate the application. If we click the Close icon ❌ instead of the Trash icon, the next flash or debug command will fail. 466 | 467 | ![Click the Trash icon, not the Close icon](images/trash-close.png) 468 | 469 | # PineTime Updater 470 | 471 | Alternatively, flash the following two files to PineTime with [__PineTime Updater__](https://github.com/lupyuen/pinetime-updater)... 472 | 473 | 1. __MCUBoot Bootloader__ 474 | 475 | File: `bin/targets/nrf52_boot/app/boot/mynewt/mynewt.elf` 476 | 477 | Address: `0x0` 478 | 479 | 1. __Rust+Mynewt Firmware__ 480 | 481 | File: `bin/targets/nrf52_my_sensor/app/apps/my_sensor_app/my_sensor_app.elf` 482 | 483 | Address: `0x8000` 484 | 485 | # Debug The Firmware 486 | 487 | 1️⃣ Build the application: In the Task Runner, click `[2] Build Application` 488 | 489 | 2️⃣ Click `Debug → Start Debugging` or press `F5` 490 | 491 | This starts the VSCode Debugger and automatically flashes our updated firmware to PineTime. 492 | 493 | 3️⃣ Click `View → Output` 494 | 495 | In the Output Panel, select `Adapter Output` 496 | 497 | The debugging messages will be displayed here. 498 | 499 | 4️⃣ The program has paused at first line of code in our firmware, the Reset Handler. 500 | 501 | In the Debug Toolbar, click `Continue` or press `F5` 502 | 503 | ![Continue](images/debug-bar-continue.png) 504 | 505 | 🛈 [_What’s a Reset Handler? Read this_](https://gist.github.com/lupyuen/b0b7782f21330e292ea65b9c875bd9a7) 506 | 507 | 5️⃣ The debugger now pauses at the first line of the main function that’s defined in rust/app/src/lib.rs 508 | 509 | This is the first line of Rust code in our Rust Application, which will call test_display in a while. 510 | 511 | In the Debug Toolbar, click Continue or press F5 512 | 513 | 🛈 [_What’s a main function? Read this_](https://gist.github.com/lupyuen/5360769a2d92ec50d988cce92622abff) 514 | 515 | # Edit, Build and Debug the Visual Rust Application on Windows 516 | 517 | ![Debugging PineTime Firmware with VSCode on Windows](images/vscode-debug-windows.png) 518 | 519 | To edit, build and debug the Visual Rust Application on Windows, follow these steps... 520 | 521 | ## _[Windows]_ Connect PineTime to ST-Link 522 | 523 | If we’re doing serious development with PineTime, I recommend getting an [ST-Link v2 USB dongle](https://www.aliexpress.com/wholesale?catId=0&initiative_id=SB_20180924134644&SearchText=st-link+v2&switch_new_app=y) ($2) that connects PineTime directly to our Windows, macOS or Linux computer. 524 | 525 | ST-Link allows us to flash PineTime directly from our computer, and it even supports firmware debugging (setting breakpoints, checking values of variables at runtime, …) 526 | 527 | Here’s how we connect PineTime to ST-Link… 528 | 529 | ![PineTime connected to ST-Link](images/stlink.jpg) 530 | 531 | | PineTime | ST-Link | Wire Colour | 532 | | :--- | :--- | :--- | 533 | | `SWDIO` | `SWDIO` | Yellow | 534 | | `SWDCLK` | `SWDCLK` | Blue | 535 | | `3.3V` | `3.3V` | Red | 536 | | `GND` | `GND` | Black | 537 | | `5V` | `5V` | Green (Optional) | 538 | 539 | Before connecting ST-Link to our Windows computer, the ST-Link USB driver should be installed... 540 | 541 | Download the ST-Link USB driver from ST-Link Driver Website (email registration required)… 542 | 543 | https://www.st.com/en/development-tools/stsw-link009.html 544 | 545 | Click `Get Software` 546 | 547 | Unzip the downloaded file. Double-click the driver installer: 548 | `dpinst_amd64.exe` 549 | 550 | ## _[Windows]_ Remove PineTime Flash Protection 551 | 552 | This must be done with a Raspberry Pi, not on Windows, because ST-Link is a High-Level Adapter that doesn't implement all flash commands. Follow the instructions above for Raspberry Pi. 553 | 554 | ## _[Windows]_ Install PineTime Build Tools 555 | 556 | 1️⃣ Download the pinetime-rust-mynewt.7z file attached below… 557 | 558 | https://github.com/lupyuen/pinetime-rust-mynewt/releases/download/v3.0.1/pinetime-rust-mynewt.7z 559 | 560 | Expand the `.7z` file with 7zip… 561 | 562 | https://www.7-zip.org/download.html 563 | 564 | 2️⃣ Click here to install Build Tools For Visual Studio 2019: 565 | 566 | https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019 567 | 568 | ![Build Tools For Visual Studio 2019](images/vsstudio-build-tools.png) 569 | 570 | Click the `Individual Components` tab 571 | 572 | Select the following components: 573 | 574 | 1. `Windows 10 SDK (10.0.18362.0)` 575 | 576 | 1. `C++ CMake Tools for Windows` 577 | 578 | 1. (This should be automatically selected) `MSVC v142 — VS 2019 C++ x64/x86 Build Tools` 579 | 580 | 3️⃣ Install `rustup` according to the instructions here: 581 | `rustup.rs` 582 | 583 | Click the link provided to download `rustup‑init.exe` 584 | 585 | Launch the downloaded file `rustup‑init.exe` 586 | 587 | If you see the message `Windows Defender SmartScreen prevented an unrecognised app from starting`… 588 | 589 | Click `More Info` 590 | 591 | Click `Run Anyway` 592 | 593 | At the `Welcome to Rust!` prompt, press Enter to select the default option: 594 | 595 | `1) Proceed with installation (default)` 596 | 597 | 4️⃣ Open the Command Prompt and enter… 598 | 599 | ```cmd 600 | :: Install Rust build tools for Arm Cortex 601 | rustup default nightly 602 | rustup update 603 | rustup target add thumbv7em-none-eabihf 604 | ``` 605 | 606 | 5️⃣ Install [GNU Arm Embedded Toolchain](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads) for Windows from Arm Developer Website… 607 | 608 | https://developer.arm.com/-/media/Files/downloads/gnu-rm/8-2019q3/RC1.1/gcc-arm-none-eabi-8-2019-q3-update-win32-sha1.exe?revision=fcadabed-d946-49dc-8f78-0732d2f43773?product=GNU%20Arm%20Embedded%20Toolchain,32-bit,,Windows,8-2019-q3-update 609 | 610 | Select this option at the last install step: 611 | `"Add path to environment variable"` 612 | 613 | 6️⃣ Install VSCode… 614 | 615 | https://code.visualstudio.com/ 616 | 617 | ## _[Windows]_ Edit The Visual Rust Application 618 | 619 | 1️⃣ Launch VSCode 620 | 621 | Click `File → Open Folder` 622 | 623 | Select the downloaded folder `pinetime-rust-mynewt` and click OK 624 | 625 | When prompted to open the workspace, click Open Workspace 626 | 627 | When prompted to install Extension Recommendations, click `Install All` 628 | 629 | 2️⃣ Install the `Visual Embedded Rust` Extension... 630 | 631 | Click `View → Extensions` 632 | 633 | Search for `Visual Embedded Rust` 634 | 635 | Install the extension 636 | 637 | 3️⃣ Enable the Visual Rust application... 638 | 639 | Browse to `rust/app/Cargo.toml` 640 | 641 | Modify the file such that `visual_app` is uncommented and the other options are commented out... 642 | 643 | ```yaml 644 | default = [ # Select the conditional compiled features 645 | # "display_app", # Disable graphics display app 646 | # "ui_app", # Disable druid UI app 647 | "visual_app", # Enable Visual Rust app 648 | # "use_float", # Disable floating-point for GPS geolocation 649 | ] 650 | ``` 651 | 652 | 4️⃣ Edit the Visual Rust application... 653 | 654 | Browse to [`rust/app/src/visual.rs`](https://github.com/lupyuen/pinetime-rust-mynewt/blob/master/rust/app/src/visual.rs) 655 | 656 | Click `Visual Editor` at top right 657 | 658 | ![Click Visual Editor](images/install3.png) 659 | 660 | Use the Visual Editor to edit the Visual Rust application 661 | 662 | ![Editing the Visual Rust application](images/animation.gif) 663 | 664 | 5️⃣ After editing, save the [`visual.rs`](https://github.com/lupyuen/pinetime-rust-mynewt/blob/master/rust/app/src/visual.rs) source file to save the visual program. Don't edit the Rust source file manually, always use the Visual Editor. 665 | 666 | ## _[Windows]_ Build And Flash The Firmware 667 | 668 | 1️⃣ At the lower left corner, there is a panel `Task Runner`. Click the panel to display the build and flash tasks. 669 | 670 | 2️⃣ In the Task Runner, click `[1] Build Bootloader` 671 | 672 | When the Terminal Panel appears, right-click the `Terminal` tab, select `Move Panel Right` 673 | 674 | After the building the Bootloader, we should see `Done` 675 | 676 | Ignore the message `There Are Task Errors` 677 | 678 | The Bootloader only needs to be built once. 679 | 680 | 3️⃣ In the Task Runner, click `[2] Build Application` 681 | 682 | After the building the Application, we should see `Done` 683 | 684 | If you see the message `Undefined Reference To Main`, click `[2] Build Application` again and it should succeed. 685 | 686 | The Application needs to be rebuilt whenever a source file has been changed. 687 | 688 | __Note:__ When we run `Build Application`, the build script will overwrite the default `.vscode/launch.json` (meant for Raspberry Pi) with the correct version `.vscode/launch-nrf52.json` (meant for ST-Link on Windows and macOS) 689 | 690 | 4️⃣ In the Task Runner, click `[3] Image Application` 691 | 692 | After the creating the Firmware Image, we should see `Done` 693 | 694 | 5️⃣ In the Task Runner, click `[4] Flash Bootloader` 695 | 696 | After flashing the Bootloader Firmware to PineTime, we should see `Done` 697 | 698 | 699 | ``` 700 | Flashing Bootloader... 701 | target halted due to debug-request, current mode: Thread 702 | xPSR: 0x01000000 pc: 0x000000d8 msp: 0x20010000 703 | Enabled ARM Semihosting to show debug output 704 | ** Programming Started ** 705 | Info : nRF52832-QFAA(build code: E1) 512kB Flash, 64kB RAM 706 | Warn : Adding extra erase range, 0x00000b78 .. 0x00000fff 707 | ** Programming Finished ** 708 | ** Verify Started ** 709 | ** Verified OK ** 710 | 711 | Restarting... 712 | target halted due to debug-request, current mode: Thread 713 | xPSR: 0x01000000 pc: 0x000000d8 msp: 0x20010000, semihosting 714 | 715 | **** Done! 716 | ``` 717 | 718 | _From https://github.com/lupyuen/pinetime-rust-mynewt/blob/master/logs/load-bootloader-pi.log_ 719 | 720 | The Bootloader only needs to be flashed once. 721 | 722 | 6️⃣ In the Task Runner, click `[5] Flash Application` 723 | 724 | After the flashing the Application Firmware to PineTime, we should see `Done! Press Ctrl-C To Exit`… 725 | 726 | ``` 727 | Flashing Application... 728 | target halted due to debug-request, current mode: Thread 729 | xPSR: 0x01000000 pc: 0x000000d8 msp: 0x20010000 730 | Enabled ARM Semihosting to show debug output 731 | ** Programming Started ** 732 | Info : nRF52832-QFAA(build code: E1) 512kB Flash, 64kB RAM 733 | Warn : Adding extra erase range, 0x0003e820 .. 0x0003efff 734 | ** Programming Finished ** 735 | ** Verify Started ** 736 | ** Verified OK ** 737 | 738 | Restarting... 739 | target halted due to debug-request, current mode: Thread 740 | xPSR: 0x01000000 pc: 0x000000d8 msp: 0x20010000, semihosting 741 | Enabled ARM Semihosting to show debug output 742 | 743 | **** Done! Press Ctrl-C to exit... 744 | ``` 745 | 746 | _From https://github.com/lupyuen/pinetime-rust-mynewt/blob/master/logs/load-application-pi.log_ 747 | 748 | 7️⃣ Our Visual Rust application starts running on PineTime 749 | 750 | ![Demo](images/demo.gif) 751 | 752 | 8️⃣ Click the Trash icon 🗑 near the top right to terminate the application. If we click the Close icon ❌ instead of the Trash icon, the next flash or debug command will fail. 753 | 754 | ![Click the Trash icon, not the Close icon](images/trash-close.png) 755 | 756 | ## _[Windows]_ Debug The Firmware 757 | 758 | 1️⃣ Build the application: In the Task Runner, click `[2] Build Application` 759 | 760 | The build script will also overwrite the default `.vscode/launch.json` (meant for Raspberry Pi) with the correct version `.vscode/launch-nrf52.json` (meant for ST-Link on Windows and macOS) 761 | 762 | 2️⃣ Click `Debug → Start Debugging` or press `F5` 763 | 764 | This starts the VSCode Debugger and automatically flashes our updated firmware to PineTime. 765 | 766 | 3️⃣ Click `View → Output` 767 | 768 | In the Output Panel, select `Adapter Output` 769 | 770 | The debugging messages will be displayed here. 771 | 772 | 4️⃣ The program has paused at first line of code in our firmware, the Reset Handler. 773 | 774 | In the Debug Toolbar, click `Continue` or press `F5` 775 | 776 | ![Continue](images/debug-bar-continue.png) 777 | 778 | 🛈 [_What’s a Reset Handler? Read this_](https://gist.github.com/lupyuen/b0b7782f21330e292ea65b9c875bd9a7) 779 | 780 | 5️⃣ The debugger now pauses at the first line of the main function that’s defined in rust/app/src/lib.rs 781 | 782 | This is the first line of Rust code in our Rust Application, which will call test_display in a while. 783 | 784 | In the Debug Toolbar, click Continue or press F5 785 | 786 | 🛈 [_What’s a main function? Read this_](https://gist.github.com/lupyuen/5360769a2d92ec50d988cce92622abff) 787 | 788 | # TODO: Edit, Build and Debug the Visual Rust Application on macOS 789 | -------------------------------------------------------------------------------- /images/animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/animation.gif -------------------------------------------------------------------------------- /images/animation1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/animation1.gif -------------------------------------------------------------------------------- /images/animation2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/animation2.gif -------------------------------------------------------------------------------- /images/animation3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/animation3.gif -------------------------------------------------------------------------------- /images/animation4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/animation4.gif -------------------------------------------------------------------------------- /images/debug-bar-continue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/debug-bar-continue.png -------------------------------------------------------------------------------- /images/debug-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/debug-bar.png -------------------------------------------------------------------------------- /images/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/demo.gif -------------------------------------------------------------------------------- /images/editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/editor.png -------------------------------------------------------------------------------- /images/infer-animate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/infer-animate.gif -------------------------------------------------------------------------------- /images/install1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/install1.png -------------------------------------------------------------------------------- /images/install2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/install2.gif -------------------------------------------------------------------------------- /images/install3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/install3.png -------------------------------------------------------------------------------- /images/install4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/install4.png -------------------------------------------------------------------------------- /images/open-workspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/open-workspace.png -------------------------------------------------------------------------------- /images/pi-spi-pin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/pi-spi-pin.jpg -------------------------------------------------------------------------------- /images/pi-spi-pinetime-small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/pi-spi-pinetime-small.jpg -------------------------------------------------------------------------------- /images/pinetime-antenna.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/pinetime-antenna.png -------------------------------------------------------------------------------- /images/pinetime-pi-pinout-spi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/pinetime-pi-pinout-spi.png -------------------------------------------------------------------------------- /images/pinetime-swd3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/pinetime-swd3.jpg -------------------------------------------------------------------------------- /images/rls-fail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/rls-fail.png -------------------------------------------------------------------------------- /images/run1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/run1.png -------------------------------------------------------------------------------- /images/run2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/run2.png -------------------------------------------------------------------------------- /images/run3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/run3.png -------------------------------------------------------------------------------- /images/rust-source-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/rust-source-files.png -------------------------------------------------------------------------------- /images/stlink.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/stlink.jpg -------------------------------------------------------------------------------- /images/trash-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/trash-close.png -------------------------------------------------------------------------------- /images/typeless-rust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/typeless-rust.png -------------------------------------------------------------------------------- /images/visual-rust-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/visual-rust-icon.png -------------------------------------------------------------------------------- /images/visual-rust-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/visual-rust-map.png -------------------------------------------------------------------------------- /images/vs-build-tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/vs-build-tools.png -------------------------------------------------------------------------------- /images/vscode-debug-windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/vscode-debug-windows.png -------------------------------------------------------------------------------- /images/vscode-flow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/vscode-flow.jpg -------------------------------------------------------------------------------- /images/vsstudio-build-tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/images/vsstudio-build-tools.png -------------------------------------------------------------------------------- /media/README.md: -------------------------------------------------------------------------------- 1 | # media 2 | 3 | This folder contains JavaScript and CSS assets accessible by the WebView. 4 | 5 | Two repositories need to be cloned into this folder: [blockly-mynewt-rust](https://github.com/lupyuen/blockly-mynewt-rust) and [closure-library](https://github.com/google/closure-library): 6 | 7 | ```bash 8 | cd media 9 | git clone https://github.com/lupyuen/blockly-mynewt-rust 10 | git clone https://github.com/google/closure-library 11 | ``` 12 | -------------------------------------------------------------------------------- /media/dep.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Slice 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /media/vscode/README.md: -------------------------------------------------------------------------------- 1 | # vscode 2 | 3 | This folder contains functions and stylesheets to override Blockly specifically for VSCode -------------------------------------------------------------------------------- /media/vscode/message.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Visual Blocks Editor 4 | * 5 | * Copyright 2012 Google Inc. 6 | * https://developers.google.com/blockly/ 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | /** 22 | * @fileoverview Handle command messages from VSCode 23 | * @author luppy@appkaki.com (Lee Lup Yuen) 24 | */ 25 | 'use strict'; 26 | 27 | // Markers for wrapping the XML blocks 28 | var blocks_begin = '-- BEGIN BLOCKS --'; 29 | var blocks_end = '-- END BLOCKS --'; 30 | 31 | // Callbacks for confirm and promp modals. 32 | var confirmResult, promptResult; 33 | 34 | // Handle the message received from VSCode 35 | window.addEventListener('message', event => { 36 | const message = event.data; // The JSON data our extension sent 37 | console.log(['recv msg', JSON.stringify(message).substr(0, 20)]); 38 | switch (message.command) { 39 | // Load the blocks into the workspace. 40 | case 'loadDoc': { 41 | // Text contains `... /* -- BEGIN BLOCKS -- ... -- END BLOCKS -- */`. Extract the blocks. 42 | const text = message.text; 43 | console.log(['loadDoc', text.substr(0, 20)]); 44 | 45 | const beginSplit = text.split(blocks_begin, 2); 46 | if (beginSplit.length < 2) { console.log(blocks_begin + ' not found'); return; } 47 | const endSplit = beginSplit[1].split(blocks_end, 2); 48 | if (endSplit.length < 2) { console.log(blocks_end + ' not found'); return; } 49 | const blocks = endSplit[0]; 50 | 51 | // Set the blocks in the workspace. 52 | var workspace = Blockly.getMainWorkspace(); if (!workspace) { console.log('Missing workspace'); return; } 53 | var xml = Blockly.Xml.textToDom(blocks); 54 | Blockly.Xml.domToWorkspace(xml, workspace); 55 | 56 | // Monitor changes and sync updates to the VSCode document. 57 | BlocklyStorage.monitorChanges_(workspace); 58 | return; 59 | } 60 | 61 | // Receive confirm result from VSCode. We trigger the confirm callback. 62 | case 'confirmResult': { 63 | const result = message.result; 64 | if (confirmResult) { confirmResult(result); } 65 | return; 66 | } 67 | 68 | // Receive prompt result from VSCode. We trigger the prompt callback. 69 | case 'promptResult': { 70 | const result = message.result; 71 | if (promptResult) { promptResult(result); } 72 | return; 73 | } 74 | 75 | default: console.error('Unknown message: ' + JSON.stringify(message)); 76 | } 77 | }); 78 | 79 | function composeDoc(xml, code) { 80 | // Given the XML blocks and the generated Rust code, compose the document to be updated in VSCode. 81 | const doc = [ 82 | code + '\n', 83 | '// ', 84 | blocks_begin, 85 | xml, 86 | blocks_end, 87 | ].join(''); 88 | return doc; 89 | } -------------------------------------------------------------------------------- /media/vscode/modal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview VSCode modals 3 | * @author luppy@appkaki.com (Lee Lup Yuen) 4 | */ 5 | 'use strict'; 6 | 7 | // Override the Blockly modal functions for alert(), confirm() and prompt() because they are not supported in VSCode 8 | function initModal(vscode) { // Pass in vscode so that we don't expose vscode to global space and cause security problems 9 | console.log('vscode/modal loaded'); 10 | 11 | /** 12 | * Override this because VSCode doesn't support alert(). 13 | * Wrapper to window.alert() that app developers may override to 14 | * provide alternatives to the modal browser window. 15 | * @param {string} message The message to display to the user. 16 | * @param {function()=} opt_callback The callback when the alert is dismissed. 17 | */ 18 | Blockly.alert = function(message, opt_callback) { 19 | // Previously: window.alert(message); 20 | console.error(message); 21 | if (opt_callback) { 22 | opt_callback(); 23 | } 24 | }; 25 | 26 | /** 27 | * Override this because VSCode doesn't support confirm(). 28 | * Wrapper to window.confirm() that app developers may override to 29 | * provide alternatives to the modal browser window. 30 | * @param {string} message The message to display to the user. 31 | * @param {!function(boolean)} callback The callback for handling user response. 32 | */ 33 | Blockly.confirm = function(message, callback) { 34 | // Pass to callback a boolean indicating whether OK (true) or Cancel (false) was selected 35 | // Previously: callback(window.confirm(message)); 36 | console.log(['confirm', message]); 37 | // Set the callback. 38 | confirmResult = result => { 39 | confirmResult = null; 40 | callback(result); 41 | }; 42 | // Call VSCode to prompt. 43 | vscode.postMessage({ 44 | command: 'confirm', 45 | message: message 46 | }); 47 | }; 48 | 49 | /** 50 | * Override this because VSCode doesn't support prompt(). 51 | * Wrapper to window.prompt() that app developers may override to provide 52 | * alternatives to the modal browser window. Built-in browser prompts are 53 | * often used for better text input experience on mobile device. We strongly 54 | * recommend testing mobile when overriding this. 55 | * @param {string} message The message to display to the user. 56 | * @param {string} defaultValue The value to initialize the prompt with. 57 | * @param {!function(string)} callback The callback for handling user response. 58 | */ 59 | Blockly.prompt = function(message, defaultValue, callback) { 60 | // Previously: callback(window.prompt(message, defaultValue)); 61 | console.log(['prompt', message, defaultValue]); 62 | // Set the callback. 63 | promptResult = result => { 64 | promptResult = null; 65 | callback(result); 66 | }; 67 | // Call VSCode to prompt. 68 | vscode.postMessage({ 69 | command: 'prompt', 70 | message: message, 71 | defaultValue: defaultValue, 72 | }); 73 | }; 74 | } -------------------------------------------------------------------------------- /media/vscode/storage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Visual Blocks Editor 4 | * 5 | * Copyright 2012 Google Inc. 6 | * https://developers.google.com/blockly/ 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | /** 22 | * @fileoverview Loading and saving blocks to VSCode 23 | * @author luppy@appkaki.com (Lee Lup Yuen) 24 | */ 25 | 'use strict'; 26 | 27 | // Create a namespace. 28 | var BlocklyStorage = {}; 29 | 30 | function initStorage(vscode) { // Pass in vscode so that we don't expose vscode to global space and cause security problems 31 | console.log('vscode/storage loaded'); 32 | 33 | /** 34 | * Backup code blocks to localStorage. 35 | * @param {!Blockly.WorkspaceSvg} workspace Workspace. 36 | * @private 37 | */ 38 | BlocklyStorage.backupBlocks_ = function(workspace) { 39 | console.log('backupBlocks_'); 40 | if ('localStorage' in window) { 41 | var xml = Blockly.Xml.workspaceToDom(workspace); 42 | // Gets the current URL, not including the hash. 43 | var url = window.location.href.split('#')[0]; 44 | // window.localStorage.setItem(url, Blockly.Xml.domToText(xml)); 45 | } 46 | }; 47 | 48 | /** 49 | * Bind the localStorage backup function to the unload event. 50 | * @param {Blockly.WorkspaceSvg=} opt_workspace Workspace. 51 | */ 52 | BlocklyStorage.backupOnUnload = function(opt_workspace) { 53 | console.log('backupOnUnload'); 54 | var workspace = opt_workspace || Blockly.getMainWorkspace(); 55 | window.addEventListener('unload', 56 | function() {BlocklyStorage.backupBlocks_(workspace);}, false); 57 | }; 58 | 59 | /** 60 | * Restore code blocks from VSCode. 61 | * @param {Blockly.WorkspaceSvg=} opt_workspace Workspace. 62 | */ 63 | BlocklyStorage.restoreBlocks = function(opt_workspace) { 64 | var url = window.location.href.split('#')[0]; 65 | console.log(['restoreBlocks', url]); 66 | vscode.postMessage({ 67 | command: 'restoreBlocks', 68 | url: url 69 | }); 70 | }; 71 | 72 | /** 73 | * Save blocks to database and return a link containing key to XML. 74 | * @param {Blockly.WorkspaceSvg=} opt_workspace Workspace. 75 | */ 76 | BlocklyStorage.link = function(opt_workspace) { 77 | console.log('link'); 78 | var workspace = opt_workspace || Blockly.getMainWorkspace(); 79 | var xml = Blockly.Xml.workspaceToDom(workspace, true); 80 | // Remove x/y coordinates from XML if there's only one block stack. 81 | // There's no reason to store this, removing it helps with anonymity. 82 | if (workspace.getTopBlocks(false).length == 1 && xml.querySelector) { 83 | var block = xml.querySelector('block'); 84 | if (block) { 85 | block.removeAttribute('x'); 86 | block.removeAttribute('y'); 87 | } 88 | } 89 | var data = Blockly.Xml.domToText(xml); 90 | BlocklyStorage.makeRequest_('/storage', 'xml', data, workspace); 91 | }; 92 | 93 | /** 94 | * Retrieve XML text from database using given key. 95 | * @param {string} key Key to XML, obtained from href. 96 | * @param {Blockly.WorkspaceSvg=} opt_workspace Workspace. 97 | */ 98 | BlocklyStorage.retrieveXml = function(key, opt_workspace) { 99 | console.log('retrieveXml'); 100 | var workspace = opt_workspace || Blockly.getMainWorkspace(); 101 | BlocklyStorage.makeRequest_('/storage', 'key', key, workspace); 102 | }; 103 | 104 | /** 105 | * Global reference to current AJAX request. 106 | * @type {XMLHttpRequest} 107 | * @private 108 | */ 109 | BlocklyStorage.httpRequest_ = null; 110 | 111 | /** 112 | * Fire a new AJAX request. 113 | * @param {string} url URL to fetch. 114 | * @param {string} name Name of parameter. 115 | * @param {string} content Content of parameter. 116 | * @param {!Blockly.WorkspaceSvg} workspace Workspace. 117 | * @private 118 | */ 119 | BlocklyStorage.makeRequest_ = function(url, name, content, workspace) { 120 | console.log('makeRequest_'); 121 | if (BlocklyStorage.httpRequest_) { 122 | // AJAX call is in-flight. 123 | BlocklyStorage.httpRequest_.abort(); 124 | } 125 | BlocklyStorage.httpRequest_ = new XMLHttpRequest(); 126 | BlocklyStorage.httpRequest_.name = name; 127 | BlocklyStorage.httpRequest_.onreadystatechange = 128 | BlocklyStorage.handleRequest_; 129 | BlocklyStorage.httpRequest_.open('POST', url); 130 | BlocklyStorage.httpRequest_.setRequestHeader('Content-Type', 131 | 'application/x-www-form-urlencoded'); 132 | BlocklyStorage.httpRequest_.send(name + '=' + encodeURIComponent(content)); 133 | BlocklyStorage.httpRequest_.workspace = workspace; 134 | }; 135 | 136 | /** 137 | * Callback function for AJAX call. 138 | * @private 139 | */ 140 | BlocklyStorage.handleRequest_ = function() { 141 | console.log('handleRequest_'); 142 | if (BlocklyStorage.httpRequest_.readyState == 4) { 143 | if (BlocklyStorage.httpRequest_.status != 200) { 144 | BlocklyStorage.alert(BlocklyStorage.HTTPREQUEST_ERROR + '\n' + 145 | 'httpRequest_.status: ' + BlocklyStorage.httpRequest_.status); 146 | } else { 147 | var data = BlocklyStorage.httpRequest_.responseText.trim(); 148 | if (BlocklyStorage.httpRequest_.name == 'xml') { 149 | window.location.hash = data; 150 | BlocklyStorage.alert(BlocklyStorage.LINK_ALERT.replace('%1', 151 | window.location.href)); 152 | } else if (BlocklyStorage.httpRequest_.name == 'key') { 153 | if (!data.length) { 154 | BlocklyStorage.alert(BlocklyStorage.HASH_ERROR.replace('%1', 155 | window.location.hash)); 156 | } else { 157 | BlocklyStorage.loadXml_(data, BlocklyStorage.httpRequest_.workspace); 158 | } 159 | } 160 | BlocklyStorage.monitorChanges_(BlocklyStorage.httpRequest_.workspace); 161 | } 162 | BlocklyStorage.httpRequest_ = null; 163 | } 164 | }; 165 | 166 | /** 167 | * Start monitoring the workspace. If a change is made that changes the XML, 168 | * update the VSCode document. 169 | * @param {!Blockly.WorkspaceSvg} workspace Workspace. 170 | * @private 171 | */ 172 | BlocklyStorage.monitorChanges_ = function(workspace) { 173 | console.log('monitorChanges_'); 174 | var startXmlDom = Blockly.Xml.workspaceToDom(workspace); 175 | var startXmlText = Blockly.Xml.domToText(startXmlDom); 176 | 177 | function generateCode() { 178 | // Generate the Rust code and send to VSCode. 179 | console.log('monitorChanges_generateCode'); 180 | var xmlDom = Blockly.Xml.workspaceToDom(workspace); 181 | var xmlText = Blockly.Xml.domToText(xmlDom); 182 | 183 | // Combine the code with the XML, enclosed by `BEGIN BLOCKS`...`END BLOCKS` tags. 184 | var code = Blockly.Rust.workspaceToCode(workspace); 185 | var text = composeDoc(xmlText, code); 186 | 187 | // Send the updated doc to VSCode to update the editor. 188 | vscode.postMessage({ 189 | command: 'updateDoc', 190 | text: text 191 | }); 192 | } 193 | 194 | function change() { 195 | // Callback for changes. Detect whether the Blocks XML has changed. If changed, regenerate the code. 196 | var xmlDom = Blockly.Xml.workspaceToDom(workspace); 197 | var xmlText = Blockly.Xml.domToText(xmlDom); 198 | if (startXmlText == xmlText) { return; } // No changes. 199 | 200 | // Generate the code. 201 | startXmlText = xmlText; 202 | console.log('monitorChanges_change'); 203 | generateCode(); 204 | } 205 | 206 | // Start monitoring for changes. 207 | workspace.addChangeListener(change); 208 | 209 | // On first load, regenerate the Rust code in case the code generator has been updated. 210 | generateCode(); 211 | }; 212 | 213 | /** 214 | * Load blocks from XML. 215 | * @param {string} xml Text representation of XML. 216 | * @param {!Blockly.WorkspaceSvg} workspace Workspace. 217 | * @private 218 | */ 219 | BlocklyStorage.loadXml_ = function(xml, workspace) { 220 | console.log('loadXml_'); 221 | try { 222 | xml = Blockly.Xml.textToDom(xml); 223 | } catch (e) { 224 | BlocklyStorage.alert(BlocklyStorage.XML_ERROR + '\nXML: ' + xml); 225 | return; 226 | } 227 | // Clear the workspace to avoid merge. 228 | workspace.clear(); 229 | Blockly.Xml.domToWorkspace(xml, workspace); 230 | }; 231 | 232 | /** 233 | * Present a text message to the user. 234 | * Designed to be overridden if an app has custom dialogs, or a butter bar. 235 | * @param {string} message Text to alert. 236 | */ 237 | BlocklyStorage.alert = function(message) { 238 | console.error(message); 239 | }; 240 | } -------------------------------------------------------------------------------- /media/vscode/style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | color: black; /* For VSCode, else text will appear grey */ 3 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "visual-embedded-rust", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.5.5", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", 10 | "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "7.5.0" 14 | } 15 | }, 16 | "@babel/highlight": { 17 | "version": "7.5.0", 18 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", 19 | "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", 20 | "dev": true, 21 | "requires": { 22 | "chalk": "2.4.2", 23 | "esutils": "2.0.2", 24 | "js-tokens": "4.0.0" 25 | } 26 | }, 27 | "@types/events": { 28 | "version": "3.0.0", 29 | "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", 30 | "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", 31 | "dev": true 32 | }, 33 | "@types/glob": { 34 | "version": "7.1.1", 35 | "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", 36 | "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", 37 | "dev": true, 38 | "requires": { 39 | "@types/events": "3.0.0", 40 | "@types/minimatch": "3.0.3", 41 | "@types/node": "10.14.12" 42 | } 43 | }, 44 | "@types/minimatch": { 45 | "version": "3.0.3", 46 | "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", 47 | "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", 48 | "dev": true 49 | }, 50 | "@types/mocha": { 51 | "version": "5.2.7", 52 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", 53 | "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", 54 | "dev": true 55 | }, 56 | "@types/node": { 57 | "version": "10.14.12", 58 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.12.tgz", 59 | "integrity": "sha512-QcAKpaO6nhHLlxWBvpc4WeLrTvPqlHOvaj0s5GriKkA1zq+bsFBPpfYCvQhLqLgYlIko8A9YrPdaMHCo5mBcpg==", 60 | "dev": true 61 | }, 62 | "@types/vscode": { 63 | "version": "1.36.0", 64 | "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.36.0.tgz", 65 | "integrity": "sha512-SbHR3Q5g/C3N+Ila3KrRf1rSZiyHxWdOZ7X3yFHXzw6HrvRLuVZrxnwEX0lTBMRpH9LkwZdqRTgXW+D075jxkg==", 66 | "dev": true 67 | }, 68 | "agent-base": { 69 | "version": "4.2.1", 70 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", 71 | "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", 72 | "dev": true, 73 | "requires": { 74 | "es6-promisify": "5.0.0" 75 | } 76 | }, 77 | "ansi-colors": { 78 | "version": "3.2.3", 79 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", 80 | "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", 81 | "dev": true 82 | }, 83 | "ansi-regex": { 84 | "version": "3.0.0", 85 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 86 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 87 | "dev": true 88 | }, 89 | "ansi-styles": { 90 | "version": "3.2.1", 91 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 92 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 93 | "dev": true, 94 | "requires": { 95 | "color-convert": "1.9.3" 96 | } 97 | }, 98 | "argparse": { 99 | "version": "1.0.10", 100 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 101 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 102 | "dev": true, 103 | "requires": { 104 | "sprintf-js": "1.0.3" 105 | } 106 | }, 107 | "balanced-match": { 108 | "version": "1.0.0", 109 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 110 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 111 | "dev": true 112 | }, 113 | "brace-expansion": { 114 | "version": "1.1.11", 115 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 116 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 117 | "dev": true, 118 | "requires": { 119 | "balanced-match": "1.0.0", 120 | "concat-map": "0.0.1" 121 | } 122 | }, 123 | "browser-stdout": { 124 | "version": "1.3.1", 125 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 126 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 127 | "dev": true 128 | }, 129 | "builtin-modules": { 130 | "version": "1.1.1", 131 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 132 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", 133 | "dev": true 134 | }, 135 | "camelcase": { 136 | "version": "5.3.1", 137 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 138 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", 139 | "dev": true 140 | }, 141 | "chalk": { 142 | "version": "2.4.2", 143 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 144 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 145 | "dev": true, 146 | "requires": { 147 | "ansi-styles": "3.2.1", 148 | "escape-string-regexp": "1.0.5", 149 | "supports-color": "5.5.0" 150 | }, 151 | "dependencies": { 152 | "supports-color": { 153 | "version": "5.5.0", 154 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 155 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 156 | "dev": true, 157 | "requires": { 158 | "has-flag": "3.0.0" 159 | } 160 | } 161 | } 162 | }, 163 | "cliui": { 164 | "version": "4.1.0", 165 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", 166 | "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", 167 | "dev": true, 168 | "requires": { 169 | "string-width": "2.1.1", 170 | "strip-ansi": "4.0.0", 171 | "wrap-ansi": "2.1.0" 172 | } 173 | }, 174 | "code-point-at": { 175 | "version": "1.1.0", 176 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 177 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", 178 | "dev": true 179 | }, 180 | "color-convert": { 181 | "version": "1.9.3", 182 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 183 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 184 | "dev": true, 185 | "requires": { 186 | "color-name": "1.1.3" 187 | } 188 | }, 189 | "color-name": { 190 | "version": "1.1.3", 191 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 192 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 193 | "dev": true 194 | }, 195 | "commander": { 196 | "version": "2.20.0", 197 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", 198 | "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", 199 | "dev": true 200 | }, 201 | "concat-map": { 202 | "version": "0.0.1", 203 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 204 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 205 | "dev": true 206 | }, 207 | "cross-spawn": { 208 | "version": "6.0.5", 209 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", 210 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", 211 | "dev": true, 212 | "requires": { 213 | "nice-try": "1.0.5", 214 | "path-key": "2.0.1", 215 | "semver": "5.7.0", 216 | "shebang-command": "1.2.0", 217 | "which": "1.3.1" 218 | } 219 | }, 220 | "debug": { 221 | "version": "3.2.6", 222 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 223 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 224 | "dev": true, 225 | "requires": { 226 | "ms": "2.1.1" 227 | } 228 | }, 229 | "decamelize": { 230 | "version": "1.2.0", 231 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 232 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 233 | "dev": true 234 | }, 235 | "define-properties": { 236 | "version": "1.1.2", 237 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", 238 | "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", 239 | "dev": true, 240 | "requires": { 241 | "foreach": "2.0.5", 242 | "object-keys": "1.0.11" 243 | } 244 | }, 245 | "diff": { 246 | "version": "3.5.0", 247 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 248 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 249 | "dev": true 250 | }, 251 | "emoji-regex": { 252 | "version": "7.0.3", 253 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 254 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 255 | "dev": true 256 | }, 257 | "end-of-stream": { 258 | "version": "1.4.1", 259 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", 260 | "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", 261 | "dev": true, 262 | "requires": { 263 | "once": "1.4.0" 264 | } 265 | }, 266 | "es-abstract": { 267 | "version": "1.10.0", 268 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.10.0.tgz", 269 | "integrity": "sha512-/uh/DhdqIOSkAWifU+8nG78vlQxdLckUdI/sPgy0VhuXi2qJ7T8czBmqIYtLQVpCIFYafChnsRsB5pyb1JdmCQ==", 270 | "dev": true, 271 | "requires": { 272 | "es-to-primitive": "1.1.1", 273 | "function-bind": "1.1.1", 274 | "has": "1.0.3", 275 | "is-callable": "1.1.3", 276 | "is-regex": "1.0.4" 277 | } 278 | }, 279 | "es-to-primitive": { 280 | "version": "1.1.1", 281 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", 282 | "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", 283 | "dev": true, 284 | "requires": { 285 | "is-callable": "1.1.3", 286 | "is-date-object": "1.0.1", 287 | "is-symbol": "1.0.1" 288 | } 289 | }, 290 | "es6-promise": { 291 | "version": "4.2.8", 292 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", 293 | "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", 294 | "dev": true 295 | }, 296 | "es6-promisify": { 297 | "version": "5.0.0", 298 | "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", 299 | "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", 300 | "dev": true, 301 | "requires": { 302 | "es6-promise": "4.2.8" 303 | } 304 | }, 305 | "escape-string-regexp": { 306 | "version": "1.0.5", 307 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 308 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 309 | "dev": true 310 | }, 311 | "esprima": { 312 | "version": "4.0.1", 313 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 314 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 315 | "dev": true 316 | }, 317 | "esutils": { 318 | "version": "2.0.2", 319 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 320 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 321 | "dev": true 322 | }, 323 | "execa": { 324 | "version": "1.0.0", 325 | "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", 326 | "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", 327 | "dev": true, 328 | "requires": { 329 | "cross-spawn": "6.0.5", 330 | "get-stream": "4.1.0", 331 | "is-stream": "1.1.0", 332 | "npm-run-path": "2.0.2", 333 | "p-finally": "1.0.0", 334 | "signal-exit": "3.0.2", 335 | "strip-eof": "1.0.0" 336 | } 337 | }, 338 | "find-up": { 339 | "version": "3.0.0", 340 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 341 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 342 | "dev": true, 343 | "requires": { 344 | "locate-path": "3.0.0" 345 | } 346 | }, 347 | "flat": { 348 | "version": "4.1.0", 349 | "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", 350 | "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", 351 | "dev": true, 352 | "requires": { 353 | "is-buffer": "2.0.3" 354 | } 355 | }, 356 | "foreach": { 357 | "version": "2.0.5", 358 | "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", 359 | "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", 360 | "dev": true 361 | }, 362 | "fs.realpath": { 363 | "version": "1.0.0", 364 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 365 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 366 | "dev": true 367 | }, 368 | "function-bind": { 369 | "version": "1.1.1", 370 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 371 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 372 | "dev": true 373 | }, 374 | "get-caller-file": { 375 | "version": "2.0.5", 376 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 377 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 378 | "dev": true 379 | }, 380 | "get-stream": { 381 | "version": "4.1.0", 382 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", 383 | "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", 384 | "dev": true, 385 | "requires": { 386 | "pump": "3.0.0" 387 | } 388 | }, 389 | "glob": { 390 | "version": "7.1.4", 391 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", 392 | "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", 393 | "dev": true, 394 | "requires": { 395 | "fs.realpath": "1.0.0", 396 | "inflight": "1.0.6", 397 | "inherits": "2.0.4", 398 | "minimatch": "3.0.4", 399 | "once": "1.4.0", 400 | "path-is-absolute": "1.0.1" 401 | } 402 | }, 403 | "growl": { 404 | "version": "1.10.5", 405 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 406 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 407 | "dev": true 408 | }, 409 | "has": { 410 | "version": "1.0.3", 411 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 412 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 413 | "dev": true, 414 | "requires": { 415 | "function-bind": "1.1.1" 416 | } 417 | }, 418 | "has-flag": { 419 | "version": "3.0.0", 420 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 421 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 422 | "dev": true 423 | }, 424 | "has-symbols": { 425 | "version": "1.0.0", 426 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", 427 | "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", 428 | "dev": true 429 | }, 430 | "he": { 431 | "version": "1.2.0", 432 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 433 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 434 | "dev": true 435 | }, 436 | "http-proxy-agent": { 437 | "version": "2.1.0", 438 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", 439 | "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", 440 | "dev": true, 441 | "requires": { 442 | "agent-base": "4.2.1", 443 | "debug": "3.1.0" 444 | }, 445 | "dependencies": { 446 | "debug": { 447 | "version": "3.1.0", 448 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 449 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 450 | "dev": true, 451 | "requires": { 452 | "ms": "2.0.0" 453 | } 454 | }, 455 | "ms": { 456 | "version": "2.0.0", 457 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 458 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 459 | "dev": true 460 | } 461 | } 462 | }, 463 | "https-proxy-agent": { 464 | "version": "2.2.1", 465 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", 466 | "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", 467 | "dev": true, 468 | "requires": { 469 | "agent-base": "4.2.1", 470 | "debug": "3.2.6" 471 | } 472 | }, 473 | "inflight": { 474 | "version": "1.0.6", 475 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 476 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 477 | "dev": true, 478 | "requires": { 479 | "once": "1.4.0", 480 | "wrappy": "1.0.2" 481 | } 482 | }, 483 | "inherits": { 484 | "version": "2.0.4", 485 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 486 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 487 | "dev": true 488 | }, 489 | "invert-kv": { 490 | "version": "2.0.0", 491 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", 492 | "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", 493 | "dev": true 494 | }, 495 | "is-buffer": { 496 | "version": "2.0.3", 497 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", 498 | "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", 499 | "dev": true 500 | }, 501 | "is-callable": { 502 | "version": "1.1.3", 503 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", 504 | "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", 505 | "dev": true 506 | }, 507 | "is-date-object": { 508 | "version": "1.0.1", 509 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", 510 | "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", 511 | "dev": true 512 | }, 513 | "is-fullwidth-code-point": { 514 | "version": "2.0.0", 515 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 516 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 517 | "dev": true 518 | }, 519 | "is-regex": { 520 | "version": "1.0.4", 521 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", 522 | "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", 523 | "dev": true, 524 | "requires": { 525 | "has": "1.0.3" 526 | } 527 | }, 528 | "is-stream": { 529 | "version": "1.1.0", 530 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 531 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", 532 | "dev": true 533 | }, 534 | "is-symbol": { 535 | "version": "1.0.1", 536 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", 537 | "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", 538 | "dev": true 539 | }, 540 | "isexe": { 541 | "version": "2.0.0", 542 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 543 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 544 | "dev": true 545 | }, 546 | "js-tokens": { 547 | "version": "4.0.0", 548 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 549 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 550 | "dev": true 551 | }, 552 | "js-yaml": { 553 | "version": "3.13.1", 554 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 555 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 556 | "dev": true, 557 | "requires": { 558 | "argparse": "1.0.10", 559 | "esprima": "4.0.1" 560 | } 561 | }, 562 | "lcid": { 563 | "version": "2.0.0", 564 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", 565 | "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", 566 | "dev": true, 567 | "requires": { 568 | "invert-kv": "2.0.0" 569 | } 570 | }, 571 | "locate-path": { 572 | "version": "3.0.0", 573 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 574 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 575 | "dev": true, 576 | "requires": { 577 | "p-locate": "3.0.0", 578 | "path-exists": "3.0.0" 579 | } 580 | }, 581 | "lodash": { 582 | "version": "4.17.15", 583 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 584 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", 585 | "dev": true 586 | }, 587 | "log-symbols": { 588 | "version": "2.2.0", 589 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", 590 | "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", 591 | "dev": true, 592 | "requires": { 593 | "chalk": "2.4.2" 594 | } 595 | }, 596 | "map-age-cleaner": { 597 | "version": "0.1.3", 598 | "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", 599 | "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", 600 | "dev": true, 601 | "requires": { 602 | "p-defer": "1.0.0" 603 | } 604 | }, 605 | "mem": { 606 | "version": "4.3.0", 607 | "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", 608 | "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", 609 | "dev": true, 610 | "requires": { 611 | "map-age-cleaner": "0.1.3", 612 | "mimic-fn": "2.1.0", 613 | "p-is-promise": "2.1.0" 614 | } 615 | }, 616 | "mimic-fn": { 617 | "version": "2.1.0", 618 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", 619 | "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", 620 | "dev": true 621 | }, 622 | "minimatch": { 623 | "version": "3.0.4", 624 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 625 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 626 | "dev": true, 627 | "requires": { 628 | "brace-expansion": "1.1.11" 629 | } 630 | }, 631 | "minimist": { 632 | "version": "0.0.8", 633 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 634 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 635 | "dev": true 636 | }, 637 | "mkdirp": { 638 | "version": "0.5.1", 639 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 640 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 641 | "dev": true, 642 | "requires": { 643 | "minimist": "0.0.8" 644 | } 645 | }, 646 | "mocha": { 647 | "version": "6.2.0", 648 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.0.tgz", 649 | "integrity": "sha512-qwfFgY+7EKAAUAdv7VYMZQknI7YJSGesxHyhn6qD52DV8UcSZs5XwCifcZGMVIE4a5fbmhvbotxC0DLQ0oKohQ==", 650 | "dev": true, 651 | "requires": { 652 | "ansi-colors": "3.2.3", 653 | "browser-stdout": "1.3.1", 654 | "debug": "3.2.6", 655 | "diff": "3.5.0", 656 | "escape-string-regexp": "1.0.5", 657 | "find-up": "3.0.0", 658 | "glob": "7.1.3", 659 | "growl": "1.10.5", 660 | "he": "1.2.0", 661 | "js-yaml": "3.13.1", 662 | "log-symbols": "2.2.0", 663 | "minimatch": "3.0.4", 664 | "mkdirp": "0.5.1", 665 | "ms": "2.1.1", 666 | "node-environment-flags": "1.0.5", 667 | "object.assign": "4.1.0", 668 | "strip-json-comments": "2.0.1", 669 | "supports-color": "6.0.0", 670 | "which": "1.3.1", 671 | "wide-align": "1.1.3", 672 | "yargs": "13.2.2", 673 | "yargs-parser": "13.0.0", 674 | "yargs-unparser": "1.5.0" 675 | }, 676 | "dependencies": { 677 | "glob": { 678 | "version": "7.1.3", 679 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", 680 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", 681 | "dev": true, 682 | "requires": { 683 | "fs.realpath": "1.0.0", 684 | "inflight": "1.0.6", 685 | "inherits": "2.0.4", 686 | "minimatch": "3.0.4", 687 | "once": "1.4.0", 688 | "path-is-absolute": "1.0.1" 689 | } 690 | } 691 | } 692 | }, 693 | "ms": { 694 | "version": "2.1.1", 695 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 696 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", 697 | "dev": true 698 | }, 699 | "nice-try": { 700 | "version": "1.0.5", 701 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 702 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", 703 | "dev": true 704 | }, 705 | "node-environment-flags": { 706 | "version": "1.0.5", 707 | "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", 708 | "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", 709 | "dev": true, 710 | "requires": { 711 | "object.getownpropertydescriptors": "2.0.3", 712 | "semver": "5.7.0" 713 | } 714 | }, 715 | "npm-run-path": { 716 | "version": "2.0.2", 717 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 718 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 719 | "dev": true, 720 | "requires": { 721 | "path-key": "2.0.1" 722 | } 723 | }, 724 | "number-is-nan": { 725 | "version": "1.0.1", 726 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 727 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", 728 | "dev": true 729 | }, 730 | "object-keys": { 731 | "version": "1.0.11", 732 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", 733 | "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", 734 | "dev": true 735 | }, 736 | "object.assign": { 737 | "version": "4.1.0", 738 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", 739 | "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", 740 | "dev": true, 741 | "requires": { 742 | "define-properties": "1.1.2", 743 | "function-bind": "1.1.1", 744 | "has-symbols": "1.0.0", 745 | "object-keys": "1.0.11" 746 | } 747 | }, 748 | "object.getownpropertydescriptors": { 749 | "version": "2.0.3", 750 | "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", 751 | "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", 752 | "dev": true, 753 | "requires": { 754 | "define-properties": "1.1.2", 755 | "es-abstract": "1.10.0" 756 | } 757 | }, 758 | "once": { 759 | "version": "1.4.0", 760 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 761 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 762 | "dev": true, 763 | "requires": { 764 | "wrappy": "1.0.2" 765 | } 766 | }, 767 | "os-locale": { 768 | "version": "3.1.0", 769 | "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", 770 | "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", 771 | "dev": true, 772 | "requires": { 773 | "execa": "1.0.0", 774 | "lcid": "2.0.0", 775 | "mem": "4.3.0" 776 | } 777 | }, 778 | "p-defer": { 779 | "version": "1.0.0", 780 | "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", 781 | "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", 782 | "dev": true 783 | }, 784 | "p-finally": { 785 | "version": "1.0.0", 786 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 787 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", 788 | "dev": true 789 | }, 790 | "p-is-promise": { 791 | "version": "2.1.0", 792 | "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", 793 | "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", 794 | "dev": true 795 | }, 796 | "p-limit": { 797 | "version": "2.2.0", 798 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", 799 | "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", 800 | "dev": true, 801 | "requires": { 802 | "p-try": "2.2.0" 803 | } 804 | }, 805 | "p-locate": { 806 | "version": "3.0.0", 807 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 808 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 809 | "dev": true, 810 | "requires": { 811 | "p-limit": "2.2.0" 812 | } 813 | }, 814 | "p-try": { 815 | "version": "2.2.0", 816 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 817 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 818 | "dev": true 819 | }, 820 | "path-exists": { 821 | "version": "3.0.0", 822 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 823 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 824 | "dev": true 825 | }, 826 | "path-is-absolute": { 827 | "version": "1.0.1", 828 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 829 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 830 | "dev": true 831 | }, 832 | "path-key": { 833 | "version": "2.0.1", 834 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 835 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", 836 | "dev": true 837 | }, 838 | "path-parse": { 839 | "version": "1.0.6", 840 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 841 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 842 | "dev": true 843 | }, 844 | "pump": { 845 | "version": "3.0.0", 846 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 847 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 848 | "dev": true, 849 | "requires": { 850 | "end-of-stream": "1.4.1", 851 | "once": "1.4.0" 852 | } 853 | }, 854 | "require-directory": { 855 | "version": "2.1.1", 856 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 857 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 858 | "dev": true 859 | }, 860 | "require-main-filename": { 861 | "version": "2.0.0", 862 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 863 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", 864 | "dev": true 865 | }, 866 | "resolve": { 867 | "version": "1.12.0", 868 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", 869 | "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", 870 | "dev": true, 871 | "requires": { 872 | "path-parse": "1.0.6" 873 | } 874 | }, 875 | "semver": { 876 | "version": "5.7.0", 877 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", 878 | "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", 879 | "dev": true 880 | }, 881 | "set-blocking": { 882 | "version": "2.0.0", 883 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 884 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", 885 | "dev": true 886 | }, 887 | "shebang-command": { 888 | "version": "1.2.0", 889 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 890 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 891 | "dev": true, 892 | "requires": { 893 | "shebang-regex": "1.0.0" 894 | } 895 | }, 896 | "shebang-regex": { 897 | "version": "1.0.0", 898 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 899 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 900 | "dev": true 901 | }, 902 | "signal-exit": { 903 | "version": "3.0.2", 904 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 905 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 906 | "dev": true 907 | }, 908 | "sprintf-js": { 909 | "version": "1.0.3", 910 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 911 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 912 | "dev": true 913 | }, 914 | "string-width": { 915 | "version": "2.1.1", 916 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 917 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 918 | "dev": true, 919 | "requires": { 920 | "is-fullwidth-code-point": "2.0.0", 921 | "strip-ansi": "4.0.0" 922 | } 923 | }, 924 | "strip-ansi": { 925 | "version": "4.0.0", 926 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 927 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 928 | "dev": true, 929 | "requires": { 930 | "ansi-regex": "3.0.0" 931 | } 932 | }, 933 | "strip-eof": { 934 | "version": "1.0.0", 935 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 936 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", 937 | "dev": true 938 | }, 939 | "strip-json-comments": { 940 | "version": "2.0.1", 941 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 942 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 943 | "dev": true 944 | }, 945 | "supports-color": { 946 | "version": "6.0.0", 947 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", 948 | "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", 949 | "dev": true, 950 | "requires": { 951 | "has-flag": "3.0.0" 952 | } 953 | }, 954 | "tslib": { 955 | "version": "1.10.0", 956 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", 957 | "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", 958 | "dev": true 959 | }, 960 | "tslint": { 961 | "version": "5.18.0", 962 | "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.18.0.tgz", 963 | "integrity": "sha512-Q3kXkuDEijQ37nXZZLKErssQVnwCV/+23gFEMROi8IlbaBG6tXqLPQJ5Wjcyt/yHPKBC+hD5SzuGaMora+ZS6w==", 964 | "dev": true, 965 | "requires": { 966 | "@babel/code-frame": "7.5.5", 967 | "builtin-modules": "1.1.1", 968 | "chalk": "2.4.2", 969 | "commander": "2.20.0", 970 | "diff": "3.5.0", 971 | "glob": "7.1.4", 972 | "js-yaml": "3.13.1", 973 | "minimatch": "3.0.4", 974 | "mkdirp": "0.5.1", 975 | "resolve": "1.12.0", 976 | "semver": "5.7.0", 977 | "tslib": "1.10.0", 978 | "tsutils": "2.29.0" 979 | } 980 | }, 981 | "tsutils": { 982 | "version": "2.29.0", 983 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", 984 | "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", 985 | "dev": true, 986 | "requires": { 987 | "tslib": "1.10.0" 988 | } 989 | }, 990 | "typescript": { 991 | "version": "3.5.3", 992 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", 993 | "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", 994 | "dev": true 995 | }, 996 | "vscode-test": { 997 | "version": "1.1.0", 998 | "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.1.0.tgz", 999 | "integrity": "sha512-uAz0O8a28GXzrtVyZHOUmEPaVI2wQyK8DUaOaZIfODXxZJ5VV75Yye/QW+8dW6I1js6o5iXfE49ypDSG4GoTPQ==", 1000 | "dev": true, 1001 | "requires": { 1002 | "http-proxy-agent": "2.1.0", 1003 | "https-proxy-agent": "2.2.1" 1004 | } 1005 | }, 1006 | "which": { 1007 | "version": "1.3.1", 1008 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 1009 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 1010 | "dev": true, 1011 | "requires": { 1012 | "isexe": "2.0.0" 1013 | } 1014 | }, 1015 | "which-module": { 1016 | "version": "2.0.0", 1017 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 1018 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", 1019 | "dev": true 1020 | }, 1021 | "wide-align": { 1022 | "version": "1.1.3", 1023 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 1024 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 1025 | "dev": true, 1026 | "requires": { 1027 | "string-width": "2.1.1" 1028 | } 1029 | }, 1030 | "wrap-ansi": { 1031 | "version": "2.1.0", 1032 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", 1033 | "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", 1034 | "dev": true, 1035 | "requires": { 1036 | "string-width": "1.0.2", 1037 | "strip-ansi": "3.0.1" 1038 | }, 1039 | "dependencies": { 1040 | "ansi-regex": { 1041 | "version": "2.1.1", 1042 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 1043 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", 1044 | "dev": true 1045 | }, 1046 | "is-fullwidth-code-point": { 1047 | "version": "1.0.0", 1048 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 1049 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 1050 | "dev": true, 1051 | "requires": { 1052 | "number-is-nan": "1.0.1" 1053 | } 1054 | }, 1055 | "string-width": { 1056 | "version": "1.0.2", 1057 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1058 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1059 | "dev": true, 1060 | "requires": { 1061 | "code-point-at": "1.1.0", 1062 | "is-fullwidth-code-point": "1.0.0", 1063 | "strip-ansi": "3.0.1" 1064 | } 1065 | }, 1066 | "strip-ansi": { 1067 | "version": "3.0.1", 1068 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1069 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1070 | "dev": true, 1071 | "requires": { 1072 | "ansi-regex": "2.1.1" 1073 | } 1074 | } 1075 | } 1076 | }, 1077 | "wrappy": { 1078 | "version": "1.0.2", 1079 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1080 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1081 | "dev": true 1082 | }, 1083 | "y18n": { 1084 | "version": "4.0.0", 1085 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 1086 | "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", 1087 | "dev": true 1088 | }, 1089 | "yargs": { 1090 | "version": "13.2.2", 1091 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", 1092 | "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", 1093 | "dev": true, 1094 | "requires": { 1095 | "cliui": "4.1.0", 1096 | "find-up": "3.0.0", 1097 | "get-caller-file": "2.0.5", 1098 | "os-locale": "3.1.0", 1099 | "require-directory": "2.1.1", 1100 | "require-main-filename": "2.0.0", 1101 | "set-blocking": "2.0.0", 1102 | "string-width": "3.1.0", 1103 | "which-module": "2.0.0", 1104 | "y18n": "4.0.0", 1105 | "yargs-parser": "13.0.0" 1106 | }, 1107 | "dependencies": { 1108 | "ansi-regex": { 1109 | "version": "4.1.0", 1110 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 1111 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 1112 | "dev": true 1113 | }, 1114 | "string-width": { 1115 | "version": "3.1.0", 1116 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 1117 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 1118 | "dev": true, 1119 | "requires": { 1120 | "emoji-regex": "7.0.3", 1121 | "is-fullwidth-code-point": "2.0.0", 1122 | "strip-ansi": "5.2.0" 1123 | } 1124 | }, 1125 | "strip-ansi": { 1126 | "version": "5.2.0", 1127 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1128 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1129 | "dev": true, 1130 | "requires": { 1131 | "ansi-regex": "4.1.0" 1132 | } 1133 | } 1134 | } 1135 | }, 1136 | "yargs-parser": { 1137 | "version": "13.0.0", 1138 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", 1139 | "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", 1140 | "dev": true, 1141 | "requires": { 1142 | "camelcase": "5.3.1", 1143 | "decamelize": "1.2.0" 1144 | } 1145 | }, 1146 | "yargs-unparser": { 1147 | "version": "1.5.0", 1148 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", 1149 | "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", 1150 | "dev": true, 1151 | "requires": { 1152 | "flat": "4.1.0", 1153 | "lodash": "4.17.15", 1154 | "yargs": "12.0.5" 1155 | }, 1156 | "dependencies": { 1157 | "get-caller-file": { 1158 | "version": "1.0.3", 1159 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", 1160 | "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", 1161 | "dev": true 1162 | }, 1163 | "require-main-filename": { 1164 | "version": "1.0.1", 1165 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", 1166 | "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", 1167 | "dev": true 1168 | }, 1169 | "yargs": { 1170 | "version": "12.0.5", 1171 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", 1172 | "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", 1173 | "dev": true, 1174 | "requires": { 1175 | "cliui": "4.1.0", 1176 | "decamelize": "1.2.0", 1177 | "find-up": "3.0.0", 1178 | "get-caller-file": "1.0.3", 1179 | "os-locale": "3.1.0", 1180 | "require-directory": "2.1.1", 1181 | "require-main-filename": "1.0.1", 1182 | "set-blocking": "2.0.0", 1183 | "string-width": "2.1.1", 1184 | "which-module": "2.0.0", 1185 | "y18n": "4.0.0", 1186 | "yargs-parser": "11.1.1" 1187 | } 1188 | }, 1189 | "yargs-parser": { 1190 | "version": "11.1.1", 1191 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", 1192 | "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", 1193 | "dev": true, 1194 | "requires": { 1195 | "camelcase": "5.3.1", 1196 | "decamelize": "1.2.0" 1197 | } 1198 | } 1199 | } 1200 | } 1201 | } 1202 | } 1203 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "visual-embedded-rust", 3 | "displayName": "Visual Embedded Rust", 4 | "description": "Visual Editor for Embedded Rust", 5 | "version": "1.0.7", 6 | "publisher": "LeeLupYuen", 7 | "icon": "images/visual-rust-icon.png", 8 | "repository": { 9 | "url": "https://github.com/lupyuen/visual-embedded-rust" 10 | }, 11 | "engines": { 12 | "vscode": "^1.32.0" 13 | }, 14 | "categories": [ 15 | "Programming Languages", 16 | "Other" 17 | ], 18 | "activationEvents": [ 19 | "onCommand:visualEmbeddedRust.helloWorld", 20 | "onCommand:visualEmbeddedRust.start", 21 | "onCommand:visualEmbeddedRust.doRefactor", 22 | "onWebviewPanel:visualEmbeddedRust" 23 | ], 24 | "main": "./out/extension.js", 25 | "contributes": { 26 | "commands": [ 27 | { 28 | "command": "visualEmbeddedRust.helloWorld", 29 | "title": "Hello World", 30 | "category": "Visual Embedded Rust" 31 | }, 32 | { 33 | "command": "visualEmbeddedRust.start", 34 | "title": "Visual editor", 35 | "category": "Visual Embedded Rust" 36 | }, 37 | { 38 | "command": "visualEmbeddedRust.doRefactor", 39 | "title": "Do some refactoring", 40 | "category": "Visual Embedded Rust" 41 | } 42 | ], 43 | "menus": { 44 | "editor/title": [ 45 | { 46 | "command": "visualEmbeddedRust.start", 47 | "group": "navigation", 48 | "when": "resourceExtname == .rs" 49 | } 50 | ] 51 | }, 52 | "viewsContainers": { 53 | "activitybar": [ 54 | { 55 | "id": "visual-embedded-rust-declarations", 56 | "title": "Declarations", 57 | "icon": "media/dep.svg" 58 | } 59 | ] 60 | }, 61 | "views": { 62 | "visual-embedded-rust-declarations": [ 63 | { 64 | "id": "visualEmbeddedRustDeclarations", 65 | "name": "All" 66 | } 67 | ] 68 | }, 69 | "colors": [ 70 | { 71 | "id": "visualEmbeddedRust.background0", 72 | "description": "Background #0", 73 | "defaults": { 74 | "dark": "#FF000055", 75 | "light": "#FF000055", 76 | "highContrast": "#FF000055" 77 | } 78 | }, 79 | { 80 | "id": "visualEmbeddedRust.background1", 81 | "description": "Background #1", 82 | "defaults": { 83 | "dark": "#00FF0055", 84 | "light": "#00FF0055", 85 | "highContrast": "#00FF0055" 86 | } 87 | } 88 | ] 89 | }, 90 | "scripts": { 91 | "vscode:prepublish": "npm run compile", 92 | "compile": "tsc -p ./", 93 | "watch": "tsc -watch -p ./", 94 | "pretest": "npm run compile", 95 | "test": "node ./out/test/runTest.js" 96 | }, 97 | "devDependencies": { 98 | "@types/glob": "^7.1.1", 99 | "@types/mocha": "^5.2.6", 100 | "@types/node": "^10.12.21", 101 | "@types/vscode": "^1.32.0", 102 | "glob": "^7.1.4", 103 | "mocha": "^6.1.4", 104 | "typescript": "^3.3.1", 105 | "tslint": "^5.12.1", 106 | "vscode-test": "^1.0.2" 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /resources/README.md: -------------------------------------------------------------------------------- 1 | # resources 2 | 3 | - [`template.rs`](template.rs): Template for new visual programs 4 | -------------------------------------------------------------------------------- /resources/dark/boolean.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/dependency.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/document.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/folder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/number.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/string.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/boolean.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/dependency.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/document.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/folder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/number.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/string.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/package-explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupyuen/visual-embedded-rust/5ddf02f45588e2b469f693d6246b5094b0ddc7da/resources/package-explorer.png -------------------------------------------------------------------------------- /resources/replay.log: -------------------------------------------------------------------------------- 1 | > Executing task in folder test-rust-macros: scripts/expand-macros.sh ; cargo build < 2 | 3 | + rustup default nightly 4 | info: using existing install for 'nightly-x86_64-apple-darwin' 5 | info: default toolchain set to 'nightly-x86_64-apple-darwin' 6 | 7 | nightly-x86_64-apple-darwin unchanged - rustc 1.37.0-nightly (5f9c0448d 2019-06-25) 8 | 9 | + cargo rustc -- -Z unstable-options --pretty expanded 10 | Compiling macros v0.1.0 (/Users/Luppy/mynewt/stm32bluepill-mynewt-sensor/rust/macros) 11 | Compiling mynewt v0.1.0 (/Users/Luppy/mynewt/stm32bluepill-mynewt-sensor/rust/mynewt) 12 | Compiling test-rust-macros v0.1.0 (/Users/Luppy/mynewt/test-rust-macros) 13 | Finished dev [unoptimized + debuginfo] target(s) in 3.86s 14 | Compiling test-rust-macros v0.1.0 (/Users/Luppy/mynewt/test-rust-macros) 15 | fname: "start_sensor_listener" 16 | para: "sensor" 17 | #s src/main.rs | 42 | 29 | 42 | 35 18 | para: "sensor_type" 19 | #s src/main.rs | 42 | 40 | 42 | 51 20 | para: "poll_time" 21 | #s src/main.rs | 42 | 56 | 42 | 65 22 | #s src/main.rs | 42 | 90 | 50 | 5 23 | #s src/main.rs | 43 | 8 | 43 | 55 24 | #s src/main.rs | 43 | 8 | 43 | 53 25 | #s src/main.rs | 43 | 8 | 43 | 51 26 | #s src/main.rs | 43 | 8 | 43 | 51 27 | fname: "sensor::set_poll_rate_ms" 28 | #m sensor::set_poll_rate_ms | src/main.rs | 43 | 8 | 43 | 51 29 | #s src/main.rs | 43 | 33 | 43 | 50 30 | sensor has inferred type &Strn 31 | #s src/main.rs | 43 | 33 | 43 | 39 32 | #i start_sensor_listener | sensor | sensor::set_poll_rate_ms | devname | &Strn 33 | poll_time has inferred type u32 34 | #s src/main.rs | 43 | 41 | 43 | 50 35 | #i start_sensor_listener | poll_time | sensor::set_poll_rate_ms | poll_rate | u32 36 | #s src/main.rs | 43 | 33 | 43 | 50 37 | #s src/main.rs | 43 | 8 | 43 | 51 38 | #s src/main.rs | 43 | 8 | 43 | 51 39 | #s src/main.rs | 43 | 8 | 43 | 53 40 | #s src/main.rs | 44 | 8 | 44 | 83 41 | #s src/main.rs | 44 | 28 | 44 | 81 42 | #s src/main.rs | 44 | 28 | 44 | 79 43 | #s src/main.rs | 44 | 28 | 44 | 79 44 | fname: "sensor::mgr_find_next_bydevname" 45 | #m sensor::mgr_find_next_bydevname | src/main.rs | 44 | 28 | 44 | 79 46 | #s src/main.rs | 44 | 60 | 44 | 78 47 | sensor has inferred type &Strn 48 | #s src/main.rs | 44 | 60 | 44 | 66 49 | #i start_sensor_listener | sensor | sensor::mgr_find_next_bydevname | devname | &Strn 50 | null_mut() has inferred type *mut sensor 51 | #s src/main.rs | 44 | 68 | 44 | 78 52 | #i start_sensor_listener | null_mut() | sensor::mgr_find_next_bydevname | prev_cursor | *mut sensor 53 | #s src/main.rs | 44 | 60 | 44 | 78 54 | #s src/main.rs | 44 | 28 | 44 | 79 55 | #s src/main.rs | 44 | 28 | 44 | 79 56 | #s src/main.rs | 44 | 28 | 44 | 81 57 | #s src/main.rs | 45 | 8 | 48 | 9 58 | #s src/main.rs | 45 | 8 | 48 | 9 59 | #s src/main.rs | 45 | 11 | 45 | 38 60 | #s src/main.rs | 45 | 11 | 45 | 24 61 | #s src/main.rs | 45 | 11 | 45 | 24 62 | #s src/main.rs | 45 | 28 | 45 | 38 63 | #s src/main.rs | 45 | 28 | 45 | 38 64 | fname: "null_mut" 65 | load_decls: test.json, "{\"send_sensor_data\":[[\"sensor_data\",\"&SensorValue\"]]}" 66 | #s src/main.rs | 45 | 28 | 45 | 38 67 | #s src/main.rs | 45 | 11 | 45 | 38 68 | #s src/main.rs | 45 | 39 | 48 | 9 69 | #s src/main.rs | 46 | 12 | 46 | 83 70 | #s src/main.rs | 46 | 27 | 46 | 81 71 | #s src/main.rs | 46 | 27 | 46 | 79 72 | #s src/main.rs | 46 | 27 | 46 | 79 73 | fname: "new_sensor_listener" 74 | #m new_sensor_listener | src/main.rs | 46 | 27 | 46 | 79 75 | #s src/main.rs | 46 | 47 | 46 | 78 76 | sensor_type has inferred type sensor_type_t 77 | #s src/main.rs | 46 | 47 | 46 | 58 78 | #i start_sensor_listener | sensor_type | new_sensor_listener | sl_sensor_type | sensor_type_t 79 | handle_sensor_data has inferred type sensor_data_func 80 | #s src/main.rs | 46 | 60 | 46 | 78 81 | #i start_sensor_listener | handle_sensor_data | new_sensor_listener | sl_func | sensor_data_func 82 | #s src/main.rs | 46 | 47 | 46 | 78 83 | #s src/main.rs | 46 | 27 | 46 | 79 84 | #s src/main.rs | 46 | 27 | 46 | 79 85 | #s src/main.rs | 46 | 27 | 46 | 81 86 | #s src/main.rs | 47 | 12 | 47 | 66 87 | #s src/main.rs | 47 | 12 | 47 | 64 88 | #s src/main.rs | 47 | 12 | 47 | 62 89 | #s src/main.rs | 47 | 12 | 47 | 62 90 | fname: "sensor::register_listener" 91 | #m sensor::register_listener | src/main.rs | 47 | 12 | 47 | 62 92 | #s src/main.rs | 47 | 38 | 47 | 61 93 | sensor_object has inferred type *mut sensor 94 | #s src/main.rs | 47 | 38 | 47 | 51 95 | #i start_sensor_listener | sensor_object | sensor::register_listener | sensor | *mut sensor 96 | listener has inferred type sensor_listener 97 | #s src/main.rs | 47 | 53 | 47 | 61 98 | #i start_sensor_listener | listener | sensor::register_listener | listener | sensor_listener 99 | #s src/main.rs | 47 | 38 | 47 | 61 100 | #s src/main.rs | 47 | 12 | 47 | 62 101 | #s src/main.rs | 47 | 12 | 47 | 62 102 | #s src/main.rs | 47 | 12 | 47 | 64 103 | #s src/main.rs | 45 | 39 | 48 | 9 104 | #s src/main.rs | 45 | 8 | 48 | 9 105 | #s src/main.rs | 49 | 8 | 49 | 14 106 | #s src/main.rs | 49 | 8 | 49 | 14 107 | #s src/main.rs | 49 | 8 | 49 | 14 108 | fname: "Ok" 109 | #s src/main.rs | 49 | 8 | 49 | 14 110 | #s src/main.rs | 42 | 90 | 50 | 5 111 | save_decls: test.json, "{\"start_sensor_listener\":[[\"sensor\",\"&Strn\"],[\"sensor_type\",\"sensor_type_t\"],[\"poll_time\",\"u32\"]],\"send_sensor_data\":[[\"sensor_data\",\"&SensorValue\"]]}" 112 | successfully wrote to test.json 113 | fname: "handle_sensor_data2" 114 | para: "sensor_data" 115 | #s src/main.rs | 55 | 27 | 55 | 38 116 | #s src/main.rs | 55 | 63 | 58 | 5 117 | #s src/main.rs | 56 | 8 | 56 | 41 118 | #s src/main.rs | 56 | 8 | 56 | 39 119 | #s src/main.rs | 56 | 8 | 56 | 37 120 | #s src/main.rs | 56 | 8 | 56 | 37 121 | fname: "send_sensor_data" 122 | #m send_sensor_data | src/main.rs | 56 | 8 | 56 | 37 123 | #s src/main.rs | 56 | 25 | 56 | 36 124 | sensor_data has inferred type &SensorValue 125 | #s src/main.rs | 56 | 25 | 56 | 36 126 | #i handle_sensor_data2 | sensor_data | send_sensor_data | sensor_data | &SensorValue 127 | #s src/main.rs | 56 | 25 | 56 | 36 128 | #s src/main.rs | 56 | 8 | 56 | 37 129 | #s src/main.rs | 56 | 8 | 56 | 37 130 | #s src/main.rs | 56 | 8 | 56 | 39 131 | #s src/main.rs | 57 | 8 | 57 | 14 132 | #s src/main.rs | 57 | 8 | 57 | 14 133 | #s src/main.rs | 57 | 8 | 57 | 14 134 | fname: "Ok" 135 | #s src/main.rs | 57 | 8 | 57 | 14 136 | #s src/main.rs | 55 | 63 | 58 | 5 137 | save_decls: test.json, "{\"handle_sensor_data2\":[[\"sensor_data\",\"&SensorValue\"]],\"send_sensor_data\":[[\"sensor_data\",\"&SensorValue\"]]}" 138 | successfully wrote to test.json 139 | fname: "send_sensor_data" 140 | para: "sensor_data" 141 | #s src/main.rs | 63 | 24 | 63 | 35 142 | #s src/main.rs | 63 | 60 | 74 | 5 143 | #s src/main.rs | 64 | 8 | 64 | 59 144 | #s src/main.rs | 64 | 24 | 64 | 57 145 | #s src/main.rs | 64 | 24 | 64 | 55 146 | #s src/main.rs | 64 | 24 | 64 | 55 147 | fname: "sensor_network::get_device_id" 148 | #s src/main.rs | 64 | 24 | 64 | 55 149 | #s src/main.rs | 64 | 24 | 64 | 57 150 | #s src/main.rs | 65 | 8 | 65 | 78 151 | #s src/main.rs | 65 | 28 | 65 | 76 152 | #s src/main.rs | 65 | 28 | 65 | 74 153 | #s src/main.rs | 65 | 28 | 65 | 74 154 | fname: "sensor_network::init_server_post" 155 | #s src/main.rs | 65 | 28 | 65 | 74 156 | #s src/main.rs | 65 | 28 | 65 | 76 157 | #s src/main.rs | 66 | 8 | 72 | 9 158 | #s src/main.rs | 66 | 8 | 72 | 9 159 | #s src/main.rs | 66 | 11 | 66 | 24 160 | #s src/main.rs | 66 | 11 | 66 | 24 161 | #s src/main.rs | 66 | 25 | 72 | 9 162 | #s src/main.rs | 67 | 12 | 70 | 15 163 | #s src/main.rs | 67 | 26 | 70 | 14 164 | #s src/main.rs | 67 | 26 | 70 | 14 165 | #m coap | src/main.rs | 67 | 26 | 70 | 14 166 | sensor_data has inferred type &SensorValue 167 | #i send_sensor_data | sensor_data | coap | singleton | &SensorValue 168 | #s src/main.rs | 67 | 26 | 70 | 14 169 | #s src/main.rs | 67 | 26 | 70 | 14 170 | #s src/main.rs | 71 | 12 | 71 | 48 171 | #s src/main.rs | 71 | 12 | 71 | 46 172 | #s src/main.rs | 71 | 12 | 71 | 44 173 | #s src/main.rs | 71 | 12 | 71 | 44 174 | fname: "sensor_network::do_server_post" 175 | #s src/main.rs | 71 | 12 | 71 | 44 176 | #s src/main.rs | 71 | 12 | 71 | 46 177 | #s src/main.rs | 66 | 25 | 72 | 9 178 | #s src/main.rs | 66 | 8 | 72 | 9 179 | #s src/main.rs | 73 | 8 | 73 | 14 180 | #s src/main.rs | 73 | 8 | 73 | 14 181 | #s src/main.rs | 73 | 8 | 73 | 14 182 | fname: "Ok" 183 | #s src/main.rs | 73 | 8 | 73 | 14 184 | #s src/main.rs | 63 | 60 | 74 | 5 185 | save_decls: test.json, "{\"send_sensor_data\":[[\"sensor_data\",\"&SensorValue\"]]}" 186 | successfully wrote to test.json 187 | error[E0308]: mismatched types 188 | --> src/main.rs:67:27 189 | | 190 | 67 | let payload = coap!( @json { 191 | | ___________________________^ 192 | 68 | | "device": &device_id, 193 | 69 | | sensor_data, 194 | 70 | | }); 195 | | |______________^ expected i8, found u8 196 | | 197 | = note: expected type `*const i8` 198 | found type `*const u8` 199 | = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) 200 | 201 | error[E0308]: mismatched types 202 | --> src/main.rs:67:27 203 | | 204 | 67 | let payload = coap!( @json { 205 | | ___________________________^ 206 | 68 | | "device": &device_id, 207 | 69 | | sensor_data, 208 | 70 | | }); 209 | | |______________^ expected i8, found u8 210 | | 211 | = note: expected type `*const i8` 212 | found type `*const u8` 213 | = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) 214 | 215 | error[E0308]: mismatched types 216 | --> src/main.rs:67:27 217 | | 218 | 67 | let payload = coap!( @json { 219 | | ___________________________^ 220 | 68 | | "device": &device_id, 221 | 69 | | sensor_data, 222 | 70 | | }); 223 | | |______________^ expected i8, found u8 224 | | 225 | = note: expected type `*const i8` 226 | found type `*const u8` 227 | = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) 228 | 229 | error[E0308]: mismatched types 230 | --> src/main.rs:67:27 231 | | 232 | 67 | let payload = coap!( @json { 233 | | ___________________________^ 234 | 68 | | "device": &device_id, 235 | 69 | | sensor_data, 236 | 70 | | }); 237 | | |______________^ expected i8, found u8 238 | | 239 | = note: expected type `*const i8` 240 | found type `*const u8` 241 | = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) 242 | 243 | error[E0308]: mismatched types 244 | --> src/main.rs:67:27 245 | | 246 | 67 | let payload = coap!( @json { 247 | | ___________________________^ 248 | 68 | | "device": &device_id, 249 | 69 | | sensor_data, 250 | 70 | | }); 251 | | |______________^ expected i8, found u8 252 | | 253 | = note: expected type `*const i8` 254 | found type `*const u8` 255 | = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) 256 | 257 | error[E0308]: mismatched types 258 | --> src/main.rs:67:27 259 | | 260 | 67 | let payload = coap!( @json { 261 | | ___________________________^ 262 | 68 | | "device": &device_id, 263 | 69 | | sensor_data, 264 | 70 | | }); 265 | | |______________^ expected i8, found u8 266 | | 267 | = note: expected type `*const i8` 268 | found type `*const u8` 269 | = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) 270 | 271 | error[E0308]: mismatched types 272 | --> src/main.rs:67:27 273 | | 274 | 67 | let payload = coap!( @json { 275 | | ___________________________^ 276 | 68 | | "device": &device_id, 277 | 69 | | sensor_data, 278 | 70 | | }); 279 | | |______________^ expected i8, found u8 280 | | 281 | = note: expected type `*const i8` 282 | found type `*const u8` 283 | = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) 284 | 285 | error: aborting due to 7 previous errors 286 | 287 | For more information about this error, try `rustc --explain E0308`. 288 | error: Could not compile `test-rust-macros`. 289 | 290 | To learn more, run the command again with --verbose. 291 | The terminal process terminated with exit code: 101 292 | 293 | Terminal will be reused by tasks, press any key to close it. 294 | -------------------------------------------------------------------------------- /resources/template.rs: -------------------------------------------------------------------------------- 1 | // -- BEGIN BLOCKS --countcount0my_label5my_buttonVisual Rust5my_labelcountmy_buttoncountADD1count1-- END BLOCKS -- -------------------------------------------------------------------------------- /src/declarations.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | 5 | // Tree of nodes 6 | let tree: any = { 7 | "To Be Inferred": { 8 | //"start_sensor_listener" : [ ["sensor", "_"], ["sensor_type", "_"], ["poll_time", "_"] ], 9 | "start_sensor_listener" : { "sensor": "_", "sensor_type": "_", "poll_time": "_" }, 10 | }, 11 | "Mynewt API": { 12 | "sensor::set_poll_rate_ms" : { "devname": "&Strn", "poll_rate": "u32" }, 13 | "sensor::mgr_find_next_bydevname" : { "devname": "&Strn", "prev_cursor": "*mut sensor" }, 14 | "sensor::register_listener" : { "sensor": "*mut sensor", "listener": "sensor_listener" }, 15 | "new_sensor_listener" : { "sl_sensor_type": "sensor_type_t", "sl_func": "sensor_data_func" } 16 | }, 17 | }; 18 | 19 | const pendingKey = Object.keys(tree)[0]; 20 | const knownKey = Object.keys(tree)[1]; 21 | let pendingNode: Node | undefined = undefined; 22 | let knownNode: Node | undefined = undefined; 23 | 24 | export function setPendingValue(pathkey: string, value: any) { 25 | // Set the value of this pending node. Return true if successfully set. 26 | const pathSplit = pathkey.split('|'); 27 | const parentPath = pathSplit.slice(0, -1).join('|'); 28 | const key = pathSplit[pathSplit.length - 1]; 29 | const parentTree = getTreeElement([pendingKey, parentPath].join('|')); 30 | // console.log('setPendingValue: ' + pathkey + ' / ' + JSON.stringify(parentTree)); 31 | if (!parentTree || parentTree[key] === undefined) { return false; } 32 | parentTree[key] = value; 33 | 34 | const node = getNode([pendingKey, pathkey].join('|')); 35 | if (node) { node.value = value; } 36 | 37 | if (provider) { provider.refresh(); } 38 | return true; 39 | } 40 | 41 | export function markPending(pathkey: string) { 42 | // Mark this pending node. 43 | const node = getNode([pendingKey, pathkey].join('|')); 44 | if (!node) { return; } 45 | 46 | // Unmark the previous pending node. 47 | if (pendingNode) { pendingNode.prefix = '✅ '; } 48 | pendingNode = node; 49 | 50 | // Mark the known node and refresh the display. 51 | node.prefix = '▶️ '; 52 | if (provider) { provider.refresh(); } 53 | } 54 | 55 | export function markKnown(pathkey: string) { 56 | // Mark this known node. 57 | const node = getNode([knownKey, pathkey].join('|')); 58 | if (!node) { return; } 59 | 60 | // Unmark the previous known node. 61 | if (knownNode) { knownNode.prefix = ''; } 62 | knownNode = node; 63 | 64 | // Mark the known node and refresh the display. 65 | node.prefix = '▶️ '; 66 | if (provider) { provider.refresh(); } 67 | } 68 | 69 | // List of nodes indexed by path e.g. `Mynewt API|sensor::set_poll_rate_ms|devname` 70 | let nodes: {[path: string]: Node} = {}; 71 | 72 | // Each node of the tree. `key` looks like ` 73 | class Node extends vscode.TreeItem { 74 | constructor( 75 | public readonly pathkey: string, 76 | public readonly key: string, 77 | public value: any, 78 | public prefix: string, 79 | public readonly collapsibleState: vscode.TreeItemCollapsibleState, 80 | public readonly command?: vscode.Command 81 | ) { 82 | super(`${prefix}${key}${(value === undefined) ? '' : (': ' + value)}`, collapsibleState); 83 | } 84 | 85 | set label(_: string) {} 86 | 87 | get label(): string { 88 | const { key, value, prefix } = this; 89 | return `${prefix}${key}${(value === undefined) ? '' : (': ' + value)}`; 90 | } 91 | 92 | get tooltip(): string { 93 | return `${this.prefix}${this.key}`; 94 | } 95 | 96 | get description(): string { 97 | return ''; 98 | } 99 | 100 | get icon(): string { 101 | return (this.collapsibleState === vscode.TreeItemCollapsibleState.None) 102 | ? 'string.svg' // Icon for no children 103 | : 'folder.svg'; // Icon for children 104 | } 105 | 106 | iconPath = { 107 | light: path.join(__filename, '..', '..', 'resources', 'light', this.icon), 108 | dark: path.join(__filename, '..', '..', 'resources', 'dark', this.icon) 109 | }; 110 | 111 | contextValue = 'Node'; 112 | } 113 | 114 | function getChildren(pathkey: string): string[] { 115 | // Return the paths of the child nodes. 116 | if (!pathkey) { 117 | return Object.keys(tree); 118 | } 119 | let treeElement = getTreeElement(pathkey); 120 | if (treeElement && typeof treeElement === 'object') { 121 | // Get the child keys. 122 | const childKeys = Object.keys(treeElement); 123 | // console.log('getChildren: ' + pathkey + JSON.stringify(treeElement)); 124 | // Append the child keys to the parent path. 125 | return childKeys.map(key => pathkey + '|' + key); 126 | } 127 | return []; 128 | } 129 | 130 | function getTreeElement(pathkey: string): any { 131 | // Return the subtree for the path e.g. `Mynewt API|sensor::set_poll_rate_ms|devname` 132 | // console.log('getTreeElement ' + pathkey); 133 | let parent = tree; 134 | // Split by `|` and walk the tree. 135 | let pathSplit = pathkey.split('|'); 136 | for (;;) { 137 | let key = pathSplit.shift(); 138 | if (key === undefined) { return null; } 139 | // console.log('getTreeElement key=' + key + ', parent=' + JSON.stringify(parent)); 140 | parent = parent[key]; 141 | if (parent === undefined) { return null; } 142 | if (pathSplit.length === 0) { return parent; } 143 | } 144 | return null; 145 | } 146 | 147 | function getNode(pathkey: string): Node { 148 | if (!nodes[pathkey]) { 149 | // Key is the last part of the path. 150 | let pathSplit = pathkey.split('|'); 151 | let key = pathSplit[pathSplit.length - 1]; 152 | let prefix = ''; 153 | const treeElement = getTreeElement(pathkey); 154 | // If this is a key/value, get the value. 155 | let value: any = undefined; 156 | if (treeElement && typeof treeElement !== 'object') { value = treeElement; } 157 | const collapsibleState = 158 | (treeElement && typeof treeElement === 'object' && Object.keys(treeElement).length) 159 | ? vscode.TreeItemCollapsibleState.Collapsed 160 | : vscode.TreeItemCollapsibleState.None; 161 | nodes[pathkey] = new Node(pathkey, key, value, prefix, collapsibleState); 162 | } 163 | return nodes[pathkey]; 164 | } 165 | 166 | class DeclarationsProvider implements vscode.TreeDataProvider { 167 | 168 | private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); 169 | readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; 170 | 171 | getChildren(element?: Node): Thenable { 172 | const children = getChildren(element ? element.pathkey : "") 173 | .map(key => getNode(key)); 174 | return Promise.resolve(children); 175 | } 176 | 177 | getTreeItem(element: Node): vscode.TreeItem { 178 | const treeItem = getNode(element.pathkey); 179 | treeItem.id = element.pathkey; 180 | return treeItem; 181 | } 182 | 183 | refresh(): void { 184 | this._onDidChangeTreeData.fire(); 185 | } 186 | 187 | constructor(private workspaceRoot: string) {} 188 | } 189 | 190 | let provider: DeclarationsProvider | undefined = undefined; 191 | let treeView: vscode.TreeView | undefined = undefined; 192 | 193 | // Called when VSCode is activated 194 | export function activate(context: vscode.ExtensionContext) { 195 | // Create the Tree View. 196 | provider = new DeclarationsProvider(vscode.workspace.rootPath || ''); 197 | treeView = vscode.window.createTreeView('visualEmbeddedRustDeclarations', { 198 | treeDataProvider: provider 199 | }); 200 | 201 | // Register the commands. 202 | vscode.commands.registerCommand('visualEmbeddedRustDeclarations.refreshEntry', 203 | () => provider ? provider.refresh() : null); 204 | 205 | vscode.commands.registerCommand('extension.openPackageOnNpm', 206 | moduleName => vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`https://www.npmjs.com/package/${moduleName}`))); 207 | 208 | vscode.commands.registerCommand('visualEmbeddedRustDeclarations.addEntry', 209 | () => vscode.window.showInformationMessage(`Successfully called add entry.`)); 210 | 211 | vscode.commands.registerCommand('visualEmbeddedRustDeclarations.editEntry', 212 | (node: Node) => vscode.window.showInformationMessage(`Successfully called edit entry on ${node.label}.`)); 213 | 214 | vscode.commands.registerCommand('visualEmbeddedRustDeclarations.deleteEntry', 215 | (node: Node) => vscode.window.showInformationMessage(`Successfully called delete entry on ${node.label}.`)); 216 | } -------------------------------------------------------------------------------- /src/decorate.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | /* 4 | // create a decorator type that we use to decorate small numbers 5 | const smallNumberDecorationType = vscode.window.createTextEditorDecorationType({ 6 | borderWidth: '1px', 7 | borderStyle: 'solid', 8 | overviewRulerColor: 'blue', 9 | overviewRulerLane: vscode.OverviewRulerLane.Right, 10 | light: { 11 | // this color will be used in light color themes 12 | borderColor: 'darkblue' 13 | }, 14 | dark: { 15 | // this color will be used in dark color themes 16 | borderColor: 'lightblue' 17 | } 18 | }); 19 | 20 | // create a decorator type that we use to decorate large numbers 21 | const largeNumberDecorationType = vscode.window.createTextEditorDecorationType({ 22 | cursor: 'crosshair', 23 | // use a themable color. See package.json for the declaration and default values. 24 | backgroundColor: { id: 'visualEmbeddedRust.largeNumberBackground' } 25 | }); 26 | */ 27 | 28 | // create a decorator type using a themable color. See package.json for the declaration and default values. 29 | const decorationTypes = [ 30 | vscode.window.createTextEditorDecorationType({ 31 | cursor: 'crosshair', 32 | backgroundColor: { id: 'visualEmbeddedRust.background0' } 33 | }), 34 | vscode.window.createTextEditorDecorationType({ 35 | cursor: 'crosshair', 36 | backgroundColor: { id: 'visualEmbeddedRust.background1' } 37 | }), 38 | ]; 39 | 40 | export function decorate(editor: vscode.TextEditor, color: number, startLine: number, startCol: number, endLine: number, endCol: number) { 41 | // Apply decoration to the active editor. All numbers are zero-based. 42 | if (!editor) { return; } 43 | const decorationOptions: vscode.DecorationOptions[][] = [ 44 | [], [] 45 | ]; 46 | const startPos = new vscode.Position(startLine, startCol); 47 | const endPos = new vscode.Position(endLine, endCol); 48 | const decoration = { 49 | range: new vscode.Range(startPos, endPos), 50 | hoverMessage: '' 51 | }; 52 | decorationOptions[color].push(decoration); 53 | for (let i = 0; i < decorationTypes.length; i++) { 54 | editor.setDecorations(decorationTypes[i], decorationOptions[i]); 55 | } 56 | } 57 | 58 | // Called when vs code is activated 59 | export function activate(context: vscode.ExtensionContext) { 60 | // console.log('decorate is activated'); 61 | 62 | /* 63 | let timeout: NodeJS.Timer | undefined = undefined; 64 | 65 | let activeEditor = vscode.window.activeTextEditor; 66 | 67 | if (activeEditor) { 68 | // triggerUpdateDecorations(); 69 | } 70 | 71 | vscode.window.onDidChangeActiveTextEditor(editor => { 72 | activeEditor = editor; 73 | if (editor) { 74 | // triggerUpdateDecorations(); 75 | } 76 | }, null, context.subscriptions); 77 | 78 | vscode.workspace.onDidChangeTextDocument(event => { 79 | if (activeEditor && event.document === activeEditor.document) { 80 | // triggerUpdateDecorations(); 81 | } 82 | }, null, context.subscriptions); 83 | 84 | function updateDecorations() { 85 | if (!activeEditor) { 86 | return; 87 | } 88 | const regEx = /\d+/g; 89 | const text = activeEditor.document.getText(); 90 | const smallNumbers: vscode.DecorationOptions[] = []; 91 | const largeNumbers: vscode.DecorationOptions[] = []; 92 | let match; 93 | while (match = regEx.exec(text)) { 94 | const startPos = activeEditor.document.positionAt(match.index); 95 | const endPos = activeEditor.document.positionAt(match.index + match[0].length); 96 | const decoration = { range: new vscode.Range(startPos, endPos), hoverMessage: 'Number **' + match[0] + '**' }; 97 | if (match[0].length < 3) { 98 | largeNumbers.push(decoration); 99 | } else { 100 | largeNumbers.push(decoration); 101 | } 102 | } 103 | activeEditor.setDecorations(smallNumberDecorationType, smallNumbers); 104 | activeEditor.setDecorations(largeNumberDecorationType, largeNumbers); 105 | } 106 | 107 | 108 | function triggerUpdateDecorations() { 109 | if (timeout) { 110 | clearTimeout(timeout); 111 | timeout = undefined; 112 | } 113 | timeout = setTimeout(updateDecorations, 500); 114 | } 115 | */ 116 | } 117 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | // Need to install into `media` folder: `blockly-mynewt-rust`, `closure-library` 2 | 3 | // The module 'vscode' contains the VS Code extensibility API 4 | // Import the module and reference it with the alias vscode in your code below 5 | import * as vscode from 'vscode'; 6 | import * as fs from 'fs'; 7 | import * as path from 'path'; 8 | import * as web from './web'; 9 | import * as decorate from './decorate'; 10 | import * as declarations from './declarations'; 11 | import * as replay from './replay'; 12 | 13 | const cats = { 14 | 'Coding Cat': 'https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif', 15 | 'Compiling Cat': 'https://media.giphy.com/media/mlvseq9yvZhba/giphy.gif', 16 | 'Testing Cat': 'https://media.giphy.com/media/3oriO0OEd9QIDdllqo/giphy.gif' 17 | }; 18 | 19 | // Called when extension is activated, the very first time the command is executed 20 | export function activate(context: vscode.ExtensionContext) { 21 | console.log('visual-embedded-rust active: '); // console.log(context); 22 | // vscode.window.showInformationMessage('To edit visually, click the Rust source file'); 23 | 24 | context.subscriptions.push( 25 | vscode.commands.registerCommand('visualEmbeddedRust.start', () => { 26 | CatCodingPanel.createOrShow(context.extensionPath); 27 | }) 28 | ); 29 | 30 | context.subscriptions.push( 31 | vscode.commands.registerCommand('visualEmbeddedRust.doRefactor', () => { 32 | if (CatCodingPanel.currentPanel) { 33 | CatCodingPanel.currentPanel.doRefactor(); 34 | } 35 | }) 36 | ); 37 | 38 | if (vscode.window.registerWebviewPanelSerializer) { 39 | // Make sure we register a serializer in activation event 40 | vscode.window.registerWebviewPanelSerializer(CatCodingPanel.viewType, { 41 | async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: any) { 42 | console.log(`Got state: ${state}`); 43 | CatCodingPanel.revive(webviewPanel, context.extensionPath); 44 | } 45 | }); 46 | } 47 | 48 | // The command has been defined in the package.json file 49 | // Now provide the implementation of the command with registerCommand 50 | // The commandId parameter must match the command field in package.json 51 | let disposable = vscode.commands.registerCommand('visualEmbeddedRust.helloWorld', () => { 52 | // The code you place here will be executed every time your command is executed 53 | 54 | // Display a message box to the user 55 | vscode.window.showInformationMessage('Hello World!'); 56 | }); 57 | 58 | context.subscriptions.push(disposable); 59 | 60 | // To enable replay, the following must be enabled... 61 | 62 | // Activate the decorators. 63 | // decorate.activate(context); 64 | 65 | // Activate the declaration view. 66 | // declarations.activate(context); 67 | 68 | // Disabled the replay log. 69 | // replay.activate(context); 70 | } 71 | 72 | /** 73 | * Manages cat coding webview panels 74 | */ 75 | class CatCodingPanel { 76 | /** 77 | * Track the currently panel. Only allow a single panel to exist at a time. 78 | */ 79 | public static currentPanel: CatCodingPanel | undefined; 80 | 81 | public static readonly viewType = 'visualEmbeddedRust'; 82 | 83 | private readonly _panel: vscode.WebviewPanel; 84 | private readonly _extensionPath: string; 85 | private _disposables: vscode.Disposable[] = []; 86 | private _editor: vscode.TextEditor | undefined; 87 | 88 | public static createOrShow(extensionPath: string) { 89 | console.log('createOrShow'); 90 | const column = (vscode.window.activeTextEditor && vscode.window.activeTextEditor.viewColumn) 91 | ? (vscode.window.activeTextEditor.viewColumn + 1) 92 | : undefined; 93 | 94 | // If we already have a panel, show it. 95 | if (CatCodingPanel.currentPanel) { 96 | CatCodingPanel.currentPanel._panel.reveal(column); 97 | return; 98 | } 99 | 100 | // Otherwise, create a new panel. 101 | const panel = vscode.window.createWebviewPanel( 102 | CatCodingPanel.viewType, 103 | 'Cat Coding', 104 | column || vscode.ViewColumn.One, 105 | { 106 | // Enable javascript in the webview 107 | enableScripts: true, 108 | 109 | // And restrict the webview to only loading content from our extension's `media` directory. 110 | localResourceRoots: [vscode.Uri.file(path.join(extensionPath, 'media'))] 111 | } 112 | ); 113 | 114 | CatCodingPanel.currentPanel = new CatCodingPanel(panel, extensionPath); 115 | } 116 | 117 | public static revive(panel: vscode.WebviewPanel, extensionPath: string) { 118 | console.log('revive'); 119 | CatCodingPanel.currentPanel = new CatCodingPanel(panel, extensionPath); 120 | } 121 | 122 | private constructor(panel: vscode.WebviewPanel, extensionPath: string) { 123 | this._panel = panel; 124 | this._extensionPath = extensionPath; 125 | 126 | // Set the webview's initial html content 127 | this._update(); 128 | 129 | // Listen for when the panel is disposed 130 | // This happens when the user closes the panel or when the panel is closed programatically 131 | this._panel.onDidDispose(() => this.dispose(), null, this._disposables); 132 | 133 | // Update the content based on view changes 134 | this._panel.onDidChangeViewState( 135 | e => { 136 | console.log('onDidChangeViewState'); 137 | if (this._panel.visible) { 138 | this._update(); 139 | } 140 | }, 141 | null, 142 | this._disposables 143 | ); 144 | 145 | // Handle messages from the webview 146 | this._panel.webview.onDidReceiveMessage( 147 | message => { 148 | console.log(JSON.stringify({onDidReceiveMessage: JSON.stringify(message).substr(0, 50)})); 149 | switch (message.command) { 150 | case 'alert': { 151 | vscode.window.showErrorMessage(message.text); 152 | return; 153 | } 154 | 155 | // Restore code blocks. Read the contexts of the active text editor and send to webview to load. 156 | case 'restoreBlocks': { 157 | // Get the active text editor. If none active, return the last active one. 158 | let editor = vscode.window.activeTextEditor; 159 | if (!editor || !CatCodingPanel._isValidEditor(editor)) { 160 | editor = this._editor; 161 | if (!editor || !CatCodingPanel._isValidEditor(editor)) { 162 | editor = vscode.window.visibleTextEditors[0]; 163 | if (!editor || !CatCodingPanel._isValidEditor(editor)) { 164 | console.log('No active editor'); return; 165 | } 166 | } 167 | } 168 | const webview = this._panel.webview; 169 | 170 | // Send a `loadDoc` message to our webview with the text. 171 | function loadDoc(text0: string) { 172 | webview.postMessage({ 173 | command: 'loadDoc', 174 | text: text0, 175 | }); 176 | } 177 | 178 | // Get the text of the doc. If file is not empty, send a `loadDoc` message to our webview with the text. 179 | const text = editor.document.getText(); 180 | if (text.length > 0) { 181 | loadDoc(text); 182 | // Remember the active text editor. We will return this at the next call. 183 | this._editor = editor; 184 | return; 185 | } 186 | 187 | // If file is empty, populate with template. 188 | vscode.window.showInformationMessage('Populate empty Rust file with Visual Embedded Rust program?', 'OK', 'Cancel') 189 | .then(selected => { 190 | if (selected !== 'OK') { return; } 191 | if (!editor) { return; } 192 | const replayPath = path.join(__filename, '..', '..', 'resources', 'template.rs'); 193 | const buf = fs.readFileSync(replayPath); 194 | const template = buf.toString(); 195 | editor.edit(editBuilder => { 196 | // Populate the template and notify webview. 197 | editBuilder.insert(new vscode.Position(0, 0), template); 198 | loadDoc(template); 199 | }); 200 | }); 201 | } 202 | 203 | // Update the Visual Rust document with the generated Rust code and the updated blocks XML. 204 | case 'updateDoc': { 205 | const newText = message.text; 206 | let editor = this._editor; 207 | if (!editor || !CatCodingPanel._isValidEditor(editor)) { console.log('No editor to update'); return; } 208 | editor.edit(editBuilder => { 209 | // Get the range of the entire doc. 210 | if (!editor) { console.log('Missing editor'); return; } 211 | const document = editor.document; 212 | const text = document.getText(); 213 | if (!text) { console.log('Missing text'); return; } 214 | const range = new vscode.Range( 215 | new vscode.Position(0, 0), 216 | document.positionAt(text.length) 217 | ); 218 | // Replace the range by the new text. 219 | editBuilder.replace(range, newText); 220 | }); 221 | return; 222 | } 223 | 224 | // Show an OK/Cancel confirmation message. Post the result (true for OK) back to WebView. 225 | case 'confirm': { 226 | const msg = message.message; 227 | vscode.window.showInformationMessage(msg, 'OK', 'Cancel') 228 | .then(selected => this._panel.webview.postMessage({ 229 | command: 'confirmResult', 230 | result: (selected === 'OK'), 231 | })); 232 | return; 233 | } 234 | 235 | // Prompt for input. Post the result back to WebView. 236 | case 'prompt': { 237 | const msg = message.message; 238 | const defaultValue = message.defaultValue; 239 | vscode.window.showInputBox({ 240 | prompt: message, 241 | value: defaultValue, 242 | }) 243 | .then(result => this._panel.webview.postMessage({ 244 | command: 'promptResult', 245 | result: result, 246 | })); 247 | return; 248 | } 249 | 250 | default: console.error('Unknown message: ' + JSON.stringify(message)); 251 | } 252 | }, 253 | null, 254 | this._disposables 255 | ); 256 | } 257 | 258 | public doRefactor() { 259 | // Send a message to the webview webview. 260 | // You can send any JSON serializable data. 261 | this._panel.webview.postMessage({ command: 'refactor' }); 262 | } 263 | 264 | public dispose() { 265 | CatCodingPanel.currentPanel = undefined; 266 | 267 | // Clean up our resources 268 | this._panel.dispose(); 269 | 270 | while (this._disposables.length) { 271 | const x = this._disposables.pop(); 272 | if (x) { 273 | x.dispose(); 274 | } 275 | } 276 | } 277 | 278 | private static _isValidEditor(editor: vscode.TextEditor): boolean { 279 | // Return true if this is a valid TextEditor with a valid *.rs Visual Rust program. 280 | // If filename is not *.rs, reuse the last active editor. 281 | if (!editor.document) { console.log('Missing document'); return false; } 282 | const filename = editor.document.fileName; 283 | if (!filename) { console.log('Missing filename'); return false; } 284 | if (!filename.endsWith(".rs") && !filename.endsWith(".RS")) { console.log('Not a .rs file'); return false; } 285 | 286 | // Get the text of the doc. 287 | // const text = editor.document.getText(); 288 | // if (!text) { console.log('Missing text'); return false; } 289 | return true; 290 | } 291 | 292 | private _update() { 293 | const z = 1 + 2; 294 | // Vary the webview's content based on where it is located in the editor. 295 | switch (this._panel.viewColumn) { 296 | case vscode.ViewColumn.Two: 297 | this._updateForCat('Compiling Cat'); 298 | return; 299 | 300 | case vscode.ViewColumn.Three: 301 | this._updateForCat('Testing Cat'); 302 | return; 303 | 304 | case vscode.ViewColumn.One: 305 | default: 306 | this._updateForCat('Coding Cat'); 307 | return; 308 | } 309 | } 310 | 311 | private _updateForCat(catName: keyof typeof cats) { 312 | this._panel.title = 'Visual Embedded Rust'; 313 | this._panel.webview.html = this._getHtmlForWebview(cats[catName]); 314 | } 315 | 316 | private _getHtmlForWebview(catGif: string) { 317 | // Local path to main script run in the webview 318 | const scriptPathOnDisk = vscode.Uri.file( 319 | path.join(this._extensionPath, 'media') 320 | ); 321 | // Parameters for the HTML 322 | const scriptUri = scriptPathOnDisk.with({ scheme: 'vscode-resource' }); 323 | const para = { 324 | // URIs we use to load this script in the webview 325 | vscodeUri: scriptUri + '/vscode', // VSCode integration scripts 326 | blocklyUri: scriptUri + '/blockly-mynewt-rust', // Blockly scripts 327 | 328 | // Use a nonce to whitelist which scripts can be run 329 | nonce: getNonce(), 330 | }; 331 | // Return the HTML with the parameters embedded. 332 | return web.getHtml(para); 333 | } 334 | } 335 | 336 | function getNonce() { 337 | let text = ''; 338 | const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 339 | for (let i = 0; i < 32; i++) { 340 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 341 | } 342 | return text; 343 | } 344 | 345 | // this method is called when your extension is deactivated 346 | export function deactivate() {} 347 | -------------------------------------------------------------------------------- /src/replay.ts: -------------------------------------------------------------------------------- 1 | // Replay the recorded inference log 2 | import * as vscode from 'vscode'; 3 | import * as fs from 'fs'; 4 | import * as path from 'path'; 5 | import * as decorate from './decorate'; 6 | import * as declarations from './declarations'; 7 | 8 | // List of log entries to replay 9 | let replayLog: string[] = []; 10 | 11 | // List of interpolated spans to replay 12 | let interpolatedSpans: number[][] = []; 13 | 14 | // Span color: 0 for red, 1 for green 15 | let spanColor: number = 0; 16 | 17 | // Called when VSCode is activated 18 | export function activate(context: vscode.ExtensionContext) { 19 | console.log('replay is activated'); 20 | 21 | // Trigger the replay when document is active. 22 | let timeout: NodeJS.Timer | undefined = undefined; 23 | let activeEditor = vscode.window.activeTextEditor; 24 | if (activeEditor) { triggerReplay(); } 25 | 26 | vscode.window.onDidChangeActiveTextEditor(editor => { 27 | activeEditor = editor; 28 | if (editor) { triggerReplay(); } 29 | }, null, context.subscriptions); 30 | 31 | vscode.workspace.onDidChangeTextDocument(event => { 32 | if (activeEditor && event.document === activeEditor.document) { triggerReplay(); } 33 | }, null, context.subscriptions); 34 | 35 | function triggerReplay() { 36 | if (timeout) { 37 | clearInterval(timeout); 38 | timeout = undefined; 39 | } 40 | // Don't replay for empty Rust file. 41 | if (activeEditor && activeEditor.document.getText().length === 0) { 42 | console.log('Skipping replay for empty document'); 43 | return; 44 | } 45 | // Don't replay for generated Rust code. 46 | if (activeEditor && activeEditor.document.getText().indexOf('BEGIN BLOCKS') >= 0) { 47 | console.log('Skipping replay for document with BEGIN BLOCKS'); 48 | return; 49 | } 50 | // Read the entire replay log and break into lines. 51 | if (replayLog.length === 0) { 52 | const replayPath = path.join(__filename, '..', '..', 'resources', 'replay.log'); 53 | const buf = fs.readFileSync(replayPath); 54 | const log = buf.toString(); 55 | replayLog = log.split('\n'); 56 | console.log('replay read log: ' + replayLog.length); 57 | } 58 | timeout = setInterval(() => { 59 | if (activeEditor) { replay(activeEditor); } 60 | }, 200); 61 | } 62 | } 63 | 64 | // Remember the last span rendered 65 | let lastStartRow: number = 0; 66 | let lastStartCol: number = 0; 67 | let lastEndRow: number = 0; 68 | let lastEndCol: number = 0; 69 | 70 | function replay(editor: vscode.TextEditor) { 71 | // Replay one line of the log. 72 | if (!editor) { return; } 73 | // If there are interpolated spans, replay them. 74 | if (interpolatedSpans.length > 0) { 75 | replayInterpolatedSpan(editor); 76 | return; 77 | } 78 | for (;;) { 79 | // Look for replay lines starting with "#". 80 | if (replayLog.length === 0) { return; } 81 | const line = replayLog.shift(); 82 | if (line === undefined || !line.startsWith('#')) { continue; } 83 | console.log('replay: ' + line); 84 | 85 | if (line.startsWith("#s")) { 86 | // Replay Span: #s src/main.rs | 43 | 8 | 43 | 51 87 | // Show the span as red. 88 | const replayed = replaySpan(editor, line, 0); 89 | if (!replayed) { continue; } // Not replayed because of duplicate, fetch next line. 90 | 91 | } else if (line.startsWith("#m")) { 92 | // Replay Match: #m sensor::set_poll_rate_ms | src/main.rs | 43 | 8 | 43 | 51 93 | // Mark the known declaration. 94 | const s = line.substr(2).split('|'); 95 | declarations.markKnown(s[2].trim()); 96 | // Show the span as green. 97 | const span = s.slice(1).join('|'); 98 | const replayed = replaySpan(editor, span, 1); 99 | 100 | } else if (line.startsWith("#i")) { 101 | // Replay Infer: #i start_sensor_listener | sensor | sensor::set_poll_rate_ms | devname | &Strn 102 | // Mark the pending and known declarations. 103 | const s = line.substr(2).split('|'); 104 | const pendingPath = [s[0].trim(), s[1].trim()].join('|'); 105 | const knownPath = [s[2].trim(), s[3].trim()].join('|'); 106 | const para = s[1].trim(); 107 | const value = s[4].trim(); 108 | const result = declarations.setPendingValue( 109 | pendingPath, 110 | value 111 | ); 112 | if (result) { vscode.window.showInformationMessage(`"${para}" was inferred as "${value}"`); } 113 | declarations.markPending( 114 | pendingPath 115 | ); 116 | declarations.markKnown( 117 | knownPath 118 | ); 119 | // Show the span as green. 120 | const span = [ 121 | '', 122 | lastStartRow + 1, 123 | lastStartCol, 124 | lastEndRow + 1, 125 | lastEndCol 126 | ].join('|'); 127 | const replayed = replaySpan(editor, span, 1); 128 | 129 | } else { continue; } 130 | break; 131 | } 132 | } 133 | 134 | function replaySpan(editor: vscode.TextEditor, line: string, color: number): boolean { 135 | // Replay Span: #s src/main.rs | 43 | 8 | 43 | 51 136 | // Return true if span has been replayed. 137 | if (!editor) { return false; } 138 | const s = line.split('|'); 139 | const startRow = parseInt(s[1]) - 1; 140 | const startCol = parseInt(s[2]); 141 | const endRow = parseInt(s[3]) - 1; 142 | const endCol = parseInt(s[4]); 143 | // If span is unchanged, fetch next line. 144 | if (startRow === lastStartRow 145 | && startCol === lastStartCol 146 | && endRow === lastEndRow 147 | && endCol === lastEndCol 148 | && spanColor === color) { 149 | return false; 150 | } 151 | // Interpolate the span into 3 intermediate spans. 152 | interpolatedSpans = interpolateSpan( 153 | lastStartRow, startRow, 154 | lastStartCol, startCol, 155 | lastEndRow, endRow, 156 | lastEndCol, endCol 157 | ); 158 | // Remember the last span. 159 | lastStartRow = startRow; 160 | lastStartCol = startCol; 161 | lastEndRow = endRow; 162 | lastEndCol = endCol; 163 | spanColor = color; 164 | // Decorate the span. 165 | // Previously: decorate.decorate(editor, startRow, startCol, endRow, endCol); 166 | replayInterpolatedSpan(editor); 167 | return true; 168 | } 169 | 170 | function replayInterpolatedSpan(editor: vscode.TextEditor) { 171 | // Replay the next interpolated span. 172 | if (!editor) { return; } 173 | if (interpolatedSpans.length === 0) { return; } 174 | const span = interpolatedSpans.shift(); 175 | if (span === undefined) { return; } 176 | decorate.decorate(editor, spanColor, span[0], span[1], span[2], span[3]); 177 | } 178 | 179 | function interpolateSpan( 180 | startRow1: number, startRow2: number, 181 | startCol1: number, startCol2: number, 182 | endRow1: number, endRow2: number, 183 | endCol1: number, endCol2: number 184 | ) { 185 | // Interpolate the span into 5 frames. 186 | const frames = 5; 187 | let result: number[][] = []; 188 | let incStartRow = (startRow2 - startRow1) / (frames * 1.0); 189 | let incStartCol = (startCol2 - startCol1) / (frames * 1.0); 190 | let incEndRow = (endRow2 - endRow1) / (frames * 1.0); 191 | let incEndCol = (endCol2 - endCol1) / (frames * 1.0); 192 | // Interpolate (n - 1) frames. 193 | for (let i = 1; i < frames; i++) { 194 | result.push([ 195 | startRow1 + Math.floor(i * incStartRow), 196 | startCol1 + Math.floor(i * incStartCol), 197 | endRow1 + Math.floor(i * incEndRow), 198 | endCol1 + Math.floor(i * incEndCol) 199 | ]); 200 | } 201 | // Push the last frame. 202 | result.push([startRow2, startCol2, endRow2, endCol2]); 203 | return result; 204 | } -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | import { runTests } from 'vscode-test'; 4 | 5 | async function main() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 17 | } catch (err) { 18 | console.error('Failed to run tests'); 19 | process.exit(1); 20 | } 21 | } 22 | 23 | main(); 24 | -------------------------------------------------------------------------------- /src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import { before } from 'mocha'; 3 | 4 | // You can import and use all API from the 'vscode' module 5 | // as well as import your extension to test it 6 | import * as vscode from 'vscode'; 7 | // import * as myExtension from '../extension'; 8 | 9 | suite('Extension Test Suite', () => { 10 | before(() => { 11 | vscode.window.showInformationMessage('Start all tests.'); 12 | }); 13 | 14 | test('Sample test', () => { 15 | assert.equal(-1, [1, 2, 3].indexOf(5)); 16 | assert.equal(-1, [1, 2, 3].indexOf(0)); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as Mocha from 'mocha'; 3 | import * as glob from 'glob'; 4 | 5 | export function run(): Promise { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: 'tdd', 9 | }); 10 | mocha.useColors(true); 11 | 12 | const testsRoot = path.resolve(__dirname, '..'); 13 | 14 | return new Promise((c, e) => { 15 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 16 | if (err) { 17 | return e(err); 18 | } 19 | 20 | // Add files to the test suite 21 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 22 | 23 | try { 24 | // Run the mocha test 25 | mocha.run(failures => { 26 | if (failures > 0) { 27 | e(new Error(`${failures} tests failed.`)); 28 | } else { 29 | c(); 30 | } 31 | }); 32 | } catch (err) { 33 | e(err); 34 | } 35 | }); 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /src/web.ts: -------------------------------------------------------------------------------- 1 | // HTML content for the web view 2 | 3 | export function getHtml(para: any) { 4 | // Return the HTML content for the web view. Derived from media/blockly-mynewt-rust/demos/code/index.html, customised for VSCode 5 | // TODO: Add security policy 6 | return ` 7 | 8 | 9 | 10 | 11 | Visual Rust for PineTime Smart Watch 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 87 | 91 | 92 | 93 | 125 | 126 | 127 | 129 | 130 |
82 |

83 | 84 | ... 85 |

86 |
88 | 89 | 90 |
94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 122 | 123 |
... Rust XML 112 | 115 | 118 | 121 |
124 |
128 |
131 |
132 |

133 |     

134 |     

135 |     

136 |     

137 |     

138 |     
139 | 
140 |     
485 | 
486 | 
487 | 		
488 | `;
489 | }
490 | 
491 | /* TODO: Set content security policy
492 | return `
493 |     
494 |     
495 |         
496 | 
497 |         
501 |         
502 | 
503 |         
504 |         Cat Coding
505 |     
506 |     
507 |         
508 |         

0

509 | 510 | 511 | 512 | `; 513 | */ -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | "strict": true /* enable all strict type-checking options */ 12 | /* Additional Checks */ 13 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 14 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 15 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | ".vscode-test" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-string-throw": true, 4 | "no-unused-expression": true, 5 | "no-duplicate-variable": true, 6 | "curly": true, 7 | "class-name": true, 8 | "semicolon": [ 9 | true, 10 | "always" 11 | ], 12 | "triple-equals": true 13 | }, 14 | "defaultSeverity": "warning" 15 | } 16 | -------------------------------------------------------------------------------- /vsc-extension-quickstart.md: -------------------------------------------------------------------------------- 1 | # Welcome to your VS Code Extension 2 | 3 | ## What's in the folder 4 | 5 | * This folder contains all of the files necessary for your extension. 6 | * `package.json` - this is the manifest file in which you declare your extension and command. 7 | * The sample plugin registers a command and defines its title and command name. With this information 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 activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. 10 | * We pass the function containing the implementation of the command as the second parameter to `registerCommand`. 11 | 12 | ## Get up and running straight away 13 | 14 | * Press `F5` to open a new window with your extension loaded. 15 | * Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`. 16 | * Set breakpoints in your code inside `src/extension.ts` to debug your extension. 17 | * Find output from your extension in the debug console. 18 | 19 | ## Make changes 20 | 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 | 25 | ## Explore the API 26 | 27 | * You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`. 28 | 29 | ## Run tests 30 | 31 | * Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`. 32 | * Press `F5` to run the tests in a new window with your extension loaded. 33 | * See the output of the test result in the debug console. 34 | * Make changes to `src/test/suite/extension.test.ts` or create new test files inside the `test/suite` folder. 35 | * The provided test runner will only consider files matching the name pattern `**.test.ts`. 36 | * You can create folders inside the `test` folder to structure your tests any way you want. 37 | 38 | ## Go further 39 | 40 | * Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/testing-extension). 41 | * [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VSCode extension marketplace. 42 | * Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration). 43 | -------------------------------------------------------------------------------- /workspace.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | }, 6 | { 7 | "path": "media/blockly-mynewt-rust" 8 | } 9 | ], 10 | "settings": { 11 | "typescript.tsc.autoDetect": "off" 12 | } 13 | } --------------------------------------------------------------------------------