/vim-jukit

Jupyter-Notebook inspired Neovim/Vim Plugin

Primary LanguageVim ScriptMIT LicenseMIT

vim-jukit

REPL plugin and Jupyter-Notebook alternative for (Neo)Vim

This plugin is aimed at users in search for a REPL plugin with lots of additional features, including but not limited to the following:

  • Easily send code to a split window running your preferred shell
  • Structure your code with cell markers and use convenient cell operations
  • Dedicated markdown cells with markdown syntax
  • Seamlessly convert from and to .ipynb notebooks
  • Display plots inside the terminal if you're using kitty terminal or iTerm2+tmux and python's matplotlib
  • Save outputs of cell executions when using IPython and display saved outputs on demand

Preview

  • Convert ipynb notebooks to scripts and vice versa

convert_from_ipynb_new convert_to_ipynb_new

  • Cell manipulations: Create, delete, move, split, and merge cells

cell_operations_new

  • Send code to the terminal

send_to_terminal_new

  • Save output from ipython shell and display saved output on demand in dedicated split window

output_saving_new

  • Optionally display saved outputs in terminal as images using überzug instead of printing in split window (experimental)

überzug_new

  • Preview file as pdf, html

convert_to_html_pdf_new

  • For kitty-terminal users: optionally open splits in seperate os-windows (useful if you have multiple monitors)

seperate_os_window_new

  • For kitty-terminal users (and for iTerm2+tmux users - experimental): in-terminal plotting via matplotlib

inline_plotting_new

Requirements

vim users

 •  vim version >= 8.2
 •  python3 support (check using `:echo has('python3')`)

neovim users

 •  neovim version >= 0.4
 •  python3 support (check using `:echo has('python3')`)

(I)Python users

 •  ipython version >= 7.3.0
 •  matplotlib version >= 3.4.0

kitty terminal users

 •  kitty version >= 0.22
 •  remote control needs to be enabled in kitty config (i.e. put `allow_remote_control yes` in your kitty.conf), or alternatively you can also always start kitty using `kitty -o allow_remote_control=yes`
 •  ImageMagick for displaying plots in the terminal must be installed (install using e.g. `sudo apt-get install imagemagick`)
 •  If you're using neovim with kitty, you need to launch kitty with the `--listen-on` option and specify an address to listen on. Furthermore, if you want to have different kitty instances simultaneously using this plugin and sending code to split windows, different addresses will need to be specified. One possible way to do this on linux machines is by simply always starting kitty with e.g. `kitty --listen-on=unix:@"$(date +%s%N)"`, which will make sure different kitty instances are launched with different, abstract sockets to listen on. On MacOS it should work using e.g. `kitty --listen-on=/tmp/kitty_"$(date +%s%N)"`. If you want, you can then simply specify an alias (i.e. put `alias jukit_kitty="kitty --listen-on=unix:@"$(date +%s%N)" -o allow_remote_control=yes"` in your .bashrc/.zshrc) which you can use to always start kitty with the necessary arguments.

iTerm2+tmux users (experimental)

 •  currently only tested using iTerm2 Build 3.4.15 + tmux version 3.2a
 •  There's a good chance it won't work with a different tmux version. To install the exact version it was tested with, use the following commands:
 ```
 wget https://raw.githubusercontent.com/Homebrew/homebrew-core/e44425df5a8b3c8c24073486fa7e355f3ac19657/Formula/tmux.rb
 brew install ./tmux.rb
 tmux -V # make sure it says tmux 3.2a
 brew pin tmux # prevent unintentional upgrade in the future
 ```
 •  NOTE: I am not a macOS user myself. I've tried to implement inline plotting for iTerm2+tmux and got it working at one point. In case you have problems, feel free to open an issue but be prepared to rely on yourself or others to solve it, since debugging this as a non macOS-user can be time consuming and I have limited time for my open source projects these days.

windows users

 •  make sure `python3` - and not just `python` - is a valid command in your terminal, if it's not then set `let g:_jukit_python_os_cmd = 'python'` in your vim config
 •  NOTE: I am not a Windows user myself. I've tried to implement a working version for Windows and got it working at one point. In case you have problems, feel free to open an issue but be prepared to rely on yourself or others to solve it, since debugging this as a non Windows-user can be time consuming and I have limited time for my open source projects these days.

