├── .gitignore ├── README.md ├── autoload └── onestatus.vim └── plugin └── onestatus.vim /.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *Disclaimer*: This plugin has been tested with neovim so vim support is not garanteed. Feel free to open a PR if you want to contribute ! 2 | 3 | # Motivation 4 | OneStatus is an interface that helps you interact with your tmux.
5 | One of my goal with it was to get rid of vim's redundant statusline and instead use tmux's. 6 | 7 | Much better ! 8 | 9 | ![onestatus](https://user-images.githubusercontent.com/26607946/90639803-7f947f00-e22f-11ea-863e-e347f9379dfe.png) 10 | 11 | # Requirements 12 | If you just want to quickly use the plugin : 13 | - a font that supports powerline to have those nice arrow separators 14 | - https://github.com/tpope/vim-fugitive or https://github.com/itchyny/vim-gitbranch installed to display your git head 15 | - copy the `onestatus.json` example given below (this file must be present in your vim config directory) 16 | 17 | If you want to play with the API : 18 | - make sure to disable the default config by setting 19 | `let g:onestatus_default_layout = 0` 20 | - create your own personal `onestatus.json` 21 | Have fun ! 22 | 23 | # Usage 24 | Since v0.2.0 you can very easily customize your statusline via a `onestatus.json` file that you put in your **config folder ($HOME for vim and $HOME/.config/nvim for nvim)**.
25 | If you want another path you can override `g:onestatus_config_path`
26 | Here's an example of configuration file that you can copy 27 | 28 | ```json 29 | { 30 | "status-right": [ 31 | { "fg": "#ffd167", "bg": "default", "label": "" }, 32 | { "fg": "#218380", "bg": "#ffd167", "labelFunc": "s:getCWD" }, 33 | { "fg": "#218380", "bg": "#ffd167", "label": "" }, 34 | { "fg": "#fcfcfc", "bg": "#218380", "labelFunc": "s:getHead" } 35 | ], 36 | "status-left": [ 37 | { "fg": "default", "bg": "default", "labelFunc": "s:getFileName" } 38 | ], 39 | "status-style": "s:getDefaultColor", 40 | "window-status-style": [ 41 | { "fg": "#6c757d", "bg": "default", "isStyleOnly": true } 42 | ], 43 | "window-status-current-style": [ 44 | { "fg": "#ffd167", "bg": "default", "isStyleOnly": true } 45 | ] 46 | } 47 | ``` 48 | 49 | Which will give your current git head and the name of the focused file just like shown in the screenshot 50 | 51 | Then you can use the full power of onestatus like in this example 52 | ``` 53 | if !empty($TMUX) 54 | au BufEnter,BufLeave,FocusGained * :OneStatus 55 | au VimLeave * :OneStatusClean 56 | set noshowmode noruler 57 | set laststatus=0 58 | endif 59 | ``` 60 | For `OneStatus` you can use `BufEnter` and use `WinEnter` or some other events but I found it sufficient for my everyday use. 61 | `OneStatusClean` on the other end is optional and is a convinient way to clean your tmux status bar when you exit your current vim process. 62 | 63 | ## The Internals 64 | The plugin's implementation is simple, it runs `tmux source 'the content of your json file'`, everything else is just deserialization and formatting. 65 | 66 | Let us write an example that displays the current file extension on the left. 67 | In your onestatus.json 68 | ```json 69 | "status-left": [ 70 | { "fg": "default", "bg": "default", "labelFunc": "CurrentFileExtension" } 71 | ] 72 | ``` 73 | And make a function in your `$MYVIMRC` to return the file type. 74 | ```vim 75 | fun CurrentFileExtension() 76 | return expand('%:e') 77 | endfun 78 | ``` 79 | 80 | The `labelFunc` attribute takes a function name and sends its output to tmux 81 | ``` 82 | tmux set-option status-left -g fg=default bg=default "{the output of function}" 83 | ``` 84 | 85 | ## The API 86 | You must have noticed that the json file has these types of attributes 87 | - tmux option: an option that will be sent to tmux, you can learn more about them in `man tmux` 88 | - tmux color: can be any color format supported by tmux (ex: #ffd167) 89 | - onestatus' builtin function: they begin with `s:` like `s:getFileName` 90 | 91 | and has this form 92 | ``` 93 | { 94 | option: attributes 95 | } 96 | ``` 97 | 98 | In order to give a maximum amount of flexibility, attributes can either be an array of attribute of this shape: 99 | ``` 100 | { 101 | "fg": optional tmux color (will default to tmux's default), 102 | "bg": optional tmux color (will default to tmux's default), 103 | "label": the text to be displayed if your option takes a string as an argument (ex: status-left ), 104 | "labelFunc": you can dynamicaly display labels by using one of the functions exposed by onestatus (will take precedance over "label"), 105 | "isStyleOnly": a boolean that has to be set to true if your option does not display a label 106 | } 107 | ``` 108 | 109 | **NB1**: You can also use also use a onestatus function to dynamically generate your attributes. 110 | Currently only `s:getDefaultColor` is supported.
111 | **NB2**: You can call any global function in labelFunc. Try putting directly the name of a function of your own ! 112 | 113 | ## Exposed functions 114 | labelFunc: 115 | - `s:getCWD` 116 | - `s:getFileName` 117 | - `s:getHead` 118 | 119 | attributes: 120 | - `s:getDefaultColor` (it will make your statusline's background match with your vim's theme) 121 | 122 | ## Exposed global variables 123 | - `g:onestatus_default_layout`: can be `0` or `1`. Defaults to `1` 124 | Setting it to 0 prevents onestatus from applying some arbitrary layout style. Useful when you want to fully customize your statusline. 125 | - `g:onestatus_config_path`: a path string 126 | It contains the default path where onestatus will look for a `onestatus.json`. You can override it if you want to use a custom path. 127 | - `g:onestatus_right_length`: the max length of your right status. Defaults to 50 128 | - `g:onestatus_left_length`: the max length of your left status. Defaults to 50 129 | 130 | ## For even more customization 131 | OneStatus also provides a helper to send more straightforward commands to tmux 132 | 133 | ```vim 134 | call onestatus#build([ 135 | \{'command' : 'set-option status-justify centre'}, 136 | \{'command': 'set-option status-right-length 30'}, 137 | \{'command': 'set-option status-left-length 50'}, 138 | \]) 139 | ``` 140 | 141 | # TODO 142 | - Write a proper `:h` 143 | - Add more default templates 144 | - Improve default template theme integration 145 | - Add more builtin functions 146 | - Setup automated tests on develop branch 147 | 148 | # License 149 | Distributed under the same terms as Vim itself. See the vim license. 150 | -------------------------------------------------------------------------------- /autoload/onestatus.vim: -------------------------------------------------------------------------------- 1 | fun onestatus#build(cmds) abort 2 | let index = 0 3 | while index < len(a:cmds) 4 | if !has_key(a:cmds[index], 'command') 5 | echoer 'command attribute missing in object ' . index 6 | endif 7 | let index = index + 1 8 | endwhile 9 | call s:apply(s:buildLine(a:cmds)) 10 | endfun 11 | 12 | fun s:wrap_in_quotes(text) 13 | return '"' . escape(a:text, '"') . '"' 14 | endfun 15 | 16 | fun s:apply(line_settings) abort 17 | try 18 | let save_cpo = &cpo 19 | set cpo&vim 20 | let temp_file = tempname() 21 | call writefile(a:line_settings, temp_file) 22 | call system("tmux source ". s:wrap_in_quotes(temp_file)) 23 | finally 24 | let &cpo = save_cpo 25 | unlet save_cpo 26 | call delete(temp_file) 27 | endtry 28 | endfun 29 | 30 | fun s:buildLine(parts) 31 | let status = [] 32 | for sections in a:parts 33 | call add(status, s:buildPart(sections)) 34 | endfor 35 | return status 36 | endfun 37 | 38 | fun s:buildSection(attrs) 39 | let bg = get(a:attrs, 'bg', '') 40 | let fg = get(a:attrs, 'fg', '') 41 | let label = get(a:attrs, 'label', '') 42 | let fmt = get(a:attrs, 'isStyleOnly', v:false) ? 'fg=%s,bg=%s%s' : '"#[fg=%s,bg=%s]%s"' 43 | let parts = printf(fmt, fg, bg, label) 44 | return parts 45 | endfun 46 | 47 | fun s:buildPart(sections) 48 | let part = [] 49 | if has_key(a:sections, 'attributes') 50 | for sect in a:sections.attributes 51 | call add(part, s:buildSection(sect)) 52 | endfor 53 | endif 54 | let res = printf('%s %s', a:sections.command, join(part, '')) 55 | return res 56 | endfun 57 | -------------------------------------------------------------------------------- /plugin/onestatus.vim: -------------------------------------------------------------------------------- 1 | if !exists(':OneStatus') 2 | if !exists('g:onestatus_default_layout') 3 | let g:onestatus_default_layout = 1 4 | command! OneStatus call s:setCurDir() | call onestatus#build(s:onestatusDefault()) 5 | endif 6 | command! OneStatusClean call s:cleanUp() | call onestatus#build(s:onestatusDefault()) 7 | endif 8 | 9 | if exists('g:loaded_onestatus') 10 | finish 11 | endif 12 | 13 | if !exists('g:onestatus_config_path') 14 | let s:currentPath = expand('$HOME') 15 | if has('nvim') 16 | let s:currentPath = s:currentPath . '/.config/nvim' 17 | endif 18 | let g:onestatus_config_path = s:currentPath . '/onestatus.json' 19 | endif 20 | 21 | if !exists('g:onestatus_right_length') 22 | let g:onestatus_right_length = 50 23 | endif 24 | 25 | if !exists('g:onestatus_left_length') 26 | let g:onestatus_left_length = 50 27 | endif 28 | 29 | let g:loaded_onestatus = 1 30 | let g:cwd_formated = "" 31 | let g:isVimLeaving = v:false 32 | 33 | function s:setCurDir() 34 | let cwd = getcwd() 35 | let g:cwd_formated = printf(' ~/%s ', get(split(cwd, '/')[-1:], 0, 'root')) 36 | endfun 37 | 38 | fun s:cleanUp() 39 | let g:isVimLeaving = v:true 40 | endfun 41 | 42 | fun s:getCWD() 43 | if g:isVimLeaving 44 | return '' 45 | endif 46 | 47 | return g:cwd_formated 48 | endfun 49 | 50 | fun s:getFileName() 51 | if g:isVimLeaving 52 | return '' 53 | endif 54 | 55 | return &filetype != '' ? printf(' %s ', expand("%:t")) : '' 56 | endfun 57 | 58 | fun s:getHead() 59 | if g:isVimLeaving 60 | return '' 61 | endif 62 | 63 | let s:headRef = { -> '' } 64 | if exists('g:loaded_fugitive') 65 | let s:headRef = funcref('FugitiveHead') 66 | elseif exists('g:loaded_gitbranch') 67 | let s:headRef = funcref('gitbranch#name') 68 | else 69 | echo 'OneStatus: please install vim-gitbranch or vim-fugitive to use s:getHead' 70 | return '' 71 | endif 72 | let head = s:headRef() 73 | if (head == "") 74 | return "" 75 | endif 76 | return printf("  %s ", head) 77 | endfun 78 | 79 | fun s:getDefaultColor() abort 80 | let s:fg = synIDattr(synIDtrans(hlID('Normal')), 'fg#') 81 | let s:bg = synIDattr(synIDtrans(hlID('Normal')), 'bg#') 82 | let s:attrs = {'isStyleOnly': v:true, 'fg': s:fg, 'bg': (len(s:bg) == 0 ? 'default' : s:bg)} 83 | return [s:attrs] 84 | endfun 85 | 86 | fun s:execLabelFuncs(val) 87 | for [key, val] in items(a:val) 88 | if key == "labelFunc" 89 | let a:val.label = function(val)() 90 | unlet a:val.labelFunc 91 | endif 92 | endfor 93 | return a:val 94 | endfun 95 | 96 | fun s:getConfig(path) abort 97 | let config = json_decode(readfile(a:path)) 98 | let template = [] 99 | for [key, val] in items(config) 100 | 101 | if type(val) == v:t_list 102 | let fmtVal = map(val, { _,dict -> s:execLabelFuncs(dict) }) 103 | elseif type(val) == v:t_string 104 | let fmtVal = function(val)() 105 | else 106 | throw printf('% has bad attribute type', key) 107 | endif 108 | 109 | call add(template, { 'command': 'set-window-option -g ' . key, 'attributes': fmtVal }) 110 | endfor 111 | return template 112 | endfun 113 | 114 | fun s:onestatusDefault() 115 | let configPath = expand(g:onestatus_config_path) 116 | if !filereadable(configPath) 117 | echo 'OneStatus: onestatus.json not found. Please Provide one at ' . s:currentPath . ' or override g:onestatus_config_path' 118 | return [] 119 | endif 120 | return s:getConfig(configPath) 121 | endfun 122 | 123 | " set default config 124 | if g:onestatus_default_layout == 1 125 | call onestatus#build([ 126 | \{'command' : 'set-option status-justify centre'}, 127 | \{'command': 'set-option status-right-length ' . g:onestatus_right_length}, 128 | \{'command': 'set-option status-left-length ' . g:onestatus_left_length}, 129 | \]) 130 | endif 131 | --------------------------------------------------------------------------------