├── UNLICENSE ├── README.md └── plugin └── longlines.vim /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | vim-longlines 2 | ============= 3 | 4 | A Vim plugin to help navigate through long soft-wrapped lines. 5 | 6 | Installation 7 | ------------ 8 | 9 | Use your favorite plugin manager or install as a Vim package (see `:help 10 | packages`). 11 | 12 | Usage 13 | ----- 14 | 15 | After installing the plugin, use `:LongLines` to toggle the longline 16 | mode and `:LongLines!` to turn it off. It's also possible to 17 | automatically enable the longline mode for certain filetypes by using an 18 | autocommand: 19 | 20 | ```vim 21 | " Enable the longlines plugin for TeX and MediaWiki files. 22 | autocmd FileType mediawiki,tex LongLines 23 | ``` 24 | 25 | When the longline mode is on, motion commands such as `j`, `k`, `gg`, 26 | `G`, etc., work on display lines rather than actual lines. Although the 27 | longline mode replicates most commands reasonably well, some mappings 28 | (e.g., `dd`, `V`, etc.) don't work very well, and scrolling moves the 29 | cursor. 30 | 31 | ### Options 32 | 33 | When the longline mode is on, text is not hardwrapped by default and 34 | options that enable automatic hardwrapping of text (e.g., Vim's default 35 | `fo=tcq`) are altered to prevent this. If you wish to keep these 36 | options unaltered, set the global variable `g:longlines_keep_opts` or 37 | the buffer variable `b:longlines_keep_opts` to a nonzero value: 38 | 39 | ```Vim 40 | let g:longlines_keep_opts = 1 41 | ``` 42 | 43 | Similarly, when the longline mode is on, all motions commands are 44 | remapped to work on display lines, even when user-defined maps exist. 45 | If you wish to preserve already existing mappings, set the global 46 | variable `g:longlines_keep_maps` or the buffer variable 47 | `b:longlines_keep_maps` to a nonzero value: 48 | 49 | ```Vim 50 | let g:longlines_keep_maps = 1 51 | ``` 52 | 53 | To always enable the longline mode, set the global variable 54 | `g:longlines_always_enable` to a nonzero value: 55 | 56 | ```Vim 57 | let g:longlines_always_enable = 1 58 | ``` 59 | 60 | License 61 | ------- 62 | 63 | Public domain. See the file UNLICENSE for more details. 64 | -------------------------------------------------------------------------------- /plugin/longlines.vim: -------------------------------------------------------------------------------- 1 | " Vim plugin to navigate through long soft-wrapped lines 2 | " Version: 0.1 3 | " License: Public domain 4 | 5 | if exists('g:longlines_loaded') || &compatible || v:version < 700 6 | finish 7 | endif 8 | 9 | let g:longlines_loaded = 1 10 | 11 | " List of options that longlines will change. 12 | let s:optkeys = ['colorcolumn', 'formatoptions', 'linebreak', 'textwidth', 'wrap', 'wrapmargin', 'cursorlineopt'] 13 | 14 | augroup longlines 15 | autocmd! 16 | autocmd OptionSet,VimEnter,VimResized,WinEnter * 17 | \ let w:longlines_lines = winheight(0) | 18 | \ let w:longlines_half_lines = winheight(0) / 2 | 19 | \ let w:longlines_columns = s:longlines_width() 20 | augroup END 21 | 22 | " Synopsis: s:longlines_width() 23 | " https://stackoverflow.com/q/26315925 24 | function! s:longlines_width() 25 | redir! => sign_list | execute 'silent sign place buffer='.bufnr('%') | redir END 26 | let number_width = max([&numberwidth, strlen(line('$')) + 1]) 27 | return winwidth(0) 28 | \ - &foldcolumn 29 | \ - ((&number || &relativenumber) ? number_width : 0) 30 | \ - (sign_list =~# '^\n[^\n]*\n$' ? 0 : 2) 31 | endfunction 32 | 33 | " Function to map keys and save existing mapping (if any). 34 | " Synopsis: s:longlines_map({lhs}, {rhs}, {mode}, {expr}) 35 | function! s:longlines_map(lhs, rhs, mode, expr) abort 36 | let map_dict = maparg(a:lhs, a:mode, 0, 1) 37 | if !empty(map_dict) 38 | if exists('g:longlines_keep_maps') && g:longlines_keep_maps != 0 || 39 | \ exists('b:longlines_keep_maps') && b:longlines_keep_maps != 0 40 | return 41 | else 42 | " b:map_restore is a list of expressions that when executed 43 | " restores all previous mappings. We should add the current 44 | " mapping to the restore list. 45 | " https://vi.stackexchange.com/a/7735 46 | let b:map_restore += [ 47 | \ ('nvoicsxlt' =~ map_dict.mode ? map_dict.mode : '') 48 | \ . (map_dict.noremap ? 'noremap ' : 'map ') 49 | \ . (map_dict.buffer ? ' ' : '') 50 | \ . (map_dict.expr ? ' ' : '') 51 | \ . (map_dict.nowait ? ' ' : '') 52 | \ . (map_dict.silent ? ' ' : '') 53 | \ . map_dict.lhs 54 | \ . ' ' 55 | \ . substitute(map_dict.rhs, '', ''.map_dict.sid.'_', 'g') 56 | \ ] 57 | endif 58 | endif 59 | 60 | " Since mappings have precedence over other 61 | " mappings, we have to always unmap the mappings we make. 62 | let b:map_restore += [a:mode.'unmap '.a:lhs] 63 | execute a:mode.'noremap ' (a:expr?'':'') a:lhs a:rhs 64 | endfunction 65 | 66 | " Synopsis: s:longlines_on() 67 | function! s:longlines_on() abort 68 | if exists('b:longlines') && b:longlines == 1 69 | return 70 | else 71 | let b:longlines = 1 72 | endif 73 | 74 | let b:map_restore = [] 75 | 76 | " Change formatting options only if required. 77 | if (!exists('g:longlines_keep_opts') || g:longlines_keep_opts == 0) && 78 | \ (!exists('b:longlines_keep_opts') || b:longlines_keep_opts == 0) 79 | " Save the options we're about to change. 80 | let b:options = {} 81 | for key in s:optkeys 82 | execute 'let b:options[key] = &'.key 83 | endfor 84 | 85 | " Remove all formatoptions that lead to automatic hardwrapping of 86 | " input text. 87 | for letter in split('1,2,a,b,c,m,t,v', ',') 88 | execute 'setlocal formatoptions-='.letter 89 | endfor 90 | setlocal formatoptions+=l 91 | 92 | " These options aren't useful when the longline mode is on. 93 | setlocal colorcolumn= 94 | setlocal linebreak 95 | setlocal textwidth=0 96 | setlocal wrap 97 | setlocal wrapmargin=0 98 | 99 | " Highlight only the screenline and line number. 100 | setlocal cursorlineopt=number,screenline 101 | endif 102 | 103 | " -- General (nvo) mappings -- " 104 | 105 | call s:longlines_map('k', 'gk', '', 0) 106 | call s:longlines_map('', 'gk', '', 0) 107 | call s:longlines_map('-', 'gkg^', '', 0) 108 | 109 | call s:longlines_map('j', 'gj', '', 0) 110 | call s:longlines_map('', 'gj', '', 0) 111 | call s:longlines_map('+', 'gj', '', 0) 112 | 113 | call s:longlines_map('0', 'g0', '', 0) 114 | call s:longlines_map('^', 'g^', '', 0) 115 | call s:longlines_map('', 'g', '', 0) 116 | 117 | " g_ doesn't make much sense with soft-wrapped lines. 118 | call s:longlines_map('$', 'g$', '', 0) 119 | call s:longlines_map('g_', 'g$', '', 0) 120 | call s:longlines_map('', 'g', '', 0) 121 | 122 | " gg and G work as if startofline is set. 123 | call s:longlines_map('gg', 'gg^', '', 0) 124 | call s:longlines_map('', 'gg^', '', 0) 125 | call s:longlines_map('G', 'Gg_g^', '', 0) 126 | call s:longlines_map('', 'Gg_', '', 0) 127 | 128 | " -- Normal mode -- " 129 | 130 | call s:longlines_map('A', 'g$a', 'n', 0) 131 | call s:longlines_map('I', 'g0i', 'n', 0) 132 | 133 | call s:longlines_map('C', 'cg$', 'n', 0) 134 | call s:longlines_map('D', 'dg$', 'n', 0) 135 | call s:longlines_map('Y', 'yg$', 'n', 0) 136 | 137 | " None of the following mappings work properly. (I'd be glad to know of ways 138 | " to map them properly.) They don't work with counts, registers, and in 139 | " general behave differently compared to their usual selves. 140 | call s:longlines_map('cc', 'w:longlines_columns', 'pumvisible()?"":"gk"', 'i', 1) 153 | call s:longlines_map('', 'pumvisible()?"":"gj"', 'i', 1) 154 | call s:longlines_map('', 'g', 'i', 0) 155 | call s:longlines_map('', 'g', 'i', 0) 156 | 157 | " -- Scrolling -- " 158 | 159 | " Approximate mouse scrolling in normal/insert mode. 160 | " By default, Vim scrolls up/down by 3 lines. 161 | call s:longlines_map('', '3gk', '', 0) 162 | call s:longlines_map('', '3gk', 'i', 0) 163 | call s:longlines_map('', '3gj', '', 0) 164 | call s:longlines_map('', '3gj', 'i', 0) 165 | 166 | " Pagewise mouse scrolling. 167 | call s:longlines_map('', 'w:longlines_lines."gk"', '', 1) 168 | call s:longlines_map('', 'w:longlines_lines."gk"', '', 1) 169 | call s:longlines_map('', 'w:longlines_lines."gj"', '', 1) 170 | call s:longlines_map('', 'w:longlines_lines."gj"', '', 1) 171 | 172 | call s:longlines_map('', 'gj', '', 0) 173 | call s:longlines_map('', 'v:count?"gj":w:longlines_half_lines."gj"', '', 1) 174 | 175 | call s:longlines_map('', 'gk', '', 0) 176 | call s:longlines_map('', 'v:count?"gk":w:longlines_half_lines."gk"', '', 1) 177 | 178 | call s:longlines_map('', 'w:longlines_lines."gj"', '', 1) 179 | call s:longlines_map('', 'w:longlines_lines."gj"', '', 1) 180 | call s:longlines_map('', 'pumvisible()?"":"".w:longlines_lines."gji"', 'i', 1) 181 | call s:longlines_map('', 'w:longlines_lines."gj"', '', 1) 182 | call s:longlines_map('', 'pumvisible()?"":"".w:longlines_lines."gji"', 'i', 1) 183 | 184 | call s:longlines_map('', 'w:longlines_lines."gk"', '', 1) 185 | call s:longlines_map('', 'w:longlines_lines."gk"', '', 1) 186 | call s:longlines_map('', 'pumvisible()?"":"".w:longlines_lines."gki"', 'i', 1) 187 | call s:longlines_map('', 'w:longlines_lines."gk"', '', 1) 188 | call s:longlines_map('', 'pumvisible()?"":"".w:longlines_lines."gki"', 'i', 1) 189 | endfunction 190 | 191 | " Synopsis: s:longlines_off() 192 | function! s:longlines_off() abort 193 | if exists('b:longlines') && b:longlines == 1 194 | unlet b:longlines 195 | else 196 | return 197 | endif 198 | 199 | " Restore options. 200 | if exists('b:options') 201 | for key in s:optkeys 202 | execute 'let &'.key.' = b:options[key]' 203 | endfor 204 | unlet b:options 205 | endif 206 | 207 | " Restore mappings. 208 | for expr in b:map_restore 209 | execute expr 210 | endfor 211 | unlet b:map_restore 212 | endfunction 213 | 214 | " Synopsis: s:longlines({bang}) 215 | function! s:longlines(bang) abort 216 | if a:bang 217 | call s:longlines_off() 218 | else 219 | if exists('b:longlines') && b:longlines == 1 220 | call s:longlines_off() 221 | else 222 | call s:longlines_on() 223 | endif 224 | endif 225 | endfunction 226 | 227 | command! -bang -bar -nargs=0 LongLines silent call s:longlines(0) 228 | 229 | if exists('g:longlines_always_enable') && g:longlines_always_enable == 1 230 | autocmd BufEnter * call s:longlines_on() 231 | endif 232 | --------------------------------------------------------------------------------