überzug users

 •  make sure `python3` - and not just `python` - is a valid command in your terminal, if it's not then set `let g:_jukit_python_os_cmd = 'python'` in your vim config
 •  required python packages:
   pillow
   beautifulsoup4
   numpy
   nbconvert >= 6.4.4
   ueberzug - NOTE: this package is no longer maintained and available via pip, so it has to be installed manually as follows:
    ```
    git clone --branch 18.1.9 https://github.com/seebye/ueberzug.git
    cd ueberzug
    python3 setup.py install
    ```
 •  required CLI tools:
   imagemagick
   cutycapt (alternatively you can also use wkhtmltoimage, if you decide to use wkhtmltoimage, `let g:jukit_ueberzug_cutycapt_cmd = '/path/to/wkhtmltoimage'` has to specified in your vim config)

Installation

With your plugin manager of choice, e.g. using vim-plug:

Plug 'luk400/vim-jukit' 

Issues

If there are any problems, feel free to open an issue. Be sure to include the operating system you're using, your vim or neovim version, python version, your terminal (kitty terminal or otherwise), and possibly relevant jukit-options you've configured. Be sure to try the plugin with an otherwise empty (neo)vim config and see if your problem persists, to narrow down possible conflicts with other settings/plugins that you're using.

Be aware that I work on this project for fun in my free time, so expect that you might not receive any help in a timely manner.

Usage

Basic usage in a nutshell (assuming default mappings)

  • Example using ipython:

If you have an ipynb file containing python code which you first need to convert, simply open it and press <leader>np. This will also preseve saved outputs.

In your python file, press <leader>os to start an output split. Now you can start sending code to the shell. Simply press <enter> to send the line of the current cursor position to the shell. Visually select code and press <enter> to send it to the shell. Press <leader><space> to send the code in the current cell to the shell. Only output of cell executions will be saved, ipython outputs from sending single lines or visual selections will not be saved.

Create a new cell below by pressing <leader>co, or <leader>cO to create one above. If you want to create a text/markdown cell below, use <leader>ct, or <leader>cT to create one above. You can also move cells up or down, split cells, or merge cells (see the mappings and explanations below).

Now say you've been coding for a while and want to know what the output of a specific cell was. Instead of searching for it by scrolling up in your shell or completely re-running it (which is often inconvenient for long-running code), you can press <leader>hs which will create a new split window where saved outputs will be displayed. Press <leader>so to display saved output of the current cell. To scroll up or down in the output-history-split, simply press <leader>j or <leader>k. If you don't need the output-history split anymore, simply press <leader>hd to close it again.

If you want to convert your .py file back to a .ipynb notebook, simply press <leader>np again. It'll convert it back and open it using jupyter-notebook. For all other functions and custimization options, please see the definitions and comments in the next sections.

  • Example using julia (or any other supported language):

In your (neo)vim config, specify the shell command via g:jukit_shell_cmd (e.g. let g:jukit_shell_cmd='julia'). If you don't want to specify this in your config because you usually don't work with julia and this is an exception, you can also simply use :let g:jukit_shell_cmd='julia' right before opening the output split. If you have an ipynb file with julia code, simply open it and press <leader>np. In the resulting julia file, press <leader>os to start an output split. Now you can start sending code to the shell. Simply press <enter> to send the line of the current cursor position to the shell. Visually select code and press <enter> to send it to the shell. Press <leader><space> to send the code in the current cell to the shell.

Create a new cell below by pressing <leader>co, or <leader>cO to create one above. If you want to create a text/markdown cell below, use <leader>ct, or <leader>cT to create one above. You can also move cells up or down, split cells, or merge cells (see the mappings and explanations below).

If you want to convert your .jl file back to a .ipynb notebook, simply press <leader>np again. It'll convert it back and open it using jupyter-notebook. For all other functions and custimization options, please see the definitions and comments in the next sections.

