├── LICENSE.txt ├── plugin ├── mynotebook.vim └── notebook.vim └── README.md /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Thomas Baruchel 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 | -------------------------------------------------------------------------------- /plugin/mynotebook.vim: -------------------------------------------------------------------------------- 1 | " Vim support file to define an APL menus 2 | " 3 | " Maintainer: Thomas Baruchel 4 | " Last Change: 2014 Feb 14 5 | " Version: 1.1 6 | 7 | " Copyright (c) 2014 Thomas Baruchel 8 | " 9 | " Permission is hereby granted, free of charge, to any person obtaining a copy 10 | " of this software and associated documentation files (the "Software"), to deal 11 | " in the Software without restriction, including without limitation the rights 12 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | " copies of the Software, and to permit persons to whom the Software is 14 | " furnished to do so, subject to the following conditions: 15 | " 16 | " The above copyright notice and this permission notice shall be included in 17 | " all copies or substantial portions of the Software. 18 | " 19 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | " THE SOFTWARE. 26 | 27 | " Make sure the '<' and 'C' flags are not included in 'cpoptions', otherwise 28 | " would not be recognized. See ":help 'cpoptions'". 29 | let s:cpo_save = &cpo 30 | set cpo&vim 31 | 32 | " Pari-GP settings 33 | " ---------------- 34 | function! MyPariGpMode() 35 | let g:notebook_cmd='/home/pi/pari-2.7.2/gp -q' 36 | let g:notebook_stop='quit()' 37 | let g:notebook_send='print(\"VIMPARIGPNOTEBOOK\");' 38 | let g:notebook_detect='VIMPARIGPNOTEBOOK' 39 | let g:notebook_send0='' 40 | let g:notebook_sendinit='default(\"readline\",0); default(\"colors\",\"no\");' 41 | endfunc 42 | 43 | " Maxima settings 44 | " --------------- 45 | function! MyMaximaMode() 46 | let g:notebook_cmd='stdbuf -i0 -o0 -e0 /usr/bin/maxima' 47 | \ . ' --disable-readline --very-quiet' 48 | let g:notebook_stop="quit();" 49 | let g:notebook_send0=" 0\$" 50 | let g:notebook_send='print(\"VIMMAXIMANOTEBOOK\")\$' 51 | let g:notebook_detect='VIMMAXIMANOTEBOOK ' 52 | let g:notebook_shell_internal = '/bin/sh' 53 | endfunc 54 | 55 | " GNU Apl settings 56 | " ---------------- 57 | function! MyAplMode() 58 | let g:notebook_cmd = '/home/pi/APL/svn/trunk/src/apl --noSV --rawCIN --noColor' 59 | let g:notebook_stop = ')OFF' 60 | let g:notebook_send0="" 61 | let g:notebook_send = "'VIMGNUAPLNOTEBOOK'" 62 | let g:notebook_detect = 'VIMGNUAPLNOTEBOOK' 63 | endfunc 64 | 65 | " J settings 66 | " ---------- 67 | function! MyJMode() 68 | let g:notebook_cmd = '~/j/j801/bin/jconsole' 69 | let g:notebook_stop = "exit ''" 70 | let g:notebook_send0="''" 71 | let g:notebook_send = "'VIMJNOTEBOOK'" 72 | let g:notebook_detect = ' VIMJNOTEBOOK' 73 | endfunc 74 | 75 | " Mathematica settings 76 | " -------------------- 77 | function! MyMathematicaMode() 78 | let g:notebook_cmd='{ script -c wolfram /dev/null; }' 79 | let g:notebook_stop="Quit" 80 | let g:notebook_send0="" 81 | let g:notebook_send='Print []; Print [ \"VIMWOLFRAMNOTEBOOK\" ]; Print []' 82 | let g:notebook_detect='VIMWOLFRAMNOTEBOOK' 83 | endfunc 84 | 85 | " Scilab settings 86 | " --------------- 87 | function! MyScilabMode() 88 | let g:notebook_cmd = '{ script -qfc scilab-cli-bin /dev/null; }' 89 | \ . ' | grep --line-buffered -Pv "\x0d$"' 90 | let g:notebook_stop = "quit" 91 | let g:notebook_send0="" 92 | let g:notebook_send = 'disp(\"VIMSCILABNOTEBOOK\")' 93 | let g:notebook_detect = ' VIMSCILABNOTEBOOK ' 94 | let g:notebook_shell_internal = '/bin/sh' 95 | endfunc 96 | 97 | " NGN APL settings 98 | " ---------------- 99 | function! MyNgnAPLMode() 100 | let g:notebook_cmd = 'nodejs ~/APL/apl.js --linewise' 101 | let g:notebook_stop = "⎕off" 102 | let g:notebook_send0="" 103 | let g:notebook_send = "'VIMNGNAPLNOTEBOOK'" 104 | let g:notebook_detect = 'VIMNGNAPLNOTEBOOK' 105 | let g:notebook_shell_internal = '/bin/sh' 106 | endfunc 107 | 108 | set wildmenu 109 | 110 | " Avoid installing the menus twice 111 | if !exists("did_install_notebook_menu") 112 | let did_install_notebook_menu = 1 113 | an 45.1 &Notebook.Pari-GP :call MyPariGpMode():NotebookStart 114 | an 45.2 &Notebook.Maxima :call MyMaximaMode():NotebookStart 115 | an 45.3 &Notebook.NGN-APL :call MyNgnAPLMode():NotebookStart 116 | an 45.4 &Notebook.GNU-APL :call MyAPLMode():NotebookStart 117 | an 45.5 &Notebook.J :call MyJMode():NotebookStart 118 | an 45.6 &Notebook.BC :call MyBcMode():NotebookStart 119 | an 45.7 &Notebook.[stop] :NotebookClose 120 | endif " !exists("did_install_syntax_menu") 121 | 122 | " Restore the previous value of 'cpoptions'. 123 | let &cpo = s:cpo_save 124 | unlet s:cpo_save 125 | 126 | " shortcut 127 | set wildcharm= 128 | 129 | " vim: set sw=2 : 130 | -------------------------------------------------------------------------------- /plugin/notebook.vim: -------------------------------------------------------------------------------- 1 | " Vim plugin for communicating with some interpreter from a notebook like document 2 | " 3 | " Maintainer: Thomas Baruchel 4 | " Last Change: 2016 Mar 06 5 | " Version: 1.2.2 6 | 7 | " Copyright (c) 2014 Thomas Baruchel 8 | " 9 | " Permission is hereby granted, free of charge, to any person obtaining a copy 10 | " of this software and associated documentation files (the "Software"), to deal 11 | " in the Software without restriction, including without limitation the rights 12 | " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | " copies of the Software, and to permit persons to whom the Software is 14 | " furnished to do so, subject to the following conditions: 15 | " 16 | " The above copyright notice and this permission notice shall be included in 17 | " all copies or substantial portions of the Software. 18 | " 19 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | " THE SOFTWARE. 26 | 27 | 28 | if !exists('g:notebook_cmd') 29 | let g:notebook_cmd = '/bin/sh 2>&1' 30 | endif 31 | if !exists('g:notebook_stop') 32 | let g:notebook_stop = 'exit' 33 | endif 34 | if !exists('g:notebook_sendinit') 35 | let g:notebook_sendinit = '' 36 | endif 37 | if !exists('g:notebook_send0') 38 | let g:notebook_send0 = '' 39 | endif 40 | if !exists('g:notebook_send') 41 | let g:notebook_send = 'echo NOTEBOOK-VIM-INTERNAL-KEY' 42 | endif 43 | if !exists('g:notebook_detect') 44 | let g:notebook_detect = 'NOTEBOOK-VIM-INTERNAL-KEY' 45 | endif 46 | if !exists('g:notebook_highlight') 47 | let g:notebook_highlight = 1 48 | endif 49 | if !exists('g:notebook_resetpos') 50 | let g:notebook_resetpos = 0 51 | endif 52 | if !exists('g:notebook_shell_internal') 53 | let g:notebook_shell_internal = '/bin/sh' 54 | endif 55 | 56 | 57 | if exists('loaded_notebook') || &cp 58 | finish 59 | endif 60 | let loaded_notebook=1 61 | 62 | 63 | " Close the current kernel 64 | function! NotebookClose() 65 | 66 | if !exists('b:notebook_pid') 67 | echo "No kernel is currently running." 68 | return 69 | endif 70 | 71 | echon 'Stopping the kernel...' 72 | 73 | exe 'autocmd! * ' 74 | 75 | " The notebook_send0 has not to be sent here. 76 | 77 | " close the process 78 | call system('echo "' . b:notebook_stop . '" >> ' . b:notebook_fifo_in) 79 | 80 | " an empty line should close the 'tail -f' process 81 | call system('echo "" >> ' . b:notebook_fifo_in) 82 | 83 | " kill the shell process 84 | call system('kill -9 ' . b:notebook_pid) 85 | 86 | " remove some variables 87 | unlet! b:notebook_stop 88 | unlet! b:notebook_send 89 | unlet! b:notebook_pid 90 | 91 | " Force killing the 'tail' process 92 | let l:cmd = 'ps x | grep -F "tail -f ' . b:notebook_fifo_in . '"' 93 | \ . ' | awk "!/grep/ {print \$1}"' 94 | let l:tmp = system(l:cmd) 95 | if l:tmp > 0 96 | call system("kill -9 " . l:tmp) 97 | endif 98 | 99 | " I have to think if it is really useful to delete the two FIFOs 100 | " (because a race condition could occur if they are removed 101 | " before the 'stop' command has actually been read, and it may be 102 | " important with interpreters that don't accept Ctrl-D like GNU APL 103 | " When Vim leaves, the directory is deleted anyway. 104 | 105 | "let l:tmp = system('rm -f ' . b:notebook_fifo_in) 106 | unlet! b:notebook_fifo_in 107 | "let l:tmp = system('rm -f ' . b:notebook_fifo_out) 108 | unlet! b:notebook_fifo_out 109 | 110 | redraw 111 | echo 112 | 113 | endfunction 114 | 115 | 116 | function! NotebookRestart() 117 | 118 | if !exists('b:notebook_pid') 119 | echo "No kernel is currently running." 120 | return 121 | endif 122 | 123 | call NotebookClose() 124 | redraw 125 | call NotebookStart() 126 | 127 | endfunction 128 | 129 | 130 | " Check whether a given line is part of a Markdown code block 131 | function! NotebookCheckLine(l) 132 | " Case 1 133 | if synIDattr(synID(a:l,1,1),"name") == "markdownCodeBlock" 134 | return 1 135 | endif 136 | if synIDattr(synID(a:l,1,1),"name") == "markdownCodeDelimiter" 137 | return 1 138 | endif 139 | let l:s = synstack(a:l,1) 140 | if len(l:s) == 0 141 | return 0 142 | endif 143 | " Case 2 144 | if synIDattr(l:s[0],"name")[0:16] == "markdownHighlight" 145 | return 1 146 | endif 147 | " Return False 148 | return 0 149 | endfunction 150 | 151 | 152 | " Evaluate the Code Block at the position of the cursor. 153 | " Since version 1.0.2 the function NotebookEvaluate() has a return value 154 | " indicating how many lines have been sent to the kernel. 155 | function! NotebookEvaluate() 156 | 157 | if !exists('b:notebook_pid') 158 | echo "No kernel is currently running." 159 | return 0 160 | endif 161 | 162 | let l:currentline = line(".") 163 | if NotebookCheckLine(l:currentline) == 0 164 | echo "Current line is not a Markdown code block" 165 | return 0 166 | endif 167 | 168 | let l:save_cursor = getpos(".") 169 | let l:time0 = localtime() 170 | 171 | let l:blockstart = l:currentline 172 | while NotebookCheckLine(l:blockstart) == 1 173 | \ && (l:blockstart >= 1) 174 | let l:blockstart = l:blockstart - 1 175 | endwhile 176 | let l:blockstart = l:blockstart + 1 177 | " Fix for fenced code block 178 | if synIDattr(synID(l:blockstart,1,1),"name") == "markdownCodeDelimiter" 179 | let l:blockstart = l:blockstart + 1 180 | endif 181 | 182 | let l:blockend = l:currentline 183 | let l:lastline = line("$") 184 | while NotebookCheckLine(l:blockend) == 1 185 | \ && (l:blockend <= l:lastline) 186 | let l:blockend = l:blockend + 1 187 | endwhile 188 | let l:blockend = l:blockend - 1 189 | " Location for writing the answer 190 | " (not always the same as l:blockend, see below) 191 | let l:blockendw = l:blockend 192 | " Fix for fenced code block 193 | if synIDattr(synID(l:blockend,1,1),"name") == "markdownCodeDelimiter" 194 | let l:blockend = l:blockend - 1 195 | endif 196 | 197 | if l:blockend < l:blockstart 198 | echo "The block of code is empty" 199 | return 0 200 | end 201 | 202 | if g:notebook_highlight != 0 203 | let l:tmp_pattern = "match Todo /" 204 | let l:currentline = l:blockstart 205 | while l:currentline < l:blockend 206 | let l:tmp_pattern = l:tmp_pattern . "\\%" . l:currentline . "l\\|" 207 | let l:currentline = l:currentline + 1 208 | endwhile 209 | let l:tmp_pattern = l:tmp_pattern . "\\%" . l:blockend . "l/" 210 | execute l:tmp_pattern 211 | redraw 212 | endif 213 | 214 | exe l:blockstart . ',' . l:blockend . 'write! >> ' . b:notebook_fifo_in 215 | if len(b:notebook_send0) > 0 216 | call system('echo "' . b:notebook_send0 . '" >> ' . b:notebook_fifo_in) 217 | endif 218 | call system('echo "' . b:notebook_send . '" >> ' . b:notebook_fifo_in) 219 | 220 | exe 'silent normal! ' . l:blockendw . 'Go' 221 | exe 'read! cat ' . b:notebook_fifo_out 222 | if g:notebook_highlight != 0 223 | match 224 | endif 225 | 226 | if g:notebook_resetpos != 0 227 | call setpos('.', l:save_cursor) 228 | endif 229 | redraw 230 | 231 | let l:mytime = localtime() - l:time0 232 | if l:mytime < 1 233 | let l:mytime = "" 234 | else 235 | let l:mytime = " Time=" . mytime . " sec." 236 | endif 237 | 238 | if l:blockstart == l:blockend 239 | echo "Line ".l:blockstart." was sent to the kernel (1 line)." . l:mytime 240 | else 241 | echo "Lines ".l:blockstart."-".l:blockend 242 | \ . " were sent to the kernel (".(l:blockend-l:blockstart+1)." lines)." 243 | \ . l:mytime 244 | endif 245 | 246 | return l:blockend - l:blockstart + 1 247 | 248 | endfunction 249 | 250 | 251 | " Start a new kernel 252 | function! NotebookStart() 253 | 254 | if exists('b:notebook_pid') 255 | echo 'Warning: a running kernel is already attached to the current buffer.' 256 | return 257 | endif 258 | 259 | if len(g:notebook_shell_internal) > 0 260 | let l:shelltmp = &shell 261 | let &shell = g:notebook_shell_internal 262 | endif 263 | 264 | echo 'Starting the kernel...' 265 | 266 | " create two fifo special files 267 | let b:notebook_fifo_in = tempname() 268 | call system('mkfifo ' . b:notebook_fifo_in) 269 | let b:notebook_fifo_out = tempname() 270 | let l:out = b:notebook_fifo_out 271 | call system('mkfifo ' . b:notebook_fifo_out) 272 | 273 | " copy the global variables to buffer variables 274 | " in order to allow the global variables to be changed 275 | let b:notebook_stop = g:notebook_stop 276 | let b:notebook_send0 = g:notebook_send0 277 | let b:notebook_send = g:notebook_send 278 | 279 | let l:tmp = 'tail -f ' . b:notebook_fifo_in 280 | let l:tmp = l:tmp . ' | ' . g:notebook_cmd 281 | let l:tmp = l:tmp . ' | { while IFS= read -r line;' 282 | let l:tmp = l:tmp . ' do { while [ "$line" != "' . g:notebook_detect .'" ];' 283 | let l:tmp = l:tmp . ' do echo "$line"; IFS= read -r line; done; } > ' . b:notebook_fifo_out .';' 284 | let l:tmp = l:tmp . ' done; } &' 285 | let l:tmp = l:tmp . ' echo "$!"' 286 | "let l:tmp = l:tmp . ' > ' . b:notebook_fifo_in 287 | let b:notebook_pid = system(l:tmp) 288 | 289 | " send an initial command to be detected 290 | " -------------------------------------- 291 | " Since version 1.1.1 sending an initial notebook_send0 is disabled; 292 | " it is replaced with sending a notebook_sendinit command 293 | if len(g:notebook_sendinit) > 0 294 | call system('echo "' . g:notebook_sendinit . '" >> ' . b:notebook_fifo_in) 295 | endif 296 | call system('echo "' . b:notebook_send . '" >> ' . b:notebook_fifo_in) 297 | 298 | set filetype=markdown 299 | syntax on 300 | 301 | call system('cat ' . l:out . ' > /dev/null') 302 | redraw 303 | 304 | if len(g:notebook_shell_internal) > 0 305 | let &shell = l:shelltmp 306 | endif 307 | 308 | " autocmd for stopping the kernel when closing the buffer 309 | exe 'autocmd BufDelete call NotebookClose()' 310 | exe 'autocmd BufUnload call NotebookClose()' 311 | " exe 'autocmd VimLeave call NotebookClose()' 312 | 313 | endfunction 314 | 315 | 316 | " Evaluate the whole document 317 | function! NotebookEvaluateAll() 318 | 319 | if !exists('b:notebook_pid') 320 | echo "No kernel is currently running." 321 | return 322 | endif 323 | 324 | let l:currentline = 0 325 | let l:total = 0 326 | let l:nbrblocks = 0 327 | let l:lastline = line('$') 328 | let l:time0 = localtime() 329 | 330 | let l:resetpostmp = g:notebook_resetpos 331 | let g:notebook_resetpos = 0 332 | 333 | " Warning : the number of lines is changing during the loop! 334 | while l:currentline < l:lastline 335 | let l:currentline = l:currentline + 1 336 | if NotebookCheckLine(l:currentline) == 1 337 | exe 'silent normal! ' . l:currentline . 'G' 338 | let l:nbr = NotebookEvaluate() 339 | redraw 340 | let l:lastline = line('$') 341 | let l:currentline = line('.') 342 | let l:total = l:total + l:nbr 343 | let l:nbrblocks = l:nbrblocks + 1 344 | endif 345 | endwhile 346 | 347 | let g:notebook_resetpos = l:resetpostmp 348 | 349 | if l:total == 1 350 | let l:count = '(1 line)' 351 | else 352 | let l:count = '(' . l:total . ' lines)' 353 | endif 354 | 355 | let l:mytime = localtime() - l:time0 356 | if l:mytime < 1 357 | let l:mytime = "" 358 | else 359 | let l:mytime = " Time=" . mytime . " sec." 360 | endif 361 | 362 | if l:nbrblocks == 0 363 | echo 'No code block was found in the current document.' 364 | elseif l:nbrblocks == 1 365 | echo 'One block was sent to the kernel ' . l:count . '.' . l:mytime 366 | else 367 | echo l:nbrblocks . ' blocks were sent to the kernel ' . l:count . '.' 368 | \ . l:mytime 369 | endif 370 | 371 | endfunction 372 | 373 | 374 | command! NotebookStart :call NotebookStart() 375 | command! NotebookEvaluate :call NotebookEvaluate() 376 | command! NotebookEvaluateAll :call NotebookEvaluateAll() 377 | command! NotebookClose :call NotebookClose() 378 | command! NotebookStop :call NotebookClose() 379 | command! NotebookRestart :call NotebookRestart() 380 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | vim-notebook 2 | ============ 3 | 4 | A plugin for the Vim editor for handling any interpreter in a Notebook style. 5 | 6 | This plugin is intended to create documents with an interpreter running in the 7 | background and evaluating some "cells" containing code whenever asked. Of course, 8 | it is very easy to execute some code from vim, but the standard way of doing it 9 | will involve a new session of the interpreter at each call, losing variables, etc. 10 | With this plugin, a single interpreter is waiting in the background with a persistent 11 | state between calls. 12 | 13 | It follows the philosophy of the Vim editor better than some similar plugins: 14 | rather than launching an interactive interpreter within a buffer, it keeps it in 15 | the background and makes it write into the buffer when the user needs it. 16 | 17 | Since version 1.2.0 the plugin is compatible with the [vim-markdown](https://github.com/tpope/vim-markdown) plugin (by Tim Pope). Fenced code blocks are now detected 18 | and correctly handled. 19 | 20 | Thus, Vim will behave like several well-known "notebook" software: 21 | 22 | * iPython Notebook 23 | * Maple 24 | * Mathematica 25 | * etc. 26 | 27 | It has been tested with several interpreters and seems to work well with _Julia_, 28 | _Octave_, _Maxima_, _GNU APL_, _J_, etc. as well as with some standard tools 29 | like `bc` or `sh`. 30 | 31 | A demo can be seen [there](https://www.youtube.com/watch?v=vHiCpRQiJuU) (another one which is related to the Julia programming language can also be found [there](https://youtu.be/MGIsOgikFuY)). 32 | 33 | [![Demo](http://img.youtube.com/vi/vHiCpRQiJuU/0.jpg)](https://www.youtube.com/watch?v=vHiCpRQiJuU) 34 | 35 | The plugin uses Unix background processes, special files, etc. and will only work 36 | on Unix-like operating systems; it has been tested under Linux and Mac OS X. 37 | 38 | When the kernel is launched, the filetype of the document will be set to "markdown" 39 | and the standard syntax file for the Markdown type will be used; syntax highlighting 40 | will also be enabled. This standard syntax file defines a "markdownCodeBlock" 41 | element made with lines of code beginning with 4 spaces: 42 | 43 | this is an example of "markdownCodeBlock" 44 | 45 | When the cursor is on such a line, using the `NotebookEvaluate` command will make the whole 46 | block (no matter where the cursor is exactly in the block) be sent to the interpreter 47 | and the output will be printed below. 48 | 49 | Of course, the document may contain anything else: headers, text, etc. It should follow 50 | the Markdown style, though it is not absolutely mandatory. 51 | 52 | # Installation 53 | 54 | Just copy the _notebook.vim_ file in your ~/.vim/plugin directory. 55 | 56 | By default, the plugin will use `sh` when launched; you will have to configure it 57 | for the interpreter you want to use. 58 | 59 | # Basic configuration 60 | 61 | Several global variables are involved in the configuration of the plugin; you should 62 | set them in your ~/.vimrc configuration file. 63 | 64 | ### Shortcuts 65 | 66 | You should probably map the the `NotebookEvaluate` command to some convenient key. 67 | For instance: 68 | 69 | map µ :NotebookEvaluate 70 | 71 | will map the µ key to the function evaluating code blocks. 72 | 73 | ### Various settings 74 | 75 | The code block being evaluated can be highlighted until the output has been printed: 76 | 77 | let g:notebook_highlight = 1 78 | 79 | By default, the cursor is on the last line of the output after evaluation. If you 80 | want rather the cursor staying at the initial location, just set: 81 | 82 | let g:notebook_resetpos = 1 83 | 84 | The previous setting has no effect when evaluating all the cells at a time. 85 | 86 | ### Configuring a kernel 87 | 88 | Many famous interpreters are given below; you will find relevant settings for them. 89 | However, you may want to configure some settings by yourself; here are the options 90 | used by the plugin. 91 | 92 | The main option is `g:notebook_cmd` which contains the command for starting the 93 | interpreter. Most of the time it is the name of your interpreter (sometimes you 94 | may want to add some "quiet" flags for reducing the output). Sometimes, the 95 | interpreter has to be encapsulated in some other command for solving issues (see 96 | below). 97 | 98 | The `g:notebook_sendinit` option allows to send some initialization command; for 99 | instance it should be used for disabling prompts, removing any escape sequences, etc. 100 | 101 | After each block of code sent to the interpreter, an "invisible" command is sent 102 | to it for making an arbitrary key being printed; generally some `print` or `echo` 103 | command will be used for that purpose; that command is in `g:notebook_send`. 104 | 105 | Intercommunication between processes can be easely lost as soon as the previous 106 | key isn't detected properly; with many interpreters, each new command will be 107 | interpreted even if an error has occured. However an extra command may be 108 | sent after the block of code and before the request for the key. If `g:notebook_send0` 109 | is not an empty string, it is sent to the interpreter before the previous command. 110 | 111 | In order to let the plugin know that the evaluation of the block is finished, it 112 | has to detect the arbitrary key printed by the interpreter. The `g:notebook_detect` option must contain the _exact_ line written by the interpreter. Some 113 | interpreters add spaces before or after the printed key and they have to be included; such spaces may be difficult to detect when trying to configure the plugin, and it is suggested to study the behaviour of your interpreter in a `script` session (editing the _typescript_ file after that will allow to see if spaces 114 | have been added anywhere). 115 | 116 | The plugin is intended to handle the interpreter in the cleanest way; a command 117 | has to be provided for closing it properly; it is set in `g:notebook_stop`. 118 | 119 | # Using the Notebook plugin 120 | 121 | Just start it with: 122 | 123 | :NotebookStart 124 | 125 | (or add some shortcut in your configuration file). 126 | 127 | Then, a block of code (at the position of the cursor) may be evaluated with: 128 | 129 | :NotebookEvaluate 130 | 131 | The whole notebook document may be evaluated with: 132 | 133 | :NotebookEvaluateAll 134 | 135 | The kernel may be stopped with one of the two following commands: 136 | 137 | :NotebookStop 138 | :NotebookClose 139 | 140 | The kernel may be stopped and restarted with: 141 | 142 | :NotebookRestart 143 | 144 | By default the plugin uses `/bin/sh` as an internal process; it is known to work 145 | also with `bash`. You may set this with: 146 | 147 | let g:notebook_shell_internal = '/bin/sh' 148 | let g:notebook_shell_internal = '/bin/bash' 149 | etc. 150 | 151 | in your configuration file; it looks like some interpreters work better with `bash` and you should try it if you encounter some issues. 152 | 153 | # Configurations for several interpreters 154 | 155 | Not all interpreters will work with the plugin, but it is intended to allow many 156 | ways of hacking and you should be able to use many different programs anyway. 157 | Have a look at different settings in order to understand them. 158 | 159 | The interpreter should not use any buffering when writing to the standard output. 160 | If it is the case, it should still be possible to use the interpreter with the 161 | help of the `stdbuf` command (see below). 162 | 163 | #### Configuring the sh kernel 164 | 165 | This is the default setting: 166 | 167 | let g:notebook_cmd = '/bin/sh 2>&1' 168 | let g:notebook_stop = 'exit' 169 | let g:notebook_send = 'echo NOTEBOOK-VIM-INTERNAL-KEY' 170 | let g:notebook_detect = 'NOTEBOOK-VIM-INTERNAL-KEY' 171 | let g:notebook_send0 = '' 172 | 173 | The first line is the command to be used for starting the interpreter. In order 174 | to catch error messages as well we added `2>&1` to the command. 175 | The second line is the command to be sent to the interpreter for leaving. 176 | The third line is a command for the interpreter making it print some arbitrary 177 | and complicated string. The fourth line is the _exact_ string printed by the 178 | interpreter from the previous command. 179 | The last line is a hack; here no setting is provided; some more complicated 180 | interpreters may need it (see below). 181 | 182 | #### Configuring the bc calculator 183 | 184 | let g:notebook_cmd='bc 2>&1' 185 | let g:notebook_stop='quit' 186 | let g:notebook_send='print \"VIMBCNOTEBOOK\n\"' 187 | let g:notebook_detect="VIMBCNOTEBOOK" 188 | let g:notebook_send0="" 189 | 190 | The settings are similar to the previous ones. 191 | 192 | #### Configuring the dc calculator 193 | 194 | let g:notebook_cmd='dc 2>&1' 195 | let g:notebook_stop='q' 196 | let g:notebook_send='[][VIMDCNOTEBOOK][]pnnpn' 197 | let g:notebook_detect='VIMDCNOTEBOOK' 198 | let g:notebook_send0='' 199 | 200 | The settings are similar to the previous ones. 201 | 202 | #### Configuring Julia 203 | 204 | let g:notebook_cmd='~/contribs/julia/julia -qi' 205 | let g:notebook_stop='exit()' 206 | let g:notebook_send0="" 207 | let g:notebook_send='println(); println(\"VIMJULIANOTEBOOK\")' 208 | let g:notebook_detect='VIMJULIANOTEBOOK' 209 | 210 | #### Configuring Octave 211 | 212 | Ocatve should work with no problem with following settings: 213 | 214 | let g:notebook_cmd='octave' 215 | let g:notebook_stop='exit' 216 | let g:notebook_send='printf \"VIMOCTAVENOTEBOOK\n\"' 217 | let g:notebook_detect='VIMOCTAVENOTEBOOK' 218 | let g:notebook_send0="" 219 | let g:notebook_shell_internal = '/bin/bash' 220 | 221 | It looks like the shell `sh` does not work here. 222 | 223 | #### Configuring Maxima 224 | 225 | The plugin was written with Maxima in mind and it should work quite well with it. 226 | But since Maxima can be compiled in many different ways, the following settings may 227 | have to be adjusted. Here are some working settings: 228 | 229 | let g:notebook_cmd='stdbuf -i0 -o0 -e0 /usr/bin/maxima' 230 | \ . ' --disable-readline --very-quiet' 231 | let g:notebook_stop="quit();" 232 | let g:notebook_send0="\;" 233 | let g:notebook_send='print(\"VIMMAXIMANOTEBOOK\")\$' 234 | let g:notebook_detect='VIMMAXIMANOTEBOOK ' 235 | 236 | The command is prefixed with `stdbuf -i0 -o0 -e0` in order to unbuffer the following 237 | command because intercommunication between processes is highly sensitive and the 238 | whole system could be stuck otherwise. 239 | 240 | The last line contains an espace character in the string; this is because Maxima 241 | seems to add an espace when printing the string. If you encounter some issues with 242 | these settings, you should carefully study wether your version of Maxima behaves 243 | like that or not (you can do it by launching Maxima in a `script` session and then 244 | study the resulting _typescript_ file). 245 | 246 | Furthermore, the `g:notebook_send0` setting _may_ be used here. It will send 247 | some more characters after each command and before asking for the internal key. 248 | If this setting is not used, the user should _never_ forget the final `;` in 249 | the code being evaluated. If the `;` (or `$` character) is forgotten, the whole 250 | session will be lost and the kernel will have to be killed and restarted. 251 | 252 | Several hacks can be used; the user can choose to never use the `;` but to add it 253 | in the `g:notebook_send0` variable: 254 | 255 | let g:notebook_send0="\;" 256 | 257 | Adding `;` by mistake will print an error message but the communication between 258 | processes will remain alive. 259 | 260 | Another strategy can be something like that: 261 | 262 | let g:notebook_send0=" 0\$" 263 | 264 | Now the user has to use the `;` (or `$`) syntax; a strange error will be printed 265 | when forgotten but the communication between processes will remain alive also. 266 | 267 | #### Configuring Pari-GP 268 | 269 | The plugin should work well with Pari-GP; however it has been tested with an old 270 | out-of-date version of Pari-GP; the settings should be something like: 271 | 272 | let g:notebook_cmd='gp -q' 273 | let g:notebook_stop='quit()' 274 | let g:notebook_send='print(\"VIMPARIGPNOTEBOOK\");' 275 | let g:notebook_detect='VIMPARIGPNOTEBOOK' 276 | let g:notebook_send0='' 277 | let g:notebook_sendinit='default(\"readline\",0); default(\"colors\",\"no\");' 278 | 279 | #### Configuring Julia 280 | 281 | The `julia` interpreter seems to work very well with the plugin: 282 | 283 | let g:notebook_cmd='julia' 284 | let g:notebook_stop='exit()' 285 | let g:notebook_send0='' 286 | let g:notebook_send='println(\"VIMJULIANOTEBOOK\")' 287 | let g:notebook_detect='VIMJULIANOTEBOOK' 288 | 289 | #### Configuring Scilab 290 | 291 | The `scilab-cli-bin` client has to be installed; then usable settings are: 292 | 293 | let g:notebook_cmd = '{ script -qfc scilab-cli-bin /dev/null; }' 294 | \ . ' | grep --line-buffered -Pv "\x0d$"' 295 | let g:notebook_stop = "quit" 296 | let g:notebook_send0="" 297 | let g:notebook_send = 'disp(\"VIMSCILABNOTEBOOK\")' 298 | let g:notebook_detect = ' VIMSCILABNOTEBOOK ' 299 | 300 | Output is not absolutely perfect however. 301 | 302 | #### Configuring Mathematica 303 | 304 | It is possible to use the Wolfram engine with the plugin. It has to be launched 305 | in a pseudo-terminal with `script`. Right now, the output contains many escape 306 | sequences; how to clean it will be explained later. 307 | 308 | let g:notebook_cmd='{ script -c wolfram /dev/null; }' 309 | let g:notebook_stop="Quit" 310 | let g:notebook_send0="" 311 | let g:notebook_send='Print []; Print [ \"VIMWOLFRAMNOTEBOOK\" ]; Print []' 312 | let g:notebook_detect='VIMWOLFRAMNOTEBOOK' 313 | 314 | #### Configuring GNU APL 315 | 316 | GNU APL works very well with the following settings: 317 | 318 | let g:notebook_cmd = '/home/pi/APL/svn/trunk/src/apl --noSV --rawCIN --noColor' 319 | let g:notebook_stop = ')OFF' 320 | let g:notebook_send0="" 321 | let g:notebook_send = "'VIMGNUAPLNOTEBOOK'" 322 | let g:notebook_detect = 'VIMGNUAPLNOTEBOOK' 323 | 324 | #### Configuring NGN APL 325 | 326 | Nick Nickolov, the author of NGN APL, was kind enough to add the `⎕off` command in order to 327 | make its interpreter compatible with the plugin. Later he also made his interpreter 328 | easier to embed in _vim-notebook_. First check you have the last version of 329 | NGN APL. Of course, you must install `nodejs` in order to make it work. The following settings 330 | work very well: 331 | 332 | let g:notebook_cmd = 'nodejs ~/APL/apl.js --linewise' 333 | let g:notebook_stop = "⎕off" 334 | let g:notebook_send0="" 335 | let g:notebook_send = "'VIMNGNAPLNOTEBOOK'" 336 | let g:notebook_detect = 'VIMNGNAPLNOTEBOOK' 337 | #### Configuring Dyalog APL 338 | 339 | Dyalog APL works very well with the following settings (tested on a Raspberry Pi): 340 | 341 | let g:notebook_cmd = 'dyalog' 342 | let g:notebook_stop = ")off" 343 | let g:notebook_send0="" 344 | let g:notebook_send = "'VIMDYALOGAPLNOTEBOOK'" 345 | let g:notebook_detect = 'VIMDYALOGAPLNOTEBOOK' 346 | let g:notebook_shell_internal = '/bin/sh' 347 | 348 | #### Configuring the J interpreter 349 | 350 | The three-spaces prompt may be an issue. A quick fix can be: 351 | 352 | let g:notebook_cmd = '~/j/j801/bin/jconsole' 353 | let g:notebook_stop = "exit ''" 354 | let g:notebook_send0="''" 355 | let g:notebook_send = "'VIMJNOTEBOOK'" 356 | let g:notebook_detect = ' VIMJNOTEBOOK' 357 | 358 | You have to be careful when copying lines 3 (no-op like) and 5 (with three spaces). 359 | 360 | #### Configuring the R interpreter 361 | 362 | The interpreter for the R programming language works very well with the 363 | following settings: 364 | 365 | let g:notebook_cmd='R --vanilla --no-readline --slave' 366 | let g:notebook_stop='q()' 367 | let g:notebook_send0="" 368 | let g:notebook_send='cat(\"VIMRNOTEBOOK\n\")' 369 | let g:notebook_detect="VIMRNOTEBOOK" 370 | 371 | #### Configuring the kdb+ interpreter (from Kx Systems) 372 | 373 | I don't use this interpreter myself, but sinc eI am interested in APL variants, I downloaded the free version and saw if I could make it work. It seems to work very well with the following settings: 374 | 375 | let g:notebook_cmd='/home/thomas/q/l32/q' 376 | let g:notebook_stop='\\\\' 377 | let g:notebook_send0="" 378 | let g:notebook_send='\"VIMQNOTEBOOK\"' 379 | let g:notebook_detect='\"VIMQNOTEBOOK\"' 380 | 381 | Of course, you will have to change the path of your executable command. 382 | 383 | #### Configuring the FriCAS interpreter 384 | 385 | I don't use FriCAS much, but I think the following settings should work fine: 386 | 387 | let g:notebook_cmd='{ script -qfc "fricas -nogr -noht -nox -noclef" /dev/null; }' 388 | \ . '| grep --line-buffered -Pv "\x0d$"' 389 | let g:notebook_stop=')quit' 390 | let g:notebook_send0="" 391 | let g:notebook_send='output \"\"; output \"VIMFRICASNOTEBOOK\"' 392 | let g:notebook_detect=' VIMFRICASNOTEBOOK' 393 | 394 | #### Configuring the OpenAxiom interpreter 395 | 396 | I don't use OpenAxiom much, but I think the following settings should work fine: 397 | 398 | let g:notebook_cmd='{ script -qfc "open-axiom -nogr -noht -nox -noclef" /dev/null; }' 399 | \ . '| grep --line-buffered -Pv "\x0d$"' 400 | let g:notebook_stop=')quit' 401 | let g:notebook_send0="" 402 | let g:notebook_send='output \"\"; output \"VIMOPENAXIOMNOTEBOOK\"' 403 | let g:notebook_detect=' VIMOPENAXIOMNOTEBOOK' 404 | 405 | # Some tricks 406 | 407 | Two things may complicate the configuration: buffering and detection of a terminal. 408 | 409 | If the interpreter uses buffering when printing to its output stream, the plugin 410 | will not be able to detect the key at the end of the evaluation. In some cases 411 | you can easely fix it by using `stdbuf`. For instance with Maxima: 412 | 413 | let g:notebook_cmd='stdbuf -i0 -o0 -e0 /usr/bin/maxima' 414 | \ . ' --disable-readline --very-quiet' 415 | 416 | Another issue can result when the interpreter has two different behaviours, one 417 | when connected to a terminal (interactive use) or to a pipe (batch use). Whta you 418 | want is probably the interactive use; this can be solved with `script`. For instance 419 | you can start `bc` with 420 | 421 | let g:notebook_cmd='{ script -c bc /dev/null; }' 422 | 423 | Older versions of NGN APL were needing the following syntax; I leave it here in 424 | order to help integrating new interpreters: 425 | 426 | let g:notebook_cmd = '{ script -qfc "nodejs ~/Downloads/apl.js" /dev/null; }' 427 | \ . ' | grep --line-buffered -Pv "\x0d$"' 428 | 429 | # Adding a menu 430 | 431 | In the main video, interpreters are launched from a menu. This menu is not a part of 432 | the plugin; however I add my personal `mynotebook.vim` plugin containing my own 433 | settings; you may hack it according to your own needs. This second plugin basically 434 | adds a menu configuring _vim-notebook_ for each interpreter. 435 | 436 | In my own `.vimrc` I mapped the menu to the `~` key: 437 | 438 | map ² :emenu Notebook. 439 | 440 | # Other videos 441 | 442 | An older demo can be seen [there](https://www.youtube.com/watch?v=wCGydHdE4b8). 443 | 444 | [![Demo](http://img.youtube.com/vi/wCGydHdE4b8/0.jpg)](https://www.youtube.com/watch?v=wCGydHdE4b8) 445 | --------------------------------------------------------------------------------