From 2845332a4fb9524ed5b060c416ed5443c2602eed Mon Sep 17 00:00:00 2001 From: Iain Patterson Date: Fri, 12 Feb 2016 14:12:32 +0000 Subject: [PATCH] Added jdaddy plugin. --- .vim/autoload/jdaddy.vim | 256 +++++++++++++++++++++++++++++++++++++++++++++++ .vim/doc/jdaddy.txt | 36 +++++++ .vim/plugin/jdaddy.vim | 19 ++++ 3 files changed, 311 insertions(+) create mode 100644 .vim/autoload/jdaddy.vim create mode 100644 .vim/doc/jdaddy.txt create mode 100644 .vim/plugin/jdaddy.vim diff --git a/.vim/autoload/jdaddy.vim b/.vim/autoload/jdaddy.vim new file mode 100644 index 0000000..d35cafa --- /dev/null +++ b/.vim/autoload/jdaddy.vim @@ -0,0 +1,256 @@ +" autoload/jdaddy.vim +" Author: Tim Pope + +if exists("g:autoloaded_jdaddy") + finish +endif +let g:autoloaded_jdaddy = 1 + +if !exists('g:jdaddy#null') + let g:jdaddy#null = ['null'] + let g:jdaddy#false = ['false'] + let g:jdaddy#true = ['true'] +endif + +function! s:sub(str,pat,rep) abort + return substitute(a:str,'\v\C'.a:pat,a:rep,'') +endfunction + +function! s:gsub(str,pat,rep) abort + return substitute(a:str,'\v\C'.a:pat,a:rep,'g') +endfunction + +" Text Objects {{{1 + +function! jdaddy#inner_pos(...) abort + let cnt = a:0 ? a:1 : 1 + let line = getline('.') + let char = line[col('.')-1] + if char ==# '"' || len(s:gsub(s:gsub(line[0 : col('.')-1], '\\.', ''), '[^"]', '')) % 2 + let cnt -= 1 + if !cnt + let quotes = [] + for pos in range(len(line)) + if exists('skip') + unlet skip + elseif line[pos] ==# '\' + let skip = 1 + elseif line[pos] ==# '"' + let quotes += [pos] + endif + endfor + let before = filter(copy(quotes), 'v:val <= col(".")-1') + let after = filter(copy(quotes), 'v:val > col(".")-1') + if before[-1] == col('.')-1 && len(before) % 2 == 0 + return [line('.'), before[-2]+1, line('.'), before[-1]+1] + else + return [line('.'), before[-1]+1, line('.'), after[0]+1] + endif + endif + elseif char =~# '[[:alnum:]._+-]' + let cnt -= 1 + if !cnt + let [start, end] = [col('.')-1, col('.')-1] + while line[start-1] =~# '[[:alnum:]._+-]' + let start -= 1 + endwhile + while line[end+1] =~# '[[:alnum:]._+-]' + let end += 1 + endwhile + return [line('.'), start+1, line('.'), end+1] + endif + endif + if char =~# '[]})]' + let cnt -= 1 + let [lclose, cclose] = [line('.'), col('.')] + else + let [lclose, cclose] = searchpairpos('[[{(]', '', '[]})]', 'W') + endif + let [lopen, copen] = searchpairpos('[[{(]', '', '[]})]', 'Wb') + if !lopen || !lclose + return [0, 0, 0, 0] + endif + return [lopen, copen, lclose, cclose] +endfunction + +function! jdaddy#outer_pos(...) abort + if getline('.')[col('.')-1] =~# '[]}]' + let [lclose, cclose] = [line('.'), col('.')] + else + let [lclose, cclose] = searchpairpos('[[{]', '', '[]}]', 'r') + endif + let [lopen, copen] = searchpairpos('[[{]', '', '[]}]', 'rb') + if lopen && lclose + return [lopen, copen, lclose, cclose] + endif + return [0, 0, 0, 0] +endfunction + +function! s:movement_string(line, col) abort + return a:line . "G0" . (a:col > 1 ? (a:col - 1) . "l" : "") +endfunction + +function! jdaddy#inner_movement(count) abort + let [lopen, copen, lclose, cclose] = jdaddy#inner_pos(a:count) + if !lopen + return "\" + endif + call setpos("'[", [0, lopen, copen, 0]) + call setpos("']", [0, lclose, cclose, 0]) + return "`[o`]" +endfunction + +function! jdaddy#outer_movement(count) abort + let [lopen, copen, lclose, cclose] = jdaddy#outer_pos(a:count) + if !lopen + return "\" + endif + call setpos("'[", [0, lopen, copen, 0]) + call setpos("']", [0, lclose, cclose, 0]) + return s:movement_string(lopen, copen) . 'o' . s:movement_string(lclose, cclose) +endfunction + +" }}}1 + +function! jdaddy#parse(string) abort + let [null, false, true] = [g:jdaddy#null, g:jdaddy#false, g:jdaddy#true] + let one_line = substitute(a:string, "[\r\n]\\s*", ' ', 'g') + let quoted_keys = substitute(one_line, + \ '\C"\(\\.\|[^"\\]\)*"\|\w\+\ze:\|[[:,]\s*\zs\h\w*\ze\s*[]},]', + \ '\=submatch(0) =~# "^\\%(\"\\|true$\\|false$\\|null$\\)" ? submatch(0) : "\"\002" . submatch(0) . "\""', + \ 'g') + let stripped = substitute(quoted_keys,'\C"\(\\.\|[^"\\]\)*"','','g') + if stripped !~# "[^,:{}\\[\\]0-9.\\-+Eaeflnr-u \n\r\t]" + try + return eval(quoted_keys) + catch + endtry + endif + throw "jdaddy: invalid JSON: ".one_line +endfunction + +let s:escapes = { + \ "\b": '\b', + \ "\f": '\f', + \ "\n": '\n', + \ "\r": '\r', + \ "\t": '\t', + \ "\"": '\"', + \ "\\": '\\'} + +function! jdaddy#dump(object, ...) abort + let opt = extend({'width': 0, 'level': 0, 'indent': 1, 'before': 0, 'seen': []}, a:0 ? copy(a:1) : {}) + let opt.seen = copy(opt.seen) + let childopt = copy(opt) + let childopt.before = 0 + let childopt.level += 1 + let indent = repeat(' ', opt.indent) + for i in range(len(opt.seen)) + if a:object is opt.seen[i] + return type(a:object) == type([]) ? '[...]' : '{...}' + endif + endfor + if a:object is g:jdaddy#null + return 'null' + elseif a:object is g:jdaddy#false + return 'false' + elseif a:object is g:jdaddy#true + return 'true' + elseif type(a:object) ==# type('') + if a:object =~# '^\%x02.' + let dump = a:object[1:-1] + else + let dump = '"'.s:gsub(a:object, "[\001-\037\"\\\\]", '\=get(s:escapes, submatch(0), printf("\\u%04x", char2nr(submatch(0))))').'"' + endif + elseif type(a:object) ==# type([]) + let childopt.seen += [a:object] + let dump = '['.join(map(copy(a:object), 'jdaddy#dump(v:val, {"seen": childopt.seen, "level": childopt.level})'), ', ').']' + if opt.width && opt.before + opt.level * opt.indent + len(s:gsub(dump, '.', '.')) > opt.width + let space = repeat(indent, opt.level) + let dump = '[' . join(map(copy(a:object), '"\n".indent.space.jdaddy#dump(v:val, childopt)'), ",") . "\n" . space . ']' + endif + elseif type(a:object) ==# type({}) + let childopt.seen += [a:object] + let keys = sort(keys(a:object)) + let dump = '{'.join(map(copy(keys), 'jdaddy#dump(v:val) . ": " . jdaddy#dump(a:object[v:val], {"seen": childopt.seen, "indent": childopt.indent, "level": childopt.level})'), ', ').'}' + if opt.width && opt.before + opt.level * opt.indent + len(s:gsub(dump, '.', '.')) > opt.width + let space = repeat(indent, opt.level) + let lines = [] + let last = get(keys, -1, '') + for k in keys + let prefix = jdaddy#dump(k) . ':' + let suffix = jdaddy#dump(a:object[k]) . ',' + if len(space . prefix . ' ' . suffix) >= opt.width - (k ==# last ? -1 : 0) + call extend(lines, [prefix . ' ' . jdaddy#dump(a:object[k], extend(copy(childopt), {'before': len(prefix)+1})) . ',']) + else + call extend(lines, [prefix . ' ' . suffix]) + endif + endfor + let dump = s:sub("{\n" . indent . space . join(lines, "\n" . indent . space), ',$', "\n" . space . '}') + endif + else + let dump = string(a:object) + endif + return dump +endfunction + +function! jdaddy#reformat(func, count, ...) abort + let [lopen, copen, lclose, cclose] = call(a:func, [a:count]) + if !lopen + return '' + endif + if lopen == lclose + let body = getline(lopen)[copen-1 : cclose-1] + else + let body = getline(lopen)[copen-1 : -1] . "\n" . join(map(getline(lopen+1, lclose-1), 'v:val."\n"'), '') . getline(lclose)[0 : cclose-1] + endif + if &filetype ==# 'vim' + let body = substitute(body, "\n\\s*\\\\", "\n", "g") + endif + try + if a:0 + let json = jdaddy#combine(jdaddy#parse(body), jdaddy#parse(getreg(a:1))) + else + let json = jdaddy#parse(body) + endif + catch /^jdaddy:/ + return 'echoerr '.string(v:exception) + endtry + let level = indent(lopen)/&sw + if &filetype ==# 'vim' && getline(lopen) =~# '^\s*\\' + let level = len(matchstr(getline(lopen), '\\\zs\s*'))/&sw + endif + let dump = + \ (copen == 1 ? '' : getline(lopen)[0 : copen-2]) . + \ jdaddy#dump(json, {'width': (&tw ? &tw : 79), 'indent': &sw, 'level': level, 'before': copen-1-level}) . + \ getline(lclose)[cclose : -1] + if &filetype ==# 'vim' && getline(lclose) =~# '^\s*\\' + let pre = matchstr(getline(lclose), '^\s*') + let dump = substitute(dump, "\n", "\n" . pre . '\\', 'g') + endif + call append(lclose, split(dump, "\n")) + silent exe lopen.','.lclose.'delete _' + call setpos('.', [0, lopen, copen, 0]) + silent! call repeat#set(":call jdaddy#reformat(".string(a:func).",".string(a:count).(a:0 ? ",".string(a:1) : "").")\") + return '' +endfunction + +function! jdaddy#combine(one, two) abort + if a:one is g:jdaddy#null || a:one is g:jdaddy#false + return a:two + elseif a:two is g:jdaddy#null || a:two is g:jdaddy#false + return a:one + elseif a:one is g:jdaddy#true + return a:one + elseif type(a:one) != type(a:two) + throw "jdaddy: Can't combine disparate types" + elseif type(a:one) == type({}) + return extend(copy(a:one), a:two) + elseif type(a:one) == type('') + return a:one . a:two + else + return a:one + a:two + endif +endfunction + +" vim:set et sw=2: diff --git a/.vim/doc/jdaddy.txt b/.vim/doc/jdaddy.txt new file mode 100644 index 0000000..5879e50 --- /dev/null +++ b/.vim/doc/jdaddy.txt @@ -0,0 +1,36 @@ +*jdaddy.txt* JSON manipulation and pretty printing + +Author: Tim Pope +Repo: https://github.com/tpope/vim-jdaddy +License: Same terms as Vim itself (see |license|) + +USAGE *jdaddy* + + *ij* +ij Text object for innermost JSON number, string, array, + object, or keyword (including but not limited to + true/false/null). + + *aj* +aj Text object for outermost JSON object or array. + + *gqij* +gqij Replace the innermost JSON with a pretty printed + version. + + *gqaj* +gqaj Replace the outermost JSON with a pretty printed + version. + + *gwij* +["x]gwij Merge the JSON from the register into the innermost + JSON. Merging extends objects, concatenates strings + and arrays, and adds numbers. + + *gwaj* +["x]gwaj Merge the JSON from the register into the outermost + JSON. + +Pretty printing indents based on 'shiftwidth' and wraps at 'textwidth'. + + vim:tw=78:et:ft=help:norl: diff --git a/.vim/plugin/jdaddy.vim b/.vim/plugin/jdaddy.vim new file mode 100644 index 0000000..ffe9475 --- /dev/null +++ b/.vim/plugin/jdaddy.vim @@ -0,0 +1,19 @@ +" plugin/jdaddy.vim +" Author: Tim Pope + +if exists("g:loaded_jdaddy") || v:version < 700 || &cp + finish +endif +let g:loaded_jdaddy = 1 + +xnoremap ij jdaddy#inner_movement(v:count1) +onoremap ij :normal vij +xnoremap aj jdaddy#outer_movement(v:count1) +onoremap aj :normal vaj + +nnoremap gqij :exe jdaddy#reformat('jdaddy#inner_pos', v:count1) +nnoremap gqaj :exe jdaddy#reformat('jdaddy#outer_pos', v:count1) +nnoremap gwij :exe jdaddy#reformat('jdaddy#inner_pos', v:count1, v:register) +nnoremap gwaj :exe jdaddy#reformat('jdaddy#outer_pos', v:count1, v:register) + +" vim:set et sw=2: -- 2.7.4