Options and global variables

For explanations see the comments underneath each variable. Make sure you set these variables in your config somewhere before the plugin is loaded.

Basic jukit options
let g:jukit_shell_cmd = 'ipython3'
"    - Specifies the command used to start a shell in the output split. Can also be an absolute path. Can also be any other shell command, e.g. `R`, `julia`, etc. (note that output saving is only possible for ipython)
let g:jukit_terminal = ''
"   - Terminal to use. Can be one of '', 'kitty', 'vimterm', 'nvimterm' or 'tmux'. If '' is given then will try to detect terminal (though this might fail, in which case it simply defaults to 'vimterm' or 'nvimterm' - depending on the output of `has("nvim")`)
let g:jukit_auto_output_hist = 0
"   - If set to 1, will create an autocmd with event `CursorHold` to show saved ipython output of current cell in output-history split. Might slow down (n)vim significantly, you can use `set updatetime=<number of milliseconds>` to control the time to wait until CursorHold events are triggered, which might improve performance if set to a higher number (e.g. `set updatetime=1000`).
let g:jukit_use_tcomment = 0
"   - Whether to use tcomment plugin (https://github.com/tomtom/tcomment_vim) to comment out cell markers. If not, then cell markers will simply be prepended with `g:jukit_comment_mark`
let g:jukit_comment_mark = '#'
"   - See description of `g:jukit_use_tcomment` above
let g:jukit_mappings = 1
"   - If set to 0, none of the default function mappings (as specified further down) will be applied
let g:jukit_mappings_ext_enabled = "*"
"   - String or list of strings specifying extensions for which the mappings will be created. For example, `let g:jukit_mappings_ext_enabled=['py', 'ipynb']` will enable the mappings only in `.py` and `.ipynb` files. Use `let g:jukit_mappings_ext_enabled='*'` to enable them for all files.
let g:jukit_notebook_viewer = 'jupyter-notebook'
"   - Command to open .ipynb files, by default jupyter-notebook is used. To use e.g. vs code instead, you could set this to `let g:jukit_notebook_viewer = 'code'`
let g:jukit_convert_overwrite_default = -1
"   - Default setting when converting from .ipynb to .py or vice versa and a file of the same name already exists. Can be of [-1, 0, 1], where -1 means no default (i.e. you'll be prompted to specify what to do), 0 means never overwrite, 1 means always overwrite
let g:jukit_convert_open_default = -1
"   - Default setting for whether the notebook should be opened after converting from .py to .ipynb. Can be of [-1, 0, 1], where -1 means no default (i.e. you'll be prompted to specify what to do), 0 means never open, 1 means always open
let g:jukit_file_encodings = 'utf-8'
"   - Default encoding for reading and writing to files in the python helper functions
let g:jukit_venv_in_output_hist = 1
"   - Whether to also use the provided terminal command for the output history split when starting the splits using the JukitOUtHist command. If 0, the provided terminal command is only used in the output split, not in the output history split.
Cell highlighting/syntax
let g:jukit_highlight_markers = 1
"    - Whether to highlight cell markers or not. You can specify the colors of cell markers by putting e.g. `highlight jukit_cellmarker_colors guifg=#1d615a guibg=#1d615a ctermbg=22 ctermfg=22` with your desired colors in your (neo)vim config. Make sure to define this highlight *after* loading a colorscheme in your (neo)vim config
let g:jukit_enable_textcell_bg_hl = 1
"    - Whether to highlight background of textcells. You can specify the color by putting `highlight jukit_textcell_bg_colors guibg=#131628 ctermbg=0` with your desired colors in your (neo)vim config. Make sure to define this highlight group *after* loading a colorscheme in your (neo)vim config.
let g:jukit_enable_textcell_syntax = 1
"    - Whether to enable markdown syntax highlighting in textcells
let g:jukit_text_syntax_file = $VIMRUNTIME . '/syntax/' . 'markdown.vim'
"    - Syntax file to use for textcells. If you want to define your own syntax matches inside of text cells, make sure to include `containedin=textcell`.
let g:jukit_hl_ext_enabled = '*'
"    - String or list of strings specifying extensions for which the relevant highlighting autocmds regarding marker-highlighting, textcell-highlighting, etc. will be created. For example, `let g:jukit_hl_extensions=['py', 'R']` will enable the defined highlighting options for `.py` and `.R` files. Use `let g:jukit_hl_extensions='*'` to enable them for all files and `let g:jukit_hl_extensions=''` to disable them completely
Kitty
let g:jukit_output_bg_color = get(g:, 'jukit_output_bg_color', '')
"    - Optional custom background color of output split window (i.e. target window of sent code)
let g:jukit_output_fg_color = get(g:, 'jukit_output_fg_color', '')
"    - Optional custom foreground color of output split window (i.e. target window of sent code)
let g:jukit_outhist_bg_color = get(g:, 'jukit_outhist_bg_color', '#090b1a')
"    - Optional custom background color of output-history window
let g:jukit_outhist_fg_color = get(g:, 'jukit_outhist_fg_color', 'gray')
"    - Optional custom foreground color of output-history window
let g:jukit_output_new_os_window = 0
"    - If set to 1, opens output split in new os-window. Can be used to e.g. write code in one kitty-os-window on your primary monitor while sending code to the shell which is in a seperate kitty-os-window on another monitor.
let g:jukit_outhist_new_os_window = 0
"    - Same as `g:jukit_output_new_os_window`, only for output-history-split
IPython
let g:jukit_in_style = 2
"    - Number between 0 and 4. Defines how the input-code should be represented in the IPython shell. One of 5 different styles can be chosen, where style 0 is the default IPython style for the IPython-`%paste` command
let g:jukit_max_size = 20
"    - Max Size of json containing saved output in MiB. When the output history json gets too large, certain jukit operations can get slow, thus a max size is specified. Once the max size is reached, you'll be asked to delete some of the saved outputs (using e.g. jukit#cells#delete_outputs - see function explanation further down) before further output can be saved.
let g:jukit_show_prompt = 0
"    - Whether to show (1) or hide (0) the previous ipython prompt after code is sent to the ipython shell

