Skip to content

Commit a1b60ed

Browse files
committed
Initial commit
0 parents  commit a1b60ed

File tree

3 files changed

+304
-0
lines changed

3 files changed

+304
-0
lines changed

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Fancy edit
2+
3+
Edit fenced code blocks as it should be.
4+
5+
6+
## Installation
7+
8+
Use your favorite installation method:
9+
10+
- via Tim Pope's [pathogen](https://github.com/tpope/vim-pathogen):
11+
12+
```sh
13+
cd .vim/bundle
14+
git clone https://github.com/vitalk/vim-fancy-edit
15+
```
16+
17+
- via Junegunn Choi [Plug](https://github.com/junegunn/vim-plug):
18+
19+
```viml
20+
Plug 'vitalk/vim-fancy-edit'
21+
```
22+
23+
```sh
24+
vim +PlugInstall +qall
25+
```
26+
27+
28+
## License
29+
30+
Copyright © by Vital Kudzelka.
31+
32+
Distributed under the [MIT license](http://mit-license.org/vitalk).

autoload/fancy.vim

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
" Internal variables and functions {{{
2+
3+
let s:id = 0
4+
let s:fancy_objects = []
5+
6+
7+
fun! s:error(message)
8+
echohl ErrorMsg | echomsg a:message | echohl NONE
9+
let v:errmsg = a:message
10+
endf
11+
12+
fun! s:function(name) abort
13+
return function(a:name)
14+
endf
15+
16+
fun! s:add_methods(namespace, method_names) abort
17+
for name in a:method_names
18+
let s:{a:namespace}_prototype[name] = s:function('s:'.a:namespace.'_'.name)
19+
endfor
20+
endf
21+
22+
fun! s:get_id()
23+
let s:id += 1
24+
return s:id
25+
endf
26+
27+
" }}}
28+
" Buffer prototype {{{
29+
30+
let s:buffer_prototype = {}
31+
32+
fun! s:buffer(...) abort
33+
let buffer = {
34+
\ '#': bufnr(a:0 ? a:1 : '%'),
35+
\ 'id': (a:0 > 1 && a:2) ? a:2 : 0,
36+
\ 'pos': getpos('.')
37+
\ }
38+
call extend(buffer, s:buffer_prototype, 'keep')
39+
return buffer
40+
endf
41+
42+
fun! s:buffer_getvar(var) dict abort
43+
return getbufvar(self['#'], a:var)
44+
endf
45+
46+
fun! s:buffer_setvar(var, value) dict abort
47+
return setbufvar(self['#'], a:var, a:value)
48+
endf
49+
50+
fun! s:buffer_spec() dict abort
51+
let full = bufname(self['#'])
52+
if full =~# '^fancy://.*//\d\+'
53+
let path = substitute(full, '^fancy://\(.*\)//\d\+', '\1', '')
54+
elseif full != ''
55+
let path = fnamemodify(full, ':p')
56+
else
57+
let path = ''
58+
endif
59+
60+
let id = (full =~# '^fancy://.*//\d\+')
61+
\ ? substitute(full, '^fancy://.*//\(\d\+\)', '\1', '')
62+
\ : self.id
63+
return 'fancy://'.path.'//'.id
64+
endf
65+
66+
fun! s:buffer_name() dict abort
67+
return self.path()
68+
endf
69+
70+
fun! s:buffer_path() dict abort
71+
return substitute(self.spec(), '^fancy://\(.*\)//\d\+', '\1', '')
72+
endf
73+
74+
fun! s:buffer_fancy_id() dict abort
75+
return substitute(self.spec(), '^fancy://.*//\(\d\+\)', '\1', '')
76+
endf
77+
78+
fun! s:buffer_exists() dict abort
79+
return bufexists(self.spec()) && (bufwinnr(self.spec()) != -1)
80+
endf
81+
82+
fun! s:buffer_delete() dict abort
83+
call delete(self.name())
84+
85+
if fnamemodify(bufname('$'), ':p') ==# self.name()
86+
sil exe 'bwipeout '.bufnr('$')
87+
endif
88+
endf
89+
90+
fun! s:buffer_read(...) dict abort
91+
return getbufline(self.name(),
92+
\ a:0 ? a:1 : 1,
93+
\ (a:0 == 2) ? a:2 : '$')
94+
endf
95+
96+
fun! s:buffer_write(...) dict abort
97+
if empty(a:0)
98+
return
99+
elseif a:0 == 1
100+
let [lnum, text] = [1, a:1]
101+
else
102+
let [lnum, text] = [a:1, a:2]
103+
endif
104+
return setline(lnum, text)
105+
endf
106+
107+
call s:add_methods('buffer', [
108+
\ 'getvar', 'setvar', 'name', 'delete', 'read', 'write',
109+
\ 'exists', 'spec', 'path', 'fancy_id'
110+
\ ])
111+
112+
" }}}
113+
" Fancy prototype {{{
114+
115+
let s:fancy_prototype = {}
116+
117+
fun! s:fancy() abort
118+
let [start, end] = s:get_start_end_position()
119+
if start == 0 && end == 0
120+
call s:error('No fanced block found! Aborting!')
121+
return
122+
endif
123+
124+
let fancy = {
125+
\ 'id': s:get_id(),
126+
\ 'start': start,
127+
\ 'end': end,
128+
\ 'buffer': s:buffer()
129+
\ }
130+
call extend(fancy, s:fancy_prototype, 'keep')
131+
132+
call add(s:fancy_objects, fancy)
133+
return fancy
134+
endf
135+
136+
fun! s:fancy_sync() dict abort
137+
return s:sync()
138+
endf
139+
140+
fun! s:fancy_filetype() dict abort
141+
let pos = getpos('.')
142+
let pos[2] = 0
143+
call setpos('.', pos)
144+
145+
let text = join(self.buffer.read(self.start, self.start), '\n')
146+
return substitute(text, '```', '', '')
147+
endf
148+
149+
fun! s:fancy_text() dict abort
150+
return self.buffer.read(self.start + 1, self.end - 1)
151+
endf
152+
153+
fun! s:fancy_destroy() dict abort
154+
call remove(s:fancy_objects, index(s:fancy_objects, self))
155+
endf
156+
157+
call s:add_methods('fancy', ['sync', 'filetype', 'text', 'destroy'])
158+
159+
160+
fun! s:lookup_fancy(id)
161+
let found = filter(copy(s:fancy_objects), 'v:val["id"] == a:id')
162+
if empty(found)
163+
call s:error('Original buffer does no longer exist! Aborting!')
164+
return
165+
endif
166+
return found[0]
167+
endf
168+
169+
fun! s:get_start_end_position()
170+
let start = search('^```\w\+$', 'bcnW')
171+
let end = search('^```$', 'cnW')
172+
return [start, end]
173+
endf
174+
175+
fun! s:edit()
176+
let fancy = fancy#fancy()
177+
if (type(fancy) != type({}))
178+
return
179+
endif
180+
181+
let name = tempname()
182+
exe 'split '.name
183+
let buffer = s:buffer(name, fancy.id)
184+
185+
call buffer.setvar('&ft', fancy.filetype())
186+
call buffer.setvar('&bufhidden', 'wipe')
187+
call buffer.write(fancy.text())
188+
189+
sil exe 'file '.buffer.spec()
190+
setl nomodified
191+
endf
192+
193+
fun! s:destroy(...)
194+
let bufnr = a:0 ? str2nr(a:1[0]) : '%'
195+
let buffer = s:buffer(bufnr)
196+
let fancy = s:lookup_fancy(buffer.fancy_id())
197+
call fancy.destroy()
198+
endf
199+
200+
fun! s:sync()
201+
let buffer = s:buffer()
202+
let fancy = s:lookup_fancy(buffer.fancy_id())
203+
204+
" Go to original buffer.
205+
let winnr = bufwinnr(fancy.buffer.name())
206+
if (winnr != winnr())
207+
exe 'noa' winnr 'wincmd w'
208+
endif
209+
210+
" Sync any changes.
211+
if (fancy.end - fancy.start > 1)
212+
exe printf('%s,%s delete _', fancy.start + 1, fancy.end - 1)
213+
endif
214+
call append(fancy.start, buffer.read())
215+
216+
" Restore the original cursor position.
217+
call setpos('.', fancy.buffer.pos)
218+
219+
" Update start/end block position.
220+
let [fancy.start, fancy.end] = s:get_start_end_position()
221+
endf
222+
223+
fun! s:write()
224+
sil exe 'write! '.s:buffer().path()
225+
setl nomodified
226+
endf
227+
228+
" }}}
229+
" Funcy public interface {{{
230+
231+
fun! fancy#fancy() abort
232+
return s:fancy()
233+
endf
234+
235+
fun! fancy#edit() abort
236+
return s:edit()
237+
endf
238+
239+
fun! fancy#sync(...) abort
240+
return s:sync()
241+
endf
242+
243+
fun! fancy#write(...) abort
244+
return s:write()
245+
endf
246+
247+
fun! fancy#destroy(...) abort
248+
return s:destroy(a:000)
249+
endf
250+
251+
" }}}

plugin/fancy.vim

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
if exists('g:loaded_fancy_edit') || &cp || version < 700
2+
finish
3+
endif
4+
let g:loaded_fancy_edit = 1
5+
6+
7+
augroup fancy_edit
8+
au!
9+
au BufWriteCmd fancy://** call fancy#write(expand('<amatch>'))
10+
au BufLeave fancy://** call fancy#sync()
11+
au BufWipeout fancy://** call fancy#destroy(expand('<abuf>'))
12+
au BufEnter fancy://**
13+
\ setl bufhidden=wipe bl noswapfile |
14+
\ nnore <buffer> q :write<bar>close<cr>
15+
augroup END
16+
17+
18+
augroup fancy_edit_markdown
19+
au!
20+
au FileType markdown nnore <s-e> :call fancy#edit()<cr>
21+
augroup END

0 commit comments

Comments
 (0)