Skip to content

Commit

Permalink
v0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
yunus-sun committed Feb 6, 2023
0 parents commit b25fcf9
Show file tree
Hide file tree
Showing 62 changed files with 9,100 additions and 0 deletions.
375 changes: 375 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

149 changes: 149 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
vim-matlab
===========

An alternative to Matlab's default editor for Vim users.

## FAQ

#### How do I run code cells (`%%` blocks)?

In Normal mode, press `<Enter>` or `<C-m>`. The editor will parse the code in the current cell and send to MATLAB for evaluation.

#### What if I need MATLAB's GUI features?

Most MATLAB windows can be launched through commands; even in `-nodisplay` mode. For example, [workspace](https://www.mathworks.com/help/matlab/ref/workspace.html) command opens the Workspace browser.

<img width="400" alt="screenshot 2016-08-18 20 57 56" src="https://cloud.githubusercontent.com/assets/1250682/17795452/82df34fe-6586-11e6-8049-6fc6712c9b0e.png">

If you need to access the debugger, use [edit](https://www.mathworks.com/help/matlab/ref/edit.html) to open the default GUI editor.

#### Error: `E492: Not an editor command`

If this the first time you're installing a Python plugin written for Neovim, you should install [python-client](https://github.com/neovim/python-client) and run `:UpdateRemotePlugins`.

The recommended way to install this plugin is to use the plugin manager [vim-plug](https://github.com/junegunn/vim-plug) and add this to your `.vimrc` or `init.vim`:

```vim
function! DoRemote(arg)
UpdateRemotePlugins
endfunction
Plug 'daeyun/vim-matlab', { 'do': function('DoRemote') }
```



## Usage

`vim-matlab` works by remotely controlling a CLI Matlab instance (launched by `vim-matlab-server.py`).

Run

```
./scripts/vim-matlab-server.py
```

This will start a Matlab REPL and redirect commands received from Vim to Matlab. When Matlab crashes (e.g. segfault during MEX development), it will launch another process.

Then open Vim in another terminal and start editing .m files.

Alternatively, launch a server instance from Vim using `:MatlabLaunchServer`. The server will be launched either in a Neovim terminal buffer or a tmux split (see [g:matlab_server_launcher](#configuration)).

- `:MatlabCliCancel` (\<leader\>c) tells the server to send SIGINT to Matlab, canceling current operation.

- `:MatlabCliRunSelection` executes the highlighted Matlab code.

- `:MatlabCliRunCell` executes code in the current cell — i.e. `%%` blocks. Similar to Ctrl-Enter in the Matlab editor.

- `:MatlabCliOpenInEditor` (,e) opens current buffer in a Matlab editor window. e.g. to access the debugger.

- `:MatlabCliHelp` (,h) prints help message for the word under the cursor.

- `:MatlabNormalModeCreateCell` (C-l) inserts a cell marker above the current line.

- `:MatlabVisualModeCreateCell` (C-l) inserts cell markers above and below the visual selection.

- `:MatlabInsertModeCreateCell` (C-l) inserts a cell marker at the beginning of the current line.

- `:MatlabLaunchServer` launches a server instance in a Vim or tmux split.

See [this file](rplugin/python/vim_matlab/__init__.py) for a list of available commands, and [vim-matlab.vim](ftplugin/matlab/vim-matlab.vim) for default key bindings.

![Screenshot](/docs/images/screenshot.png)



## Installation

1. Install the python3 client for Neovim:

```
pip3 install neovim
```

2. Add to `.vimrc` (or `~/.config/nvim/init.vim`). e.g. using [vim-plug](https://github.com/junegunn/vim-plug):

```
Plug 'daeyun/vim-matlab'
```

3. Register the plugin inside Neovim:

```
:UpdateRemotePlugins
```

### Optional steps (recommended)

4. Install a snippet engine such as [SirVer/ultisnips](https://github.com/SirVer/ultisnips) or [vim-snipmate](https://github.com/garbas/vim-snipmate) to use the [included snippets](https://github.com/daeyun/vim-matlab/blob/master/snippets/matlab.snippets). We also recommend installing [vim-snippets](https://github.com/honza/vim-snippets). We welcome any contributions to extend the `matlab.snippets` file.

5. Install pexpect for improved interactivity with the MATLAB console (e.g. tab completion):

```
pip3 install pexpect
```


## Configuration

Use `g:matlab_auto_mappings` to control whether the plugin automatically generates key mappings (default = 1).

```vim
let g:matlab_auto_mappings = 0 "automatic mappings disabled
let g:matlab_auto_mappings = 1 "automatic mappings enabled
```

Use `g:matlab_server_launcher` to control whether `:MatlabLaunchServer` uses a Vim or tmux split (default = `'vim'`).

```vim
let g:matlab_server_launcher = 'vim' "launch the server in a Neovim terminal buffer
let g:matlab_server_launcher = 'tmux' "launch the server in a tmux split
```

Use `g:matlab_server_split` to control whether `:MatlabLaunchServer` uses a vertical or horizontal split (default = `'vertical'`).

```vim
let g:matlab_server_split = 'vertical' "launch the server in a vertical split
let g:matlab_server_split = 'horizontal' "launch the server in a horizontal split
```


## Development

Set up a symlink so that the plugin directory points to your repository.

```
git clone [email protected]:daeyun/vim-matlab.git
rm -r ~/.vim/plugged/vim-matlab
ln -nsf $(pwd)/vim-matlab ~/.vim/plugged/
```

After changing the code, run `scripts/reload-vim.sh` (optionally pass in a file to open) to reload.

For testing, install [pytest](https://github.com/pytest-dev/pytest/) and run `scripts/run-tests.sh`.


## Recommended Plugins

- [MatlabFilesEdition](http://www.vim.org/scripts/script.php?script_id=2407)
- [matchit.zip](http://www.vim.org/scripts/script.php?script_id=39)
27 changes: 27 additions & 0 deletions autoload/vim_snippets.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
" this is well known Filename found in snipmate (and the other engines), but
" rewritten and documented :)
"
" optional arg1: string in which to replace '$1' by filename with extension
" and path dropped. Defaults to $1
" optional arg2: return this value if buffer has no filename
" But why not use the template in this case, too?
" Doesn't make sense to me
fun! vim_snippets#Filename(...)
let template = get(a:000, 0, "$1")
let arg2 = get(a:000, 1, "")

let basename = expand('%:t:r')

if basename == ''
return arg2
else
return substitute(template, '$1', basename, 'g')
endif
endf

" original code:
" fun! Filename(...)
" let filename = expand('%:t:r')
" if filename == '' | return a:0 == 2 ? a:2 : '' | endif
" return !a:0 || a:1 == '' ? filename : substitute(a:1, '$1', filename, 'g')
" endf
Binary file added docs/images/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions ftplugin/matlab/vim-matlab.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
setlocal shortmess+=A
setlocal formatoptions-=cro

if !exists('g:matlab_server_launcher')
let g:matlab_server_launcher = 'vim'
endif

if !exists('g:matlab_server_split')
let g:matlab_server_split = 'vertical'
endif

if g:matlab_server_launcher ==? 'tmux' && g:matlab_server_split ==? 'horizontal'
let s:split_command = ':!tmux split-window '
elseif g:matlab_server_launcher ==? 'tmux' && g:matlab_server_split ==? 'vertical'
let s:split_command = ':!tmux split-window -h '
elseif g:matlab_server_launcher ==? 'vim' && g:matlab_server_split ==? 'horizontal'
let s:split_command = ':split term://'
else
let s:split_command = ':vsplit term://'
endif
" let s:server_command = expand('<sfile>:p:h') . '/../../scripts/vim-matlab-server.py'
let s:server_command = expand('<sfile>:p:h') . '/../../vscode-matlab-interactive-terminal-master/interfaces/standard/ml_terminal.py'

command! MatlabLaunchServer :execute 'normal! ' . s:split_command . s:server_command . '<CR>'

command! MatlabNormalModeCreateCell :execute 'normal! :set paste<CR>m`O%%<ESC>``:set nopaste<CR>'
command! MatlabVisualModeCreateCell :execute 'normal! gvD:set paste<CR>O%%<CR>%%<ESC>P:set nopaste<CR>'
command! MatlabInsertModeCreateCell :execute 'normal! I%% '

if !exists('g:matlab_auto_mappings')
let g:matlab_auto_mappings = 1
endif

if g:matlab_auto_mappings
nnoremap <buffer> <leader>rn :MatlabRename
nnoremap <buffer><silent> <leader>fn :MatlabFixName<CR>
vnoremap <buffer><silent> <C-m> <ESC>:MatlabCliRunSelection<CR>
nnoremap <buffer><silent> <C-m> <ESC>:MatlabCliRunCell<CR>
nnoremap <buffer><silent> <C-h> :MatlabCliRunLine<CR>
nnoremap <buffer><silent> ,i <ESC>:MatlabCliViewVarUnderCursor<CR>
vnoremap <buffer><silent> ,i <ESC>:MatlabCliViewSelectedVar<CR>
nnoremap <buffer><silent> ,h <ESC>:MatlabCliHelp<CR>
nnoremap <buffer><silent> ,e <ESC>:MatlabCliOpenInMatlabEditor<CR>
nnoremap <buffer><silent> <leader>c :MatlabCliCancel<CR>
nnoremap <buffer><silent> <C-l> :MatlabNormalModeCreateCell<CR>
vnoremap <buffer><silent> <C-l> :<C-u>MatlabVisualModeCreateCell<CR>
inoremap <buffer><silent> <C-l> <C-o>:MatlabInsertModeCreateCell<CR>
endif
147 changes: 147 additions & 0 deletions indent/matlab.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
" Matlab indent file
" Language: Matlab
" Maintainer: Original author: Fabrice Guy <fabrice.guy at gmail dot com>
" Last Change: 2009 Nov 23 - Added support for if/end block on the same line
" 2015 Oct 29 - Fixed function indentation (daeyun)

" Only load this indent file when no other was loaded.
if exists("b:did_indent")
finish
endif
let b:did_indent = 1
let s:functionWithoutEndStatement = 0

setlocal indentexpr=GetMatlabIndent()
setlocal indentkeys=!,o,O=end,=case,=else,=elseif,=otherwise,=catch

" Only define the function once.
if exists("*GetMatlabIndent")
finish
endif

function! s:IsMatlabContinuationLine(lnum)
let continuationLine = 0
if a:lnum > 0
let pnbline = getline(prevnonblank(a:lnum))
" if we have the line continuation operator (... at the end of a line or
" ... followed by a comment) it may be a line continuation
if pnbline =~ '\.\.\.\s*$' || pnbline =~ '\.\.\.\s*%.*$'
let continuationLine = 1
" but if the ... are part of a string or a comment, it is not a
" continuation line
let col = match(pnbline, '\.\.\.\s*$')
if col == -1
let col = match(pnbline, '\.\.\.\s*%.*$')
endif
if has('syntax_items')
if synIDattr(synID(prevnonblank(a:lnum), col + 1, 1), "name") =~ "matlabString" ||
\ synIDattr(synID(prevnonblank(a:lnum), col + 1, 1), "name") =~ "matlabComment"
let continuationLine = 0
endif
endif
endif
endif
return continuationLine
endfunction

function GetMatlabIndent()
" Find a non-blank line above the current line.
let plnum = prevnonblank(v:lnum - 1)

" If the previous line is a continuation line, get the beginning of the block to
" use the indent of that line
if s:IsMatlabContinuationLine(plnum - 1)
while s:IsMatlabContinuationLine(plnum - 1)
let plnum = plnum - 1
endwhile
endif

" At the start of the file use zero indent.
if plnum == 0
return 0
endif

let curind = indent(plnum)
if s:IsMatlabContinuationLine(v:lnum - 1)
let curind = curind + &sw
endif
" Add a 'shiftwidth' after classdef, properties, switch, methods, events,
" function, if, while, for, otherwise, case, try, catch, else, elseif
if getline(plnum) =~ '^\s*\(classdef\|properties\|switch\|methods\|events\|function\|if\|while\|for\|otherwise\|case\|try\|catch\|else\|elseif\)\>'
let curind = curind + &sw
" In Matlab we have different kind of functions
" - the main function (the function with the same name than the filename)
" - the nested functions
" - the functions defined in methods (for classes)
" - subfunctions
" Principles for the indentation :
" - all the function keywords are indented (corresponding to the
" 'indent all functions' in the Matlab Editor)
" - if we have only subfonctions (ie if the main function doesn't have
" any mayching end), then each function is dedented
if getline(plnum) =~ '^\s*\function\>'
let pplnum = plnum - 1
while pplnum > 1 && (getline(pplnum) =~ '^\s*%')
let pplnum = pplnum - 1
endwhile
" If it is the main function, determine if function has a matching end
" or not
if pplnum <= 1
" look for a matching end :
" - if we find a matching end everything is fine : end of functions
" will be dedented when 'end' is reached
" - if not, then all other functions are subfunctions : 'function'
" keyword has to be dedended
let old_lnum = v:lnum
let motion = plnum . "gg"
execute "normal" . motion
normal %
if getline(line('.')) =~ '^\s*end'
let s:functionWithoutEndStatement = 0
else
let s:functionWithoutEndStatement = 1
endif
normal %
let motion = old_lnum . "gg"
execute "normal" . motion
endif
endif
" if the for-end block (or while-end) is on the same line : dedent
if getline(plnum) =~ '[,;]\?\s*end'
let curind = curind - &sw
endif
endif

" Subtract a 'shiftwidth' on a else, elseif, end, catch, otherwise, case
if getline(v:lnum) =~ '^\s*\(else\|elseif\|end\|catch\|otherwise\|case\)\>'
let curind = curind - &sw
endif
" No indentation in a subfunction
if getline(plnum) =~ '^\s*\function\>' && s:functionWithoutEndStatement
let curind = curind - &sw
endif
" First case after a switch : indent
if getline(v:lnum) =~ '^\s*case'
while plnum > 0 && (getline(plnum) =~ '^\s*%' || getline(plnum) =~ '^\s*$')
let plnum = plnum - 1
endwhile
if getline(plnum) =~ '^\s*switch'
let curind = indent(plnum) + &sw
endif
endif

" end in a switch / end block : dedent twice
" we use the matchit script to know if this end is the end of a switch block
if exists("b:match_words")
if getline(v:lnum) =~ '^\s*end'
normal %
if getline(line('.')) =~ '^\s*switch'
let curind = curind - &sw
endif
normal %
end
end
return curind
endfunction

" vim:sw=2
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
testpaths = tests
Empty file added rplugin/python3/vim_matlab.py
Empty file.
Loading

0 comments on commit b25fcf9

Please sign in to comment.