" IF AN IPYTHON SHELL COMMAND IS USED:
let g:jukit_save_output = 1
"    - Whether to save ipython output or not. This is the default value if an ipython shell command is used.
" ELSE:
let g:jukit_save_output = 0
"    - Whether to save ipython output or not. This is the default value if ipython is not used.

let g:jukit_clean_outhist_freq = 60 * 10
"    - Frequency in seconds with which to delete saved ipython output (including cached überzug images) of cells which are not present anymore. (After executing a cell of a buffer for the first time in a session, a CursorHold autocmd is created for this buffer which checks whether the last time obsolete output got deleted was more than `g:jukit_clean_outhist_freq` seconds ago, and if so, deletes all saved output of cells which are not present in the buffer anymore from the output-history-json)
Matplotlib
let g:jukit_savefig_dpi = 150
"    - Value for `dpi` argument for matplotlibs `savefig` function
let g:jukit_mpl_block = 1
"    - If set to 0, then `plt.show()` will by default be executed as if `plt.show(block=False)` was specified
let g:jukit_custom_backend = -1
"    - Custom matplotlib backend to use

" IF KITTY IS USED:
let g:jukit_mpl_style = jukit#util#plugin_path() . '/helpers/matplotlib-backend-kitty/backend.mplstyle'
"    - File specifying matplotlib plot options. This is the default value if kitty terminal is used
" ELSE:
let g:jukit_mpl_style = ''
"    - File specifying matplotlib plot options. This is the default value if kitty terminal is NOT used. If '' is specified, no custom mpl-style is applied.

" IF KITTY OR TMUX IS USED:
let g:jukit_inline_plotting = 1
"    - Enable in-terminal-plotting. Only supported for kitty or tmux+iTerm2 -> BE SURE TO SPECIFY THE TERMINAL VIA `g:jukit_terminal`! (see variables in section 'Basic jukit options')
" ELSE:
let g:jukit_inline_plotting = 0
"    - Disable in-terminal-plotting
Split layout
" You can define a custom split layout as a dictionary, the default is:
let g:jukit_layout = {
    \'split': 'horizontal',
    \'p1': 0.6, 
    \'val': [
        \'file_content',
        \{
            \'split': 'vertical',
            \'p1': 0.6,
            \'val': ['output', 'output_history']
        \}
    \]
\}

