├── README.md ├── Rakefile ├── VERSION ├── autoload ├── transform.vim └── transform │ ├── environment.vim │ ├── route.vim │ └── transformer │ ├── _ │ ├── date_time.py │ ├── duplicate.rb │ ├── input │ ├── ruby_p.rb │ ├── ruby_pp.rb │ ├── stringfy_line.rb │ └── stringfy_word.rb │ └── go │ ├── const_stringfy.rb │ ├── import.rb │ └── test │ ├── const_stringfy │ └── import ├── misc ├── demo │ ├── after.go │ └── before.go ├── example.vim └── ruby_handler │ ├── README.md │ ├── config.vim │ ├── handler.rb │ └── lib │ ├── transformer.rb │ └── transformer │ ├── base.rb │ └── go.rb ├── plugin └── transform.vim └── test └── all.vim /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | # STDIN > transform > STDOUT 12 | 13 | Thats' filter command. 14 | Filter command in other word => transformer. 15 | You can transform shorthand syntax within buffer on the fly. 16 | 17 | You can write transformer whichever language you like. 18 | This have great possibility to reduce typing! 19 | 20 | * Auto generate Go's String() methods from const 21 | ![Movie](https://raw.githubusercontent.com/t9md/t9md/465f597e88cdf977b415248942d62c2584dd2c5f/img/vim-transform/transform.gif) 22 | 23 | * Auto insert command result. 24 | ![Movie](https://raw.githubusercontent.com/t9md/t9md/465f597e88cdf977b415248942d62c2584dd2c5f/img/vim-transform/transform-2.gif) 25 | 26 | # Requirement 27 | Vim 7.4+ 28 | 29 | ## How it works 30 | 31 | 1. select area or simply place cursor where you want to transform. 32 | 2. content are piped to `STDIN` of transformer and get result from `STDOUT` of transformer 33 | 3. replace buffer with result. 34 | 35 | # Config in vimrc 36 | 37 | ```vim 38 | " Mac? 39 | nmap (transform) 40 | xmap (transform) 41 | imap (transform) 42 | 43 | " Linux or Win 44 | nmap (transform) 45 | xmap (transform) 46 | imap (transform) 47 | ``` 48 | 49 | ## Customize 50 | 51 | ```vim 52 | " all configuration goes into this dictionary 53 | let g:transform = {} 54 | 55 | " disable default handler set 56 | let g:transform.options.enable_default_config = 0 57 | 58 | " specify where to find transformer script 59 | let g:transform.options.path = "/Users/t9md/my_transformer" 60 | 61 | " handler functions for each &filetype 62 | "--------------------------------------- 63 | " each handler function take only one argment in this example `e`. 64 | " This `e` is environment vim-transformer use. 65 | " You can call `run()` or `get()` method on `e` to execute transformer. 66 | 67 | " `_` is special handler called when other filetype specific handler didn't match(=didn't call `run()`). 68 | function! g:transform._(e) 69 | call a:e.run("_/stringfy_word.rb") 70 | endfunction 71 | 72 | " `go` is handler called when &filetype=go. you can define your own handler based on &filetype. 73 | function! s:route.go(e) "{{{1 74 | let e = a:e 75 | let c = e.content 76 | if c.line_s =~# '\v^const\s*\(' && c.line_e =~# '\v\)\s*' 77 | call e.run("go/const_stringfy.rb") 78 | elseif c['line_s-1'] =~# '\v^import\s*\(' 79 | call e.run("go/import.rb") 80 | endif 81 | endfunction 82 | ``` 83 | 84 | As you see above, you can configure handler function to choose appropriate transformer based on context. 85 | `e` is environment variable, you can use several methods `e` provides. 86 | 87 | * `e.run(cmd)`: take command or list of command, after execute command, immediately finish, never return. 88 | * `e.get(cmd)`: returnable version of `run()`. 89 | 90 | You can pass list of commands to `run()` or `get()` like following 91 | 92 | ```vim 93 | call e.run([ {'hello': 'echo hello'}, { 'bye': 'echo bye'} ]) 94 | ``` 95 | 96 | `g:transform` is Dictionary with `key=&filetype`, `value=Function`. 97 | The magical `_` function is like `default_route` which always be called after &filetype specific function didn't invoke `run()`. 98 | 99 | Your configuration will be merged into [default_conf](https://github.com/t9md/vim-transform/blob/master/autoload/transform/route.vim) by `extend(default_conf, user__conf)` 100 | 101 | ### Advanced example 102 | 103 | See [example.vim](https://github.com/t9md/vim-transform/blob/master/misc/example.vim). 104 | 105 | ## I don't like default transformer set, 106 | Agree, you can disable it. 107 | 108 | ```vim 109 | let g:transform = {} 110 | let g:transform.options = {} 111 | let g:transform.options.enable_default_config = 0 112 | ``` 113 | 114 | ## How vim-transform find transformer 115 | 116 | If file begin with '/'(ex /bin/ls ) or filename not include '/'(ex some.py, some.rb) then search $PATH. 117 | 118 | A. Absolute path ex) /bin/ls 119 | B. Filename not include '/' ex) some.py some.rb 120 | C. Filename include '/' in the middle of filenmae. 121 | 122 | for A, B, vim-transform pass command to `system()` as-is, means use $PATH. 123 | 124 | for C, vim-transform search like this. 125 | 126 | 1. user's transformer_ directory 127 | 2. system default transformer directory 128 | 129 | You can set user's transformer directories with comma sepalated list of directory. 130 | 131 | ```vim 132 | let g:transform.options.path = "/Users/t9md/transformer,/Users/work/myfriend/transformer" 133 | ``` 134 | 135 | NOTE: As explained in C. you need '/' in flie name. 136 | 137 | ```vim 138 | " filename include '/' try search from transformer dir 139 | call e.run("go/const_stringfy.rb") 140 | 141 | " since filename not include '/' not trying to search from from $PATH. 142 | call e.run("const_stringfy.rb") 143 | ``` 144 | 145 | ## Want to change routing based on filename 146 | 147 | Google translate only if file name include `translate.md` 148 | ```vim 149 | if e.buffer.filename =~# 'translate.md' 150 | call e.run('google_translate.py') 151 | endif 152 | ``` 153 | 154 | ## I don't need filetype spefic function, want to controll in one place. 155 | 156 | You can, if you disable default handler and didn't define filtype specific handler, all transform request fall into `_` handler. 157 | 158 | ```vim 159 | let g:transform = {} 160 | let g:transform.options = {} 161 | let g:transform.options.enable_default_config = 0 162 | 163 | " you can use get filetype via env.buffer.filetype 164 | function! g:transform._(e) 165 | let e = a:e 166 | let c = e.content 167 | 168 | if e.buffer.filetype ==# 'go' 169 | if c.line_s =~# '\v^const\s*\(' && c.line_e =~# '\v\)\s*' 170 | call e.run("go/const_stringfy.rb") 171 | elseif c['line_s-1'] =~# '\v^import\s*\(' 172 | call e.run("go/import.rb") 173 | endif 174 | endif 175 | 176 | call e.run("_/stringfy_word.rb") 177 | endfunction 178 | ``` 179 | 180 | ## What is the `e` argument passed to `g:transform[&ft](e)` function? 181 | 182 | This is environment vim-transform use. 183 | You can see its value by 184 | 185 | ```vim 186 | " requre vim-prettyprint to use PP() 187 | " 1 = line_start, 10 = line_end, n = normal mode(use v for visual) 188 | :echo PP(transform#environment#new(1, 10, 'n')) 189 | ``` 190 | 191 | example output of `environment` 192 | 193 | ```vim 194 | { 195 | 'buffer': { 196 | 'bufnr': 4, 197 | 'filename': 'tryit.vim', 198 | 'filetype': 'vim', 199 | 'line_e': 5, 200 | 'line_e+1': 6, 201 | 'line_s': 1, 202 | 'line_s-1': 0 203 | }, 204 | 'content': { 205 | 'all': [ 206 | 'echo PP(transform#environment#new(1, 5, ''n''))', 207 | '', 208 | '', 209 | '', 210 | '' 211 | ], 212 | 'len': 5, 213 | 'line_e': '', 214 | 'line_e+1': '', 215 | 'line_s': 216 | 'echo PP(transform#environment#new(1, 5, ''n''))', 217 | 'line_s-1': '', 218 | 'update': function('971') 219 | }, 220 | 'get': function('251'), 221 | 'mode': 'n', 222 | 'new': function('249'), 223 | 'path': { 224 | 'dir_base': 225 | '/Users/t9md/.vim/bundle/vim-transform/autoload/transform', 226 | 'dir_transformer': 227 | '/Users/t9md/.vim/bundle/vim-transform/autoload/transform/transformer' 228 | }, 229 | 'run': function('250'), 230 | 'set_buffer': function('254'), 231 | 'set_content': function('253'), 232 | 'set_path': function('252') 233 | } 234 | ``` 235 | 236 | ## I don't want write any Vimscript, want to completely handle my faviorite language. 237 | 238 | OK, you don't like routing logic written in Vimscript. 239 | If so, let Vim delegate all request to your favorite handler. 240 | * in Vim side, all request is forwarded to handler.rb 241 | * handler.rb have responsible both request routing and response(=transformation). 242 | * `env` informatino is available as JSON object within external handler! 243 | 244 | So you can write routing logic like below(authogh this is rough example handler, hope improve by your side). 245 | ```ruby 246 | Transformer.register do 247 | if FILE_TYPE == 'go' 248 | if $env['content']['line_s-1'] =~ /^import\s*\(/ 249 | get /./ do |req| 250 | puts TF::Go::Import.run(req) 251 | end 252 | end 253 | 254 | get /^const\s*\(.*\)$/m do |req| 255 | puts TF::Go::ConstStringfy.run(req) 256 | end 257 | end 258 | end 259 | ``` 260 | 261 | Check example [ruby_handler](https://github.com/t9md/vim-transform/blob/master/misc/ruby_handler) for more detail. 262 | 263 | 264 | # once Ideally => now MUST 265 | Keep transformer script itself independent from editor, mean sharable between several editors. 266 | 267 | # TODO? 268 | * ` 0%` currently input is always treated as linewise, support charwise to transform partial area within single line 269 | * ` 1%` good default config and tranformer set? 270 | * ` 0%` Unite transformer? 271 | * ` 0%` inlucde standard Ruby/CoffeeScript/Go/Lua/Python handler. and enable user choose favorite handler from configuration. 272 | 273 | # DONE 274 | * `100%` Pass list of command to `run()`,`get()`, and execute by choice. 275 | * `100%` command line arguments(or parameters) to transformer? 276 | => user's preference, shoud work. 277 | * `100%` support directly excutable transformer( except windows ). 278 | * `100%` determine appropreate run command like 'ruby', 'python', 'go run' from extention of each transfomer? 279 | 'ext => runner' table is not mature. 280 | * `100%`choose appropriate set of transformer from `&filetype` => call appropriate handler function based on &ft. 281 | * `100%` support arbitrary directory for user's transformer 282 | * `100%` chosing appropriate transformer is hard, better to `do_what_I_mean` behavior by invoking controller and controller choose appropriate transformer from context(language and passed string). 283 | * ` 50%` make `:Transform` accept arg for directly specify transformer 284 | => need doc => `:Transform 'v|n' TRANSFORMER`. v = visual, n = normal 285 | * `100%` Make multiple tranformer chainable so that we can stringfy then surround by `import(` and `)`. 286 | => You can use pipe `|` in xNIX OS but need to exutable except first comand. 287 | => introduce get(), returnable/chainable verion of run(). 288 | 289 | # DONT( THINK / CARE / DO ) 290 | * Whats' defference in advanced snipett plugin?(maybe this is way simple). 291 | * CofferScript will be great helper as transformer for its simple syntax to JavaScript syntax(some of which is legal in other language). => nothing to do, user's preference. 292 | * Template engine like erb is better in most case? => you can by your own transformer. 293 | * Transformer Specification? when first arg is 'check', it shoud return 1 or 0 which is used by controller to determine appropreate transformer. => STDIN > STDOUT that's all. KISS. 294 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | desc "zip" 2 | task :zip do 3 | version = File.read("VERSION").chomp 4 | dirname = File.basename( File.dirname(File.expand_path(__FILE__))) 5 | zipname = "#{dirname}-#{version}.zip" 6 | sh "zip -r #{zipname} README.md autoload doc plugin -x doc/tags" 7 | end 8 | 9 | # desc "release" 10 | # task :release => [:tag, :zip] 11 | 12 | desc "tag" 13 | task :tag do 14 | version = File.read("VERSION").chomp 15 | ver_s = "v#{version}" 16 | sh "git tag -a #{ver_s} -m '#{ver_s}'" 17 | sh "git push -u origin master #{ver_s}" 18 | end 19 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.1 2 | -------------------------------------------------------------------------------- /autoload/transform.vim: -------------------------------------------------------------------------------- 1 | " Table to determine runner from file extention. 2 | let s:ext2run = { 3 | \ "rb": "ruby", 4 | \ "py": "python", 5 | \ "pl": "perl", 6 | \ "sh": "sh", 7 | \ "go": "go run", 8 | \ "coffee": "coffee", 9 | \ "js": "node", 10 | \ "lua": "lua", 11 | \ } 12 | 13 | let s:options_default = { 14 | \ 'enable_default_config': 1, 15 | \ 'path': '', 16 | \ } 17 | 18 | " Utility: 19 | " ------------------------------------------------ 20 | function! s:exists_Dictionary(var) "{{{1 21 | return exists(a:var) && s:is_Dictionary(eval(a:var)) 22 | endfunction 23 | 24 | function! s:str_strip(s) "{{{1 25 | " strip leading and trailing whilte space 26 | return substitute(a:s, '\v(^\s*)|(\s*$)', '', 'g') 27 | endfunction 28 | 29 | function! s:define_type_checker() "{{{1 30 | " dynamically define s:is_Number(v) etc.. 31 | let types = { 32 | \ "Number": 0, 33 | \ "String": 1, 34 | \ "Funcref": 2, 35 | \ "List": 3, 36 | \ "Dictionary": 4, 37 | \ "Float": 5, 38 | \ } 39 | 40 | for [type, number] in items(types) 41 | let s = '' 42 | let s .= 'function! s:is_' . type . '(v)' . "\n" 43 | let s .= ' return type(a:v) ==# ' . number . "\n" 44 | let s .= 'endfunction' . "\n" 45 | execute s 46 | endfor 47 | endfunction 48 | "}}} 49 | call s:define_type_checker() 50 | unlet! s:define_type_checker 51 | 52 | function! s:cmd_parse(cmd) "{{{1 53 | " split `cmd` to [bin, arg] like following 54 | " Example: 55 | " ' /bin/ls -l ' => ['/bin/ls', ' -l'] 56 | " 'grep -v "^\s*$"' => ['grep', ' -v "^\s*$"'] 57 | " '/bin/ls' => ['/bin/ls', ''] 58 | let cmd = s:str_strip(a:cmd) 59 | let i = stridx(cmd, ' ') 60 | if i ==# -1 61 | let bin = cmd 62 | let arg = '' 63 | else 64 | let bin = strpart(cmd, 0, i) 65 | let arg = strpart(cmd, i) 66 | endif 67 | return [bin, arg] 68 | endfunction 69 | "}}} 70 | 71 | " Main: 72 | " ------------------------------------------------ 73 | let s:T = {} 74 | let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95') 75 | 76 | function! s:T.read_config() "{{{1 77 | " Prepare all configuration used in vim-transformer. 78 | let conf_user = 79 | \ s:exists_Dictionary('g:transform') 80 | \ ? g:transform 81 | \ : {} 82 | 83 | let conf = {} 84 | call extend(conf, conf_user) 85 | if !s:is_Dictionary(get(conf, 'options')) 86 | let conf.options = {} 87 | endif 88 | call extend(conf.options, s:options_default, 'keep') 89 | 90 | if conf.options.enable_default_config 91 | call extend(conf, transform#route#default(), 'keep') 92 | endif 93 | return conf 94 | endfunction 95 | 96 | function! s:T.handle() "{{{1 97 | " Call handler function based on &filetype. 98 | let handlers = [self.env.buffer.filetype, "_" ] 99 | 100 | for handler in handlers 101 | unlet! TF 102 | let TF = get(self.conf, handler) 103 | if !s:is_Funcref(TF) 104 | continue 105 | endif 106 | call call(TF, [self.env], self.conf) 107 | endfor 108 | 109 | throw "NOTHING_TODO" 110 | endfunction 111 | 112 | function! s:T.select(cmds) "{{{1 113 | " Return command from list of command by let user choose one. 114 | " 115 | " IN: 116 | " [ {'hello': 'echo hello'}, { 'bye': 'echo bye'} ] 117 | " OUT: 118 | " chose 1 => 'echo hello' 119 | " chose 2 => 'echo bye' 120 | " chose 0 => throw 'OUT_OF_RANGE' 121 | " chose 9 => throw 'OUT_OF_RANGE' 122 | let cmds = a:cmds 123 | let menu = ['Transform: '] 124 | let num2cmd = {} 125 | 126 | let i = 0 127 | let desc_longest = 0 128 | for D in cmds 129 | if !s:is_Dictionary(D) 130 | continue 131 | unlet! D 132 | endif 133 | 134 | let i += 1 135 | let [desc, cmd] = items(D)[0] 136 | let num2cmd[i] = cmd 137 | let desc_longest = max([desc_longest, len(desc)]) 138 | let fmt = " %d: %-" . desc_longest . "s => '%s'" 139 | call add(menu, printf(fmt, i, desc, cmd)) 140 | unlet! D 141 | endfor 142 | 143 | let R = get(num2cmd, inputlist(menu), '') 144 | if !empty(R) 145 | return R 146 | endif 147 | throw 'OUT_OF_RANGE' 148 | endfunction 149 | 150 | function! s:T.run(...) "{{{1 151 | " Execute transformer 152 | " Selected area > STDIN > transformer > STDOUT > result 153 | " TF = transformer 154 | try 155 | let [_cmd; other] = a:000 156 | let cmd = s:is_String(_cmd) ? _cmd : self.select(_cmd) 157 | 158 | let [TF, TF_arg] = s:cmd_parse(cmd) 159 | 160 | let TF_path = self.path_resolv(TF) 161 | let cmd = executable(TF_path) && !s:is_windows 162 | \ ? TF_path 163 | \ : self.run_cmd(TF_path) 164 | 165 | let STDIN = self.env.content.all 166 | let result = system(cmd . TF_arg, STDIN) 167 | call self.env.content.update(split(result, '\n' )) 168 | endtry 169 | endfunction 170 | 171 | function! s:T.run_cmd(tf) "{{{1 172 | " Return command 173 | " 'foo.rb' => 'ruby foo.rb' 174 | " 'bar.py' => 'python bar.py' 175 | let TF = a:tf 176 | let ext = fnamemodify(TF, ":t:e") 177 | let run = get(s:ext2run, ext, '') 178 | 179 | if empty(run) | throw "CANT_DETERMINE_RUNNER" | endif 180 | if !executable(run) | throw "RUNNER_NOT_EXECUTETABLE: " . run | endif 181 | return run . " " . TF 182 | endfunction 183 | 184 | function! s:T.path_resolv(filename) "{{{1 185 | " Resolv path of transformer 186 | let TF = expand(a:filename) 187 | let TF_include_slash = stridx(TF[1:],'/') !=# -1 188 | if TF[0] ==# '/' || !TF_include_slash 189 | " absolute path(begin with '/') or filename not inclulde '/' 190 | " ex) /bin/ls, script.rb 191 | return TF 192 | endif 193 | 194 | " Search from user speficied directory and vim-transformer's directory. 195 | let dirs = join([self.conf.options.path, self.env.path.dir_transformer], ',') 196 | let found = split(globpath(dirs, TF), "\n") 197 | if !empty(found) 198 | return found[0] 199 | endif 200 | throw "TRANSFORMER_NOT_FOUND" 201 | endfunction 202 | 203 | function! s:T.start(...) "{{{1 204 | " Setup env and conf and start! 205 | let [line_s, line_e; rest] = a:000 206 | let mode = line_s !=# line_e ? 'v' : 'n' 207 | let TF = len(rest) ==# 1 ? rest[0] : '' 208 | try 209 | let self.env = transform#environment#new(line_s, line_e, mode) 210 | let self.conf = self.read_config() 211 | 212 | if !empty(TF) 213 | " User explicitly specified transformer 214 | call self.run(TF) 215 | throw 'SUCCESS' 216 | endif 217 | 218 | call self.handle() 219 | 220 | catch /^SUCCESS/ 221 | if !empty(self.env.content.all) 222 | call self.write() 223 | endif 224 | catch 225 | echom 'transform:' v:exception 226 | endtry 227 | endfunction 228 | 229 | function! s:T.write() "{{{1 230 | " Replace Vim's buffer with transformed content. 231 | if self.env.mode ==# 'v' 232 | normal! gv"_d 233 | else 234 | normal! "_dd 235 | endif 236 | call append(self.env.buffer['line_s-1'], self.env.content.all) 237 | call setpos('.', self.env.buffer.pos) 238 | endfunction 239 | 240 | " Public API: 241 | function! transform#start(...) "{{{1 242 | call call(s:T.start, a:000, s:T) 243 | endfunction 244 | 245 | function! transform#config() "{{{1 246 | return s:T.read_config() 247 | endfunction 248 | 249 | function! transform#_app() "{{{1 250 | " Internally use don't call this. 251 | return s:T 252 | endfunction 253 | " }}} 254 | " vim: foldmethod=marker 255 | -------------------------------------------------------------------------------- /autoload/transform/environment.vim: -------------------------------------------------------------------------------- 1 | let s:dir_base = expand(":p:h") 2 | 3 | let s:Env = {} 4 | function! s:Env.new(line_s, line_e, mode) "{{{1 5 | let e = { 6 | \ "mode": a:mode, 7 | \ "path": self.set_path(), 8 | \ "buffer": self.set_buffer(a:line_s, a:line_e), 9 | \ "content": self.set_content(a:line_s, a:line_e), 10 | \ } 11 | return extend(e, self) 12 | endfunction 13 | 14 | function! s:Env.toJSON() "{{{1 15 | try 16 | let env = filter(deepcopy(self), 'type(self[v:key]) isnot 2') 17 | let json = webapi#json#encode(env) 18 | catch /E117:/ 19 | throw 'toJSON require "mattn/webapi-vim"' 20 | endtry 21 | return json 22 | endfunction 23 | 24 | function! s:Env.run(...) "{{{1 25 | let app = transform#_app() 26 | call call(app.run, a:000, app) 27 | throw 'SUCCESS' 28 | endfunction 29 | 30 | function! s:Env.get(...) "{{{1 31 | let app = transform#_app() 32 | call call(app.run, a:000, app) 33 | return self 34 | endfunction 35 | 36 | function! s:Env.set_path() "{{{1 37 | let R = { 38 | \ "dir_base": s:dir_base, 39 | \ "dir_transformer": join([s:dir_base, "transformer"], "/"), 40 | \ } 41 | return R 42 | endfunction 43 | 44 | function! s:Env.set_content(line_s, line_e) "{{{1 45 | let content = getline(a:line_s, a:line_e) 46 | if !len(content) || len(content) ==# 1 && empty(content[0]) 47 | throw 'NO_CONTENT' 48 | endif 49 | let R = { 50 | \ "all": content, 51 | \ "len": len(content), 52 | \ "line_s-1": getline(a:line_s - 1), 53 | \ "line_s": content[0], 54 | \ "line_e": content[-1], 55 | \ "line_e+1": getline(a:line_e + 1), 56 | \ } 57 | function! R.update(content) 58 | let self.all = a:content 59 | let self.len = len(a:content) 60 | endfunction 61 | return R 62 | endfunction 63 | 64 | function! s:Env.set_buffer(line_s, line_e) "{{{1 65 | let bufname = bufname('') 66 | let R = { 67 | \ "filetype": &filetype, 68 | \ "filepath": fnamemodify(bufname, ':p'), 69 | \ "filename": fnamemodify(bufname, ':t'), 70 | \ "dirname": fnamemodify(bufname, ':h'), 71 | \ "ext": fnamemodify(bufname, ':e'), 72 | \ "cword": expand(''), 73 | \ "cWORD": expand(''), 74 | \ "pos": getpos('.'), 75 | \ "bufnr": bufnr(''), 76 | \ "line_s": a:line_s, 77 | \ "line_e": a:line_e, 78 | \ "line_s-1": a:line_s - 1, 79 | \ "line_e+1": a:line_e + 1, 80 | \ } 81 | return R 82 | endfunction 83 | 84 | " API: 85 | function! transform#environment#new(...) "{{{1 86 | return call(s:Env.new, a:000, s:Env) 87 | endfunction 88 | " }}} 89 | " vim: foldmethod=marker 90 | -------------------------------------------------------------------------------- /autoload/transform/route.vim: -------------------------------------------------------------------------------- 1 | " Default config 2 | let s:route = {} 3 | 4 | function! s:route._(e) "{{{1 5 | call a:e.run("_/stringfy_word.rb") 6 | endfunction 7 | 8 | function! s:route.go(e) "{{{1 9 | let e = a:e 10 | let c = e.content 11 | if c.line_s =~# '\v^const\s*\(' && c.line_e =~# '\v\)\s*' 12 | call e.run("go/const_stringfy.rb") 13 | elseif c['line_s-1'] =~# '\v^import\s*\(' 14 | call e.run("go/import.rb") 15 | endif 16 | endfunction 17 | 18 | function! transform#route#default() 19 | return deepcopy(s:route) 20 | endfunction 21 | -------------------------------------------------------------------------------- /autoload/transform/transformer/_/date_time.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | print (datetime.datetime.utcnow()) 3 | -------------------------------------------------------------------------------- /autoload/transform/transformer/_/duplicate.rb: -------------------------------------------------------------------------------- 1 | input = STDIN.read 2 | puts(input) 3 | puts(input) 4 | 5 | __END__ 6 | ## Input 7 | 8 | const ( 9 | Running State = iota 10 | Stopped 11 | Rebooting 12 | Terminated 13 | ) 14 | 15 | ## Output 16 | const ( 17 | Running State = iota 18 | Stopped 19 | Rebooting 20 | Terminated 21 | ) 22 | const ( 23 | Running State = iota 24 | Stopped 25 | Rebooting 26 | Terminated 27 | ) 28 | -------------------------------------------------------------------------------- /autoload/transform/transformer/_/input: -------------------------------------------------------------------------------- 1 | const ( 2 | Running State = iota 3 | Stopped 4 | Rebooting 5 | ) 6 | -------------------------------------------------------------------------------- /autoload/transform/transformer/_/ruby_p.rb: -------------------------------------------------------------------------------- 1 | input = STDIN.read 2 | p eval(input) 3 | 4 | __END__ 5 | ## Input 6 | [1,2,3] 7 | 8 | ## Transformed 9 | 1 10 | 2 11 | 3 12 | -------------------------------------------------------------------------------- /autoload/transform/transformer/_/ruby_pp.rb: -------------------------------------------------------------------------------- 1 | require 'pp' 2 | input = STDIN.read 3 | pp eval(input) 4 | 5 | __END__ 6 | ## Input 7 | [1,2,3] 8 | 9 | ## Transformed 10 | 1 11 | 2 12 | 3 13 | -------------------------------------------------------------------------------- /autoload/transform/transformer/_/stringfy_line.rb: -------------------------------------------------------------------------------- 1 | input = STDIN.readlines 2 | input.each do |l| 3 | nl = l.delete!("\n") 4 | print %!"#{l.strip}"! 5 | puts "\n" if nl 6 | end 7 | -------------------------------------------------------------------------------- /autoload/transform/transformer/_/stringfy_word.rb: -------------------------------------------------------------------------------- 1 | input = STDIN.readlines 2 | input.each do |l| 3 | puts l.chomp.split.map {|e| %!"#{e}"! }.join(", ") 4 | end 5 | -------------------------------------------------------------------------------- /autoload/transform/transformer/go/const_stringfy.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | def parse(s) 3 | type = "" 4 | enums = [] 5 | s.split("\n").each do |e| 6 | next if e =~ /\(|\)/ 7 | n = e.split 8 | type << n[1] if n.size > 1 9 | enums << n.first 10 | end 11 | [type, enums] 12 | end 13 | 14 | def transform(s) 15 | type, enums = *parse(s) 16 | v = type[0].downcase 17 | 18 | out = "" 19 | enums.each do |e| 20 | out << " case #{e}:\n" 21 | out << " return \"#{e}\"\n" 22 | end 23 | return <<-EOS 24 | #{s} 25 | 26 | func (#{v} #{type}) String() string { 27 | switch #{v} { 28 | #{out.chomp} 29 | default: 30 | return "Unknown" 31 | } 32 | } 33 | EOS 34 | end 35 | 36 | # "const (\n\tRunning State = iota\n\tStopped\n\tRebooting\n\tTerminated\n)" 37 | 38 | input = STDIN.read 39 | if input =~ /\s*const/ 40 | puts transform(input) 41 | end 42 | __END__ 43 | 44 | ## Original 45 | 46 | const ( 47 | Running State = iota 48 | Stopped 49 | Rebooting 50 | Terminated 51 | ) 52 | 53 | ## Transform 54 | 55 | const ( 56 | Running State = iota 57 | Stopped 58 | Rebooting 59 | Terminated 60 | ) 61 | 62 | func (s State) String() string { 63 | switch s { 64 | case Stopped: 65 | return "Stopped" 66 | case Rebooting: 67 | return "Rebooting" 68 | case Terminated: 69 | return "Terminated" 70 | default: 71 | return "Unknown" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /autoload/transform/transformer/go/import.rb: -------------------------------------------------------------------------------- 1 | input = STDIN.readlines 2 | 3 | r = [] 4 | repos = { 5 | gh: "github.com", 6 | cg: "code.google.com", 7 | gl: "golang.org", 8 | } 9 | 10 | input.each do |l| 11 | l.strip.split.map do |e| 12 | repos.each do |k, v| 13 | k = k.to_s 14 | if e =~ /^#{k}\// 15 | e = e.sub k, v 16 | break 17 | end 18 | end 19 | r << e 20 | end 21 | end 22 | puts r.map { |e| %!\t"#{e}"! }.join("\n").chomp 23 | -------------------------------------------------------------------------------- /autoload/transform/transformer/go/test/const_stringfy: -------------------------------------------------------------------------------- 1 | const ( 2 | Running State = iota 3 | Stopped 4 | Rebooting 5 | Terminated 6 | ) 7 | -------------------------------------------------------------------------------- /autoload/transform/transformer/go/test/import: -------------------------------------------------------------------------------- 1 | fmt os gh/kr/pretty gl/x/tools/go/loader 2 | cg/p/x/y 3 | 4 | -------------------------------------------------------------------------------- /misc/demo/after.go: -------------------------------------------------------------------------------- 1 | // After you type 2 | package main 3 | 4 | // import 5 | import ( 6 | fmt net/http os gh/kr/pretty 7 | ) 8 | 9 | // Stringfy const 10 | type State int 11 | 12 | const ( 13 | Running State = iota 14 | Stopped 15 | Rebooting 16 | ) 17 | 18 | // main 19 | func main() { 20 | // tedious array/slice literal 21 | a := []string{ 22 | a b c d e 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /misc/demo/before.go: -------------------------------------------------------------------------------- 1 | // Before you type 2 | package main 3 | 4 | // import 5 | 6 | // Stringfy const 7 | // main 8 | func main() { 9 | // tedious array/slice literal 10 | } 11 | -------------------------------------------------------------------------------- /misc/example.vim: -------------------------------------------------------------------------------- 1 | let g:transform = {} 2 | let g:transform.options = {} 3 | let g:transform.options.enable_default_config = 0 4 | let g:transform.options.path = "/Users/t9md/transformer" 5 | 6 | function! g:transform._(e) 7 | let e = a:e 8 | let c = e.content 9 | let FILENAME = e.buffer.filename 10 | let FILETYPE = e.buffer.filetype 11 | 12 | if FILETYPE ==# 'go' 13 | if c.line_s =~# '\v^const\s*\(' && c.line_e =~# '\v\)\s*' 14 | call e.run("go/const_stringfy.rb") 15 | elseif c['line_s-1'] =~# '\v^import\s*\(' 16 | call e.run("go/import.rb") 17 | endif 18 | endif 19 | 20 | if FILETYPE =~# 'markdown' 21 | " Dangerous example 22 | " if line is four leading space and '$', then execute string after '$' char. 23 | " ex) ' $ ls -l' => trasnsformed result of 'ls -l' 24 | 25 | let pat = '\v^ \$(.*)$' 26 | if c.len ==# 1 && c.line_s =~# pat 27 | let cmd = substitute(c.line_s, pat, '\1', '') 28 | call e.run(cmd) 29 | endif 30 | 31 | " replace URL to actual content 32 | if c.len ==# 1 && c.line_s =~# '\v^\s*https?://\S*$' 33 | call e.run('curl ' . c.line_s) 34 | endif 35 | endif 36 | 37 | " Demo: get() 38 | if FILENAME ==# 'demo.md' 39 | " run() never return, you can use get() to chain multiple transformer. 40 | " in this silly sample, supporse ~/testfile contains 'foo\nbar\n'. 41 | " final result will be `FOO` 42 | call e.get("cat ~/testfile").get("grep foo").run("tr '[:lower:]' '[:upper:]'") 43 | endif 44 | 45 | " Demo: run() with selectin 46 | " You can pass list of command to run() to choose command interactively. 47 | if FILENAME ==# 'demo.md' 48 | call e.run([ {'hello': 'echo hello'}, { 'bye': 'echo bye'} ]) 49 | endif 50 | 51 | if FILENAME =~# 'translate.md' 52 | call e.run('google_translate.py') 53 | endif 54 | call e.run("_/stringfy_word.rb") 55 | endfunction 56 | -------------------------------------------------------------------------------- /misc/ruby_handler/README.md: -------------------------------------------------------------------------------- 1 | # Example Ruby handler 2 | This is merely example to show power of transformer. 3 | 4 | ## Setup 5 | ```sh 6 | mkdir ~/transformer 7 | cp -a ruby_handler ~/transformer 8 | ``` 9 | 10 | copy & paste content of `config.vim` in your `.vimrc` 11 | 12 | ## What infomration is available in handler? 13 | As much infomation as Vim's handler. 14 | JSON serialized `env` is passed to first line of STDIN. 15 | In this example handler.rb. 16 | You can check this `env` variable like following. 17 | 18 | ```sh 19 | touch sandbox.json 20 | vim sandbox.json 21 | ``` 22 | in Vim's buffer, input `env!!` as-is then trigger invoke `(transform)`. 23 | 24 | ```sh 25 | env!!_<= YOUR_CURSOR_HERE and invoke Transformer 26 | ``` 27 | Bang! You got environment json dumped by ruby's `pp()` to buffer. 28 | Now you can start modifying handler.rb as you like, edit handler.rb and check how routing and transformer invoking like. 29 | 30 | ```json 31 | {"path"=> 32 | {"dir_base"=>"/Users/t9md/.vim/bundle/vim-transform/autoload/transform", 33 | "dir_transformer"=> 34 | "/Users/t9md/.vim/bundle/vim-transform/autoload/transform/transformer"}, 35 | "mode"=>"n", 36 | "content"=> 37 | {"line_e+1"=>"", 38 | "all"=>["env!!"], 39 | "len"=>1, 40 | "line_s"=>"env!!", 41 | "line_s-1"=>"", 42 | "line_e"=>"env!!", 43 | "update"=>0}, 44 | "buffer"=> 45 | {"line_e+1"=>2, 46 | "bufnr"=>18, 47 | "pos"=>[0, 1, 5, 0], 48 | "cWORD"=>"env!!", 49 | "filepath"=>"/Users/t9md/sandbox.json", 50 | "line_s"=>1, 51 | "line_s-1"=>0, 52 | "ext"=>"json", 53 | "dirname"=>".", 54 | "line_e"=>1, 55 | "cword"=>"!!", 56 | "filetype"=>"json", 57 | "filename"=>"sandbox.json"}} 58 | ``` 59 | -------------------------------------------------------------------------------- /misc/ruby_handler/config.vim: -------------------------------------------------------------------------------- 1 | let g:transform = {} 2 | let g:transform.options = {} 3 | let g:transform.options.enable_default_config = 0 4 | let g:transform.options.path = "$HOME/transformer" 5 | 6 | function! g:transform._(e) 7 | " To avoid mess in passing `env` as argment. 8 | " We serialize env to JSON and pass as first line of STDIN. 9 | " handler.rb should pop first line of STDIN and decode as JSON. 10 | " rest of STDIN is original STDIN in handler.rb 11 | let STDIN = [a:e.toJSON()] + a:e.content.all 12 | call a:e.content.update(STDIN) 13 | call a:e.run('ruby_handler/handler.rb') 14 | endfunction 15 | -------------------------------------------------------------------------------- /misc/ruby_handler/handler.rb: -------------------------------------------------------------------------------- 1 | # This is example, you need to change as you like. 2 | 3 | require 'json' 4 | require 'pp' 5 | 6 | require_relative './lib/transformer' 7 | require_relative './lib/transformer/base' 8 | require_relative './lib/transformer/go' 9 | 10 | # first line of STDIN is JSON string which inlucde `env` information. 11 | input = STDIN.read 12 | json, input = input.split("\n", 2) 13 | $env = JSON.parse(json) 14 | 15 | TF = Transformer 16 | FILE_NAME = $env['buffer']['filename'] 17 | FILE_TYPE = $env['buffer']['filetype'] 18 | 19 | TF.register do 20 | if FILE_TYPE == 'go' 21 | if $env['content']['line_s-1'] =~ /^import\s*\(/ 22 | get /./ do |req| 23 | puts TF::Go::Import.run(req) 24 | end 25 | end 26 | 27 | get /^const\s*\(.*\)$/m do |req| 28 | puts TF::Go::ConstStringfy.run(req) 29 | end 30 | end 31 | 32 | ## if filename is `sandbox.json` and line begin line is `env!!` only, dump $env to buffer 33 | if FILE_NAME == "sandbox.json" && $env['content']['len'] == 1 34 | get /^env!!$/ do 35 | pp $env 36 | end 37 | end 38 | 39 | if FILE_NAME == "tryit.md" 40 | # Insert content of URL 41 | get %r!^\s*https?://.*$! do |req, m| 42 | puts req 43 | system %!curl '#{req}'! 44 | end 45 | 46 | # Insert command output ( not necessarily transformed ) 47 | # ===================================== 48 | # Before: '|' indicate HOL 49 | # | $ ls 50 | # After: 51 | # | $ ls -l 52 | # | total 32 53 | # | -rw-r--r-- 1 t9md staff 9294 Feb 22 01:58 README.md 54 | # | drwxr-xr-x 4 t9md staff 136 Feb 20 16:31 autoload 55 | # | drwxr-xr-x 5 t9md staff 170 Feb 21 17:01 misc 56 | # | drwxr-xr-x 3 t9md staff 102 Feb 19 12:07 plugin 57 | # | -rw-r--r-- 1 t9md staff 2640 Feb 20 00:54 tags 58 | # 59 | get /^ \$(.*)$/ do |req, m| 60 | puts req 61 | puts `#{m[1]}`.split("\n").map { |e| " #{e}" }.join("\n") 62 | end 63 | 64 | # Send command to tmux's pane 65 | # ===================================== 66 | # send `hostname` command to tmux's pane specified in bracket. 67 | # 68 | # Before: 69 | # 70 | # |!!tmux[0] hostname 71 | # 72 | # Side Effect: 73 | # 74 | # send `hostname` command to tmux's pane 0 75 | # 76 | # After: nothing changed 77 | # 78 | # |!!tmux[0] hostname 79 | # 80 | get /!!tmux\[(\d+)\] (.*)$/ do |req, m| 81 | cmd = %!tmux send-keys -t#{m[1]} "#{m[2]}" Enter! 82 | system cmd 83 | end 84 | end 85 | 86 | # stringfy 87 | get /.*/ do |req| 88 | puts TF::Base::StringfyWord.run(req) 89 | end 90 | end 91 | 92 | TF.start input 93 | -------------------------------------------------------------------------------- /misc/ruby_handler/lib/transformer.rb: -------------------------------------------------------------------------------- 1 | # This is sample handler to serve all request 2 | # You can experiment, extend based on this sample. 3 | # For configuration of vim side, refer config.vim in same directory. 4 | module Transformer 5 | class << self 6 | def get(condition, &blk) 7 | handlers.push([condition, blk]) 8 | end 9 | 10 | def handlers 11 | @handlers ||= [] 12 | @handlers 13 | end 14 | 15 | def register(&blk) 16 | instance_eval(&blk) 17 | end 18 | 19 | def start(input) 20 | @handlers.each do |regexp, blk| 21 | m = regexp.match(input) 22 | if m 23 | blk.call(input, m) 24 | return 25 | end 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /misc/ruby_handler/lib/transformer/base.rb: -------------------------------------------------------------------------------- 1 | module Transformer 2 | module Base 3 | class SimpleCommand 4 | def initialize(command) 5 | @command = command 6 | end 7 | def run(input) 8 | IO.popen(@command, "r+") do |io| 9 | io.puts input 10 | io.close_write 11 | io.read 12 | end 13 | end 14 | end 15 | 16 | class StringfyWord 17 | def self.run(input) 18 | r = [] 19 | input.each_line do |l| 20 | r << l.chomp.split.map {|e| %!"#{e}"! }.join(", ") 21 | end 22 | r.join("\n") 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /misc/ruby_handler/lib/transformer/go.rb: -------------------------------------------------------------------------------- 1 | module Transformer 2 | module Go 3 | class Import 4 | def self.run(input) 5 | r = [] 6 | repos = { 7 | gh: "github.com", 8 | cg: "code.google.com", 9 | gl: "golang.org", 10 | } 11 | 12 | input.each_line do |l| 13 | l.strip.split.map do |e| 14 | repos.each do |k, v| 15 | k = k.to_s 16 | if e =~ /^#{k}\// 17 | e = e.sub k, v 18 | break 19 | end 20 | end 21 | r << e 22 | end 23 | end 24 | return r.map { |e| %!\t"#{e}"! }.join("\n").chomp 25 | end 26 | end 27 | 28 | class ConstStringfy 29 | class << self 30 | def parse(s) 31 | type = "" 32 | enums = [] 33 | s.split("\n").each do |e| 34 | next if e =~ /\(|\)/ 35 | n = e.split 36 | type << n[1] if n.size > 1 37 | enums << n.first 38 | end 39 | [type, enums] 40 | end 41 | 42 | def run(s) 43 | type, enums = *parse(s) 44 | v = type[0].downcase 45 | 46 | out = "" 47 | enums.each do |e| 48 | out << "\tcase #{e}:\n" 49 | out << "\t\treturn \"#{e}\"\n" 50 | end 51 | return <<-EOS 52 | #{s} 53 | 54 | func (#{v} #{type}) String() string { 55 | \tswitch #{v} { 56 | #{out.chomp} 57 | \tdefault: 58 | \t\treturn "Unknown" 59 | \t} 60 | } 61 | EOS 62 | end 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /plugin/transform.vim: -------------------------------------------------------------------------------- 1 | " GUARD: 2 | if expand("%:p") ==# expand(":p") 3 | unlet! g:loaded_transform 4 | endif 5 | if exists('g:loaded_transform') 6 | finish 7 | endif 8 | let g:loaded_transform = 1 9 | let s:old_cpo = &cpo 10 | set cpo&vim 11 | 12 | " Main: 13 | 14 | " AutoCmd: 15 | 16 | " Command: 17 | command! -range -bar -nargs=* Transform call transform#start(, , ) 18 | 19 | " KeyMap: 20 | nnoremap (transform) :Transform 21 | xnoremap (transform) :Transform 22 | inoremap (transform) :Transform 23 | 24 | " Finish: 25 | let &cpo = s:old_cpo 26 | " vim: foldmethod=marker 27 | -------------------------------------------------------------------------------- /test/all.vim: -------------------------------------------------------------------------------- 1 | function! s:report(subject, result) "{{{1 2 | echo '" ' . a:subject 3 | echo '" --------------------------------------------------------------' 4 | echo PP(a:result) 5 | echo "" 6 | endfunction 7 | 8 | let s:T = {} 9 | 10 | function! s:T.env() "{{{1 11 | return transform#environment#new(1, 5, 'n') 12 | endfunction 13 | 14 | function! s:T.config() "{{{1 15 | unlet! g:transform 16 | call s:report('[config] g:transform not exist', transform#config()) 17 | 18 | unlet! g:transform 19 | let g:transform = {} 20 | let g:transform.options = {} 21 | let g:transform.options.enable_default_config = 0 22 | call s:report('[config] dsiable default route', transform#config()) 23 | 24 | unlet! g:transform 25 | let g:transform = {} 26 | function g:transform.rb() 27 | endfunction 28 | call s:report('g:transform have not options field', transform#config()) 29 | 30 | unlet! g:transform 31 | let g:transform = {} 32 | let g:transform.options = 1 33 | call s:report('g:transform.options is not dictionary', transform#config()) 34 | endfunction 35 | 36 | function! s:T._app() "{{{1 37 | return transform#_app() 38 | endfunction 39 | 40 | function! s:T._run() "{{{1 41 | for test in [ "config", "env", "_app"] 42 | unlet! R 43 | let R = call(self[test], [], self) 44 | call s:report(test, R) 45 | endfor 46 | 47 | endfunction 48 | 49 | call s:T._run() 50 | --------------------------------------------------------------------------------