├── .editorconfig ├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── LICENSE ├── README.md ├── UltiSnips └── arduino.snippets ├── autoload ├── arduino.vim └── arduino │ ├── chooser.vim │ └── job.vim ├── bin └── run-headless ├── doc └── arduino.txt ├── ftdetect └── arduino.vim ├── ftplugin └── arduino.vim ├── lua └── arduino │ ├── init.lua │ └── telescope.lua ├── plugin └── arduino.vim └── syntax └── arduino.vim /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.vim] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: General issue 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **System information** 14 | - OS: [linux/mac/windows] 15 | - Vim: [vim or neovim version] 16 | - Arduino: [output of `arduino --version`] 17 | - CLI version: [output of `arduino-cli version`, if you're using it] 18 | - ArduinoInfo: [output of `:ArduinoInfo` command] 19 | 20 | **To Reproduce** 21 | Steps to reproduce the behavior: 22 | 1. 23 | 2. 24 | 3. 25 | 26 | **Expected behavior** 27 | A clear and concise description of what you expected to happen. 28 | 29 | **Working command** 30 | If you are encountering an issue with verify or upload, please find a command line command that works as expected and put it here (you can find the command vim-arduino is using with `:ArduinoInfo`). If you cannot find a command that works, then the issue is with the arduino toolchain and not with this plugin. 31 | 32 | **Screenshots** 33 | If applicable, add screenshots to help explain your problem. 34 | 35 | **Additional context** 36 | Add any other context about the problem here. 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | doc/tags 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Steven Arcangeli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vim-arduino 2 | 3 | Vim plugin for compiling, uploading, and debugging arduino sketches. It uses 4 | [arduino-cli](https://arduino.github.io/arduino-cli/latest/) when available 5 | (recommended), and falls back to using the Arduino IDE's [commandline 6 | interface](https://github.com/arduino/Arduino/blob/master/build/shared/manpage.adoc) 7 | (new in 1.5.x). 8 | 9 | ## Installation 10 | 11 | vim-arduino works with all the usual plugin managers 12 | 13 |
14 | Packer (Neovim only) 15 | 16 | ```lua 17 | require('packer').startup(function() 18 | use {'stevearc/vim-arduino'} 19 | end) 20 | ``` 21 | 22 |
23 | 24 |
25 | Paq (Neovim only) 26 | 27 | ```lua 28 | require "paq" { 29 | {'stevearc/vim-arduino'}; 30 | } 31 | ``` 32 | 33 |
34 | 35 |
36 | vim-plug 37 | 38 | ```vim 39 | Plug 'stevearc/vim-arduino' 40 | ``` 41 | 42 |
43 | 44 |
45 | dein 46 | 47 | ```vim 48 | call dein#add('stevearc/vim-arduino') 49 | ``` 50 | 51 |
52 | 53 |
54 | Pathogen 55 | 56 | ```sh 57 | git clone --depth=1 https://github.com/stevearc/vim-arduino.git ~/.vim/bundle/ 58 | ``` 59 | 60 |
61 | 62 |
63 | Neovim native package 64 | 65 | ```sh 66 | git clone --depth=1 https://github.com/stevearc/vim-arduino.git \ 67 | "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/pack/vim-arduino/start/vim-arduino 68 | ``` 69 | 70 |
71 | 72 |
73 | Vim8 native package 74 | 75 | ```sh 76 | git clone --depth=1 https://github.com/stevearc/vim-arduino.git \ 77 | "$HOME"/.vim/pack/vim-arduino/start/vim-arduino 78 | ``` 79 | 80 |
81 | 82 | ## Requirements 83 | 84 | Linux and Mac are tested and functioning. I have not tested on Windows, but have 85 | heard that it works via WSL. See [this 86 | issue](https://github.com/stevearc/vim-arduino/issues/4) for discussion. 87 | 88 | It is recommended to use `arduino-cli`, installation instructions here: https://arduino.github.io/arduino-cli/latest/installation/ 89 | 90 | However it is also possible to use the arduino IDE directly. Download [Arduino 91 | IDE](https://www.arduino.cc/en/Main/Software) (version 1.5 or newer). Linux 92 | users make sure the `arduino` command is in your PATH. 93 | 94 | ## Commands 95 | 96 | | Command | arg | description | 97 | | ------------------------- | ------------ | --------------------------------------------------------------------------- | 98 | | `ArduinoAttach` | [port] | Automatically attach to your board (see `arduino-cli board attach -h`) | 99 | | `ArduinoChooseBoard` | [board] | Select the type of board. With no arg, will present a choice dialog. | 100 | | `ArduinoChooseProgrammer` | [programmer] | Select the programmer. With no arg, will present a choice dialog. | 101 | | `ArduinoChoosePort` | [port] | Select the serial port. With no arg, will present a choice dialog. | 102 | | `ArduinoVerify` | | Build the sketch. | 103 | | `ArduinoUpload` | | Build and upload the sketch. | 104 | | `ArduinoSerial` | | Connect to the board for debugging over a serial port. | 105 | | `ArduinoUploadAndSerial` | | Build, upload, and connect for debugging. | 106 | | `ArduinoInfo` | | Display internal information. Useful for debugging issues with vim-arduino. | 107 | 108 | To make easy use of these, you may want to bind them to a key combination. You 109 | can put them in `ftplugin/arduino.vim`: 110 | 111 | ```vim 112 | " Change these as desired 113 | nnoremap aa ArduinoAttach 114 | nnoremap av ArduinoVerify 115 | nnoremap au ArduinoUpload 116 | nnoremap aus ArduinoUploadAndSerial 117 | nnoremap as ArduinoSerial 118 | nnoremap ab ArduinoChooseBoard 119 | nnoremap ap ArduinoChooseProgrammer 120 | ``` 121 | 122 | ## Configuration 123 | 124 | By default you should not _need_ to set any options for vim-arduino to work 125 | (especially if you're using `arduino-cli`, which tends to behave better). If 126 | you want to see what's available for customization, there is detailed 127 | information [in the vim docs](https://github.com/stevearc/vim-arduino/blob/master/doc/arduino.txt). 128 | 129 | ## Integrations 130 | 131 | ### Dialog / picker plugins 132 | 133 | The built-in mechanism for choosing items (e.g. `:ArduinoChooseBoard`) uses 134 | `inputlist()` and is not very pretty or ergonomic. If you would like to improve 135 | the UI, there are two approaches: 136 | 137 | - **Neovim:** override `vim.ui.select` (e.g. by using a plugin like [dressing.nvim](https://github.com/stevearc/dressing.nvim)) 138 | - **Vim8:** install [ctrlp](https://github.com/ctrlpvim/ctrlp.vim) or [fzf](https://github.com/junegunn/fzf.vim). They will automatically be detected and used 139 | 140 | ### Tmux / screen 141 | 142 | If you want to run the arduino commands in a separate tmux or screen pane, use 143 | [vim-slime](https://github.com/jpalardy/vim-slime). By setting `let g:arduino_use_slime = 1` vim-arduino will send the commands via `slime#send()` instead of running them inside a vim terminal. 144 | 145 | ### Status Line 146 | 147 | You may want to display the arduino state in your status line. There are four 148 | pieces of data you may find interesting: 149 | 150 | - **g:arduino_board** - the currently selected board 151 | - **g:arduino_programmer** - the currently selected programmer 152 | - **g:arduino_serial_baud** - the baud rate that will be used for Serial commands 153 | - **arduino#GetPort()** - returns the port that will be used for communication 154 | 155 | An example with vanilla vim or nvim, added to `ftplugin/arduino.vim`: 156 | 157 | ```vim 158 | " my_file.ino [arduino:avr:uno] [arduino:usbtinyisp] (/dev/ttyACM0:9600) 159 | function! ArduinoStatusLine() 160 | let port = arduino#GetPort() 161 | let line = '[' . g:arduino_board . '] [' . g:arduino_programmer . ']' 162 | if !empty(port) 163 | let line = line . ' (' . port . ':' . g:arduino_serial_baud . ')' 164 | endif 165 | return line 166 | endfunction 167 | augroup ArduinoStatusLine 168 | autocmd! * 169 | autocmd BufWinEnter setlocal stl=%f\ %h%w%m%r\ %{ArduinoStatusLine()}\ %=\ %(%l,%c%V\ %=\ %P%) 170 | augroup END 171 | ``` 172 | 173 | To do the same thing with [vim-airline](https://github.com/vim-airline/vim-airline): 174 | 175 | ```vim 176 | autocmd BufNewFile,BufRead *.ino let g:airline_section_x='%{MyStatusLine()}' 177 | ``` 178 | 179 | For [lualine](https://github.com/nvim-lualine/lualine.nvim) (Neovim only) I use 180 | the following function: 181 | 182 | ```lua 183 | local function arduino_status() 184 | if vim.bo.filetype ~= "arduino" then 185 | return "" 186 | end 187 | local port = vim.fn["arduino#GetPort"]() 188 | local line = string.format("[%s]", vim.g.arduino_board) 189 | if vim.g.arduino_programmer ~= "" then 190 | line = line .. string.format(" [%s]", vim.g.arduino_programmer) 191 | end 192 | if port ~= 0 then 193 | line = line .. string.format(" (%s:%s)", port, vim.g.arduino_serial_baud) 194 | end 195 | return line 196 | end 197 | ``` 198 | 199 | ## License 200 | 201 | Everything is under the [MIT 202 | License](https://github.com/stevearc/vim-arduino/blob/master/LICENSE) except for 203 | the wonderful syntax file, which was created by Johannes Hoff and copied from 204 | [vim.org](http://www.vim.org/scripts/script.php?script_id=2654) and is under the 205 | [Vim License](http://vimdoc.sourceforge.net/htmldoc/uganda.html). 206 | -------------------------------------------------------------------------------- /UltiSnips/arduino.snippets: -------------------------------------------------------------------------------- 1 | priority -50 2 | 3 | extends cpp 4 | -------------------------------------------------------------------------------- /autoload/arduino.vim: -------------------------------------------------------------------------------- 1 | if (exists('g:loaded_arduino_autoload') && g:loaded_arduino_autoload) 2 | finish 3 | endif 4 | let g:loaded_arduino_autoload = 1 5 | let s:has_cli = executable('arduino-cli') == 1 6 | if has('win64') || has('win32') || has('win16') 7 | echoerr 'vim-arduino does not support windows :(' 8 | finish 9 | endif 10 | let s:HERE = resolve(expand(':p:h:h')) 11 | let s:OS = substitute(system('uname'), '\n', '', '') 12 | " In neovim, run the shell commands using :terminal to preserve interactivity 13 | if has('nvim') 14 | let s:TERM = 'botright split | terminal! ' 15 | elseif has('terminal') 16 | " In vim, doing terminal! will automatically open in a new split 17 | let s:TERM = 'terminal! ' 18 | else 19 | " Backwards compatible with old versions of vim 20 | let s:TERM = '!' 21 | endif 22 | let s:hardware_dirs = {} 23 | let s:SKETCHFILE = v:null 24 | 25 | " Initialization {{{1 26 | " Set up all user configuration variables 27 | function! arduino#InitializeConfig() abort 28 | if exists('g:arduino_did_initialize') 29 | return 30 | endif 31 | call arduino#LoadCache() 32 | 33 | if !exists('g:arduino_board') 34 | if exists('g:_cache_arduino_board') 35 | let g:arduino_board = g:_cache_arduino_board 36 | else 37 | let g:arduino_board = 'arduino:avr:uno' 38 | endif 39 | endif 40 | if !exists('g:arduino_programmer') 41 | if exists('g:_cache_arduino_programmer') 42 | let g:arduino_programmer = g:_cache_arduino_programmer 43 | else 44 | let g:arduino_programmer = '' 45 | endif 46 | endif 47 | if !exists('g:arduino_args') 48 | let g:arduino_args = '--verbose-upload' 49 | endif 50 | if !exists('g:arduino_cli_args') 51 | let g:arduino_cli_args = '-v' 52 | endif 53 | if !exists('g:arduino_serial_cmd') 54 | let g:arduino_serial_cmd = 'screen {port} {baud}' 55 | endif 56 | if !exists('g:arduino_build_path') 57 | let g:arduino_build_path = '{project_dir}/build' 58 | endif 59 | 60 | if !exists('g:arduino_serial_baud') 61 | let g:arduino_serial_baud = 9600 62 | endif 63 | if !exists('g:arduino_auto_baud') 64 | let g:arduino_auto_baud = 1 65 | endif 66 | if !exists('g:arduino_use_slime') 67 | let g:arduino_use_slime = 0 68 | endif 69 | if !exists('g:arduino_use_vimux') || !exists('$TMUX') 70 | let g:arduino_use_vimux = 0 71 | endif 72 | 73 | if !exists('g:arduino_run_headless') 74 | let g:arduino_run_headless = executable('Xvfb') == 1 75 | endif 76 | 77 | if !exists('g:arduino_serial_port_globs') 78 | let g:arduino_serial_port_globs = ['/dev/ttyACM*', 79 | \'/dev/ttyUSB*', 80 | \'/dev/tty.usbmodem*', 81 | \'/dev/tty.usbserial*', 82 | \'/dev/tty.wchusbserial*'] 83 | endif 84 | if !exists('g:arduino_use_cli') 85 | let g:arduino_use_cli = s:has_cli 86 | elseif g:arduino_use_cli && !s:has_cli 87 | echoerr 'arduino-cli: command not found' 88 | endif 89 | call arduino#ReloadBoards() 90 | call s:ReadSketchJson(expand('%:p:h')) 91 | aug ArduinoReadSketch 92 | au! 93 | au BufReadPost *.ino call s:ReadSketchJson(expand(':p:h')) 94 | aug END 95 | let g:arduino_did_initialize = 1 96 | endfunction 97 | 98 | function! arduino#RunCmd(cmd) abort 99 | if g:arduino_use_slime 100 | call slime#send(a:cmd . "\r") 101 | elseif g:arduino_use_vimux 102 | call VimuxRunCommand(a:cmd) 103 | else 104 | exe s:TERM . a:cmd 105 | endif 106 | endfunction 107 | 108 | " Boards and programmer definitions {{{1 109 | function! arduino#ReloadBoards() abort 110 | " TODO in the future if we're using arduino-cli we shouldn't have to do this, 111 | " but at the moment I'm having issues where `arduino-cli board details 112 | " adafruit:avr:gemma --list-programmers` is empty 113 | 114 | " First let's search the arduino system install for boards 115 | " The path looks like /hardware///boards.txt 116 | let arduino_dir = arduino#GetArduinoDir() 117 | let filenames = split(globpath(arduino_dir . '/hardware', '**/boards.txt'), '\n') 118 | for filename in filenames 119 | let pieces = split(filename, '/') 120 | let package = pieces[-3] 121 | let arch = pieces[-2] 122 | call arduino#AddHardwareDir(package, arch, filename) 123 | endfor 124 | 125 | " Now search any packages installed in the home dir 126 | " The path looks like /packages//hardware///boards.txt 127 | let arduino_home_dir = arduino#GetArduinoHomeDir() 128 | let packagedirs = split(globpath(arduino_home_dir . '/packages', '*'), '\n') 129 | for packagedir in packagedirs 130 | let package = fnamemodify(packagedir, ':t') 131 | let archdirs = split(globpath(packagedir . '/hardware', '*'), '\n') 132 | for archdir in archdirs 133 | let arch = fnamemodify(archdir, ':t') 134 | let filenames = split(globpath(archdir, '**/boards.txt'), '\n') 135 | for filename in filenames 136 | call arduino#AddHardwareDir(package, arch, filename) 137 | endfor 138 | endfor 139 | endfor 140 | 141 | " Some platforms put the default arduino boards/programmers in /etc/arduino 142 | if filereadable('/etc/arduino/boards.txt') 143 | call arduino#AddHardwareDir('arduino', 'avr', '/etc/arduino') 144 | endif 145 | if empty(s:hardware_dirs) 146 | echoerr 'Could not find any boards.txt or programmers.txt files. Please set g:arduino_dir and/or g:arduino_home_dir (see help for details)' 147 | endif 148 | endfunction 149 | 150 | function! arduino#AddHardwareDir(package, arch, file) abort 151 | " If a boards.txt file was passed in, get the parent dir 152 | if !isdirectory(a:file) 153 | let filepath = fnamemodify(a:file, ':h') 154 | else 155 | let filepath = a:file 156 | endif 157 | if !isdirectory(filepath) 158 | echoerr 'Could not find hardware directory or file '. a:file 159 | return 160 | endif 161 | let s:hardware_dirs[filepath] = { 162 | \ 'package': a:package, 163 | \ 'arch': a:arch, 164 | \} 165 | endfunction 166 | 167 | " Caching {{{1 168 | " Load the saved defaults 169 | function! arduino#LoadCache() abort 170 | let s:cache_dir = exists('$XDG_CACHE_HOME') ? $XDG_CACHE_HOME : $HOME . '/.cache' 171 | let s:cache = s:cache_dir . '/arduino_cache.vim' 172 | if filereadable(s:cache) 173 | exec 'source ' . s:cache 174 | endif 175 | endfunction 176 | 177 | " Save settings to a source-able cache file 178 | function! arduino#SaveCache() abort 179 | if !isdirectory(s:cache_dir) 180 | call mkdir(s:cache_dir, 'p') 181 | endif 182 | let lines = [] 183 | call s:CacheLine(lines, 'g:_cache_arduino_board') 184 | call s:CacheLine(lines, 'g:_cache_arduino_programmer') 185 | call writefile(lines, s:cache) 186 | endfunction 187 | 188 | " Arduino command helpers {{{1 189 | function! arduino#GetArduinoExecutable() abort 190 | if exists('g:arduino_cmd') 191 | return g:arduino_cmd 192 | elseif s:OS ==? 'Darwin' 193 | return '/Applications/Arduino.app/Contents/MacOS/Arduino' 194 | else 195 | return 'arduino' 196 | endif 197 | endfunction 198 | 199 | function! arduino#GetBuildPath() abort 200 | if empty(g:arduino_build_path) 201 | return '' 202 | endif 203 | let l:path = g:arduino_build_path 204 | let l:path = substitute(l:path, '{file}', expand('%:p'), 'g') 205 | let l:path = substitute(l:path, '{project_dir}', expand('%:p:h'), 'g') 206 | return l:path 207 | endfunction 208 | 209 | function! arduino#GetCLICompileCommand(...) abort 210 | let cmd = 'arduino-cli compile' 211 | if s:SKETCHFILE == v:null 212 | let cmd = cmd . ' -b ' . g:arduino_board 213 | let port = arduino#GetPort() 214 | if !empty(port) 215 | let cmd = cmd . ' -p ' . port 216 | endif 217 | endif 218 | if !empty(g:arduino_programmer) 219 | let cmd = cmd . ' -P ' . g:arduino_programmer 220 | endif 221 | let l:build_path = arduino#GetBuildPath() 222 | if !empty(l:build_path) 223 | let cmd = cmd . ' --build-path "' . l:build_path . '"' 224 | endif 225 | if a:0 226 | let cmd = cmd . ' ' . a:1 227 | endif 228 | return cmd . ' ' . g:arduino_cli_args . ' "' . expand('%:p') . '"' 229 | endfunction 230 | 231 | function! arduino#GetArduinoCommand(cmd) abort 232 | let arduino = arduino#GetArduinoExecutable() 233 | 234 | if g:arduino_run_headless 235 | let arduino = s:HERE . '/bin/run-headless ' . arduino 236 | endif 237 | 238 | let cmd = arduino . ' ' . a:cmd . ' --board ' . g:arduino_board 239 | let port = arduino#GetPort() 240 | if !empty(port) 241 | let cmd = cmd . ' --port ' . port 242 | endif 243 | if !empty(g:arduino_programmer) 244 | let cmd = cmd . ' --pref programmer=' . g:arduino_programmer 245 | endif 246 | let l:build_path = arduino#GetBuildPath() 247 | if !empty(l:build_path) 248 | let cmd = cmd . ' --pref ' . '"build.path=' . l:build_path . '"' 249 | endif 250 | let cmd = cmd . ' ' . g:arduino_args . ' "' . expand('%:p') . '"' 251 | return cmd 252 | endfunction 253 | 254 | function! arduino#GetBoards() abort 255 | let boards = [] 256 | if g:arduino_use_cli 257 | let boards_data = json_decode(system('arduino-cli board listall --format json')) 258 | for board in boards_data['boards'] 259 | call add(boards, { 260 | \ 'label': board['name'], 261 | \ 'value': board['fqbn'] 262 | \ }) 263 | endfor 264 | else 265 | let seen = {} 266 | for [dir,meta] in items(s:hardware_dirs) 267 | if !isdirectory(dir) 268 | continue 269 | endif 270 | let filename = dir . '/boards.txt' 271 | if !filereadable(filename) 272 | continue 273 | endif 274 | let lines = readfile(filename) 275 | for line in lines 276 | if line =~? '^[^.]*\.name=.*$' 277 | let linesplit = split(line, '\.') 278 | let board = linesplit[0] 279 | let linesplit = split(line, '=') 280 | let name = linesplit[1] 281 | let board = meta.package . ':' . meta.arch . ':' . board 282 | if !has_key(seen, board) 283 | let seen[board] = 1 284 | call add(boards, { 285 | \ 'label': name, 286 | \ 'value': board 287 | \ }) 288 | endif 289 | endif 290 | endfor 291 | unlet dir meta 292 | endfor 293 | endif 294 | call sort(boards, 's:ChooserItemOrder') 295 | return boards 296 | endfunction 297 | 298 | function! arduino#GetBoardOptions(board) abort 299 | if g:arduino_use_cli 300 | let ret = [] 301 | let data = json_decode(system('arduino-cli board details -b ' . a:board . ' --format json')) 302 | if !has_key(data, 'config_options') 303 | return ret 304 | endif 305 | let opts = data['config_options'] 306 | for opt in opts 307 | let values = [] 308 | for entry in opt['values'] 309 | call add(values, { 310 | \ 'label': entry['value_label'], 311 | \ 'value': entry['value'] 312 | \ }) 313 | endfor 314 | call add(ret, { 315 | \ 'option': opt['option'], 316 | \ 'option_label': opt['option_label'], 317 | \ 'values': values 318 | \ }) 319 | endfor 320 | return ret 321 | endif 322 | 323 | " Board will be in the format package:arch:board 324 | let [package, arch, boardname] = split(a:board, ':') 325 | 326 | " Find all boards.txt files with that package/arch 327 | let boardfiles = [] 328 | for [dir,meta] in items(s:hardware_dirs) 329 | if meta.package == package && meta.arch == arch 330 | call add(boardfiles, dir.'/boards.txt') 331 | endif 332 | unlet dir meta 333 | endfor 334 | 335 | " Find the boards.txt file with the board definition and read the options 336 | for filename in boardfiles 337 | if !filereadable(filename) 338 | continue 339 | endif 340 | let lines = readfile(filename) 341 | let pattern = '^' . boardname . '\.menu\.\([^.]*\)\.\([^.]*\)=' 342 | let options = {} 343 | let matched = 0 344 | for line in lines 345 | if line =~? pattern 346 | let matched = 1 347 | let groups = matchlist(line, pattern) 348 | let option = groups[1] 349 | let value = groups[2] 350 | if !has_key(options, option) 351 | let options[option] = [] 352 | endif 353 | let optlist = get(options, option) 354 | call add(optlist, value) 355 | endif 356 | endfor 357 | if matched 358 | let ret = [] 359 | for value in keys(options) 360 | call add(ret, { 361 | \ 'option': value, 362 | \ 'option_label': value, 363 | \ 'values': options[value] 364 | \ }) 365 | endfor 366 | return ret 367 | endif 368 | endfor 369 | return [] 370 | endfunction 371 | 372 | function! arduino#GetProgrammers() abort 373 | let programmers = [{ 374 | \ 'label': '-None-', 375 | \ 'value': '', 376 | \}] 377 | if g:arduino_use_cli 378 | let data = json_decode(system('arduino-cli board details -b ' . g:arduino_board . ' --list-programmers --format json')) 379 | if has_key(data, 'programmers') 380 | for entry in data['programmers'] 381 | call add(programmers, { 382 | \ 'label': entry['name'], 383 | \ 'value': entry['id'], 384 | \ }) 385 | endfor 386 | " I'm running into some issues with 3rd party boards (e.g. adafruit:avr:gemma) where the programmer list is empty. If so, fall back to the hardware directory method 387 | if !empty(programmers) 388 | return sort(programmers, 's:ChooserItemOrder') 389 | endif 390 | endif 391 | endif 392 | 393 | let seen = {} 394 | for [dir,meta] in items(s:hardware_dirs) 395 | if !isdirectory(dir) 396 | continue 397 | endif 398 | let filename = dir . '/programmers.txt' 399 | if !filereadable(filename) 400 | continue 401 | endif 402 | let lines = readfile(filename) 403 | for line in lines 404 | if line =~? '^[^.]*\.name=.*$' 405 | let linesplit = split(line, '\.') 406 | let programmer = linesplit[0] 407 | let linesplit = split(line, '=') 408 | let name = linesplit[1] 409 | let prog = meta.package . ':' . programmer 410 | if !has_key(seen, prog) 411 | let seen[prog] = 1 412 | call add(programmers, { 413 | \ 'label': name, 414 | \ 'value': prog 415 | \ }) 416 | endif 417 | endif 418 | endfor 419 | endfor 420 | return sort(programmers) 421 | endfunction 422 | 423 | function! arduino#RebuildMakePrg() abort 424 | if g:arduino_use_cli 425 | let &l:makeprg = arduino#GetCLICompileCommand() 426 | else 427 | let &l:makeprg = arduino#GetArduinoCommand('--verify') 428 | endif 429 | endfunction 430 | 431 | function! s:ChooserItemOrder(i1, i2) abort 432 | let l1 = has_key(a:i1, 'label') ? a:i1['label'] : a:i1['value'] 433 | let l2 = has_key(a:i2, 'label') ? a:i2['label'] : a:i2['value'] 434 | return l1 == l2 ? 0 : l1 > l2 ? 1 : -1 435 | endfunction 436 | 437 | function! arduino#Attach(...) abort 438 | call arduino#InitializeConfig() 439 | if !s:has_cli 440 | echoerr 'ArduinoAttach requires arduino-cli' 441 | return 442 | end 443 | let port = v:null 444 | if a:0 445 | let port = a:1 446 | let dir = expand('%:p:h') 447 | function PostAttach() closure 448 | call s:ReadSketchJson(dir) 449 | call s:notify('Arduino attached to board ' . g:arduino_board) 450 | endfunction 451 | call arduino#job#run(['arduino-cli', 'board', 'attach', '-p', port], funcref('PostAttach')) 452 | else 453 | let ports = arduino#GetPorts() 454 | if empty(ports) 455 | echoerr 'No likely serial ports detected!' 456 | elseif len(ports) == 1 457 | call arduino#Attach(ports[0]) 458 | else 459 | call arduino#chooser#Choose('Select Port', ports, 'arduino#Attach') 460 | endif 461 | endif 462 | endfunction 463 | 464 | " Port selection {{{2 465 | 466 | function! arduino#ChoosePort(...) abort 467 | call arduino#InitializeConfig() 468 | if a:0 469 | let g:arduino_serial_port = a:1 470 | return 471 | endif 472 | let ports = arduino#GetPorts() 473 | if empty(ports) 474 | echoerr 'No likely serial ports detected!' 475 | else 476 | call arduino#chooser#Choose('Select Port', ports, 'arduino#SelectPort') 477 | endif 478 | endfunction 479 | 480 | function! arduino#SelectPort(port) abort 481 | let g:arduino_serial_port = a:port 482 | call s:WriteSketchKey('port', 'serial://' . g:arduino_serial_port) 483 | endfunction 484 | 485 | " Board selection {{{2 486 | 487 | let s:callback_data = {} 488 | 489 | " Display a list of boards to the user and allow them to choose the active one 490 | function! arduino#ChooseBoard(...) abort 491 | call arduino#InitializeConfig() 492 | if a:0 493 | call arduino#SetBoard(a:1) 494 | return 495 | endif 496 | let boards = arduino#GetBoards() 497 | call arduino#chooser#Choose('Select Board', boards, 'arduino#SelectBoard') 498 | endfunction 499 | 500 | " Callback from board selection. Sets the board and prompts for any options 501 | function! arduino#SelectBoard(board) abort 502 | let options = arduino#GetBoardOptions(a:board) 503 | call arduino#SetBoard(a:board) 504 | let s:callback_data = { 505 | \ 'board': a:board, 506 | \ 'available_opts': options, 507 | \ 'opts': {}, 508 | \ 'active_option': '', 509 | \} 510 | " Have to delay this to give the previous chooser UI time to clear 511 | call timer_start(10, {tid -> arduino#ChooseBoardOption()}) 512 | if empty(options) 513 | call s:WriteSketchKey('fqbn', g:arduino_board) 514 | endif 515 | endfunction 516 | 517 | " Prompt user for the next unselected board option 518 | function! arduino#ChooseBoardOption() abort 519 | let available_opts = s:callback_data.available_opts 520 | for opt in available_opts 521 | if !has_key(s:callback_data.opts, opt.option) 522 | let s:callback_data.active_option = opt.option 523 | call arduino#chooser#Choose(opt.option_label, opt.values, 'arduino#SelectOption') 524 | return v:true 525 | endif 526 | endfor 527 | return v:false 528 | endfunction 529 | 530 | " Callback from option selection 531 | function! arduino#SelectOption(value) abort 532 | let opt = s:callback_data.active_option 533 | let s:callback_data.opts[opt] = a:value 534 | call arduino#SetBoard(s:callback_data.board, s:callback_data.opts) 535 | let choosing = arduino#ChooseBoardOption() 536 | if !choosing 537 | call s:WriteSketchKey('fqbn', g:arduino_board) 538 | endif 539 | endfunction 540 | 541 | " Programmer selection {{{2 542 | 543 | function! arduino#ChooseProgrammer(...) abort 544 | call arduino#InitializeConfig() 545 | if a:0 546 | call arduino#SetProgrammer(a:1) 547 | return 548 | endif 549 | let programmers = arduino#GetProgrammers() 550 | call arduino#chooser#Choose('Select Programmer', programmers, 'arduino#SetProgrammer') 551 | endfunction 552 | 553 | function! arduino#SetProgrammer(programmer) abort 554 | let g:_cache_arduino_programmer = a:programmer 555 | let g:arduino_programmer = a:programmer 556 | call arduino#RebuildMakePrg() 557 | call arduino#SaveCache() 558 | endfunction 559 | 560 | " Command functions {{{2 561 | 562 | " Set the active board 563 | function! arduino#SetBoard(board, ...) abort 564 | let board = a:board 565 | if a:0 566 | let options = a:1 567 | let prevchar = ':' 568 | for key in keys(options) 569 | let board = board . prevchar . key . '=' . options[key] 570 | let prevchar = ',' 571 | endfor 572 | endif 573 | let g:arduino_board = board 574 | let g:_cache_arduino_board = board 575 | call arduino#RebuildMakePrg() 576 | call arduino#SaveCache() 577 | endfunction 578 | 579 | function! arduino#Verify() abort 580 | call arduino#InitializeConfig() 581 | if g:arduino_use_cli 582 | let cmd = arduino#GetCLICompileCommand() 583 | else 584 | let cmd = arduino#GetArduinoCommand('--verify') 585 | endif 586 | 587 | call arduino#RunCmd(cmd) 588 | return v:shell_error 589 | endfunction 590 | 591 | function! arduino#Upload() abort 592 | call arduino#InitializeConfig() 593 | if g:arduino_use_cli 594 | let cmd = arduino#GetCLICompileCommand('-u') 595 | else 596 | if empty(g:arduino_programmer) 597 | let cmd_options = '--upload' 598 | else 599 | let cmd_options = '--upload --useprogrammer' 600 | endif 601 | let cmd = arduino#GetArduinoCommand(cmd_options) 602 | endif 603 | 604 | call arduino#RunCmd(cmd) 605 | return v:shell_error 606 | endfunction 607 | 608 | function! arduino#Serial() abort 609 | call arduino#InitializeConfig() 610 | let cmd = arduino#GetSerialCmd() 611 | if empty(cmd) | return | endif 612 | 613 | call arduino#RunCmd(cmd) 614 | endfunction 615 | 616 | function! arduino#UploadAndSerial() 617 | call arduino#InitializeConfig() 618 | " Since 'terminal!' is non-blocking '!' must be used to provide this functionality 619 | let termBackup = s:TERM 620 | let s:TERM = '!' 621 | let ret = arduino#Upload() 622 | if ret == 0 623 | call arduino#Serial() 624 | endif 625 | let s:TERM = termBackup 626 | endfunction 627 | 628 | " Serial helpers {{{2 629 | 630 | function! arduino#GetSerialCmd() abort 631 | let port = arduino#GetPort() 632 | if empty(port) 633 | echoerr 'Error! No serial port found' 634 | return '' 635 | endif 636 | let l:cmd = substitute(g:arduino_serial_cmd, '{port}', port, 'g') 637 | let l:cmd = substitute(l:cmd, '{baud}', g:arduino_serial_baud, 'g') 638 | return l:cmd 639 | endfunction 640 | 641 | function! arduino#SetBaud(baud) abort 642 | let g:arduino_serial_baud = a:baud 643 | endfunction 644 | 645 | function! arduino#SetAutoBaud() abort 646 | let n = 1 647 | while n < line('$') 648 | let match = matchlist(getline(n), 'Serial[0-9]*\.begin(\([0-9]*\)') 649 | if len(match) >= 2 650 | let g:arduino_serial_baud = match[1] 651 | return 652 | endif 653 | let n = n + 1 654 | endwhile 655 | endfunction 656 | 657 | function! arduino#GetPorts() abort 658 | let ports = [] 659 | for l:glob in g:arduino_serial_port_globs 660 | let found = glob(l:glob, 1, 1) 661 | for port in found 662 | call add(ports, port) 663 | endfor 664 | endfor 665 | return ports 666 | endfunction 667 | 668 | function! arduino#GuessSerialPort() abort 669 | let ports = arduino#GetPorts() 670 | if empty(ports) 671 | return 0 672 | else 673 | return ports[0] 674 | endif 675 | endfunction 676 | 677 | function! arduino#GetPort() abort 678 | if exists('g:arduino_serial_port') 679 | return g:arduino_serial_port 680 | else 681 | return arduino#GuessSerialPort() 682 | endif 683 | endfunction 684 | 685 | "}}}2 686 | 687 | " Utility functions {{{1 688 | 689 | function! s:ReadSketchJson(dir) abort 690 | let dir = a:dir 691 | while v:true 692 | let sketch = dir . '/sketch.json' 693 | if filereadable(sketch) 694 | let data = json_decode(join(readfile(sketch))) 695 | let cpu = get(data, 'cpu', {}) 696 | if !empty(cpu) 697 | let s:SKETCHFILE = sketch 698 | let board = get(cpu, 'fqbn', '') 699 | if !empty(board) 700 | let g:arduino_board = board 701 | endif 702 | let port = get(cpu, 'port', '') 703 | if !empty(port) 704 | if port =~? '^serial://' 705 | let port = strcharpart(port, 9) 706 | endif 707 | let g:arduino_serial_port = port 708 | endif 709 | endif 710 | return 711 | endif 712 | let next_dir = fnamemodify(dir, ':h') 713 | if next_dir == dir 714 | break 715 | else 716 | let dir = next_dir 717 | endif 718 | endwhile 719 | let s:SKETCHFILE = v:null 720 | endfunction 721 | 722 | function s:WriteSketchKey(key, value) abort 723 | if s:SKETCHFILE == v:null 724 | return 725 | endif 726 | let data = json_decode(join(readfile(s:SKETCHFILE))) 727 | let cpu = get(data, 'cpu', {}) 728 | let cpu[a:key] = a:value 729 | call writefile([json_encode(data)], s:SKETCHFILE) 730 | endfunction 731 | 732 | function! s:CacheLine(lines, varname) abort 733 | if exists(a:varname) 734 | let value = eval(a:varname) 735 | call add(a:lines, 'let ' . a:varname . ' = "' . value . '"') 736 | endif 737 | endfunction 738 | 739 | function! arduino#GetArduinoDir() abort 740 | if exists('g:arduino_dir') 741 | return g:arduino_dir 742 | endif 743 | let executable = arduino#GetArduinoExecutable() 744 | let arduino_cmd = exepath(executable) 745 | let arduino_dir = fnamemodify(arduino_cmd, ':h') 746 | if s:OS ==? 'Darwin' 747 | let arduino_dir = fnamemodify(arduino_dir, ':h') . '/Java' 748 | endif 749 | return arduino_dir 750 | endfunction 751 | 752 | function! arduino#GetArduinoHomeDir() abort 753 | if exists('g:arduino_home_dir') 754 | return g:arduino_home_dir 755 | endif 756 | if s:OS ==? 'Darwin' 757 | return $HOME . '/Library/Arduino15' 758 | endif 759 | 760 | return $HOME . '/.arduino15' 761 | endfunction 762 | 763 | " Print the current configuration 764 | function! arduino#GetInfo() abort 765 | call arduino#InitializeConfig() 766 | let port = arduino#GetPort() 767 | if empty(port) 768 | let port = 'none' 769 | endif 770 | let dirs = join(keys(s:hardware_dirs), ', ') 771 | if empty(dirs) 772 | let dirs = 'None' 773 | endif 774 | echo 'Board : ' . g:arduino_board 775 | echo 'Programmer : ' . g:arduino_programmer 776 | echo 'Port : ' . port 777 | echo 'Baud rate : ' . g:arduino_serial_baud 778 | echo 'Hardware dirs : ' . dirs 779 | if g:arduino_use_cli 780 | echo 'Verify command: ' . arduino#GetCLICompileCommand() 781 | else 782 | echo 'Verify command: ' . arduino#GetArduinoCommand('--verify') 783 | endif 784 | endfunction 785 | 786 | function! s:notify(msg) abort 787 | if has('nvim') 788 | call luaeval('vim.notify(_A)', a:msg) 789 | else 790 | echo a:msg 791 | endif 792 | endfunction 793 | 794 | " vim:fen:fdm=marker:fmr={{{,}}} 795 | -------------------------------------------------------------------------------- /autoload/arduino/chooser.vim: -------------------------------------------------------------------------------- 1 | " items should be a list of dictionary items with the following keys: 2 | " label (optional) The string to display 3 | " value The corresponding value passed to the callback 4 | " items may also be a raw list of strings. They will be treated as values 5 | function! arduino#chooser#Choose(title, raw_items, callback) abort 6 | let items = [] 7 | let dict_type = type({}) 8 | for item in a:raw_items 9 | if type(item) == dict_type 10 | call add(items, item) 11 | else 12 | call add(items, {'value': item}) 13 | endif 14 | endfor 15 | 16 | if get(g:, 'arduino_nvim_select_enabled', 0) 17 | call luaeval("require('arduino').select_shim(_A[1], _A[2], _A[3])", [a:title, items, a:callback]) 18 | elseif get(g:, 'arduino_telescope_enabled', 0) 19 | call luaeval("require('arduino.telescope').choose('".a:title."', _A, '".a:callback."')", items) 20 | elseif g:arduino_ctrlp_enabled 21 | let ext_data = get(g:ctrlp_ext_vars, s:ctrlp_idx) 22 | let ext_data.lname = a:title 23 | let s:ctrlp_list = items 24 | let s:ctrlp_callback = a:callback 25 | call ctrlp#init(s:ctrlp_id) 26 | elseif g:arduino_fzf_enabled 27 | let s:fzf_counter += 1 28 | call fzf#run({ 29 | \ 'source': s:ConvertItemsToLabels(items), 30 | \ 'sink': s:mk_fzf_callback(a:callback), 31 | \ 'options': '--prompt="'.a:title.': "' 32 | \ }) 33 | else 34 | let labels = map(copy(s:ConvertItemsToLabels(items)), {i, l -> 35 | \ i < 9 36 | \ ? ' '.(i+1).') '.l 37 | \ : (i+1).') '.l 38 | \ }) 39 | let labels = [" " . a:title] + labels 40 | let choice = inputlist(labels) 41 | if choice > 0 42 | call call(a:callback, [items[choice-1]['value']]) 43 | endif 44 | endif 45 | endfunction 46 | 47 | function! s:ConvertItemsToLabels(items) abort 48 | let longest = 1 49 | for item in a:items 50 | if has_key(item, 'label') 51 | let longest = max([longest, strchars(item['label'])]) 52 | endif 53 | endfor 54 | return map(copy(a:items), 's:ChooserItemLabel(v:val, ' . longest . ')') 55 | endfunction 56 | 57 | function! s:ChooserItemLabel(item, ...) abort 58 | let pad_amount = a:0 ? a:1 : 0 59 | if has_key(a:item, 'label') 60 | let label = a:item['label'] 61 | let spacing = 1 + max([pad_amount - strchars(label), 0]) 62 | return label . repeat(' ', spacing) . '[' . a:item['value'] . ']' 63 | endif 64 | return a:item['value'] 65 | endfunction 66 | 67 | function! s:ChooserValueFromLabel(label) abort 68 | " The label may be in the format 'label [value]'. 69 | " If so, we need to parse out the value 70 | let groups = matchlist(a:label, '\[\(.*\)\]$') 71 | if empty(groups) 72 | return a:label 73 | else 74 | return groups[1] 75 | endif 76 | endfunction 77 | 78 | " Ctrlp extension {{{1 79 | if exists('g:ctrlp_ext_vars') 80 | if !exists('g:arduino_ctrlp_enabled') 81 | let g:arduino_ctrlp_enabled = 1 82 | endif 83 | let s:ctrlp_idx = len(g:ctrlp_ext_vars) 84 | call add(g:ctrlp_ext_vars, { 85 | \ 'init': 'arduino#chooser#ctrlp_GetData()', 86 | \ 'accept': 'arduino#chooser#ctrlp_Callback', 87 | \ 'lname': 'arduino', 88 | \ 'sname': 'arduino', 89 | \ 'type': 'line', 90 | \ }) 91 | 92 | let s:ctrlp_id = g:ctrlp_builtins + len(g:ctrlp_ext_vars) 93 | else 94 | let g:arduino_ctrlp_enabled = 0 95 | endif 96 | 97 | function! arduino#chooser#ctrlp_GetData() abort 98 | return s:ConvertItemsToLabels(s:ctrlp_list) 99 | endfunction 100 | 101 | function! arduino#chooser#ctrlp_Callback(mode, str) abort 102 | call ctrlp#exit() 103 | let value = s:ChooserValueFromLabel(a:str) 104 | call call(s:ctrlp_callback, [value]) 105 | endfunction 106 | 107 | " fzf extension {{{1 108 | if !exists('g:arduino_fzf_enabled') 109 | if exists("*fzf#run") 110 | let g:arduino_fzf_enabled = 1 111 | else 112 | let g:arduino_fzf_enabled = 0 113 | endif 114 | endif 115 | 116 | let s:fzf_counter = 0 117 | function! s:fzf_leave(callback, item) 118 | call function(a:callback)(a:item) 119 | let s:fzf_counter -= 1 120 | endfunction 121 | function! s:mk_fzf_callback(callback) 122 | return { item -> s:fzf_leave(a:callback, s:ChooserValueFromLabel(item)) } 123 | endfunction 124 | 125 | " telescope extension {{{1 126 | if !exists('g:arduino_telescope_enabled') && has('nvim') 127 | let g:arduino_telescope_enabled = luaeval("pcall(require, 'telescope')") 128 | endif 129 | 130 | " neovim vim.ui.select {{{1 131 | if !exists('g:arduino_nvim_select_enabled') && has('nvim') 132 | let g:arduino_nvim_select_enabled = luaeval('(vim.ui and vim.ui.select) ~= nil') 133 | endif 134 | 135 | " vim:fen:fdm=marker:fmr={{{,}}} 136 | -------------------------------------------------------------------------------- /autoload/arduino/job.vim: -------------------------------------------------------------------------------- 1 | let s:vim8_jobs = {} 2 | let s:vim8_next_id = 0 3 | 4 | function! arduino#job#run(cmd, callback) 5 | if has('nvim') 6 | call s:nvim_run(a:cmd, a:callback) 7 | else 8 | call s:vim8_run(a:cmd, a:callback) 9 | endif 10 | endfunction 11 | 12 | function! s:nvim_run(cmd, callback) 13 | let Callback = a:callback 14 | function On_exit(job_id, exit_code, event) closure 15 | if !a:exit_code 16 | call Callback() 17 | endif 18 | endfunction 19 | let job_id = jobstart(a:cmd, { 20 | \ 'on_exit': funcref('On_exit'), 21 | \ 'on_stderr': funcref('s:nvim_on_stderr'), 22 | \ 'stderr_buffered': v:true, 23 | \}) 24 | if job_id == 0 25 | echoerr 'Error running job: invalid arguments' 26 | elseif job_id == -1 27 | echoerr 'Error running job: command is not executable' 28 | endif 29 | endfunction 30 | 31 | function! s:nvim_on_stderr(job_id, data, name) 32 | for line in a:data 33 | if !empty(line) 34 | echoerr line 35 | endif 36 | endfor 37 | endfunction 38 | 39 | function! s:nvim_on_exit(job_id, exit_code, event) 40 | if a:exit_code 41 | echoerr 'Error running job ' . a:exit_code 42 | end 43 | endfunction 44 | 45 | function! s:vim8_run(cmd, callback) 46 | let job_id = s:vim8_next_id 47 | let s:vim8_next_id += 1 48 | let Callback = a:callback 49 | function! OnClose(channel) closure 50 | let job = s:vim8_jobs[job_id] 51 | while ch_status(a:channel, {'part': 'err'}) ==? 'buffered' 52 | echoerr ch_read(a:channel, {'part': 'err'}) 53 | endwhile 54 | call remove(s:vim8_jobs, job_id) 55 | let exit_code = job_info(job)['exitval'] 56 | if exit_code 57 | echoerr 'Error running job ' . exit_code 58 | else 59 | call Callback() 60 | endif 61 | endfunction 62 | let job = job_start(a:cmd, {'close_cb': 'OnClose'}) 63 | let s:vim8_jobs[job_id] = job 64 | endfunction 65 | -------------------------------------------------------------------------------- /bin/run-headless: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Wrapper script that will automatically use Xvfb to run a command 3 | declare -r XVFB="Xvfb :1 -nolisten tcp -screen :1 1280x800x24" 4 | 5 | cleanup() { 6 | if [[ -n "$xvfb_proc" ]] && kill -0 $xvfb_proc; then 7 | kill -9 $xvfb_proc 8 | fi 9 | } 10 | 11 | main() { 12 | # If we're using --verbose or --verbose-build, show the output of xvfb 13 | local silent=1 14 | for arg in $@; do 15 | if [[ "$arg" == "--verbose" ]] || [[ "$arg" == "--verbose-build" ]]; then 16 | silent= 17 | break 18 | fi 19 | done 20 | if [ $silent ]; then 21 | $XVFB > /dev/null 2> /dev/null & 22 | else 23 | $XVFB & 24 | fi 25 | xvfb_proc="$!" 26 | trap cleanup SIGINT SIGTERM EXIT 27 | DISPLAY=:1 "$@" 28 | } 29 | 30 | main "$@" 31 | -------------------------------------------------------------------------------- /doc/arduino.txt: -------------------------------------------------------------------------------- 1 | *arduino.txt* 2 | *Arduino* *arduino* *'vim-arduino'* 3 | =============================================================================== 4 | CONTENTS *arduino-contents* 5 | 6 | 1. Intro...........................................|arduino-intro| 7 | 2. Options.........................................|arduino-options| 8 | 3. Commands........................................|arduino-commands| 9 | 10 | =============================================================================== 11 | INTRO *arduino-intro* 12 | 13 | This is a vim plugin to provide basic compatibility and quality-of-life tools 14 | that integrate with the arduino IDE's commandline API. It requires the arduino 15 | IDE to be installed. 16 | 17 | The basic operations that are supported are compiling, uploading, and 18 | debugging your projects from within vim. See the |arduino-commands| for 19 | details. 20 | 21 | =============================================================================== 22 | OPTIONS *arduino-options* 23 | 24 | Overview:~ 25 | 26 | |arduino_cmd|..................Path to the arduino executable 27 | |arduino_use_cli|..............Selects usage of newer arduino-cli tool 28 | |arduino_dir|..................Path to the arduino install directory 29 | |arduino_home_dir|.............Path to the arduino user install directory 30 | |arduino_build_path|...........Path to use for building the sketch 31 | |arduino_run_headless|.........Try to run inside Xvfb 32 | |arduino_args|.................Additional args to pass to 'arduino' command 33 | |arduino_cli_args|.............Additional args to pass to 'arduino-cli' command 34 | |arduino_board|................The fully-qualified name of the board 35 | |arduino_programmer|...........The programmer type 36 | |arduino_serial_cmd|...........Command to run to attach to serial port 37 | |arduino_serial_baud|..........The baud rate for the serial connection 38 | |arduino_auto_baud|............Auto-detect the baud rate 39 | |arduino_use_slime|............Use vim-slime to run commands in tmux/screen/etc 40 | |arduino_serial_port|..........Location of the serial port 41 | |arduino_serial_port_globs|....Globs to auto-search for serial port 42 | 43 | ------------------------------------------------------------------------------- 44 | Detailed descriptions and default values:~ 45 | 46 | *'g:arduino_cmd'* 47 | The path to the 'arduino' command. By default it will look in your PATH, or in 48 | Applications on Mac. > 49 | let g:arduino_cmd = '/usr/share/local/arduino/arduino' 50 | < 51 | *'g:arduino_use_cli'* 52 | The newest and least janky way to interact with arduino on the command line is 53 | using arduino-cli (see https://arduino.github.io/arduino-cli/latest/). If we 54 | detect 'arduino-cli' in your PATH, we will use it. If it is not found, we fall 55 | back to using the Arduino IDE commandline args (see 56 | https://github.com/arduino/Arduino/blob/master/build/shared/manpage.adoc). You 57 | may set this value explicitly to force using one tool or the other. > 58 | let g:arduino_use_cli = 0 " this will always use the Arduino IDE 59 | let g:arduino_use_cli = 1 " this will always use arduino-cli 60 | < 61 | *'g:arduino_dir'* 62 | The path to your 'arduino' directory. Usually vim-arduino will be able to 63 | detect it, but if it cannot you can set the value manually. This is used to 64 | search for the built-in board definitions (i.e. the Uno and Nano) > 65 | let g:arduino_dir = '/usr/share/local/arduino' 66 | < 67 | 68 | *'g:arduino_home_dir'* 69 | The path to your user's 'arduino' data directory. Usually vim-arduino will be 70 | able to detect it, but if it cannot you can set the value manually. This is used 71 | to search for board definitions installed by the IDE Board Manager. > 72 | let g:arduino_home_dir = $HOME . ".arduino15" 73 | < 74 | 75 | *'g:arduino_build_path'* 76 | The path where the sketch will be built and all intermediate object files will 77 | be placed. The final binary (.bin) can be found after building/verification in 78 | the folder. 79 | For a dynamic path you can following substitutions: 80 | - {file} is substituted with the current sketch file (.ino) 81 | - {project_dir} is substituted with the folder the sketch resides in 82 | Usage of a build path can be disabled with g:arduino_build_path = ''. 83 | If disabled, arduino ide chooses a temporary path and will do a full rebuild. > 84 | let g:arduino_build_path = "{project_dir}/build" 85 | < 86 | 87 | *'g:arduino_run_headless'* 88 | Run the arduino command inside a Xvfb. Requires Xvfb to be installed and in the 89 | PATH. > 90 | let g:arduino_run_headless = 1 91 | < 92 | *'g:arduino_args'* 93 | Additional arguments that will be passed to the 'arduino' command during build 94 | and upload. See 95 | https://github.com/arduino/Arduino/blob/master/build/shared/manpage.adoc for 96 | more detail. > 97 | let g:arduino_args = '--verbose-upload' 98 | < 99 | *'g:arduino_cli_args'* 100 | Additional arguments that will be passed to the 'arduino-cli' command during 101 | build and upload. See 102 | https://arduino.github.io/arduino-cli/latest/commands/arduino-cli_compile/ for 103 | more detail. > 104 | let g:arduino_args = '-v' 105 | < 106 | *'g:arduino_board'* 107 | The board type to use when compiling and uploading. See also 108 | |:ArduinoChooseBoard|. > 109 | let g:arduino_board = 'arduino:avr:uno' 110 | < 111 | *'g:arduino_programmer'* 112 | The programmer type to use when compiling and uploading. See also 113 | |:ArduinoChooseProgrammer|. > 114 | let g:arduino_programmer = 'arduino:usbtinyisp' 115 | < 116 | *'g:arduino_serial_cmd'* 117 | Command used to connect to the serial port for debugging. The strings '{port}' 118 | and '{baud}' will be replaced with the port and baud values. > 119 | let g:arduino_serial_cmd = 'screen {port} {baud}' 120 | let g:arduino_serial_cmd = 'picocom {port} -b {baud} -l' 121 | < 122 | 123 | *'g:arduino_serial_baud'* 124 | The baud rate to use for the debugging serial connection. > 125 | let g:arduino_serial_baud = 9600 126 | < 127 | 128 | *'g:arduino_auto_baud'* 129 | Automatically set the baud rate by searching for 'Serial.begin()' > 130 | let g:arduino_auto_baud = 1 131 | < 132 | 133 | *'g:arduino_use_slime'* 134 | Allows vim-slime to send the command to tmux/screen/... . 135 | See :help slime for configuration of slime. Disabled by default. > 136 | let g:arduino_use_slime = 0 137 | < 138 | 139 | *'g:arduino_serial_port'* 140 | Connect to this serial port when uploading & debugging. This is not set by 141 | default. If not set, vim-arduino will attempt to guess which port to use. See 142 | also |:ArduinoChoosePort| > 143 | let g:arduino_serial_port = '/dev/ttyACM0' 144 | < 145 | 146 | *'g:arduino_serial_port_globs'* 147 | Search these patterns to find a likely serial port to upload to. > 148 | let g:arduino_serial_port_globs = ['/dev/ttyACM*', 149 | \'/dev/ttyUSB*', 150 | \'/dev/tty.usbmodem*', 151 | \'/dev/tty.usbserial*'] 152 | < 153 | 154 | =============================================================================== 155 | COMMANDS *arduino-commands* 156 | 157 | *:ArduinoAttach* 158 | :ArduinoAttach [port] 159 | Automatically attach to your board (see `arduino-cli board attach -h`). If 160 | no port is provided and there is more than one option, you will be prompted 161 | to select one. 162 | 163 | *:ArduinoChooseBoard* 164 | :ArduinoChooseBoard [board] 165 | Set [board] to be the currently selected board. It should match the format 166 | of 'package:arch:board[:parameters]'. 167 | 168 | If |g:arduino_board| is not set, the board passed in will be saved to disk 169 | and used when you start new vim sessions. 170 | 171 | If passed no arguments, open a list and let the user select one from the 172 | list. If there are any special options for the board (e.g. cpu) successive 173 | list selections will be opened for those. 174 | 175 | *:ArduinoChooseProgrammer* 176 | :ArduinoChooseProgrammer [programmer] 177 | Set [programmer] to be the currently selected board. It should match the 178 | format of 'package:programmer'. 179 | 180 | If |g:arduino_programmer| is not set, the programmer passed in will be 181 | saved to disk and used when you start new vim sessions. 182 | 183 | If passed no arguments, open a list and let the user select one from the 184 | list. 185 | 186 | *:ArduinoChoosePort* 187 | :ArduinoChoosePort [port] 188 | Set [port] to be the currently selected serial port. If passed no 189 | arguments, open a list of likely ports and let the user select one. 190 | 191 | *:ArduinoVerify* 192 | :ArduinoVerify 193 | Compile your project. This will also be the default behavior of the |:make| 194 | command. 195 | *:ArduinoUpload* 196 | :ArduinoUpload 197 | Compile and upload your project. 198 | *:ArduinoSerial* 199 | :ArduinoSerial 200 | Open a connection to the serial port for debugging. 201 | *:ArduinoUploadAndSerial* 202 | :ArduinoUploadAndSerial 203 | Compile and upload your project. If successful, open a connection to the 204 | serial port for debugging. 205 | *:ArduinoInfo* 206 | :ArduinoInfo 207 | Display information about the internal state of vim-arduino, including the 208 | board, port, and the command that will be run in the terminal to verify your 209 | sketch. 210 | 211 | *:ArduinoSetBaud* 212 | :ArduinoSetBaud [baud] 213 | Set the baud rate used for serial connections. See |g:arduino_serial_baud| 214 | 215 | =============================================================================== 216 | vim:ft=help:et:ts=2:sw=2:sts=2:norl 217 | -------------------------------------------------------------------------------- /ftdetect/arduino.vim: -------------------------------------------------------------------------------- 1 | au BufRead,BufNewFile *.ino setlocal ft=arduino 2 | au BufRead,BufNewFile */Arduino/**/*.h setlocal ft=arduino 3 | au BufRead,BufNewFile */Arduino/**/*.c setlocal ft=arduino 4 | au BufRead,BufNewFile */Arduino/**/*.cpp setlocal ft=arduino 5 | -------------------------------------------------------------------------------- /ftplugin/arduino.vim: -------------------------------------------------------------------------------- 1 | if exists('b:did_arduino_ftplugin') 2 | finish 3 | endif 4 | let b:did_arduino_ftplugin = 1 5 | call arduino#InitializeConfig() 6 | 7 | " Use C rules for indentation 8 | setl cindent 9 | 10 | call arduino#RebuildMakePrg() 11 | 12 | if g:arduino_auto_baud 13 | aug ArduinoBaud 14 | au! 15 | au BufReadPost,BufWritePost *.ino call arduino#SetAutoBaud() 16 | aug END 17 | endif 18 | -------------------------------------------------------------------------------- /lua/arduino/init.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | M.select_shim = function(title, items, callback) 4 | local opts = { 5 | prompt = title, 6 | format_item = function(item) return item.label or item.value end 7 | } 8 | vim.ui.select(items, opts, function(item) 9 | if item then 10 | vim.call(callback, item.value) 11 | end 12 | end) 13 | end 14 | 15 | return M 16 | -------------------------------------------------------------------------------- /lua/arduino/telescope.lua: -------------------------------------------------------------------------------- 1 | local themes = require('telescope.themes') 2 | local actions = require('telescope.actions') 3 | local state = require('telescope.actions.state') 4 | local pickers = require('telescope.pickers') 5 | local finders = require('telescope.finders') 6 | local conf = require('telescope.config').values 7 | 8 | local M = {} 9 | 10 | local entry_maker = function(item) 11 | return { 12 | display = item.label or item.value, 13 | ordinal = item.label or item.value, 14 | value = item.value, 15 | } 16 | end 17 | 18 | M.choose = function(title, items, callback) 19 | local opts = themes.get_dropdown{ 20 | previewer = false, 21 | } 22 | pickers.new(opts, { 23 | prompt_title = title, 24 | finder = finders.new_table { 25 | results = items, 26 | entry_maker = entry_maker, 27 | }, 28 | sorter = conf.generic_sorter(opts), 29 | attach_mappings = function(prompt_bufnr) 30 | actions.select_default:replace(function() 31 | local selection = state.get_selected_entry() 32 | actions.close(prompt_bufnr) 33 | if type(callback) == "string" then 34 | vim.call(callback, selection.value) 35 | else 36 | callback(selection.value) 37 | end 38 | end) 39 | 40 | return true 41 | end 42 | }):find() 43 | end 44 | 45 | return M 46 | -------------------------------------------------------------------------------- /plugin/arduino.vim: -------------------------------------------------------------------------------- 1 | command! -buffer -bar -nargs=? ArduinoAttach call arduino#Attach() 2 | command! -buffer -bar -nargs=? ArduinoChooseBoard call arduino#ChooseBoard() 3 | command! -buffer -bar -nargs=? ArduinoChooseProgrammer call arduino#ChooseProgrammer() 4 | command! -buffer -bar ArduinoVerify call arduino#Verify() 5 | command! -buffer -bar ArduinoUpload call arduino#Upload() 6 | command! -buffer -bar ArduinoSerial call arduino#Serial() 7 | command! -buffer -bar ArduinoUploadAndSerial call arduino#UploadAndSerial() 8 | command! -buffer -bar ArduinoGetInfo call arduino#GetInfo() 9 | command! -buffer -bar ArduinoInfo call arduino#GetInfo() 10 | command! -buffer -bar -nargs=? ArduinoChoosePort call arduino#ChoosePort() 11 | command! -buffer -bar -nargs=1 ArduinoSetBaud call arduino#SetBaud() 12 | -------------------------------------------------------------------------------- /syntax/arduino.vim: -------------------------------------------------------------------------------- 1 | " Vim syntax file 2 | " Language: Arduino 3 | " Maintainer: Johannes Hoff 4 | " Last Change: 17 May 2015 5 | " License: VIM license (:help license, replace vim by arduino.vim) 6 | 7 | " Syntax highlighting like in the Arduino IDE 8 | " Automatically generated by the script available at 9 | " https://bitbucket.org/johannes/arduino-vim-syntax 10 | " Using keywords from /build/shared/lib/keywords.txt 11 | " From version: ARDUINO 1.6.4 - 2015.05.06 12 | 13 | " Thanks to Rik, Erik Nomitch, Adam Obeng, Graeme Cross and Niall Parker 14 | " for helpful feedback! 15 | 16 | " For version 5.x: Clear all syntax items 17 | " For version 6.x: Quit when a syntax file was already loaded 18 | if version < 600 19 | syntax clear 20 | elseif exists("b:current_syntax") 21 | finish 22 | endif 23 | 24 | " Read the C syntax to start with 25 | if version < 600 26 | so :p:h/cpp.vim 27 | else 28 | runtime! syntax/cpp.vim 29 | endif 30 | 31 | syn keyword arduinoConstant BIN CHANGE DEC DEFAULT EXTERNAL FALLING HALF_PI HEX 32 | syn keyword arduinoConstant HIGH INPUT INPUT_PULLUP INTERNAL INTERNAL1V1 33 | syn keyword arduinoConstant INTERNAL2V56 LOW LSBFIRST MSBFIRST OCT OUTPUT PI 34 | syn keyword arduinoConstant RISING TWO_PI 35 | 36 | syn keyword arduinoFunc analogRead analogReference analogWrite 37 | syn keyword arduinoFunc attachInterrupt bit bitClear bitRead bitSet 38 | syn keyword arduinoFunc bitWrite delay delayMicroseconds detachInterrupt 39 | syn keyword arduinoFunc digitalRead digitalWrite highByte interrupts 40 | syn keyword arduinoFunc lowByte micros millis noInterrupts noTone pinMode 41 | syn keyword arduinoFunc pulseIn shiftIn shiftOut tone yield 42 | 43 | syn keyword arduinoMethod available availableForWrite begin charAt compareTo 44 | syn keyword arduinoMethod concat end endsWith equals equalsIgnoreCase find 45 | syn keyword arduinoMethod findUntil flush getBytes indexOf lastIndexOf length 46 | syn keyword arduinoMethod loop parseFloat parseInt peek print println read 47 | syn keyword arduinoMethod readBytes readBytesUntil readString readStringUntil 48 | syn keyword arduinoMethod replace setCharAt setTimeout setup startsWith 49 | syn keyword arduinoMethod substring toCharArray toInt toLowerCase toUpperCase 50 | syn keyword arduinoMethod trim word 51 | 52 | syn keyword arduinoModule Keyboard Mouse Serial Serial1 Serial2 Serial3 53 | syn keyword arduinoModule SerialUSB 54 | 55 | syn keyword arduinoStdFunc abs accept acos asin atan atan2 ceil click constrain 56 | syn keyword arduinoStdFunc cos degrees exp floor isPressed log map max min 57 | syn keyword arduinoStdFunc move pow press radians random randomSeed release 58 | syn keyword arduinoStdFunc releaseAll round sin sq sqrt tan 59 | 60 | syn keyword arduinoType boolean byte null String word 61 | 62 | hi def link arduinoType Type 63 | hi def link arduinoConstant Constant 64 | hi def link arduinoStdFunc Function 65 | hi def link arduinoFunc Function 66 | hi def link arduinoMethod Function 67 | hi def link arduinoModule Identifier 68 | --------------------------------------------------------------------------------