" this results in the following split layout:
"  ______________________________________
" |                      |               |
" |                      |               |
" |                      |               |
" |                      |               |
" |                      |     output    |
" |                      |               |
" |                      |               |
" |    file_content      |               |
" |                      |_______________|
" |                      |               |
" |                      |               |
" |                      | output_history|
" |                      |               |
" |                      |               |
" |______________________|_______________|
"
" The positions of all 3 split windows must be defined in the dictionary, even if 
" you don't plan on using the output_history split.
"
" dictionary keys:
" 'split':  Split direction of the two splits specified in 'val'. Either 'horizontal' or 'vertical'
" 'p1':     Proportion of the first split specified in 'val'. Value must be a float with 0 < p1 < 1
" 'val':    A list of length 2 which specifies the two splits for which to apply the above two options.
"           One of the two items in the list must be a string and one must be a dictionary in case of
"           the 'outer' dictionary, while the two items in the list must both be strings in case of
"           the 'inner' dictionary.
"           The 3 strings must be different and can be one of: 'file_content', 'output', 'output_history'
"
" To not use any layout, specify `let g:jukit_layout=-1`
Überzug
let g:jukit_hist_use_ueberzug = 0
"   - Set to 1 to use Überzug to display saved outputs instead of an ipython split window
let g:jukit_ueberzug_use_cached = 1
"   - Whether to cache created images of saved outputs. If set to 0, will convert saved outputs to png from scratch each time. Note that this will make displaying saved outputs significantly slower. 
let g:jukit_ueberzug_pos = [0.25, 0.25, 0.4, 0.6]
"   - position and dimension of Überzug window WITH output split present - [x, y, width, height]. Use `:call jukit#ueberzug#set_default_pos()` to modify/visualize.
let g:jukit_ueberzug_pos_noout = [0.25, 0.25, 0.4, 0.6]
"   - position and dimension of Überzug window WITHOUT output split present - [x, y, width, height]. Use `:call jukit#ueberzug#set_default_pos()` to modify/visualize.
let g:jukit_kill_ueberzug_on_focus_lost = 1
"   - whether to kill ueberzug when the focus to neovim is lost (detecting focus might only work on neovim). if set to 0, the ueberzug image keeps being displayed even when neovim loses focus (e.g. when switching tabs in terminal).

let g:jukit_ueberzug_border_color = get(g:, 'jukit_ueberzug_border_color', 'blue')
"   - border color of Überzug images
let g:jukit_ueberzug_theme = 'dark'
"   - choose dark or light theme for markdown cells
let g:jukit_ueberzug_term_hw_ratio = -1
"   - this is relevant in case the shown ueberzug image is cut off horizontally. In that case, the determined width/height ratio of your terminal cells is determined incorrectly. A value of -1 means the ratio should be determined automatically. A ratio of 2.2 is used by default if the ratio can't be determined automatically. If you get a cut off image, try setting this parameter and vary the values around 2.0 (e.g. `let g:jukit_ueberzug_term_hw_ratio = 2.3` or `let g:jukit_ueberzug_term_hw_ratio = 1.9`) until the image is displayed correctly to determine your needed ratio.
let g:jukit_ueberzug_python_cmd = 'python3'
"   - path to python3 executable for which the überzug requirements (beautifulsoup4, pillow, ueberzug) are installed. By default it just uses the python3 command found in your environment. If you started an output split in a virtual environment, make sure that you either have all the requirements in the virtual requirements or set the absolute path to the python3 command.
let g:jukit_ueberzug_jupyter_cmd = 'jupyter'
"   - path to jupyter executable. By default it just uses the jupyter command found in your environment. If you started an output split in a virtual environment, make sure that you either have jupyter installed in that environment or set the absolute path to the python3 command.
let g:jukit_ueberzug_cutycapt_cmd = 'cutycapt'
"   - path to cutycapt executable
let g:jukit_ueberzug_imagemagick_cmd = 'convert'
"   - path to imagemagick (`convert` command) executable

