├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question.md ├── PULL_REQUEST_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE │ ├── bug_fix.md │ ├── issue_resolving.md │ ├── new_feature.md │ └── trivial_fix.md └── workflows │ └── test.yml ├── LICENSE.txt ├── README.md ├── autoload ├── quickrun.vim ├── quickrun │ ├── command.vim │ ├── config.vim │ ├── hook │ │ ├── cd.vim │ │ ├── eval.vim │ │ ├── output_encode.vim │ │ ├── shebang.vim │ │ ├── sweep.vim │ │ └── time.vim │ ├── module.vim │ ├── outputter │ │ ├── browser.vim │ │ ├── buffer.vim │ │ ├── buffer_legacy.vim │ │ ├── buffered.vim │ │ ├── error.vim │ │ ├── file.vim │ │ ├── loclist.vim │ │ ├── message.vim │ │ ├── multi.vim │ │ ├── null.vim │ │ ├── popup.vim │ │ ├── quickfix.vim │ │ └── variable.vim │ ├── runner │ │ ├── concurrent_process.vim │ │ ├── job.vim │ │ ├── python.vim │ │ ├── remote.vim │ │ ├── shell.vim │ │ ├── system.vim │ │ ├── terminal.vim │ │ ├── vimproc.vim │ │ └── vimscript.vim │ └── session.vim └── vital │ ├── _quickrun.vim │ ├── _quickrun │ ├── ConcurrentProcess.vim │ ├── Data │ │ ├── Dict.vim │ │ ├── List.vim │ │ └── String.vim │ ├── Prelude.vim │ ├── Process.vim │ ├── System │ │ ├── File.vim │ │ └── Filepath.vim │ └── Vim │ │ ├── Buffer.vim │ │ ├── Guard.vim │ │ ├── Message.vim │ │ ├── Type.vim │ │ └── ViewTracer.vim │ ├── quickrun.vim │ └── quickrun.vital ├── doc ├── quickrun.jax └── quickrun.txt ├── plugin └── quickrun.vim └── test ├── .themisrc └── outputter └── buffer.vimspec /.gitattributes: -------------------------------------------------------------------------------- 1 | autoload/vital/** binary linguist-vendored linguist-generated=true 2 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at @thinca. All complaints will be 59 | reviewed and investigated and will result in a response that is deemed 60 | necessary and appropriate to the circumstances. The project team is obligated 61 | to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for taking your time for this project! 4 | 5 | First, see [Code of Conduct](./CODE_OF_CONDUCT.md). 6 | 7 | 8 | ## Issues 9 | 10 | There are some templates. 11 | Please choose a template and fill them. 12 | 13 | When you cannot find an appropriate template, you do not have to use it. 14 | 15 | 16 | ### About `question` 17 | 18 | Please check [past `question` issues](../../../issues?q=label%3Aquestion) before you ask. 19 | 20 | 21 | ## Pull Requests 22 | 23 | There are some templates. 24 | Please choose a template and fill them. 25 | 26 | Please consider opening a issue before opening a PR when your proposal is big. 27 | 28 | 29 | ### About Git Repository 30 | 31 | #### Branches 32 | 33 | There are two important branches. 34 | 35 | - `develop` branch 36 | - A branch for development. 37 | - This branch may not exist. 38 | - Please send a Pull Request to this if this exists. 39 | - Exclude some of the urgent ones. 40 | - `master` branch 41 | - A branch for release. 42 | 43 | 44 | #### Commits 45 | 46 | Keep clean. When you pushed dirty commits, please organize before merge. 47 | 48 | 49 | #### Force Push 50 | 51 | You can use force pushing freely. GitHub shows a link to see the difference between before force pushing and after force pushing. 52 | 53 | 54 | ## About Neovim 55 | 56 | My plugin does not support Neovim. And, there are no plans to include codes for supporting Neovim. 57 | Because I do not use Neovim, so I cannot keep the codes working on Neovim. 58 | Sorry. 59 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: 'bug' 6 | assignees: '' 7 | --- 8 | 9 | 10 | 11 | ### Describe the bug 12 | 13 | 14 | ### Provide a minimal vimrc with less than 50 lines to reproduce 15 | 16 | 17 | ```vim 18 | ``` 19 | 20 | ### How to reproduce the problem from Vim startup 21 | 22 | 23 | ### Expected behavior 24 | 25 | 26 | ### Actual behavior 27 | 28 | 29 | ### Screenshots (If possible) 30 | 31 | 32 | ### Your environment 33 | 34 | - OS: 35 | - Vim's version: 36 | - Plugin's version: 37 | 38 | ### Additional context (If any) 39 | 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: 'enhancement' 6 | assignees: '' 7 | --- 8 | 9 | 10 | 11 | ### Is your feature request related to a problem? 12 | 13 | 14 | ### Describe the solution you'd like 15 | 16 | 17 | ### Additional context 18 | 19 | 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Any questions about usage, features, or others 4 | title: '' 5 | labels: 'question' 6 | assignees: '' 7 | --- 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | You can choose Pull Request template. (Select `Preview` tab first) 2 | 3 | - [Issue resolving](?template=issue_resolving.md&expand=1) 4 | - Resolve an existing issue 5 | - [Bug fix](?template=bug_fix.md&labels=bug&expand=1) 6 | - Fix a bug 7 | - [Trivial fix](?template=trivial_fix.md&labels=trivial&expand=1) 8 | - A small improvement fixing a typo, coding style, default configuration, and so on 9 | - [New feature](?template=new_feature.md&labels=enhancement&expand=1) 10 | - Propose a new feature to improve the project 11 | 12 | Or, remove all text and describe about your Pull Request! 13 | 14 | (Note that DO NOT USE this(you look) template. This is a navigation page.) 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/bug_fix.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | ### Describe the bug 8 | 9 | 10 | ### Provide a minimal vimrc with less than 50 lines to reproduce 11 | 12 | 13 | ```vim 14 | ``` 15 | 16 | ### How to reproduce the problem from Vim startup 17 | 18 | 19 | ### Expected behavior 20 | 21 | 22 | ### Actual behavior 23 | 24 | 25 | ### Screenshots (If possible) 26 | 27 | 28 | ### Your environment 29 | 30 | - OS: 31 | - Vim's version: 32 | - Plugin's version: 33 | 34 | ### Additional context (If any) 35 | 36 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/issue_resolving.md: -------------------------------------------------------------------------------- 1 | 2 | This pull request closes #xxx 3 | 4 | ### How to resolve 5 | 6 | 7 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/new_feature.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Is your suggesting new feature related to a problem? 4 | 5 | 6 | ### Describe the solution you'd like 7 | 8 | 9 | ### Additional context 10 | 11 | 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/trivial_fix.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: 'Test' 2 | on: ['push', 'pull_request'] 3 | 4 | jobs: 5 | test: 6 | name: 'Vim ${{ matrix.vim_version }} on ${{ matrix.platform }}' 7 | strategy: 8 | matrix: 9 | vim_version: 10 | - 'head' 11 | - 'v9.1.0000' 12 | - 'v9.0.0000' 13 | - 'v8.2.0000' 14 | - 'v8.1.0001' 15 | platform: ['Linux', 'MacOS', 'Windows'] 16 | 17 | include: 18 | - platform: 'Linux' 19 | os: 'ubuntu-latest' 20 | download: 'never' 21 | - platform: 'MacOS' 22 | os: 'macos-latest' 23 | download: 'never' 24 | - platform: 'Windows' 25 | os: 'windows-latest' 26 | download: 'always' 27 | 28 | fail-fast: false 29 | 30 | runs-on: '${{ matrix.os }}' 31 | timeout-minutes: 10 32 | 33 | steps: 34 | - uses: 'actions/checkout@v4' 35 | - name: 'Setup Vim' 36 | id: 'vim' 37 | uses: 'thinca/action-setup-vim@v1' 38 | with: 39 | vim_version: '${{ matrix.vim_version }}' 40 | download: '${{ matrix.download }}' 41 | - name: 'Show Vim version' 42 | run: | 43 | ${{ steps.vim.outputs.executable }} --version 44 | - name: 'Setup themis.vim' 45 | uses: 'actions/checkout@v4' 46 | with: 47 | repository: 'thinca/vim-themis' 48 | ref: 'v1.7.0' 49 | path: 'themis' 50 | - name: 'Run test' 51 | env: 52 | THEMIS_VIM: '${{ steps.vim.outputs.executable }}' 53 | run: | 54 | ./themis/bin/themis --reporter dot 55 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | zlib License 2 | 3 | (C) 2009 thinca 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any damages 7 | arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software 15 | in a product, an acknowledgment in the product documentation would be 16 | appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 3. This notice may not be removed or altered from any source distribution. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # quickrun.vim 2 | 3 | quickrun.vim is a Vim plugin to run commands and show its result quickly. 4 | See [help document](doc/quickrun.txt) for detail. 5 | 6 | 7 | ## License 8 | 9 | [zlib License](LICENSE.txt) 10 | -------------------------------------------------------------------------------- /autoload/quickrun/command.vim: -------------------------------------------------------------------------------- 1 | let s:V = g:quickrun#V 2 | 3 | 4 | function quickrun#command#execute(argline, use_range, line1, line2) abort 5 | try 6 | let config = quickrun#command#parse(a:argline) 7 | if a:use_range 8 | let config.region = { 9 | \ 'first': [a:line1, 0, 0], 10 | \ 'last': [a:line2, 0, 0], 11 | \ 'wise': 'V', 12 | \ } 13 | endif 14 | call quickrun#run(config) 15 | catch /^quickrun:/ 16 | call s:V.Vim.Message.error(v:exception) 17 | endtry 18 | endfunction 19 | 20 | function quickrun#command#complete(lead, cmd, pos) abort 21 | let line = split(a:cmd[: a:pos - 1], '', 1) 22 | let head = line[-1] 23 | let kinds = quickrun#module#get_kinds() 24 | if 2 <= len(line) && line[-2] =~# '^-' 25 | " a value of option. 26 | let opt = line[-2][1 :] 27 | if opt !=# 'type' 28 | let list = [] 29 | if opt ==# 'mode' 30 | let list = ['n', 'v'] 31 | elseif 0 <= index(kinds, opt) 32 | let list = map(filter(quickrun#module#get(opt), 33 | \ 'v:val.available()'), 'v:val.name') 34 | endif 35 | return filter(list, 'v:val =~# "^" . a:lead') 36 | endif 37 | 38 | elseif head =~# '^-' 39 | " a name of option. 40 | let list = ['type', 'src', 'srcfile', 'input', 'runner', 'outputter', 41 | \ 'command', 'exec', 'cmdopt', 'args', 'tempfile', 'mode'] 42 | let mod_options = {} 43 | for kind in kinds 44 | for module in filter(quickrun#module#get(kind), 'v:val.available()') 45 | for opt in keys(module.config) 46 | let mod_options[opt] = 1 47 | let mod_options[kind . '/' . opt] = 1 48 | let mod_options[module.name . '/' . opt] = 1 49 | let mod_options[kind . '/' . module.name . '/' . opt] = 1 50 | endfor 51 | endfor 52 | endfor 53 | let list += keys(mod_options) 54 | call map(list, '"-" . v:val') 55 | 56 | endif 57 | if !exists('list') 58 | " no context: types 59 | let list = keys(extend(exists('g:quickrun_config') ? 60 | \ copy(g:quickrun_config) : {}, g:quickrun#default_config)) 61 | call filter(list, 'v:val !~# "^[_*]$"') 62 | endif 63 | 64 | let re = '^\V' . escape(head, '\') . '\v[^/]*/?' 65 | return uniq(sort(map(list, 'matchstr(v:val, re)'))) 66 | endfunction 67 | 68 | function quickrun#command#parse(argline) abort 69 | return s:from_arglist(s:parse_argline(a:argline)) 70 | endfunction 71 | 72 | " foo 'bar buz' "hoge \"huga" 73 | " => ['foo', 'bar buz', 'hoge "huga'] 74 | " TODO: More improve. 75 | " ex: 76 | " foo ba'r b'uz "hoge \nhuga" 77 | " => ['foo, 'bar buz', "hoge \nhuga"] 78 | function s:parse_argline(argline) abort 79 | let argline = a:argline 80 | let arglist = [] 81 | while argline !~# '^\s*$' 82 | let argline = matchstr(argline, '^\s*\zs.*$') 83 | if argline[0] =~# '[''"]' 84 | let arg = matchstr(argline, '\v([''"])\zs.{-}\ze\\@' 117 | if arg[1] ==# '>' 118 | let config.append = 1 119 | let arg = arg[1 :] 120 | endif 121 | let config.outputter = arg[1 :] 122 | elseif arg[0] ==# '<' 123 | let config.input = arg[1 :] 124 | else 125 | let config.type = arg 126 | endif 127 | endfor 128 | return config 129 | endfunction 130 | -------------------------------------------------------------------------------- /autoload/quickrun/config.vim: -------------------------------------------------------------------------------- 1 | " Converts a string as argline or a list of config to config object. 2 | function quickrun#config#normalize(config) abort 3 | let t = type(a:config) 4 | if t is# v:t_string 5 | return quickrun#command#parse(a:config) 6 | elseif t is# v:t_list 7 | let config = {} 8 | for c in a:config 9 | call extend(config, quickrun#config#normalize(c)) 10 | endfor 11 | return config 12 | elseif t is# v:t_dict 13 | return deepcopy(a:config) 14 | endif 15 | throw 'quickrun: Unsupported config type: ' . type(a:config) 16 | endfunction 17 | 18 | function quickrun#config#apply_recent_region(config) abort 19 | if !has_key(a:config, 'mode') 20 | let a:config.mode = histget(':') =~# "^'<,'>\\s*Q\\%[uickRun]" ? 'v' : 'n' 21 | endif 22 | if a:config.mode ==# 'v' 23 | let a:config.region = { 24 | \ 'first': getpos("'<")[1 :], 25 | \ 'last': getpos("'>")[1 :], 26 | \ 'wise': visualmode(), 27 | \ } 28 | endif 29 | endfunction 30 | 31 | function quickrun#config#build(type, ...) abort 32 | let config = a:0 ? a:1 : {} 33 | let type = {'type': a:type} 34 | for c in [ 35 | \ 'b:quickrun_config', 36 | \ 'type', 37 | \ 'g:quickrun_config[config.type]', 38 | \ 'g:quickrun#default_config[config.type]', 39 | \ 'g:quickrun_config["_"]', 40 | \ 'g:quickrun_config["*"]', 41 | \ 'g:quickrun#default_config["_"]', 42 | \ ] 43 | if exists(c) 44 | let new_config = eval(c) 45 | if 0 <= stridx(c, 'config.type') 46 | let config_type = '' 47 | while has_key(config, 'type') 48 | \ && has_key(new_config, 'type') 49 | \ && config.type !=# '' 50 | \ && config.type !=# config_type 51 | let config_type = config.type 52 | call extend(config, new_config, 'keep') 53 | let config.type = new_config.type 54 | let new_config = exists(c) ? eval(c) : {} 55 | endwhile 56 | endif 57 | call extend(config, new_config, 'keep') 58 | endif 59 | endfor 60 | return config 61 | endfunction 62 | -------------------------------------------------------------------------------- /autoload/quickrun/hook/cd.vim: -------------------------------------------------------------------------------- 1 | " quickrun: hook/cd: Changes current directory. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:hook = { 6 | \ 'config': { 7 | \ 'directory': '', 8 | \ }, 9 | \ } 10 | 11 | function s:hook.init(session) abort 12 | let self._cd = '' 13 | if self.config.directory ==# '' 14 | let self.config.enable = 0 15 | endif 16 | endfunction 17 | 18 | function s:hook.on_ready(session, context) abort 19 | let self._cd = getcwd() 20 | let self._localdir = haslocaldir() 21 | let directory = a:session.build_command(self.config.directory) 22 | if self._localdir 23 | let self._id = {} 24 | let w:quickrun_hook_cd = self._id 25 | lcd `=directory` 26 | else 27 | cd `=directory` 28 | endif 29 | if self._cd ==# getcwd() 30 | " CD wasn't changed. 31 | let self._cd = '' 32 | endif 33 | endfunction 34 | 35 | function s:hook.sweep() abort 36 | if self._cd ==# '' 37 | return 38 | endif 39 | if self._localdir 40 | if exists('w:quickrun_hook_cd') && w:quickrun_hook_cd is self._id 41 | unlet w:quickrun_hook_cd 42 | lcd `=self._cd` 43 | else 44 | for tabnr in range(1, tabpagenr('$')) 45 | for winnr in range(1, tabpagewinnr(tabnr, '$')) 46 | let w = gettabwinvar(tabnr, winnr, '') 47 | if get(w, 'quickrun_hook_cd') is self._id 48 | let [curtab, curwin] = [tabpagenr(), winnr()] 49 | let lz = &lazyredraw 50 | set lazyredraw 51 | try 52 | call s:move_tabwin(tabnr, winnr) 53 | unlet w:quickrun_hook_cd 54 | lcd `=self._cd` 55 | call s:move_tabwin(curtab, curwin) 56 | finally 57 | let &lazyredraw = lz 58 | endtry 59 | return 60 | endif 61 | endfor 62 | endfor 63 | endif 64 | " Couldn't restore... notify? 65 | else 66 | cd `=self._cd` 67 | endif 68 | endfunction 69 | 70 | function s:move_tabwin(tab, win) abort 71 | execute 'tabnext' a:tab 72 | execute a:win 'wincmd w' 73 | endfunction 74 | 75 | function quickrun#hook#cd#new() abort 76 | return deepcopy(s:hook) 77 | endfunction 78 | -------------------------------------------------------------------------------- /autoload/quickrun/hook/eval.vim: -------------------------------------------------------------------------------- 1 | " quickrun: hook/eval: Converts to evaluable code. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:hook = { 6 | \ 'config': { 7 | \ 'enable': 0, 8 | \ 'template': '', 9 | \ } 10 | \ } 11 | 12 | function s:hook.init(session) abort 13 | if self.config.template !~# '%s' 14 | let self.config.enable = 0 15 | endif 16 | endfunction 17 | 18 | function s:hook.on_module_loaded(session, context) abort 19 | let src = join(readfile(a:session.config.srcfile, 'b'), "\n") 20 | let new_src = printf(self.config.template, src) 21 | let srcfile = a:session.tempname(quickrun#expand(a:session.config.tempfile)) 22 | if writefile(split(new_src, "\n", 1), srcfile, 'b') == 0 23 | let a:session.config.srcfile = srcfile 24 | endif 25 | endfunction 26 | 27 | function quickrun#hook#eval#new() abort 28 | return deepcopy(s:hook) 29 | endfunction 30 | -------------------------------------------------------------------------------- /autoload/quickrun/hook/output_encode.vim: -------------------------------------------------------------------------------- 1 | " quickrun: hook/output_encode: Converts the encoding of output. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:hook = { 6 | \ 'config': { 7 | \ 'encoding': '&fileencoding', 8 | \ 'fileformat': '', 9 | \ }, 10 | \ '_fileformats': {'unix': "\n", 'dos': "\r\n", 'mac': "\r"}, 11 | \ } 12 | 13 | let s:M = g:quickrun#V.import('Vim.Message') 14 | 15 | function s:hook.init(session) abort 16 | let enc = split(self.config.encoding, '[^[:alnum:]-_]') 17 | if len(enc) is 1 18 | let enc += [&encoding] 19 | endif 20 | if len(enc) is 2 && enc[0] !=# '' && enc[1] !=# '' && enc[0] !=# enc[1] 21 | let [self._from, self._to] = enc 22 | else 23 | let [self._from, self._to] = ['', ''] 24 | endif 25 | let self._eol = get(self._fileformats, self.config.fileformat, '') 26 | if self.config.fileformat !=# '' && self._eol ==# '' 27 | call s:M.warn('Invalid type in `hook/output_encode/fileformat`.') 28 | endif 29 | if self._from ==# '' && self._eol ==# '' 30 | let self.config.enable = 0 31 | endif 32 | endfunction 33 | 34 | function s:hook.on_output(session, context) abort 35 | let data = a:context.data 36 | if self._from !=# '' 37 | let data = iconv(data, self._from, self._to) 38 | endif 39 | if self._eol !=# '' 40 | let data = substitute(data, '\r\n\?\|\n', self._eol, 'g') 41 | endif 42 | let a:context.data = data 43 | endfunction 44 | 45 | function quickrun#hook#output_encode#new() abort 46 | return deepcopy(s:hook) 47 | endfunction 48 | -------------------------------------------------------------------------------- /autoload/quickrun/hook/shebang.vim: -------------------------------------------------------------------------------- 1 | " quickrun: hook/shebang: Detects shebang. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:Filepath = g:quickrun#V.import('System.Filepath') 6 | 7 | let s:hook = {} 8 | 9 | function s:hook.on_module_loaded(session, context) abort 10 | let line = get(readfile(a:session.config.srcfile, 0, 1), 0, '') 11 | if line =~# '^#!' 12 | let a:session.config.command = s:Filepath.realpath(line[2 :]) 13 | call map(a:session.config.exec, 's:replace_cmd(v:val)') 14 | endif 15 | endfunction 16 | 17 | function s:replace_cmd(cmd) abort 18 | return substitute(a:cmd, '%\@ 3 | " License: zlib License 4 | 5 | let s:hook = { 6 | \ 'config': { 7 | \ 'files': [], 8 | \ } 9 | \ } 10 | 11 | function s:hook.init(session) abort 12 | if empty(self.config.files) 13 | let self.config.enable = 0 14 | endif 15 | endfunction 16 | 17 | function s:hook.on_ready(session, context) abort 18 | for file in self.config.files 19 | call a:session.tempname(a:session.build_command(file)) 20 | endfor 21 | endfunction 22 | 23 | function quickrun#hook#sweep#new() abort 24 | return deepcopy(s:hook) 25 | endfunction 26 | -------------------------------------------------------------------------------- /autoload/quickrun/hook/time.vim: -------------------------------------------------------------------------------- 1 | " quickrun: hook/time: Measures execution time. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:hook = { 6 | \ 'config': { 7 | \ 'enable': 0, 8 | \ 'format': "\n*** time: %g ***", 9 | \ 'dest': '', 10 | \ }, 11 | \ } 12 | 13 | function s:hook.init(session) abort 14 | if self.config.enable && !empty(self.config.dest) 15 | let self._outputter = a:session.make_module('outputter', self.config.dest) 16 | endif 17 | endfunction 18 | 19 | function s:hook.on_ready(session, context) abort 20 | let self._start = reltime() 21 | endfunction 22 | 23 | function s:hook.on_finish(session, context) abort 24 | let self._end = reltime() 25 | let time = str2float(reltimestr(reltime(self._start, self._end))) 26 | let text = printf(self.config.format, time) 27 | if has_key(self, '_outputter') 28 | call self._outputter.start(a:session) 29 | call self._outputter.output(text, a:session) 30 | call self._outputter.finish(a:session) 31 | else 32 | call a:session.output(text) 33 | endif 34 | endfunction 35 | 36 | function quickrun#hook#time#new() abort 37 | return deepcopy(s:hook) 38 | endfunction 39 | -------------------------------------------------------------------------------- /autoload/quickrun/module.vim: -------------------------------------------------------------------------------- 1 | " Module system for quickrun.vim. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | " Templates. {{{1 6 | let s:templates = {} 7 | " Template of module. {{{2 8 | let s:module = {'config': {}, 'config_order': []} 9 | function s:module.available() abort 10 | try 11 | call self.validate() 12 | catch 13 | return 0 14 | endtry 15 | return 1 16 | endfunction 17 | function s:module.validate() abort 18 | endfunction 19 | function s:module.init(session) abort 20 | endfunction 21 | function s:module.sweep() abort 22 | endfunction 23 | 24 | " Template of runner. {{{2 25 | let s:templates.runner = deepcopy(s:module) 26 | function s:templates.runner.run(commands, input, session) abort 27 | throw 'quickrun: A runner must implement run()' 28 | endfunction 29 | function s:templates.runner.shellescape(str) abort 30 | return shellescape(a:str) 31 | endfunction 32 | 33 | " Template of outputter. {{{2 34 | let s:templates.outputter = deepcopy(s:module) 35 | function s:templates.outputter.start(session) abort 36 | endfunction 37 | function s:templates.outputter.output(data, session) abort 38 | throw 'quickrun: An outputter must implement output()' 39 | endfunction 40 | function s:templates.outputter.finish(session) abort 41 | endfunction 42 | 43 | " Template of hook. {{{2 44 | let s:templates.hook = deepcopy(s:module) 45 | function s:templates.hook.priority(point) abort 46 | return 0 47 | endfunction 48 | let s:templates.hook.config.enable = 1 49 | 50 | let s:modules = map(copy(s:templates), '{}') 51 | 52 | 53 | " functions. {{{1 54 | function quickrun#module#build(kind, module) abort 55 | return s:deepextend(deepcopy(s:templates[a:kind]), a:module) 56 | endfunction 57 | 58 | function quickrun#module#register(module, ...) abort 59 | call s:validate_module(a:module) 60 | let overwrite = a:0 && a:1 61 | let kind = a:module.kind 62 | let name = a:module.name 63 | if !has_key(s:modules, kind) 64 | let s:modules[kind] = {} 65 | endif 66 | if overwrite || !quickrun#module#exists(kind, name) 67 | let s:modules[kind][name] = quickrun#module#build(kind, a:module) 68 | endif 69 | endfunction 70 | 71 | function quickrun#module#unregister(...) abort 72 | if a:0 && type(a:1) == v:t_dict 73 | let kind = get(a:1, 'kind', '') 74 | let name = get(a:1, 'name', '') 75 | elseif 2 <= a:0 76 | let kind = a:1 77 | let name = a:2 78 | else 79 | return 0 80 | endif 81 | 82 | if quickrun#module#exists(kind, name) 83 | call remove(s:modules[kind], name) 84 | return 1 85 | endif 86 | return 0 87 | endfunction 88 | 89 | function quickrun#module#exists(kind, name) abort 90 | return has_key(s:modules, a:kind) && has_key(s:modules[a:kind], a:name) 91 | endfunction 92 | 93 | function quickrun#module#get(kind, ...) abort 94 | if !has_key(s:modules, a:kind) 95 | throw 'quickrun: Unknown kind of module: ' . a:kind 96 | endif 97 | if a:0 == 0 98 | return values(s:modules[a:kind]) 99 | endif 100 | let name = a:1 101 | if !has_key(s:modules[a:kind], name) 102 | throw 'quickrun: Unregistered module: ' . a:kind . '/' . name 103 | endif 104 | return s:modules[a:kind][name] 105 | endfunction 106 | 107 | function quickrun#module#get_kinds() abort 108 | return keys(s:modules) 109 | endfunction 110 | 111 | function quickrun#module#load(...) abort 112 | let overwrite = a:0 && a:1 113 | for kind in keys(s:templates) 114 | let pat = 'autoload/quickrun/' . kind . '/*.vim' 115 | for name in map(split(globpath(&runtimepath, pat), "\n"), 116 | \ 'fnamemodify(v:val, ":t:r")') 117 | try 118 | let module = quickrun#{kind}#{name}#new() 119 | let module.kind = kind 120 | let module.name = name 121 | call quickrun#module#register(module, overwrite) 122 | catch /:E\%(117\|716\):/ 123 | endtry 124 | endfor 125 | endfor 126 | endfunction 127 | 128 | function s:validate_module(module) abort 129 | if !has_key(a:module, 'kind') 130 | throw 'quickrun: A module must have a "kind" attribute.' 131 | endif 132 | if !has_key(a:module, 'name') 133 | throw 'quickrun: A module must have a "name" attribute.' 134 | endif 135 | endfunction 136 | 137 | function s:deepextend(a, b) abort 138 | let type_a = type(a:a) 139 | if type_a != type(a:b) 140 | throw 'deepextend: Different type: ' . string(a:a) . ' : ' . string(a:b) 141 | endif 142 | if type_a == v:t_list 143 | call extend(a:a, a:b) 144 | elseif type_a == v:t_dict 145 | for [k, V] in items(a:b) 146 | let copied = 0 147 | if has_key(a:a, k) 148 | let type_k = type(a:a[k]) 149 | if type_k == type(V) && 150 | \ (type_k == v:t_list || type_k == v:t_dict) 151 | call s:deepextend(a:a[k], V) 152 | let copied = 1 153 | endif 154 | endif 155 | if !copied 156 | let a:a[k] = deepcopy(V) 157 | endif 158 | unlet V 159 | endfor 160 | else 161 | throw 'deepextend: Unsupported type: ' . string(a:a) 162 | endif 163 | return a:a 164 | endfunction 165 | -------------------------------------------------------------------------------- /autoload/quickrun/outputter/browser.vim: -------------------------------------------------------------------------------- 1 | " quickrun: outputter/browser: Outputs to a web browser. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:default_name = tempname() . '.html' 6 | 7 | let s:outputter = quickrun#outputter#file#new() 8 | 9 | let s:outputter.init_file = s:outputter.init 10 | 11 | function s:outputter.validate() abort 12 | call openbrowser#load() 13 | if !exists('*openbrowser#open') 14 | throw 'Needs open-browser.vim.' 15 | endif 16 | endfunction 17 | 18 | function! s:outputter.init(session) abort 19 | if self.config.name ==# '' 20 | let self.config.name = s:default_name 21 | endif 22 | call self.init_file(a:session) 23 | endfunction 24 | 25 | function! s:outputter.finish(session) abort 26 | let saved = g:openbrowser_open_filepath_in_vim 27 | try 28 | let g:openbrowser_open_filepath_in_vim = 0 29 | call openbrowser#open(self._file) 30 | finally 31 | let g:openbrowser_open_filepath_in_vim = saved 32 | endtry 33 | endfunction 34 | 35 | 36 | function quickrun#outputter#browser#new() abort 37 | return deepcopy(s:outputter) 38 | endfunction 39 | -------------------------------------------------------------------------------- /autoload/quickrun/outputter/buffer.vim: -------------------------------------------------------------------------------- 1 | " quickrun: outputter/buffer: Outputs to a vim buffer. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | 6 | let s:outputter = { 7 | \ 'config': { 8 | \ 'bufname': 'quickrun://output', 9 | \ 'filetype': 'quickrun', 10 | \ 'append': 0, 11 | \ 'opener': '%{winwidth(0) * 2 < winheight(0) * 5 ? "split" : "vsplit"}', 12 | \ 'into': 0, 13 | \ 'running_mark': 'running...', 14 | \ 'close_on_empty': 0, 15 | \ } 16 | \ } 17 | 18 | function s:outputter.init(session) abort 19 | let self._bufnr = 0 20 | let self._baseline = 0 21 | endfunction 22 | 23 | function s:outputter.start(session) abort 24 | let init = !self.config.append 25 | let self._bufnr = s:try_open_result_window(self, a:session, init, 0) 26 | if self._bufnr 27 | call s:set_running_mark(self._bufnr, self.config.running_mark) 28 | else 29 | let self._buffered = '' 30 | endif 31 | endfunction 32 | 33 | function s:outputter.output(data, session) abort 34 | if self._bufnr 35 | call s:output(self._bufnr, a:data) 36 | else 37 | let init = !self.config.append 38 | let self._bufnr = s:try_open_result_window(self, a:session, init, 0) 39 | if self._bufnr 40 | call s:output(self._bufnr, self._buffered . a:data) 41 | unlet self._buffered 42 | else 43 | let self._buffered .= a:data 44 | endif 45 | endif 46 | if self._bufnr 47 | call s:set_running_mark(self._bufnr, self.config.running_mark) 48 | call s:execute_in_result_window(self._bufnr, 'noautocmd normal! G') 49 | endif 50 | endfunction 51 | 52 | function s:outputter.finish(session) abort 53 | let config = self.config 54 | let should_close = 0 55 | if self._bufnr 56 | call s:remove_running_mark(self._bufnr) 57 | let should_close = config.close_on_empty && s:is_empty_buffer(self._bufnr) 58 | endif 59 | let into = config.into || should_close 60 | 61 | let cur_winid = win_getid() 62 | let bufnr = s:try_open_result_window(self, a:session, 0, into) 63 | 64 | if !bufnr 65 | " FIXME: Eventually, the result buffer could not be opened. 66 | return 67 | endif 68 | 69 | if self._baseline 70 | let cmd = printf('normal! %dGzt', self._baseline) 71 | call s:execute_in_result_window(self._bufnr, cmd) 72 | endif 73 | 74 | if should_close 75 | close 76 | call win_gotoid(cur_winid) 77 | redraw 78 | echohl MoreMsg 79 | echomsg 'quickrun: outputter/buffer: Empty output.' 80 | echohl NONE 81 | endif 82 | endfunction 83 | 84 | function s:try_open_result_window(outputter, session, clear, into) abort 85 | let cur_winid = win_getid() 86 | let into = a:into 87 | try 88 | let bufnr = s:open_result_window(a:outputter, a:session) 89 | if a:clear 90 | call s:clear_buffer(bufnr) 91 | let a:outputter._baseline = 1 92 | elseif a:outputter._baseline is# 0 93 | let a:outputter._baseline = line('$') 94 | endif 95 | return bufnr 96 | catch /^Vim([^)]\+):E11:/ 97 | " When the user staying at cmdwin, cursor can not move to other window. 98 | " This is a common case and should be ignored. 99 | let into = 1 100 | return 0 101 | catch 102 | echohl ErrorMsg 103 | echomsg v:exception 104 | echomsg v:throwpoint 105 | echohl NONE 106 | return 0 107 | finally 108 | if !into 109 | call win_gotoid(cur_winid) 110 | endif 111 | endtry 112 | endfunction 113 | 114 | function s:open_result_window(outputter, session) abort 115 | let config = a:outputter.config 116 | let opened = 0 117 | if bufexists(config.bufname) 118 | let bufnr = s:bufnr_from_strict_bufname(config.bufname) 119 | let wins = win_findbuf(bufnr) 120 | let tabnr = tabpagenr() 121 | call filter(map(wins, 'win_id2tabwin(v:val)'), 'v:val[0] is# tabnr') 122 | if empty(wins) 123 | if config.opener =~# '\S' 124 | execute config.opener fnameescape(config.bufname) 125 | let opened = 1 126 | endif 127 | else 128 | execute wins[0][1] 'wincmd w' 129 | endif 130 | else 131 | if config.opener =~# '\S' 132 | execute config.opener fnameescape(config.bufname) 133 | let bufnr = bufnr('%') 134 | let opened = 1 135 | else 136 | let bufnr = bufnr(config.bufname, 1) 137 | call bufload(bufnr) 138 | endif 139 | call setbufvar(bufnr, '&bufhidden', 'hide') 140 | call setbufvar(bufnr, '&buftype', 'nofile') 141 | call setbufvar(bufnr, '&swapfile', 0) 142 | call setbufvar(bufnr, '&buflisted', 0) 143 | call setbufvar(bufnr, '&fileformat', 'unix') 144 | endif 145 | 146 | if opened 147 | call a:session.invoke_hook('outputter_buffer_opened') 148 | endif 149 | 150 | if getbufvar(bufnr, '&filetype') !=# config.filetype 151 | call setbufvar(bufnr, '&filetype', config.filetype) 152 | endif 153 | 154 | return bufnr 155 | endfunction 156 | 157 | function s:clear_buffer(bufnr) abort 158 | call deletebufline(a:bufnr, 1, '$') 159 | call setbufvar(a:bufnr, '&fileformat', 'unix') 160 | let ff_info = {'lf': 0, 'crlf': 0, 'cr': 0} 161 | call setbufvar(a:bufnr, 'quickrun_ff_info', ff_info) 162 | endfunction 163 | 164 | function s:bufnr_from_strict_bufname(bufname) abort 165 | let l = 0 <= stridx(a:bufname, '://') ? 0 : -len(a:bufname) 166 | for info in getbufinfo() 167 | if info.name[l :] is# a:bufname 168 | return info.bufnr 169 | endif 170 | endfor 171 | return -1 172 | endfunction 173 | 174 | function s:is_empty_buffer(bufnr) abort 175 | let lines = getbufline(a:bufnr, 1, 2) 176 | return len(lines) is# 1 && lines[0] is# '' 177 | endfunction 178 | 179 | function s:output(bufnr, data) abort 180 | call s:remove_running_mark(a:bufnr) 181 | let lines = s:adjust_fileformat(a:bufnr, a:data) 182 | let [lastline] = getbufline(a:bufnr, '$') 183 | let lines[0] = lastline . lines[0] 184 | call setbufline(a:bufnr, '$', lines) 185 | endfunction 186 | 187 | let s:breaks = { 188 | \ 'unix': "\n", 189 | \ 'dos': "\r\n", 190 | \ 'mac': "\r", 191 | \ } 192 | function s:adjust_fileformat(bufnr, data) abort 193 | let ff = getbufvar(a:bufnr, '&fileformat') 194 | let ff_info = getbufvar(a:bufnr, 'quickrun_ff_info') 195 | 196 | if a:data =~# "\r\n" 197 | let ff_info.crlf = 1 198 | endif 199 | if a:data =~# "[^\r]\n" 200 | let ff_info.lf = 1 201 | endif 202 | if a:data =~# "\r\\%([^\n]\\|$\\)" 203 | let ff_info.cr = 1 204 | endif 205 | 206 | if ff isnot# 'mac' && !ff_info.crlf && !ff_info.lf && ff_info.cr 207 | let new_ff = 'mac' 208 | elseif ff isnot# 'dos' && ff_info.crlf && !ff_info.lf && !ff_info.cr 209 | let new_ff = 'dos' 210 | elseif ff isnot# 'unix' && ff_info.lf 211 | let new_ff = 'unix' 212 | let lines = getbufline(a:bufnr, 1, '$') 213 | call deletebufline(a:bufnr, 1, '$') 214 | if ff is# 'mac' 215 | call setbufline(a:bufnr, 1, split(join(lines, "\r"), "\n", 1)) 216 | elseif ff is# 'dos' 217 | let lastline = remove(lines, -1) 218 | call map(lines, 'v:val . "\r"') 219 | call setbufline(a:bufnr, 1, lines + [lastline]) 220 | endif 221 | endif 222 | 223 | if exists('new_ff') 224 | let ff = new_ff 225 | call setbufvar(a:bufnr, '&fileformat', ff) 226 | endif 227 | 228 | return split(a:data, s:breaks[ff], 1) 229 | endfunction 230 | 231 | function s:set_running_mark(bufnr, mark) abort 232 | if a:mark is# '' || getbufvar(a:bufnr, 'quickrun_running_mark', 0) 233 | return 234 | endif 235 | call appendbufline(a:bufnr, '$', a:mark) 236 | call setbufvar(a:bufnr, 'quickrun_running_mark', 1) 237 | endfunction 238 | 239 | function s:remove_running_mark(bufnr) abort 240 | let vars = getbufvar(a:bufnr, '') 241 | if get(vars, 'quickrun_running_mark', 0) 242 | call deletebufline(a:bufnr, '$') 243 | call remove(vars, 'quickrun_running_mark') 244 | endif 245 | endfunction 246 | 247 | function s:execute_in_result_window(bufnr, cmd) abort 248 | for winid in win_findbuf(a:bufnr) 249 | call win_execute(winid, a:cmd) 250 | endfor 251 | endfunction 252 | 253 | 254 | function quickrun#outputter#buffer#new() abort 255 | return deepcopy(s:outputter) 256 | endfunction 257 | -------------------------------------------------------------------------------- /autoload/quickrun/outputter/buffer_legacy.vim: -------------------------------------------------------------------------------- 1 | " quickrun: outputter/buffer: Outputs to a vim buffer. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:VT = g:quickrun#V.import('Vim.ViewTracer') 6 | 7 | let s:outputter = { 8 | \ 'config': { 9 | \ 'name': '[quickrun output]', 10 | \ 'filetype': 'quickrun', 11 | \ 'append': 0, 12 | \ 'split': '%{winwidth(0) * 2 < winheight(0) * 5 ? "" : "vertical"}', 13 | \ 'into': 0, 14 | \ 'running_mark': ':-)', 15 | \ 'close_on_empty': 0, 16 | \ } 17 | \ } 18 | 19 | function s:outputter.init(session) abort 20 | let self._append = self.config.append 21 | let self._line = 0 22 | let self._crlf = 0 23 | let self._lf = 0 24 | endfunction 25 | 26 | function s:outputter.start(session) abort 27 | let prev_window = s:VT.trace_window() 28 | call s:open_result_window(self.config, a:session) 29 | if !self._append 30 | silent % delete _ 31 | setlocal fileformat=unix 32 | endif 33 | call s:set_running_mark(self.config.running_mark) 34 | call s:VT.jump(prev_window) 35 | endfunction 36 | 37 | function s:outputter.output(data, session) abort 38 | let prev_window = s:VT.trace_window() 39 | call s:open_result_window(self.config, a:session) 40 | 41 | let oneline = line('$') == 1 42 | let data = getline('$') . a:data 43 | silent $ delete _ 44 | 45 | if self._line == 0 46 | let self._line = line('$') + (oneline ? 0 : 1) 47 | if 2 <= self._line 48 | let self._crlf = &l:fileformat ==# 'dos' || search("\r$", 'wn') 49 | let self._lf = &l:fileformat ==# 'unix' && search("[^\r]$", 'wn') 50 | endif 51 | endif 52 | 53 | let self._crlf = self._crlf || a:data =~# "\r\n" 54 | let self._lf = self._lf || a:data =~# "[^\r]\n" 55 | 56 | call s:normalize_fileformat(self._crlf, self._lf) 57 | 58 | if &l:fileformat ==# 'dos' 59 | let data = substitute(data, "\r\n", "\n", 'g') 60 | endif 61 | 62 | if data =~# '\n$' 63 | " :put command do not insert the last line. 64 | let data .= "\n" 65 | endif 66 | silent $ put = data 67 | if oneline 68 | silent 1 delete _ 69 | endif 70 | 71 | call s:set_running_mark(self.config.running_mark) 72 | call s:VT.jump(prev_window) 73 | redraw 74 | endfunction 75 | 76 | function s:outputter.finish(session) abort 77 | let prev_window = s:VT.trace_window() 78 | call s:open_result_window(self.config, a:session) 79 | execute self._line 80 | silent normal! zt 81 | let closed = self.config.close_on_empty && s:is_empty_buffer() 82 | if closed 83 | close 84 | endif 85 | if closed || !self.config.into 86 | call s:VT.jump(prev_window) 87 | endif 88 | redraw 89 | if closed 90 | echohl MoreMsg 91 | echomsg 'quickrun: outputter/buffer: Empty output.' 92 | echohl NONE 93 | endif 94 | endfunction 95 | 96 | 97 | function s:open_result_window(config, session) abort 98 | let sp = a:config.split 99 | let sname = s:escape_file_pattern(a:config.name) 100 | let opened = 0 101 | if !bufexists(a:config.name) 102 | execute sp 'split' 103 | edit `=a:config.name` 104 | nnoremap q c 105 | setlocal bufhidden=hide buftype=nofile noswapfile nobuflisted 106 | setlocal fileformat=unix 107 | let opened = 1 108 | elseif bufwinnr(sname) != -1 109 | execute bufwinnr(sname) 'wincmd w' 110 | else 111 | execute sp 'split' 112 | execute 'buffer' bufnr(sname) 113 | let opened = 1 114 | endif 115 | if opened 116 | call a:session.invoke_hook('outputter_buffer_opened') 117 | endif 118 | if &l:filetype !=# a:config.filetype 119 | let &l:filetype = a:config.filetype 120 | endif 121 | if exists('b:quickrun_running_mark') 122 | silent undo 123 | unlet b:quickrun_running_mark 124 | endif 125 | endfunction 126 | 127 | function s:normalize_fileformat(crlf, lf) abort 128 | " XXX Do not care `fileformat=mac` 129 | if &l:fileformat ==# 'unix' && a:crlf && !a:lf 130 | setlocal fileformat=dos 131 | elseif &l:fileformat ==# 'dos' && a:lf 132 | setlocal fileformat=unix 133 | for lnum in range(1, line('$')) 134 | call setline(lnum, getline(lnum) . "\r") 135 | endfor 136 | endif 137 | endfunction 138 | 139 | function s:set_running_mark(mark) abort 140 | if a:mark !=# '' && !exists('b:quickrun_running_mark') 141 | let &undolevels = &undolevels " split the undo block 142 | silent $ put =a:mark 143 | let b:quickrun_running_mark = 1 144 | endif 145 | endfunction 146 | 147 | function s:is_empty_buffer() abort 148 | return line('$') == 1 && getline(1) =~# '^\s*$' 149 | endfunction 150 | 151 | function s:escape_file_pattern(pat) abort 152 | return join(map(split(a:pat, '\zs'), '"[" . v:val . "]"'), '') 153 | endfunction 154 | 155 | 156 | function quickrun#outputter#buffer_legacy#new() abort 157 | return deepcopy(s:outputter) 158 | endfunction 159 | -------------------------------------------------------------------------------- /autoload/quickrun/outputter/buffered.vim: -------------------------------------------------------------------------------- 1 | " quickrun: outputter/buffered: Meta outputter; Buffers the output. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:outputter = { 6 | \ 'config': { 7 | \ 'target': '', 8 | \ }, 9 | \ } 10 | 11 | function s:outputter.init(session) abort 12 | let self._result = '' 13 | endfunction 14 | 15 | function s:outputter.output(data, session) abort 16 | let self._result .= a:data 17 | endfunction 18 | 19 | function s:outputter.finish(session) abort 20 | let outputter = a:session.make_module('outputter', self.config.target) 21 | call outputter.start(a:session) 22 | call outputter.output(self._result, a:session) 23 | call outputter.finish(a:session) 24 | endfunction 25 | 26 | 27 | function quickrun#outputter#buffered#new() abort 28 | return deepcopy(s:outputter) 29 | endfunction 30 | -------------------------------------------------------------------------------- /autoload/quickrun/outputter/error.vim: -------------------------------------------------------------------------------- 1 | " quickrun: outputter/error: Meta outputter; Switches outputters by result. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:outputter = quickrun#outputter#buffered#new() 6 | let s:outputter.config = { 7 | \ 'success': 'null', 8 | \ 'error': 'null', 9 | \ } 10 | let s:outputter.config_order = ['success', 'error'] 11 | 12 | function! s:outputter.finish(session) abort 13 | let outputter = a:session.make_module('outputter', 14 | \ self.config[a:session.exit_code ? 'error' : 'success']) 15 | call outputter.start(a:session) 16 | call outputter.output(self._result, a:session) 17 | call outputter.finish(a:session) 18 | endfunction 19 | 20 | 21 | function quickrun#outputter#error#new() abort 22 | return deepcopy(s:outputter) 23 | endfunction 24 | -------------------------------------------------------------------------------- /autoload/quickrun/outputter/file.vim: -------------------------------------------------------------------------------- 1 | " quickrun: outputter/file: Outputs to a file. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:outputter = { 6 | \ 'config': { 7 | \ 'name': '', 8 | \ 'append': 0, 9 | \ }, 10 | \ 'config_order': ['name', 'append'], 11 | \ } 12 | 13 | function s:outputter.init(session) abort 14 | let file = self.config.name 15 | if file is# '' 16 | throw 'Specify the file.' 17 | endif 18 | if isdirectory(file) 19 | throw 'Target is a directory.' 20 | endif 21 | if !self.config.append && filereadable(file) 22 | call delete(file) 23 | endif 24 | let self._file = fnamemodify(file, ':p') 25 | let self._size = 0 26 | endfunction 27 | 28 | function s:outputter.output(data, session) abort 29 | execute 'redir >> ' . self._file 30 | silent! echon a:data 31 | redir END 32 | let self._size += len(a:data) 33 | endfunction 34 | 35 | function s:outputter.finish(session) abort 36 | echo printf('Output to "%s" (%d bytes)', self.config.name, self._size) 37 | endfunction 38 | 39 | 40 | function quickrun#outputter#file#new() abort 41 | return deepcopy(s:outputter) 42 | endfunction 43 | -------------------------------------------------------------------------------- /autoload/quickrun/outputter/loclist.vim: -------------------------------------------------------------------------------- 1 | " quickrun: outputter/loclist: Outputs to location list. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:outputter = quickrun#outputter#quickfix#new() 6 | let s:outputter.config.open_cmd = 'lopen' 7 | 8 | function! s:outputter._apply_result(expr) abort 9 | lgetexpr a:expr 10 | return getloclist(0) 11 | endfunction 12 | 13 | function! s:outputter._apply_result_list(result_list) abort 14 | call setloclist(0, a:result_list) 15 | endfunction 16 | 17 | function! s:outputter._close_window() abort 18 | lclose 19 | endfunction 20 | 21 | 22 | function quickrun#outputter#loclist#new() abort 23 | return deepcopy(s:outputter) 24 | endfunction 25 | -------------------------------------------------------------------------------- /autoload/quickrun/outputter/message.vim: -------------------------------------------------------------------------------- 1 | " quickrun: outputter/message: Outputs to messages area. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:outputter = { 6 | \ 'config': {'log': 0}, 7 | \ } 8 | 9 | function s:outputter.init(session) abort 10 | let self._buf = '' 11 | endfunction 12 | 13 | function s:outputter.output(data, session) abort 14 | if !self.config.log 15 | echon a:data 16 | return 17 | endif 18 | let lines = split(a:data, "\n", 1) 19 | let lines[0] = self._buf . lines[0] 20 | let self._buf = lines[-1] 21 | for line in lines[: -2] 22 | echomsg line 23 | endfor 24 | endfunction 25 | 26 | function s:outputter.finish(session) abort 27 | if self.config.log && self._buf !=# '' 28 | echomsg self._buf 29 | endif 30 | endfunction 31 | 32 | 33 | function quickrun#outputter#message#new() abort 34 | return deepcopy(s:outputter) 35 | endfunction 36 | -------------------------------------------------------------------------------- /autoload/quickrun/outputter/multi.vim: -------------------------------------------------------------------------------- 1 | " quickrun: outputter/multi: Meta outputter; Outputs to multiple outputters. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:outputter = { 6 | \ 'config': { 7 | \ 'targets': [], 8 | \ }, 9 | \ } 10 | 11 | function s:outputter.init(session) abort 12 | let self._outputters = 13 | \ map(self.config.targets, 'a:session.make_module("outputter", v:val)') 14 | endfunction 15 | 16 | function s:outputter.start(session) abort 17 | for outputter in self._outputters 18 | call outputter.start(a:session) 19 | endfor 20 | endfunction 21 | 22 | function s:outputter.output(data, session) abort 23 | for outputter in self._outputters 24 | call outputter.output(a:data, a:session) 25 | endfor 26 | endfunction 27 | 28 | function s:outputter.finish(session) abort 29 | for outputter in self._outputters 30 | call outputter.finish(a:session) 31 | endfor 32 | endfunction 33 | 34 | 35 | function quickrun#outputter#multi#new() abort 36 | return deepcopy(s:outputter) 37 | endfunction 38 | -------------------------------------------------------------------------------- /autoload/quickrun/outputter/null.vim: -------------------------------------------------------------------------------- 1 | " quickrun: outputter/null: Doesn't output. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:outputter = {} 6 | 7 | function s:outputter.output(data, session) abort 8 | endfunction 9 | 10 | 11 | function quickrun#outputter#null#new() abort 12 | return deepcopy(s:outputter) 13 | endfunction 14 | -------------------------------------------------------------------------------- /autoload/quickrun/outputter/popup.vim: -------------------------------------------------------------------------------- 1 | " quickrun: outputter/popup: Outputs to a popup window. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | 6 | let s:outputter = quickrun#outputter#buffered#new() 7 | 8 | let s:winid = 0 9 | 10 | function s:outputter.validate() abort 11 | if !exists('*popup_create') 12 | throw 'Needs popup feature.' 13 | endif 14 | endfunction 15 | 16 | function! s:outputter.finish(session) abort 17 | if s:winid 18 | call popup_close(s:winid) 19 | endif 20 | let result = split(self._result, "\n") 21 | let width = max(map(copy(result), { _, l -> strwidth(l) })) 22 | let s:winid = popup_create(result, {'minwidth': width}) 23 | endfunction 24 | 25 | function quickrun#outputter#popup#new() abort 26 | return deepcopy(s:outputter) 27 | endfunction 28 | 29 | augroup plugin-quickrun-outputter-popup 30 | autocmd! 31 | autocmd CursorMoved,CursorMovedI,InsertEnter,InsertLeave,WinEnter * 32 | \ if 0 < s:winid | call popup_close(s:winid) | let s:winid = 0 | endif 33 | augroup END 34 | -------------------------------------------------------------------------------- /autoload/quickrun/outputter/quickfix.vim: -------------------------------------------------------------------------------- 1 | " quickrun: outputter/quickfix: Outputs to quickfix. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:VT = g:quickrun#V.import('Vim.ViewTracer') 6 | 7 | let s:outputter = quickrun#outputter#buffered#new() 8 | let s:outputter.config = { 9 | \ 'errorformat': '', 10 | \ 'open_cmd': 'copen', 11 | \ 'into': 0, 12 | \ } 13 | 14 | let s:outputter.init_buffered = s:outputter.init 15 | 16 | function! s:outputter.init(session) abort 17 | call self.init_buffered(a:session) 18 | let self.config.errorformat 19 | \ = !empty(self.config.errorformat) ? self.config.errorformat 20 | \ : !empty(&l:errorformat) ? &l:errorformat 21 | \ : &g:errorformat 22 | let self._target_window = s:VT.trace_window() 23 | let self._target_buf = bufnr('%') 24 | endfunction 25 | 26 | function! s:outputter.finish(session) abort 27 | try 28 | let errorformat = &g:errorformat 29 | let &g:errorformat = self.config.errorformat 30 | let current_window = s:VT.trace_window() 31 | call s:VT.jump(self._target_window) 32 | let result_list = self._apply_result(self._result) 33 | if self._fix_result_list(a:session, result_list) 34 | call self._apply_result_list(result_list) 35 | endif 36 | execute self.config.open_cmd 37 | if &buftype ==# 'quickfix' 38 | let w:quickfix_title = 'quickrun: ' . join(a:session.commands, ' && ') 39 | endif 40 | let result_empty = len(result_list) == 0 41 | if result_empty 42 | call self._close_window() 43 | endif 44 | if result_empty || !self.config.into 45 | call s:VT.jump(current_window) 46 | endif 47 | finally 48 | let &g:errorformat = errorformat 49 | endtry 50 | endfunction 51 | 52 | function s:outputter._fix_result_list(session, result_list) abort 53 | let region = get(a:session.config, 'region', {}) 54 | let srcfile = get(a:session.config, 'srcfile', '') 55 | if empty(region) || srcfile ==# '' 56 | return 0 57 | endif 58 | let fixed = 0 59 | let loffset = region.first[0] - 1 60 | for row in a:result_list 61 | if bufname(row.bufnr) ==# srcfile 62 | let row.bufnr = self._target_buf 63 | let row.lnum += loffset 64 | let fixed = 1 65 | endif 66 | endfor 67 | return fixed 68 | endfunction 69 | 70 | function s:outputter._apply_result(expr) abort 71 | cgetexpr a:expr 72 | return getqflist() 73 | endfunction 74 | 75 | function s:outputter._apply_result_list(result_list) abort 76 | call setqflist(a:result_list) 77 | endfunction 78 | 79 | function s:outputter._close_window() abort 80 | cclose 81 | endfunction 82 | 83 | 84 | function quickrun#outputter#quickfix#new() abort 85 | return deepcopy(s:outputter) 86 | endfunction 87 | -------------------------------------------------------------------------------- /autoload/quickrun/outputter/variable.vim: -------------------------------------------------------------------------------- 1 | " quickrun: outputter/variable: Outputs to a variable. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:outputter = { 6 | \ 'config': { 7 | \ 'name': '', 8 | \ 'append': 0, 9 | \ }, 10 | \ 'config_order': ['name', 'append'], 11 | \ } 12 | 13 | function s:outputter.init(session) abort 14 | let name = self.config.name 15 | if name is# '' 16 | throw 'Specify the variable name.' 17 | endif 18 | if name !~# '\W' 19 | let name = 'g:' . name 20 | endif 21 | let assign = self.config.append && 22 | \ (name[0] =~# '\W' || exists(name)) ? '.=' : '=' 23 | let self._name = name 24 | let self._assign = assign 25 | let self._size = 0 26 | endfunction 27 | 28 | function s:outputter.output(data, session) abort 29 | execute 'let' self._name self._assign 'a:data' 30 | let self._assign = '.=' 31 | let self._size += len(a:data) 32 | endfunction 33 | 34 | function s:outputter.finish(session) abort 35 | echo printf('Output to variable "%s" (%d bytes)', 36 | \ self.config.name, self._size) 37 | endfunction 38 | 39 | 40 | function quickrun#outputter#variable#new() abort 41 | return deepcopy(s:outputter) 42 | endfunction 43 | -------------------------------------------------------------------------------- /autoload/quickrun/runner/concurrent_process.vim: -------------------------------------------------------------------------------- 1 | " quickrun: runner/concurrent_process: Runs by Vital.ConcurrentProcess 2 | " Author: ujihisa 3 | " License: zlib License 4 | 5 | let s:runner = { 6 | \ 'config': { 7 | \ 'load': 'load %s', 8 | \ 'prompt': '>>> ', 9 | \ } 10 | \ } 11 | 12 | let s:M = g:quickrun#V.import('Vim.Message') 13 | let s:B = g:quickrun#V.import('Vim.Buffer') 14 | let s:CP = g:quickrun#V.import('ConcurrentProcess') 15 | 16 | augroup plugin-quickrun-concurrent-process 17 | augroup END 18 | 19 | function s:runner.validate() abort 20 | if !s:CP.is_available() 21 | throw 'Needs vimproc.' 22 | endif 23 | endfunction 24 | 25 | function s:runner.run(commands, input, session) abort 26 | let type = a:session.config.type 27 | 28 | let message = a:session.build_command(self.config.load) 29 | if message ==# '' 30 | return 0 31 | endif 32 | 33 | let cmd = printf('%s %s', a:session.config.command, a:session.config.cmdopt) 34 | let cmd = g:quickrun#V.Process.iconv(cmd, &encoding, &termencoding) 35 | 36 | let label = s:CP.of(cmd, '', [ 37 | \ ['*read*', '_', self.config.prompt]]) 38 | 39 | if s:CP.is_done(label, 'x') 40 | call s:CP.queue(label, [ 41 | \ ['*writeln*', message], 42 | \ ['*read*', 'x', self.config.prompt]]) 43 | else 44 | call s:CP.shutdown(label) 45 | call s:M.warn('Previous process was still running. Restarted.') 46 | " TODO be dry, or use ConcurrentProcess' new feature 47 | let label = s:CP.of(cmd, '', [ 48 | \ ['*read*', '_', self.config.prompt]]) 49 | call s:CP.queue(label, [ 50 | \ ['*writeln*', message], 51 | \ ['*read*', 'x', self.config.prompt]]) 52 | endif 53 | 54 | let a:session._cmd = cmd 55 | let a:session._prompt = self.config.prompt 56 | let key = a:session.continue() 57 | augroup plugin-quickrun-concurrent-process 58 | execute 'autocmd! CursorHold,CursorHoldI * call' 59 | \ 's:receive(' . string(key) . ')' 60 | augroup END 61 | let self._autocmd = 1 62 | let self._updatetime = &updatetime 63 | let &updatetime = 50 64 | endfunction 65 | 66 | function s:receive(key) abort 67 | if s:B.is_cmdwin() 68 | return 0 69 | endif 70 | 71 | let session = quickrun#session#get(a:key) 72 | let label = s:CP.of(session._cmd, '', [['*read*', '_', session._prompt]]) 73 | let [out, err] = s:CP.consume(label, 'x') 74 | call session.output(out . (err ==# '' ? '' : printf('!!!%s!!!', err))) 75 | if s:CP.is_done(label, 'x') 76 | call session.finish(1) 77 | return 1 78 | endif 79 | 80 | call quickrun#trigger_keys() 81 | return 0 82 | endfunction 83 | 84 | function s:runner.sweep() abort 85 | if has_key(self, '_autocmd') 86 | autocmd! plugin-quickrun-concurrent-process 87 | endif 88 | if has_key(self, '_updatetime') 89 | let &updatetime = self._updatetime 90 | endif 91 | endfunction 92 | 93 | function quickrun#runner#concurrent_process#new() abort 94 | return deepcopy(s:runner) 95 | endfunction 96 | -------------------------------------------------------------------------------- /autoload/quickrun/runner/job.vim: -------------------------------------------------------------------------------- 1 | " quickrun: runner/job: Runs by job feature. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:is_win = has('win32') 6 | let s:runner = { 7 | \ 'config': { 8 | \ 'pty': 0, 9 | \ 'interval': 0, 10 | \ } 11 | \ } 12 | 13 | function s:runner.validate() abort 14 | if !has('job') 15 | throw 'Needs +job feature.' 16 | endif 17 | if !exists('*ch_close_in') 18 | throw 'Needs ch_close_in() builtin function' 19 | endif 20 | if !s:is_win && !executable('sh') 21 | throw 'Needs "sh" on other than MS Windows.' 22 | endif 23 | endfunction 24 | 25 | function s:runner.run(commands, input, session) abort 26 | let command = join(a:commands, ' && ') 27 | let cmd_arg = s:is_win ? printf('cmd.exe /c (%s)', command) 28 | \ : ['sh', '-c', command] 29 | let options = { 30 | \ 'mode': 'raw', 31 | \ 'callback': self._job_cb, 32 | \ 'close_cb': self._job_close_cb, 33 | \ 'exit_cb': self._job_exit_cb, 34 | \ } 35 | if has('patch-8.0.0744') 36 | let options.pty = self.config.pty 37 | endif 38 | if a:input ==# '' 39 | let options.in_io = 'null' 40 | else 41 | let in_filename = a:session.tempname() 42 | call writefile(split(a:input, "\n", 1), in_filename, 'b') 43 | let options.in_io = 'file' 44 | let options.in_name = in_filename 45 | endif 46 | 47 | if self.config.interval 48 | let self._timer = 49 | \ timer_start(self.config.interval, self._timer_cb, {'repeat': -1}) 50 | endif 51 | 52 | let self._key = a:session.continue() 53 | let self._job = job_start(cmd_arg, options) 54 | endfunction 55 | 56 | function s:runner.sweep() abort 57 | if has_key(self, '_job') 58 | while job_status(self._job) ==# 'run' 59 | call job_stop(self._job) 60 | endwhile 61 | endif 62 | if has_key(self, '_timer') 63 | call timer_stop(self._timer) 64 | endif 65 | endfunction 66 | 67 | function s:runner._job_cb(channel, message) abort 68 | call quickrun#session#call(self._key, 'output', a:message) 69 | endfunction 70 | 71 | function s:runner._job_close_cb(channel) abort 72 | if has_key(self, '_job_exited') 73 | call quickrun#session#call(self._key, 'finish', self._job_exited) 74 | else 75 | let self._job_exited = 0 76 | endif 77 | endfunction 78 | 79 | function s:runner._job_exit_cb(job, exit_status) abort 80 | if has_key(self, '_job_exited') 81 | call quickrun#session#call(self._key, 'finish', a:exit_status) 82 | else 83 | let self._job_exited = a:exit_status 84 | endif 85 | endfunction 86 | 87 | function s:runner._timer_cb(timer) abort 88 | if has_key(self, '_job') 89 | call job_status(self._job) 90 | endif 91 | endfunction 92 | 93 | function quickrun#runner#job#new() abort 94 | return deepcopy(s:runner) 95 | endfunction 96 | -------------------------------------------------------------------------------- /autoload/quickrun/runner/python.vim: -------------------------------------------------------------------------------- 1 | " quickrun: runner/python: Runs by thread of +python feature asynchronously. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:python_loaded = 0 6 | if has('python') 7 | try 8 | python < 3 | " License: zlib License 4 | 5 | let s:is_win = has('win32') || has('win64') 6 | let s:runner = { 7 | \ 'config': { 8 | \ 'vimproc': 0, 9 | \ } 10 | \ } 11 | 12 | function s:runner.validate() abort 13 | if !has('clientserver') || v:servername ==# '' 14 | throw 'Needs +clientserver feature.' 15 | endif 16 | if !s:is_win && !executable('sh') 17 | throw 'Needs "sh" on other than MS Windows. Sorry.' 18 | endif 19 | endfunction 20 | 21 | function s:runner.run(commands, input, session) abort 22 | let selfvim = s:is_win ? 'vim.exe' : 23 | \ !empty($_) ? $_ : v:progname 24 | 25 | let key = a:session.continue() 26 | let outfile = a:session.tempname() 27 | let readfile = printf('join(readfile(%s, 1), "\n")', string(outfile)) 28 | let expr = printf('quickrun#session#call(%s, "output", %s) + ' . 29 | \ 'quickrun#session#call(%s, "finish")', 30 | \ string(key), readfile, string(key)) 31 | let cmds = a:commands 32 | let callback = s:make_command(self, 33 | \ [selfvim, '--servername', v:servername, '--remote-expr', expr]) 34 | 35 | let in = a:input 36 | if in !=# '' 37 | let inputfile = a:session.tempname() 38 | call writefile(split(in, "\n", 1), inputfile, 'b') 39 | let in = ' <' . self.shellescape(inputfile) 40 | endif 41 | 42 | " Execute by script file to unify the environment. 43 | let script = tempname() 44 | let scriptbody = [ 45 | \ printf('(%s)%s >%s 2>&1', join(cmds, '&&'), in, self.shellescape(outfile)), 46 | \ callback, 47 | \ ] 48 | if s:is_win 49 | let script .= '.bat' 50 | call insert(scriptbody, '@echo off') 51 | call map(scriptbody, 'v:val . "\r"') 52 | endif 53 | call map(scriptbody, 'g:quickrun#V.Process.iconv(v:val, &encoding, &termencoding)') 54 | call a:session.tempname(script) 55 | call writefile(scriptbody, script, 'b') 56 | 57 | let available_vimproc = globpath(&runtimepath, 'autoload/vimproc.vim') !=# '' 58 | if available_vimproc && self.config.vimproc 59 | if s:is_win 60 | let a:session._vimproc = vimproc#popen2(['cmd.exe', '/C', script]) 61 | else 62 | let a:session._vimproc = vimproc#popen2(['sh', script]) 63 | endif 64 | else 65 | if s:is_win 66 | if 703 <= v:version && has('patch203') 67 | \ || 703 < v:version 68 | silent! execute '!start /b' script 69 | else 70 | silent! execute '!start /min' script 71 | endif 72 | 73 | else "if executable('sh') " Simpler shell. 74 | silent! execute '!sh' script '&' 75 | endif 76 | endif 77 | endfunction 78 | 79 | function s:make_command(runner, args) abort 80 | return join([shellescape(a:args[0])] + 81 | \ map(a:args[1 :], 's:shellescape(v:val)'), ' ') 82 | endfunction 83 | 84 | function s:shellescape(str) abort 85 | if s:is_cmd_exe() 86 | return '^"' . substitute(substitute(substitute(a:str, 87 | \ '[&|<>()^"%]', '^\0', 'g'), 88 | \ '\\\+\ze"', '\=repeat(submatch(0), 2)', 'g'), 89 | \ '\^"', '\\\0', 'g') . '^"' 90 | endif 91 | return shellescape(a:str) 92 | endfunction 93 | 94 | function s:is_cmd_exe() abort 95 | return &shell =~? 'cmd\.exe' 96 | endfunction 97 | 98 | 99 | function quickrun#runner#remote#new() abort 100 | return deepcopy(s:runner) 101 | endfunction 102 | -------------------------------------------------------------------------------- /autoload/quickrun/runner/shell.vim: -------------------------------------------------------------------------------- 1 | " quickrun: runner/shell: Runs by :! . 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:runner = { 6 | \ 'config': { 7 | \ 'shellcmd': &shell =~? 'cmd\.exe' ? 'silent !%s & pause ' : '!%s', 8 | \ } 9 | \ } 10 | 11 | function s:runner.init(session) abort 12 | let a:session.config.outputter = 'null' 13 | endfunction 14 | 15 | function s:runner.run(commands, input, session) abort 16 | if a:input !=# '' 17 | let inputfile = a:session.tempname() 18 | call writefile(split(a:input, "\n", 1), inputfile, 'b') 19 | endif 20 | 21 | for cmd in a:commands 22 | if a:input !=# '' 23 | let cmd .= ' <' . self.shellescape(inputfile) 24 | endif 25 | 26 | call s:execute(printf(self.config.shellcmd, cmd)) 27 | if v:shell_error != 0 28 | break 29 | endif 30 | endfor 31 | endfunction 32 | 33 | function s:execute(cmd) abort 34 | let is_cmd_exe = &shell =~? 'cmd\.exe' 35 | try 36 | if is_cmd_exe 37 | let sxq = &shellxquote 38 | let &shellxquote = '"' 39 | endif 40 | execute g:quickrun#V.Process.iconv(a:cmd, &encoding, &termencoding) 41 | finally 42 | if is_cmd_exe 43 | let &shellxquote = sxq 44 | endif 45 | endtry 46 | endfunction 47 | 48 | 49 | function quickrun#runner#shell#new() abort 50 | return deepcopy(s:runner) 51 | endfunction 52 | -------------------------------------------------------------------------------- /autoload/quickrun/runner/system.vim: -------------------------------------------------------------------------------- 1 | " quickrun: runner/system: Runs by system(). 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:runner = {} 6 | 7 | function s:runner.run(commands, input, session) abort 8 | let code = 0 9 | for cmd in a:commands 10 | let [result, code] = s:execute(cmd, a:input) 11 | call a:session.output(result) 12 | if code != 0 13 | break 14 | endif 15 | endfor 16 | return code 17 | endfunction 18 | 19 | function s:execute(cmd, input) abort 20 | let is_cmd_exe = &shell =~? 'cmd\.exe' 21 | try 22 | if is_cmd_exe 23 | let sxq = &shellxquote 24 | let &shellxquote = '"' 25 | endif 26 | let cmd = a:cmd 27 | 28 | if v:version < 704 || (v:version == 704 && !has('patch132')) 29 | let cmd = g:quickrun#V.Process.iconv(cmd, &encoding, &termencoding) 30 | endif 31 | let result = a:input ==# '' ? system(cmd) 32 | \ : system(cmd, a:input) 33 | finally 34 | if is_cmd_exe 35 | let &shellxquote = sxq 36 | endif 37 | endtry 38 | return [result, v:shell_error] 39 | endfunction 40 | 41 | 42 | function quickrun#runner#system#new() abort 43 | return deepcopy(s:runner) 44 | endfunction 45 | -------------------------------------------------------------------------------- /autoload/quickrun/runner/terminal.vim: -------------------------------------------------------------------------------- 1 | " quickrun: runner/terminal: Runs by terminal feature. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:is_win = has('win32') 6 | let s:runner = { 7 | \ 'config': { 8 | \ 'name': 'default', 9 | \ 'opener': 'new', 10 | \ 'into': 0, 11 | \ }, 12 | \ } 13 | 14 | let s:wins = {} 15 | 16 | function s:runner.validate() abort 17 | if !has('terminal') 18 | throw 'Needs +terminal feature.' 19 | endif 20 | if !s:is_win && !executable('sh') 21 | throw 'Needs "sh" on other than MS Windows.' 22 | endif 23 | endfunction 24 | 25 | function s:runner.init(session) abort 26 | let a:session.config.outputter = 'null' 27 | endfunction 28 | 29 | function s:runner.run(commands, input, session) abort 30 | let command = join(a:commands, ' && ') 31 | if a:input !=# '' 32 | let inputfile = a:session.tempname() 33 | call writefile(split(a:input, "\n", 1), inputfile, 'b') 34 | let command = printf('(%s) < %s', command, inputfile) 35 | endif 36 | let cmd_arg = s:is_win ? printf('cmd.exe /c (%s)', command) 37 | \ : ['sh', '-c', command] 38 | let options = { 39 | \ 'term_name': 'quickrun: ' . command, 40 | \ 'curwin': 1, 41 | \ 'close_cb': self._job_close_cb, 42 | \ 'exit_cb': self._job_exit_cb, 43 | \ } 44 | 45 | let self._key = a:session.continue() 46 | let prev_winid = win_getid() 47 | 48 | let jumped = s:goto_last_win(self.config.name) 49 | if !jumped 50 | execute self.config.opener 51 | let s:wins[self.config.name] += [win_getid()] 52 | endif 53 | let self._bufnr = term_start(cmd_arg, options) 54 | setlocal bufhidden=wipe 55 | if !self.config.into 56 | call win_gotoid(prev_winid) 57 | endif 58 | endfunction 59 | 60 | function s:runner.sweep() abort 61 | if has_key(self, '_bufnr') && bufexists(self._bufnr) 62 | let job = term_getjob(self._bufnr) 63 | while job_status(job) ==# 'run' 64 | call job_stop(job) 65 | endwhile 66 | endif 67 | endfunction 68 | 69 | function s:runner._job_close_cb(channel) abort 70 | if has_key(self, '_job_exited') 71 | call quickrun#session#call(self._key, 'finish', self._job_exited) 72 | else 73 | let self._job_exited = 0 74 | endif 75 | endfunction 76 | 77 | function s:runner._job_exit_cb(job, exit_status) abort 78 | if has_key(self, '_job_exited') 79 | call quickrun#session#call(self._key, 'finish', a:exit_status) 80 | else 81 | let self._job_exited = a:exit_status 82 | endif 83 | endfunction 84 | 85 | function s:goto_last_win(name) abort 86 | if !has_key(s:wins, a:name) 87 | let s:wins[a:name] = [] 88 | endif 89 | 90 | " sweep 91 | call filter(s:wins[a:name], 'win_id2tabwin(v:val)[0] != 0') 92 | 93 | for win_id in s:wins[a:name] 94 | let winnr = win_id2win(win_id) 95 | if winnr 96 | call win_gotoid(win_id) 97 | return 1 98 | endif 99 | endfor 100 | return 0 101 | endfunction 102 | 103 | function quickrun#runner#terminal#new() abort 104 | return deepcopy(s:runner) 105 | endfunction 106 | -------------------------------------------------------------------------------- /autoload/quickrun/runner/vimproc.vim: -------------------------------------------------------------------------------- 1 | " quickrun: runner/vimproc: Runs by vimproc at background. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | " Create augroup. 6 | augroup plugin-quickrun-runner-vimproc 7 | augroup END 8 | 9 | let s:runner = { 10 | \ 'config': { 11 | \ 'updatetime': 0, 12 | \ 'sleep': 50, 13 | \ 'read_timeout': 100, 14 | \ 'pipe_status_index': -1, 15 | \ } 16 | \ } 17 | let s:bufsize = -1 18 | 19 | let s:M = g:quickrun#V.import('Vim.Message') 20 | 21 | function s:runner.validate() abort 22 | if globpath(&runtimepath, 'autoload/vimproc.vim') ==# '' 23 | throw 'Needs vimproc.' 24 | endif 25 | endfunction 26 | 27 | function s:runner.init(session) abort 28 | if type(self.config.pipe_status_index) != v:t_number && 29 | \ string(self.config.pipe_status_index) !=# string('pipefail') 30 | call s:M.warn('Invalid value in `runner/vimproc/pipe_status_index`: ' 31 | \ . string(self.config.pipe_status_index)) 32 | let self.config.pipe_status_index = -1 33 | endif 34 | endfunction 35 | 36 | function s:runner.run(commands, input, session) abort 37 | let vimproc = vimproc#pgroup_open(join(a:commands, ' && ')) 38 | call vimproc.stdin.write(a:input) 39 | call vimproc.stdin.close() 40 | 41 | let a:session._vimproc = vimproc 42 | let key = a:session.continue() 43 | 44 | " Wait a little because execution might end immediately. 45 | if self.config.sleep 46 | execute 'sleep' self.config.sleep . 'm' 47 | endif 48 | if s:receive_vimproc_result(key, self.config.read_timeout, 49 | \ self.config.pipe_status_index) 50 | return 51 | endif 52 | " Execution is continuing. 53 | augroup plugin-quickrun-runner-vimproc 54 | execute 'autocmd! CursorHold,CursorHoldI * call' 55 | \ 's:receive_vimproc_result(' . string(key) . ',' 56 | \ string(self.config.read_timeout) . ',' 57 | \ string(self.config.pipe_status_index) . ')' 58 | augroup END 59 | let self._autocmd = 1 60 | if self.config.updatetime 61 | let self._updatetime = &updatetime 62 | let &updatetime = self.config.updatetime 63 | endif 64 | endfunction 65 | 66 | function s:runner.shellescape(str) abort 67 | return '"' . escape(a:str, '\"') . '"' 68 | endfunction 69 | 70 | function s:runner.sweep() abort 71 | if has_key(self, '_autocmd') 72 | autocmd! plugin-quickrun-runner-vimproc 73 | endif 74 | if has_key(self, '_updatetime') 75 | let &updatetime = self._updatetime 76 | endif 77 | endfunction 78 | 79 | 80 | function s:receive_vimproc_result(key, read_timeout, pipe_status_index) abort 81 | let session = quickrun#session#get(a:key) 82 | 83 | let vimproc = session._vimproc 84 | 85 | try 86 | if !vimproc.stdout.eof 87 | call session.output(vimproc.stdout.read(s:bufsize, a:read_timeout)) 88 | endif 89 | if !vimproc.stderr.eof 90 | call session.output(vimproc.stderr.read(s:bufsize, a:read_timeout)) 91 | endif 92 | 93 | if !(vimproc.stdout.eof && vimproc.stderr.eof) 94 | call quickrun#trigger_keys() 95 | return 0 96 | endif 97 | catch 98 | " XXX: How is an internal error displayed? 99 | call session.output( 100 | \ 'quickrun: vimproc: ' . v:throwpoint . "\n" . v:exception) 101 | endtry 102 | 103 | call vimproc.stdout.close() 104 | call vimproc.stderr.close() 105 | let status = vimproc.waitpid()[1] 106 | if has_key(vimproc, 'pipe_status') 107 | let session.pipe_status = map(copy(vimproc.pipe_status), 'v:val[1]') 108 | if string(a:pipe_status_index) ==# string('pipefail') 109 | let status = get(filter(copy(session.pipe_status), 'v:val'), -1, 0) 110 | else 111 | let status = get(session.pipe_status, a:pipe_status_index, status) 112 | endif 113 | endif 114 | call session.finish(status) 115 | return 1 116 | endfunction 117 | 118 | 119 | function quickrun#runner#vimproc#new() abort 120 | return deepcopy(s:runner) 121 | endfunction 122 | -------------------------------------------------------------------------------- /autoload/quickrun/runner/vimscript.vim: -------------------------------------------------------------------------------- 1 | " quickrun: runner/vimscript: Runs commands as vim commands. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | let s:runner = {} 6 | 7 | function s:runner.run(commands, input, session) abort 8 | let code = 0 9 | for cmd in a:commands 10 | let [result, code] = s:execute(cmd) 11 | call a:session.output(result) 12 | if code != 0 13 | break 14 | endif 15 | endfor 16 | return code 17 | endfunction 18 | 19 | function s:execute(cmd) abort 20 | let result = '' 21 | let error = 0 22 | let temp = tempname() 23 | 24 | let save_vfile = &verbosefile 25 | let &verbosefile = temp 26 | 27 | try 28 | silent execute a:cmd 29 | catch 30 | let error = 1 31 | silent echo v:throwpoint 32 | silent echo matchstr(v:exception, '^Vim\%((\w*)\)\?:\s*\zs.*') 33 | finally 34 | if &verbosefile ==# temp 35 | let &verbosefile = save_vfile 36 | endif 37 | endtry 38 | 39 | if filereadable(temp) 40 | let result .= join(readfile(temp, 'b'), "\n") 41 | call delete(temp) 42 | endif 43 | 44 | return [result, error] 45 | endfunction 46 | 47 | 48 | function quickrun#runner#vimscript#new() abort 49 | return deepcopy(s:runner) 50 | endfunction 51 | -------------------------------------------------------------------------------- /autoload/quickrun/session.vim: -------------------------------------------------------------------------------- 1 | let s:V = g:quickrun#V 2 | 3 | 4 | function quickrun#session#new(config) abort 5 | let session = copy(s:Session) 6 | call session.initialize(a:config) 7 | return session 8 | endfunction 9 | 10 | function quickrun#session#get(key) abort 11 | return get(s:sessions, a:key, {}) 12 | endfunction 13 | 14 | function quickrun#session#call(key, func, ...) abort 15 | let session = quickrun#session#get(a:key) 16 | if !empty(session) 17 | return call(session[a:func], a:000, session) 18 | endif 19 | endfunction 20 | 21 | function quickrun#session#sweep() abort 22 | call map(keys(s:sessions), 's:dispose_session(v:val)') 23 | endfunction 24 | 25 | function quickrun#session#exists() abort 26 | return !empty(s:sessions) 27 | endfunction 28 | 29 | 30 | let s:sessions = {} " Store for sessions. 31 | 32 | function s:save_session(session) abort 33 | let key = has('reltime') ? reltimestr(reltime()) : string(localtime()) 34 | let s:sessions[key] = a:session 35 | return key 36 | endfunction 37 | 38 | function s:dispose_session(key) abort 39 | if has_key(s:sessions, a:key) 40 | let session = remove(s:sessions, a:key) 41 | call session.sweep() 42 | endif 43 | endfunction 44 | 45 | 46 | let s:Session = {} " {{{1 47 | " Initialize of instance. 48 | function s:Session.initialize(config) abort 49 | let self.base_config = extend(copy(g:quickrun#default_config._), a:config) 50 | endfunction 51 | 52 | " The option is appropriately set referring to default options. 53 | function s:Session.normalize(config) abort 54 | let config = a:config 55 | if has_key(config, 'input') 56 | let input = quickrun#expand(config.input) 57 | try 58 | let config.input = input[0] ==# '=' ? input[1 :] 59 | \ : join(readfile(input, 'b'), "\n") 60 | catch 61 | throw 'quickrun: Can not treat input: ' . v:exception 62 | endtry 63 | else 64 | let config.input = '' 65 | endif 66 | 67 | let exec = get(config, 'exec', '') 68 | let config.exec = type(exec) == v:t_list ? exec : [exec] 69 | let config.command = get(config, 'command', get(config, 'type', '')) 70 | 71 | if has_key(config, 'srcfile') 72 | let config.srcfile = quickrun#expand(expand(config.srcfile)) 73 | elseif !has_key(config, 'src') 74 | if filereadable(expand('%:p')) && 75 | \ !has_key(config, 'region') && !&modified 76 | " Use file in direct. 77 | let config.srcfile = expand('%:p') 78 | else 79 | let config.region = get(config, 'region', { 80 | \ 'first': [1, 0, 0], 81 | \ 'last': [line('$'), 0, 0], 82 | \ 'wise': 'V', 83 | \ }) 84 | " Executes on the temporary file. 85 | let body = s:get_region(config.region) 86 | 87 | let body = s:V.Process.iconv(body, &encoding, &fileencoding) 88 | 89 | if !&l:binary && 90 | \ (!exists('&fixendofline') || &l:fixendofline || &l:endofline) 91 | let body .= "\n" 92 | endif 93 | if &l:fileformat ==# 'mac' 94 | let body = substitute(body, "\n", "\r", 'g') 95 | elseif &l:fileformat ==# 'dos' 96 | let body = substitute(body, "\n", "\r\n", 'g') 97 | endif 98 | 99 | let config.src = body 100 | endif 101 | endif 102 | 103 | if !has_key(config, 'srcfile') 104 | let fname = quickrun#expand(config.tempfile) 105 | call self.tempname(fname) 106 | call writefile(split(config.src, "\n", 1), fname, 'b') 107 | let config.srcfile = fname 108 | endif 109 | 110 | for opt in ['cmdopt', 'args'] 111 | let config[opt] = quickrun#expand(config[opt]) 112 | endfor 113 | return config 114 | endfunction 115 | 116 | function s:Session.setup() abort 117 | try 118 | if has_key(self, 'exit_code') 119 | call remove(self, 'exit_code') 120 | endif 121 | let self.config = deepcopy(self.base_config) 122 | 123 | let hooks = map(quickrun#module#get('hook'), 'v:val.name') 124 | if has_key(self.config, 'hooks') 125 | let hooks = self.config.hooks + hooks 126 | endif 127 | let self.hooks = map(hooks, 'self.make_module("hook", v:val)') 128 | call self.invoke_hook('hook_loaded') 129 | call filter(self.hooks, 'v:val.config.enable') 130 | let self.config = self.normalize(self.config) 131 | call self.invoke_hook('normalized') 132 | 133 | let self.runner = self.make_module('runner', self.config.runner) 134 | let self.outputter = self.make_module('outputter', self.config.outputter) 135 | call self.invoke_hook('module_loaded') 136 | 137 | let commands = copy(self.config.exec) 138 | call filter(map(commands, 'self.build_command(quickrun#expand(v:val))'), 139 | \ 'v:val =~# "\\S"') 140 | let self.commands = commands 141 | catch /^quickrun:/ 142 | call self.sweep() 143 | throw v:exception 144 | catch 145 | call self.sweep() 146 | throw join(['quickrun: Error occurred in setup():', 147 | \ v:exception, v:throwpoint], "\n") 148 | endtry 149 | endfunction 150 | 151 | function s:Session.make_module(kind, modualizable) abort 152 | let modualizable = a:modualizable 153 | let modualizable_t = type(modualizable) 154 | let args = [] 155 | 156 | if modualizable_t == v:t_list 157 | let [modualizable; args] = modualizable 158 | let modualizable_t = type(modualizable) 159 | endif 160 | 161 | if modualizable_t == v:t_dict 162 | let module = quickrun#module#build(a:kind, modualizable) 163 | let name = get(module, 'name', '(no name)') 164 | elseif modualizable_t == v:t_string 165 | let [name; line_args] = split(modualizable, '^\w\+\zs', 1) 166 | call extend(args, line_args) 167 | 168 | let module = deepcopy(quickrun#module#get(a:kind, name)) 169 | endif 170 | 171 | try 172 | call module.validate() 173 | catch 174 | let exception = matchstr(v:exception, '^\%(quickrun:\s*\)\?\zs.*') 175 | throw printf('quickrun: Specified %s is not available: %s: %s', 176 | \ a:kind, name, exception) 177 | endtry 178 | 179 | try 180 | call s:apply_module_config(module, [self.config] + args) 181 | call map(module.config, 'quickrun#expand(v:val)') 182 | call module.init(self) 183 | catch 184 | let exception = matchstr(v:exception, '^\%(quickrun:\s*\)\?\zs.*') 185 | throw printf('quickrun: %s/%s: %s', 186 | \ a:kind, name, exception) 187 | endtry 188 | 189 | return module 190 | endfunction 191 | 192 | function s:Session.run() abort 193 | if has_key(self, '_running') 194 | throw 'quickrun: session.run() was called in running.' 195 | endif 196 | let self._running = 1 197 | call self.setup() 198 | call self.invoke_hook('ready') 199 | let exit_code = 1 200 | try 201 | call self.outputter.start(self) 202 | let exit_code = self.runner.run(self.commands, self.config.input, self) 203 | finally 204 | if !has_key(self, '_continue_key') 205 | call self.finish(exit_code) 206 | endif 207 | endtry 208 | endfunction 209 | 210 | function s:Session.continue() abort 211 | let self._continue_key = s:save_session(self) 212 | return self._continue_key 213 | endfunction 214 | 215 | function s:Session.output(data) abort 216 | let context = {'data': a:data} 217 | call self.invoke_hook('output', context) 218 | if context.data !=# '' 219 | call self.outputter.output(context.data, self) 220 | endif 221 | endfunction 222 | 223 | function s:Session.finish(...) abort 224 | if !has_key(self, 'exit_code') 225 | let self.exit_code = a:0 ? a:1 : 0 226 | if self.exit_code == 0 227 | call self.invoke_hook('success') 228 | else 229 | call self.invoke_hook('failure', {'exit_code': self.exit_code}) 230 | endif 231 | call self.invoke_hook('finish') 232 | call self.outputter.finish(self) 233 | call self.sweep() 234 | call self.invoke_hook('exit') 235 | endif 236 | endfunction 237 | 238 | " Build a command to execute it from options. 239 | " XXX: Undocumented yet. This is used by core modules only. 240 | function s:Session.build_command(tmpl) abort 241 | let config = self.config 242 | let command = config.command 243 | let rule = { 244 | \ 'c': command, 245 | \ 's': config.srcfile, 246 | \ 'o': config.cmdopt, 247 | \ 'a': config.args, 248 | \ '%': '%', 249 | \} 250 | let rest = a:tmpl 251 | let result = '' 252 | while 1 253 | let pos = match(rest, '%') 254 | if pos < 0 255 | let result .= rest 256 | break 257 | elseif pos != 0 258 | let result .= rest[: pos - 1] 259 | let rest = rest[pos :] 260 | endif 261 | 262 | let symbol = rest[1] 263 | let value = get(rule, tolower(symbol), '') 264 | 265 | if symbol ==? 'c' && value ==# '' 266 | throw 'quickrun: "command" option is empty.' 267 | endif 268 | 269 | let rest = rest[2 :] 270 | if symbol =~? '^[cs]$' 271 | if symbol ==# 'c' 272 | let value_ = s:V.System.Filepath.which(value) 273 | if value_ !=# '' 274 | let value = value_ 275 | endif 276 | endif 277 | let mod = matchstr(rest, '^\v\zs%(\:[p8~.htre]|\:g?s(.).{-}\1.{-}\1)*') 278 | let value = fnamemodify(value, mod) 279 | if symbol =~# '\U' 280 | let value = self.runner.shellescape(value) 281 | endif 282 | let rest = rest[len(mod) :] 283 | endif 284 | let result .= value 285 | endwhile 286 | return substitute(result, '[\r\n]\+', ' ', 'g') 287 | endfunction 288 | 289 | function s:Session.tempname(...) abort 290 | let name = a:0 ? a:1 : tempname() 291 | if !has_key(self, '_temp_names') 292 | let self._temp_names = [] 293 | endif 294 | call add(self._temp_names, name) 295 | return name 296 | endfunction 297 | 298 | " Sweep the session. 299 | function s:Session.sweep() abort 300 | " Remove temporary files. 301 | if has_key(self, '_temp_names') 302 | for name in self._temp_names 303 | call delete(name, 'rf') 304 | endfor 305 | endif 306 | 307 | " Sweep the execution of vimproc. 308 | if has_key(self, '_vimproc') 309 | try 310 | call self._vimproc.kill(15) 311 | call self._vimproc.waitpid() 312 | catch 313 | endtry 314 | call remove(self, '_vimproc') 315 | endif 316 | 317 | if has_key(self, '_continue_key') 318 | if has_key(s:sessions, self._continue_key) 319 | call remove(s:sessions, self._continue_key) 320 | endif 321 | call remove(self, '_continue_key') 322 | endif 323 | 324 | if has_key(self, 'runner') 325 | call self.runner.sweep() 326 | endif 327 | if has_key(self, 'outputter') 328 | call self.outputter.sweep() 329 | endif 330 | if has_key(self, 'hooks') 331 | for hook in self.hooks 332 | call hook.sweep() 333 | endfor 334 | endif 335 | 336 | if has_key(self, '_running') 337 | call remove(self, '_running') 338 | endif 339 | endfunction 340 | 341 | function s:Session.invoke_hook(point, ...) abort 342 | let context = a:0 ? a:1 : {} 343 | let func = 'on_' . a:point 344 | let hooks = copy(self.hooks) 345 | let hooks = map(hooks, '[v:val, s:get_hook_priority(v:val, a:point)]') 346 | let hooks = s:V.Data.List.sort_by(hooks, 'v:val[1]') 347 | let hooks = map(hooks, 'v:val[0]') 348 | for hook in hooks 349 | if has_key(hook, func) && type(hook[func]) is# v:t_func 350 | call call(hook[func], [self, context], hook) 351 | endif 352 | endfor 353 | endfunction 354 | 355 | function s:get_hook_priority(hook, point) abort 356 | try 357 | return a:hook.priority(a:point) - 0 358 | catch 359 | return 0 360 | endtry 361 | endfunction 362 | 363 | function s:apply_module_config(module, configs) abort 364 | for config in a:configs 365 | if type(config) == v:t_dict 366 | for name in keys(a:module.config) 367 | if has_key(a:module, 'name') 368 | let key_names = [ 369 | \ a:module.kind . '/' . a:module.name . '/' . name, 370 | \ a:module.name . '/' . name, 371 | \ a:module.kind . '/' . name, 372 | \ name, 373 | \ ] 374 | else 375 | let key_names = [name] 376 | endif 377 | for conf in key_names 378 | if has_key(config, conf) 379 | let val = config[conf] 380 | if type(a:module.config[name]) is# v:t_list 381 | let a:module.config[name] += type(val) is# v:t_list ? val : [val] 382 | else 383 | let a:module.config[name] = val 384 | endif 385 | unlet val 386 | break 387 | endif 388 | endfor 389 | endfor 390 | elseif type(config) == v:t_string && config !=# '' 391 | call s:parse_module_option(a:module, config) 392 | endif 393 | unlet config 394 | endfor 395 | endfunction 396 | 397 | function s:parse_module_option(module, argline) abort 398 | let sep = a:argline[0] 399 | let args = split(a:argline[1 :], '\V' . escape(sep, '\')) 400 | let order = copy(a:module.config_order) 401 | for arg in args 402 | let name = matchstr(arg, '^\w\+\ze=') 403 | if !empty(name) 404 | let value = matchstr(arg, '^\w\+=\zs.*') 405 | elseif len(a:module.config) == 1 406 | let [name, value] = [keys(a:module.config)[0], arg] 407 | elseif !empty(order) 408 | let name = remove(order, 0) 409 | let value = arg 410 | endif 411 | if empty(name) 412 | throw 'could not parse the option: ' . arg 413 | endif 414 | if !has_key(a:module.config, name) 415 | throw 'unknown option: ' . name 416 | endif 417 | if type(a:module.config[name]) == v:t_list 418 | call add(a:module.config[name], value) 419 | else 420 | let a:module.config[name] = value 421 | endif 422 | endfor 423 | endfunction 424 | 425 | " Get the text of specified region. 426 | " region = { 427 | " 'first': [line, col, off], 428 | " 'last': [line, col, off], 429 | " 'wise': 'v' / 'V' / "\", 430 | " 'selection': 'inclusive' / 'exclusive' / 'old' 431 | " } 432 | function s:get_region(region) abort 433 | let wise = get(a:region, 'wise', 'V') 434 | if wise ==# 'V' 435 | return join(getline(a:region.first[0], a:region.last[0]), "\n") 436 | endif 437 | 438 | if has_key(a:region, 'selection') 439 | let save_sel = &selection 440 | let &selection = a:region.selection 441 | endif 442 | let [reg_save, reg_save_type] = [getreg('"'), getregtype('"')] 443 | let [pos_c, pos_s, pos_e] = [getpos('.'), getpos("'<"), getpos("'>")] 444 | 445 | call cursor(a:region.first) 446 | execute 'silent normal!' wise 447 | call cursor(a:region.last) 448 | normal! y 449 | let selected = @" 450 | 451 | " Restore '< '> 452 | call setpos('.', pos_s) 453 | execute 'normal!' wise 454 | call setpos('.', pos_e) 455 | execute 'normal!' wise 456 | call setpos('.', pos_c) 457 | 458 | call setreg('"', reg_save, reg_save_type) 459 | 460 | if exists('save_sel') 461 | let &selection = save_sel 462 | endif 463 | return selected 464 | endfunction 465 | -------------------------------------------------------------------------------- /autoload/vital/_quickrun.vim: -------------------------------------------------------------------------------- 1 | let s:_plugin_name = expand(':t:r') 2 | 3 | function! vital#{s:_plugin_name}#new() abort 4 | return vital#{s:_plugin_name[1:]}#new() 5 | endfunction 6 | 7 | function! vital#{s:_plugin_name}#function(funcname) abort 8 | silent! return function(a:funcname) 9 | endfunction 10 | -------------------------------------------------------------------------------- /autoload/vital/_quickrun/ConcurrentProcess.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not mofidify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_quickrun#ConcurrentProcess#import() abort', printf("return map({'_vital_depends': '', 'of': '', 'is_busy': '', 'is_done': '', 'log_dump': '', 'shutdown': '', 'log_clear': '', 'consume_all_blocking': '', 'tick': '', 'is_available': '', 'queue': '', 'consume': '', '_vital_loaded': ''}, \"vital#_quickrun#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:save_cpo = &cpo 11 | set cpo&vim 12 | 13 | " * queries: [(QueueLabel, QueueBody)] 14 | " * logs: [(String, String, String)] stdin, stdout, stderr 15 | " * vp: vimproc dict 16 | " * buffer_out, buffer_err: String 17 | " * current buffered vp output/error 18 | " * vars: dict 19 | " * supervisor: (String, String, String) to try of() again 20 | let s:_process_info = {} 21 | 22 | function! s:_vital_loaded(V) abort 23 | let s:L = a:V.import('Data.List') 24 | let s:S = a:V.import('Data.String') 25 | let s:P = a:V.import('Process') 26 | endfunction 27 | 28 | function! s:_vital_depends() abort 29 | return ['Data.List', 'Data.String', 'Process'] 30 | endfunction 31 | 32 | function! s:is_available() abort 33 | return s:P.has_vimproc() 34 | endfunction 35 | 36 | " supervisor strategy 37 | " * Failed to spawn the process: exception 38 | " * The process has been dead: start from scratch silently (see tick() for details) 39 | function! s:of(command, dir, initial_queries) abort 40 | let label = s:S.hash(printf( 41 | \ '%s--%s--%s', 42 | \ type(a:command) == type('') ? a:command : join(a:command, ' '), 43 | \ a:dir, 44 | \ join(a:initial_queries, ';'))) 45 | 46 | " Reset if the process is dead 47 | if has_key(s:_process_info, label) 48 | if get(s:_process_info[label].vp.checkpid(), 0, '') !=# 'run' 49 | call remove(s:_process_info, label) 50 | endif 51 | endif 52 | 53 | if !has_key(s:_process_info, label) 54 | if len(a:dir) 55 | let cwd = getcwd() 56 | execute 'lcd' a:dir 57 | endif 58 | try 59 | let vp = vimproc#popen3(a:command) 60 | finally 61 | if exists('cwd') 62 | execute 'lcd' cwd 63 | endif 64 | endtry 65 | 66 | let supervisor = { 67 | \ 'command': a:command, 68 | \ 'dir': a:dir, 69 | \ 'initial_queries': a:initial_queries} 70 | let s:_process_info[label] = { 71 | \ 'logs': [], 'queries': a:initial_queries, 'vp': vp, 72 | \ 'buffer_out': '', 'buffer_err': '', 'vars': {}, 73 | \ 'supervisor': supervisor} 74 | endif 75 | 76 | call s:tick(label) 77 | return label 78 | endfunction 79 | 80 | function! s:_split_at_last_newline(str) abort 81 | if len(a:str) == 0 82 | return ['', ''] 83 | endif 84 | 85 | let xs = split(a:str, ".*\n\\zs", 1) 86 | if len(xs) >= 2 87 | return [xs[0], xs[1]] 88 | else 89 | return ['', a:str] 90 | endif 91 | endfunction 92 | 93 | function! s:_read(pi, rname) abort 94 | let pi = a:pi 95 | 96 | let [out, err] = [pi.vp.stdout.read(-1, 0), pi.vp.stderr.read(-1, 0)] 97 | call add(pi.logs, ['', out, err]) 98 | 99 | " stdout: store into vars and buffer_out 100 | if !has_key(pi.vars, a:rname) 101 | let pi.vars[a:rname] = ['', ''] 102 | endif 103 | let [left, right] = s:_split_at_last_newline(pi.buffer_out . out) 104 | if a:rname !=# '_' 105 | let pi.vars[a:rname][0] .= left 106 | endif 107 | let pi.buffer_out = right 108 | 109 | " stderr: directly store into buffer_err 110 | let pi.buffer_err .= err 111 | endfunction 112 | 113 | function! s:tick(label) abort 114 | let pi = s:_process_info[a:label] 115 | 116 | if len(pi.queries) == 0 117 | return 118 | endif 119 | 120 | let is_alive = get(pi.vp.checkpid(), 0, '') ==# 'run' 121 | 122 | if !is_alive 123 | " Use the default supervisor. 124 | " Default supervisor: restart the process with fresh state. 125 | " (Accumulated queue won't be kept) 126 | call s:of(pi.supervisor.command, pi.supervisor.dir, pi.supervisor.initial_queries) 127 | return 128 | endif 129 | 130 | let qlabel = pi.queries[0][0] 131 | 132 | if qlabel ==# '*read*' 133 | let rname = pi.queries[0][1] 134 | let rtil = pi.queries[0][2] 135 | 136 | call s:_read(pi, rname) 137 | 138 | let pattern = "\\(^\\|\n\\)" . rtil . '$' 139 | " when wait ended: 140 | if pi.buffer_out =~ pattern 141 | if rname !=# '_' 142 | let pi.vars[rname][0] .= s:S.substitute_last(pi.buffer_out, pattern, '') 143 | let pi.vars[rname][1] = pi.buffer_err 144 | endif 145 | 146 | call remove(pi.queries, 0) 147 | let pi.buffer_out = '' 148 | let pi.buffer_err = '' 149 | 150 | call s:tick(a:label) 151 | endif 152 | elseif qlabel ==# '*read-all*' 153 | let rname = pi.queries[0][1] 154 | call pi.vp.stdin.close() 155 | call s:_read(pi, rname) 156 | 157 | " when wait ended: 158 | if get(s:_process_info[a:label].vp.checkpid(), 0, '') !=# 'run' 159 | if rname !=# '_' 160 | let pi.vars[rname][0] .= pi.buffer_out 161 | let pi.vars[rname][1] = pi.buffer_err 162 | endif 163 | 164 | call remove(pi.queries, 0) 165 | let pi.buffer_out = '' 166 | let pi.buffer_err = '' 167 | endif 168 | 169 | elseif qlabel ==# '*writeln*' 170 | let wbody = pi.queries[0][1] 171 | call pi.vp.stdin.write(wbody . "\n") 172 | call remove(pi.queries, 0) 173 | 174 | call add(pi.logs, [wbody . "\n", '', '']) 175 | 176 | call s:tick(a:label) 177 | else 178 | " must not happen 179 | throw 'vital: ConcurrentProcess: must not happen' 180 | endif 181 | endfunction 182 | 183 | " returns [out, err, timedout_p] 184 | function! s:consume_all_blocking(label, varname, timeout_sec) abort 185 | let start = reltime() 186 | while 1 187 | call s:tick(a:label) 188 | if s:is_done(a:label, a:varname) 189 | return s:consume(a:label, a:varname) + [0] " 0 as 'Did not timed out' 190 | elseif reltime(start)[0] >= a:timeout_sec 191 | return s:consume(a:label, a:varname) + [1] " 1 as 'Unfortunately it timed out' 192 | endif 193 | endwhile 194 | endfunction 195 | 196 | function! s:consume(label, varname) abort 197 | call s:tick(a:label) 198 | let pi = s:_process_info[a:label] 199 | 200 | if has_key(pi.vars, a:varname) 201 | let memo = pi.vars[a:varname] 202 | call remove(pi.vars, a:varname) 203 | return memo 204 | else 205 | return ['', ''] 206 | endif 207 | endfunction 208 | 209 | function! s:is_done(label, rname) abort 210 | let reads = filter( 211 | \ copy(s:_process_info[a:label].queries), 212 | \ "v:val[0] ==# '*read*' || v:val[0] ==# '*read-all*'") 213 | return s:L.all( 214 | \ printf('v:val[1] !=# %s', string(a:rname)), 215 | \ reads) 216 | endfunction 217 | 218 | function! s:queue(label, queries) abort 219 | call s:tick(a:label) 220 | let s:_process_info[a:label].queries += a:queries 221 | endfunction 222 | 223 | function! s:is_busy(label) abort 224 | call s:tick(a:label) 225 | 226 | return len(s:_process_info[a:label].queries) > 0 227 | endfunction 228 | 229 | function! s:shutdown(label) abort 230 | let pi = s:_process_info[a:label] 231 | call pi.vp.kill(g:vimproc#SIGKILL) 232 | call pi.vp.checkpid() 233 | unlet s:_process_info[a:label] 234 | endfunction 235 | 236 | " Just to wipe out the log 237 | function! s:log_clear(label) abort 238 | let s:_process_info[a:label].logs = [] 239 | endfunction 240 | 241 | " Print out log, and wipe out the log 242 | function! s:log_dump(label) abort 243 | echo '-----------------------------' 244 | for [stdin, stdout, stderr] in s:_process_info[a:label].logs 245 | echon stdin 246 | echon stdout 247 | if stderr !=# '' 248 | echon printf('!!!%s!!!', stderr) 249 | endif 250 | endfor 251 | let s:_process_info[a:label].logs = [] 252 | endfunction 253 | 254 | let &cpo = s:save_cpo 255 | unlet s:save_cpo 256 | " vim:set et ts=2 sts=2 sw=2 tw=0: 257 | -------------------------------------------------------------------------------- /autoload/vital/_quickrun/Data/Dict.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not mofidify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_quickrun#Data#Dict#import() abort', printf("return map({'_vital_depends': '', 'clear': '', 'max_by': '', 'foldl': '', 'pick': '', 'from_list': '', 'swap': '', 'omit': '', 'min_by': '', 'foldr': '', 'make_index': '', 'make': '', '_vital_loaded': ''}, \"vital#_quickrun#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | " Utilities for dictionary. 11 | 12 | let s:save_cpo = &cpo 13 | set cpo&vim 14 | 15 | function! s:_vital_loaded(V) abort 16 | let s:t = a:V.import('Vim.Type').types 17 | endfunction 18 | 19 | function! s:_vital_depends() abort 20 | return ['Vim.Type'] 21 | endfunction 22 | 23 | 24 | function! s:_ensure_key(key) abort 25 | let t = type(a:key) 26 | if t != s:t.string && t != s:t.number 27 | throw 'vital: Data.Dict: Invalid key: ' . string(a:key) 28 | endif 29 | endfunction 30 | 31 | function! s:from_list(list) abort 32 | let dict = {} 33 | let i = 0 34 | let len = len(a:list) 35 | while i < len 36 | if type(a:list[i]) == s:t.list 37 | let key_value = a:list[i] 38 | if len(key_value) != 2 39 | throw 'vital: Data.Dict: Invalid key-value pair index at ' . i 40 | endif 41 | call s:_ensure_key(key_value[0]) 42 | let dict[key_value[0]] = key_value[1] 43 | let i += 1 44 | else 45 | if len <= i + 1 46 | throw 'vital: Data.Dict: Invalid key-value pair index at ' . i 47 | endif 48 | call s:_ensure_key(a:list[i]) 49 | let dict[a:list[i]] = a:list[i + 1] 50 | let i += 2 51 | endif 52 | endwhile 53 | return dict 54 | endfunction 55 | 56 | " Makes a dict from keys and values 57 | function! s:make(keys, values, ...) abort 58 | let dict = {} 59 | let fill = a:0 ? a:1 : 0 60 | for i in range(len(a:keys)) 61 | let key = type(a:keys[i]) == s:t.string ? a:keys[i] : string(a:keys[i]) 62 | if key ==# '' 63 | throw "vital: Data.Dict: Can't use an empty string for key." 64 | endif 65 | let dict[key] = get(a:values, i, fill) 66 | endfor 67 | return dict 68 | endfunction 69 | 70 | " Swaps keys and values 71 | function! s:swap(dict) abort 72 | return s:make(values(a:dict), keys(a:dict)) 73 | endfunction 74 | 75 | " Makes a index dict from a list 76 | function! s:make_index(list, ...) abort 77 | let value = a:0 ? a:1 : 1 78 | return s:make(a:list, [], value) 79 | endfunction 80 | 81 | function! s:pick(dict, keys) abort 82 | let new_dict = {} 83 | for key in a:keys 84 | if has_key(a:dict, key) 85 | let new_dict[key] = a:dict[key] 86 | endif 87 | endfor 88 | return new_dict 89 | endfunction 90 | 91 | function! s:omit(dict, keys) abort 92 | let new_dict = copy(a:dict) 93 | for key in a:keys 94 | if has_key(a:dict, key) 95 | call remove(new_dict, key) 96 | endif 97 | endfor 98 | return new_dict 99 | endfunction 100 | 101 | function! s:clear(dict) abort 102 | for key in keys(a:dict) 103 | call remove(a:dict, key) 104 | endfor 105 | return a:dict 106 | endfunction 107 | 108 | function! s:_max_by(dict, expr) abort 109 | let dict = s:swap(map(copy(a:dict), a:expr)) 110 | let key = dict[max(keys(dict))] 111 | return [key, a:dict[key]] 112 | endfunction 113 | 114 | function! s:max_by(dict, expr) abort 115 | if empty(a:dict) 116 | throw 'vital: Data.Dict: Empty dictionary' 117 | endif 118 | return s:_max_by(a:dict, a:expr) 119 | endfunction 120 | 121 | function! s:min_by(dict, expr) abort 122 | if empty(a:dict) 123 | throw 'vital: Data.Dict: Empty dictionary' 124 | endif 125 | return s:_max_by(a:dict, '-(' . a:expr . ')') 126 | endfunction 127 | 128 | function! s:_foldl(f, init, xs) abort 129 | let memo = a:init 130 | for [k, v] in a:xs 131 | let expr = substitute(a:f, 'v:key', string(k), 'g') 132 | let expr = substitute(expr, 'v:val', string(v), 'g') 133 | let expr = substitute(expr, 'v:memo', string(memo), 'g') 134 | unlet memo 135 | let memo = eval(expr) 136 | endfor 137 | return memo 138 | endfunction 139 | 140 | function! s:foldl(f, init, dict) abort 141 | return s:_foldl(a:f, a:init, items(a:dict)) 142 | endfunction 143 | 144 | function! s:foldr(f, init, dict) abort 145 | return s:_foldl(a:f, a:init, reverse(items(a:dict))) 146 | endfunction 147 | 148 | let &cpo = s:save_cpo 149 | unlet s:save_cpo 150 | 151 | " vim:set et ts=2 sts=2 sw=2 tw=0: 152 | -------------------------------------------------------------------------------- /autoload/vital/_quickrun/Data/List.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not mofidify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_quickrun#Data#List#import() abort', printf("return map({'combinations': '', 'and': '', 'sort_by': '', 'foldr1': '', 'sort': '', 'flatten': '', 'has_index': '', 'filter': '', 'find_indices': '', 'any': '', 'map': '', 'unshift': '', 'span': '', 'pop': '', 'binary_search': '', 'uniq_by': '', 'or': '', 'all': '', 'zip': '', 'find_last_index': '', 'find': '', 'partition': '', 'shift': '', 'permutations': '', 'break': '', 'max_by': '', 'foldl': '', 'foldr': '', 'new': '', 'find_index': '', 'drop_while': '', 'group_by': '', 'take_while': '', 'conj': '', 'push': '', 'char_range': '', 'cons': '', 'foldl1': '', 'intersect': '', 'concat': '', 'map_accum': '', 'clear': '', 'has_common_items': '', 'product': '', 'uncons': '', 'zip_fill': '', 'uniq': '', 'has': '', 'min_by': '', 'with_index': ''}, \"vital#_quickrun#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | " Utilities for list. 11 | 12 | let s:save_cpo = &cpo 13 | set cpo&vim 14 | 15 | function! s:new(size, f) abort 16 | return map(range(a:size), a:f) 17 | endfunction 18 | 19 | function! s:pop(list) abort 20 | return remove(a:list, -1) 21 | endfunction 22 | 23 | function! s:push(list, val) abort 24 | call add(a:list, a:val) 25 | return a:list 26 | endfunction 27 | 28 | function! s:shift(list) abort 29 | return remove(a:list, 0) 30 | endfunction 31 | 32 | function! s:unshift(list, val) abort 33 | return insert(a:list, a:val) 34 | endfunction 35 | 36 | function! s:cons(x, xs) abort 37 | return [a:x] + a:xs 38 | endfunction 39 | 40 | function! s:uncons(xs) abort 41 | if len(a:xs) < 1 42 | throw 'vital: Data.List: uncons() requires non empty list' 43 | endif 44 | " This is pair (tuple) 45 | return [a:xs[0], a:xs[1:]] 46 | endfunction 47 | 48 | function! s:conj(xs, x) abort 49 | return a:xs + [a:x] 50 | endfunction 51 | 52 | function! s:map(xs, f) abort 53 | let l:Call = s:_get_unary_caller(a:f) 54 | let result = [] 55 | for x in a:xs 56 | call add(result, l:Call(a:f, [x])) 57 | endfor 58 | return result 59 | endfunction 60 | 61 | function! s:filter(xs, f) abort 62 | let l:Call = s:_get_unary_caller(a:f) 63 | let result = [] 64 | for x in a:xs 65 | if l:Call(a:f, [x]) 66 | call add(result, x) 67 | endif 68 | endfor 69 | return result 70 | endfunction 71 | 72 | " Removes duplicates from a list. 73 | function! s:uniq(list) abort 74 | return s:uniq_by(a:list, 'v:val') 75 | endfunction 76 | 77 | " Removes duplicates from a list. 78 | function! s:uniq_by(list, f) abort 79 | let l:Call = s:_get_unary_caller(a:f) 80 | let applied = [] 81 | let result = [] 82 | for x in a:list 83 | let y = l:Call(a:f, [x]) 84 | if !s:has(applied, y) 85 | call add(result, x) 86 | call add(applied, y) 87 | endif 88 | unlet x y 89 | endfor 90 | return result 91 | endfunction 92 | 93 | function! s:clear(list) abort 94 | if !empty(a:list) 95 | unlet! a:list[0 : len(a:list) - 1] 96 | endif 97 | return a:list 98 | endfunction 99 | 100 | " Concatenates a list of lists. 101 | " XXX: Should we verify the input? 102 | function! s:concat(list) abort 103 | let memo = [] 104 | for Value in a:list 105 | let memo += Value 106 | endfor 107 | return memo 108 | endfunction 109 | 110 | " Take each elements from lists to a new list. 111 | function! s:flatten(list, ...) abort 112 | let limit = a:0 > 0 ? a:1 : -1 113 | let memo = [] 114 | if limit == 0 115 | return a:list 116 | endif 117 | let limit -= 1 118 | for Value in a:list 119 | let memo += 120 | \ type(Value) == type([]) ? 121 | \ s:flatten(Value, limit) : 122 | \ [Value] 123 | unlet! Value 124 | endfor 125 | return memo 126 | endfunction 127 | 128 | " Sorts a list with expression to compare each two values. 129 | " a:a and a:b can be used in string expression ({f}). 130 | function! s:sort(list, f) abort 131 | if type(a:f) is type(function('function')) 132 | return sort(a:list, a:f) 133 | else 134 | " Give up job safety (atomically) 135 | let s:sort_expr = a:f 136 | return sort(a:list, 's:_compare_by_string_expr') 137 | endif 138 | endfunction 139 | 140 | " Lifts the string expression to the function. 141 | " s:sort_expr must be defined as the string expression of the binary function 142 | " before this is called. 143 | " a:a and a:b are used in s:sort_expr . 144 | " @vimlint(EVL103, 1) 145 | function! s:_compare_by_string_expr(a, b) abort 146 | return eval(s:sort_expr) 147 | endfunction 148 | " @vimlint(EVL103, 0) 149 | 150 | " Sorts a list using a set of keys generated by mapping the values in the list 151 | " through the given {unary_f}. 152 | function! s:sort_by(list, unary_f) abort 153 | let list = s:zip(a:list, s:map(copy(a:list), a:unary_f)) 154 | return map(sort(list, 's:_compare_with'), 'v:val[0]') 155 | endfunction 156 | 157 | let s:__number_pair = [type(10), type(10)] 158 | let s:__string_pair = [type(''), type('')] 159 | let s:__list_pair = [type([]), type([])] 160 | let s:__dict_pair = [type({}), type({})] 161 | let s:__function_pair = [type(function('function')), type(function('function'))] 162 | function! s:_compare_with(x, y) abort 163 | let x = a:x[1] 164 | let y = a:y[1] 165 | 166 | let type_pair = [type(x), type(y)] 167 | return type_pair ==# s:__number_pair ? s:_basic_comparator(x, y) 168 | \ : type_pair ==# s:__string_pair ? s:_basic_comparator(x, y) 169 | \ : type_pair ==# s:__list_pair ? s:_list_comparator(x, y) 170 | \ : type_pair ==# s:__dict_pair ? 1 171 | \ : type_pair ==# s:__function_pair 172 | \ ? execute('throw "vital: Data.List: sort_by() cannot compare a function and a function"') 173 | \ : execute(printf("throw 'vital: Data.List: sort_by() cannot compare %s and %s'", string(x), string(y))) 174 | endfunction 175 | 176 | " The basic comparator for Number and String 177 | function! s:_basic_comparator(x, y) abort 178 | return a:x ># a:y ? 1 179 | \ : a:x <# a:y ? -1 180 | \ : 0 181 | endfunction 182 | 183 | " The comparator of the dictionary order 184 | function! s:_list_comparator(xs, ys) abort 185 | let [xlen, ylen] = [len(a:xs), len(a:ys)] 186 | if xlen isnot ylen 187 | return s:_basic_comparator(xlen, ylen) 188 | endif 189 | 190 | for z in s:zip(a:xs, a:ys) 191 | let [x, y] = z 192 | let order = s:_basic_comparator(x, y) 193 | if order isnot 0 194 | return order 195 | endif 196 | endfor 197 | 198 | " if a:xs equals a:ys 199 | return 0 200 | endfunction 201 | 202 | " Returns a maximum value in {list} through given {function}. 203 | " Returns 0 if {list} is empty. 204 | " v:val is used in {function} if {function} is string expression 205 | function! s:max_by(list, f) abort 206 | if empty(a:list) 207 | return 0 208 | endif 209 | let list = s:map(copy(a:list), a:f) 210 | return a:list[index(list, max(list))] 211 | endfunction 212 | 213 | " Returns a minimum value in {list} through given {expr}. 214 | " Returns 0 if {list} is empty. 215 | " v:val is used in {expr}. 216 | " FIXME: -0x80000000 == 0x80000000 217 | function! s:min_by(list, f) abort 218 | if empty(a:list) 219 | return 0 220 | endif 221 | let list = s:map(copy(a:list), a:f) 222 | return a:list[index(list, min(list))] 223 | endfunction 224 | 225 | " Returns List of character sequence between [a:from, a:to] . 226 | " e.g.: s:char_range('a', 'c') returns ['a', 'b', 'c'] 227 | function! s:char_range(from, to) abort 228 | return map( 229 | \ range(char2nr(a:from), char2nr(a:to)), 230 | \ 'nr2char(v:val)' 231 | \) 232 | endfunction 233 | 234 | " Returns true if a:list has a:value. 235 | " Returns false otherwise. 236 | function! s:has(list, value) abort 237 | return index(a:list, a:value) isnot -1 238 | endfunction 239 | 240 | " Returns true if a:list[a:index] exists. 241 | " Returns false otherwise. 242 | " NOTE: Returns false when a:index is negative number. 243 | function! s:has_index(list, index) abort 244 | " Return true when negative index? 245 | " let index = a:index >= 0 ? a:index : len(a:list) + a:index 246 | return 0 <= a:index && a:index < len(a:list) 247 | endfunction 248 | 249 | " Similar to Haskell's Data.List.span . 250 | function! s:span(f, xs) abort 251 | let body = s:take_while(a:f, a:xs) 252 | let tail = a:xs[len(body) :] 253 | return [body, tail] 254 | endfunction 255 | 256 | " Similar to Haskell's Data.List.break . 257 | function! s:break(f, xs) abort 258 | let l:Call = s:_get_unary_caller(a:f) 259 | let first = [] 260 | for x in a:xs 261 | if l:Call(a:f, [x]) 262 | break 263 | endif 264 | call add(first, x) 265 | endfor 266 | return [first, a:xs[len(first) :]] 267 | endfunction 268 | 269 | " Similar to Haskell's Data.List.takeWhile . 270 | function! s:take_while(f, xs) abort 271 | let l:Call = s:_get_unary_caller(a:f) 272 | let result = [] 273 | for x in a:xs 274 | if l:Call(a:f, [x]) 275 | call add(result, x) 276 | else 277 | return result 278 | endif 279 | endfor 280 | endfunction 281 | 282 | " Similar to Haskell's Data.List.dropWhile . 283 | function! s:drop_while(f, xs) abort 284 | let l:Call = s:_get_unary_caller(a:f) 285 | let i = -1 286 | for x in a:xs 287 | if !l:Call(a:f, [x]) 288 | break 289 | endif 290 | let i += 1 291 | endfor 292 | return a:xs[i + 1 :] 293 | endfunction 294 | 295 | " Similar to Haskell's Data.List.partition . 296 | function! s:partition(f, xs) abort 297 | let l:Call = s:_get_unary_caller(a:f) 298 | let satisfied = s:filter(a:xs, a:f) 299 | let dissatisfied = [] 300 | for x in a:xs 301 | if !l:Call(a:f, [x]) 302 | call add(dissatisfied, x) 303 | endif 304 | endfor 305 | return [satisfied, dissatisfied] 306 | endfunction 307 | 308 | " Similar to Haskell's Prelude.all . 309 | function! s:all(f, xs) abort 310 | return empty(filter(s:map(a:xs, a:f), '!v:val')) 311 | endfunction 312 | 313 | " Similar to Haskell's Prelude.any . 314 | function! s:any(f, xs) abort 315 | return !empty(filter(s:map(a:xs, a:f), 'v:val')) 316 | endfunction 317 | 318 | " Similar to Haskell's Prelude.and . 319 | function! s:and(xs) abort 320 | return s:all('v:val', a:xs) 321 | endfunction 322 | 323 | " Similar to Haskell's Prelude.or . 324 | function! s:or(xs) abort 325 | return s:any('v:val', a:xs) 326 | endfunction 327 | 328 | function! s:map_accum(binary_f, xs, init) abort 329 | let l:Call = s:_get_binary_caller_(a:binary_f) 330 | let results = [] 331 | let acc = a:init 332 | for x in a:xs 333 | let [result, acc] = l:Call(a:binary_f, [x, acc]) 334 | call add(results, result) 335 | endfor 336 | return results 337 | endfunction 338 | 339 | " Similar to Haskell's Prelude.foldl . 340 | function! s:foldl(f, init, xs) abort 341 | "NOTE: The 'Call' should be named with l: for the conflict problem 342 | let l:Call = s:_get_binary_caller(a:f) 343 | let memo = a:init 344 | for x in a:xs 345 | let memo_new = l:Call(a:f, [memo, x]) 346 | unlet memo 347 | let memo = memo_new 348 | endfor 349 | return memo 350 | endfunction 351 | 352 | " Similar to Haskell's Prelude.foldl1 . 353 | function! s:foldl1(f, xs) abort 354 | if len(a:xs) == 0 355 | throw 'vital: Data.List: foldl1' 356 | endif 357 | return s:foldl(a:f, a:xs[0], a:xs[1:]) 358 | endfunction 359 | 360 | " Similar to Haskell's Prelude.foldr . 361 | function! s:foldr(f, init, xs) abort 362 | "NOTE: The 'Call' should be named with l: for the conflict problem 363 | let l:Call = s:_get_binary_caller(a:f) 364 | return s:_foldr_internal(l:Call, a:f, a:init, a:xs) 365 | endfunction 366 | 367 | " Avoids caller's overhead 368 | function! s:_foldr_internal(call, f, state, xs) abort 369 | if empty(a:xs) 370 | return a:state 371 | endif 372 | 373 | let [y, ys] = s:uncons(a:xs) 374 | return a:call(a:f, [y, s:_foldr_internal(a:call, a:f, a:state, ys)]) 375 | endfunction 376 | 377 | " Similar to Haskell's Prelude.fold11 . 378 | function! s:foldr1(f, xs) abort 379 | if len(a:xs) == 0 380 | throw 'vital: Data.List: foldr1' 381 | endif 382 | return s:foldr(a:f, a:xs[-1], a:xs[0:-2]) 383 | endfunction 384 | 385 | " Similar to python's zip() . 386 | function! s:zip(...) abort 387 | return map(range(min(map(copy(a:000), 'len(v:val)'))), "map(copy(a:000), 'v:val['.v:val.']')") 388 | endfunction 389 | 390 | " Similar to zip(), but goes until the longer one. 391 | function! s:zip_fill(xs, ys, filler) abort 392 | if empty(a:xs) && empty(a:ys) 393 | return [] 394 | elseif empty(a:ys) 395 | return s:cons([a:xs[0], a:filler], s:zip_fill(a:xs[1 :], [], a:filler)) 396 | elseif empty(a:xs) 397 | return s:cons([a:filler, a:ys[0]], s:zip_fill([], a:ys[1 :], a:filler)) 398 | else 399 | return s:cons([a:xs[0], a:ys[0]], s:zip_fill(a:xs[1 :], a:ys[1: ], a:filler)) 400 | endif 401 | endfunction 402 | 403 | " Inspired by Ruby's with_index method. 404 | function! s:with_index(list, ...) abort 405 | let base = a:0 > 0 ? a:1 : 0 406 | return map(copy(a:list), '[v:val, v:key + base]') 407 | endfunction 408 | 409 | " Similar to Ruby's detect or Haskell's find. 410 | function! s:find(list, default, f) abort 411 | let l:Call = s:_get_unary_caller(a:f) 412 | for x in a:list 413 | if l:Call(a:f, [x]) 414 | return x 415 | endif 416 | endfor 417 | return a:default 418 | endfunction 419 | 420 | " Returns the index of the first element which satisfies the given expr. 421 | function! s:find_index(xs, f, ...) abort 422 | let len_xs = len(a:xs) 423 | let default = get(a:000, 1, -1) 424 | 425 | let start = get(a:000, 0, 0) 426 | " Emulate list[-n] 427 | if start < 0 428 | let start += len_xs 429 | endif 430 | 431 | if len_xs <= start 432 | return default 433 | endif 434 | 435 | let l:Call = s:_get_unary_caller(a:f) 436 | for i in range(start, len_xs - 1) 437 | let x = a:xs[i] 438 | if l:Call(a:f, [x]) 439 | return i 440 | endif 441 | endfor 442 | return default 443 | endfunction 444 | 445 | " Returns the index of the last element which satisfies the given expr. 446 | function! s:find_last_index(xs, f, ...) abort 447 | let len_xs = len(a:xs) 448 | let default = get(a:000, 1, -1) 449 | 450 | let start = get(a:000, 0, len_xs - 1) 451 | if start < 0 452 | let start += len_xs 453 | endif 454 | 455 | if len_xs <= start 456 | return default 457 | endif 458 | 459 | let l:Call = s:_get_unary_caller(a:f) 460 | for i in range(start, 0, -1) 461 | let x = a:xs[i] 462 | if l:Call(a:f, [x]) 463 | return i 464 | endif 465 | endfor 466 | return default 467 | endfunction 468 | 469 | " Similar to find_index but returns the list of indices satisfying the given expr. 470 | function! s:find_indices(xs, f, ...) abort 471 | let len_xs = len(a:xs) 472 | 473 | let start = get(a:000, 0, 0) 474 | " Emulate list[-n] 475 | if start < 0 476 | let start += len_xs 477 | endif 478 | 479 | if len_xs <= start 480 | return [] 481 | endif 482 | 483 | let l:Call = s:_get_unary_caller(a:f) 484 | let result = [] 485 | for i in range(start, len_xs - 1) 486 | let x = a:xs[i] 487 | if l:Call(a:f, [x]) 488 | call add(result, i) 489 | endif 490 | endfor 491 | return result 492 | endfunction 493 | 494 | " Return non-zero if a:list1 and a:list2 have any common item(s). 495 | " Return zero otherwise. 496 | function! s:has_common_items(list1, list2) abort 497 | return !empty(filter(copy(a:list1), 'index(a:list2, v:val) isnot -1')) 498 | endfunction 499 | 500 | function! s:intersect(list1, list2) abort 501 | let items = [] 502 | " for funcref 503 | for X in a:list1 504 | if index(a:list2, X) != -1 && index(items, X) == -1 505 | let items += [X] 506 | endif 507 | endfor 508 | return items 509 | endfunction 510 | 511 | " Similar to Ruby's group_by. 512 | function! s:group_by(xs, f) abort 513 | let result = {} 514 | let l:Call = s:_get_unary_caller(a:f) 515 | 516 | for l:X in a:xs 517 | let a_key = l:Call(a:f, [l:X]) 518 | let key = type(a_key) isnot type('') ? string(a_key) : a_key 519 | unlet a_key 520 | 521 | if has_key(result, key) 522 | call add(result[key], l:X) 523 | else 524 | let result[key] = [l:X] 525 | endif 526 | endfor 527 | 528 | return result 529 | endfunction 530 | 531 | function! s:binary_search(list, value, ...) abort 532 | let Predicate = a:0 >= 1 ? a:1 : 's:_basic_comparator' 533 | let dic = a:0 >= 2 ? a:2 : {} 534 | let start = 0 535 | let end = len(a:list) - 1 536 | 537 | while 1 538 | if start > end 539 | return -1 540 | endif 541 | 542 | let middle = (start + end) / 2 543 | 544 | let compared = call(Predicate, [a:value, a:list[middle]], dic) 545 | 546 | if compared < 0 547 | let end = middle - 1 548 | elseif compared > 0 549 | let start = middle + 1 550 | else 551 | return middle 552 | endif 553 | endwhile 554 | endfunction 555 | 556 | function! s:product(lists) abort 557 | let result = [[]] 558 | for pool in a:lists 559 | let tmp = [] 560 | for x in result 561 | let tmp += map(copy(pool), 'x + [v:val]') 562 | endfor 563 | let result = tmp 564 | endfor 565 | return result 566 | endfunction 567 | 568 | function! s:permutations(list, ...) abort 569 | if a:0 > 1 570 | throw 'vital: Data.List: too many arguments' 571 | endif 572 | let r = a:0 == 1 ? a:1 : len(a:list) 573 | if r > len(a:list) 574 | return [] 575 | elseif r < 0 576 | throw 'vital: Data.List: {r} must be non-negative integer' 577 | endif 578 | let n = len(a:list) 579 | let result = [] 580 | for indices in s:product(map(range(r), 'range(n)')) 581 | if len(s:uniq(indices)) == r 582 | call add(result, map(indices, 'a:list[v:val]')) 583 | endif 584 | endfor 585 | return result 586 | endfunction 587 | 588 | function! s:combinations(list, r) abort 589 | if a:r > len(a:list) 590 | return [] 591 | elseif a:r < 0 592 | throw 'vital: Data.List: {r} must be non-negative integer' 593 | endif 594 | let n = len(a:list) 595 | let result = [] 596 | for indices in s:permutations(range(n), a:r) 597 | if s:sort(copy(indices), 'a:a - a:b') == indices 598 | call add(result, map(indices, 'a:list[v:val]')) 599 | endif 600 | endfor 601 | return result 602 | endfunction 603 | 604 | 605 | " Takes the unary function of the funcref or the string expression. 606 | " Returns the caller function that is like call() . 607 | function! s:_get_unary_caller(f) abort 608 | return type(a:f) is type(function('function')) 609 | \ ? function('call') 610 | \ : function('s:_call_string_expr') 611 | endfunction 612 | 613 | " Takes the binary function of the funcref or the string expression. 614 | " if the binary function is the string expression, v:val and v:memo can be used in it. 615 | " Returns the caller function that is like call(), but it takes a tuple as an argument. 616 | function! s:_get_binary_caller(binary_f) abort 617 | return type(a:binary_f) is type(function('function')) 618 | \ ? function('call') 619 | \ : function('s:_call_binary_string_expr') 620 | endfunction 621 | 622 | " Returns the result of that apply the two element list to the binary string expression. 623 | " The binary expression has 'v:memo' and 'v:val'. 624 | function! s:_call_binary_string_expr(expr, pair) abort 625 | let expr = substitute(a:expr, 'v:memo', string(a:pair[0]), 'g') 626 | let expr = substitute(expr, 'v:val', string(a:pair[1]), 'g') 627 | return eval(expr) 628 | endfunction 629 | 630 | " This is similar to s:_get_binary_caller(), 631 | " but the behavior is different if a:binary_f is the string expression. 632 | " This uses s:_call_binary_string_expr_val_memo() . 633 | function! s:_get_binary_caller_(binary_f) abort 634 | return type(a:binary_f) is type(function('function')) 635 | \ ? function('call') 636 | \ : function('s:_call_binary_string_expr_val_memo') 637 | endfunction 638 | 639 | " This is similar to s:_call_binary_string_expr(), 640 | " but a:pair[0] is regarted as v:val, and a:pair[1] is regarted as v:memo. 641 | function! s:_call_binary_string_expr_val_memo(expr, pair) abort 642 | let x = substitute(a:expr, 'v:memo', string(a:pair[1]), 'g') 643 | let y = substitute(x, 'v:val', string(a:pair[0]), 'g') 644 | return eval(y) 645 | endfunction 646 | 647 | " Applies the string expression to the head element of a:args. 648 | " Returns the result. 649 | function! s:_call_string_expr(expr, args) abort 650 | return map([a:args[0]], a:expr)[0] 651 | endfunction 652 | 653 | let &cpo = s:save_cpo 654 | unlet s:save_cpo 655 | 656 | " vim:set et ts=2 sts=2 sw=2 tw=0: 657 | -------------------------------------------------------------------------------- /autoload/vital/_quickrun/Prelude.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not mofidify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_quickrun#Prelude#import() abort', printf("return map({'escape_pattern': '', 'is_funcref': '', 'path2directory': '', 'wcswidth': '', 'is_string': '', 'input_helper': '', 'is_number': '', 'is_cygwin': '', 'path2project_directory': '', 'strwidthpart_reverse': '', 'input_safe': '', 'is_list': '', 'truncate_skipping': '', 'glob': '', 'truncate': '', 'is_dict': '', 'set_default': '', 'is_numeric': '', 'getchar_safe': '', 'substitute_path_separator': '', 'is_mac': '', 'strwidthpart': '', 'getchar': '', 'is_unix': '', 'is_windows': '', 'globpath': '', 'escape_file_searching': '', 'is_float': '', 'smart_execute_command': ''}, \"vital#_quickrun#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:save_cpo = &cpo 11 | set cpo&vim 12 | 13 | function! s:glob(expr) abort 14 | return glob(a:expr, 1, 1) 15 | endfunction 16 | 17 | function! s:globpath(path, expr) abort 18 | return globpath(a:path, a:expr, 1, 1) 19 | endfunction 20 | 21 | " Wrapper functions for type(). 22 | let [ 23 | \ s:__TYPE_NUMBER, 24 | \ s:__TYPE_STRING, 25 | \ s:__TYPE_FUNCREF, 26 | \ s:__TYPE_LIST, 27 | \ s:__TYPE_DICT, 28 | \ s:__TYPE_FLOAT] = [ 29 | \ v:t_number, 30 | \ v:t_string, 31 | \ v:t_func, 32 | \ v:t_list, 33 | \ v:t_dict, 34 | \ v:t_float] 35 | 36 | " Number or Float 37 | function! s:is_numeric(Value) abort 38 | let _ = type(a:Value) 39 | return _ ==# s:__TYPE_NUMBER 40 | \ || _ ==# s:__TYPE_FLOAT 41 | endfunction 42 | 43 | " Number 44 | function! s:is_number(Value) abort 45 | return type(a:Value) ==# s:__TYPE_NUMBER 46 | endfunction 47 | 48 | " String 49 | function! s:is_string(Value) abort 50 | return type(a:Value) ==# s:__TYPE_STRING 51 | endfunction 52 | 53 | " Funcref 54 | function! s:is_funcref(Value) abort 55 | return type(a:Value) ==# s:__TYPE_FUNCREF 56 | endfunction 57 | 58 | " List 59 | function! s:is_list(Value) abort 60 | return type(a:Value) ==# s:__TYPE_LIST 61 | endfunction 62 | 63 | " Dictionary 64 | function! s:is_dict(Value) abort 65 | return type(a:Value) ==# s:__TYPE_DICT 66 | endfunction 67 | 68 | " Float 69 | function! s:is_float(Value) abort 70 | return type(a:Value) ==# s:__TYPE_FLOAT 71 | endfunction 72 | 73 | 74 | function! s:truncate_skipping(str, max, footer_width, separator) abort 75 | call s:_warn_deprecated('truncate_skipping', 'Data.String.truncate_skipping') 76 | 77 | let width = s:wcswidth(a:str) 78 | if width <= a:max 79 | let ret = a:str 80 | else 81 | let header_width = a:max - s:wcswidth(a:separator) - a:footer_width 82 | let ret = s:strwidthpart(a:str, header_width) . a:separator 83 | \ . s:strwidthpart_reverse(a:str, a:footer_width) 84 | endif 85 | 86 | return s:truncate(ret, a:max) 87 | endfunction 88 | 89 | function! s:truncate(str, width) abort 90 | " Original function is from mattn. 91 | " http://github.com/mattn/googlereader-vim/tree/master 92 | 93 | call s:_warn_deprecated('truncate', 'Data.String.truncate') 94 | 95 | if a:str =~# '^[\x00-\x7f]*$' 96 | return len(a:str) < a:width ? 97 | \ printf('%-'.a:width.'s', a:str) : strpart(a:str, 0, a:width) 98 | endif 99 | 100 | let ret = a:str 101 | let width = s:wcswidth(a:str) 102 | if width > a:width 103 | let ret = s:strwidthpart(ret, a:width) 104 | let width = s:wcswidth(ret) 105 | endif 106 | 107 | if width < a:width 108 | let ret .= repeat(' ', a:width - width) 109 | endif 110 | 111 | return ret 112 | endfunction 113 | 114 | function! s:strwidthpart(str, width) abort 115 | call s:_warn_deprecated('strwidthpart', 'Data.String.strwidthpart') 116 | 117 | if a:width <= 0 118 | return '' 119 | endif 120 | let ret = a:str 121 | let width = s:wcswidth(a:str) 122 | while width > a:width 123 | let char = matchstr(ret, '.$') 124 | let ret = ret[: -1 - len(char)] 125 | let width -= s:wcswidth(char) 126 | endwhile 127 | 128 | return ret 129 | endfunction 130 | function! s:strwidthpart_reverse(str, width) abort 131 | call s:_warn_deprecated('strwidthpart_reverse', 'Data.String.strwidthpart_reverse') 132 | 133 | if a:width <= 0 134 | return '' 135 | endif 136 | let ret = a:str 137 | let width = s:wcswidth(a:str) 138 | while width > a:width 139 | let char = matchstr(ret, '^.') 140 | let ret = ret[len(char) :] 141 | let width -= s:wcswidth(char) 142 | endwhile 143 | 144 | return ret 145 | endfunction 146 | 147 | function! s:wcswidth(str) abort 148 | call s:_warn_deprecated('wcswidth', 'Data.String.wcswidth') 149 | return strwidth(a:str) 150 | endfunction 151 | 152 | let s:is_windows = has('win32') " This means any versions of windows https://github.com/vim-jp/vital.vim/wiki/Coding-Rule#how-to-check-if-the-runtime-os-is-windows 153 | let s:is_cygwin = has('win32unix') 154 | let s:is_mac = !s:is_windows && !s:is_cygwin 155 | \ && (has('mac') || has('macunix') || has('gui_macvim') || 156 | \ (!isdirectory('/proc') && executable('sw_vers'))) 157 | let s:is_unix = has('unix') 158 | 159 | function! s:is_windows() abort 160 | return s:is_windows 161 | endfunction 162 | 163 | function! s:is_cygwin() abort 164 | return s:is_cygwin 165 | endfunction 166 | 167 | function! s:is_mac() abort 168 | return s:is_mac 169 | endfunction 170 | 171 | function! s:is_unix() abort 172 | return s:is_unix 173 | endfunction 174 | 175 | function! s:_warn_deprecated(name, alternative) abort 176 | try 177 | echohl Error 178 | echomsg 'Prelude.' . a:name . ' is deprecated! Please use ' . a:alternative . ' instead.' 179 | finally 180 | echohl None 181 | endtry 182 | endfunction 183 | 184 | function! s:smart_execute_command(action, word) abort 185 | execute a:action . ' ' . (a:word ==# '' ? '' : '`=a:word`') 186 | endfunction 187 | 188 | function! s:escape_file_searching(buffer_name) abort 189 | return escape(a:buffer_name, '*[]?{}, ') 190 | endfunction 191 | 192 | function! s:escape_pattern(str) abort 193 | call s:_warn_deprecated( 194 | \ 'escape_pattern', 195 | \ 'Data.String.escape_pattern', 196 | \) 197 | return escape(a:str, '~"\.^$[]*') 198 | endfunction 199 | 200 | function! s:getchar(...) abort 201 | let c = call('getchar', a:000) 202 | return type(c) == type(0) ? nr2char(c) : c 203 | endfunction 204 | 205 | function! s:getchar_safe(...) abort 206 | let c = s:input_helper('getchar', a:000) 207 | return type(c) == type('') ? c : nr2char(c) 208 | endfunction 209 | 210 | function! s:input_safe(...) abort 211 | return s:input_helper('input', a:000) 212 | endfunction 213 | 214 | function! s:input_helper(funcname, args) abort 215 | let success = 0 216 | if inputsave() !=# success 217 | throw 'vital: Prelude: inputsave() failed' 218 | endif 219 | try 220 | return call(a:funcname, a:args) 221 | finally 222 | if inputrestore() !=# success 223 | throw 'vital: Prelude: inputrestore() failed' 224 | endif 225 | endtry 226 | endfunction 227 | 228 | function! s:set_default(var, val) abort 229 | if !exists(a:var) || type({a:var}) != type(a:val) 230 | let {a:var} = a:val 231 | endif 232 | endfunction 233 | 234 | function! s:substitute_path_separator(path) abort 235 | return s:is_windows ? substitute(a:path, '\\', '/', 'g') : a:path 236 | endfunction 237 | 238 | function! s:path2directory(path) abort 239 | return s:substitute_path_separator(isdirectory(a:path) ? a:path : fnamemodify(a:path, ':p:h')) 240 | endfunction 241 | 242 | function! s:_path2project_directory_git(path) abort 243 | let parent = a:path 244 | 245 | while 1 246 | let path = parent . '/.git' 247 | if isdirectory(path) || filereadable(path) 248 | return parent 249 | endif 250 | let next = fnamemodify(parent, ':h') 251 | if next == parent 252 | return '' 253 | endif 254 | let parent = next 255 | endwhile 256 | endfunction 257 | 258 | function! s:_path2project_directory_svn(path) abort 259 | let search_directory = a:path 260 | let directory = '' 261 | 262 | let find_directory = s:escape_file_searching(search_directory) 263 | let d = finddir('.svn', find_directory . ';') 264 | if d ==# '' 265 | return '' 266 | endif 267 | 268 | let directory = fnamemodify(d, ':p:h:h') 269 | 270 | " Search parent directories. 271 | let parent_directory = s:path2directory( 272 | \ fnamemodify(directory, ':h')) 273 | 274 | if parent_directory !=# '' 275 | let d = finddir('.svn', parent_directory . ';') 276 | if d !=# '' 277 | let directory = s:_path2project_directory_svn(parent_directory) 278 | endif 279 | endif 280 | return directory 281 | endfunction 282 | 283 | function! s:_path2project_directory_others(vcs, path) abort 284 | let vcs = a:vcs 285 | let search_directory = a:path 286 | 287 | let find_directory = s:escape_file_searching(search_directory) 288 | let d = finddir(vcs, find_directory . ';') 289 | if d ==# '' 290 | return '' 291 | endif 292 | return fnamemodify(d, ':p:h:h') 293 | endfunction 294 | 295 | function! s:path2project_directory(path, ...) abort 296 | let is_allow_empty = get(a:000, 0, 0) 297 | let search_directory = s:path2directory(a:path) 298 | let directory = '' 299 | 300 | " Search VCS directory. 301 | for vcs in ['.git', '.bzr', '.hg', '.svn'] 302 | if vcs ==# '.git' 303 | let directory = s:_path2project_directory_git(search_directory) 304 | elseif vcs ==# '.svn' 305 | let directory = s:_path2project_directory_svn(search_directory) 306 | else 307 | let directory = s:_path2project_directory_others(vcs, search_directory) 308 | endif 309 | if directory !=# '' 310 | break 311 | endif 312 | endfor 313 | 314 | " Search project file. 315 | if directory ==# '' 316 | for d in ['build.xml', 'prj.el', '.project', 'pom.xml', 'package.json', 317 | \ 'Makefile', 'configure', 'Rakefile', 'NAnt.build', 318 | \ 'P4CONFIG', 'tags', 'gtags'] 319 | let d = findfile(d, s:escape_file_searching(search_directory) . ';') 320 | if d !=# '' 321 | let directory = fnamemodify(d, ':p:h') 322 | break 323 | endif 324 | endfor 325 | endif 326 | 327 | if directory ==# '' 328 | " Search /src/ directory. 329 | let base = s:substitute_path_separator(search_directory) 330 | if base =~# '/src/' 331 | let directory = base[: strridx(base, '/src/') + 3] 332 | endif 333 | endif 334 | 335 | if directory ==# '' && !is_allow_empty 336 | " Use original path. 337 | let directory = search_directory 338 | endif 339 | 340 | return s:substitute_path_separator(directory) 341 | endfunction 342 | 343 | let &cpo = s:save_cpo 344 | unlet s:save_cpo 345 | 346 | " vim:set et ts=2 sts=2 sw=2 tw=0: 347 | -------------------------------------------------------------------------------- /autoload/vital/_quickrun/Process.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not mofidify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_quickrun#Process#import() abort', printf("return map({'shellescape': '', 'has_vimproc': '', 'system': '', 'iconv': '', 'spawn': '', 'get_last_status': ''}, \"vital#_quickrun#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | " TODO: move all comments to doc file. 11 | " 12 | " 13 | " FIXME: This module name should be Vital.System ? 14 | " But the name has been already taken. 15 | 16 | let s:save_cpo = &cpo 17 | set cpo&vim 18 | 19 | 20 | " FIXME: Unfortunately, can't use s:_vital_loaded() for this purpose. 21 | " Because these variables are used when this script file is loaded. 22 | let s:is_windows = has('win32') " This means any versions of windows https://github.com/vim-jp/vital.vim/wiki/Coding-Rule#how-to-check-if-the-runtime-os-is-windows 23 | let s:is_unix = has('unix') 24 | 25 | let s:TYPE_DICT = type({}) 26 | let s:TYPE_LIST = type([]) 27 | let s:TYPE_STRING = type('') 28 | 29 | function! s:spawn(expr, ...) abort 30 | if type(a:expr) is s:TYPE_LIST 31 | let special = 1 32 | let cmdline = join(map(a:expr, 's:shellescape(v:val, special)'), ' ') 33 | elseif type(a:expr) is s:TYPE_STRING 34 | let cmdline = a:expr 35 | if a:0 && a:1 36 | " for :! command 37 | let cmdline = substitute(cmdline, '\([!%#]\|<[^<>]\+>\)', '\\\1', 'g') 38 | endif 39 | else 40 | throw 'vital: Process: invalid argument (value type:' . type(a:expr) . ')' 41 | endif 42 | if s:is_windows 43 | silent execute '!start' cmdline 44 | else 45 | silent execute '!' cmdline '&' 46 | endif 47 | return '' 48 | endfunction 49 | 50 | " iconv() wrapper for safety. 51 | function! s:iconv(expr, from, to) abort 52 | if a:from ==# '' || a:to ==# '' || a:from ==? a:to 53 | return a:expr 54 | endif 55 | let result = iconv(a:expr, a:from, a:to) 56 | return result !=# '' ? result : a:expr 57 | endfunction 58 | 59 | " Check vimproc. 60 | function! s:has_vimproc() abort 61 | if !exists('s:exists_vimproc') 62 | try 63 | call vimproc#version() 64 | let s:exists_vimproc = 1 65 | catch 66 | let s:exists_vimproc = 0 67 | endtry 68 | endif 69 | return s:exists_vimproc 70 | endfunction 71 | 72 | " * {command} [, {input} [, {timeout}]] 73 | " * {command} [, {dict}] 74 | " {dict} = { 75 | " use_vimproc: bool, 76 | " input: string, 77 | " timeout: bool, 78 | " background: bool, 79 | " } 80 | function! s:system(str, ...) abort 81 | " Process optional arguments at first 82 | " because use_vimproc is required later 83 | " for a:str argument. 84 | let input = '' 85 | let use_vimproc = s:has_vimproc() 86 | let background = 0 87 | let args = [] 88 | if a:0 ==# 1 89 | " {command} [, {dict}] 90 | " a:1 = {dict} 91 | if type(a:1) is s:TYPE_DICT 92 | if has_key(a:1, 'use_vimproc') 93 | let use_vimproc = a:1.use_vimproc 94 | endif 95 | if has_key(a:1, 'input') 96 | let args += [s:iconv(a:1.input, &encoding, 'char')] 97 | endif 98 | if use_vimproc && has_key(a:1, 'timeout') 99 | " ignores timeout unless you have vimproc. 100 | let args += [a:1.timeout] 101 | endif 102 | if has_key(a:1, 'background') 103 | let background = a:1.background 104 | endif 105 | elseif type(a:1) is s:TYPE_STRING 106 | let args += [s:iconv(a:1, &encoding, 'char')] 107 | else 108 | throw 'vital: Process: invalid argument (value type:' . type(a:1) . ')' 109 | endif 110 | elseif a:0 >= 2 111 | " {command} [, {input} [, {timeout}]] 112 | " a:000 = [{input} [, {timeout}]] 113 | let [input; rest] = a:000 114 | let input = s:iconv(input, &encoding, 'char') 115 | let args += [input] + rest 116 | endif 117 | 118 | " Process a:str argument. 119 | if type(a:str) is s:TYPE_LIST 120 | let expr = use_vimproc ? '"''" . v:val . "''"' : 's:shellescape(v:val)' 121 | let command = join(map(copy(a:str), expr), ' ') 122 | elseif type(a:str) is s:TYPE_STRING 123 | let command = a:str 124 | else 125 | throw 'vital: Process: invalid argument (value type:' . type(a:str) . ')' 126 | endif 127 | let args = [command] + args 128 | if background && (use_vimproc || !s:is_windows) 129 | if has('nvim') 130 | throw "vital: Process: neovim's system() doesn't support background(&) process (cmdline:" . string(a:str) . ')' 131 | endif 132 | let args[0] = args[0] . ' &' 133 | endif 134 | 135 | let funcname = use_vimproc ? 'vimproc#system' : 'system' 136 | let output = call(funcname, args) 137 | let output = s:iconv(output, 'char', &encoding) 138 | return output 139 | endfunction 140 | 141 | function! s:get_last_status() abort 142 | return s:has_vimproc() ? 143 | \ vimproc#get_last_status() : v:shell_error 144 | endfunction 145 | 146 | if s:is_windows 147 | function! s:shellescape(...) abort 148 | try 149 | let shellslash = &shellslash 150 | set noshellslash 151 | return call('shellescape', a:000) 152 | finally 153 | let &shellslash = shellslash 154 | endtry 155 | endfunction 156 | else 157 | function! s:shellescape(...) abort 158 | return call('shellescape', a:000) 159 | endfunction 160 | endif 161 | 162 | 163 | let &cpo = s:save_cpo 164 | unlet s:save_cpo 165 | 166 | " vim:set et ts=2 sts=2 sw=2 tw=0: 167 | -------------------------------------------------------------------------------- /autoload/vital/_quickrun/System/File.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not mofidify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_quickrun#System#File#import() abort', printf("return map({'copy_dir_vim': '', '_vital_depends': '', 'mkdir_nothrow': '', 'copy_exe': '', 'open': '', 'move_vim': '', 'copy': '', 'move': '', 'copy_dir_exe': '', 'copy_vim': '', 'move_exe': '', 'copy_dir': '', 'rmdir': '', '_vital_loaded': ''}, \"vital#_quickrun#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | " Utilities for file copy/move/mkdir/etc. 11 | 12 | let s:save_cpo = &cpo 13 | set cpo&vim 14 | 15 | function! s:_vital_loaded(V) abort 16 | let s:Filepath = a:V.import('System.Filepath') 17 | endfunction 18 | 19 | function! s:_vital_depends() abort 20 | return ['System.Filepath'] 21 | endfunction 22 | 23 | let s:is_unix = has('unix') 24 | let s:is_windows = has('win32') " This means any versions of windows https://github.com/vim-jp/vital.vim/wiki/Coding-Rule#how-to-check-if-the-runtime-os-is-windows 25 | let s:is_cygwin = has('win32unix') 26 | let s:is_mac = !s:is_windows && !s:is_cygwin 27 | \ && (has('mac') || has('macunix') || has('gui_macvim') || 28 | \ (!isdirectory('/proc') && executable('sw_vers'))) 29 | 30 | " Open a file. 31 | function! s:open(filename) abort 32 | let filename = fnamemodify(a:filename, ':p') 33 | 34 | " Detect desktop environment. 35 | if s:is_windows 36 | " For URI only. 37 | " Note: 38 | " # and % required to be escaped (:help cmdline-special) 39 | silent execute printf( 40 | \ '!start rundll32 url.dll,FileProtocolHandler %s', 41 | \ escape(filename, '#%'), 42 | \) 43 | elseif s:is_cygwin 44 | " Cygwin. 45 | call system(printf('%s %s', 'cygstart', 46 | \ shellescape(filename))) 47 | elseif executable('xdg-open') 48 | " Linux. 49 | call system(printf('%s %s &', 'xdg-open', 50 | \ shellescape(filename))) 51 | elseif exists('$KDE_FULL_SESSION') && $KDE_FULL_SESSION ==# 'true' 52 | " KDE. 53 | call system(printf('%s %s &', 'kioclient exec', 54 | \ shellescape(filename))) 55 | elseif exists('$GNOME_DESKTOP_SESSION_ID') 56 | " GNOME. 57 | call system(printf('%s %s &', 'gnome-open', 58 | \ shellescape(filename))) 59 | elseif executable('exo-open') 60 | " Xfce. 61 | call system(printf('%s %s &', 'exo-open', 62 | \ shellescape(filename))) 63 | elseif s:is_mac && executable('open') 64 | " Mac OS. 65 | call system(printf('%s %s &', 'open', 66 | \ shellescape(filename))) 67 | else 68 | " Give up. 69 | throw 'vital: System.File: open(): Not supported.' 70 | endif 71 | endfunction 72 | 73 | 74 | " Move a file. 75 | " Dispatch s:move_exe() or s:move_vim(). 76 | " FIXME: Currently s:move_vim() does not support 77 | " moving a directory. 78 | function! s:move(src, dest) abort 79 | if s:_has_move_exe() || isdirectory(a:src) 80 | return s:move_exe(a:src, a:dest) 81 | else 82 | return s:move_vim(a:src, a:dest) 83 | endif 84 | endfunction 85 | 86 | if s:is_unix 87 | function! s:_has_move_exe() abort 88 | return executable('mv') 89 | endfunction 90 | elseif s:is_windows 91 | function! s:_has_move_exe() abort 92 | return 1 93 | endfunction 94 | else 95 | function! s:_has_move_exe() abort 96 | throw 'vital: System.File: _has_move_exe(): your platform is not supported' 97 | endfunction 98 | endif 99 | 100 | " Move a file. 101 | " Implemented by external program. 102 | if s:is_unix 103 | function! s:move_exe(src, dest) abort 104 | if !s:_has_move_exe() 105 | return 0 106 | endif 107 | let [src, dest] = [a:src, a:dest] 108 | call system('mv ' . shellescape(src) . ' ' . shellescape(dest)) 109 | return !v:shell_error 110 | endfunction 111 | elseif s:is_windows 112 | function! s:move_exe(src, dest) abort 113 | if !s:_has_move_exe() 114 | return 0 115 | endif 116 | let [src, dest] = [a:src, a:dest] 117 | " Normalize successive slashes to one slash. 118 | let src = substitute(src, '[/\\]\+', '\', 'g') 119 | let dest = substitute(dest, '[/\\]\+', '\', 'g') 120 | " src must not have trailing '\'. 121 | let src = substitute(src, '\\$', '', 'g') 122 | let cmd_exe = (&shell =~? 'cmd\.exe$' ? '' : 'cmd /c ') 123 | call system(cmd_exe . 'move /y "' . src . '" "' . dest . '"') 124 | return !v:shell_error 125 | endfunction 126 | else 127 | function! s:move_exe() abort 128 | throw 'vital: System.File: move_exe(): your platform is not supported' 129 | endfunction 130 | endif 131 | 132 | " Move a file. 133 | " Implemented by pure Vim script. 134 | function! s:move_vim(src, dest) abort 135 | return !rename(a:src, a:dest) 136 | endfunction 137 | 138 | function! s:copy_dir(src, dest) abort 139 | if s:_has_copy_dir_exe() 140 | return s:copy_dir_exe(a:src, a:dest) 141 | else 142 | return s:copy_dir_vim(a:src, a:dest) 143 | endif 144 | endfunction 145 | 146 | " Copy a directory. 147 | " Implemented by external program. 148 | if s:is_unix 149 | function! s:copy_dir_exe(src, dest) abort 150 | if !s:_has_copy_dir_exe() 151 | return 0 152 | endif 153 | let [src, dest] = [a:src, a:dest] 154 | call system('cp -R ' . shellescape(src) . ' ' . shellescape(dest)) 155 | return !v:shell_error 156 | endfunction 157 | elseif s:is_windows 158 | function! s:copy_dir_exe(src, dest) abort 159 | if !s:_has_copy_dir_exe() 160 | return 0 161 | endif 162 | let src = s:_shellescape_robocopy(a:src) 163 | let dest = s:_shellescape_robocopy(a:dest) 164 | call system('robocopy /e "' . src . '" "' . dest . '"') 165 | return v:shell_error <# 8 166 | endfunction 167 | function! s:_shellescape_robocopy(path) abort 168 | let path = tr(a:path, '/', '\') 169 | let path = escape(path, '"') 170 | return '"' . path . '"' 171 | endfunction 172 | else 173 | function! s:copy_dir_exe() abort 174 | throw 'vital: System.File: copy_dir_exe(): your platform is not supported' 175 | endfunction 176 | endif 177 | 178 | " Copy a file. 179 | " Implemented by pure Vim script. 180 | function! s:copy_dir_vim(src, dest) abort 181 | if isdirectory(a:src) 182 | for src in glob(s:Filepath.join(a:src, '*'), 1, 1) 183 | let basename = s:Filepath.basename(src) 184 | let dest = s:Filepath.join(a:dest, basename) 185 | if !s:copy_dir_vim(src, dest) 186 | return 0 187 | endif 188 | endfor 189 | return 1 190 | elseif filereadable(a:src) 191 | return s:copy_vim(a:src, a:dest) 192 | else " XXX: ??? 193 | return 0 194 | endif 195 | endfunction 196 | 197 | if s:is_unix 198 | function! s:_has_copy_dir_exe() abort 199 | return executable('cp') 200 | endfunction 201 | elseif s:is_windows 202 | function! s:_has_copy_dir_exe() abort 203 | return executable('robocopy') 204 | endfunction 205 | else 206 | function! s:_has_copy_dir_exe() abort 207 | throw 'vital: System.File: copy_dir_exe(): ' 208 | \ . 'your platform is not supported' 209 | endfunction 210 | endif 211 | 212 | 213 | " Copy a file. 214 | " Dispatch s:copy_exe() or s:copy_vim(). 215 | function! s:copy(src, dest) abort 216 | if s:_has_copy_exe() 217 | return s:copy_exe(a:src, a:dest) 218 | else 219 | return s:copy_vim(a:src, a:dest) 220 | endif 221 | endfunction 222 | 223 | if s:is_unix 224 | function! s:_has_copy_exe() abort 225 | return executable('cp') 226 | endfunction 227 | elseif s:is_windows 228 | function! s:_has_copy_exe() abort 229 | return 1 230 | endfunction 231 | else 232 | function! s:_has_copy_exe() abort 233 | throw 'vital: System.File: _has_copy_exe(): your platform is not supported' 234 | endfunction 235 | endif 236 | 237 | " Copy a file. 238 | " Implemented by external program. 239 | if s:is_unix 240 | function! s:copy_exe(src, dest) abort 241 | if !s:_has_copy_exe() 242 | return 0 243 | endif 244 | let [src, dest] = [a:src, a:dest] 245 | call system('cp ' . shellescape(src) . ' ' . shellescape(dest)) 246 | return !v:shell_error 247 | endfunction 248 | elseif s:is_windows 249 | function! s:copy_exe(src, dest) abort 250 | if !s:_has_copy_exe() 251 | return 0 252 | endif 253 | let [src, dest] = [a:src, a:dest] 254 | let src = substitute(src, '/', '\', 'g') 255 | let dest = substitute(dest, '/', '\', 'g') 256 | let cmd_exe = (&shell =~? 'cmd\.exe$' ? '' : 'cmd /c ') 257 | call system(cmd_exe . 'copy /y "' . src . '" "' . dest . '"') 258 | return !v:shell_error 259 | endfunction 260 | else 261 | function! s:copy_exe() abort 262 | throw 'vital: System.File: copy_exe(): your platform is not supported' 263 | endfunction 264 | endif 265 | 266 | " Copy a file. 267 | " Implemented by pure Vim script. 268 | function! s:copy_vim(src, dest) abort 269 | let ret = writefile(readfile(a:src, 'b'), a:dest, 'b') 270 | if ret == -1 271 | return 0 272 | endif 273 | return 1 274 | endfunction 275 | 276 | " mkdir() but does not throw an exception. 277 | " Returns true if success. 278 | " Returns false if failure. 279 | function! s:mkdir_nothrow(...) abort 280 | try 281 | return call('mkdir', a:000) 282 | catch 283 | return 0 284 | endtry 285 | endfunction 286 | 287 | 288 | " Delete a file/directory. 289 | function! s:rmdir(path, ...) abort 290 | let flags = a:0 ? a:1 : '' 291 | let delete_flags = flags =~# 'r' ? 'rf' : 'd' 292 | let result = delete(a:path, delete_flags) 293 | if result == -1 294 | throw 'vital: System.File: rmdir(): cannot delete "' . a:path . '"' 295 | endif 296 | endfunction 297 | 298 | 299 | let &cpo = s:save_cpo 300 | unlet s:save_cpo 301 | 302 | " vim:set et ts=2 sts=2 sw=2 tw=0: 303 | -------------------------------------------------------------------------------- /autoload/vital/_quickrun/System/Filepath.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not mofidify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_quickrun#System#Filepath#import() abort', printf("return map({'path_separator': '', 'is_case_tolerant': '', 'dirname': '', 'abspath': '', 'relpath': '', 'realpath': '', 'unify_separator': '', 'to_slash': '', 'is_root_directory': '', 'split': '', 'path_extensions': '', 'unixpath': '', 'which': '', 'winpath': '', 'from_slash': '', 'join': '', 'separator': '', 'is_relative': '', 'basename': '', 'remove_last_separator': '', 'is_absolute': '', 'contains': ''}, \"vital#_quickrun#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | " You should check the following related builtin functions. 11 | " fnamemodify() 12 | " resolve() 13 | " simplify() 14 | 15 | let s:save_cpo = &cpo 16 | set cpo&vim 17 | 18 | let s:path_sep_pattern = (exists('+shellslash') ? '[\\/]' : '/') . '\+' 19 | 20 | " See https://github.com/vim-jp/vital.vim/wiki/Coding-Rule#how-to-check-if-the-runtime-os-is-windows 21 | let s:is_windows = has('win32') 22 | 23 | let s:is_cygwin = has('win32unix') 24 | let s:is_mac = !s:is_windows && !s:is_cygwin 25 | \ && (has('mac') || has('macunix') || has('gui_macvim') || 26 | \ (!isdirectory('/proc') && executable('sw_vers'))) 27 | let s:is_case_tolerant = filereadable(expand(':r') . '.VIM') 28 | 29 | if s:is_windows 30 | function! s:to_slash(path) abort 31 | return tr(a:path, '\', '/') 32 | endfunction 33 | else 34 | function! s:to_slash(path) abort 35 | return a:path 36 | endfunction 37 | endif 38 | 39 | if s:is_windows 40 | function! s:from_slash(path) abort 41 | return tr(a:path, '/', '\') 42 | endfunction 43 | else 44 | function! s:from_slash(path) abort 45 | return a:path 46 | endfunction 47 | endif 48 | 49 | 50 | " Get the directory separator. 51 | function! s:separator() abort 52 | return fnamemodify('.', ':p')[-1 :] 53 | endfunction 54 | 55 | " Get the path separator. 56 | let s:path_separator = s:is_windows ? ';' : ':' 57 | function! s:path_separator() abort 58 | return s:path_separator 59 | endfunction 60 | 61 | " Get the path extensions 62 | function! s:path_extensions() abort 63 | if !exists('s:path_extensions') 64 | if s:is_windows 65 | if exists('$PATHEXT') 66 | let pathext = $PATHEXT 67 | else 68 | " get default PATHEXT 69 | let pathext = matchstr(system('set pathext'), '\C^pathext=\zs.*\ze\n', 'i') 70 | endif 71 | let s:path_extensions = map(split(pathext, s:path_separator), 'tolower(v:val)') 72 | elseif s:is_cygwin 73 | " cygwin is not use $PATHEXT 74 | let s:path_extensions = ['', '.exe'] 75 | else 76 | let s:path_extensions = [''] 77 | endif 78 | endif 79 | return s:path_extensions 80 | endfunction 81 | 82 | " Convert all directory separators to "/". 83 | function! s:unify_separator(path) abort 84 | return substitute(a:path, s:path_sep_pattern, '/', 'g') 85 | endfunction 86 | 87 | " Get the full path of command. 88 | if exists('*exepath') 89 | function! s:which(str) abort 90 | return exepath(a:str) 91 | endfunction 92 | else 93 | function! s:which(command, ...) abort 94 | let pathlist = a:command =~# s:path_sep_pattern ? [''] : 95 | \ !a:0 ? split($PATH, s:path_separator) : 96 | \ type(a:1) == type([]) ? copy(a:1) : 97 | \ split(a:1, s:path_separator) 98 | 99 | let pathext = s:path_extensions() 100 | if index(pathext, '.' . tolower(fnamemodify(a:command, ':e'))) != -1 101 | let pathext = [''] 102 | endif 103 | 104 | let dirsep = s:separator() 105 | for dir in pathlist 106 | let head = dir ==# '' ? '' : dir . dirsep 107 | for ext in pathext 108 | let full = fnamemodify(head . a:command . ext, ':p') 109 | if filereadable(full) 110 | if s:is_case_tolerant() 111 | let full = glob(substitute( 112 | \ toupper(full), '\u:\@!', '[\0\L\0]', 'g'), 1) 113 | endif 114 | if full !=# '' 115 | return full 116 | endif 117 | endif 118 | endfor 119 | endfor 120 | 121 | return '' 122 | endfunction 123 | endif 124 | 125 | " Split the path with directory separator. 126 | " Note that this includes the drive letter of MS Windows. 127 | function! s:split(path) abort 128 | return split(a:path, s:path_sep_pattern) 129 | endfunction 130 | 131 | " Join the paths. 132 | " join('foo', 'bar') => 'foo/bar' 133 | " join('foo/', 'bar') => 'foo/bar' 134 | " join('/foo/', ['bar', 'buz/']) => '/foo/bar/buz/' 135 | function! s:join(...) abort 136 | let sep = s:separator() 137 | let path = '' 138 | for part in a:000 139 | let path .= sep . 140 | \ (type(part) is type([]) ? call('s:join', part) : 141 | \ part) 142 | unlet part 143 | endfor 144 | return substitute(path[1 :], s:path_sep_pattern, sep, 'g') 145 | endfunction 146 | 147 | " Check if the path is absolute path. 148 | if s:is_windows 149 | function! s:is_absolute(path) abort 150 | return a:path =~# '^[a-zA-Z]:[/\\]' 151 | endfunction 152 | else 153 | function! s:is_absolute(path) abort 154 | return a:path[0] ==# '/' 155 | endfunction 156 | endif 157 | 158 | function! s:is_relative(path) abort 159 | return !s:is_absolute(a:path) 160 | endfunction 161 | 162 | " Return the parent directory of the path. 163 | " NOTE: fnamemodify(path, ':h') does not return the parent directory 164 | " when path[-1] is the separator. 165 | function! s:dirname(path) abort 166 | let path = a:path 167 | let orig = a:path 168 | 169 | let path = s:remove_last_separator(path) 170 | if path ==# '' 171 | return orig " root directory 172 | endif 173 | 174 | let path = fnamemodify(path, ':h') 175 | return path 176 | endfunction 177 | 178 | " Return the basename of the path. 179 | " NOTE: fnamemodify(path, ':h') does not return basename 180 | " when path[-1] is the separator. 181 | function! s:basename(path) abort 182 | let path = a:path 183 | let orig = a:path 184 | 185 | let path = s:remove_last_separator(path) 186 | if path ==# '' 187 | return orig " root directory 188 | endif 189 | 190 | let path = fnamemodify(path, ':t') 191 | return path 192 | endfunction 193 | 194 | " Remove the separator at the end of a:path. 195 | function! s:remove_last_separator(path) abort 196 | let sep = s:separator() 197 | let pat = escape(sep, '\') . '\+$' 198 | return substitute(a:path, pat, '', '') 199 | endfunction 200 | 201 | 202 | " Return true if filesystem ignores alphabetic case of a filename. 203 | " Return false otherwise. 204 | function! s:is_case_tolerant() abort 205 | return s:is_case_tolerant 206 | endfunction 207 | 208 | 209 | function! s:abspath(path) abort 210 | if s:is_absolute(a:path) 211 | return a:path 212 | endif 213 | " Note: 214 | " the behavior of ':p' for non existing file path/directory is not defined 215 | return (filereadable(a:path) || isdirectory(a:path)) 216 | \ ? fnamemodify(a:path, ':p') 217 | \ : s:join(fnamemodify(getcwd(), ':p'), a:path) 218 | endfunction 219 | 220 | function! s:relpath(path) abort 221 | if s:is_relative(a:path) 222 | return a:path 223 | endif 224 | return fnamemodify(a:path, ':~:.') 225 | endfunction 226 | 227 | function! s:unixpath(path) abort 228 | return fnamemodify(a:path, ':gs?\\?/?') 229 | endfunction 230 | 231 | function! s:winpath(path) abort 232 | return fnamemodify(a:path, ':gs?/?\\?') 233 | endfunction 234 | 235 | if s:is_windows 236 | function! s:realpath(path) abort 237 | if exists('&shellslash') && &shellslash 238 | return s:unixpath(a:path) 239 | else 240 | return s:winpath(a:path) 241 | endif 242 | endfunction 243 | else 244 | function! s:realpath(path) abort 245 | return s:unixpath(a:path) 246 | endfunction 247 | endif 248 | 249 | if s:is_windows 250 | function! s:is_root_directory(path) abort 251 | return a:path =~# '^[a-zA-Z]:[/\\]$' 252 | endfunction 253 | else 254 | function! s:is_root_directory(path) abort 255 | return a:path ==# '/' 256 | endfunction 257 | endif 258 | 259 | function! s:contains(path, base) abort 260 | if a:path ==# '' || a:base ==# '' 261 | return 0 262 | endif 263 | let pathlist = s:split(a:path) 264 | let baselist = s:split(a:base) 265 | let pathlistlen = len(pathlist) 266 | let baselistlen = len(baselist) 267 | if pathlistlen < baselistlen 268 | return 0 269 | endif 270 | if baselistlen == 0 271 | return 1 272 | endif 273 | if s:is_case_tolerant 274 | call map(pathlist, 'tolower(v:val)') 275 | call map(baselist, 'tolower(v:val)') 276 | endif 277 | return pathlist[: baselistlen - 1] ==# baselist 278 | endfunction 279 | 280 | let &cpo = s:save_cpo 281 | unlet s:save_cpo 282 | 283 | " vim:set et ts=2 sts=2 sw=2 tw=0: 284 | -------------------------------------------------------------------------------- /autoload/vital/_quickrun/Vim/Buffer.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not mofidify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_quickrun#Vim#Buffer#import() abort', printf("return map({'parse_cmdarg': '', '_vital_depends': '', 'read_content': '', 'get_selected_text': '', 'is_cmdwin': '', 'edit_content': '', 'open': '', 'get_last_selected': '', '_vital_loaded': ''}, \"vital#_quickrun#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:save_cpo = &cpo 11 | set cpo&vim 12 | 13 | let s:t_funcref = type(function('tr')) 14 | let s:t_string = type('') 15 | let s:t_number = type(0) 16 | 17 | function! s:_vital_loaded(V) abort 18 | let s:V = a:V 19 | let s:Guard = s:V.import('Vim.Guard') 20 | endfunction 21 | 22 | function! s:_vital_depends() abort 23 | return ['Vim.Guard'] 24 | endfunction 25 | 26 | if exists('*getcmdwintype') 27 | function! s:is_cmdwin() abort 28 | return getcmdwintype() !=# '' 29 | endfunction 30 | else 31 | function! s:is_cmdwin() abort 32 | return bufname('%') ==# '[Command Line]' 33 | endfunction 34 | endif 35 | 36 | function! s:open(buffer, ...) abort 37 | if a:0 == 1 && (type(a:1) == s:t_string || type(a:1) == s:t_funcref) 38 | " For backward compatibility 39 | let options = {'opener': a:1} 40 | else 41 | let options = get(a:000, 0, {}) 42 | endif 43 | let options = extend({ 44 | \ 'mods': '', 45 | \ 'cmdarg': '', 46 | \ 'opener': empty(a:buffer) ? 'enew' : 'edit', 47 | \}, options 48 | \) 49 | 50 | let guard = s:Guard.store(['&wildignore']) 51 | try 52 | let &wildignore = '' 53 | if type(options.opener) == s:t_funcref 54 | let loaded = !bufloaded(a:buffer) 55 | call options.opener(a:buffer) 56 | elseif a:buffer is 0 || a:buffer is# '' 57 | let loaded = 1 58 | silent execute options.mods options.opener 59 | enew 60 | else 61 | let loaded = !bufloaded(a:buffer) 62 | if type(a:buffer) == s:t_string 63 | execute options.mods options.opener options.cmdarg '`=a:buffer`' 64 | elseif type(a:buffer) == s:t_number 65 | silent execute options.mods options.opener 66 | execute a:buffer 'buffer' 67 | else 68 | throw 'vital: Vim.Buffer: Unknown {buffer} type.' 69 | endif 70 | endif 71 | finally 72 | call guard.restore() 73 | endtry 74 | return loaded 75 | endfunction 76 | 77 | function! s:get_selected_text(...) abort 78 | echohl WarningMsg 79 | echom "vital: Vim.Buffer: Warning: s:get_selected_text() is deprecated. Use 's:get_last_selected()'." 80 | echohl None 81 | return call('s:get_last_selected', a:000) 82 | endfunction 83 | 84 | " Get the last selected text in visual mode 85 | " without using |gv| to avoid |textlock|. 86 | " NOTE: 87 | " * This function uses |gv| only when using |CTRL-V| 88 | " because |gv| is the only way to get selected text 89 | " when using $ . 90 | " Please see #192 for the details. 91 | " * If you don't care about |textlock|, 92 | " you can use simple version of this function. 93 | " https://github.com/vim-jp/vital.vim/commit/39aae80f3839fdbeebd838ff14d87327a6b889a9 94 | function! s:get_last_selected() abort 95 | if visualmode() ==# "\" 96 | let save = getreg('"', 1) 97 | let save_type = getregtype('"') 98 | try 99 | normal! gv""y 100 | return @" 101 | finally 102 | call setreg('"', save, save_type) 103 | endtry 104 | else 105 | let [begin, end] = [getpos("'<"), getpos("'>")] 106 | let lastchar = matchstr(getline(end[1])[end[2]-1 :], '.') 107 | if begin[1] ==# end[1] 108 | let lines = [getline(begin[1])[begin[2]-1 : end[2]-2]] 109 | else 110 | let lines = [getline(begin[1])[begin[2]-1 :]] 111 | \ + (end[1] - begin[1] <# 2 ? [] : getline(begin[1]+1, end[1]-1)) 112 | \ + [getline(end[1])[: end[2]-2]] 113 | endif 114 | return join(lines, "\n") . lastchar . (visualmode() ==# 'V' ? "\n" : '') 115 | endif 116 | endfunction 117 | 118 | function! s:read_content(content, ...) abort 119 | let options = extend({ 120 | \ 'tempfile': '', 121 | \ 'fileformat': '', 122 | \ 'encoding': '', 123 | \ 'binary': 0, 124 | \ 'nobinary': 0, 125 | \ 'bad': '', 126 | \ 'edit': 0, 127 | \ 'line': '', 128 | \ 'lockmarks': 0, 129 | \}, get(a:000, 0, {})) 130 | let tempfile = empty(options.tempfile) ? tempname() : options.tempfile 131 | let optnames = [ 132 | \ empty(options.fileformat) ? '' : '++ff=' . options.fileformat, 133 | \ empty(options.encoding) ? '' : '++enc=' . options.encoding, 134 | \ empty(options.binary) ? '' : '++bin', 135 | \ empty(options.nobinary) ? '' : '++nobin', 136 | \ empty(options.bad) ? '' : '++bad=' . options.bad, 137 | \ empty(options.edit) ? '' : '++edit', 138 | \] 139 | let optname = join(filter(optnames, '!empty(v:val)')) 140 | try 141 | call writefile(a:content, tempfile) 142 | execute printf('keepalt keepjumps %s%sread %s%s', 143 | \ options.lockmarks ? 'lockmarks ' : '', 144 | \ options.line, 145 | \ empty(optname) ? '' : optname . ' ', 146 | \ fnameescape(tempfile), 147 | \) 148 | finally 149 | call delete(tempfile) 150 | " To remove 'tempfile' from unlisted-buffer #439 151 | silent execute 'bwipeout!' fnameescape(tempfile) 152 | endtry 153 | endfunction 154 | 155 | function! s:edit_content(content, ...) abort 156 | let options = extend({ 157 | \ 'edit': 1, 158 | \ 'lockmarks': 0, 159 | \}, get(a:000, 0, {})) 160 | let guard = s:Guard.store(['&l:modifiable']) 161 | let saved_view = winsaveview() 162 | try 163 | let &l:modifiable=1 164 | silent execute printf( 165 | \ 'keepjumps %s%%delete _', 166 | \ options.lockmarks ? 'lockmarks ' : '', 167 | \) 168 | silent call s:read_content(a:content, options) 169 | silent execute printf( 170 | \ 'keepjumps %s1delete _', 171 | \ options.lockmarks ? 'lockmarks ' : '', 172 | \) 173 | finally 174 | keepjumps call winrestview(saved_view) 175 | call guard.restore() 176 | endtry 177 | setlocal nomodified 178 | endfunction 179 | 180 | function! s:parse_cmdarg(...) abort 181 | let cmdarg = get(a:000, 0, v:cmdarg) 182 | let options = {} 183 | if cmdarg =~# '++enc=' 184 | let options.encoding = matchstr(cmdarg, '++enc=\zs[^ ]\+\ze') 185 | endif 186 | if cmdarg =~# '++ff=' 187 | let options.fileformat = matchstr(cmdarg, '++ff=\zs[^ ]\+\ze') 188 | endif 189 | if cmdarg =~# '++bad=' 190 | let options.bad = matchstr(cmdarg, '++bad=\zs[^ ]\+\ze') 191 | endif 192 | if cmdarg =~# '++bin' 193 | let options.binary = 1 194 | endif 195 | if cmdarg =~# '++nobin' 196 | let options.nobinary = 1 197 | endif 198 | if cmdarg =~# '++edit' 199 | let options.edit = 1 200 | endif 201 | return options 202 | endfunction 203 | 204 | let &cpo = s:save_cpo 205 | unlet s:save_cpo 206 | 207 | " vim:set et ts=2 sts=2 sw=2 tw=0: 208 | -------------------------------------------------------------------------------- /autoload/vital/_quickrun/Vim/Guard.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not mofidify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_quickrun#Vim#Guard#import() abort', printf("return map({'_vital_depends': '', '_vital_created': '', 'store': '', '_vital_loaded': ''}, \"vital#_quickrun#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:save_cpo = &cpo 11 | set cpo&vim 12 | 13 | " Use a Funcref as a special term _UNDEFINED 14 | function! s:_undefined() abort 15 | return 'undefined' 16 | endfunction 17 | let s:_UNDEFINED = function('s:_undefined') 18 | 19 | function! s:_vital_loaded(V) abort 20 | let s:V = a:V 21 | let s:Prelude = s:V.import('Prelude') 22 | let s:List = s:V.import('Data.List') 23 | let s:Dict = s:V.import('Data.Dict') 24 | endfunction 25 | function! s:_vital_depends() abort 26 | return ['Prelude', 'Data.List', 'Data.Dict'] 27 | endfunction 28 | function! s:_vital_created(module) abort 29 | " define constant variables 30 | if !exists('s:const') 31 | let s:const = {} 32 | " These variables are provided for 7.4 or earlier. 33 | " But we leave this code to keep API. 34 | let s:const.is_local_variable_supported = 1 35 | let s:const.is_third_argument_of_getreg_supported = 1 36 | lockvar s:const 37 | endif 38 | call extend(a:module, s:const) 39 | endfunction 40 | function! s:_throw(msg) abort 41 | throw printf('vital: Vim.Guard: %s', a:msg) 42 | endfunction 43 | 44 | let s:option = {} 45 | function! s:_new_option(name) abort 46 | if a:name !~# '^&' 47 | call s:_throw(printf( 48 | \'An option name "%s" requires to be started from "&"', a:name 49 | \)) 50 | elseif !exists(a:name) 51 | call s:_throw(printf( 52 | \'An option name "%s" does not exist', a:name 53 | \)) 54 | endif 55 | let option = copy(s:option) 56 | let option.name = a:name 57 | let option.value = eval(a:name) 58 | return option 59 | endfunction 60 | function! s:option.restore() abort 61 | execute printf('let %s = %s', self.name, string(self.value)) 62 | endfunction 63 | 64 | let s:register = {} 65 | function! s:_new_register(name) abort 66 | if len(a:name) != 2 67 | call s:_throw(printf( 68 | \'A register name "%s" requires to be "@" + a single character', a:name 69 | \)) 70 | elseif a:name !~# '^@' 71 | call s:_throw(printf( 72 | \'A register name "%s" requires to be started from "@"', a:name 73 | \)) 74 | elseif a:name =~# '^@[:.%]$' 75 | call s:_throw(printf( 76 | \'A register name "%s" is read only', a:name 77 | \)) 78 | elseif a:name !~# '^@[@0-9a-zA-Z#=*+~_/-]$' 79 | call s:_throw(printf( 80 | \'A register name "%s" does not exist. See ":help let-register"', a:name 81 | \)) 82 | endif 83 | let name = a:name ==# '@@' ? '' : a:name[1] 84 | let register = copy(s:register) 85 | let register.name = name 86 | let register.value = getreg(name, 1, 1) 87 | let register.type = getregtype(name) 88 | return register 89 | endfunction 90 | function! s:register.restore() abort 91 | " https://github.com/vim/vim/commit/5a50c2255c447838d08d3b4895a3be3a41cd8eda 92 | if has('patch-7.4.243') || self.name !=# '=' 93 | call setreg(self.name, self.value, self.type) 94 | else 95 | let @= = self.value 96 | endif 97 | endfunction 98 | 99 | let s:environment = {} 100 | function! s:_new_environment(name) abort 101 | if a:name !~# '^\$' 102 | call s:_throw(printf( 103 | \'An environment variable name "%s" requires to be started from "$"', a:name 104 | \)) 105 | elseif !exists(a:name) 106 | call s:_throw(printf( 107 | \'An environment variable name "%s" does not exist. While Vim cannot unlet environment variable, it requires to exist', a:name 108 | \)) 109 | endif 110 | let environment = copy(s:environment) 111 | let environment.name = a:name 112 | let environment.value = eval(a:name) 113 | return environment 114 | endfunction 115 | function! s:environment.restore() abort 116 | execute printf('let %s = %s', self.name, string(self.value)) 117 | endfunction 118 | 119 | let s:variable = {} 120 | function! s:_new_variable(name, ...) abort 121 | if a:0 == 0 122 | let m = matchlist(a:name, '^\([bwtg]:\)\(.*\)$') 123 | if empty(m) 124 | call s:_throw(printf( 125 | \ join([ 126 | \ 'An variable name "%s" requires to start from b:, w:, t:, or g:', 127 | \ 'while no {namespace} is specified', 128 | \ ]), 129 | \ a:name, 130 | \)) 131 | endif 132 | let [prefix, name] = m[1 : 2] 133 | let namespace = eval(prefix) 134 | else 135 | let name = a:name 136 | let namespace = a:1 137 | endif 138 | let variable = copy(s:variable) 139 | let variable.name = name 140 | let variable.value = get(namespace, name, s:_UNDEFINED) 141 | let variable.value = 142 | \ type(variable.value) == type({}) || type(variable.value) == type([]) 143 | \ ? deepcopy(variable.value) 144 | \ : variable.value 145 | let variable._namespace = namespace 146 | return variable 147 | endfunction 148 | function! s:variable.restore() abort 149 | " unlet the variable to prevent variable type mis-match in case 150 | silent! unlet! self._namespace[self.name] 151 | if type(self.value) == type(s:_UNDEFINED) && self.value == s:_UNDEFINED 152 | " do nothing, leave the variable as undefined 153 | else 154 | let self._namespace[self.name] = self.value 155 | endif 156 | endfunction 157 | 158 | let s:instance = {} 159 | function! s:_new_instance(instance, ...) abort 160 | let shallow = get(a:000, 0, 0) 161 | if !s:Prelude.is_list(a:instance) && !s:Prelude.is_dict(a:instance) 162 | call s:_throw(printf( 163 | \'An instance "%s" requires to be List or Dictionary', string(a:instance) 164 | \)) 165 | endif 166 | let instance = copy(s:instance) 167 | let instance.instance = a:instance 168 | let instance.values = shallow ? copy(a:instance) : deepcopy(a:instance) 169 | return instance 170 | endfunction 171 | function! s:instance.restore() abort 172 | if s:Prelude.is_list(self.instance) 173 | call s:List.clear(self.instance) 174 | else 175 | call s:Dict.clear(self.instance) 176 | endif 177 | call extend(self.instance, self.values) 178 | endfunction 179 | 180 | let s:guard = {} 181 | function! s:store(targets) abort 182 | let resources = [] 183 | for meta in a:targets 184 | if s:Prelude.is_list(meta) 185 | if len(meta) == 1 186 | call add(resources, s:_new_instance(meta[0])) 187 | elseif len(meta) == 2 188 | if s:Prelude.is_string(meta[0]) 189 | call add(resources, call('s:_new_variable', meta)) 190 | else 191 | call add(resources, call('s:_new_instance', meta)) 192 | endif 193 | else 194 | call s:_throw('List assignment requires one or two elements') 195 | endif 196 | elseif type(meta) == type('') 197 | if meta =~# '^[bwtgls]:' 198 | " Note: 199 | " To improve an error message, handle l:XXX or s:XXX as well 200 | call add(resources, s:_new_variable(meta)) 201 | elseif meta =~# '^&' 202 | call add(resources, s:_new_option(meta)) 203 | elseif meta =~# '^@' 204 | call add(resources, s:_new_register(meta)) 205 | elseif meta =~# '^\$' 206 | call add(resources, s:_new_environment(meta)) 207 | else 208 | call s:_throw(printf( 209 | \ 'Unknown value "%s" was specified', 210 | \ meta 211 | \)) 212 | endif 213 | endif 214 | unlet meta 215 | endfor 216 | let guard = copy(s:guard) 217 | let guard._resources = resources 218 | return guard 219 | endfunction 220 | function! s:guard.restore() abort 221 | for resource in self._resources 222 | call resource.restore() 223 | endfor 224 | endfunction 225 | 226 | let &cpo = s:save_cpo 227 | unlet! s:save_cpo 228 | " vim:set et ts=2 sts=2 sw=2 tw=0 fdm=marker: 229 | -------------------------------------------------------------------------------- /autoload/vital/_quickrun/Vim/Message.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not mofidify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_quickrun#Vim#Message#import() abort', printf("return map({'capture': '', 'echomsg': '', 'echo': '', 'warn': '', 'get_hit_enter_max_length': '', 'error': ''}, \"vital#_quickrun#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:save_cpo = &cpo 11 | set cpo&vim 12 | 13 | 14 | 15 | function! s:echo(hl, msg) abort 16 | execute 'echohl' a:hl 17 | try 18 | echo a:msg 19 | finally 20 | echohl None 21 | endtry 22 | endfunction 23 | 24 | function! s:echomsg(hl, msg) abort 25 | execute 'echohl' a:hl 26 | try 27 | for m in split(a:msg, "\n") 28 | echomsg m 29 | endfor 30 | finally 31 | echohl None 32 | endtry 33 | endfunction 34 | 35 | function! s:error(msg) abort 36 | call s:echomsg('ErrorMsg', a:msg) 37 | endfunction 38 | 39 | function! s:warn(msg) abort 40 | call s:echomsg('WarningMsg', a:msg) 41 | endfunction 42 | 43 | function! s:capture(command) abort 44 | try 45 | redir => out 46 | silent execute a:command 47 | finally 48 | redir END 49 | endtry 50 | return out 51 | endfunction 52 | 53 | " * Get max length of |hit-enter|. 54 | " If a string length of a message is greater than the max length, 55 | " Vim waits for user input according to |hit-enter|. 56 | " XXX: Those fixed values may be different between different OSes? 57 | " Currently tested on only Windows. 58 | function! s:get_hit_enter_max_length() abort 59 | let maxlen = &columns * &cmdheight - 1 60 | if &ruler 61 | " TODO 62 | endif 63 | if &showcmd 64 | let maxlen -= 11 65 | endif 66 | return maxlen 67 | endfunction 68 | 69 | 70 | 71 | let &cpo = s:save_cpo 72 | unlet s:save_cpo 73 | 74 | " vim:set et ts=2 sts=2 sw=2 tw=0: 75 | -------------------------------------------------------------------------------- /autoload/vital/_quickrun/Vim/Type.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not mofidify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_quickrun#Vim#Type#import() abort', printf("return map({'is_comparable': '', '_vital_created': '', 'is_predicate': '', 'is_numeric': '', 'is_special': ''}, \"vital#_quickrun#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:typelist = ['number', 'string', 'func', 'list', 'dict', 'float', 'bool', 'none', 'job', 'channel', 'blob'] 11 | 12 | let s:types = {} 13 | let s:type_names = {} 14 | 15 | for s:idx in range(len(s:typelist)) 16 | let s:types[s:typelist[s:idx]] = s:idx 17 | let s:type_names[s:idx] = s:typelist[s:idx] 18 | endfor 19 | unlet s:idx 20 | unlet s:typelist 21 | 22 | lockvar 1 s:types 23 | lockvar 1 s:type_names 24 | 25 | function! s:_vital_created(module) abort 26 | let a:module.types = s:types 27 | let a:module.type_names = s:type_names 28 | endfunction 29 | 30 | 31 | function! s:is_numeric(value) abort 32 | let t = type(a:value) 33 | return t == s:types.number || t == s:types.float 34 | endfunction 35 | 36 | function! s:is_special(value) abort 37 | let t = type(a:value) 38 | return t == s:types.bool || t == s:types.none 39 | endfunction 40 | 41 | function! s:is_predicate(value) abort 42 | let t = type(a:value) 43 | return t == s:types.number || t == s:types.string || 44 | \ t == s:types.bool || t == s:types.none 45 | endfunction 46 | 47 | function! s:is_comparable(value1, value2) abort 48 | if !exists('s:is_comparable_cache') 49 | let s:is_comparable_cache = s:_make_is_comparable_cache() 50 | endif 51 | return s:is_comparable_cache[type(a:value1)][type(a:value2)] 52 | endfunction 53 | 54 | function! s:_make_is_comparable_cache() abort 55 | let vals = [ 56 | \ 0, '', function('type'), [], {}, 0.0, 57 | \ get(v:, 'false'), 58 | \ get(v:, 'null'), 59 | \ exists('*test_null_job') ? test_null_job() : 0, 60 | \ exists('*test_null_channel') ? test_null_channel() : 0, 61 | \ ] 62 | if has('patch-8.1.0735') 63 | let vals += [0z00] 64 | endif 65 | 66 | let result = [] 67 | for l:V1 in vals 68 | let result_V1 = [] 69 | let result += [result_V1] 70 | for l:V2 in vals 71 | try 72 | let _ = V1 == V2 73 | let result_V1 += [1] 74 | catch 75 | let result_V1 += [0] 76 | endtry 77 | unlet V2 78 | endfor 79 | unlet V1 80 | endfor 81 | return result 82 | endfunction 83 | -------------------------------------------------------------------------------- /autoload/vital/_quickrun/Vim/ViewTracer.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not mofidify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_quickrun#Vim#ViewTracer#import() abort', printf("return map({'jump': '', 'winnr': '', 'exists': '', 'find': '', 'trace_tabpage': '', 'tabnr': '', 'trace_window': ''}, \"vital#_quickrun#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:save_cpo = &cpo 11 | set cpo&vim 12 | 13 | function! s:trace_window(...) abort 14 | let tabnr = get(a:000, 0, tabpagenr()) 15 | let winnr = get(a:000, 1, winnr()) 16 | return {'window': gettabwinvar(tabnr, winnr, '')} 17 | endfunction 18 | 19 | function! s:trace_tabpage(...) abort 20 | let tabnr = get(a:000, 0, tabpagenr()) 21 | return {'tabpage': s:_gettabdict(tabnr)} 22 | endfunction 23 | 24 | function! s:find(handle) abort 25 | if has_key(a:handle, 'window') 26 | return s:_find_window(a:handle.window) 27 | elseif has_key(a:handle, 'tabpage') 28 | return s:_find_tabpage(a:handle.tabpage) 29 | endif 30 | return [0, 0] 31 | endfunction 32 | 33 | function! s:_find_window(scope_var) abort 34 | for tabnr in range(1, tabpagenr('$')) 35 | for winnr in range(1, tabpagewinnr(tabnr, '$')) 36 | if gettabwinvar(tabnr, winnr, '') is a:scope_var 37 | return [tabnr, winnr] 38 | endif 39 | endfor 40 | endfor 41 | return [0, 0] 42 | endfunction 43 | 44 | function! s:_find_tabpage(scope_var) abort 45 | for tabnr in range(1, tabpagenr('$')) 46 | if s:_gettabdict(tabnr) is a:scope_var 47 | return [tabnr, 0] 48 | endif 49 | endfor 50 | return [0, 0] 51 | endfunction 52 | 53 | function! s:exists(handle) abort 54 | return s:find(a:handle) != [0, 0] 55 | endfunction 56 | 57 | function! s:tabnr(handle) abort 58 | return s:find(a:handle)[0] 59 | endfunction 60 | 61 | function! s:winnr(handle) abort 62 | return s:find(a:handle)[1] 63 | endfunction 64 | 65 | function! s:jump(handle) abort 66 | let [tabnr, winnr] = s:find(a:handle) 67 | if tabnr == 0 68 | return 69 | endif 70 | call s:_move(tabnr, winnr) 71 | endfunction 72 | 73 | function! s:_move(tabnr, winnr) abort 74 | if a:tabnr != 0 && a:tabnr != tabpagenr() 75 | execute a:tabnr 'tabnext' 76 | endif 77 | if a:winnr != 0 && a:winnr != winnr() 78 | execute a:winnr 'wincmd w' 79 | endif 80 | endfunction 81 | 82 | function! s:_gettabdict(tabnr) abort 83 | return gettabvar(a:tabnr, '') 84 | endfunction 85 | 86 | let &cpo = s:save_cpo 87 | unlet s:save_cpo 88 | -------------------------------------------------------------------------------- /autoload/vital/quickrun.vim: -------------------------------------------------------------------------------- 1 | let s:plugin_name = expand(':t:r') 2 | let s:vital_base_dir = expand(':h') 3 | let s:project_root = expand(':h:h:h') 4 | let s:is_vital_vim = s:plugin_name is# 'vital' 5 | 6 | let s:loaded = {} 7 | let s:cache_sid = {} 8 | 9 | function! vital#{s:plugin_name}#new() abort 10 | return s:new(s:plugin_name) 11 | endfunction 12 | 13 | function! vital#{s:plugin_name}#import(...) abort 14 | if !exists('s:V') 15 | let s:V = s:new(s:plugin_name) 16 | endif 17 | return call(s:V.import, a:000, s:V) 18 | endfunction 19 | 20 | let s:Vital = {} 21 | 22 | function! s:new(plugin_name) abort 23 | let base = deepcopy(s:Vital) 24 | let base._plugin_name = a:plugin_name 25 | return base 26 | endfunction 27 | 28 | function! s:vital_files() abort 29 | if !exists('s:vital_files') 30 | let s:vital_files = map( 31 | \ s:is_vital_vim ? s:_global_vital_files() : s:_self_vital_files(), 32 | \ 'fnamemodify(v:val, ":p:gs?[\\\\/]?/?")') 33 | endif 34 | return copy(s:vital_files) 35 | endfunction 36 | let s:Vital.vital_files = function('s:vital_files') 37 | 38 | function! s:import(name, ...) abort dict 39 | let target = {} 40 | let functions = [] 41 | for a in a:000 42 | if type(a) == type({}) 43 | let target = a 44 | elseif type(a) == type([]) 45 | let functions = a 46 | endif 47 | unlet a 48 | endfor 49 | let module = self._import(a:name) 50 | if empty(functions) 51 | call extend(target, module, 'keep') 52 | else 53 | for f in functions 54 | if has_key(module, f) && !has_key(target, f) 55 | let target[f] = module[f] 56 | endif 57 | endfor 58 | endif 59 | return target 60 | endfunction 61 | let s:Vital.import = function('s:import') 62 | 63 | function! s:load(...) abort dict 64 | for arg in a:000 65 | let [name; as] = type(arg) == type([]) ? arg[: 1] : [arg, arg] 66 | let target = split(join(as, ''), '\W\+') 67 | let dict = self 68 | let dict_type = type({}) 69 | while !empty(target) 70 | let ns = remove(target, 0) 71 | if !has_key(dict, ns) 72 | let dict[ns] = {} 73 | endif 74 | if type(dict[ns]) == dict_type 75 | let dict = dict[ns] 76 | else 77 | unlet dict 78 | break 79 | endif 80 | endwhile 81 | if exists('dict') 82 | call extend(dict, self._import(name)) 83 | endif 84 | unlet arg 85 | endfor 86 | return self 87 | endfunction 88 | let s:Vital.load = function('s:load') 89 | 90 | function! s:unload() abort dict 91 | let s:loaded = {} 92 | let s:cache_sid = {} 93 | unlet! s:vital_files 94 | endfunction 95 | let s:Vital.unload = function('s:unload') 96 | 97 | function! s:exists(name) abort dict 98 | if a:name !~# '\v^\u\w*%(\.\u\w*)*$' 99 | throw 'vital: Invalid module name: ' . a:name 100 | endif 101 | return s:_module_path(a:name) isnot# '' 102 | endfunction 103 | let s:Vital.exists = function('s:exists') 104 | 105 | function! s:search(pattern) abort dict 106 | let paths = s:_extract_files(a:pattern, self.vital_files()) 107 | let modules = sort(map(paths, 's:_file2module(v:val)')) 108 | return s:_uniq(modules) 109 | endfunction 110 | let s:Vital.search = function('s:search') 111 | 112 | function! s:plugin_name() abort dict 113 | return self._plugin_name 114 | endfunction 115 | let s:Vital.plugin_name = function('s:plugin_name') 116 | 117 | function! s:_self_vital_files() abort 118 | let builtin = printf('%s/__%s__/', s:vital_base_dir, s:plugin_name) 119 | let installed = printf('%s/_%s/', s:vital_base_dir, s:plugin_name) 120 | let base = builtin . ',' . installed 121 | return split(globpath(base, '**/*.vim', 1), "\n") 122 | endfunction 123 | 124 | function! s:_global_vital_files() abort 125 | let pattern = 'autoload/vital/__*__/**/*.vim' 126 | return split(globpath(&runtimepath, pattern, 1), "\n") 127 | endfunction 128 | 129 | function! s:_extract_files(pattern, files) abort 130 | let tr = {'.': '/', '*': '[^/]*', '**': '.*'} 131 | let target = substitute(a:pattern, '\.\|\*\*\?', '\=tr[submatch(0)]', 'g') 132 | let regexp = printf('autoload/vital/[^/]\+/%s.vim$', target) 133 | return filter(a:files, 'v:val =~# regexp') 134 | endfunction 135 | 136 | function! s:_file2module(file) abort 137 | let filename = fnamemodify(a:file, ':p:gs?[\\/]?/?') 138 | let tail = matchstr(filename, 'autoload/vital/_\w\+/\zs.*\ze\.vim$') 139 | return join(split(tail, '[\\/]\+'), '.') 140 | endfunction 141 | 142 | " @param {string} name e.g. Data.List 143 | function! s:_import(name) abort dict 144 | if has_key(s:loaded, a:name) 145 | return copy(s:loaded[a:name]) 146 | endif 147 | let module = self._get_module(a:name) 148 | if has_key(module, '_vital_created') 149 | call module._vital_created(module) 150 | endif 151 | let export_module = filter(copy(module), 'v:key =~# "^\\a"') 152 | " Cache module before calling module.vital_loaded() to avoid cyclic 153 | " dependences but remove the cache if module._vital_loaded() fails. 154 | " let s:loaded[a:name] = export_module 155 | let s:loaded[a:name] = export_module 156 | if has_key(module, '_vital_loaded') 157 | try 158 | call module._vital_loaded(vital#{s:plugin_name}#new()) 159 | catch 160 | unlet s:loaded[a:name] 161 | throw 'vital: fail to call ._vital_loaded(): ' . v:exception . " from:\n" . s:_format_throwpoint(v:throwpoint) 162 | endtry 163 | endif 164 | return copy(s:loaded[a:name]) 165 | endfunction 166 | let s:Vital._import = function('s:_import') 167 | 168 | function! s:_format_throwpoint(throwpoint) abort 169 | let funcs = [] 170 | let stack = matchstr(a:throwpoint, '^function \zs.*\ze, line \d\+$') 171 | for line in split(stack, '\.\.') 172 | let m = matchlist(line, '^\%(\(\d\+\)_\)\?\(.\+\)\[\(\d\+\)\]$') 173 | if empty(m) 174 | call add(funcs, line) 175 | continue 176 | endif 177 | let [sid, name, lnum] = m[1:3] 178 | let attr = '' 179 | if !empty(sid) 180 | let name = printf('%d_%s', sid, name) 181 | endif 182 | let file = s:_get_file_by_func_name(name) 183 | call add(funcs, printf('function %s(...)%s Line:%d (%s)', name, attr, lnum, file)) 184 | endfor 185 | return join(funcs, "\n") 186 | endfunction 187 | 188 | function! s:_get_file_by_func_name(name) abort 189 | try 190 | redir => body 191 | silent execute 'verbose function' a:name 192 | finally 193 | redir END 194 | endtry 195 | let lines = split(body, "\n") 196 | let signature = matchstr(lines[0], '^\s*\zs.*') 197 | let file = matchstr(lines[1], '^\t\%(Last set from\|.\{-}:\)\s*\zs.*$') 198 | return substitute(file, '[/\\]\+', '/', 'g') 199 | endfunction 200 | 201 | " s:_get_module() returns module object wihch has all script local functions. 202 | function! s:_get_module(name) abort dict 203 | let funcname = s:_import_func_name(self.plugin_name(), a:name) 204 | try 205 | return call(funcname, []) 206 | catch /^Vim\%((\a\+)\)\?:E117:/ 207 | return s:_get_builtin_module(a:name) 208 | endtry 209 | endfunction 210 | 211 | function! s:_get_builtin_module(name) abort 212 | return s:sid2sfuncs(s:_module_sid(a:name)) 213 | endfunction 214 | 215 | if s:is_vital_vim 216 | " For vital.vim, we can use s:_get_builtin_module directly 217 | let s:Vital._get_module = function('s:_get_builtin_module') 218 | else 219 | let s:Vital._get_module = function('s:_get_module') 220 | endif 221 | 222 | function! s:_import_func_name(plugin_name, module_name) abort 223 | return printf('vital#_%s#%s#import', a:plugin_name, s:_dot_to_sharp(a:module_name)) 224 | endfunction 225 | 226 | function! s:_module_sid(name) abort 227 | let path = s:_module_path(a:name) 228 | if !filereadable(path) 229 | throw 'vital: module not found: ' . a:name 230 | endif 231 | let vital_dir = s:is_vital_vim ? '__\w\+__' : printf('_\{1,2}%s\%%(__\)\?', s:plugin_name) 232 | let base = join([vital_dir, ''], '[/\\]\+') 233 | let p = base . substitute('' . a:name, '\.', '[/\\\\]\\+', 'g') 234 | let sid = s:_sid(path, p) 235 | if !sid 236 | call s:_source(path) 237 | let sid = s:_sid(path, p) 238 | if !sid 239 | throw printf('vital: cannot get from path: %s', path) 240 | endif 241 | endif 242 | return sid 243 | endfunction 244 | 245 | function! s:_module_path(name) abort 246 | return get(s:_extract_files(a:name, s:vital_files()), 0, '') 247 | endfunction 248 | 249 | function! s:_module_sid_base_dir() abort 250 | return s:is_vital_vim ? &rtp : s:project_root 251 | endfunction 252 | 253 | function! s:_dot_to_sharp(name) abort 254 | return substitute(a:name, '\.', '#', 'g') 255 | endfunction 256 | 257 | function! s:_source(path) abort 258 | execute 'source' fnameescape(a:path) 259 | endfunction 260 | 261 | " @vimlint(EVL102, 1, l:_) 262 | " @vimlint(EVL102, 1, l:__) 263 | function! s:_sid(path, filter_pattern) abort 264 | let unified_path = s:_unify_path(a:path) 265 | if has_key(s:cache_sid, unified_path) 266 | return s:cache_sid[unified_path] 267 | endif 268 | for line in filter(split(s:_execute(':scriptnames'), "\n"), 'v:val =~# a:filter_pattern') 269 | let [_, sid, path; __] = matchlist(line, '^\s*\(\d\+\):\s\+\(.\+\)\s*$') 270 | if s:_unify_path(path) is# unified_path 271 | let s:cache_sid[unified_path] = sid 272 | return s:cache_sid[unified_path] 273 | endif 274 | endfor 275 | return 0 276 | endfunction 277 | 278 | " We want to use a execute() builtin function instead of s:_execute(), 279 | " however there is a bug in execute(). 280 | " execute() returns empty string when it is called in 281 | " completion function of user defined ex command. 282 | " https://github.com/vim-jp/issues/issues/1129 283 | function! s:_execute(cmd) abort 284 | let [save_verbose, save_verbosefile] = [&verbose, &verbosefile] 285 | set verbose=0 verbosefile= 286 | redir => res 287 | silent! execute a:cmd 288 | redir END 289 | let [&verbose, &verbosefile] = [save_verbose, save_verbosefile] 290 | return res 291 | endfunction 292 | 293 | if filereadable(expand(':r') . '.VIM') " is case-insensitive or not 294 | let s:_unify_path_cache = {} 295 | " resolve() is slow, so we cache results. 296 | " Note: On windows, vim can't expand path names from 8.3 formats. 297 | " So if getting full path via and $HOME was set as 8.3 format, 298 | " vital load duplicated scripts. Below's :~ avoid this issue. 299 | function! s:_unify_path(path) abort 300 | if has_key(s:_unify_path_cache, a:path) 301 | return s:_unify_path_cache[a:path] 302 | endif 303 | let value = tolower(fnamemodify(resolve(fnamemodify( 304 | \ a:path, ':p')), ':~:gs?[\\/]?/?')) 305 | let s:_unify_path_cache[a:path] = value 306 | return value 307 | endfunction 308 | else 309 | function! s:_unify_path(path) abort 310 | return resolve(fnamemodify(a:path, ':p:gs?[\\/]?/?')) 311 | endfunction 312 | endif 313 | 314 | " copied and modified from Vim.ScriptLocal 315 | let s:SNR = join(map(range(len("\")), '"[\\x" . printf("%0x", char2nr("\"[v:val])) . "]"'), '') 316 | function! s:sid2sfuncs(sid) abort 317 | let fs = split(s:_execute(printf(':function /^%s%s_', s:SNR, a:sid)), "\n") 318 | let r = {} 319 | let pattern = printf('\m^function\s%d_\zs\w\{-}\ze(', a:sid) 320 | for fname in map(fs, 'matchstr(v:val, pattern)') 321 | let r[fname] = function(s:_sfuncname(a:sid, fname)) 322 | endfor 323 | return r 324 | endfunction 325 | 326 | "" Return funcname of script local functions with SID 327 | function! s:_sfuncname(sid, funcname) abort 328 | return printf('%s_%s', a:sid, a:funcname) 329 | endfunction 330 | 331 | if exists('*uniq') 332 | function! s:_uniq(list) abort 333 | return uniq(a:list) 334 | endfunction 335 | else 336 | function! s:_uniq(list) abort 337 | let i = len(a:list) - 1 338 | while 0 < i 339 | if a:list[i] ==# a:list[i - 1] 340 | call remove(a:list, i) 341 | endif 342 | let i -= 1 343 | endwhile 344 | return a:list 345 | endfunction 346 | endif 347 | -------------------------------------------------------------------------------- /autoload/vital/quickrun.vital: -------------------------------------------------------------------------------- 1 | quickrun 2 | 833c903179d5601704e4cd8c0d325984a2ccb548 3 | 4 | Data.List 5 | System.File 6 | System.Filepath 7 | Process 8 | Vim.Message 9 | ConcurrentProcess 10 | Vim.ViewTracer 11 | Vim.Buffer 12 | -------------------------------------------------------------------------------- /plugin/quickrun.vim: -------------------------------------------------------------------------------- 1 | " Run commands quickly. 2 | " Author : thinca 3 | " License: zlib License 4 | 5 | if exists('g:loaded_quickrun') 6 | finish 7 | endif 8 | let g:loaded_quickrun = 1 9 | 10 | 11 | command! -nargs=* -range=0 -complete=customlist,quickrun#command#complete 12 | \ QuickRun call quickrun#command#execute(, , , ) 13 | 14 | 15 | nnoremap (quickrun-op) 16 | \ :set operatorfunc=quickrun#operatorg@ 17 | 18 | nnoremap (quickrun) :QuickRun -mode n 19 | vnoremap (quickrun) :QuickRun -mode v 20 | -------------------------------------------------------------------------------- /test/.themisrc: -------------------------------------------------------------------------------- 1 | call themis#option('recursive', 1) 2 | 3 | call themis#helper('command').with(themis#helper('assert')) 4 | 5 | let s:aliases = { 6 | \ 'Session': quickrun#session#new({}), 7 | \ } 8 | for s:kind in quickrun#module#get_kinds() 9 | for s:module in quickrun#module#get(s:kind) 10 | let s:aliases[printf('kind/%s', s:module.name)] = s:module 11 | endfor 12 | endfor 13 | call themis#func_alias(s:aliases) 14 | 15 | 16 | " runner/puppet for outputter test 17 | let g:PuppetRunner = { 18 | \ 'config': { 19 | \ 'result': [], 20 | \ } 21 | \ } 22 | 23 | function g:PuppetRunner.run(commands, input, session) abort 24 | if !empty(self.config.result) 25 | call a:session.output(remove(self.config.result, 0)) 26 | endif 27 | if empty(self.config.result) 28 | call a:session.finish() 29 | else 30 | let self._key = a:session.continue() 31 | endif 32 | endfunction 33 | 34 | function g:PuppetRunner.next() abort 35 | if !has_key(self, '_key') 36 | return 37 | endif 38 | let session = quickrun#session#get(self._key) 39 | let result = session.runner.config.result 40 | call session.output(remove(result, 0)) 41 | if empty(result) 42 | call session.finish() 43 | endif 44 | endfunction 45 | -------------------------------------------------------------------------------- /test/outputter/buffer.vimspec: -------------------------------------------------------------------------------- 1 | Describe outputter/buffer 2 | function s:session(outputter_config, result, ...) abort 3 | let outputter_config = extend({'running_mark': ''}, a:outputter_config) 4 | let config = { 5 | \ 'runner': [g:PuppetRunner, {'result': a:result}], 6 | \ 'outputter': ['buffer', outputter_config], 7 | \ 'exec': '', 8 | \ } 9 | if a:0 10 | call extend(config, a:1) 11 | endif 12 | return quickrun#session#new(config) 13 | endfunction 14 | 15 | Before each 16 | if v:version < 802 17 | Skip requires Vim 8.2 18 | endif 19 | End 20 | 21 | After each 22 | silent! bwipeout quickrun://output 23 | End 24 | 25 | It opens a result buffer and outputs the result to it 26 | let result = ['foo', "bar\nbuz", '', "hoge\npiyo"] 27 | let session = s:session({}, result) 28 | call session.run() 29 | Assert True(bufexists('quickrun://output')) 30 | let bufnr = bufnr('quickrun://output') 31 | Assert Equal(getbufline(bufnr, 1, '$'), ['foo']) 32 | call session.runner.next() 33 | Assert Equal(getbufline(bufnr, 1, '$'), ['foobar', 'buz']) 34 | call session.runner.next() 35 | Assert Equal(getbufline(bufnr, 1, '$'), ['foobar', 'buz']) 36 | call session.runner.next() 37 | Assert Equal(getbufline(bufnr, 1, '$'), ['foobar', 'buzhoge', 'piyo']) 38 | End 39 | 40 | Describe opening a buffer 41 | Context when there are no target buffer 42 | It opens a new window with a new buffer 43 | Assert False(bufexists('quickrun://output')) 44 | let session = s:session({}, []) 45 | call session.run() 46 | Assert True(bufexists('quickrun://output')) 47 | let bufnr = bufnr('quickrun://output') 48 | Assert Equal(len(win_findbuf(bufnr)), 1) 49 | End 50 | End 51 | 52 | Context when there is alerady a target buffer 53 | Before each 54 | let session = s:session({}, ['foo']) 55 | call session.run() 56 | let bufnr = bufnr('quickrun://output') 57 | End 58 | 59 | Context when the buffer is opend 60 | It reuses 61 | Assert Equal(len(win_findbuf(bufnr)), 1) 62 | let session = s:session({}, ['bar']) 63 | call session.run() 64 | Assert Equal(len(win_findbuf(bufnr)), 1) 65 | Assert Equal(getbufline(bufnr, 1, '$'), ['bar']) 66 | End 67 | End 68 | 69 | Context when the buffer is not opened 70 | Before each 71 | for winid in win_findbuf(bufnr) 72 | call win_execute(winid, 'noautocmd close') 73 | endfor 74 | End 75 | 76 | It opens a new window with an existed buffer 77 | Assert Equal(len(win_findbuf(bufnr)), 0) 78 | let session = s:session({}, ['bar']) 79 | call session.run() 80 | Assert Equal(bufnr('quickrun://output'), bufnr) 81 | Assert Equal(len(win_findbuf(bufnr)), 1) 82 | Assert Equal(getbufline(bufnr, 1, '$'), ['bar']) 83 | End 84 | End 85 | End 86 | 87 | Context when buffer name contains special characters 88 | Before each 89 | let config = {'bufname': '[v]'} 90 | let session = s:session(config, ['foo']) 91 | call session.run() 92 | let bufnr = bufnr('^[[]v[]]$') 93 | End 94 | 95 | Context when the buffer is opend 96 | It reuses 97 | Assert Equal(len(win_findbuf(bufnr)), 1) 98 | let session = s:session(config, ['bar']) 99 | call session.run() 100 | Assert Equal(len(win_findbuf(bufnr)), 1) 101 | Assert Equal(getbufline(bufnr, 1, '$'), ['bar']) 102 | End 103 | End 104 | 105 | Context when the buffer is not opened 106 | Before each 107 | for winid in win_findbuf(bufnr) 108 | call win_execute(winid, 'noautocmd close') 109 | endfor 110 | End 111 | 112 | It opens a new window with an existed buffer 113 | Assert Equal(len(win_findbuf(bufnr)), 0) 114 | let session = s:session(config, ['bar']) 115 | call session.run() 116 | Assert Equal(bufnr('[[]v[]]$'), bufnr) 117 | Assert Equal(len(win_findbuf(bufnr)), 1) 118 | Assert Equal(getbufline(bufnr, 1, '$'), ['bar']) 119 | End 120 | End 121 | End 122 | 123 | Context when cmdwin is opened 124 | " TODO: 125 | " How to open a cmdwin from Vim script? 126 | End 127 | 128 | Describe hook 129 | Context when a buffer is opened 130 | It invokes 'outputter_buffer_opened' hook 131 | let hook = { 132 | \ 'on_outputter_buffer_opened': 133 | \ { session -> extend(session, {'hook_called': 1}) } 134 | \ } 135 | let result = ['foo'] 136 | 137 | let session = s:session({}, result, {'hooks': [hook]}) 138 | let session.hook_called = 0 139 | Assert False(session.hook_called) 140 | call session.run() 141 | Assert True(session.hook_called) 142 | End 143 | End 144 | 145 | Context when a buffer is already opened 146 | It does not invoke 'outputter_buffer_opened' hook 147 | let result = ['foo'] 148 | 149 | " Open the result buffer 150 | let session = s:session({}, result) 151 | call session.run() 152 | 153 | let hook = { 154 | \ 'on_outputter_buffer_opened': 155 | \ { session -> extend(session, {'hook_called': 1}) } 156 | \ } 157 | 158 | let session = s:session({}, result, {'hooks': [hook]}) 159 | let session.hook_called = 0 160 | Assert False(session.hook_called) 161 | call session.run() 162 | Assert False(session.hook_called) 163 | End 164 | End 165 | End 166 | End 167 | 168 | Describe scrolling a window with the result 169 | It scrolls to the tail 170 | let result = ["1\n2\n3\n", "4\n5\n", ''] 171 | let session = s:session({'opener': '3new'}, result) 172 | call session.run() 173 | let bufnr = bufnr('quickrun://output') 174 | let winid = win_findbuf(bufnr)[0] 175 | call win_gotoid(winid) 176 | Assert Equal(line('.'), 4) 177 | call session.runner.next() 178 | Assert Equal(line('.'), 6) 179 | call session.runner.next() 180 | Assert Equal(line('.'), 1) 181 | 182 | let session = s:session({'opener': '3new'}, ["1\n2\n3\n"]) 183 | call session.run() 184 | call session.runner.next() 185 | Assert Equal(line('.'), 1) 186 | End 187 | End 188 | 189 | Describe adjusting fileformat 190 | Context when there is no line breaks 191 | It sets 'fileformat' to `unix` 192 | let result = ['foobarbuz'] 193 | let session = s:session({}, result) 194 | call session.run() 195 | Assert True(bufexists('quickrun://output')) 196 | let bufnr = bufnr('quickrun://output') 197 | Assert Equal(getbufvar(bufnr, '&fileformat'), 'unix') 198 | Assert Equal(getbufline(bufnr, 1, '$'), ['foobarbuz']) 199 | End 200 | End 201 | 202 | Context when all lines are ended by LF 203 | It sets 'fileformat' to `unix` 204 | let result = ["foo\nbar\nbuz\n"] 205 | let session = s:session({}, result) 206 | call session.run() 207 | Assert True(bufexists('quickrun://output')) 208 | let bufnr = bufnr('quickrun://output') 209 | Assert Equal(getbufvar(bufnr, '&fileformat'), 'unix') 210 | Assert Equal(getbufline(bufnr, 1, '$'), ['foo', 'bar', 'buz', '']) 211 | End 212 | End 213 | 214 | Context when all lines are ended by CRLF 215 | It sets 'fileformat' to `dos` 216 | let result = ["foo\r\nbar\r\nbuz\r\n"] 217 | let session = s:session({}, result) 218 | call session.run() 219 | Assert True(bufexists('quickrun://output')) 220 | let bufnr = bufnr('quickrun://output') 221 | Assert Equal(getbufvar(bufnr, '&fileformat'), 'dos') 222 | Assert Equal(getbufline(bufnr, 1, '$'), ['foo', 'bar', 'buz', '']) 223 | End 224 | End 225 | 226 | Context when all lines are ended by CR 227 | It sets 'fileformat' to `mac` 228 | let result = ["foo\rbar\rbuz\r"] 229 | let session = s:session({}, result) 230 | call session.run() 231 | Assert True(bufexists('quickrun://output')) 232 | let bufnr = bufnr('quickrun://output') 233 | Assert Equal(getbufvar(bufnr, '&fileformat'), 'mac') 234 | Assert Equal(getbufline(bufnr, 1, '$'), ['foo', 'bar', 'buz', '']) 235 | End 236 | End 237 | 238 | Context when end of lines are mixed by LF and CRLF 239 | It sets 'fileformat' to unix and ^M at the end of CRLF line 240 | let result = ["foo\nbar\r\nbuz\n"] 241 | let session = s:session({}, result) 242 | call session.run() 243 | Assert True(bufexists('quickrun://output')) 244 | let bufnr = bufnr('quickrun://output') 245 | Assert Equal(getbufvar(bufnr, '&fileformat'), 'unix') 246 | Assert Equal(getbufline(bufnr, 1, '$'), ['foo', "bar\r", 'buz', '']) 247 | End 248 | 249 | Context when CRLF appears first then appears LF after 250 | It is `dos` first but changes to `unix` after 251 | let result = ["foo\r\nbar\r\n", "buz\nqux"] 252 | let session = s:session({}, result) 253 | call session.run() 254 | Assert True(bufexists('quickrun://output')) 255 | let bufnr = bufnr('quickrun://output') 256 | Assert Equal(getbufvar(bufnr, '&fileformat'), 'dos') 257 | Assert Equal(getbufline(bufnr, 1, '$'), ['foo', 'bar', '']) 258 | call session.runner.next() 259 | Assert Equal(getbufvar(bufnr, '&fileformat'), 'unix') 260 | Assert Equal(getbufline(bufnr, 1, '$'), ["foo\r", "bar\r", 'buz', 'qux']) 261 | End 262 | End 263 | 264 | Context when CR appears first then appears CRLF then appears LF after 265 | It is `mac` first but changes to `unix` at last 266 | let result = ["foo\r", "bar\r\n", "buz\nqux"] 267 | let session = s:session({}, result) 268 | call session.run() 269 | Assert True(bufexists('quickrun://output')) 270 | let bufnr = bufnr('quickrun://output') 271 | Assert Equal(getbufvar(bufnr, '&fileformat'), 'mac') 272 | Assert Equal(getbufline(bufnr, 1, '$'), ['foo', '']) 273 | call session.runner.next() 274 | Assert Equal(getbufvar(bufnr, '&fileformat'), 'mac') 275 | Assert Equal(getbufline(bufnr, 1, '$'), ['foo', "bar", "\n"]) 276 | call session.runner.next() 277 | Assert Equal(getbufvar(bufnr, '&fileformat'), 'unix') 278 | Assert Equal(getbufline(bufnr, 1, '$'), ["foo\rbar\r", 'buz', 'qux']) 279 | End 280 | End 281 | End 282 | 283 | Context when end of lines are mixed by CR, LF, and CRLF 284 | It sets 'fileformat' to unix and CR line is joined by ^M 285 | let result = ["foo\nbar\r\nbuz\rqux"] 286 | let session = s:session({}, result) 287 | call session.run() 288 | Assert True(bufexists('quickrun://output')) 289 | let bufnr = bufnr('quickrun://output') 290 | Assert Equal(getbufvar(bufnr, '&fileformat'), 'unix') 291 | Assert Equal(getbufline(bufnr, 1, '$'), ['foo', "bar\r", "buz\rqux"]) 292 | End 293 | 294 | Context when CR appears first then appears LF after 295 | It is `dos` first but changes to `unix` after 296 | let result = ["foo\rbar\r", "buz\nqux"] 297 | let session = s:session({}, result) 298 | call session.run() 299 | Assert True(bufexists('quickrun://output')) 300 | let bufnr = bufnr('quickrun://output') 301 | Assert Equal(getbufvar(bufnr, '&fileformat'), 'mac') 302 | Assert Equal(getbufline(bufnr, 1, '$'), ['foo', 'bar', '']) 303 | call session.runner.next() 304 | Assert Equal(getbufvar(bufnr, '&fileformat'), 'unix') 305 | Assert Equal(getbufline(bufnr, 1, '$'), ["foo\rbar\rbuz", 'qux']) 306 | End 307 | End 308 | End 309 | End 310 | 311 | Describe config.bufname 312 | It is used for buffer name 313 | let session = s:session({'bufname': '[quickrun output]'}, ['result']) 314 | call session.run() 315 | Assert True(bufexists('[quickrun output]')) 316 | let bufnr = bufnr('[[]quickrun output[]]') 317 | Assert Equal(getbufline(bufnr, 1, '$'), ['result']) 318 | execute bufnr 'bwipeout' 319 | End 320 | End 321 | 322 | Describe config.filetype 323 | It sets filetype 324 | let session = s:session({'filetype': 'foobar'}, []) 325 | call session.run() 326 | Assert True(bufexists('quickrun://output')) 327 | let bufnr = bufnr('quickrun://output') 328 | Assert Equal(getbufvar(bufnr, '&filetype'), 'foobar') 329 | End 330 | End 331 | 332 | Describe config.append 333 | Before each 334 | let session = s:session({}, ['foo']) 335 | call session.run() 336 | End 337 | 338 | Context when it is TRUE 339 | It appends the result to a buffer 340 | let session = s:session({'append': 1}, ['bar']) 341 | call session.run() 342 | let bufnr = bufnr('quickrun://output') 343 | Assert Equal(getbufline(bufnr, 1, '$'), ['foobar']) 344 | End 345 | End 346 | 347 | Context when it is FALSE 348 | It clears a buffer at first 349 | let session = s:session({'append': 0}, ['bar']) 350 | call session.run() 351 | let bufnr = bufnr('quickrun://output') 352 | Assert Equal(getbufline(bufnr, 1, '$'), ['bar']) 353 | End 354 | End 355 | End 356 | 357 | Describe config.opener 358 | " TODO: How to test? 359 | End 360 | 361 | Describe config.into 362 | Context when it is TRUE 363 | It moves the cursor into the result buffer 364 | let result = ["foo\nbar\n"] 365 | let session = s:session({'into': 1}, result) 366 | call session.run() 367 | Assert Equal(bufnr('quickrun://output'), bufnr('%')) 368 | End 369 | End 370 | 371 | Context when it is FALSE 372 | It moves the cursor into the result buffer 373 | let result = ["foo\nbar\n"] 374 | let session = s:session({'into': 0}, result) 375 | call session.run() 376 | Assert NotEqual(bufnr('quickrun://output'), bufnr('%')) 377 | End 378 | End 379 | End 380 | 381 | Describe config.running_mark 382 | It sets running_mark at the tail of buffer while running 383 | let result = ['foo', "bar\nbuz", '', "hoge\npiyo"] 384 | let session = s:session({'running_mark': '***'}, result) 385 | call session.run() 386 | Assert True(bufexists('quickrun://output')) 387 | let bufnr = bufnr('quickrun://output') 388 | Assert Equal(getbufline(bufnr, 1, '$'), ['foo', '***']) 389 | call session.runner.next() 390 | Assert Equal(getbufline(bufnr, 1, '$'), ['foobar', 'buz', '***']) 391 | call session.runner.next() 392 | Assert Equal(getbufline(bufnr, 1, '$'), ['foobar', 'buz', '***']) 393 | call session.runner.next() 394 | Assert Equal(getbufline(bufnr, 1, '$'), ['foobar', 'buzhoge', 'piyo']) 395 | End 396 | End 397 | 398 | Describe config.close_on_empty 399 | Context when it is TRUE 400 | Context when the result is empty 401 | It closes the result buffer 402 | let result = [''] 403 | let session = s:session({'close_on_empty': 1}, result) 404 | call session.run() 405 | let bufnr = bufnr('quickrun://output') 406 | Assert Empty(win_findbuf(bufnr)) 407 | End 408 | End 409 | 410 | Context when the result is not empty 411 | It does not close the result buffer 412 | let result = ['foo'] 413 | let session = s:session({'close_on_empty': 1}, result) 414 | call session.run() 415 | let bufnr = bufnr('quickrun://output') 416 | Assert NotEmpty(win_findbuf(bufnr)) 417 | End 418 | End 419 | End 420 | 421 | Context when it is FALSE 422 | Context when the result is empty 423 | It does not close the result buffer 424 | let result = [''] 425 | let session = s:session({'close_on_empty': 0}, result) 426 | call session.run() 427 | let bufnr = bufnr('quickrun://output') 428 | Assert NotEmpty(win_findbuf(bufnr)) 429 | End 430 | End 431 | End 432 | End 433 | End 434 | --------------------------------------------------------------------------------