├── 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 |
--------------------------------------------------------------------------------