Notes:

  • If you're using ueberzug with virtual environments, be sure that all required packages and commands are available in that environment OR specify absolute paths in ueberzug variables above
  • If ueberzug is used and the python process is killed while the saved output is converted to an image (which is done automatically each time a cell is executed), it's possible that a template image (i.e. update images which are shown during the process of converting) is cached as the final image and so ueberzug will be stuck displaying this image. If this ever happens, you can simply use :let g:jukit_ueberzug_use_cached = 0 | call jukit#splits#show_last_cell_output(1) | let g:jukit_ueberzug_use_cached = 1 to recreate the image (or you can just re-execute the cell, which will also recreate the image)

Functions and Default Mappings

For function explanations see the comments below each mapping

Splits
nnoremap <leader>os :call jukit#splits#output()<cr>
"   - Opens a new output window and executes the command specified in `g:jukit_shell_cmd`
nnoremap <leader>ts :call jukit#splits#term()<cr>
"   - Opens a new output window without executing any command
nnoremap <leader>hs :call jukit#splits#history()<cr>
"   - Opens a new output-history window, where saved ipython outputs are displayed
nnoremap <leader>ohs :call jukit#splits#output_and_history()<cr>
"   - Shortcut for opening output terminal and output-history
nnoremap <leader>hd :call jukit#splits#close_history()<cr>
"   - Close output-history window
nnoremap <leader>od :call jukit#splits#close_output_split()<cr>
"   - Close output window
nnoremap <leader>ohd :call jukit#splits#close_output_and_history(1)<cr>
"   - Close both windows. Argument: Whether or not to ask you to confirm before closing.
nnoremap <leader>so :call jukit#splits#show_last_cell_output(1)<cr>
"   - Show output of current cell (determined by current cursor position) in output-history window. Argument: Whether or not to reload outputs if cell id of outputs to display is the same as the last cell id for which outputs were displayed
nnoremap <leader>j :call jukit#splits#out_hist_scroll(1)<cr>
"   - Scroll down in output-history window. Argument: whether to scroll down (1) or up (0)
nnoremap <leader>k :call jukit#splits#out_hist_scroll(0)<cr>
"   - Scroll up in output-history window. Argument: whether to scroll down (1) or up (0)
nnoremap <leader>ah :call jukit#splits#toggle_auto_hist()<cr>
"   - Create/delete autocmd for displaying saved output on CursorHold. Also, see explanation for `g:jukit_auto_output_hist`
nnoremap <leader>sl :call jukit#layouts#set_layout()<cr>
"   - Apply layout (see `g:jukit_layout`) to current splits - NOTE: it is expected that this function is called from the main file buffer/split
Sending code
nnoremap <leader><space> :call jukit#send#section(0)<cr>
"   - Send code within the current cell to output split (also saves the output if ipython is used and `g:jukit_save_output==1`). Argument: if 1, will move the cursor to the next cell below after sending the code to the split, otherwise cursor position stays the same.
nnoremap <cr> :call jukit#send#line()<cr>
"   - Send current line to output split
vnoremap <cr> :<C-U>call jukit#send#selection()<cr>
"   - Send visually selected code to output split
nnoremap <leader>cc :call jukit#send#until_current_section()<cr>
"   - Execute all cells until the current cell
nnoremap <leader>all :call jukit#send#all()<cr>
"   - Execute all cells
Cells
nnoremap <leader>co :call jukit#cells#create_below(0)<cr>
"   - Create new code cell below. Argument: Whether to create code cell (0) or markdown cell (1)
nnoremap <leader>cO :call jukit#cells#create_above(0)<cr>
"   - Create new code cell above. Argument: Whether to create code cell (0) or markdown cell (1)
nnoremap <leader>ct :call jukit#cells#create_below(1)<cr>
"   - Create new textcell below. Argument: Whether to create code cell (0) or markdown cell (1)
nnoremap <leader>cT :call jukit#cells#create_above(1)<cr>
"   - Create new textcell above. Argument: Whether to create code cell (0) or markdown cell (1)
nnoremap <leader>cd :call jukit#cells#delete()<cr>
"   - Delete current cell
nnoremap <leader>cs :call jukit#cells#split()<cr>
"   - Split current cell (saved output will then be assigned to the resulting cell above)
nnoremap <leader>cM :call jukit#cells#merge_above()<cr>
"   - Merge current cell with the cell above
nnoremap <leader>cm :call jukit#cells#merge_below()<cr>
"   - Merge current cell with the cell below
nnoremap <leader>ck :call jukit#cells#move_up()<cr>
"   - Move current cell up
nnoremap <leader>cj :call jukit#cells#move_down()<cr>
"   - Move current cell down
nnoremap <leader>J :call jukit#cells#jump_to_next_cell()<cr>
"   - Jump to the next cell below
nnoremap <leader>K :call jukit#cells#jump_to_previous_cell()<cr>
"   - Jump to the previous cell above
nnoremap <leader>ddo :call jukit#cells#delete_outputs(0)<cr>
"   - Delete saved output of current cell. Argument: Whether to delete all saved outputs (1) or only saved output of current cell (0)
nnoremap <leader>dda :call jukit#cells#delete_outputs(1)<cr>
"   - Delete saved outputs of all cells. Argument: Whether to delete all saved outputs (1) or only saved output of current cell (0)
ipynb conversion
nnoremap <leader>np :call jukit#convert#notebook_convert("jupyter-notebook")<cr>
"   - Convert from ipynb to py or vice versa. Argument: Optional. If an argument is specified, then its value is used to open the resulting ipynb file after converting script.
nnoremap <leader>ht :call jukit#convert#save_nb_to_file(0,1,'html')<cr>
"   - Convert file to html (including all saved outputs) and open it using the command specified in `g:jukit_html_viewer'. If `g:jukit_html_viewer` is not defined, then will default to `g:jukit_html_viewer='xdg-open'`. Arguments: 1.: Whether to rerun all cells when converting 2.: Whether to open it after converting 3.: filetype to convert to 
nnoremap <leader>rht :call jukit#convert#save_nb_to_file(1,1,'html')<cr>
"   - same as above, but will (re-)run all cells when converting to html
nnoremap <leader>pd :call jukit#convert#save_nb_to_file(0,1,'pdf')<cr>
"   - Convert file to pdf (including all saved outputs) and open it using the command specified in `g:jukit_pdf_viewer'. If `g:jukit_pdf_viewer` is not defined, then will default to `g:jukit_pdf_viewer='xdg-open'`. Arguments: 1.: Whether to rerun all cells when converting 2.: Whether to open it after converting 3.: filetype to convert to. NOTE: If the function doesn't work there may be issues with your nbconvert or latex version - to debug, try converting to pdf using `jupyter nbconvert --to pdf --allow-errors --log-level='ERROR' --HTMLExporter.theme=dark </abs/path/to/ipynb> && xdg-open </abs/path/to/pdf>` in your terminal and check the output for possible issues.
nnoremap <leader>rpd :call jukit#convert#save_nb_to_file(1,1,'pdf')<cr>
"   - same as above, but will (re-)run all cells when converting to pdf. NOTE: If the function doesn't work there may be issues with your nbconvert or latex version - to debug, try converting to pdf using `jupyter nbconvert --to pdf --allow-errors --log-level='ERROR' --HTMLExporter.theme=dark </abs/path/to/ipynb> && xdg-open </abs/path/to/pdf>` in your terminal and check the output for possible issues.

