├── .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 |
--------------------------------------------------------------------------------