]>
Commit | Line | Data |
---|---|---|
1 | "============================================================================= | |
2 | " File: gist.vim | |
3 | " Author: Yasuhiro Matsumoto <mattn.jp@gmail.com> | |
4 | " Last Change: 20-Aug-2011. | |
5 | " Version: 5.0 | |
6 | " WebPage: http://github.com/mattn/gist-vim | |
7 | " License: BSD | |
8 | " Usage: | |
9 | " | |
10 | " :Gist | |
11 | " post current buffer to gist, using default privicy option | |
12 | " (see g:gist_private) | |
13 | " | |
14 | " :'<,'>Gist | |
15 | " post selected text to gist., using default privicy option | |
16 | " This applies to all permutations listed below (except multi) | |
17 | " (see g:gist_private) | |
18 | " | |
19 | " :Gist -p | |
20 | " create a private gist | |
21 | " | |
22 | " :Gist -P | |
23 | " create a public gist | |
24 | " (only relevant if you've set gists to be private by default) | |
25 | " | |
26 | " :Gist -P | |
27 | " post whole text to gist as public | |
28 | " This is only relevant if you've set gists to be private by default | |
29 | " :Gist -a | |
30 | " create a gist anonymously | |
31 | " | |
32 | " :Gist -m | |
33 | " create a gist with all open buffers | |
34 | " | |
35 | " :Gist -e | |
36 | " edit the gist. (you need to have opend the gist buffer first) | |
37 | " you can update the gist with :w command on gist buffer | |
38 | " | |
39 | " :Gist -d | |
40 | " delete the gist. (you need to have opend the gist buffer first) | |
41 | " password authentication is needed | |
42 | " | |
43 | " :Gist -f | |
44 | " fork the gist. (you need to have opend the gist buffer first) | |
45 | " password authentication is needed | |
46 | " | |
47 | " :Gist -e foo.js | |
48 | " edit the gist with name 'foo.js'. (you need to have opend the gist buffer first) | |
49 | " | |
50 | " :Gist XXXXX | |
51 | " get gist XXXXX | |
52 | " | |
53 | " :Gist -c XXXXX | |
54 | " get gist XXXXX and add to clipboard | |
55 | " | |
56 | " :Gist -l | |
57 | " list your public gists | |
58 | " | |
59 | " :Gist -l mattn | |
60 | " list gists from mattn | |
61 | " | |
62 | " :Gist -la | |
63 | " list all your (public and private) gists | |
64 | " | |
65 | " Tips: | |
66 | " * if set g:gist_clip_command, gist.vim will copy the gist code | |
67 | " with option '-c'. | |
68 | " | |
69 | " # mac | |
70 | " let g:gist_clip_command = 'pbcopy' | |
71 | " | |
72 | " # linux | |
73 | " let g:gist_clip_command = 'xclip -selection clipboard' | |
74 | " | |
75 | " # others(cygwin?) | |
76 | " let g:gist_clip_command = 'putclip' | |
77 | " | |
78 | " * if you want to detect filetype from gist's filename... | |
79 | " | |
80 | " # detect filetype if vim failed auto-detection. | |
81 | " let g:gist_detect_filetype = 1 | |
82 | " | |
83 | " # detect filetype always. | |
84 | " let g:gist_detect_filetype = 2 | |
85 | " | |
86 | " * if you want to open browser after the post... | |
87 | " | |
88 | " let g:gist_open_browser_after_post = 1 | |
89 | " | |
90 | " * if you want to change the browser... | |
91 | " | |
92 | " let g:gist_browser_command = 'w3m %URL%' | |
93 | " | |
94 | " or | |
95 | " | |
96 | " let g:gist_browser_command = 'opera %URL% &' | |
97 | " | |
98 | " on windows, should work with original setting. | |
99 | " | |
100 | " * if you want to show your private gists with ':Gist -l' | |
101 | " | |
102 | " let g:gist_show_privates = 1 | |
103 | " | |
104 | " * if don't you want to copy URL of the post... | |
105 | " | |
106 | " let g:gist_put_url_to_clipboard_after_post = 0 | |
107 | " | |
108 | " or if you want to copy URL and add linefeed at the last of URL, | |
109 | " | |
110 | " let g:gist_put_url_to_clipboard_after_post = 2 | |
111 | " | |
112 | " default value is 1. | |
113 | " | |
114 | " Thanks: | |
115 | " MATSUU Takuto: | |
116 | " removed carriage return | |
117 | " gist_browser_command enhancement | |
118 | " edit support | |
119 | " | |
120 | " GetLatestVimScripts: 2423 1 :AutoInstall: gist.vim | |
121 | " script type: plugin | |
122 | ||
123 | if &cp || (exists('g:loaded_gist_vim') && g:loaded_gist_vim) | |
124 | finish | |
125 | endif | |
126 | let g:loaded_gist_vim = 1 | |
127 | ||
128 | if (!exists('g:github_user') || !exists('g:github_token')) && !executable('git') | |
129 | echohl ErrorMsg | echomsg "Gist: require 'git' command" | echohl None | |
130 | finish | |
131 | endif | |
132 | ||
133 | if !executable('curl') | |
134 | echohl ErrorMsg | echomsg "Gist: require 'curl' command" | echohl None | |
135 | finish | |
136 | endif | |
137 | ||
138 | if !exists('g:gist_open_browser_after_post') | |
139 | let g:gist_open_browser_after_post = 0 | |
140 | endif | |
141 | ||
142 | if !exists('g:gist_put_url_to_clipboard_after_post') | |
143 | let g:gist_put_url_to_clipboard_after_post = 1 | |
144 | endif | |
145 | ||
146 | if !exists('g:gist_curl_options') | |
147 | let g:gist_curl_options = "" | |
148 | endif | |
149 | ||
150 | if !exists('g:gist_browser_command') | |
151 | if has('win32') || has('win64') | |
152 | let g:gist_browser_command = "!start rundll32 url.dll,FileProtocolHandler %URL%" | |
153 | elseif has('mac') | |
154 | let g:gist_browser_command = "open %URL%" | |
155 | elseif executable('xdg-open') | |
156 | let g:gist_browser_command = "xdg-open %URL%" | |
157 | else | |
158 | let g:gist_browser_command = "firefox %URL% &" | |
159 | endif | |
160 | endif | |
161 | ||
162 | if !exists('g:gist_detect_filetype') | |
163 | let g:gist_detect_filetype = 0 | |
164 | endif | |
165 | ||
166 | if !exists('g:gist_private') | |
167 | let g:gist_private = 0 | |
168 | endif | |
169 | ||
170 | if !exists('g:gist_show_privates') | |
171 | let g:gist_show_privates = 0 | |
172 | endif | |
173 | ||
174 | if !exists('g:gist_cookie_dir') | |
175 | let g:gist_cookie_dir = substitute(expand('<sfile>:p:h'), '[/\\]plugin$', '', '').'/cookies' | |
176 | endif | |
177 | ||
178 | function! s:nr2hex(nr) | |
179 | let n = a:nr | |
180 | let r = "" | |
181 | while n | |
182 | let r = '0123456789ABCDEF'[n % 16] . r | |
183 | let n = n / 16 | |
184 | endwhile | |
185 | return r | |
186 | endfunction | |
187 | ||
188 | function! s:encodeURIComponent(instr) | |
189 | let instr = iconv(a:instr, &enc, "utf-8") | |
190 | let len = strlen(instr) | |
191 | let i = 0 | |
192 | let outstr = '' | |
193 | while i < len | |
194 | let ch = instr[i] | |
195 | if ch =~# '[0-9A-Za-z-._~!''()*]' | |
196 | let outstr = outstr . ch | |
197 | elseif ch == ' ' | |
198 | let outstr = outstr . '+' | |
199 | else | |
200 | let outstr = outstr . '%' . substitute('0' . s:nr2hex(char2nr(ch)), '^.*\(..\)$', '\1', '') | |
201 | endif | |
202 | let i = i + 1 | |
203 | endwhile | |
204 | return outstr | |
205 | endfunction | |
206 | ||
207 | " Note: A colon in the file name has side effects on Windows due to NTFS Alternate Data Streams; avoid it. | |
208 | let s:bufprefix = 'gist' . (has('unix') ? ':' : '_') | |
209 | function! s:GistList(user, token, gistls, page) | |
210 | if a:gistls == '-all' | |
211 | let url = 'https://gist.github.com/gists' | |
212 | elseif g:gist_show_privates && a:gistls == a:user | |
213 | let url = 'https://gist.github.com/mine' | |
214 | else | |
215 | let url = 'https://gist.github.com/'.a:gistls | |
216 | endif | |
217 | let winnum = bufwinnr(bufnr(s:bufprefix.a:gistls)) | |
218 | if winnum != -1 | |
219 | if winnum != bufwinnr('%') | |
220 | exe winnum 'wincmd w' | |
221 | endif | |
222 | setlocal modifiable | |
223 | else | |
224 | exec 'silent split' s:bufprefix.a:gistls | |
225 | endif | |
226 | if a:page > 1 | |
227 | let oldlines = getline(0, line('$')) | |
228 | let url = url . '?page=' . a:page | |
229 | endif | |
230 | ||
231 | setlocal foldmethod=manual | |
232 | let oldlines = [] | |
233 | if g:gist_show_privates | |
234 | echon 'Login to gist... ' | |
235 | silent %d _ | |
236 | let res = s:GistGetPage(url, a:user, '', '-L') | |
237 | silent put =res.content | |
238 | else | |
239 | silent %d _ | |
240 | exec 'silent r! curl -s' g:gist_curl_options url | |
241 | endif | |
242 | ||
243 | 1delete _ | |
244 | silent! %s/>/>\r/g | |
245 | silent! %s/</\r</g | |
246 | silent! %g/<pre/,/<\/pre/join! | |
247 | silent! %g/<span class="date"/,/<\/span/join | |
248 | silent! %g/^<span class="date"/s/> */>/g | |
249 | silent! %v/^\(gist:\|<pre>\|<span class="date">\)/d _ | |
250 | silent! %s/<div[^>]*>/\r /g | |
251 | silent! %s/<\/pre>/\r/g | |
252 | silent! %g/^gist:/,/<span class="date"/join | |
253 | silent! %s/<[^>]\+>//g | |
254 | silent! %s/\r//g | |
255 | silent! %s/ / /g | |
256 | silent! %s/"/"/g | |
257 | silent! %s/&/\&/g | |
258 | silent! %s/>/>/g | |
259 | silent! %s/</</g | |
260 | silent! %s/&#\(\d\d\);/\=nr2char(submatch(1))/g | |
261 | silent! %g/^gist: /s/ //g | |
262 | ||
263 | call append(0, oldlines) | |
264 | $put='more...' | |
265 | ||
266 | let b:user = a:user | |
267 | let b:token = a:token | |
268 | let b:gistls = a:gistls | |
269 | let b:page = a:page | |
270 | setlocal buftype=nofile bufhidden=hide noswapfile | |
271 | setlocal nomodified | |
272 | syntax match SpecialKey /^gist:/he=e-1 | |
273 | nnoremap <silent> <buffer> <cr> :call <SID>GistListAction()<cr> | |
274 | ||
275 | cal cursor(1+len(oldlines),1) | |
276 | setlocal foldmethod=expr | |
277 | setlocal foldexpr=getline(v:lnum)=~'^\\(gist:\\\|more\\)'?'>1':'=' | |
278 | setlocal foldtext=getline(v:foldstart) | |
279 | endfunction | |
280 | ||
281 | function! s:GistGetFileName(gistid) | |
282 | let url = 'https://gist.github.com/'.a:gistid | |
283 | let res = system('curl -s '.g:gist_curl_options.' '.url) | |
284 | let res = substitute(res, '^.*<a href="/raw/[^"]\+/\([^"]\+\)".*$', '\1', '') | |
285 | if res =~ '/' | |
286 | return '' | |
287 | else | |
288 | return res | |
289 | endif | |
290 | endfunction | |
291 | ||
292 | function! s:GistDetectFiletype(gistid) | |
293 | let url = 'https://gist.github.com/'.a:gistid | |
294 | let mx = '^.*<div class=".\{-}type-\([^"]\+\)">.*$' | |
295 | let res = system('curl -s '.g:gist_curl_options.' '.url) | |
296 | let res = substitute(matchstr(res, mx), mx, '\1', '') | |
297 | let res = substitute(res, '.*\(\.[^\.]\+\)$', '\1', '') | |
298 | let res = substitute(res, '-', '', 'g') | |
299 | " TODO: more filetype detection that is specified in html. | |
300 | if res == 'bat' | let res = 'dosbatch' | endif | |
301 | if res == 'as' | let res = 'actionscript' | endif | |
302 | if res == 'bash' | let res = 'sh' | endif | |
303 | if res == 'cl' | let res = 'lisp' | endif | |
304 | if res == 'rb' | let res = 'ruby' | endif | |
305 | if res == 'viml' | let res = 'vim' | endif | |
306 | if res == 'plain' || res == 'text' | let res = '' | endif | |
307 | ||
308 | if res =~ '^\.' | |
309 | silent! exec "doau BufRead *".res | |
310 | else | |
311 | silent! exec "setlocal ft=".tolower(res) | |
312 | endif | |
313 | endfunction | |
314 | ||
315 | function! s:GistWrite(fname) | |
316 | if substitute(a:fname, '\\', '/', 'g') == expand("%:p:gs@\\@/@") | |
317 | Gist -e | |
318 | else | |
319 | exe "w".(v:cmdbang ? "!" : "") fnameescape(v:cmdarg) fnameescape(a:fname) | |
320 | silent! exe "file" fnameescape(a:fname) | |
321 | silent! au! BufWriteCmd <buffer> | |
322 | endif | |
323 | endfunction | |
324 | ||
325 | function! s:GistGet(user, token, gistid, clipboard) | |
326 | let url = 'https://raw.github.com/gist/'.a:gistid | |
327 | let winnum = bufwinnr(bufnr(s:bufprefix.a:gistid)) | |
328 | if winnum != -1 | |
329 | if winnum != bufwinnr('%') | |
330 | exe winnum 'wincmd w' | |
331 | endif | |
332 | setlocal modifiable | |
333 | else | |
334 | exec 'silent split' s:bufprefix.a:gistid | |
335 | endif | |
336 | filetype detect | |
337 | silent %d _ | |
338 | exec 'silent 0r! curl -s' g:gist_curl_options url | |
339 | $delete _ | |
340 | setlocal buftype=acwrite bufhidden=delete noswapfile | |
341 | setlocal nomodified | |
342 | doau StdinReadPost <buffer> | |
343 | if (&ft == '' && g:gist_detect_filetype == 1) || g:gist_detect_filetype == 2 | |
344 | call s:GistDetectFiletype(a:gistid) | |
345 | endif | |
346 | if a:clipboard | |
347 | if exists('g:gist_clip_command') | |
348 | exec 'silent w !'.g:gist_clip_command | |
349 | else | |
350 | %yank + | |
351 | endif | |
352 | endif | |
353 | 1 | |
354 | au! BufWriteCmd <buffer> call s:GistWrite(expand("<amatch>")) | |
355 | endfunction | |
356 | ||
357 | function! s:GistListAction() | |
358 | let line = getline('.') | |
359 | let mx = '^gist:\(\w\+\).*' | |
360 | if line =~# mx | |
361 | let gistid = substitute(line, mx, '\1', '') | |
362 | call s:GistGet(g:github_user, g:github_token, gistid, 0) | |
363 | return | |
364 | endif | |
365 | if line =~# '^more\.\.\.$' | |
366 | delete | |
367 | call s:GistList(b:user, b:token, b:gistls, b:page+1) | |
368 | return | |
369 | endif | |
370 | endfunction | |
371 | ||
372 | function! s:GistUpdate(user, token, content, gistid, gistnm) | |
373 | if len(a:gistnm) == 0 | |
374 | let name = s:GistGetFileName(a:gistid) | |
375 | else | |
376 | let name = a:gistnm | |
377 | endif | |
378 | let namemx = '^[^.]\+\(.\+\)$' | |
379 | let ext = '' | |
380 | if name =~ namemx | |
381 | let ext = substitute(name, namemx, '\1', '') | |
382 | endif | |
383 | let query = [ | |
384 | \ '_method=put', | |
385 | \ 'file_ext[gistfile1%s]=%s', | |
386 | \ 'file_name[gistfile1%s]=%s', | |
387 | \ 'file_contents[gistfile1%s]=%s', | |
388 | \ 'login=%s', | |
389 | \ 'token=%s', | |
390 | \ ] | |
391 | let squery = printf(join(query, '&'), | |
392 | \ s:encodeURIComponent(ext), s:encodeURIComponent(ext), | |
393 | \ s:encodeURIComponent(ext), s:encodeURIComponent(name), | |
394 | \ s:encodeURIComponent(ext), s:encodeURIComponent(a:content), | |
395 | \ s:encodeURIComponent(a:user), | |
396 | \ s:encodeURIComponent(a:token)) | |
397 | unlet query | |
398 | ||
399 | let file = tempname() | |
400 | call writefile([squery], file) | |
401 | echon 'Updating it to gist... ' | |
402 | let quote = &shellxquote == '"' ? "'" : '"' | |
403 | let url = 'https://gist.github.com/gists/'.a:gistid | |
404 | let res = system('curl -i '.g:gist_curl_options.' -d @'.quote.file.quote.' '.url) | |
405 | call delete(file) | |
406 | let headers = split(res, '\(\r\?\n\|\r\n\?\)') | |
407 | let location = matchstr(headers, '^Location: ') | |
408 | let location = substitute(location, '^[^:]\+: ', '', '') | |
409 | if len(location) > 0 && location =~ '^\(http\|https\):\/\/gist\.github\.com\/' | |
410 | setlocal nomodified | |
411 | redraw | |
412 | echo 'Done: '.location | |
413 | else | |
414 | let message = matchstr(headers, '^Status: ') | |
415 | let message = substitute(message, '^[^:]\+: [0-9]\+ ', '', '') | |
416 | echohl ErrorMsg | echomsg 'Edit failed: '.message | echohl None | |
417 | endif | |
418 | return location | |
419 | endfunction | |
420 | ||
421 | function! s:GistGetPage(url, user, param, opt) | |
422 | if !isdirectory(g:gist_cookie_dir) | |
423 | call mkdir(g:gist_cookie_dir, 'p') | |
424 | endif | |
425 | let cookie_file = g:gist_cookie_dir.'/github' | |
426 | ||
427 | if len(a:url) == 0 | |
428 | call delete(cookie_file) | |
429 | return | |
430 | endif | |
431 | ||
432 | let quote = &shellxquote == '"' ? "'" : '"' | |
433 | if !filereadable(cookie_file) | |
434 | let password = inputsecret('Password:') | |
435 | if len(password) == 0 | |
436 | echo 'Canceled' | |
437 | return | |
438 | endif | |
439 | let url = 'https://gist.github.com/login?return_to=gist' | |
440 | let res = system('curl -L -s '.g:gist_curl_options.' -c '.quote.cookie_file.quote.' '.quote.url.quote) | |
441 | let token = substitute(res, '^.* name="authenticity_token" type="hidden" value="\([^"]\+\)".*$', '\1', '') | |
442 | ||
443 | let query = [ | |
444 | \ 'authenticity_token=%s', | |
445 | \ 'login=%s', | |
446 | \ 'password=%s', | |
447 | \ 'return_to=gist', | |
448 | \ 'commit=Log+in', | |
449 | \ ] | |
450 | let squery = printf(join(query, '&'), | |
451 | \ s:encodeURIComponent(token), | |
452 | \ s:encodeURIComponent(a:user), | |
453 | \ s:encodeURIComponent(password)) | |
454 | unlet query | |
455 | ||
456 | let file = tempname() | |
457 | let command = 'curl -s '.g:gist_curl_options.' -i' | |
458 | let command .= ' -b '.quote.cookie_file.quote | |
459 | let command .= ' -c '.quote.cookie_file.quote | |
460 | let command .= ' '.quote.'https://gist.github.com/session'.quote | |
461 | let command .= ' -d @' . quote.file.quote | |
462 | call writefile([squery], file) | |
463 | let res = system(command) | |
464 | call delete(file) | |
465 | let res = matchstr(split(res, '\(\r\?\n\|\r\n\?\)'), '^Location: ') | |
466 | let res = substitute(res, '^[^:]\+: ', '', '') | |
467 | if len(res) == 0 | |
468 | call delete(cookie_file) | |
469 | return '' | |
470 | endif | |
471 | endif | |
472 | let command = 'curl -s '.g:gist_curl_options.' -i '.a:opt | |
473 | if len(a:param) | |
474 | let command .= ' -d '.quote.a:param.quote | |
475 | endif | |
476 | let command .= ' -b '.quote.cookie_file.quote | |
477 | let command .= ' '.quote.a:url.quote | |
478 | let res = iconv(system(command), "utf-8", &encoding) | |
479 | let pos = stridx(res, "\r\n\r\n") | |
480 | if pos != -1 | |
481 | let content = res[pos+4:] | |
482 | else | |
483 | let pos = stridx(res, "\n\n") | |
484 | let content = res[pos+2:] | |
485 | endif | |
486 | return { | |
487 | \ "header" : split(res[0:pos], '\r\?\n'), | |
488 | \ "content" : content | |
489 | \} | |
490 | endfunction | |
491 | ||
492 | function! s:GistDelete(user, token, gistid) | |
493 | echon 'Deleting gist... ' | |
494 | let res = s:GistGetPage('https://gist.github.com/'.a:gistid, a:user, '', '') | |
495 | if (!len(res)) | |
496 | echohl ErrorMsg | echomsg 'Wrong password? no response received from github trying to delete ' . a:gistid | echohl None | |
497 | return | |
498 | endif | |
499 | let mx = '^.* name="authenticity_token" type="hidden" value="\([^"]\+\)".*$' | |
500 | let token = substitute(matchstr(res.content, mx), mx, '\1', '') | |
501 | if len(token) > 0 | |
502 | let res = s:GistGetPage('https://gist.github.com/delete/'.a:gistid, a:user, '_method=delete&authenticity_token='.token, '') | |
503 | if len(res.content) > 0 | |
504 | redraw | |
505 | echo 'Done: ' | |
506 | else | |
507 | let message = matchstr(res.header, '^Status: ') | |
508 | let message = substitute(message, '^[^:]\+: [0-9]\+ ', '', '') | |
509 | echohl ErrorMsg | echomsg 'Delete failed: '.message | echohl None | |
510 | endif | |
511 | else | |
512 | echohl ErrorMsg | echomsg 'Delete failed' | echohl None | |
513 | endif | |
514 | endfunction | |
515 | ||
516 | ||
517 | " GistPost function: | |
518 | " Post new gist to github | |
519 | " | |
520 | " if there is an embedded gist url or gist id in your file, | |
521 | " it will just update it. | |
522 | " -- by c9s | |
523 | " | |
524 | " embedded gist url format: | |
525 | " | |
526 | " Gist: https://gist.github.com/123123 | |
527 | " | |
528 | " embedded gist id format: | |
529 | " | |
530 | " GistID: 123123 | |
531 | " | |
532 | function! s:GistPost(user, token, content, private) | |
533 | ||
534 | " find GistID: in content, then we should just update | |
535 | for l in split(a:content, "\n") | |
536 | if l =~ '\<GistID:' | |
537 | let gistid = matchstr(l, 'GistID:\s*[0-9a-z]\+') | |
538 | ||
539 | if strlen(gistid) == 0 | |
540 | echohl WarningMsg | echo "GistID error" | echohl None | |
541 | return | |
542 | endif | |
543 | echo "Found GistID: " . gistid | |
544 | ||
545 | cal s:GistUpdate(a:user, a:token, a:content, gistid, '') | |
546 | return | |
547 | elseif l =~ '\<Gist:' | |
548 | let gistid = matchstr(l, 'Gist:\s*https://gist.github.com/[0-9a-z]\+') | |
549 | ||
550 | if strlen(gistid) == 0 | |
551 | echohl WarningMsg | echo "GistID error" | echohl None | |
552 | return | |
553 | endif | |
554 | echo "Found GistID: " . gistid | |
555 | ||
556 | cal s:GistUpdate(a:user, a:token, a:content, gistid, '') | |
557 | return | |
558 | endif | |
559 | endfor | |
560 | ||
561 | let ext = expand('%:e') | |
562 | let ext = len(ext) ? '.'.ext : '' | |
563 | let name = expand('%:t') | |
564 | ||
565 | let query = [ | |
566 | \ 'file_ext[gistfile1]=%s', | |
567 | \ 'file_name[gistfile1]=%s', | |
568 | \ 'file_contents[gistfile1]=%s', | |
569 | \ ] | |
570 | ||
571 | if len(a:user) > 0 && len(a:token) > 0 | |
572 | call add(query, 'login=%s') | |
573 | call add(query, 'token=%s') | |
574 | else | |
575 | call add(query, '%.0s%.0s') | |
576 | endif | |
577 | ||
578 | if a:private | |
579 | call add(query, 'action_button=private') | |
580 | endif | |
581 | let squery = printf(join(query, '&'), | |
582 | \ s:encodeURIComponent(ext), | |
583 | \ s:encodeURIComponent(name), | |
584 | \ s:encodeURIComponent(a:content), | |
585 | \ s:encodeURIComponent(a:user), | |
586 | \ s:encodeURIComponent(a:token)) | |
587 | unlet query | |
588 | ||
589 | let file = tempname() | |
590 | call writefile([squery], file) | |
591 | echon 'Posting it to gist... ' | |
592 | let quote = &shellxquote == '"' ? "'" : '"' | |
593 | let url = 'https://gist.github.com/gists' | |
594 | let res = system('curl -i '.g:gist_curl_options.' -d @'.quote.file.quote.' '.url) | |
595 | call delete(file) | |
596 | let headers = split(res, '\(\r\?\n\|\r\n\?\)') | |
597 | let location = matchstr(headers, '^Location: ') | |
598 | let location = substitute(location, '^[^:]\+: ', '', '') | |
599 | if len(location) > 0 && location =~ '^\(http\|https\):\/\/gist\.github\.com\/' | |
600 | redraw | |
601 | echo 'Done: '.location | |
602 | else | |
603 | let message = matchstr(headers, '^Status: ') | |
604 | let message = substitute(message, '^[^:]\+: [0-9]\+ ', '', '') | |
605 | echohl ErrorMsg | echomsg 'Post failed: '.message | echohl None | |
606 | endif | |
607 | return location | |
608 | endfunction | |
609 | ||
610 | function! s:GistPostBuffers(user, token, private) | |
611 | let bufnrs = range(1, bufnr("$")) | |
612 | let bn = bufnr('%') | |
613 | let query = [] | |
614 | if len(a:user) > 0 && len(a:token) > 0 | |
615 | call add(query, 'login=%s') | |
616 | call add(query, 'token=%s') | |
617 | else | |
618 | call add(query, '%.0s%.0s') | |
619 | endif | |
620 | if a:private | |
621 | call add(query, 'action_button=private') | |
622 | endif | |
623 | let squery = printf(join(query, "&"), | |
624 | \ s:encodeURIComponent(a:user), | |
625 | \ s:encodeURIComponent(a:token)) . '&' | |
626 | ||
627 | let query = [ | |
628 | \ 'file_ext[gistfile]=%s', | |
629 | \ 'file_name[gistfile]=%s', | |
630 | \ 'file_contents[gistfile]=%s', | |
631 | \ ] | |
632 | let format = join(query, "&") . '&' | |
633 | ||
634 | let index = 1 | |
635 | for bufnr in bufnrs | |
636 | if !bufexists(bufnr) || buflisted(bufnr) == 0 | |
637 | continue | |
638 | endif | |
639 | echo "Creating gist content".index."... " | |
640 | silent! exec "buffer!" bufnr | |
641 | let content = join(getline(1, line('$')), "\n") | |
642 | let ext = expand('%:e') | |
643 | let ext = len(ext) ? '.'.ext : '' | |
644 | let name = expand('%:t') | |
645 | let squery .= printf(substitute(format, 'gistfile', 'gistfile'.index, 'g'), | |
646 | \ s:encodeURIComponent(ext), | |
647 | \ s:encodeURIComponent(name), | |
648 | \ s:encodeURIComponent(content)) | |
649 | let index = index + 1 | |
650 | endfor | |
651 | silent! exec "buffer!" bn | |
652 | ||
653 | let file = tempname() | |
654 | call writefile([squery], file) | |
655 | echo "Posting it to gist... " | |
656 | let quote = &shellxquote == '"' ? "'" : '"' | |
657 | let url = 'https://gist.github.com/gists' | |
658 | let res = system('curl -i '.g:gist_curl_options.' -d @'.quote.file.quote.' '.url) | |
659 | call delete(file) | |
660 | let res = matchstr(split(res, '\(\r\?\n\|\r\n\?\)'), '^Location: ') | |
661 | let res = substitute(res, '^.*: ', '', '') | |
662 | if len(res) > 0 && res =~ '^\(http\|https\):\/\/gist\.github\.com\/' | |
663 | redraw | |
664 | echo 'Done: '.res | |
665 | else | |
666 | echohl ErrorMsg | echomsg 'Post failed' | echohl None | |
667 | endif | |
668 | return res | |
669 | endfunction | |
670 | ||
671 | function! Gist(line1, line2, ...) | |
672 | if !exists('g:github_user') | |
673 | let g:github_user = substitute(system('git config --global github.user'), "\n", '', '') | |
674 | if strlen(g:github_user) == 0 | |
675 | let g:github_user = $GITHUB_USER | |
676 | end | |
677 | endif | |
678 | if !exists('g:github_token') | |
679 | let g:github_token = substitute(system('git config --global github.token'), "\n", '', '') | |
680 | if strlen(g:github_token) == 0 | |
681 | let g:github_token = $GITHUB_TOKEN | |
682 | end | |
683 | endif | |
684 | if strlen(g:github_user) == 0 || strlen(g:github_token) == 0 | |
685 | echohl ErrorMsg | |
686 | echomsg "You have no setting for github." | |
687 | echohl WarningMsg | |
688 | echo "git config --global github.user your-name" | |
689 | echo "git config --global github.token your-token" | |
690 | echo "or set g:github_user and g:github_token in your vimrc" | |
691 | echo "or set shell env vars GITHUB_USER and GITHUB_TOKEN" | |
692 | echohl None | |
693 | return 0 | |
694 | end | |
695 | ||
696 | let bufname = bufname("%") | |
697 | let user = g:github_user | |
698 | let token = g:github_token | |
699 | let gistid = '' | |
700 | let gistls = '' | |
701 | let gistnm = '' | |
702 | let private = g:gist_private | |
703 | let multibuffer = 0 | |
704 | let clipboard = 0 | |
705 | let deletepost = 0 | |
706 | let editpost = 0 | |
707 | let listmx = '^\(-l\|--list\)\s*\([^\s]\+\)\?$' | |
708 | let bufnamemx = '^' . s:bufprefix .'\([0-9a-f]\+\)$' | |
709 | ||
710 | let args = (a:0 > 0) ? split(a:1, ' ') : [] | |
711 | for arg in args | |
712 | if arg =~ '^\(-la\|--listall\)$\C' | |
713 | let gistls = '-all' | |
714 | elseif arg =~ '^\(-l\|--list\)$\C' | |
715 | if g:gist_show_privates | |
716 | let gistls = 'mine' | |
717 | else | |
718 | let gistls = g:github_user | |
719 | endif | |
720 | elseif arg == '--abandon\C' | |
721 | call s:GistGetPage('', '', '', '') | |
722 | return | |
723 | elseif arg =~ '^\(-m\|--multibuffer\)$\C' | |
724 | let multibuffer = 1 | |
725 | elseif arg =~ '^\(-p\|--private\)$\C' | |
726 | let private = 1 | |
727 | elseif arg =~ '^\(-P\|--public\)$\C' | |
728 | let private = 0 | |
729 | elseif arg =~ '^\(-a\|--anonymous\)$\C' | |
730 | let user = '' | |
731 | let token = '' | |
732 | elseif arg =~ '^\(-c\|--clipboard\)$\C' | |
733 | let clipboard = 1 | |
734 | elseif arg =~ '^\(-d\|--delete\)$\C' && bufname =~ bufnamemx | |
735 | let deletepost = 1 | |
736 | let gistid = substitute(bufname, bufnamemx, '\1', '') | |
737 | elseif arg =~ '^\(-e\|--edit\)$\C' && bufname =~ bufnamemx | |
738 | let editpost = 1 | |
739 | let gistid = substitute(bufname, bufnamemx, '\1', '') | |
740 | elseif arg =~ '^\(-f\|--fork\)$\C' && bufname =~ bufnamemx | |
741 | let gistid = substitute(bufname, bufnamemx, '\1', '') | |
742 | let res = s:GistGetPage("https://gist.github.com/fork/".gistid, g:github_user, '', '') | |
743 | let loc = filter(res.header, 'v:val =~ "^Location:"')[0] | |
744 | let loc = substitute(loc, '^[^:]\+: ', '', '') | |
745 | let mx = '^https://gist.github.com/\([0-9a-z]\+\)$' | |
746 | if loc =~ mx | |
747 | let gistid = substitute(loc, mx, '\1', '') | |
748 | else | |
749 | echohl ErrorMsg | echomsg 'Fork failed' | echohl None | |
750 | return | |
751 | endif | |
752 | elseif arg !~ '^-' && len(gistnm) == 0 | |
753 | if editpost == 1 || deletepost == 1 | |
754 | let gistnm = arg | |
755 | elseif len(gistls) > 0 && arg != '^\w\+$\C' | |
756 | let gistls = arg | |
757 | elseif arg =~ '^[0-9a-z]\+$\C' | |
758 | let gistid = arg | |
759 | else | |
760 | echohl ErrorMsg | echomsg 'Invalid arguments' | echohl None | |
761 | unlet args | |
762 | return 0 | |
763 | endif | |
764 | elseif len(arg) > 0 | |
765 | echohl ErrorMsg | echomsg 'Invalid arguments' | echohl None | |
766 | unlet args | |
767 | return 0 | |
768 | endif | |
769 | endfor | |
770 | unlet args | |
771 | "echo "gistid=".gistid | |
772 | "echo "gistls=".gistls | |
773 | "echo "gistnm=".gistnm | |
774 | "echo "private=".private | |
775 | "echo "clipboard=".clipboard | |
776 | "echo "editpost=".editpost | |
777 | "echo "deletepost=".deletepost | |
778 | ||
779 | if len(gistls) > 0 | |
780 | call s:GistList(user, token, gistls, 1) | |
781 | elseif len(gistid) > 0 && editpost == 0 && deletepost == 0 | |
782 | call s:GistGet(user, token, gistid, clipboard) | |
783 | else | |
784 | let url = '' | |
785 | if multibuffer == 1 | |
786 | let url = s:GistPostBuffers(user, token, private) | |
787 | else | |
788 | let content = join(getline(a:line1, a:line2), "\n") | |
789 | if editpost == 1 | |
790 | let url = s:GistUpdate(user, token, content, gistid, gistnm) | |
791 | elseif deletepost == 1 | |
792 | call s:GistDelete(user, token, gistid) | |
793 | else | |
794 | let url = s:GistPost(user, token, content, private) | |
795 | endif | |
796 | endif | |
797 | if len(url) > 0 | |
798 | if g:gist_open_browser_after_post | |
799 | let cmd = substitute(g:gist_browser_command, '%URL%', url, 'g') | |
800 | if cmd =~ '^!' | |
801 | silent! exec cmd | |
802 | elseif cmd =~ '^:[A-Z]' | |
803 | exec cmd | |
804 | else | |
805 | call system(cmd) | |
806 | endif | |
807 | endif | |
808 | if g:gist_put_url_to_clipboard_after_post > 0 | |
809 | if g:gist_put_url_to_clipboard_after_post == 2 | |
810 | let url = url . "\n" | |
811 | endif | |
812 | if exists('g:gist_clip_command') | |
813 | call system(g:gist_clip_command, url) | |
814 | elseif has('unix') && !has('xterm_clipboard') | |
815 | let @" = url | |
816 | else | |
817 | let @+ = url | |
818 | endif | |
819 | endif | |
820 | endif | |
821 | endif | |
822 | return 1 | |
823 | endfunction | |
824 | ||
825 | command! -nargs=? -range=% Gist :call Gist(<line1>, <line2>, <f-args>) | |
826 | " vim:set et: |