NOTE: in case you're using the snap version of firefox to display ipynb notebooks, see this issue.

Überzug
nnoremap <leader>pos :call jukit#ueberzug#set_default_pos()<cr>
"   - set position and dimension of überzug window

Commands

:JukitOut some command to be run before opening shell
:JukitOutHist some command to be run before opening shell

When working in a virtual environment, you can activate it before running the shell command using the JukitOut or JukitOutHist command, for example:

:JukitOut conda activate MyCondaEnv

This will open a new output split, activate the virtual conda-environment, and then start the output shell as usual. JukitOutHist does the same thing but will additionally open an output-history window.

Creating your own convenience functions

Using jukit#send#send_to_split, you can create mappings for commands you often use in your programming workflow. Here are a few examples which I personally use regularly:

  • Example 1: When working with pandas in python, I often find myself typing df.columns to print out the columns of the dataframe in the shell. The following makes it so I can simply visually select the variable df, press C, and df.columns will be sent to the output split.
fun! DFColumns()
    let visual_selection = jukit#util#get_visual_selection()
    let cmd = visual_selection . '.columns'
    call jukit#send#send_to_split(cmd)
endfun
vnoremap C :call DFColumns()<cr>
  • Example 2: Displaying help and documentation for a given function or object. The following makes it so I can visually select an object/function and press H to get documentation for it. E.g.: simply visually select df.plot in your code, press H and it'll display the documentation for the plot method of the dataframe.
fun! PythonHelp()
    let visual_selection = jukit#util#get_visual_selection()
    let cmd = 'help(' . visual_selection . ')'
    call jukit#send#send_to_split(cmd)
endfun
vnoremap H :call PythonHelp()<cr>
  • Example 3: Getting all attributes of a visually selected object which contain the specified string argument. E.g.: visually select df in your code, press A, type "set" (in quotes!), then press enter, and it'll display all attributes of the pandas dataframe containing "set" in their name.
fun! GetAttr(str)
    let visual_selection = jukit#util#get_visual_selection()
    let cmd = '[el for el in dir(' . visual_selection . ') if "' . a:str . '" in el.lower()]'
    call jukit#send#send_to_split(cmd)
endfun
command! -nargs=1 GetAttr :call GetAttr(<args>)
vnoremap A :<c-u>GetAttr

Jupyter notebook conversion - currently supported languages

Below you'll find the currently supported languages for converting notebooks to scripts and vice versa. It's very easy to add support for most languages (should only be a single line in the ipynb_convert helper module). If you're working with a language which is currently not listed below, please create a quick issue specifying the missing language and I'll try to add it.

Already supported:

  • python
  • r
  • matlab
  • julia
  • java
  • rust
  • lua

Notes to be aware of

  • vim-jukit creates a directory called .jukit in the directory of your relevant python script for communicating with ipython and for saving ipython outputs
  • If you want to save cell outputs from ipython, you should always try using jukit functions to create/delete cell markers (i.e. use the jukit#cells#create_...(), jukit#cells#delete(), jukit#cells#merge_...(), jukit#cells#split() functions) instead of simply deleting them with e.g. dd or yanking and pasting them to create new ones. This is because the cell ids assigned to cells above/below of a marker (by which the saved outputs for specific cells are identified) are encoded in the line of the cell marker, and simply deleting those without correcting the cell-ids in lines of adjacent cell markers or creating duplicate cell ids by yanking and pasting them may lead to unexpected cell-id-assignments for saved outputs (even though vim-jukit tries to detect and correct such manual cell marker modifications).
  • If you need to switch from sending code to the output split via the ipython magic command to directly sending the text to the output split without using magic commands (for example when debugging using pdb), you can do so by using :let g:jukit_ipython=0, and :let g:jukit_ipython=1 to switch back to using jukit ipython-magic
  • Converting .ipynb files currently only works for notebooks with notebook-format v4+, older notebook versions must first be converted using e.g. jupyter nbconvert --to notebook --nbformat 4 <FILENAME>
  • if you're often working with different languages and don't always want to manually set the g:jukit_comment_mark variable to comment out created cell markers when switching filetypes, you can install the tcomment plugin and specify let g:jukit_use_tcomment = 1 in your (neo)vim config.

Credit

vim-jukit uses a for this plugin modified version of the module ipynb_py_convert as well as a modified version of matplotlib-backend-kitty, which were the starting point and the initial inspiration for this plugin. It also uses the imgcat script from python-imgcat for displaying matplotlib plots in terminal when using tmux+iterm2.