Merge commit '19b843f85139130344e4238488a9b94aa4a5ff51' as 'vim/bundle/tsuquyomi'

main
Buddy Sandidge 6 years ago
commit 9ef9ca3bc0

@ -0,0 +1,11 @@
*.swp
*.swo
*.un~
node_modules/
NUL
.tmp*
*.log
neobundle.vim/
bundle/
vim/
local/

@ -0,0 +1,16 @@
env:
global:
- GIT_COMMITTER_NAME=y-kurami
- GIT_COMMITTER_EMAIL=yosuke.kurami@gmail.com
- GIT_AUTHOR_NAME=Quramy
- GIT_AUTHOR_EMAIL=yosuke.kurami@gmail.com
- HIDE_VIM=1
language: node_js
node_js:
- '11'
before_install:
- 'pushd test/ && yarn install && popd'
script:
- bash runtest-all-ts.sh
cache:
yarn: true

@ -0,0 +1,336 @@
# Tsuquyomi [![Build Status](https://travis-ci.org/Quramy/tsuquyomi.svg?branch=master)](https://travis-ci.org/Quramy/tsuquyomi) [![Greenkeeper badge](https://badges.greenkeeper.io/Quramy/tsuquyomi.svg)](https://greenkeeper.io/)
Make your Vim a TypeScript IDE.
![capture](screen.gif)
## Features
Tsuquyomi works as a client for **TSServer** (which is an editor service bundled into TypeScript).
So, installing Tsuquyomi, your vim gets the following features provided by TSServer:
+ Completion (omni-completion)
+ Navigate to the location where a symbol is defined.
+ Show location(s) where a symbol is referenced.
+ Display a list of syntax and semantics errors to Vim quickfix window.
+ and so on,,,
### Relevant plugins
Tsuquyomi does not provide syntax-highlight nor indentation. You can use the following Vim plugins for writing .ts:
* [leafgarland/typescript-vim](https://github.com/leafgarland/typescript-vim) provides syntax highlight.
* [Quramy/vim-js-pretty-template](https://github.com/Quramy/vim-js-pretty-template) provides syntax highlight for contents in Template Strings.
* [jason0x43/vim-js-indent](https://github.com/jason0x43/vim-js-indent) provides function of indent for both JavaScript and TypeScript.
* [Quramy/vim-dtsm](https://github.com/Quramy/vim-dtsm) provides `.d.ts` management for [dtsm](https://github.com/vvakame/dtsm) users.
* [mhartington/vim-typings](https://github.com/mhartington/vim-typings) provides `.d.ts` management for [typings](https://github.com/typings/typings) users.
## How to install
Tsuquyomi requires the following:
+ [Vim](http://www.vim.org/) (vim7.4 or later)
+ [Node.js](https://nodejs.org/) & [TypeScript](https://github.com/Microsoft/TypeScript)
+ [Shougo/vimproc.vim](https://github.com/Shougo/vimproc.vim) (Not required if you use vim8 or later)
### Install TypeScript
```bash
npm -g install typescript
```
### Install Tsuquyomi
Download zip.file from [here](https://github.com/Quramy/tsuquyomi/archive/master.zip), or use your favorite Vim plugin manager.
#### Pathogen
See https://github.com/tpope/vim-pathogen for instructions to install pathogen itself
(very simple one-line install, one-line config)
```
# create bundle folder if it doesn't exist
mkdir -p ~/.vim/bundle
# Install and compile procvim.vim
git clone https://github.com/Shougo/vimproc.vim.git ~/.vim/bundle/vimproc.vim
pushd ~/.vim/bundle/vimproc.vim
make
popd
# Install tsuquyomi
git clone https://github.com/Quramy/tsuquyomi.git ~/.vim/bundle/tsuquyomi
```
#### NeoBundle
If you use [NeoBundle](https://github.com/Shougo/neobundle.vim) for Vim plugin management, append the following to your `.vimrc`:
```vim
NeoBundle 'Shougo/vimproc.vim', {
\ 'build' : {
\ 'windows' : 'tools\\update-dll-mingw',
\ 'cygwin' : 'make -f make_cygwin.mak',
\ 'mac' : 'make -f make_mac.mak',
\ 'linux' : 'make',
\ 'unix' : 'gmake',
\ },
\ }
NeoBundle 'Quramy/tsuquyomi'
```
And exec `:NeoBundleInstall`.
(About vimproc installation, please see [the original install guide](https://github.com/Shougo/vimproc.vim#install).)
## How to use
### Completion
Tsuquyomi supports Omni-Completion.
By the default, type `<C-x> <C-o>` in insert mode, Tsuquyomi shows completions.
#### Customize completion
You can configure completion with the `completeopt` option.
If you don't want the popup menu:
```vim
autocmd FileType typescript setlocal completeopt-=menu
```
If you want to show a method's signature in the popup menu, set `g:tsuquyomi_completion_detail`. **Remarks: This option makes completion slow**
```vim
let g:tsuquyomi_completion_detail = 1
```
If you want to show a method's signature in the preview window when you complete this method's arguments (default):
(The preview window isn't shown when completion properties or variables)
```vim
autocmd FileType typescript setlocal completeopt+=menu,preview
```
### Navigations
#### Definition
Type `<C-]>` in normal mode or visual mode, Tsuquyomi navigates to the location where the symbol under the cursor is defined.
Alternatively, call the Ex command `:TsuquyomiDefinition` or `:TsuDefinition`.
(All Tsuquyomi's commands have aliases with short prefix `'Tsu'`.)
And type `<C-t>` , Tsuquyomi moves the cursor to the location where the last `<C-]>` was typed.
#### Type Definition
`:TsuTypeDefinition` command is similar to `:TsuDefinition`. `:TsuTypeDefinition` navigates to the location where the type of the symbol under the cursor is defined.
#### References
Type `<C-^>` in normal mode or visual mode, Tsuquyomi shows a list of location where the symbol under the cursor is referenced.
Alternatively, call the Ex command `:TsuReferences`.
If you want where an interface is implemented, use `:TsuImplementation`.
#### Keyword search
Call the Ex command `:TsuSearch {keyword}` to get the list of locations which contain the keyword. This command searches the keyword from opened or referenced files in your project.
The search term minimum length can be configured with `let g:tsuquyomi_search_term_min_length = 3`.
#### Disable default mappings
If you do not want to use the above key mappings please add `let g:tsuquyomi_disable_default_mappings = 1` to your `.vimrc` file.
### Show compile errors
When a buffer is saved, Tsuquyomi checks syntax and semantics. Alternatively call the Ex command `:TsuGeterr`.
And if it contains errors, Tsuquyomi shows them to Vim quickfix window.
If your use TypeScript v1.6.0 or later, you can use `:TsuGeterrProject` command.
This command shows all compilation errors contained in your project to quickfix window.
#### Quick fix
If the cursor is on an error and TypeScript's LanguageService has a code fix for this error, call `:TsuQuickFix`.
The code fix will be applied.
#### Configure compile options
Make [tsconfig.json](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
For example:
```json
{
"compilerOptions": {
"noImplicitAny": true,
"target": "es5",
"module": "commonjs"
}
}
```
When you change tsconfig.json after opening `*.ts` files, you should exec `:TsuquyomiReloadProject` command.
So, the changes of tsconfig.json are reflected in the TSServer.
#### Integrate with syntastic
If you use [syntastic](https://github.com/scrooloose/syntastic), you can use syntastic for displaying syntax and semantics errors instead of vim's default quickfix window. To integrate syntastic, write the following setting to your .vimrc.
```vim
let g:tsuquyomi_disable_quickfix = 1
let g:syntastic_typescript_checkers = ['tsuquyomi'] " You shouldn't use 'tsc' checker.
```
syntastic has default TypeScript checker whose name is 'tsc'. You shouldn't use it with running Tusuquyomi because they don't share compile options.
Tusuquyomi's checker whose name is 'tsuquyomi' uses tsserver and your tsconfig.json.
### Refactor
#### Rename symbols
Using the command `:TsuRenameSymbol`, you can rename the identifier under the cursor to a new name.
If you want to rename identifiers including in comments, you can use `:TsuRenameSymbolC` command.
For example, this command is useful when you want rename `opt` in the following code:
```typescript
/**
*
* @param opt
*
**/
var someFunc = (opt: any) => {...};
```
This feature does not have the default key mapping.
If you need, configure your `.vimrc` . For example:
```vim
autocmd FileType typescript nmap <buffer> <Leader>e <Plug>(TsuquyomiRenameSymbol)
autocmd FileType typescript nmap <buffer> <Leader>E <Plug>(TsuquyomiRenameSymbolC)
```
### Tooltip
Tsuquyomi can display tooltip window about symbol under the mouse cursor. If you want to use this feature, configure `.vimrc` as follows:
```vim
set ballooneval
autocmd FileType typescript setlocal balloonexpr=tsuquyomi#balloonexpr()
```
The `ballonexpr` option is not available in terminal Vim. So, Tsuquyomi also provides a tooltip function `tsuquyomi#hint()`.
For example:
```vim
autocmd FileType typescript nmap <buffer> <Leader>t : <C-u>echo tsuquyomi#hint()<CR>
```
The above example works in terminal Vim.
### Unite sources
Tsuquyomi provides some [unite](https://github.com/Shougo/unite.vim) sources.
#### Show project information(a source of unite)
Execute the following command, your project information is displayed.
```vim
:Unite tsproject
```
The project information contains:
* tsconfig.json which the current buffer use.
* .ts(or .tsx) source files which TypeScript compiles. These files are determined by tsconfig.json
This feature requires TypeScript@1.6.0 or later.
#### Show outline(an extension of unite-outline)
This feature requires Vim plugins:
* [unite-outline](https://github.com/Shougo/unite-outline).
If you have installed these plugins, calling the following Ex command, the outline of the current buffer is displayed.
```vim
:Unite outline
```
### Use TypeScript installed locally
By the default, Tsuquyomi searches locally installed TypeScript. If not hit, Tsuquyomi uses TypeScript installed globally.
And execute the following command, you can confirm the path of tsserver:
```vim
:echo tsuquyomi#config#tsscmd()
```
### Create es6 import declaration
**It's highly experimental**
For example, if your buffer is the following state:
```ts
readFile('hoge', 'utf-8', (err, content) => {
if(!err) console.log(content);
});
```
Move cursor onto `readFile` and call `:TsuImport`, so Tsuquyomi appends the import declaration.
```ts
import { readFile } from 'fs';
readFile('hoge', 'utf-8', (err, content) => {
if(!err) console.log(content);
});
```
To allow Tsuquyomi to import the shortest path instead of the complete one (where the initial module declaration is) one, put this in your .vimrc:
```
let g:tsuquyomi_shortest_import_path = 1
```
For example, if your project has the following 2 files, the plugin will use: `import { foo } from './lib';` instead of: `import { foo } from './lib/foo';`.
```ts
/* lib/index.ts */
export * from './foo';
```
```ts
/* lib/foo.ts */
export const foo = 'FOO'
```
### More details
If you want more details, please see [doc](doc/tsuquyomi.txt).
## Contribute
### How to test
Prepare test
```sh
cd test/
yarn install
cd ..
```
Run test cases
```sh
# Run all test cases with all supported TypeScript version
./runtest-all-ts.sh
# Run all test cases with the latest TypeScript version
./runtest.sh
# Run all test cases with the specified TypeScript version
VERSION=2.3 ./runtest.sh
# Run a test file
./run-one-test.sh test/path/to/test.spec.vim
# Run a test file with the specified TypeScript version
VERSION=2.3 ./run-one-test.sh
```
## License
MIT

File diff suppressed because it is too large Load Diff

@ -0,0 +1,165 @@
"============================================================================
" FILE: bufManager.vim
" AUTHOR: Quramy <yosuke.kurami@gmail.com>
"============================================================================
scriptencoding utf-8
let s:save_cpo = &cpo
set cpo&vim
let s:buf_info_map = {}
function! s:normalize(buf_name)
return substitute(a:buf_name, '\\', '/', 'g')
endfunction
function! tsuquyomi#bufManager#normalizePath(buf_name)
return s:normalize(a:buf_name)
endfunction
function! tsuquyomi#bufManager#open(file_name)
if bufnr(a:file_name) == -1
return 0
endif
let info = {
\'is_opened': 1,
\'is_dirty': 0,
\'bufname': a:file_name,
\'nav_def_stack': []
\}
let s:buf_info_map[s:normalize(a:file_name)] = info
return info
endfunction
function! tsuquyomi#bufManager#isNotOpenable(file_name)
if (match(a:file_name, '^[^\/]*:\/\/') + 1) && !(match(a:file_name, '^file:\/\/') + 1)
return 1
else
return 0
endif
endfunction
function! tsuquyomi#bufManager#openedFiles()
return filter(copy(s:buf_info_map), 'v:val.is_opened')
endfunction
function! tsuquyomi#bufManager#clearMap()
let s:buf_info_map = {}
return 1
endfunction
function! tsuquyomi#bufManager#bufName(file_name)
let name = s:normalize(a:file_name)
if !has_key(s:buf_info_map, name)
return 0
endif
return s:buf_info_map[name].bufname
endfunction
function! tsuquyomi#bufManager#close(file_name)
let name = s:normalize(a:file_name)
if !has_key(s:buf_info_map, name)
return 0
endif
let s:buf_info_map[name].is_opened = 0
return 1
endfunction
function! tsuquyomi#bufManager#isOpened(file_name)
let name = s:normalize(a:file_name)
if !has_key(s:buf_info_map, name)
return 0
endif
return s:buf_info_map[name].is_opened
endfunction
function! tsuquyomi#bufManager#setDirty(file_name, state)
let name = s:normalize(a:file_name)
if !has_key(s:buf_info_map, name)
return 0
endif
let s:buf_info_map[name].is_dirty = a:state
return 1
endfunction
function! tsuquyomi#bufManager#isDirty(file_name)
let name = s:normalize(a:file_name)
if !has_key(s:buf_info_map, name)
return 0
endif
return s:buf_info_map[name].is_dirty
endfunction
function! tsuquyomi#bufManager#whichDirty(file_name_list)
let result = []
for file_name in a:file_name_list
if tsuquyomi#bufManager#isDirty(file_name)
call add(result, file_name)
endif
endfor
return result
endfunction
function! tsuquyomi#bufManager#tmpfile(file_name)
let name = s:normalize(a:file_name)
if !has_key(s:buf_info_map, name)
return 0
endif
if !has_key(s:buf_info_map[name], 'tmpfile')
let tmpfile = tempname()
let s:buf_info_map[name].tmpfile = tmpfile
return tmpfile
else
return s:buf_info_map[name].tmpfile
endif
endfunction
function! tsuquyomi#bufManager#saveTmp(file_name)
let tmpfile = tsuquyomi#bufManager#tmpfile(a:file_name)
call writefile(getbufline(a:file_name, 1, '$'), tmpfile)
return 1
endfunction
function! tsuquyomi#bufManager#pushNavDef(file_name, loc)
let name = s:normalize(a:file_name)
if !has_key(s:buf_info_map, name)
return 0
endif
call add(s:buf_info_map[name].nav_def_stack, a:loc)
return 1
endfunction
function! tsuquyomi#bufManager#popNavDef(file_name)
let name = s:normalize(a:file_name)
if !has_key(s:buf_info_map, name)
return {}
endif
if len(s:buf_info_map[name].nav_def_stack)
return remove(s:buf_info_map[name].nav_def_stack, -1)
else
return {}
endif
endfunction
let s:win_nav_map = {}
function! tsuquyomi#bufManager#winPushNavDef(winnm, file_name, loc)
if !has_key(s:win_nav_map, a:winnm)
let s:win_nav_map[a:winnm] = []
endif
call add(s:win_nav_map[a:winnm], {'file_name': a:file_name, 'loc': a:loc})
endfunction
function! tsuquyomi#bufManager#winPopNavDef(winnm)
if !has_key(s:win_nav_map, a:winnm)
return [0, {}]
endif
if !len(s:win_nav_map[a:winnm])
return [0, {}]
endif
return [1, remove(s:win_nav_map[a:winnm], -1)]
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo

@ -0,0 +1,320 @@
"============================================================================
" FILE: autoload/tsuquyomi/config.vim
" AUTHOR: Quramy <yosuke.kurami@gmail.com>
"============================================================================
"
scriptencoding utf-8
let s:save_cpo = &cpo
set cpo&vim
let s:V = vital#of('tsuquyomi')
let s:P = s:V.import('ProcessManager')
let s:Process = s:V.import('Process')
let s:Prelude = s:V.import('Prelude')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = expand('<sfile>:p:h')
let s:tss_cmd = ''
let s:tss_version = {'is_valid': 0, 'out': '???'}
let s:is_vim8 = has('patch-8.0.1')
function! tsuquyomi#config#preconfig()
if !exists('g:tsuquyomi_is_available')
if !s:is_vim8 && !s:P.is_available()
" 1. vimproc or vim8 installation check
let g:tsuquyomi_is_available = 0
call s:deleteCommand()
echom '[Tsuquyomi] Shougo/vimproc.vim or vim8 is not installed. Please install it.'
return 0
else
" 2. tsserver installation check
let s:tss_cmd = tsuquyomi#config#tsscmd()
if s:tss_cmd == ''
let g:tsuquyomi_is_available = 0
call s:deleteCommand()
return 0
endif
" 3. TypeScript version check
call tsuquyomi#config#getVersion()
if !s:tss_version.is_valid
let g:tsuquyomi_is_available = 0
call s:deleteCommand()
echom '[Tsuquyomi] Your TypeScript version is invalid. '.s:tss_version.out
return 0
endif
if !tsuquyomi#config#isHigher(150)
let g:tsuquyomi_is_available = 0
call s:deleteCommand()
echom '[Tsuquyomi] tsuquyomi requires typescript@~1.5.0'
return 0
endif
let g:tsuquyomi_is_available = 1
endif
endif
return g:tsuquyomi_is_available
endfunction
function! s:deleteCommand()
delc TsuquyomiStartServer
delc TsuStartServer
delc TsuquyomiStopServer
delc TsuStopServer
delc TsuquyomiStatusServer
delc TsuStatusServer
delc TsuquyomiReloadProject
delc TsuReloadProject
endfunction
function! tsuquyomi#config#tssargs()
let args = []
call add(args, '--locale '.g:tsuquyomi_locale)
return join(args, ' ')
endfunction
" Search from cwd upward looking for first directory with package.json
function! tsuquyomi#config#_path2project_directory_ts()
let parent = getcwd()
while 1
let path = parent . '/package.json'
if filereadable(path)
return parent
endif
let next = fnamemodify(parent, ':h')
if next == parent
return ''
endif
let parent = next
endwhile
endfunction
function! tsuquyomi#config#tsscmd()
if s:tss_cmd !=# ''
return s:tss_cmd
endif
if g:tsuquyomi_use_local_typescript != 0
let l:prj_dir = tsuquyomi#config#_path2project_directory_ts()
if l:prj_dir == ''
" Fallback to generic project root search
let l:prj_dir = s:Prelude.path2project_directory(getcwd(), 1)
endif
if l:prj_dir !=# ''
let l:searched_tsserver_path = s:Filepath.join(l:prj_dir, 'node_modules/typescript/bin/tsserver')
if filereadable(l:searched_tsserver_path)
return g:tsuquyomi_nodejs_path.' "'.l:searched_tsserver_path.'"'
endif
endif
endif
if g:tsuquyomi_use_dev_node_module == 0
let l:cmd = 'tsserver'
if has('win32') || has('win64')
let l:cmd .= '.cmd'
endif
if !executable(l:cmd)
echom '[Tsuquyomi] tsserver is not installed. Try "npm -g install typescript".'
return ''
endif
else
if g:tsuquyomi_use_dev_node_module == 1
let l:path = s:Filepath.join(s:script_dir, '../../node_modules/typescript/bin/tsserver')
elseif g:tsuquyomi_use_dev_node_module == 2
let l:path = g:tsuquyomi_tsserver_path
else
echom '[Tsuquyomi] Invalid option value "g:tsuquyomi_use_dev_node_module".'
return ''
endif
if (has('win32') || has('win64')) && l:path !~ '\.cmd$'
let l:path .= '.cmd'
endif
if filereadable(l:path) != 1
echom '[Tsuquyomi] tsserver.js does not exist. Try "npm install"., '.l:path
return ''
endif
if !s:is_vim8
let l:cmd = g:tsuquyomi_nodejs_path.' "'.l:path.'"'
else
let l:cmd = g:tsuquyomi_nodejs_path.' '.l:path
endif
endif
return l:cmd
endfunction
function! tsuquyomi#config#getVersion()
if s:tss_version.is_valid
return s:tss_version
endif
let l:cmd = substitute(tsuquyomi#config#tsscmd(), 'tsserver', 'tsc', '')
let l:cmd = substitute(l:cmd, "\\", "/", "g")
let out = s:Process.system(l:cmd.' --version')
let pattern = '\vVersion\s+(\d+)\.(\d+)\.(\d+)-?([^\.\n\s]*)'
let matched = matchlist(out, pattern)
if !len(matched)
let s:tss_version = {'is_valid': 0, 'out': out}
return s:tss_version
endif
let [major, minor, patch] = [str2nr(matched[1]), str2nr(matched[2]), str2nr(matched[3])]
let s:tss_version = {
\ 'is_valid': 1,
\ 'major': major, 'minor': minor, 'patch': patch,
\ 'channel': matched[4],
\ }
return s:tss_version
endfunction
function! tsuquyomi#config#isHigher(target)
if !s:tss_version.is_valid
return 0
endif
let numeric_version = s:tss_version.major * 100 + s:tss_version.minor * 10 + s:tss_version.patch
return numeric_version >= a:target
endfunction
function! tsuquyomi#config#createBufLocalCommand()
command! -buffer -nargs=* -complete=buffer TsuquyomiOpen :call tsuquyomi#open(<f-args>)
command! -buffer -nargs=* -complete=buffer TsuOpen :call tsuquyomi#open(<f-args>)
command! -buffer -nargs=* -complete=buffer TsuquyomiClose :call tsuquyomi#close(<f-args>)
command! -buffer -nargs=* -complete=buffer TsuClose :call tsuquyomi#close(<f-args>)
command! -buffer -nargs=* -complete=buffer TsuquyomiReload :call tsuquyomi#reload(<f-args>)
command! -buffer -nargs=* -complete=buffer TsuReload :call tsuquyomi#reload(<f-args>)
command! -buffer -nargs=* -complete=buffer TsuquyomiDump :call tsuquyomi#dump(<f-args>)
command! -buffer -nargs=* -complete=buffer TsuDump :call tsuquyomi#dump(<f-args>)
command! -buffer -nargs=1 TsuquyomiSearch :call tsuquyomi#navtoByLoclistContain(<f-args>)
command! -buffer -nargs=1 TsuSearch :call tsuquyomi#navtoByLoclistContain(<f-args>)
command! -buffer TsuquyomiDefinition :call tsuquyomi#definition()
command! -buffer TsuDefinition :call tsuquyomi#definition()
command! -buffer TsuquyomiSplitDefinition :call tsuquyomi#splitDefinition()
command! -buffer TsuSplitDefinition :call tsuquyomi#splitDefinition()
command! -buffer TsuquyomiGoBack :call tsuquyomi#goBack()
command! -buffer TsuGoBack :call tsuquyomi#goBack()
command! -buffer TsuquyomiImplementation :call tsuquyomi#implementation()
command! -buffer TsuImplementation :call tsuquyomi#implementation()
command! -buffer TsuquyomiReferences :call tsuquyomi#references()
command! -buffer TsuReferences :call tsuquyomi#references()
command! -buffer TsuquyomiTypeDefinition :call tsuquyomi#typeDefinition()
command! -buffer TsuTypeDefinition :call tsuquyomi#typeDefinition()
command! -buffer TsuquyomiGeterr :call tsuquyomi#geterr()
command! -buffer TsuGeterr :call tsuquyomi#geterr()
command! -buffer TsuquyomiGeterrProject :call tsuquyomi#geterrProject()
command! -buffer TsuGeterrProject :call tsuquyomi#geterrProject()
command! -buffer TsuquyomiRenameSymbol :call tsuquyomi#renameSymbol()
command! -buffer TsuRenameSymbol :call tsuquyomi#renameSymbol()
command! -buffer TsuquyomiRenameSymbolC :call tsuquyomi#renameSymbolWithComments()
command! -buffer TsuRenameSymbolC :call tsuquyomi#renameSymbolWithComments()
command! -buffer TsuquyomiQuickFix :call tsuquyomi#quickFix()
command! -buffer TsuQuickFix :call tsuquyomi#quickFix()
command! -buffer TsuquyomiSignatureHelp :call tsuquyomi#signatureHelp()
command! -buffer TsuSignatureHelp :call tsuquyomi#signatureHelp()
command! -buffer TsuAsyncGeterr :call tsuquyomi#asyncGeterr()
command! -buffer TsuquyomiAsyncGeterr :call tsuquyomi#asyncGeterr()
" TODO These commands don't work correctly.
command! -buffer TsuquyomiRenameSymbolS :call tsuquyomi#renameSymbolWithStrings()
command! -buffer TsuRenameSymbolS :call tsuquyomi#renameSymbolWithStrings()
command! -buffer TsuquyomiRenameSymbolCS :call tsuquyomi#renameSymbolWithCommentsStrings()
command! -buffer TsuRenameSymbolCS :call tsuquyomi#renameSymbolWithCommentsStrings()
command! -buffer TsuquyomiImport :call tsuquyomi#es6import#complete()
command! -buffer TsuImport :call tsuquyomi#es6import#complete()
endfunction
function! tsuquyomi#config#createBufLocalMap()
noremap <silent> <buffer> <Plug>(TsuquyomiDefinition) :TsuquyomiDefinition <CR>
noremap <silent> <buffer> <Plug>(TsuquyomiSplitDefinition) :TsuquyomiSplitDefinition <CR>
noremap <silent> <buffer> <Plug>(TsuquyomiTypeDefinition) :TsuquyomiTypeDefinition <CR>
noremap <silent> <buffer> <Plug>(TsuquyomiGoBack) :TsuquyomiGoBack <CR>
noremap <silent> <buffer> <Plug>(TsuquyomiImplementation) :TsuquyomiImplementation <CR>
noremap <silent> <buffer> <Plug>(TsuquyomiReferences) :TsuquyomiReferences <CR>
noremap <silent> <buffer> <Plug>(TsuquyomiRenameSymbol) :TsuquyomiRenameSymbol <CR>
noremap <silent> <buffer> <Plug>(TsuquyomiRenameSymbolC) :TsuquyomiRenameSymbolC <CR>
noremap <silent> <buffer> <Plug>(TsuquyomiQuickFix) :TsuquyomiQuickFix <CR>
noremap <silent> <buffer> <Plug>(TsuquyomiSignatureHelp) :TsuquyomiSignatureHelp <CR>
noremap <silent> <buffer> <Plug>(TsuquyomiImport) :TsuquyomiImport <CR>
" TODO These commands don't work correctly.
noremap <silent> <buffer> <Plug>(TsuquyomiRenameSymbolS) :TsuquyomiRenameSymbolS <CR>
noremap <silent> <buffer> <Plug>(TsuquyomiRenameSymbolCS) :TsuquyomiRenameSymbolCS <CR>
endfunction
function! tsuquyomi#config#applyBufLocalDefaultMap()
if(!exists('g:tsuquyomi_disable_default_mappings'))
if !hasmapto('<Plug>(TsuquyomiDefinition)')
map <buffer> <C-]> <Plug>(TsuquyomiDefinition)
endif
if !hasmapto('<Plug>(TsuquyomiSplitDefinition)')
map <buffer> <C-W>] <Plug>(TsuquyomiSplitDefinition)
map <buffer> <C-W><C-]> <Plug>(TsuquyomiSplitDefinition)
endif
if !hasmapto('<Plug>(TsuquyomiGoBack)')
map <buffer> <C-t> <Plug>(TsuquyomiGoBack)
endif
if !hasmapto('<Plug>(TsuquyomiReferences)')
map <buffer> <C-^> <Plug>(TsuquyomiReferences)
endif
endif
endfunction
let s:autocmd_patterns = []
function! tsuquyomi#config#applyBufLocalAutocmd(pattern)
if index(s:autocmd_patterns, a:pattern) == -1
call add(s:autocmd_patterns, a:pattern)
endif
let all_patterns = join(s:autocmd_patterns, ",")
if !g:tsuquyomi_disable_quickfix
augroup tsuquyomi_geterr
autocmd!
execute 'autocmd BufWritePost '.all_patterns.' silent! call tsuquyomi#reloadAndGeterr()'
augroup END
endif
augroup tsuquyomi_defaults
autocmd!
autocmd BufWinEnter * silent! call tsuquyomi#setPreviewOption()
execute 'autocmd TextChanged,TextChangedI '.all_patterns.' silent! call tsuquyomi#letDirty()'
augroup END
endfunction
function! tsuquyomi#config#applyBufLocalFunctions()
setlocal omnifunc=tsuquyomi#complete
if exists('+bexpr')
setlocal bexpr=tsuquyomi#balloonexpr()
endif
endfunction
function! tsuquyomi#config#registerInitialCallback()
call tsuquyomi#tsClient#registerCallback('tsuquyomi#tsClient#readDiagnostics', 'diagnostics')
endfunction
let s:async_initialized = 0
function! tsuquyomi#config#initBuffer(opt)
if !has_key(a:opt, 'pattern')
echom '[Tsuquyomi] missing options. "pattern"'
return 0
endif
let pattern = a:opt.pattern
call tsuquyomi#config#createBufLocalCommand()
call tsuquyomi#config#createBufLocalMap()
call tsuquyomi#config#applyBufLocalDefaultMap()
call tsuquyomi#config#applyBufLocalAutocmd(pattern)
call tsuquyomi#config#applyBufLocalFunctions()
if g:tsuquyomi_auto_open
silent! call tsuquyomi#open()
silent! call tsuquyomi#sendConfigure()
endif
if s:is_vim8 && g:tsuquyomi_use_vimproc == 0 && s:async_initialized == 0
call tsuquyomi#config#registerInitialCallback()
let s:async_initialized = 1
endif
return 1
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo

@ -0,0 +1,509 @@
"============================================================================
" FILE: autoload/tsuquyomi/es6import.vim
" AUTHOR: Quramy <yosuke.kurami@gmail.com>
"============================================================================
scriptencoding utf-8
let s:save_cpo = &cpo
set cpo&vim
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:JSON = s:V.import('Web.JSON')
function! s:normalizePath(path)
return substitute(a:path, '\\', '/', 'g')
endfunction
function! s:is_valid_identifier(symbol_str)
return a:symbol_str =~ '^[A-Za-z_\$][A-Za-z_\$0-9]*$'
endfunction
function! s:get_keyword_under_cursor()
let l:line_str = getline('.')
let l:line = line('.')
let l:offset = col('.')
" search backwards for start of identifier (iskeyword pattern)
let l:start = l:offset
let l:end = l:offset
while l:start > 0 && l:line_str[l:start-2] =~ "\\k"
let l:start -= 1
endwhile
while l:end <= strlen(l:line_str) && l:line_str[l:end] =~ "\\k"
let l:end += 1
endwhile
return {
\ 'text': l:line_str[l:start-1:l:end-1],
\ 'start': { 'offset': l:start, 'line': l:line },
\ 'end': { 'offset': l:end, 'line': l:line }
\ }
endfunction
function! s:relativePath(from, to)
let l:from_parts = s:Filepath.split(s:Filepath.dirname(a:from))
let l:to_parts = s:Filepath.split(a:to)
let l:count_node_modules = len(filter(copy(l:to_parts), 'v:val==#"node_modules"'))
if l:count_node_modules > 1
return ['', 0]
elseif l:count_node_modules == 1
return [substitute(a:to, '^.*\/node_modules\/', '', ''), 1]
endif
let l:idx = 0
while idx < min([len(l:from_parts), len(l:to_parts)]) && l:from_parts[l:idx] ==# l:to_parts[l:idx]
let l:idx += 1
endwhile
call remove(l:from_parts, 0, l:idx - 1)
call remove(l:to_parts, 0, l:idx - 1)
if len(l:from_parts)
return [join(map(l:from_parts, '"../"'), '').join(l:to_parts, '/'), 1]
else
return ['./'.join(l:to_parts, '/'), 1]
endif
endfunction
let s:external_module_cache_dict = {}
function! tsuquyomi#es6import#checkExternalModule(name, file, no_use_cache)
let l:cache = s:external_module_cache_dict
if a:no_use_cache || !has_key(l:cache, a:file) || !has_key(l:cache[a:file], a:name)
if !has_key(l:cache, a:file)
let l:cache[a:file] = {}
endif
let l:result = tsuquyomi#tsClient#tsNavBar(a:file)
let l:modules = map(filter(l:result, 'v:val.kind==#"module"'), 'v:val.text')
let l:cache[a:file][a:name] = 0
for module_name in l:modules
if module_name[0] ==# '"' || module_name[0] ==# "'"
if module_name[1:-2] ==# a:name
let l:cache[a:file][a:name] = 1
break
endif
endif
endfor
endif
return l:cache[a:file][a:name]
endfunction
function! tsuquyomi#es6import#createImportBlock(text)
let l:identifier = a:text
if !s:is_valid_identifier(l:identifier)
return []
endif
let [l:nav_list, l:hit] = tsuquyomi#navto(l:identifier, 'export', 2)
if !l:hit || !len(l:nav_list)
return []
endif
let l:from = s:normalizePath(expand('%:p'))
let l:result_list = []
for nav in l:nav_list
if has_key(nav, 'containerKind') && nav.containerKind ==# 'module'
if tsuquyomi#es6import#checkExternalModule(nav.containerName, nav.file, 0)
let l:importDict = {
\ 'identifier': nav.name,
\ 'path': nav.containerName,
\ 'nav': nav
\ }
call add(l:result_list, l:importDict)
endif
else
let l:to = s:normalizePath(nav.file)
let [l:relative_path, l:result] = s:relativePath(l:from, l:to)
if !l:result
return []
endif
let l:relative_path = s:removeTSExtensions(l:relative_path)
if g:tsuquyomi_shortest_import_path == 1
let l:path = s:getShortestImportPath(l:to, l:identifier, l:relative_path)
elseif g:tsuquyomi_baseurl_import_path == 1
let l:base_url_import_path = s:getBaseUrlImportPath(nav.file)
let l:path = l:base_url_import_path != '' ? l:base_url_import_path : l:relative_path
else
let l:path = l:relative_path
endif
let l:importDict = {
\ 'identifier': nav.name,
\ 'path': l:path,
\ 'nav': nav
\ }
call add(l:result_list, l:importDict)
endif
endfor
if g:tsuquyomi_case_sensitive_imports == 1
call filter(l:result_list, 'v:val.identifier ==# l:identifier')
endif
" Make the possible imports list unique per path
let dictionary = {}
for i in l:result_list
let dictionary[i.path] = i
endfor
let l:unique_result_list = []
if (exists('a:1'))
let l:unique_result_list = sort(values(dictionary), a:1)
else
let l:unique_result_list = sort(values(dictionary))
endif
return l:unique_result_list
endfunction
function! s:removeTSExtensions(path)
let l:path = a:path
let l:path = substitute(l:path, '\.d\.ts$', '', '')
let l:path = substitute(l:path, '\.ts$', '', '')
let l:path = substitute(l:path, '\.tsx$', '', '')
let l:path = substitute(l:path, '^@types/', '', '')
let l:path = substitute(l:path, '/index$', '', '')
return l:path
endfunction
function! s:getShortestImportPath(absolute_path, module_identifier, relative_path)
let l:splitted_relative_path = split(a:relative_path, '/')
if l:splitted_relative_path[0] == '..'
let l:paths_to_visit = substitute(a:relative_path, '\.\.\/', '', 'g')
let l:path_moves_to_do = len(split(l:paths_to_visit, '/'))
else
let l:path_moves_to_do = len(l:splitted_relative_path) - 1
endif
let l:shortened_path = l:splitted_relative_path[len(l:splitted_relative_path) - 1]
let l:path_move_count = 0
let l:splitted_absolute_path = split(a:absolute_path, '/')
while l:path_move_count != l:path_moves_to_do
let l:splitted_absolute_path = l:splitted_absolute_path[0:len(splitted_absolute_path) - 2]
let l:shortened_path = s:getShortenedPath(l:splitted_absolute_path, l:shortened_path, a:module_identifier)
let l:path_move_count += 1
endwhile
let l:shortened_path = substitute(l:shortened_path, '\[\/\]\*\[\index\]\*', '', 'g')
if l:splitted_relative_path[0] == '.'
return './' . s:getPathWithSkippedRoot(l:shortened_path)
elseif l:splitted_relative_path[0] == '..'
let l:count = 0
let l:current = '..'
let l:prefix = ''
while l:current == '..' || l:count == len(l:splitted_relative_path) - 1
let l:current = l:splitted_relative_path[l:count]
if l:current == '..'
let l:prefix = l:prefix . l:current . '/'
endif
let l:count += 1
endwhile
return l:prefix . s:getPathWithSkippedRoot(l:shortened_path)
endif
return l:shortened_path
endfunction
function! s:getPathWithSkippedRoot(path)
return join(split(a:path, '/')[1:len(a:path) -1], '/')
endfunction
function! s:getShortenedPath(splitted_absolute_path, previous_shortened_path, module_identifier)
let l:shortened_path = a:previous_shortened_path
let l:absolute_path_to_search_in = '/' . join(a:splitted_absolute_path, '/') . '/'
let l:found_module_reference = s:findExportingFileForModule(a:module_identifier, l:shortened_path, l:absolute_path_to_search_in)
let l:current_directory_name = a:splitted_absolute_path[len(a:splitted_absolute_path) -1]
let l:path_separator = '/'
while l:found_module_reference != ''
if l:found_module_reference == 'index'
let l:found_module_reference = '[index]*'
let l:path_separator = '[/]*'
else
let l:path_separator = '/'
endif
let l:shortened_path = l:found_module_reference
let l:found_module_reference = s:findExportingFileForModule(a:module_identifier, l:found_module_reference, l:absolute_path_to_search_in)
if l:found_module_reference != ''
let l:shortened_path = l:found_module_reference
endif
endwhile
return l:current_directory_name . l:path_separator . l:shortened_path
endfunction
function! s:getBaseUrlImportPath(module_absolute_path)
let [l:tsconfig, l:tsconfig_file_path] = s:getTsconfig(a:module_absolute_path)
if empty(l:tsconfig) || l:tsconfig_file_path == ''
return ''
endif
let l:project_root_path = fnamemodify(l:tsconfig_file_path, ':h').'/'
" We assume that baseUrl is a path relative to tsconfig.json path.
let l:base_url_config = has_key(l:tsconfig.compilerOptions, 'baseUrl') ? l:tsconfig.compilerOptions.baseUrl : '.'
let l:base_url_path = simplify(l:project_root_path.l:base_url_config)
return s:removeTSExtensions(substitute(a:module_absolute_path, l:base_url_path, '', ''))
endfunction
let s:tsconfig = {}
let s:tsconfig_file_path = ''
function! s:getTsconfig(module_absolute_path)
if empty(s:tsconfig)
let l:project_info = tsuquyomi#tsClient#tsProjectInfo(a:module_absolute_path, 0)
if has_key(l:project_info, 'configFileName')
let s:tsconfig_file_path = l:project_info.configFileName
else
echom '[Tsuquyomi] Cannot find projects tsconfig.json to compute baseUrl import path.'
endif
let l:json = join(readfile(s:tsconfig_file_path),'')
try
let s:tsconfig = s:JSON.decode(l:json)
catch
echom '[Tsuquyomi] Cannot parse projects tsconfig.json. Does it have comments?'
endtry
endif
return [s:tsconfig, s:tsconfig_file_path]
endfunction
function! s:findExportingFileForModule(module, current_module_file, module_directory_path)
execute
\"silent! noautocmd vimgrep /export\\s*\\({.*\\(\\s\\|,\\)"
\. a:module
\."\\(\\s\\|,\\)*.*}\\|\\*\\)\\s\\+from\\s\\+\\(\\'\\|\\\"\\)\\.\\\/"
\. substitute(a:current_module_file, '\/', '\\/', '')
\."[\\/]*\\(\\'\\|\\\"\\)[;]*/j "
\. a:module_directory_path
\. "*.ts"
redir => l:grep_result
silent! clist
redir END
if l:grep_result =~ 'No Errors'
return ''
endif
let l:raw_result = split(l:grep_result, ' ')[2]
let l:raw_result = split(l:raw_result, ':')[0]
let l:raw_result_parts = split(l:raw_result, '/')
let l:extracted_file_name = l:raw_result_parts[len(l:raw_result_parts) -1 ]
let l:extracted_file_name = s:removeTSExtensions(l:extracted_file_name)
return l:extracted_file_name
endfunction
function! s:comp_alias(alias1, alias2)
return a:alias2.spans[0].end.line - a:alias1.spans[0].end.line
endfunction
function! tsuquyomi#es6import#createImportPosition(nav_bar_list)
if !len(a:nav_bar_list)
return {}
endif
if len(a:nav_bar_list) == 1
if a:nav_bar_list[0].kind ==# 'module'
if !len(filter(copy(a:nav_bar_list[0].childItems), 'v:val.kind ==#"alias"'))
let l:start_line = a:nav_bar_list[0].spans[0].start.line - 1
let l:end_line = l:start_line
else
let l:start_line = a:nav_bar_list[0].spans[0].start.line
let l:end_line = a:nav_bar_list[0].spans[0].end.line
endif
else
let l:start_line = a:nav_bar_list[0].spans[0].start.line - 1
let l:end_line = l:start_line
endif
elseif len(a:nav_bar_list) > 1
let l:start_line = a:nav_bar_list[0].spans[0].start.line
let l:end_line = a:nav_bar_list[1].spans[0].start.line - 1
endif
return { 'start': { 'line': l:start_line }, 'end': { 'line': l:end_line } }
endfunction
function! tsuquyomi#es6import#getImportDeclarations(fileName, content_list)
let l:nav_bar_list = tsuquyomi#tsClient#tsNavBar(a:fileName)
if !len(l:nav_bar_list)
return [[], {}, 'no_nav_bar']
endif
let l:position = tsuquyomi#es6import#createImportPosition(l:nav_bar_list)
let l:module_infos = filter(copy(l:nav_bar_list), 'v:val.kind ==# "module"')
if !len(l:module_infos)
return [[], l:position, 'no_module_info']
endif
let l:result_list = []
let l:alias_list = filter(l:module_infos[0].childItems, 'v:val.kind ==# "alias"')
let l:end_line = position.end.line
let l:last_module_end_line = 0
for alias in sort(l:alias_list, "s:comp_alias")
let l:hit = 0
let [l:has_brace, l:brace] = [0, {}]
let [l:has_from, l:from] = [0, { 'start': {}, 'end': {} }]
let [l:has_module, l:module] = [0, { 'name': '', 'start': {}, 'end': {} }]
let l:line = alias.spans[0].start.line
while !l:hit && l:line <= l:end_line
if !len(a:content_list)
let l:line_str = getline(l:line)
else
let l:line_str = a:content_list[l:line - 1]
endif
let l:brace_end_offset = match(l:line_str, "}")
let l:from_offset = match(l:line_str, 'from')
if l:brace_end_offset + 1 && !l:has_brace && !l:has_from
let l:has_brace = 1
let l:brace = {
\ 'end': { 'offset': l:brace_end_offset + 1, 'line': l:line }
\ }
endif
if l:from_offset + 1
let l:has_from = 1
let l:from = {
\ 'start': { 'offset': l:from_offset + 1, 'line': l:line },
\ 'end': { 'offset': l:from_offset + 4, 'line': l:line }
\ }
endif
if l:has_from
let l:module_name_sq = matchstr(l:line_str, "\\m'\\zs.*\\ze'")
if l:module_name_sq !=# ''
let l:has_module = 1
let l:module_name = l:module_name_sq
else
let l:module_name_dq = matchstr(l:line_str, '\m"\zs.*\ze"')
if l:module_name_dq !=# ''
let l:has_module = 1
let l:module_name = l:module_name_dq
endif
endif
endif
if l:has_module
let [l:hit, l:end_line] = [1, l:line]
let l:module = {
\ 'name': l:module_name,
\ 'start': { 'line': l:line },
\ 'end': { 'line': l:line },
\ }
if !l:last_module_end_line
let l:last_module_end_line = l:line
endif
else
let l:line += 1
endif
endwhile
if l:hit
let l:info = {
\ 'module': l:module,
\ 'has_from': l:has_from,
\ 'from_span': l:from,
\ 'has_brace': l:has_brace,
\ 'brace': l:brace,
\ 'alias_info': alias,
\ 'is_oneliner': alias.spans[0].start.line == l:module.end.line
\ }
call add(l:result_list, l:info)
endif
endfor
if l:last_module_end_line
let l:position.end.line = l:last_module_end_line
endif
return [l:result_list, l:position, '']
endfunction
let s:importable_module_list = []
function! tsuquyomi#es6import#moduleComplete(arg_lead, cmd_line, cursor_pos)
return join(s:importable_module_list, "\n")
endfunction
function! tsuquyomi#es6import#selectModule()
echohl String
let l:selected_module = input("[Tsuquyomi] You can import from 2 or more modules.\n" . join(s:importable_module_list, "\n") . "\nSelect one: ", '', 'custom,tsuquyomi#es6import#moduleComplete')
echohl none
echo ' '
if len(filter(copy(s:importable_module_list), 'v:val==#l:selected_module'))
return [l:selected_module, 1]
else
echohl Error
echom '[Tsuquyomi] Invalid module path.'
echohl none
return ['', 0]
endif
endfunction
function! tsuquyomi#es6import#complete()
if !tsuquyomi#bufManager#isOpened(expand('%:p'))
return
end
call tsuquyomi#flush()
let l:identifier_info = s:get_keyword_under_cursor()
let l:list = tsuquyomi#es6import#createImportBlock(l:identifier_info.text)
if len(l:list) > 1
let s:importable_module_list = map(copy(l:list), 'v:val.path')
let [l:selected_module, l:code] = tsuquyomi#es6import#selectModule()
if !l:code
echohl Error
echom '[Tsuquyomi] No search result.'
echohl none
return
endif
let l:block = filter(l:list, 'v:val.path==#l:selected_module')[0]
elseif len(l:list) == 1
let l:block = l:list[0]
else
return
endif
let [l:import_list, l:dec_position, l:reason] = tsuquyomi#es6import#getImportDeclarations(expand('%:p'), [])
let l:module_end_line = has_key(l:dec_position, 'end') ? l:dec_position.end.line : 0
let l:same_path_import_list = filter(l:import_list, 'v:val.has_brace && v:val.module.name ==# l:block.path')
if len(l:same_path_import_list) && len(filter(copy(l:same_path_import_list), 'v:val.alias_info.text ==# l:block.identifier'))
echohl Error
echom '[Tsuquyomi] '.l:block.identifier.' is already imported.'
echohl none
return
endif
"Replace search keyword to hit result identifer
let l:line = getline(l:identifier_info.start.line)
let l:new_line = l:block.identifier
if l:identifier_info.start.offset > 1
let l:new_line = l:line[0:l:identifier_info.start.offset - 2].l:new_line
endif
let l:new_line = l:new_line.l:line[l:identifier_info.end.offset: -1]
call setline(l:identifier_info.start.line, l:new_line)
if g:tsuquyomi_import_curly_spacing == 0
let l:curly_spacing = ''
else
let l:curly_spacing = ' '
end
"Add import declaration
if !len(l:same_path_import_list)
if g:tsuquyomi_semicolon_import
let l:semicolon = ';'
else
let l:semicolon = ''
endif
if g:tsuquyomi_single_quote_import
let l:expression = "import {".l:curly_spacing.l:block.identifier.l:curly_spacing."} from '".l:block.path."'".l:semicolon
else
let l:expression = 'import {'.l:curly_spacing.l:block.identifier.l:curly_spacing.'} from "'.l:block.path.'"'.l:semicolon
endif
call append(l:module_end_line, l:expression)
else
let l:target_import = l:same_path_import_list[0]
if l:target_import.is_oneliner
let l:line = getline(l:target_import.brace.end.line)
let l:injection_position = target_import.brace.end.offset - 2 - strlen(l:curly_spacing)
let l:expression = l:line[0:l:injection_position].', '.l:block.identifier.l:curly_spacing.l:line[l:target_import.brace.end.offset - 1: -1]
call setline(l:target_import.brace.end.line, l:expression)
else
let l:before_line = getline(l:target_import.brace.end.line - 1)
let l:indent = matchstr(l:before_line, '\m^\s*')
let l:before_has_trailing_comma = matchstr(l:before_line, ',\s*$')
if l:before_has_trailing_comma !=# ''
let l:prev_trailing_comma = ''
let l:new_trailing_comma = ','
else
let l:prev_trailing_comma = ','
let l:new_trailing_comma = ''
endif
call setline(l:target_import.brace.end.line - 1, l:before_line.l:prev_trailing_comma)
call append(l:target_import.brace.end.line - 1, l:indent.l:block.identifier.l:new_trailing_comma)
endif
endif
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo

@ -0,0 +1,32 @@
"============================================================================
" FILE: perfLogger.vim
" AUTHOR: Quramy <yosuke.kurami@gmail.com>
"============================================================================
scriptencoding utf-8
let s:log_buffer = []
let s:start_time = reltime()
function! tsuquyomi#perfLogger#reset()
let s:log_buffer = []
let s:start_time = reltime()
endfunction
function! tsuquyomi#perfLogger#getTime()
let num_row = len(s:log_buffer)
let j = len(s:log_buffer) - num_row + 1
while j < num_row
let t = s:log_buffer[j]
let prev = s:log_buffer[j - 1]
echo reltimestr(t.elapse) t.name reltimestr(reltime(prev.elapse, t.elapse))
let j = j + 1
endwhile
endfunction
function! tsuquyomi#perfLogger#record(event_name)
if g:tsuquyomi_debug
call add(s:log_buffer, {'name': a:event_name, 'elapse': reltime(s:start_time)})
endif
endfunction

@ -0,0 +1,940 @@
"============================================================================
" FILE: tsuquyomi.vim
" AUTHOR: Quramy <yosuke.kurami@gmail.com>
"============================================================================
scriptencoding utf-8
let s:save_cpo = &cpo
set cpo&vim
if !tsuquyomi#config#preconfig()
finish
endif
let s:script_dir = expand('<sfile>:p:h')
let s:V = vital#of('tsuquyomi')
let s:JSON = s:V.import('Web.JSON')
let s:Filepath = s:V.import('System.Filepath')
let s:is_vim8 = !has('nvim') && has('patch-8.0.1')
if !s:is_vim8 || g:tsuquyomi_use_vimproc
let s:P = s:V.import('ProcessManager')
let s:tsq = 'tsuquyomiTSServer'
else
let s:tsq = {'job':0}
endif
let s:request_seq = 0
let s:ignore_response_conditions = []
" ignore events configFileDiag triggered by reload event. See also #99
call add(s:ignore_response_conditions, '"type":"event","event":"configFileDiag"')
call add(s:ignore_response_conditions, '"type":"event","event":"telemetry"')
call add(s:ignore_response_conditions, '"type":"event","event":"projectsUpdatedInBackground"')
call add(s:ignore_response_conditions, '"type":"event","event":"typingsInstallerPid"')
call add(s:ignore_response_conditions, 'npm notice created a lockfile')
" ### Async variables
let s:callbacks = {}
let s:notify_callback = {}
let s:quickfix_list = []
" ### }}}
" ### Utilites {{{
function! s:error(msg)
echoerr (a:msg)
endfunction
function! s:debugLog(msg)
if g:tsuquyomi_debug
echom a:msg
endif
endfunction
" ### Utilites }}}
" ### Core Functions {{{
"
" If not exsiting process of TSServer, create it.
function! s:startTssVimproc()
if s:P.state(s:tsq) == 'existing'
return 'existing'
endif
let l:cmd = substitute(tsuquyomi#config#tsscmd(), '\\', '\\\\', 'g').' '.tsuquyomi#config#tssargs()
let l:is_new = s:P.touch(s:tsq, l:cmd)
if l:is_new == 'new'
let [out, err, type] = s:P.read_wait(s:tsq, 0.1, [])
let st = tsuquyomi#tsClient#statusTss()
if !g:tsuquyomi_tsserver_debug
if err != ''
call s:error('Fail to start TSServer... '.err)
endif
endif
endif
return l:is_new
endfunction
function! s:startTssVim8()
if type(s:tsq['job']) == 8 && job_info(s:tsq['job']).status == 'run'
return 'existing'
endif
let l:cmd = substitute(tsuquyomi#config#tsscmd(), '\\', '\\\\', 'g').' '.tsuquyomi#config#tssargs()
try
let s:tsq['job'] = job_start(l:cmd, {
\ 'out_cb': {ch, msg -> tsuquyomi#tsClient#handleMessage(ch, msg)},
\ })
let s:tsq['channel'] = job_getchannel(s:tsq['job'])
let out = ch_readraw(s:tsq['channel'])
let st = tsuquyomi#tsClient#statusTss()
if !g:tsuquyomi_tsserver_debug
if err != ''
call s:error('Fail to start TSServer... '.err)
return 0
endif
endif
catch
return 0
endtry
return 1
endfunction
function! s:getEventType(item)
if type(a:item) == v:t_dict
\ && has_key(a:item, 'type')
\ && a:item.type ==# 'event'
\ && (a:item.event ==# 'syntaxDiag'
\ || a:item.event ==# 'semanticDiag'
\ || a:item.event ==# 'requestCompleted')
return 'diagnostics'
endif
return 0
endfunction
function! tsuquyomi#tsClient#startTss()
if !s:is_vim8 || g:tsuquyomi_use_vimproc
return s:startTssVimproc()
else
return s:startTssVim8()
endif
endfunction
"
"Terminate TSServer process if it exsits.
function! tsuquyomi#tsClient#stopTss()
if tsuquyomi#tsClient#statusTss() != 'dead'
if !s:is_vim8 || g:tsuquyomi_use_vimproc
let l:res = s:P.term(s:tsq)
return l:res
else
let l:res = job_stop(s:tsq['job'])
return l:res
endif
endif
endfunction
function! tsuquyomi#tsClient#stopTssSync() abort
let res = tsuquyomi#tsClient#stopTss()
call s:ensureStatus('dead')
return res
endfunction
" Wait for the status to become the argument.
" Note: It throws an error to avoid infinite loop after 1s.
function! s:ensureStatus(expected) abort
let cnt = 0
while v:true
let got = tsuquyomi#tsClient#statusTss()
if got ==# a:expected
return
endif
if cnt > 100
throw "TSServer status does not become " . a:expected . " in 1s. It is " . got . "."
endif
let cnt += 1
sleep 10m
endwhile
endfunction
" RETURNS: {string} 'run' or 'dead'
function! tsuquyomi#tsClient#statusTss()
try
if !s:is_vim8 || g:tsuquyomi_use_vimproc
let stat = s:P.state(s:tsq)
if stat == 'undefined'
return 'dead'
elseif stat == 'reading'
return 'run'
else
return stat
endif
else
return job_info(s:tsq['job']).status
endif
catch
return 'dead'
endtry
endfunction
"
"Read diagnostics and add to QuickList.
"
" PARAM: {dict} response
function! tsuquyomi#tsClient#readDiagnostics(item)
if a:item.event == 'requestCompleted'
if has_key(s:notify_callback, 'diagnostics')
let Callback = function(s:notify_callback['diagnostics'], [s:quickfix_list])
call Callback()
let s:quickfix_list = []
let s:request_seq = s:request_seq + 1
endif
else
" Cache syntaxDiag and semanticDiag messages until request was completed.
let l:qflist = tsuquyomi#parseDiagnosticEvent(a:item, [])
let s:quickfix_list += l:qflist
endif
endfunction
function! tsuquyomi#tsClient#registerNotify(callback, key)
let s:notify_callback[a:key] = a:callback
endfunction
"
" Handle TSServer responses.
"
function! tsuquyomi#tsClient#handleMessage(ch, msg)
if type(a:msg) != 1 || a:msg == ''
" Not a string or blank message.
return
endif
let l:res_item = substitute(a:msg, 'Content-Length: \d\+', '', 'g')
if l:res_item == ''
" Ignore content-length.
return
endif
" Ignore messages.
let l:to_be_ignored = 0
for ignore_reg in s:ignore_response_conditions
let l:to_be_ignored = l:to_be_ignored || (l:res_item =~ ignore_reg)
if l:to_be_ignored
return
endif
endfor
let l:item = json_decode(l:res_item)
let l:eventName = s:getEventType(l:item)
if(has_key(s:callbacks, l:eventName))
let Callback = function(s:callbacks[l:eventName], [l:item])
call Callback()
endif
endfunction
function! tsuquyomi#tsClient#clearCallbacks()
let s:callbacks = {}
endfunction
function! tsuquyomi#tsClient#registerCallback(callback, eventName)
let s:callbacks[a:eventName] = a:callback
endfunction
function! tsuquyomi#tsClient#sendAsyncRequest(line)
if s:is_vim8 && g:tsuquyomi_use_vimproc == 0
call tsuquyomi#tsClient#startTss()
call ch_sendraw(s:tsq['channel'], a:line . "\n")
endif
endfunction
"
"Write to stdin of tsserver proc, and return stdout.
"
" PARAM: {string} line Stdin input.
" PARAM: {float} delay Wait time(sec) after request, until response.
" PARAM: {int} retry_count Retry count.
" PARAM: {int} response_length The number of JSONs contained by this response.
" RETURNS: {list<dict>} A list of response.
function! tsuquyomi#tsClient#sendRequest(line, delay, retry_count, response_length)
"call s:debugLog('called! '.a:line)
call tsuquyomi#tsClient#startTss()
if !s:is_vim8 || g:tsuquyomi_use_vimproc
call s:P.writeln(s:tsq, a:line)
else
call ch_sendraw(s:tsq['channel'], a:line."\n")
endif
let l:retry = 0
let response_list = []
while len(response_list) < a:response_length
if !s:is_vim8 || g:tsuquyomi_use_vimproc
let [out, err, type] = s:P.read_wait(s:tsq, a:delay, ['Content-Length: \d\+'])
call s:debugLog('out: '.out.', type:'.type)
if type == 'timedout'
let retry_delay = 0.05
while l:retry < a:retry_count
let [out, err, type] = s:P.read_wait(s:tsq, retry_delay, ['Content-Length: \d\+'])
if type == 'matched'
call tsuquyomi#perfLogger#record('tssMatched')
"call s:debugLog('retry: '.l:retry.', length: '.len(response_list))
break
endif
let l:retry = l:retry + 1
call tsuquyomi#perfLogger#record('tssRetry:'.l:retry)
endwhile
endif
else
let out = ch_readraw(s:tsq['channel'])
let type = 'matched'
endif
if type == 'matched'
let l:tmp1 = substitute(out, 'Content-Length: \d\+', '', 'g')
let l:tmp2 = substitute(l:tmp1, '\r', '', 'g')
let l:res_list = split(l:tmp2, '\n\+')
for res_item in l:res_list
let l:to_be_ignored = 0
for ignore_reg in s:ignore_response_conditions + ['"type":"event","event":"requestCompleted"']
let l:to_be_ignored = l:to_be_ignored || (res_item =~ ignore_reg)
if l:to_be_ignored
break
endif
endfor
let l:decoded_res_item = s:JSON.decode(res_item)
let l:to_be_ignored = l:to_be_ignored || (has_key(l:decoded_res_item, 'request_seq') && l:decoded_res_item.request_seq != s:request_seq)
if !l:to_be_ignored
call add(response_list, decoded_res_item)
endif
endfor
else
echom '[Tsuquyomi] TSServer request was timeout:'.a:line
return response_list
endif
endwhile
"call s:debugLog(a:response_length.', '.len(response_list))
let s:request_seq = s:request_seq + 1
return response_list
endfunction
"
" Send a command to TSServer.
" This function is called pseudo synchronously.
" PARAM: {string} cmd Command type. e.g. 'completion', etc...
" PARAM: {dictionary} args Arguments object. e.g. {'file': 'myApp.ts'}.
" RETURNS: {list<dictionary>}
function! tsuquyomi#tsClient#sendCommandSyncResponse(cmd, args)
let l:input = s:JSON.encode({'command': a:cmd, 'arguments': a:args, 'type': 'request', 'seq': s:request_seq})
call tsuquyomi#perfLogger#record('beforeCmd:'.a:cmd)
let l:stdout_list = tsuquyomi#tsClient#sendRequest(l:input, str2float("0.0001"), 1000, 1)
call tsuquyomi#perfLogger#record('afterCmd:'.a:cmd)
let l:length = len(l:stdout_list)
if l:length == 1
"if res.success == 0
" echom '[Tsuquyomi] TSServer command fail. command: '.res.command.', message: '.res.message
"endif
call tsuquyomi#perfLogger#record('afterDecode:'.a:cmd)
return l:stdout_list
else
return []
endif
endfunction
function! tsuquyomi#tsClient#sendCommandSyncEvents(cmd, args, delay, length)
let l:input = s:JSON.encode({'command': a:cmd, 'arguments': a:args, 'type': 'request', 'seq': s:request_seq})
let l:stdout_list = tsuquyomi#tsClient#sendRequest(l:input, a:delay, 2000, a:length)
"echo l:stdout_list
let l:length = len(l:stdout_list)
let l:result_list = []
if l:length > 0
for res in l:stdout_list
if res.type != 'event'
"echom '[Tsuquyomi] TSServer return invalid response: '.string(res)
else
call add(l:result_list, res)
endif
endfor
return l:result_list
else
return []
endif
endfunction
function! tsuquyomi#tsClient#sendCommandOneWay(cmd, args)
let l:input = s:JSON.encode({'command': a:cmd, 'arguments': a:args, 'type': 'request', 'seq': s:request_seq})
call tsuquyomi#tsClient#sendRequest(l:input, str2float("0.01"), 0, 0)
return []
endfunction
function! tsuquyomi#tsClient#getResponseBodyAsList(responses)
if len(a:responses) != 1
return []
endif
let response = a:responses[0]
if has_key(response, 'success') && response.success && has_key(response, 'body')
return response.body
else
return []
endif
endfunction
function! tsuquyomi#tsClient#getResponseBodyAsDict(responses)
if len(a:responses) != 1
return {}
endif
let response = a:responses[0]
if has_key(response, 'success') && response.success && has_key(response, 'body')
return response.body
else
return {}
endif
endfunction
"
" Send a command to TSServer.
" This function is called asynchronously.
" PARAM: {string} cmd Command type. e.g. 'completion', etc...
" PARAM: {dictionary} args Arguments object. e.g. {'file': 'myApp.ts'}.
function! tsuquyomi#tsClient#sendCommandAsyncEvents(cmd, args)
let s:quickfix_list = []
let l:input = json_encode({'command': a:cmd, 'arguments': a:args, 'type': 'request', 'seq': s:request_seq})
" call tsuquyomi#perfLogger#record('beforeCmd:'.a:cmd)
call tsuquyomi#tsClient#sendAsyncRequest(l:input)
endfunction
"
" ### Core Functions }}}
" ### TSServer command wrappers {{{
"
" There are client's methods of TSServer.
" If you want to know details of APIs, see 'typescript/src/server/protocol.d.ts'.
"
" Send oepn command to TSServer.
" This command does not return any response.
function! tsuquyomi#tsClient#tsOpen(file)
let l:args = {'file': a:file}
call tsuquyomi#tsClient#sendCommandOneWay('open', l:args)
endfunction
" Send close command to TSServer.
" This command does not return any response.
function! tsuquyomi#tsClient#tsClose(file)
let l:args = {'file': a:file}
return tsuquyomi#tsClient#sendCommandOneWay('close', l:args)
endfunction
" Save an opened file to tmpfile.
" This function can be called for only debugging.
" This command does not return any response.
function! tsuquyomi#tsClient#tsSaveto(file, tmpfile)
let l:args = {'file': a:file, 'tmpfile': a:tmpfile}
return tsuquyomi#tsClient#sendCommandOneWay('saveto', l:args)
endfunction
" Fetch keywards to complete from TSServer.
" PARAM: {string} file File name.
" PARAM: {string} line The line number of location to complete.
" PARAM: {string} offset The col number of location to complete.
" PARAM: {string} prefix Prefix to filter result set.
" RETURNS: {list} A List of completion info Dictionary.
" e.g. :
" [
" {'name': 'close', 'kindModifiers': 'declare', 'kind': 'function'},
" {'name': 'clipboardData', 'kindModifiers': 'declare', 'kind': 'var'}
" ]
function! tsuquyomi#tsClient#tsCompletions(file, line, offset, prefix)
let l:args = {'file': a:file, 'line': a:line, 'offset': a:offset, 'prefix': a:prefix}
let l:result = tsuquyomi#tsClient#sendCommandSyncResponse('completions', l:args)
return tsuquyomi#tsClient#getResponseBodyAsList(l:result)
endfunction
" Emmit to change file to TSServer.
" Param: {string} file File name to change.
" Param: {int} line The line number of starting point of range to change.
" Param: {int} offset The col number of starting point of range to change.
" Param: {int} endLine The line number of end point of range to change.
" Param: {int} endOffset The col number of end point of range to change.
" Param: {string} insertString String after replacing
" This command does not return any response.
function! tsuquyomi#tsClient#tsChange(file, line, offset, endLine, endOffset, insertString)
let l:args = {'file': a:file, 'line': a:line, 'offset': a:offset, 'endLine': a:endLine, 'endOffset': a:endOffset, 'insertString': a:insertString}
return tsuquyomi#tsClient#sendCommandOneWay('change', l:args)
endfunction
" Fetch details of completion from TSServer.
" PARAM: {string} file File name.
" PARAM: {int} line The line number of location to complete.
" PARAM: {int} offset The col number of location to complete.
" PARAM: {list<string>} entryNames A list of names. These names may be fetched by tsuquyomi#tsClient#tsCompletions function.
" RETURNS: {list} A list of details.
" e.g. :
" [{
" 'name': 'DOMError',
" 'kind': 'var',
" 'kindModifier': 'declare',
" 'displayParts': [
" {'kind': 'keyword', 'text': 'interface'},
" {'kind': 'space', 'text': ' '},
" ...
" {'kind': 'lineBreak', 'text': '\n'},
" ...
" ]
" }, ...]
function! tsuquyomi#tsClient#tsCompletionEntryDetails(file, line, offset, entryNames)
let l:args = {'file': a:file, 'line': a:line, 'offset': a:offset, 'entryNames': a:entryNames}
let l:result = tsuquyomi#tsClient#sendCommandSyncResponse('completionEntryDetails', l:args)
return tsuquyomi#tsClient#getResponseBodyAsList(l:result)
endfunction
" Configure editor parameter
" PARAM: {string} file File name.
" PARAM: {string} hostInfo Information of Vim
" PARAM: {dict} formatOptions Options for editor. See the following FormatCodeSetting interface.
" PARAM: {list} extraFileExtensions List of extensions
"
" interface FormatCodeSetting {
" baseIndentSize?: number;
" indentSize?: number;
" tabSize?: number;
" newLineCharacter?: string;
" convertTabsToSpaces?: boolean;
" indentStyle?: IndentStyle | ts.IndentStyle;
" insertSpaceAfterCommaDelimiter?: boolean;
" insertSpaceAfterSemicolonInForStatements?: boolean;
" insertSpaceBeforeAndAfterBinaryOperators?: boolean;
" insertSpaceAfterConstructor?: boolean;
" insertSpaceAfterKeywordsInControlFlowStatements?: boolean;
" insertSpaceAfterFunctionKeywordForAnonymousFunctions?: boolean;
" insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis?: boolean;
" insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets?: boolean;
" insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces?: boolean;
" insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces?: boolean;
" insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces?: boolean;
" insertSpaceBeforeFunctionParenthesis?: boolean;
" placeOpenBraceOnNewLineForFunctions?: boolean;
" placeOpenBraceOnNewLineForControlBlocks?: boolean;
" }
let s:NON_BOOLEAN_KEYS_IN_FOPT = [ 'baseIndentSize', 'indentSize', 'tabSize', 'newLineCharacter', 'convertTabsToSpaces', 'indentStyle' ]
function! tsuquyomi#tsClient#tsConfigure(file, hostInfo, formatOptions, extraFileExtensions)
let fopt = { }
for k in keys(a:formatOptions)
if index(s:NON_BOOLEAN_KEYS_IN_FOPT, k) != -1
let fopt[k] = a:formatOptions[k]
else
let fopt[k] = a:formatOptions[k] ? s:JSON.true : s.JSON.false
endif
endfor
let l:args = {
\ 'file': a:file,
\ 'hostInfo': a:hostInfo,
\ 'formatOptions': fopt,
\ 'extraFileExtensions': a:extraFileExtensions
\ }
return tsuquyomi#tsClient#sendCommandOneWay('configure', l:args)
endfunction
" Fetch location where the symbol at cursor(line, offset) in file is defined.
" PARAM: {string} file File name.
" PARAM: {int} line The line number of location to complete.
" PARAM: {int} offset The col number of location to complete.
" RETURNS: {list<dict>} A list of dictionaries of definition location.
" e.g. :
" [{'file': 'hogehoge.ts', 'start': {'line': 3, 'offset': 2}, 'end': {'line': 3, 'offset': 10}}]
function! tsuquyomi#tsClient#tsDefinition(file, line, offset)
let l:args = {'file': a:file, 'line': a:line, 'offset': a:offset}
let l:result = tsuquyomi#tsClient#sendCommandSyncResponse('definition', l:args)
return tsuquyomi#tsClient#getResponseBodyAsList(l:result)
endfunction
" Format = "format";
function! tsuquyomi#tsClient#tsFormat(file, line, offset, endLine, endOffset)
call s:error('not implemented!')
endfunction
" Formatonkey = "formatonkey";
function! tsuquyomi#tsClient#tsFormationkey(file, line, offset, key)
call s:error('not implemented!')
endfunction
" Get error for files.
" PARAM: {list<string>} files List of filename
" PARAM: {int} delay Delay time [msec].
" PARAM: {list<dict>} error event list
function! tsuquyomi#tsClient#tsGeterr(files, delay)
let l:args = {'files': a:files, 'delay': a:delay}
let l:delaySec = a:delay * 1.0 / 1000.0
let l:typeCount = tsuquyomi#config#isHigher(280) ? 3 : 2
let l:result = tsuquyomi#tsClient#sendCommandSyncEvents('geterr', l:args, l:delaySec, len(a:files) * l:typeCount)
return l:result
endfunction
" Get errors for project.
" This command is available only at tsserver ~v.1.6
" PARAM: {string} file File name in target project.
" PARAM: {int} delay Delay time [msec].
" PARAM: {count} count The number of files in the project(you can fetch this from tsProjectInfo).
" PARAM: {list<dict>} error event list
function! tsuquyomi#tsClient#tsGeterrForProject(file, delay, count)
let l:args = {'file': a:file, 'delay': a:delay}
let l:delaySec = a:delay * 1.0 / 1000.0
let l:typeCount = tsuquyomi#config#isHigher(280) ? 3 : 2
let l:result = tsuquyomi#tsClient#sendCommandSyncEvents('geterrForProject', l:args, l:delaySec, a:count * l:typeCount)
return l:result
endfunction
" Fetch a list of implementations of an interface.
" PARAM: {string} file File name.
" PARAM: {int} line The line number of the symbol's position.
" PARAM: {int} offset The col number of the symbol's position.
" RETURNS: {list<dict>} Reference information.
" e.g. :
" [
" {
" 'file': 'SomeClass.ts',
" 'start': {'offset': 11, 'line': 23}, 'end': {'offset': 5, 'line': 35}
" }, {
" 'file': 'OtherClass.ts',
" 'start': {'offset': 31, 'line': 26}, 'end': {'offset': 68, 'line': 26}
" }
" ]
function! tsuquyomi#tsClient#tsImplementation(file, line, offset)
let l:args = {'file': a:file, 'line': a:line, 'offset': a:offset}
let l:result = tsuquyomi#tsClient#sendCommandSyncResponse('implementation', l:args)
return tsuquyomi#tsClient#getResponseBodyAsList(l:result)
endfunction
" Fetch navigation list from TSServer.
" PARAM: {string} file File name.
" RETURNS: {list<dict>} Navigation info
" e.g. :
" [{
" 'text': 'ModName',
" 'kind': 'module',
" 'kindModifiers: '',
" 'spans': [{
" 'start': {'line': 1, 'offset': 5},
" 'end': {'line': 1, 'offset': 12},
" }],
" childItems: [
" ... " REMAKS: childItems contains a recursive structure.
" ]
" }]
function! tsuquyomi#tsClient#tsNavBar(file)
let l:args = {'file': a:file}
let l:result = tsuquyomi#tsClient#sendCommandSyncResponse('navbar', l:args)
return tsuquyomi#tsClient#getResponseBodyAsList(l:result)
endfunction
" Navto = "navto";
function! tsuquyomi#tsClient#tsNavto(file, searchValue, maxResultCount)
let l:args = {'file': a:file, 'searchValue': a:searchValue, 'maxResultCount': a:maxResultCount}
let l:result = tsuquyomi#tsClient#sendCommandSyncResponse('navto', l:args)
return tsuquyomi#tsClient#getResponseBodyAsList(l:result)
endfunction
" Fetch quickinfo from TSServer.
" PARAM: {string} file File name.
" PARAM: {int} line The line number of the symbol's position.
" PARAM: {int} offset The col number of the symbol's position.
" RETURNS: {dict}
" e.g. :
" {
" 'kind': 'method',
" 'kindModifiers': '',
" 'displayString': '(method) SimpleModule.MyClass.say(): string',
" 'start': {'line': 2, 'offset': 2},
" 'start': {'line': 2, 'offset': 9}
" }
function! tsuquyomi#tsClient#tsQuickinfo(file, line, offset)
let l:args = {'file': a:file, 'line': a:line, 'offset': a:offset}
let l:result = tsuquyomi#tsClient#sendCommandSyncResponse('quickinfo', l:args)
return tsuquyomi#tsClient#getResponseBodyAsDict(l:result)
endfunction
" Fetch a list of references.
" PARAM: {string} file File name.
" PARAM: {int} line The line number of the symbol's position.
" PARAM: {int} offset The col number of the symbol's position.
" RETURNS: {dictionary} Reference information.
" e.g. :
" {
" 'symbolName': 'SomeClass',
" 'symbolDisplayString': 'SomeModule.SomeClass',
" 'refs': [
" {
" 'file': 'SomeClass.ts', 'isWriteAccess': 1,
" 'start': {'line': 3', 'offset': 2}, 'end': {'line': 3, 'offset': 20},
" 'lineText': 'export class SomeClass {'
" }, {
" 'file': 'OtherClass.ts', 'isWriteAccess': 0,
" 'start': {'line': 5', 'offset': 2}, 'end': {'line': 5, 'offset': 20},
" 'lineText': 'export class OtherClass extends SomeClass{'
" }
" ]
" }
function! tsuquyomi#tsClient#tsReferences(file, line, offset)
let l:args = {'file': a:file, 'line': a:line, 'offset': a:offset}
let l:result = tsuquyomi#tsClient#sendCommandSyncResponse('references', l:args)
return tsuquyomi#tsClient#getResponseBodyAsDict(l:result)
endfunction
" Reload an opend file.
" It can be used for telling change of buffer to TSServer.
" PARAM: {string} file File name
" PARAM: {string} tmpfile
" RETURNS: {0|1}
function! tsuquyomi#tsClient#tsReload(file, tmpfile)
let l:arg = {'file': a:file, 'tmpfile': a:tmpfile}
" With ts > 2.6 and ts <=1.9, tsserver emit 2 responses by reload request.
" ignore 2nd response of reload command. See also #62
if tsuquyomi#config#isHigher(260) || !tsuquyomi#config#isHigher(190)
let l:res_count = 1
else
let l:res_count = 2
endif
let l:result = tsuquyomi#tsClient#sendCommandSyncEvents('reload', l:arg, 0.01, l:res_count)
"echo l:result
if(len(l:result) >= 1)
if(has_key(l:result[0], 'success'))
return l:result[0].success
else
return 0
endif
else
return 0
endif
endfunction
" Fetch locatoins of symbols to be replaced from TSServer.
" PARAM: {string} file File name.
" PARAM: {int} line The line number of the symbol's position.
" PARAM: {int} offset The col number of the symbol's position.
" PARAM: {0|1} findInComments Whether result contains word in comments.
" PARAM: {0|1} findInString Whether result contains word in String literals.
" RETURNS: {dict} Rename information dictionary.
" e.g.:
" {
" 'info': {
" 'canRename': 1,
" 'displayName': 'myApp',
" 'fullDisplayName': 'myApp',
" 'kind': 'class',
" 'kindModifiers': '',
" 'triggerSpan': {
" 'start': 44,
" 'length': 5
" },
" },
" 'locs': [{
" 'file': 'hoge.ts'',
" 'locs': [
" {'start':{'line': 3, 'offset': 4}, 'end':{'line': 3, 'offset': 12}},
" ...
" ]
" },
" ...,
" ]
" }
function! tsuquyomi#tsClient#tsRename(file, line, offset, findInComments, findInString)
" TODO findInString parameter does not work... why?
let l:arg = {'file': a:file, 'line': a:line, 'offset': a:offset,
\ 'findInComments': a:findInComments ? s:JSON.true : s:JSON.false,
\ 'findInString' : a:findInString ? s:JSON.true : s:JSON.false
\ }
let l:result = tsuquyomi#tsClient#sendCommandSyncResponse('rename', l:arg)
return tsuquyomi#tsClient#getResponseBodyAsDict(l:result)
endfunction
" Find brace matching pair.
" Vim has brace matching natively, so Tsuquyomi does not support this method.
function! tsuquyomi#tsClient#tsBrace(file, line, offset)
call s:error('not implemented!')
endfunction
" This command is available only at tsserver ~v.1.6
function! tsuquyomi#tsClient#tsDocumentHighlights(file, line, offset, filesToSearch)
call s:error('not implemented!')
endfunction
" Fetch project information.
" This command is available only at tsserver ~v.1.6
" PARAM: {string} file File name.
" PARAM: {0|1} needFileNameList Whether include list of files in response.
" RETURNS: dict Project information dictionary.
" e.g.:
" {
" 'configFileName': '/samplePrjs/prj001/tsconfig.json',
" 'fileNames': [
" '/PATH_TO_TYPESCRIPT/node_modules/typescript/lib/lib.d.ts',
" '/samplePrjs/prj001/main.ts'
" ]
" }
function! tsuquyomi#tsClient#tsProjectInfo(file, needFileNameList)
let l:arg = {'file': a:file,
\ 'needFileNameList': a:needFileNameList ? s:JSON.true : s:JSON.false
\ }
let l:result = tsuquyomi#tsClient#sendCommandSyncResponse('projectInfo', l:arg)
return tsuquyomi#tsClient#getResponseBodyAsDict(l:result)
endfunction
" Fetch method signature information from TSServer.
" PARAM: {string} file File name.
" PARAM: {int} line The line number of the symbol's position.
" PARAM: {int} offset The col number of the symbol's position.
" RETURNS: {dict}
" e.g. :
" {
" 'selectedItemIndex': 0,
" 'argumentCount': 1,
" 'argumentIndex': 0,
" 'applicableSpan': {
" 'start': { 'offset': 27, 'line': 25 }, 'end': { 'offset': 40, 'line': 25 }
" },
" 'items': [{
" 'tags': [],
" 'separatorDisplayParts': [
" { 'kind': 'punctuation', 'text': ',' },
" { 'kind': 'space', 'text': ' ' }
" ],
" 'prefixDisplayParts': [
" { 'kind': 'methodName', 'text': 'deleteTerms' },
" { 'kind': 'punctuation', 'text': '(' }
" ],
" 'parameters': [
" {
" 'isOptional': 0,
" 'name': 'id',
" 'documentation': [],
" 'displayParts': [
" { 'kind': 'parameterName', 'text': 'id' },
" { 'kind': 'punctuation', 'text': ':' },
" { 'kind': 'space', 'text': ' ' },
" { 'kind': 'keyword', 'text': 'number' }
" ]
" }
" ],
" 'suffixDisplayParts': [
" { 'kind': 'punctuation', 'text': ')' },
" { 'kind': 'punctuation', 'text': ':' },
" { 'kind': 'space', 'text': ' ' },
" { 'kind': 'className', 'text': 'Observable' },
" { 'kind': 'punctuation', 'text': '<' },
" { 'kind': 'interfaceName', 'text': 'ApiResponseData' },
" { 'kind': 'punctuation', 'text': '>' }
" ],
" 'isVariadic': 0,
" 'documentation': []
" }]
" }
"
" This can be combined into a simple signature like this:
" deleteTerms(id: number): Observable<ApiResponseData>
function! tsuquyomi#tsClient#tsSignatureHelp(file, line, offset)
let l:args = {'file': a:file, 'line': a:line, 'offset': a:offset}
let l:result = tsuquyomi#tsClient#sendCommandSyncResponse('signatureHelp', l:args)
return tsuquyomi#tsClient#getResponseBodyAsDict(l:result)
endfunction
" Fetch location where the type of the symbol at cursor(line, offset) in file is defined.
" PARAM: {string} file File name.
" PARAM: {int} line The line number of location to complete.
" PARAM: {int} offset The col number of location to complete.
" RETURNS: {list<dict>} A list of dictionaries of type definition location.
" e.g. :
" [{'file': 'hogehoge.ts', 'start': {'line': 3, 'offset': 2}, 'end': {'line': 3, 'offset': 10}}]
function! tsuquyomi#tsClient#tsTypeDefinition(file, line, offset)
let l:args = {'file': a:file, 'line': a:line, 'offset': a:offset}
let l:result = tsuquyomi#tsClient#sendCommandSyncResponse('typeDefinition', l:args)
return tsuquyomi#tsClient#getResponseBodyAsList(l:result)
endfunction
" Reload prjects.
" This command is available only at tsserver ~v.1.6
" This command does not return any response.
function! tsuquyomi#tsClient#tsReloadProjects()
return tsuquyomi#tsClient#sendCommandOneWay('reloadProjects', {})
endfunction
" This command is available only at tsserver ~v.2.1
" PARAM: {string} file File name.
" PARAM: {number} startLine The line number for the req
" PARAM: {number} startOffset The character offset for the req
" PARAM: {number} endLine The line number for the req
" PARAM: {number} endOffset The character offset for the req
" PARAM: {list<number>} errorCodes Error codes we want to get the fixes for
" RETURNS: {list<dict>}
" e.g.:
" [
" {
" 'description': 'Add missing ''super()'' call.',
" 'changes': [
" {
" 'fileName': '/SamplePrj/codeFixesTest.ts',
" 'textChanges': [
" {
" 'start': {'offset': 20, 'line': 6},
" 'end': {'offset': 20, 'line': 6},
" 'newText': 'super();'
" }
" ]
" }
" ]
" }
" ]
function! tsuquyomi#tsClient#tsGetCodeFixes(file, startLine, startOffset, endLine, endOffset, errorCodes)
let l:arg = {
\ 'file': a:file,
\ 'startLine': a:startLine, 'startOffset': a:startOffset,
\ 'endLine': a:endLine, 'endOffset': a:endOffset,
\ 'errorCodes': a:errorCodes
\ }
let l:result = tsuquyomi#tsClient#sendCommandSyncResponse('getCodeFixes', l:arg)
return tsuquyomi#tsClient#getResponseBodyAsList(l:result)
endfunction
" Get available code fixes list
" This command is available only at tsserver ~v.2.1
function! tsuquyomi#tsClient#tsGetSupportedCodeFixes()
let l:result = tsuquyomi#tsClient#sendCommandSyncResponse('getSupportedCodeFixes', {})
let l:body = tsuquyomi#tsClient#getResponseBodyAsDict(l:result)
if (type(l:body) != v:t_list)
return []
else
return l:body
endif
endfunction
"
" Emmit to change file to TSServer.
" Param: {string} file File name to change.
" Param: {int} line The line number of starting point of range to change.
" Param: {int} offset The col number of starting point of range to change.
" Param: {int} endLine The line number of end point of range to change.
" Param: {int} endOffset The col number of end point of range to change.
" Param: {string} insertString String after replacing
" This command does not return any response.
function! tsuquyomi#tsClient#tsAsyncChange(file, line, offset, endLine, endOffset, insertString)
let l:args = {'file': a:file, 'line': a:line, 'offset': a:offset, 'endLine': a:endLine, 'endOffset': a:endOffset, 'insertString': a:insertString}
call tsuquyomi#tsClient#sendCommandAsyncEvents('change', l:args)
endfunction
"
" Get error for files.
" PARAM: {list<string>} files List of filename
" PARAM: {int} delay Delay time [msec].
function! tsuquyomi#tsClient#tsAsyncGeterr(files, delay)
let l:args = {'files': a:files, 'delay': a:delay}
let l:delaySec = a:delay * 1.0 / 1000.0
let l:typeCount = tsuquyomi#config#isHigher(280) ? 3 : 2
call tsuquyomi#tsClient#sendCommandAsyncEvents('geterr', l:args)
endfunction
" ### TSServer command wrappers }}}
let &cpo = s:save_cpo
unlet s:save_cpo

@ -0,0 +1,66 @@
"============================================================================
" FILE: autoload/unite/sources/outline/typescript.vim
" AUTHOR: Quramy <yosuke.kurami@gmail.com>
"============================================================================
scriptencoding utf-8
" Tsuquyomi outline info for TypeScript.
function! unite#sources#outline#typescript#outline_info()
return s:outline_info
endfunction
let s:Tree = unite#sources#outline#import('Tree')
let s:outline_info = {
\ 'is_volatile': 1,
\ 'auto_update': 0,
\ 'highlight_rules': [
\ {'name': 'package',
\ 'pattern': '/\S\+\s\+:module/'},
\ {'name': 'method',
\ 'pattern': '/\S\+\s\+:\%(method\|call\|construct\)/'},
\ {'name': 'id',
\ 'pattern': '/\S\+\s\+:\%(var\|alias\)/'},
\ {'name': 'expanded',
\ 'pattern': '/\S\+\s\+:\%(property\|index\)/'},
\ {'name': 'type',
\ 'pattern': '/\S\+\s\+:\%(class\|interface\)/'}
\ ]
\ }
function! s:createHeadingFromNavitem(navitem)
let heading = {}
let heading.word = a:navitem.text."\t:".a:navitem.kind
let heading.type = a:navitem.kind
if has_key(a:navitem, 'spans') && len(a:navitem.spans)
let heading.lnum = a:navitem.spans[0].start.line
endif
return heading
endfunction
function! s:createNodeRecursive(parent_node, navitem_list)
for navitem in a:navitem_list
let heading = s:createHeadingFromNavitem(navitem)
call s:Tree.append_child(a:parent_node, heading)
if has_key(navitem, 'childItems') && len(navitem.childItems)
call s:createNodeRecursive(heading, navitem.childItems)
endif
endfor
return a:parent_node
endfunction
function! s:outline_info.extract_headings(context)
let root = s:Tree.new()
" 1. Fetch navigation info from TSServer.
let [navbar_list, is_success] = tsuquyomi#navBar()
if is_success
let root = s:createNodeRecursive(root, navbar_list)
endif
return root
endfunction

@ -0,0 +1,66 @@
"============================================================================
" FILE: autoload/unite/sources/tsproject.vim
" AUTHOR: Quramy <yosuke.kurami@gmail.com>
"============================================================================
scriptencoding utf-8
let s:source = {
\ 'name': 'tsproject',
\ 'is_grouped': 1,
\ 'description': 'TypeScript project information',
\ }
function! s:source.gather_candidates(args, context)
if len(a:args)
let selected_group_name = a:args[0]
else
let selected_group_name = '.'
endif
let buf_name = expand('%:p')
let pinfo = tsuquyomi#projectInfo(buf_name)
let result = []
if has_key(pinfo, 'configFileName')
call add(result, {
\ 'group': 'tsconfig',
\ 'kind': 'file',
\ 'action__path': pinfo.configFileName,
\ 'word': pinfo.configFileName,
\ 'abbr': "\t".pinfo.configFileName,
\ 'source': 'tsproject'
\ })
else
call add(result, {
\ 'group': 'tsconfig',
\ 'kind': 'common',
\ 'is_dummy': 1,
\ 'word': '(your project does not have tsconfig.json)',
\ 'abbr': "\t(your project does not have tsconfig.json)",
\ 'source': 'tsproject'
\ })
endif
if has_key(pinfo, 'filteredFileNames')
for fileName in sort(copy(pinfo.filteredFileNames))
call add(result, {
\ 'group': 'files',
\ 'word': fileName,
\ 'abbr': "\t".fileName,
\ 'kind': 'file',
\ 'action__path': fileName,
\ 'source': 'tsproject'
\ })
endfor
endif
return result
endfunction
function! unite#sources#tsproject#define()
if tsuquyomi#config#isHigher(160)
return s:source
endif
endfunction

@ -0,0 +1,12 @@
function! vital#of(name) abort
let files = globpath(&runtimepath, 'autoload/vital/' . a:name . '.vital', 1)
let file = split(files, "\n")
if empty(file)
throw 'vital: version file not found: ' . a:name
endif
let ver = readfile(file[0], 'b')
if empty(ver)
throw 'vital: invalid version file: ' . a:name
endif
return vital#_{substitute(ver[0], '\W', '', 'g')}#new()
endfunction

@ -0,0 +1,5 @@
let s:_plugin_name = expand('<sfile>:t:r')
function! vital#{s:_plugin_name}#new() abort
return vital#{s:_plugin_name[1:]}#new()
endfunction

@ -0,0 +1,457 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
if v:version > 703 || v:version == 703 && has('patch1170')
function! vital#_tsuquyomi#Data#List#import() abort
return map({'combinations': '', 'and': '', 'sort_by': '', 'foldr1': '', 'sort': '', 'flatten': '', 'has_index': '', 'find_indices': '', 'any': '', 'unshift': '', 'span': '', 'pop': '', 'binary_search': '', 'uniq_by': '', 'or': '', 'all': '', 'zip': '', 'find_last_index': '', 'find': '', 'partition': '', 'map_accum': '', 'permutations': '', 'break': '', 'max_by': '', 'foldl': '', 'foldr': '', 'find_index': '', 'group_by': '', 'take_while': '', 'conj': '', 'push': '', 'char_range': '', 'cons': '', 'foldl1': '', 'intersect': '', 'concat': '', 'shift': '', 'clear': '', 'has_common_items': '', 'product': '', 'zip_fill': '', 'uniq': '', 'has': '', 'min_by': '', 'with_index': ''}, 'function("s:" . v:key)')
endfunction
else
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_tsuquyomi#Data#List#import() abort', printf("return map({'combinations': '', 'and': '', 'sort_by': '', 'foldr1': '', 'sort': '', 'flatten': '', 'has_index': '', 'find_indices': '', 'any': '', 'unshift': '', 'span': '', 'pop': '', 'binary_search': '', 'uniq_by': '', 'or': '', 'all': '', 'zip': '', 'find_last_index': '', 'find': '', 'partition': '', 'map_accum': '', 'permutations': '', 'break': '', 'max_by': '', 'foldl': '', 'foldr': '', 'find_index': '', 'group_by': '', 'take_while': '', 'conj': '', 'push': '', 'char_range': '', 'cons': '', 'foldl1': '', 'intersect': '', 'concat': '', 'shift': '', 'clear': '', 'has_common_items': '', 'product': '', 'zip_fill': '', 'uniq': '', 'has': '', 'min_by': '', 'with_index': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
endif
" ___vital___
" Utilities for list.
let s:save_cpo = &cpo
set cpo&vim
function! s:pop(list) abort
return remove(a:list, -1)
endfunction
function! s:push(list, val) abort
call add(a:list, a:val)
return a:list
endfunction
function! s:shift(list) abort
return remove(a:list, 0)
endfunction
function! s:unshift(list, val) abort
return insert(a:list, a:val)
endfunction
function! s:cons(x, xs) abort
return [a:x] + a:xs
endfunction
function! s:conj(xs, x) abort
return a:xs + [a:x]
endfunction
" Removes duplicates from a list.
function! s:uniq(list) abort
return s:uniq_by(a:list, 'v:val')
endfunction
" Removes duplicates from a list.
function! s:uniq_by(list, f) abort
let list = map(copy(a:list), printf('[v:val, %s]', a:f))
let i = 0
let seen = {}
while i < len(list)
let key = string(list[i][1])
if has_key(seen, key)
call remove(list, i)
else
let seen[key] = 1
let i += 1
endif
endwhile
return map(list, 'v:val[0]')
endfunction
function! s:clear(list) abort
if !empty(a:list)
unlet! a:list[0 : len(a:list) - 1]
endif
return a:list
endfunction
" Concatenates a list of lists.
" XXX: Should we verify the input?
function! s:concat(list) abort
let memo = []
for Value in a:list
let memo += Value
endfor
return memo
endfunction
" Take each elements from lists to a new list.
function! s:flatten(list, ...) abort
let limit = a:0 > 0 ? a:1 : -1
let memo = []
if limit == 0
return a:list
endif
let limit -= 1
for Value in a:list
let memo +=
\ type(Value) == type([]) ?
\ s:flatten(Value, limit) :
\ [Value]
unlet! Value
endfor
return memo
endfunction
" Sorts a list with expression to compare each two values.
" a:a and a:b can be used in {expr}.
function! s:sort(list, expr) abort
if type(a:expr) == type(function('function'))
return sort(a:list, a:expr)
endif
let s:expr = a:expr
return sort(a:list, 's:_compare')
endfunction
function! s:_compare(a, b) abort
return eval(s:expr)
endfunction
" Sorts a list using a set of keys generated by mapping the values in the list
" through the given expr.
" v:val is used in {expr}
function! s:sort_by(list, expr) abort
let pairs = map(a:list, printf('[v:val, %s]', a:expr))
return map(s:sort(pairs,
\ 'a:a[1] ==# a:b[1] ? 0 : a:a[1] ># a:b[1] ? 1 : -1'), 'v:val[0]')
endfunction
" Returns a maximum value in {list} through given {expr}.
" Returns 0 if {list} is empty.
" v:val is used in {expr}
function! s:max_by(list, expr) abort
if empty(a:list)
return 0
endif
let list = map(copy(a:list), a:expr)
return a:list[index(list, max(list))]
endfunction
" Returns a minimum value in {list} through given {expr}.
" Returns 0 if {list} is empty.
" v:val is used in {expr}
" FIXME: -0x80000000 == 0x80000000
function! s:min_by(list, expr) abort
return s:max_by(a:list, '-(' . a:expr . ')')
endfunction
" Returns List of character sequence between [a:from, a:to]
" e.g.: s:char_range('a', 'c') returns ['a', 'b', 'c']
function! s:char_range(from, to) abort
return map(
\ range(char2nr(a:from), char2nr(a:to)),
\ 'nr2char(v:val)'
\)
endfunction
" Returns true if a:list has a:value.
" Returns false otherwise.
function! s:has(list, value) abort
return index(a:list, a:value) isnot -1
endfunction
" Returns true if a:list[a:index] exists.
" Returns false otherwise.
" NOTE: Returns false when a:index is negative number.
function! s:has_index(list, index) abort
" Return true when negative index?
" let index = a:index >= 0 ? a:index : len(a:list) + a:index
return 0 <= a:index && a:index < len(a:list)
endfunction
" similar to Haskell's Data.List.span
function! s:span(f, xs) abort
let border = len(a:xs)
for i in range(len(a:xs))
if !eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g'))
let border = i
break
endif
endfor
return border == 0 ? [[], copy(a:xs)] : [a:xs[: border - 1], a:xs[border :]]
endfunction
" similar to Haskell's Data.List.break
function! s:break(f, xs) abort
return s:span(printf('!(%s)', a:f), a:xs)
endfunction
" similar to Haskell's Data.List.takeWhile
function! s:take_while(f, xs) abort
return s:span(a:f, a:xs)[0]
endfunction
" similar to Haskell's Data.List.partition
function! s:partition(f, xs) abort
return [filter(copy(a:xs), a:f), filter(copy(a:xs), '!(' . a:f . ')')]
endfunction
" similar to Haskell's Prelude.all
function! s:all(f, xs) abort
return !s:any(printf('!(%s)', a:f), a:xs)
endfunction
" similar to Haskell's Prelude.any
function! s:any(f, xs) abort
return !empty(filter(map(copy(a:xs), a:f), 'v:val'))
endfunction
" similar to Haskell's Prelude.and
function! s:and(xs) abort
return s:all('v:val', a:xs)
endfunction
" similar to Haskell's Prelude.or
function! s:or(xs) abort
return s:any('v:val', a:xs)
endfunction
function! s:map_accum(expr, xs, init) abort
let memo = []
let init = a:init
for x in a:xs
let expr = substitute(a:expr, 'v:memo', init, 'g')
let expr = substitute(expr, 'v:val', x, 'g')
let [tmp, init] = eval(expr)
call add(memo, tmp)
endfor
return memo
endfunction
" similar to Haskell's Prelude.foldl
function! s:foldl(f, init, xs) abort
let memo = a:init
for x in a:xs
let expr = substitute(a:f, 'v:val', string(x), 'g')
let expr = substitute(expr, 'v:memo', string(memo), 'g')
unlet memo
let memo = eval(expr)
endfor
return memo
endfunction
" similar to Haskell's Prelude.foldl1
function! s:foldl1(f, xs) abort
if len(a:xs) == 0
throw 'vital: Data.List: foldl1'
endif
return s:foldl(a:f, a:xs[0], a:xs[1:])
endfunction
" similar to Haskell's Prelude.foldr
function! s:foldr(f, init, xs) abort
return s:foldl(a:f, a:init, reverse(copy(a:xs)))
endfunction
" similar to Haskell's Prelude.fold11
function! s:foldr1(f, xs) abort
if len(a:xs) == 0
throw 'vital: Data.List: foldr1'
endif
return s:foldr(a:f, a:xs[-1], a:xs[0:-2])
endfunction
" similar to python's zip()
function! s:zip(...) abort
return map(range(min(map(copy(a:000), 'len(v:val)'))), "map(copy(a:000), 'v:val['.v:val.']')")
endfunction
" similar to zip(), but goes until the longer one.
function! s:zip_fill(xs, ys, filler) abort
if empty(a:xs) && empty(a:ys)
return []
elseif empty(a:ys)
return s:cons([a:xs[0], a:filler], s:zip_fill(a:xs[1 :], [], a:filler))
elseif empty(a:xs)
return s:cons([a:filler, a:ys[0]], s:zip_fill([], a:ys[1 :], a:filler))
else
return s:cons([a:xs[0], a:ys[0]], s:zip_fill(a:xs[1 :], a:ys[1: ], a:filler))
endif
endfunction
" Inspired by Ruby's with_index method.
function! s:with_index(list, ...) abort
let base = a:0 > 0 ? a:1 : 0
return map(copy(a:list), '[v:val, v:key + base]')
endfunction
" similar to Ruby's detect or Haskell's find.
function! s:find(list, default, f) abort
for x in a:list
if eval(substitute(a:f, 'v:val', string(x), 'g'))
return x
endif
endfor
return a:default
endfunction
" Returns the index of the first element which satisfies the given expr.
function! s:find_index(xs, f, ...) abort
let len = len(a:xs)
let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : 0
let default = a:0 > 1 ? a:2 : -1
if start >=# len || start < 0
return default
endif
for i in range(start, len - 1)
if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g'))
return i
endif
endfor
return default
endfunction
" Returns the index of the last element which satisfies the given expr.
function! s:find_last_index(xs, f, ...) abort
let len = len(a:xs)
let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : len - 1
let default = a:0 > 1 ? a:2 : -1
if start >=# len || start < 0
return default
endif
for i in range(start, 0, -1)
if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g'))
return i
endif
endfor
return default
endfunction
" Similar to find_index but returns the list of indices satisfying the given expr.
function! s:find_indices(xs, f, ...) abort
let len = len(a:xs)
let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : 0
let result = []
if start >=# len || start < 0
return result
endif
for i in range(start, len - 1)
if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g'))
call add(result, i)
endif
endfor
return result
endfunction
" Return non-zero if a:list1 and a:list2 have any common item(s).
" Return zero otherwise.
function! s:has_common_items(list1, list2) abort
return !empty(filter(copy(a:list1), 'index(a:list2, v:val) isnot -1'))
endfunction
function! s:intersect(list1, list2) abort
let items = []
" for funcref
for X in a:list1
if index(a:list2, X) != -1 && index(items, X) == -1
let items += [X]
endif
endfor
return items
endfunction
" similar to Ruby's group_by.
function! s:group_by(xs, f) abort
let result = {}
let list = map(copy(a:xs), printf('[v:val, %s]', a:f))
for x in list
let Val = x[0]
let key = type(x[1]) !=# type('') ? string(x[1]) : x[1]
if has_key(result, key)
call add(result[key], Val)
else
let result[key] = [Val]
endif
unlet Val
endfor
return result
endfunction
function! s:_default_compare(a, b) abort
return a:a <# a:b ? -1 : a:a ># a:b ? 1 : 0
endfunction
function! s:binary_search(list, value, ...) abort
let Predicate = a:0 >= 1 ? a:1 : 's:_default_compare'
let dic = a:0 >= 2 ? a:2 : {}
let start = 0
let end = len(a:list) - 1
while 1
if start > end
return -1
endif
let middle = (start + end) / 2
let compared = call(Predicate, [a:value, a:list[middle]], dic)
if compared < 0
let end = middle - 1
elseif compared > 0
let start = middle + 1
else
return middle
endif
endwhile
endfunction
function! s:product(lists) abort
let result = [[]]
for pool in a:lists
let tmp = []
for x in result
let tmp += map(copy(pool), 'x + [v:val]')
endfor
let result = tmp
endfor
return result
endfunction
function! s:permutations(list, ...) abort
if a:0 > 1
throw 'vital: Data.List: too many arguments'
endif
let r = a:0 == 1 ? a:1 : len(a:list)
if r > len(a:list)
return []
elseif r < 0
throw 'vital: Data.List: {r} must be non-negative integer'
endif
let n = len(a:list)
let result = []
for indices in s:product(map(range(r), 'range(n)'))
if len(s:uniq(indices)) == r
call add(result, map(indices, 'a:list[v:val]'))
endif
endfor
return result
endfunction
function! s:combinations(list, r) abort
if a:r > len(a:list)
return []
elseif a:r < 0
throw 'vital: Data:List: {r} must be non-negative integer'
endif
let n = len(a:list)
let result = []
for indices in s:permutations(range(n), a:r)
if s:sort(copy(indices), 'a:a - a:b') == indices
call add(result, map(indices, 'a:list[v:val]'))
endif
endfor
return result
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo
" vim:set et ts=2 sts=2 sw=2 tw=0:

@ -0,0 +1,633 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
if v:version > 703 || v:version == 703 && has('patch1170')
function! vital#_tsuquyomi#Data#String#import() abort
return map({'starts_with': '', 'split3': '', 'replace_first': '', 'chop': '', 'unescape': '', 'split_posix_text': '', 'replace': '', 'scan': '', 'strwidthpart': '', 'common_head': '', 'reverse': '', 'escape_pattern': '', 'trim_end': '', '_vital_depends': '', 'wrap': '', 'join_posix_lines': '', 'contains_multibyte': '', 'truncate_skipping': '', 'split_leftright': '', 'ends_with': '', 'nsplit': '', 'strwidthpart_reverse': '', 'unescape_pattern': '', 'levenshtein_distance': '', 'trim_start': '', 'justify_equal_spacing': '', 'nr2hex': '', 'iconv': '', 'pad_left': '', 'nr2enc_char': '', 'lines': '', 'repair_posix_text': '', 'nr2byte': '', 'trim': '', 'diffidx': '', 'truncate': '', 'split_by_displaywidth': '', '_vital_created': '', 'padding_by_displaywidth': '', 'hash': '', 'chomp': '', 'pad_between_letters': '', 'dstring': '', 'pad_both_sides': '', 'substitute_last': '', 'pad_right': '', 'remove_ansi_sequences': '', '_vital_loaded': ''}, 'function("s:" . v:key)')
endfunction
else
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_tsuquyomi#Data#String#import() abort', printf("return map({'starts_with': '', 'split3': '', 'replace_first': '', 'chop': '', 'unescape': '', 'split_posix_text': '', 'replace': '', 'scan': '', 'strwidthpart': '', 'common_head': '', 'reverse': '', 'escape_pattern': '', 'trim_end': '', '_vital_depends': '', 'wrap': '', 'join_posix_lines': '', 'contains_multibyte': '', 'truncate_skipping': '', 'split_leftright': '', 'ends_with': '', 'nsplit': '', 'strwidthpart_reverse': '', 'unescape_pattern': '', 'levenshtein_distance': '', 'trim_start': '', 'justify_equal_spacing': '', 'nr2hex': '', 'iconv': '', 'pad_left': '', 'nr2enc_char': '', 'lines': '', 'repair_posix_text': '', 'nr2byte': '', 'trim': '', 'diffidx': '', 'truncate': '', 'split_by_displaywidth': '', '_vital_created': '', 'padding_by_displaywidth': '', 'hash': '', 'chomp': '', 'pad_between_letters': '', 'dstring': '', 'pad_both_sides': '', 'substitute_last': '', 'pad_right': '', 'remove_ansi_sequences': '', '_vital_loaded': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
endif
" ___vital___
" Utilities for string.
let s:save_cpo = &cpo
set cpo&vim
function! s:_vital_loaded(V) abort
let s:V = a:V
let s:L = s:V.import('Data.List')
endfunction
function! s:_vital_depends() abort
return ['Data.List']
endfunction
function! s:_vital_created(module) abort
" Expose script-local funcref
if exists('s:strchars')
let a:module.strchars = s:strchars
endif
if exists('s:wcswidth')
let a:module.wcswidth = s:wcswidth
endif
endfunction
" Substitute a:from => a:to by string.
" To substitute by pattern, use substitute() instead.
function! s:replace(str, from, to) abort
return s:_replace(a:str, a:from, a:to, 'g')
endfunction
" Substitute a:from => a:to only once.
" cf. s:replace()
function! s:replace_first(str, from, to) abort
return s:_replace(a:str, a:from, a:to, '')
endfunction
" implement of replace() and replace_first()
function! s:_replace(str, from, to, flags) abort
return substitute(a:str, '\V'.escape(a:from, '\'), escape(a:to, '\'), a:flags)
endfunction
function! s:scan(str, pattern) abort
let list = []
call substitute(a:str, a:pattern, '\=add(list, submatch(0)) == [] ? "" : ""', 'g')
return list
endfunction
function! s:reverse(str) abort
return join(reverse(split(a:str, '.\zs')), '')
endfunction
function! s:starts_with(str, prefix) abort
return stridx(a:str, a:prefix) == 0
endfunction
function! s:ends_with(str, suffix) abort
let idx = strridx(a:str, a:suffix)
return 0 <= idx && idx + len(a:suffix) == len(a:str)
endfunction
function! s:common_head(strs) abort
if empty(a:strs)
return ''
endif
let len = len(a:strs)
if len == 1
return a:strs[0]
endif
let strs = len == 2 ? a:strs : sort(copy(a:strs))
let pat = substitute(strs[0], '.', '\="[" . escape(submatch(0), "^\\") . "]"', 'g')
return pat ==# '' ? '' : matchstr(strs[-1], '\C^\%[' . pat . ']')
endfunction
" Split to two elements of List. ([left, right])
" e.g.: s:split3('neocomplcache', 'compl') returns ['neo', 'compl', 'cache']
function! s:split_leftright(expr, pattern) abort
let [left, _, right] = s:split3(a:expr, a:pattern)
return [left, right]
endfunction
function! s:split3(expr, pattern) abort
let ERROR = ['', '', '']
if a:expr ==# '' || a:pattern ==# ''
return ERROR
endif
let begin = match(a:expr, a:pattern)
if begin is -1
return ERROR
endif
let end = matchend(a:expr, a:pattern)
let left = begin <=# 0 ? '' : a:expr[: begin - 1]
let right = a:expr[end :]
return [left, a:expr[begin : end-1], right]
endfunction
" Slices into strings determines the number of substrings.
" e.g.: s:nsplit("neo compl cache", 2, '\s') returns ['neo', 'compl cache']
function! s:nsplit(expr, n, ...) abort
let pattern = get(a:000, 0, '\s')
let keepempty = get(a:000, 1, 1)
let ret = []
let expr = a:expr
if a:n <= 1
return [expr]
endif
while 1
let pos = match(expr, pattern)
if pos == -1
if expr !~ pattern || keepempty
call add(ret, expr)
endif
break
elseif pos >= 0
let left = pos > 0 ? expr[:pos-1] : ''
if pos > 0 || keepempty
call add(ret, left)
endif
let ml = len(matchstr(expr, pattern))
if pos == 0 && ml == 0
let pos = 1
endif
let expr = expr[pos+ml :]
endif
if len(expr) == 0
break
endif
if len(ret) == a:n - 1
call add(ret, expr)
break
endif
endwhile
return ret
endfunction
" Returns the number of character in a:str.
" NOTE: This returns proper value
" even if a:str contains multibyte character(s).
" s:strchars(str) {{{
if exists('*strchars')
let s:strchars = function('strchars')
else
function! s:strchars(str) abort
return strlen(substitute(copy(a:str), '.', 'x', 'g'))
endfunction
endif "}}}
" Returns the bool of contains any multibyte character in s:str
function! s:contains_multibyte(str) abort "{{{
return strlen(a:str) != s:strchars(a:str)
endfunction "}}}
" Remove last character from a:str.
" NOTE: This returns proper value
" even if a:str contains multibyte character(s).
function! s:chop(str) abort "{{{
return substitute(a:str, '.$', '', '')
endfunction "}}}
" Remove last \r,\n,\r\n from a:str.
function! s:chomp(str) abort "{{{
return substitute(a:str, '\%(\r\n\|[\r\n]\)$', '', '')
endfunction "}}}
" wrap() and its internal functions
" * _split_by_wcswidth_once()
" * _split_by_wcswidth()
" * _concat()
" * wrap()
"
" NOTE _concat() is just a copy of Data.List.concat().
" FIXME don't repeat yourself
function! s:_split_by_wcswidth_once(body, x) abort
let fst = s:strwidthpart(a:body, a:x)
let snd = s:strwidthpart_reverse(a:body, s:wcswidth(a:body) - s:wcswidth(fst))
return [fst, snd]
endfunction
function! s:_split_by_wcswidth(body, x) abort
let memo = []
let body = a:body
while s:wcswidth(body) > a:x
let [tmp, body] = s:_split_by_wcswidth_once(body, a:x)
call add(memo, tmp)
endwhile
call add(memo, body)
return memo
endfunction
function! s:trim(str) abort
return matchstr(a:str,'^\s*\zs.\{-}\ze\s*$')
endfunction
function! s:trim_start(str) abort
return matchstr(a:str,'^\s*\zs.\{-}$')
endfunction
function! s:trim_end(str) abort
return matchstr(a:str,'^.\{-}\ze\s*$')
endfunction
function! s:wrap(str,...) abort
let _columns = a:0 > 0 ? a:1 : &columns
return s:L.concat(
\ map(split(a:str, '\r\n\|[\r\n]'), 's:_split_by_wcswidth(v:val, _columns - 1)'))
endfunction
function! s:nr2byte(nr) abort
if a:nr < 0x80
return nr2char(a:nr)
elseif a:nr < 0x800
return nr2char(a:nr/64+192).nr2char(a:nr%64+128)
else
return nr2char(a:nr/4096%16+224).nr2char(a:nr/64%64+128).nr2char(a:nr%64+128)
endif
endfunction
function! s:nr2enc_char(charcode) abort
if &encoding ==# 'utf-8'
return nr2char(a:charcode)
endif
let char = s:nr2byte(a:charcode)
if strlen(char) > 1
let char = strtrans(iconv(char, 'utf-8', &encoding))
endif
return char
endfunction
function! s:nr2hex(nr) abort
let n = a:nr
let r = ''
while n
let r = '0123456789ABCDEF'[n % 16] . r
let n = n / 16
endwhile
return r
endfunction
" If a ==# b, returns -1.
" If a !=# b, returns first index of different character.
function! s:diffidx(a, b) abort
return a:a ==# a:b ? -1 : strlen(s:common_head([a:a, a:b]))
endfunction
function! s:substitute_last(expr, pat, sub) abort
return substitute(a:expr, printf('.*\zs%s', a:pat), a:sub, '')
endfunction
function! s:dstring(expr) abort
let x = substitute(string(a:expr), "^'\\|'$", '', 'g')
let x = substitute(x, "''", "'", 'g')
return printf('"%s"', escape(x, '"'))
endfunction
function! s:lines(str) abort
return split(a:str, '\r\?\n')
endfunction
function! s:_pad_with_char(str, left, right, char) abort
return repeat(a:char, a:left). a:str. repeat(a:char, a:right)
endfunction
function! s:pad_left(str, width, ...) abort
let char = get(a:, 1, ' ')
if strdisplaywidth(char) != 1
throw "vital: Data.String: Can't use non-half-width characters for padding."
endif
let left = max([0, a:width - strdisplaywidth(a:str)])
return s:_pad_with_char(a:str, left, 0, char)
endfunction
function! s:pad_right(str, width, ...) abort
let char = get(a:, 1, ' ')
if strdisplaywidth(char) != 1
throw "vital: Data.String: Can't use non-half-width characters for padding."
endif
let right = max([0, a:width - strdisplaywidth(a:str)])
return s:_pad_with_char(a:str, 0, right, char)
endfunction
function! s:pad_both_sides(str, width, ...) abort
let char = get(a:, 1, ' ')
if strdisplaywidth(char) != 1
throw "vital: Data.String: Can't use non-half-width characters for padding."
endif
let space = max([0, a:width - strdisplaywidth(a:str)])
let left = space / 2
let right = space - left
return s:_pad_with_char(a:str, left, right, char)
endfunction
function! s:pad_between_letters(str, width, ...) abort
let char = get(a:, 1, ' ')
if strdisplaywidth(char) != 1
throw "vital: Data.String: Can't use non-half-width characters for padding."
endif
let letters = split(a:str, '\zs')
let each_width = a:width / len(letters)
let str = join(map(letters, 's:pad_both_sides(v:val, each_width, char)'), '')
if a:width - strdisplaywidth(str) > 0
return char. s:pad_both_sides(str, a:width - 1, char)
endif
return str
endfunction
function! s:justify_equal_spacing(str, width, ...) abort
let char = get(a:, 1, ' ')
if strdisplaywidth(char) != 1
throw "vital: Data.String: Can't use non-half-width characters for padding."
endif
let letters = split(a:str, '\zs')
let first_letter = letters[0]
" {width w/o the first letter} / {length w/o the first letter}
let each_width = (a:width - strdisplaywidth(first_letter)) / (len(letters) - 1)
let remainder = (a:width - strdisplaywidth(first_letter)) % (len(letters) - 1)
return first_letter. join(s:L.concat([
\ map(letters[1:remainder], 's:pad_left(v:val, each_width + 1, char)'),
\ map(letters[remainder + 1:], 's:pad_left(v:val, each_width, char)')
\ ]), '')
endfunction
function! s:levenshtein_distance(str1, str2) abort
let letters1 = split(a:str1, '\zs')
let letters2 = split(a:str2, '\zs')
let length1 = len(letters1)
let length2 = len(letters2)
let distances = map(range(1, length1 + 1), 'map(range(1, length2 + 1), ''0'')')
for i1 in range(0, length1)
let distances[i1][0] = i1
endfor
for i2 in range(0, length2)
let distances[0][i2] = i2
endfor
for i1 in range(1, length1)
for i2 in range(1, length2)
let cost = (letters1[i1 - 1] ==# letters2[i2 - 1]) ? 0 : 1
let distances[i1][i2] = min([
\ distances[i1 - 1][i2 ] + 1,
\ distances[i1 ][i2 - 1] + 1,
\ distances[i1 - 1][i2 - 1] + cost,
\])
endfor
endfor
return distances[length1][length2]
endfunction
function! s:padding_by_displaywidth(expr, width, float) abort
let padding_char = ' '
let n = a:width - strdisplaywidth(a:expr)
if n <= 0
let n = 0
endif
if a:float < 0
return a:expr . repeat(padding_char, n)
elseif 0 < a:float
return repeat(padding_char, n) . a:expr
else
if n % 2 is 0
return repeat(padding_char, n / 2) . a:expr . repeat(padding_char, n / 2)
else
return repeat(padding_char, (n - 1) / 2) . a:expr . repeat(padding_char, (n - 1) / 2) . padding_char
endif
endif
endfunction
function! s:split_by_displaywidth(expr, width, float, is_wrap) abort
if a:width is 0
return ['']
endif
let lines = []
let cs = split(a:expr, '\zs')
let cs_index = 0
let text = ''
while cs_index < len(cs)
if cs[cs_index] is# "\n"
let text = s:padding_by_displaywidth(text, a:width, a:float)
let lines += [text]
let text = ''
else
let w = strdisplaywidth(text . cs[cs_index])
if w < a:width
let text .= cs[cs_index]
elseif a:width < w
let text = s:padding_by_displaywidth(text, a:width, a:float)
else
let text .= cs[cs_index]
endif
if a:width <= w
let lines += [text]
let text = ''
if a:is_wrap
if a:width < w
if a:width < strdisplaywidth(cs[cs_index])
while get(cs, cs_index, "\n") isnot# "\n"
let cs_index += 1
endwhile
continue
else
let text = cs[cs_index]
endif
endif
else
while get(cs, cs_index, "\n") isnot# "\n"
let cs_index += 1
endwhile
continue
endif
endif
endif
let cs_index += 1
endwhile
if !empty(text)
let lines += [ s:padding_by_displaywidth(text, a:width, a:float) ]
endif
return lines
endfunction
function! s:hash(str) abort
if exists('*sha256')
return sha256(a:str)
else
" This gives up sha256ing but just adds up char with index.
let sum = 0
for i in range(len(a:str))
let sum += char2nr(a:str[i]) * (i + 1)
endfor
return printf('%x', sum)
endif
endfunction
function! s:truncate(str, width) abort
" Original function is from mattn.
" http://github.com/mattn/googlereader-vim/tree/master
if a:str =~# '^[\x00-\x7f]*$'
return len(a:str) < a:width
\ ? printf('%-' . a:width . 's', a:str)
\ : strpart(a:str, 0, a:width)
endif
let ret = a:str
let width = s:wcswidth(a:str)
if width > a:width
let ret = s:strwidthpart(ret, a:width)
let width = s:wcswidth(ret)
endif
if width < a:width
let ret .= repeat(' ', a:width - width)
endif
return ret
endfunction
function! s:truncate_skipping(str, max, footer_width, separator) abort
let width = s:wcswidth(a:str)
if width <= a:max
let ret = a:str
else
let header_width = a:max - s:wcswidth(a:separator) - a:footer_width
let ret = s:strwidthpart(a:str, header_width) . a:separator
\ . s:strwidthpart_reverse(a:str, a:footer_width)
endif
return s:truncate(ret, a:max)
endfunction
function! s:strwidthpart(str, width) abort
let str = tr(a:str, "\t", ' ')
let vcol = a:width + 2
return matchstr(str, '.*\%<' . (vcol < 0 ? 0 : vcol) . 'v')
endfunction
function! s:strwidthpart_reverse(str, width) abort
let str = tr(a:str, "\t", ' ')
let vcol = s:wcswidth(str) - a:width
return matchstr(str, '\%>' . (vcol < 0 ? 0 : vcol) . 'v.*')
endfunction
if v:version >= 703
" Use builtin function.
let s:wcswidth = function('strwidth')
else
function! s:wcswidth(str) abort
if a:str =~# '^[\x00-\x7f]*$'
return strlen(a:str)
endif
let mx_first = '^\(.\)'
let str = a:str
let width = 0
while 1
let ucs = char2nr(substitute(str, mx_first, '\1', ''))
if ucs == 0
break
endif
let width += s:_wcwidth(ucs)
let str = substitute(str, mx_first, '', '')
endwhile
return width
endfunction
" UTF-8 only.
function! s:_wcwidth(ucs) abort
let ucs = a:ucs
if (ucs >= 0x1100
\ && (ucs <= 0x115f
\ || ucs == 0x2329
\ || ucs == 0x232a
\ || (ucs >= 0x2e80 && ucs <= 0xa4cf
\ && ucs != 0x303f)
\ || (ucs >= 0xac00 && ucs <= 0xd7a3)
\ || (ucs >= 0xf900 && ucs <= 0xfaff)
\ || (ucs >= 0xfe30 && ucs <= 0xfe6f)
\ || (ucs >= 0xff00 && ucs <= 0xff60)
\ || (ucs >= 0xffe0 && ucs <= 0xffe6)
\ || (ucs >= 0x20000 && ucs <= 0x2fffd)
\ || (ucs >= 0x30000 && ucs <= 0x3fffd)
\ ))
return 2
endif
return 1
endfunction
endif
function! s:remove_ansi_sequences(text) abort
return substitute(a:text, '\e\[\%(\%(\d;\)\?\d\{1,2}\)\?[mK]', '', 'g')
endfunction
function! s:escape_pattern(str) abort
" escape characters for no-magic
return escape(a:str, '^$~.*[]\')
endfunction
function! s:unescape_pattern(str) abort
" unescape characters for no-magic
return s:unescape(a:str, '^$~.*[]\')
endfunction
function! s:unescape(str, chars) abort
let chars = map(split(a:chars, '\zs'), 'escape(v:val, ''^$~.*[]\'')')
return substitute(a:str, '\\\(' . join(chars, '\|') . '\)', '\1', 'g')
endfunction
function! s:iconv(expr, from, to) abort
if a:from ==# '' || a:to ==# '' || a:from ==? a:to
return a:expr
endif
let result = iconv(a:expr, a:from, a:to)
return empty(result) ? a:expr : result
endfunction
" NOTE:
" A definition of a TEXT file is "A file that contains characters organized
" into one or more lines."
" A definition of a LINE is "A sequence of zero or more non- <newline>s
" plus a terminating <newline>"
" That's why {stdin} always ends with <newline> ideally. However, there are
" some programs which does not follow the POSIX rule and a Vim's way to join
" List into TEXT; join({text}, "\n"); does not add <newline> to the end of
" the last line.
" That's why add a trailing <newline> if it does not exist.
" REF:
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_392
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_205
" :help split()
" NOTE:
" it does nothing if the text is a correct POSIX text
function! s:repair_posix_text(text, ...) abort
let newline = get(a:000, 0, "\n")
return a:text =~# '\n$' ? a:text : a:text . newline
endfunction
" NOTE:
" A definition of a TEXT file is "A file that contains characters organized
" into one or more lines."
" A definition of a LINE is "A sequence of zero or more non- <newline>s
" plus a terminating <newline>"
" REF:
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_392
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_205
function! s:join_posix_lines(lines, ...) abort
let newline = get(a:000, 0, "\n")
return join(a:lines, newline) . newline
endfunction
" NOTE:
" A definition of a TEXT file is "A file that contains characters organized
" into one or more lines."
" A definition of a LINE is "A sequence of zero or more non- <newline>s
" plus a terminating <newline>"
" TEXT into List; split({text}, '\r\?\n', 1); add an extra empty line at the
" end of List because the end of TEXT ends with <newline> and keepempty=1 is
" specified. (btw. keepempty=0 cannot be used because it will remove
" emptylines in the head and the tail).
" That's why removing a trailing <newline> before proceeding to 'split' is required
" REF:
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_392
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_205
function! s:split_posix_text(text, ...) abort
let newline = get(a:000, 0, '\r\?\n')
let text = substitute(a:text, newline . '$', '', '')
return split(text, newline, 1)
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo
" vim:set et ts=2 sts=2 sw=2 tw=0:

@ -0,0 +1,409 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
if v:version > 703 || v:version == 703 && has('patch1170')
function! vital#_tsuquyomi#Prelude#import() abort
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': ''}, 'function("s:" . v:key)')
endfunction
else
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_tsuquyomi#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': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
endif
" ___vital___
let s:save_cpo = &cpo
set cpo&vim
if v:version > 703 ||
\ (v:version == 703 && has('patch465'))
function! s:glob(expr) abort
return glob(a:expr, 1, 1)
endfunction
else
function! s:glob(expr) abort
return split(glob(a:expr, 1), '\n')
endfunction
endif
if v:version > 704 ||
\ (v:version == 704 && has('patch279'))
function! s:globpath(path, expr) abort
return globpath(a:path, a:expr, 1, 1)
endfunction
else
function! s:globpath(path, expr) abort
return split(globpath(a:path, a:expr, 1), '\n')
endfunction
endif
" Wrapper functions for type().
let [
\ s:__TYPE_NUMBER,
\ s:__TYPE_STRING,
\ s:__TYPE_FUNCREF,
\ s:__TYPE_LIST,
\ s:__TYPE_DICT,
\ s:__TYPE_FLOAT] = [
\ type(3),
\ type(''),
\ type(function('tr')),
\ type([]),
\ type({}),
\ has('float') ? type(str2float('0')) : -1]
" __TYPE_FLOAT = -1 when -float
" This doesn't match to anything.
" Number or Float
function! s:is_numeric(Value) abort
let _ = type(a:Value)
return _ ==# s:__TYPE_NUMBER
\ || _ ==# s:__TYPE_FLOAT
endfunction
" Number
function! s:is_number(Value) abort
return type(a:Value) ==# s:__TYPE_NUMBER
endfunction
" Float
function! s:is_float(Value) abort
return type(a:Value) ==# s:__TYPE_FLOAT
endfunction
" String
function! s:is_string(Value) abort
return type(a:Value) ==# s:__TYPE_STRING
endfunction
" Funcref
function! s:is_funcref(Value) abort
return type(a:Value) ==# s:__TYPE_FUNCREF
endfunction
" List
function! s:is_list(Value) abort
return type(a:Value) ==# s:__TYPE_LIST
endfunction
" Dictionary
function! s:is_dict(Value) abort
return type(a:Value) ==# s:__TYPE_DICT
endfunction
function! s:truncate_skipping(str, max, footer_width, separator) abort
call s:_warn_deprecated('truncate_skipping', 'Data.String.truncate_skipping')
let width = s:wcswidth(a:str)
if width <= a:max
let ret = a:str
else
let header_width = a:max - s:wcswidth(a:separator) - a:footer_width
let ret = s:strwidthpart(a:str, header_width) . a:separator
\ . s:strwidthpart_reverse(a:str, a:footer_width)
endif
return s:truncate(ret, a:max)
endfunction
function! s:truncate(str, width) abort
" Original function is from mattn.
" http://github.com/mattn/googlereader-vim/tree/master
call s:_warn_deprecated('truncate', 'Data.String.truncate')
if a:str =~# '^[\x00-\x7f]*$'
return len(a:str) < a:width ?
\ printf('%-'.a:width.'s', a:str) : strpart(a:str, 0, a:width)
endif
let ret = a:str
let width = s:wcswidth(a:str)
if width > a:width
let ret = s:strwidthpart(ret, a:width)
let width = s:wcswidth(ret)
endif
if width < a:width
let ret .= repeat(' ', a:width - width)
endif
return ret
endfunction
function! s:strwidthpart(str, width) abort
call s:_warn_deprecated('strwidthpart', 'Data.String.strwidthpart')
if a:width <= 0
return ''
endif
let ret = a:str
let width = s:wcswidth(a:str)
while width > a:width
let char = matchstr(ret, '.$')
let ret = ret[: -1 - len(char)]
let width -= s:wcswidth(char)
endwhile
return ret
endfunction
function! s:strwidthpart_reverse(str, width) abort
call s:_warn_deprecated('strwidthpart_reverse', 'Data.String.strwidthpart_reverse')
if a:width <= 0
return ''
endif
let ret = a:str
let width = s:wcswidth(a:str)
while width > a:width
let char = matchstr(ret, '^.')
let ret = ret[len(char) :]
let width -= s:wcswidth(char)
endwhile
return ret
endfunction
if v:version >= 703
" Use builtin function.
function! s:wcswidth(str) abort
call s:_warn_deprecated('wcswidth', 'Data.String.wcswidth')
return strwidth(a:str)
endfunction
else
function! s:wcswidth(str) abort
call s:_warn_deprecated('wcswidth', 'Data.String.wcswidth')
if a:str =~# '^[\x00-\x7f]*$'
return strlen(a:str)
end
let mx_first = '^\(.\)'
let str = a:str
let width = 0
while 1
let ucs = char2nr(substitute(str, mx_first, '\1', ''))
if ucs == 0
break
endif
let width += s:_wcwidth(ucs)
let str = substitute(str, mx_first, '', '')
endwhile
return width
endfunction
" UTF-8 only.
function! s:_wcwidth(ucs) abort
let ucs = a:ucs
if (ucs >= 0x1100
\ && (ucs <= 0x115f
\ || ucs == 0x2329
\ || ucs == 0x232a
\ || (ucs >= 0x2e80 && ucs <= 0xa4cf
\ && ucs != 0x303f)
\ || (ucs >= 0xac00 && ucs <= 0xd7a3)
\ || (ucs >= 0xf900 && ucs <= 0xfaff)
\ || (ucs >= 0xfe30 && ucs <= 0xfe6f)
\ || (ucs >= 0xff00 && ucs <= 0xff60)
\ || (ucs >= 0xffe0 && ucs <= 0xffe6)
\ || (ucs >= 0x20000 && ucs <= 0x2fffd)
\ || (ucs >= 0x30000 && ucs <= 0x3fffd)
\ ))
return 2
endif
return 1
endfunction
endif
let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95')
let s:is_cygwin = has('win32unix')
let s:is_mac = !s:is_windows && !s:is_cygwin
\ && (has('mac') || has('macunix') || has('gui_macvim') ||
\ (!isdirectory('/proc') && executable('sw_vers')))
let s:is_unix = has('unix')
function! s:is_windows() abort
return s:is_windows
endfunction
function! s:is_cygwin() abort
return s:is_cygwin
endfunction
function! s:is_mac() abort
return s:is_mac
endfunction
function! s:is_unix() abort
return s:is_unix
endfunction
function! s:_warn_deprecated(name, alternative) abort
try
echohl Error
echomsg 'Prelude.' . a:name . ' is deprecated! Please use ' . a:alternative . ' instead.'
finally
echohl None
endtry
endfunction
function! s:smart_execute_command(action, word) abort
execute a:action . ' ' . (a:word ==# '' ? '' : '`=a:word`')
endfunction
function! s:escape_file_searching(buffer_name) abort
return escape(a:buffer_name, '*[]?{}, ')
endfunction
function! s:escape_pattern(str) abort
call s:_warn_deprecated(
\ 'escape_pattern',
\ 'Data.String.escape_pattern',
\)
return escape(a:str, '~"\.^$[]*')
endfunction
function! s:getchar(...) abort
let c = call('getchar', a:000)
return type(c) == type(0) ? nr2char(c) : c
endfunction
function! s:getchar_safe(...) abort
let c = s:input_helper('getchar', a:000)
return type(c) == type('') ? c : nr2char(c)
endfunction
function! s:input_safe(...) abort
return s:input_helper('input', a:000)
endfunction
function! s:input_helper(funcname, args) abort
let success = 0
if inputsave() !=# success
throw 'vital: Prelude: inputsave() failed'
endif
try
return call(a:funcname, a:args)
finally
if inputrestore() !=# success
throw 'vital: Prelude: inputrestore() failed'
endif
endtry
endfunction
function! s:set_default(var, val) abort
if !exists(a:var) || type({a:var}) != type(a:val)
let {a:var} = a:val
endif
endfunction
function! s:substitute_path_separator(path) abort
return s:is_windows ? substitute(a:path, '\\', '/', 'g') : a:path
endfunction
function! s:path2directory(path) abort
return s:substitute_path_separator(isdirectory(a:path) ? a:path : fnamemodify(a:path, ':p:h'))
endfunction
function! s:_path2project_directory_git(path) abort
let parent = a:path
while 1
let path = parent . '/.git'
if isdirectory(path) || filereadable(path)
return parent
endif
let next = fnamemodify(parent, ':h')
if next == parent
return ''
endif
let parent = next
endwhile
endfunction
function! s:_path2project_directory_svn(path) abort
let search_directory = a:path
let directory = ''
let find_directory = s:escape_file_searching(search_directory)
let d = finddir('.svn', find_directory . ';')
if d ==# ''
return ''
endif
let directory = fnamemodify(d, ':p:h:h')
" Search parent directories.
let parent_directory = s:path2directory(
\ fnamemodify(directory, ':h'))
if parent_directory !=# ''
let d = finddir('.svn', parent_directory . ';')
if d !=# ''
let directory = s:_path2project_directory_svn(parent_directory)
endif
endif
return directory
endfunction
function! s:_path2project_directory_others(vcs, path) abort
let vcs = a:vcs
let search_directory = a:path
let find_directory = s:escape_file_searching(search_directory)
let d = finddir(vcs, find_directory . ';')
if d ==# ''
return ''
endif
return fnamemodify(d, ':p:h:h')
endfunction
function! s:path2project_directory(path, ...) abort
let is_allow_empty = get(a:000, 0, 0)
let search_directory = s:path2directory(a:path)
let directory = ''
" Search VCS directory.
for vcs in ['.git', '.bzr', '.hg', '.svn']
if vcs ==# '.git'
let directory = s:_path2project_directory_git(search_directory)
elseif vcs ==# '.svn'
let directory = s:_path2project_directory_svn(search_directory)
else
let directory = s:_path2project_directory_others(vcs, search_directory)
endif
if directory !=# ''
break
endif
endfor
" Search project file.
if directory ==# ''
for d in ['build.xml', 'prj.el', '.project', 'pom.xml', 'package.json',
\ 'Makefile', 'configure', 'Rakefile', 'NAnt.build',
\ 'P4CONFIG', 'tags', 'gtags']
let d = findfile(d, s:escape_file_searching(search_directory) . ';')
if d !=# ''
let directory = fnamemodify(d, ':p:h')
break
endif
endfor
endif
if directory ==# ''
" Search /src/ directory.
let base = s:substitute_path_separator(search_directory)
if base =~# '/src/'
let directory = base[: strridx(base, '/src/') + 3]
endif
endif
if directory ==# '' && !is_allow_empty
" Use original path.
let directory = search_directory
endif
return s:substitute_path_separator(directory)
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo
" vim:set et ts=2 sts=2 sw=2 tw=0:

@ -0,0 +1,181 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
if v:version > 703 || v:version == 703 && has('patch1170')
function! vital#_tsuquyomi#Process#import() abort
return map({'shellescape': '', 'has_vimproc': '', 'system': '', 'iconv': '', 'spawn': '', 'get_last_status': ''}, 'function("s:" . v:key)')
endfunction
else
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_tsuquyomi#Process#import() abort', printf("return map({'shellescape': '', 'has_vimproc': '', 'system': '', 'iconv': '', 'spawn': '', 'get_last_status': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
endif
" ___vital___
" TODO: move all comments to doc file.
"
"
" FIXME: This module name should be Vital.System ?
" But the name has been already taken.
let s:save_cpo = &cpo
set cpo&vim
" FIXME: Unfortunately, can't use s:_vital_loaded() for this purpose.
" Because these variables are used when this script file is loaded.
let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95')
let s:is_unix = has('unix')
" As of 7.4.122, the system()'s 1st argument is converted internally by Vim.
" Note that Patch 7.4.122 does not convert system()'s 2nd argument and
" return-value. We must convert them manually.
let s:need_trans = v:version < 704 || (v:version == 704 && !has('patch122'))
let s:TYPE_DICT = type({})
let s:TYPE_LIST = type([])
let s:TYPE_STRING = type('')
function! s:spawn(expr, ...) abort
let shellslash = 0
if s:is_windows
let shellslash = &l:shellslash
setlocal noshellslash
endif
try
if type(a:expr) is s:TYPE_LIST
let special = 1
let cmdline = join(map(a:expr, 'shellescape(v:val, special)'), ' ')
elseif type(a:expr) is s:TYPE_STRING
let cmdline = a:expr
if a:0 && a:1
" for :! command
let cmdline = substitute(cmdline, '\([!%#]\|<[^<>]\+>\)', '\\\1', 'g')
endif
else
throw 'Process.spawn(): invalid argument (value type:'.type(a:expr).')'
endif
if s:is_windows
silent execute '!start' cmdline
else
silent execute '!' cmdline '&'
endif
finally
if s:is_windows
let &l:shellslash = shellslash
endif
endtry
return ''
endfunction
" iconv() wrapper for safety.
function! s:iconv(expr, from, to) abort
if a:from ==# '' || a:to ==# '' || a:from ==? a:to
return a:expr
endif
let result = iconv(a:expr, a:from, a:to)
return result !=# '' ? result : a:expr
endfunction
" Check vimproc.
function! s:has_vimproc() abort
if !exists('s:exists_vimproc')
try
call vimproc#version()
let s:exists_vimproc = 1
catch
let s:exists_vimproc = 0
endtry
endif
return s:exists_vimproc
endfunction
" * {command} [, {input} [, {timeout}]]
" * {command} [, {dict}]
" {dict} = {
" use_vimproc: bool,
" input: string,
" timeout: bool,
" background: bool,
" }
function! s:system(str, ...) abort
" Process optional arguments at first
" because use_vimproc is required later
" for a:str argument.
let input = ''
let use_vimproc = s:has_vimproc()
let background = 0
let args = []
if a:0 ==# 1
" {command} [, {dict}]
" a:1 = {dict}
if type(a:1) is s:TYPE_DICT
if has_key(a:1, 'use_vimproc')
let use_vimproc = a:1.use_vimproc
endif
if has_key(a:1, 'input')
let args += [s:iconv(a:1.input, &encoding, 'char')]
endif
if use_vimproc && has_key(a:1, 'timeout')
" ignores timeout unless you have vimproc.
let args += [a:1.timeout]
endif
if has_key(a:1, 'background')
let background = a:1.background
endif
elseif type(a:1) is s:TYPE_STRING
let args += [s:iconv(a:1, &encoding, 'char')]
else
throw 'Process.system(): invalid argument (value type:'.type(a:1).')'
endif
elseif a:0 >= 2
" {command} [, {input} [, {timeout}]]
" a:000 = [{input} [, {timeout}]]
let [input; rest] = a:000
let input = s:iconv(input, &encoding, 'char')
let args += [input] + rest
endif
" Process a:str argument.
if type(a:str) is s:TYPE_LIST
let expr = use_vimproc ? '"''" . v:val . "''"' : 's:shellescape(v:val)'
let command = join(map(copy(a:str), expr), ' ')
elseif type(a:str) is s:TYPE_STRING
let command = a:str
else
throw 'Process.system(): invalid argument (value type:'.type(a:str).')'
endif
if s:need_trans
let command = s:iconv(command, &encoding, 'char')
endif
let args = [command] + args
if background && (use_vimproc || !s:is_windows)
let args[0] = args[0] . ' &'
endif
let funcname = use_vimproc ? 'vimproc#system' : 'system'
let output = call(funcname, args)
let output = s:iconv(output, 'char', &encoding)
return output
endfunction
function! s:get_last_status() abort
return s:has_vimproc() ?
\ vimproc#get_last_status() : v:shell_error
endfunction
if s:is_windows
function! s:shellescape(command) abort
return substitute(a:command, '[&()[\]{}^=;!''+,`~]', '^\0', 'g')
endfunction
else
function! s:shellescape(...) abort
return call('shellescape', a:000)
endfunction
endif
let &cpo = s:save_cpo
unlet s:save_cpo
" vim:set et ts=2 sts=2 sw=2 tw=0:

@ -0,0 +1,147 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
if v:version > 703 || v:version == 703 && has('patch1170')
function! vital#_tsuquyomi#ProcessManager#import() abort
return map({'read': '', '_vital_depends': '', 'touch': '', 'read_wait': '', 'writeln': '', 'write': '', 'kill': '', 'state': '', 'term': '', 'is_available': '', 'debug_processes': '', 'status': '', '_vital_loaded': ''}, 'function("s:" . v:key)')
endfunction
else
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_tsuquyomi#ProcessManager#import() abort', printf("return map({'read': '', '_vital_depends': '', 'touch': '', 'read_wait': '', 'writeln': '', 'write': '', 'kill': '', 'state': '', 'term': '', 'is_available': '', 'debug_processes': '', 'status': '', '_vital_loaded': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
endif
" ___vital___
let s:save_cpo = &cpo
set cpo&vim
let s:_processes = {}
function! s:_vital_loaded(V) abort
let s:V = a:V
let s:S = s:V.import('Data.String')
let s:P = s:V.import('Process')
endfunction
function! s:_vital_depends() abort
return ['Data.String', 'Process']
endfunction
function! s:is_available() abort
return s:P.has_vimproc()
endfunction
function! s:touch(name, cmd) abort
if has_key(s:_processes, a:name)
return 'existing'
else
let p = vimproc#popen3(a:cmd)
let s:_processes[a:name] = p
return 'new'
endif
endfunction
function! s:_stop(i, ...) abort
let p = s:_processes[a:i]
call p.kill(get(a:000, 0, 0) ? g:vimproc#SIGKILL : g:vimproc#SIGTERM)
" call p.waitpid()
unlet s:_processes[a:i]
if has_key(s:state, a:i)
unlet s:state[a:i]
endif
endfunction
function! s:term(i) abort
return s:_stop(a:i, 0)
endfunction
function! s:kill(i) abort
return s:_stop(a:i, 1)
endfunction
function! s:read(i, endpatterns) abort
return s:read_wait(a:i, 0.05, a:endpatterns)
endfunction
let s:state = {}
function! s:read_wait(i, wait, endpatterns) abort
if !has_key(s:_processes, a:i)
throw printf("vital: ProcessManager: doesn't know about %s", a:i)
endif
let p = s:_processes[a:i]
if s:status(a:i) ==# 'inactive'
let s:state[a:i] = 'inactive'
return [p.stdout.read(), p.stderr.read(), 'inactive']
endif
let out_memo = ''
let err_memo = ''
let lastchanged = reltime()
while 1
let [x, y] = [p.stdout.read(-1, 0), p.stderr.read(-1, 0)]
if x ==# '' && y ==# ''
if str2float(reltimestr(reltime(lastchanged))) > a:wait
let s:state[a:i] = 'reading'
return [out_memo, err_memo, 'timedout']
endif
else
let lastchanged = reltime()
let out_memo .= x
let err_memo .= y
for pattern in a:endpatterns
if out_memo =~ ("\\(^\\|\n\\)" . pattern)
let s:state[a:i] = 'idle'
return [s:S.substitute_last(out_memo, pattern, ''), err_memo, 'matched']
endif
endfor
endif
endwhile
endfunction
function! s:state(i) abort
return get(s:state, a:i, 'undefined')
endfunction
function! s:write(i, str) abort
if !has_key(s:_processes, a:i)
throw printf("vital: ProcessManager: doesn't know about %s", a:i)
endif
if s:status(a:i) ==# 'inactive'
return 'inactive'
endif
let p = s:_processes[a:i]
call p.stdin.write(a:str)
return 'active'
endfunction
function! s:writeln(i, str) abort
return s:write(a:i, a:str . "\n")
endfunction
function! s:status(i) abort
if !has_key(s:_processes, a:i)
throw printf("vital: ProcessManager: doesn't know about %s", a:i)
endif
let p = s:_processes[a:i]
" vimproc.kill isn't to stop but to ask for the current state.
" return p.kill(0) ? 'inactive' : 'active'
" ... checkpid() checks if the process is running AND does waitpid() in C,
" so it solves zombie processes.
return get(p.checkpid(), 0, '') ==# 'run'
\ ? 'active'
\ : 'inactive'
endfunction
function! s:debug_processes() abort
return s:_processes
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo
" vim:set et ts=2 sts=2 sw=2 tw=0:

@ -0,0 +1,265 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
if v:version > 703 || v:version == 703 && has('patch1170')
function! vital#_tsuquyomi#System#Filepath#import() abort
return map({'path_separator': '', 'is_case_tolerant': '', 'dirname': '', 'abspath': '', 'relpath': '', 'realpath': '', 'unify_separator': '', 'is_root_directory': '', 'split': '', 'path_extensions': '', 'unixpath': '', 'which': '', 'winpath': '', 'join': '', 'separator': '', 'is_relative': '', 'basename': '', 'remove_last_separator': '', 'is_absolute': '', 'contains': ''}, 'function("s:" . v:key)')
endfunction
else
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_tsuquyomi#System#Filepath#import() abort', printf("return map({'path_separator': '', 'is_case_tolerant': '', 'dirname': '', 'abspath': '', 'relpath': '', 'realpath': '', 'unify_separator': '', 'is_root_directory': '', 'split': '', 'path_extensions': '', 'unixpath': '', 'which': '', 'winpath': '', 'join': '', 'separator': '', 'is_relative': '', 'basename': '', 'remove_last_separator': '', 'is_absolute': '', 'contains': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
endif
" ___vital___
" You should check the following related builtin functions.
" fnamemodify()
" resolve()
" simplify()
let s:save_cpo = &cpo
set cpo&vim
let s:path_sep_pattern = (exists('+shellslash') ? '[\\/]' : '/') . '\+'
let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95')
let s:is_cygwin = has('win32unix')
let s:is_mac = !s:is_windows && !s:is_cygwin
\ && (has('mac') || has('macunix') || has('gui_macvim') ||
\ (!isdirectory('/proc') && executable('sw_vers')))
let s:is_case_tolerant = filereadable(expand('<sfile>:r') . '.VIM')
" Get the directory separator.
function! s:separator() abort
return fnamemodify('.', ':p')[-1 :]
endfunction
" Get the path separator.
let s:path_separator = s:is_windows ? ';' : ':'
function! s:path_separator() abort
return s:path_separator
endfunction
" Get the path extensions
function! s:path_extensions() abort
if !exists('s:path_extensions')
if s:is_windows
if exists('$PATHEXT')
let pathext = $PATHEXT
else
" get default PATHEXT
let pathext = matchstr(system('set pathext'), '\C^pathext=\zs.*\ze\n', 'i')
endif
let s:path_extensions = map(split(pathext, s:path_separator), 'tolower(v:val)')
elseif s:is_cygwin
" cygwin is not use $PATHEXT
let s:path_extensions = ['', '.exe']
else
let s:path_extensions = ['']
endif
endif
return s:path_extensions
endfunction
" Convert all directory separators to "/".
function! s:unify_separator(path) abort
return substitute(a:path, s:path_sep_pattern, '/', 'g')
endfunction
" Get the full path of command.
if exists('*exepath')
function! s:which(str) abort
return exepath(a:str)
endfunction
else
function! s:which(command, ...) abort
let pathlist = a:command =~# s:path_sep_pattern ? [''] :
\ !a:0 ? split($PATH, s:path_separator) :
\ type(a:1) == type([]) ? copy(a:1) :
\ split(a:1, s:path_separator)
let pathext = s:path_extensions()
if index(pathext, '.' . tolower(fnamemodify(a:command, ':e'))) != -1
let pathext = ['']
endif
let dirsep = s:separator()
for dir in pathlist
let head = dir ==# '' ? '' : dir . dirsep
for ext in pathext
let full = fnamemodify(head . a:command . ext, ':p')
if filereadable(full)
if s:is_case_tolerant()
let full = glob(substitute(
\ toupper(full), '\u:\@!', '[\0\L\0]', 'g'), 1)
endif
if full !=# ''
return full
endif
endif
endfor
endfor
return ''
endfunction
endif
" Split the path with directory separator.
" Note that this includes the drive letter of MS Windows.
function! s:split(path) abort
return split(a:path, s:path_sep_pattern)
endfunction
" Join the paths.
" join('foo', 'bar') => 'foo/bar'
" join('foo/', 'bar') => 'foo/bar'
" join('/foo/', ['bar', 'buz/']) => '/foo/bar/buz/'
function! s:join(...) abort
let sep = s:separator()
let path = ''
for part in a:000
let path .= sep .
\ (type(part) is type([]) ? call('s:join', part) :
\ part)
unlet part
endfor
return substitute(path[1 :], s:path_sep_pattern, sep, 'g')
endfunction
" Check if the path is absolute path.
if s:is_windows
function! s:is_absolute(path) abort
return a:path =~# '^[a-zA-Z]:[/\\]'
endfunction
else
function! s:is_absolute(path) abort
return a:path[0] ==# '/'
endfunction
endif
function! s:is_relative(path) abort
return !s:is_absolute(a:path)
endfunction
" Return the parent directory of the path.
" NOTE: fnamemodify(path, ':h') does not return the parent directory
" when path[-1] is the separator.
function! s:dirname(path) abort
let path = a:path
let orig = a:path
let path = s:remove_last_separator(path)
if path ==# ''
return orig " root directory
endif
let path = fnamemodify(path, ':h')
return path
endfunction
" Return the basename of the path.
" NOTE: fnamemodify(path, ':h') does not return basename
" when path[-1] is the separator.
function! s:basename(path) abort
let path = a:path
let orig = a:path
let path = s:remove_last_separator(path)
if path ==# ''
return orig " root directory
endif
let path = fnamemodify(path, ':t')
return path
endfunction
" Remove the separator at the end of a:path.
function! s:remove_last_separator(path) abort
let sep = s:separator()
let pat = escape(sep, '\') . '\+$'
return substitute(a:path, pat, '', '')
endfunction
" Return true if filesystem ignores alphabetic case of a filename.
" Return false otherwise.
function! s:is_case_tolerant() abort
return s:is_case_tolerant
endfunction
function! s:abspath(path) abort
if s:is_absolute(a:path)
return a:path
endif
" Note:
" the behavior of ':p' for non existing file path is not defined
return filereadable(a:path)
\ ? fnamemodify(a:path, ':p')
\ : s:join(fnamemodify(getcwd(), ':p'), a:path)
endfunction
function! s:relpath(path) abort
if s:is_relative(a:path)
return a:path
endif
return fnamemodify(a:path, ':~:.')
endfunction
function! s:unixpath(path) abort
return fnamemodify(a:path, ':gs?\\?/?')
endfunction
function! s:winpath(path) abort
return fnamemodify(a:path, ':gs?/?\\?')
endfunction
if s:is_windows
function! s:realpath(path) abort
if exists('&shellslash') && &shellslash
return s:unixpath(a:path)
else
return s:winpath(a:path)
endif
endfunction
else
function! s:realpath(path) abort
return s:unixpath(a:path)
endfunction
endif
if s:is_windows
function! s:is_root_directory(path) abort
return a:path =~# '^[a-zA-Z]:[/\\]$'
endfunction
else
function! s:is_root_directory(path) abort
return a:path ==# '/'
endfunction
endif
function! s:contains(path, base) abort
if a:path ==# '' || a:base ==# ''
return 0
endif
let pathlist = s:split(a:path)
let baselist = s:split(a:base)
let pathlistlen = len(pathlist)
let baselistlen = len(baselist)
if pathlistlen < baselistlen
return 0
endif
if baselistlen == 0
return 1
endif
if s:is_case_tolerant
call map(pathlist, 'tolower(v:val)')
call map(baselist, 'tolower(v:val)')
endif
return pathlist[: baselistlen - 1] ==# baselist
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo
" vim:set et ts=2 sts=2 sw=2 tw=0:

@ -0,0 +1,184 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
if v:version > 703 || v:version == 703 && has('patch1170')
function! vital#_tsuquyomi#Web#JSON#import() abort
return map({'decode': '', '_vital_depends': '', '_vital_created': '', 'encode': '', '_vital_loaded': ''}, 'function("s:" . v:key)')
endfunction
else
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_tsuquyomi#Web#JSON#import() abort', printf("return map({'decode': '', '_vital_depends': '', '_vital_created': '', 'encode': '', '_vital_loaded': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
endif
" ___vital___
let s:save_cpo = &cpo
set cpo&vim
function! s:_true() abort
return 1
endfunction
function! s:_false() abort
return 0
endfunction
function! s:_null() abort
return 0
endfunction
function! s:_resolve(val, prefix) abort
let t = type(a:val)
if t == type('')
let m = matchlist(a:val, '^' . a:prefix . '\(null\|true\|false\)$')
if !empty(m)
return s:const[m[1]]
endif
elseif t == type([]) || t == type({})
return map(a:val, 's:_resolve(v:val, a:prefix)')
endif
return a:val
endfunction
function! s:_vital_created(module) abort
" define constant variables
if !exists('s:const')
let s:const = {}
let s:const.true = function('s:_true')
let s:const.false = function('s:_false')
let s:const.null = function('s:_null')
lockvar s:const
endif
call extend(a:module, s:const)
endfunction
function! s:_vital_loaded(V) abort
let s:V = a:V
let s:string = s:V.import('Data.String')
endfunction
function! s:_vital_depends() abort
return ['Data.String']
endfunction
" @vimlint(EVL102, 1, l:null)
" @vimlint(EVL102, 1, l:true)
" @vimlint(EVL102, 1, l:false)
function! s:decode(json, ...) abort
let settings = extend({
\ 'use_token': 0,
\}, get(a:000, 0, {}))
let json = iconv(a:json, 'utf-8', &encoding)
let json = join(split(json, "\n"), '')
let json = substitute(json, '\\u34;', '\\"', 'g')
let json = substitute(json, '\\u\(\x\x\x\x\)', '\=s:string.nr2enc_char("0x".submatch(1))', 'g')
if settings.use_token
let prefix = '__Web.JSON__'
while stridx(json, prefix) != -1
let prefix .= '_'
endwhile
let [null,true,false] = map(['null','true','false'], 'prefix . v:val')
sandbox return s:_resolve(eval(json), prefix)
else
let [null,true,false] = [s:const.null(),s:const.true(),s:const.false()]
sandbox return eval(json)
endif
endfunction
" @vimlint(EVL102, 0, l:null)
" @vimlint(EVL102, 0, l:true)
" @vimlint(EVL102, 0, l:false)
function! s:encode(val, ...) abort
let settings = extend({
\ 'indent': 0,
\}, get(a:000, 0, {})
\)
if type(a:val) == 0
return a:val
elseif type(a:val) == 1
let json = '"' . escape(a:val, '\"') . '"'
let json = substitute(json, "\r", '\\r', 'g')
let json = substitute(json, "\n", '\\n', 'g')
let json = substitute(json, "\t", '\\t', 'g')
return iconv(json, &encoding, 'utf-8')
elseif type(a:val) == 2
if s:const.true == a:val
return 'true'
elseif s:const.false == a:val
return 'false'
elseif s:const.null == a:val
return 'null'
else
" backward compatibility
return string(a:val)
endif
elseif type(a:val) == 3
return s:_encode_list(a:val, settings)
elseif type(a:val) == 4
return s:_encode_dict(a:val, settings)
else
return string(a:val)
endif
endfunction
" @vimlint(EVL102, 1, l:ns)
function! s:_encode_list(val, settings) abort
if empty(a:val)
return '[]'
elseif !a:settings.indent
let encoded_candidates = map(copy(a:val), 's:encode(v:val, a:settings)')
return printf('[%s]', join(encoded_candidates, ','))
else
let previous_indent = get(a:settings, '_previous_indent')
let indent = previous_indent + a:settings.indent
let ns = extend(copy(a:settings), {
\ '_previous_indent': indent,
\})
let encoded_candidates = map(
\ copy(a:val),
\ printf('''%s'' . s:encode(v:val, ns)', repeat(' ', indent)),
\)
return printf(
\ "[\n%s\n%s]",
\ join(encoded_candidates, ",\n"),
\ repeat(' ', previous_indent)
\)
endif
endfunction
" @vimlint(EVL102, 0, l:ns)
" @vimlint(EVL102, 1, l:ns)
function! s:_encode_dict(val, settings) abort
if empty(a:val)
return '{}'
elseif !a:settings.indent
let encoded_candidates = map(keys(a:val),
\ 's:encode(v:val, a:settings) . '':'' . s:encode(a:val[v:val], a:settings)'
\)
return printf('{%s}', join(encoded_candidates, ','))
else
let previous_indent = get(a:settings, '_previous_indent')
let indent = previous_indent + a:settings.indent
let ns = extend(copy(a:settings), {
\ '_previous_indent': indent,
\})
let encoded_candidates = map(keys(a:val),
\ printf(
\ '''%s'' . s:encode(v:val, ns) . '': '' . s:encode(a:val[v:val], ns)',
\ repeat(' ', indent),
\ ),
\)
return printf("{\n%s\n%s}",
\ join(encoded_candidates, ",\n"),
\ repeat(' ', previous_indent),
\)
endif
endfunction
" @vimlint(EVL102, 0, l:ns)
let &cpo = s:save_cpo
unlet s:save_cpo
" vim:set et ts=2 sts=2 sw=2 tw=0:

@ -0,0 +1,339 @@
let s:plugin_name = expand('<sfile>:t:r')
let s:vital_base_dir = expand('<sfile>:h')
let s:project_root = expand('<sfile>:h:h:h')
let s:is_vital_vim = s:plugin_name is# 'vital'
let s:loaded = {}
let s:cache_sid = {}
" function() wrapper
if v:version > 703 || v:version == 703 && has('patch1170')
function! s:_function(fstr) abort
return function(a:fstr)
endfunction
else
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
let s:_s = '<SNR>' . s:_SID() . '_'
function! s:_function(fstr) abort
return function(substitute(a:fstr, 's:', s:_s, 'g'))
endfunction
endif
function! vital#{s:plugin_name}#new() abort
return s:new(s:plugin_name)
endfunction
function! vital#{s:plugin_name}#import(...) abort
if !exists('s:V')
let s:V = s:new(s:plugin_name)
endif
return call(s:V.import, a:000, s:V)
endfunction
let s:Vital = {}
function! s:new(plugin_name) abort
let base = deepcopy(s:Vital)
let base._plugin_name = a:plugin_name
return base
endfunction
function! s:vital_files() abort
if !exists('s:vital_files')
let s:vital_files = map(
\ s:is_vital_vim ? s:_global_vital_files() : s:_self_vital_files(),
\ 'fnamemodify(v:val, ":p:gs?[\\\\/]?/?")')
endif
return copy(s:vital_files)
endfunction
let s:Vital.vital_files = s:_function('s:vital_files')
function! s:import(name, ...) abort dict
let target = {}
let functions = []
for a in a:000
if type(a) == type({})
let target = a
elseif type(a) == type([])
let functions = a
endif
unlet a
endfor
let module = self._import(a:name)
if empty(functions)
call extend(target, module, 'keep')
else
for f in functions
if has_key(module, f) && !has_key(target, f)
let target[f] = module[f]
endif
endfor
endif
return target
endfunction
let s:Vital.import = s:_function('s:import')
function! s:load(...) abort dict
for arg in a:000
let [name; as] = type(arg) == type([]) ? arg[: 1] : [arg, arg]
let target = split(join(as, ''), '\W\+')
let dict = self
let dict_type = type({})
while !empty(target)
let ns = remove(target, 0)
if !has_key(dict, ns)
let dict[ns] = {}
endif
if type(dict[ns]) == dict_type
let dict = dict[ns]
else
unlet dict
break
endif
endwhile
if exists('dict')
call extend(dict, self._import(name))
endif
unlet arg
endfor
return self
endfunction
let s:Vital.load = s:_function('s:load')
function! s:unload() abort dict
let s:loaded = {}
let s:cache_sid = {}
unlet! s:vital_files
endfunction
let s:Vital.unload = s:_function('s:unload')
function! s:exists(name) abort dict
if a:name !~# '\v^\u\w*%(\.\u\w*)*$'
throw 'vital: Invalid module name: ' . a:name
endif
return s:_module_path(a:name) isnot# ''
endfunction
let s:Vital.exists = s:_function('s:exists')
function! s:search(pattern) abort dict
let paths = s:_extract_files(a:pattern, self.vital_files())
let modules = sort(map(paths, 's:_file2module(v:val)'))
return s:_uniq(modules)
endfunction
let s:Vital.search = s:_function('s:search')
function! s:plugin_name() abort dict
return self._plugin_name
endfunction
let s:Vital.plugin_name = s:_function('s:plugin_name')
function! s:_self_vital_files() abort
let builtin = printf('%s/__%s__/', s:vital_base_dir, s:plugin_name)
let installed = printf('%s/_%s/', s:vital_base_dir, s:plugin_name)
let base = builtin . ',' . installed
return split(globpath(base, '**/*.vim', 1), "\n")
endfunction
function! s:_global_vital_files() abort
let pattern = 'autoload/vital/__*__/**/*.vim'
return split(globpath(&runtimepath, pattern, 1), "\n")
endfunction
function! s:_extract_files(pattern, files) abort
let tr = {'.': '/', '*': '[^/]*', '**': '.*'}
let target = substitute(a:pattern, '\.\|\*\*\?', '\=tr[submatch(0)]', 'g')
let regexp = printf('autoload/vital/[^/]\+/%s.vim$', target)
return filter(a:files, 'v:val =~# regexp')
endfunction
function! s:_file2module(file) abort
let filename = fnamemodify(a:file, ':p:gs?[\\/]?/?')
let tail = matchstr(filename, 'autoload/vital/_\w\+/\zs.*\ze\.vim$')
return join(split(tail, '[\\/]\+'), '.')
endfunction
" @param {string} name e.g. Data.List
function! s:_import(name) abort dict
if has_key(s:loaded, a:name)
return copy(s:loaded[a:name])
endif
let module = self._get_module(a:name)
if has_key(module, '_vital_created')
call module._vital_created(module)
endif
let export_module = filter(copy(module), 'v:key =~# "^\\a"')
" Cache module before calling module.vital_loaded() to avoid cyclic
" dependences but remove the cache if module._vital_loaded() fails.
" let s:loaded[a:name] = export_module
let s:loaded[a:name] = export_module
if has_key(module, '_vital_loaded')
try
call module._vital_loaded(vital#{s:plugin_name}#new())
catch
unlet s:loaded[a:name]
throw 'vital: fail to call ._vital_loaded(): ' . v:exception
endtry
endif
return copy(s:loaded[a:name])
endfunction
let s:Vital._import = s:_function('s:_import')
" s:_get_module() returns module object wihch has all script local functions.
function! s:_get_module(name) abort dict
let funcname = s:_import_func_name(self.plugin_name(), a:name)
if s:_exists_autoload_func_with_source(funcname)
return call(funcname, [])
else
return s:_get_builtin_module(a:name)
endif
endfunction
function! s:_get_builtin_module(name) abort
return s:sid2sfuncs(s:_module_sid(a:name))
endfunction
if s:is_vital_vim
" For vital.vim, we can use s:_get_builtin_module directly
let s:Vital._get_module = s:_function('s:_get_builtin_module')
else
let s:Vital._get_module = s:_function('s:_get_module')
endif
function! s:_import_func_name(plugin_name, module_name) abort
return printf('vital#_%s#%s#import', a:plugin_name, s:_dot_to_sharp(a:module_name))
endfunction
function! s:_module_sid(name) abort
let path = s:_module_path(a:name)
if !filereadable(path)
throw 'vital: module not found: ' . a:name
endif
let vital_dir = s:is_vital_vim ? '__\w\+__' : printf('_\{1,2}%s\%%(__\)\?', s:plugin_name)
let base = join([vital_dir, ''], '[/\\]\+')
let p = base . substitute('' . a:name, '\.', '[/\\\\]\\+', 'g')
let sid = s:_sid(path, p)
if !sid
call s:_source(path)
let sid = s:_sid(path, p)
if !sid
throw printf('vital: cannot get <SID> from path: %s', path)
endif
endif
return sid
endfunction
function! s:_module_path(name) abort
return get(s:_extract_files(a:name, s:vital_files()), 0, '')
endfunction
function! s:_module_sid_base_dir() abort
return s:is_vital_vim ? &rtp : s:project_root
endfunction
function! s:_dot_to_sharp(name) abort
return substitute(a:name, '\.', '#', 'g')
endfunction
" It will sources autoload file if a given func is not already defined.
function! s:_exists_autoload_func_with_source(funcname) abort
if exists('*' . a:funcname)
" Return true if a given func is already defined
return 1
endif
" source a file which may include a given func definition and try again.
let path = 'autoload/' . substitute(substitute(a:funcname, '#[^#]*$', '.vim', ''), '#', '/', 'g')
call s:_runtime(path)
return exists('*' . a:funcname)
endfunction
function! s:_runtime(path) abort
execute 'runtime' fnameescape(a:path)
endfunction
function! s:_source(path) abort
execute 'source' fnameescape(a:path)
endfunction
" @vimlint(EVL102, 1, l:_)
" @vimlint(EVL102, 1, l:__)
function! s:_sid(path, filter_pattern) abort
let unified_path = s:_unify_path(a:path)
if has_key(s:cache_sid, unified_path)
return s:cache_sid[unified_path]
endif
for line in filter(split(s:_redir(':scriptnames'), "\n"), 'v:val =~# a:filter_pattern')
let [_, sid, path; __] = matchlist(line, '^\s*\(\d\+\):\s\+\(.\+\)\s*$')
if s:_unify_path(path) is# unified_path
let s:cache_sid[unified_path] = sid
return s:cache_sid[unified_path]
endif
endfor
return 0
endfunction
function! s:_redir(cmd) abort
let [save_verbose, save_verbosefile] = [&verbose, &verbosefile]
set verbose=0 verbosefile=
redir => res
silent! execute a:cmd
redir END
let [&verbose, &verbosefile] = [save_verbose, save_verbosefile]
return res
endfunction
if filereadable(expand('<sfile>:r') . '.VIM') " is case-insensitive or not
let s:_unify_path_cache = {}
" resolve() is slow, so we cache results.
" Note: On windows, vim can't expand path names from 8.3 formats.
" So if getting full path via <sfile> and $HOME was set as 8.3 format,
" vital load duplicated scripts. Below's :~ avoid this issue.
function! s:_unify_path(path) abort
if has_key(s:_unify_path_cache, a:path)
return s:_unify_path_cache[a:path]
endif
let value = tolower(fnamemodify(resolve(fnamemodify(
\ a:path, ':p')), ':~:gs?[\\/]?/?'))
let s:_unify_path_cache[a:path] = value
return value
endfunction
else
function! s:_unify_path(path) abort
return resolve(fnamemodify(a:path, ':p:gs?[\\/]?/?'))
endfunction
endif
" copied and modified from Vim.ScriptLocal
let s:SNR = join(map(range(len("\<SNR>")), '"[\\x" . printf("%0x", char2nr("\<SNR>"[v:val])) . "]"'), '')
function! s:sid2sfuncs(sid) abort
let fs = split(s:_redir(printf(':function /^%s%s_', s:SNR, a:sid)), "\n")
let r = {}
let pattern = printf('\m^function\s<SNR>%d_\zs\w\{-}\ze(', a:sid)
for fname in map(fs, 'matchstr(v:val, pattern)')
let r[fname] = function(s:_sfuncname(a:sid, fname))
endfor
return r
endfunction
"" Return funcname of script local functions with SID
function! s:_sfuncname(sid, funcname) abort
return printf('<SNR>%s_%s', a:sid, a:funcname)
endfunction
if exists('*uniq')
function! s:_uniq(list) abort
return uniq(a:list)
endfunction
else
function! s:_uniq(list) abort
let i = len(a:list) - 1
while 0 < i
if a:list[i] ==# a:list[i - 1]
call remove(a:list, i)
endif
let i -= 1
endwhile
return a:list
endfunction
endif

@ -0,0 +1,7 @@
tsuquyomi
bd6fd747ba07d5124619347efa32c2f068e22318
Web.JSON
ProcessManager
System.Filepath
Prelude

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

@ -0,0 +1,519 @@
*tsuquyomi* はTypeScript向けのVim plugin です.
Version: 0.5.1
Author : Quramy <yosuke.kurami@gmail.com>
License: MIT license {{{
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}}}
目次 *tsuquyomi-contents*
概要 |tsuquyomi-introduction|
インストール |tsuquyomi-install|
インターフェイス |tsuquyomi-interface|
コマンド |tsuquyomi-commands|
オプション |tsuquyomi-variables|
キーマッピング |tsuquyomi-key-mappings|
関数 |tsuquyomi-functions|
設定例 |tsuquyomi-examples|
==============================================================================
概要 *tsuquyomi-introduction*
tsuquyomi は TSServer のclient として動作します
(TSServerはTypeScriptにバンドルされているエディタ向けツールです).
tsuquyomi は以下の機能を提供します.
+ TSServer からソースコードが持つ情報を参照します.
(補完, 定義/参照箇所, エラー, etc...)
+ TSServerの起動や停止を行う機能
==============================================================================
インストール *tsuquyomi-install*
tsuquyomiのインストールには下記が必要です.
* Vim (v.7.4.0 or later)
* Shougo/vimproc.vim (https://github.com/Shougo/vimproc.vim)
(vim8 もしくはそれ以降をお使いならば必要ありません)
* Node.js and TypeScript (v.1.5.0 or later)
最初に, |vimproc|が未インストールであれば, インストールしてください.
(インストール方法は https://github.com/Shougo/vimproc.vim を参考にしてください)
次に, 最新の TypeScript をインストールします.
Prompt:
>
$ npm -g install typescript
<
最後に, tsuquyomiをVim plugin用のディレクトリに配置します.
もし |NeoBundle| でプラグイン管理をしているのであれば,
`.vimrc` に以下を追記します
>
NeoBundle 'Shougo/vimproc.vim', {
\ 'build' : {
\ 'windows' : 'tools\\update-dll-mingw',
\ 'cygwin' : 'make -f make_cygwin.mak',
\ 'mac' : 'make -f make_mac.mak',
\ 'linux' : 'make',
\ 'unix' : 'gmake',
\ },
\ }
NeoBundle 'Quramy/tsuquyomi'
>
|:NeoBundleInstall| を実行し, プラグインをインストールしてください.
また, tsuquyomi は|unite-outline|の拡張機能を提供します.
|unite| と |unite-outline| がインストールされていれば, |:Unite outline| を実行
することでカレントバッファをOutline 表示できます.
==============================================================================
インターフェイス *tsuquyomi-interface*
------------------------------------------------------------------------------
コマンド *tsuquyomi-commands*
*:TsuquyomiStartServer*
*:TsuStartServer*
:TsuquyomiStartServer
TSServerのプロセスを起動します.
すでにプロセスが起動している場合は何もしません.
*:TsuquyomiStopServer*
*:TsuStopServer*
:TsuquyomiStopServer
TSServerのプロセスが存在する場合, 停止させます.
*:TsuquyomiStatusServer*
*:TsuStatusServer*
:TsuquyomiStatusServer
TSServerの起動状態を表示します.
TSServerが起動している場合, "reading" のメッセージが
表示されます.
*:TsuquyomiOpen*
*:TsuOpen*
:TsuquyomiOpen [{file} ...]
TSServerで{file}を開きます.
TSServerでファイルを開くと, ファイルに対する様々な処理が
行えるようになります.
引数{file}を省略すると, TSServerはカレントバッファのファイルを
開きます.
デフォルトでは, |g:tsuquyomi_auto_open| が有効化されています.
このため, 通常は明示的にこのコマンドを実行する必要は
ありません.
*:TsuquyomiClose*
*:TsuClose*
:TsuquyomiClose [{file} ...]
既に開いている{file}をTSServer上でクローズします.
引数{file}を省略すると, TSServerはカレントバッファに紐づいた
ファイルをクローズします.
*:TsuquyomiReload*
*:TsuReload*
:TsuquyomiReload [{file} ...]
TSServerに{file}をリロードさせます.
引数{file}を省略すると, TSServerにカレントバッファを
リロードさせます.
*:TsuquyomiDump*
*:TsuDump*
:TsuquyomiDump [{file} ...]
TSServerのバッファをファイルに保存します.
デバッグのために利用します.
引数{file}を省略すると, カレントバッファに紐づくファイルを
ダンプします.
Note: ダンプされるファイル名は下記となります:
`オリジナルファイル名` + `'.dump'`.
*:TsuquyomiDefinition*
*:TsuDefinition*
:TsuquyomiDefinition
シンボルが定義された場所へ遷移させます.
*:TsuquyomiSplitDefinition*
*:TsuSplitDefinition*
:TsuquyomiSplitDefinition
現在のウィンドウを2つに分割し、片方にシンボルが定義された
場所を表示します.
*:TsuquyomiTypeDefinition*
*:TsuTypeDefinition*
:TsuquyomiTypeDefinition
シンボルの型が定義されている場所へ遷移させます.
*:TsuquyomiGoBack*
*:TsuGoBack*
:TsuquyomiGoBack
最後に|:TsuquyomiDefinition|, |:TsuTypeDefinition|
を実行した箇所へカーソルを移動させます.
*:TsuquyomiImplementation*
*:TsuImplementation*
:TsuquyomiImplementation
interfaceの実装箇所へナビゲートします.
参照箇所の結果は|location-list|に表示されます.
*:TsuquyomiReferences*
*:TsuReferences*
:TsuquyomiReferences
シンボルを参照している箇所へナビゲートします.
参照箇所の結果は|location-list|に表示されます.
*:TsuquyomiRenameSymbol*
*:TsuRenameSymbol*
:TsuquyomiRenameSymbol
カーソル直下の識別子を別名に変更します.
対象の識別子が別の.tsファイルから参照されている場合,
このコマンドを実行するより前に対象ファイルをバッファに
開いておく必要があります.
*:TsuquyomiRenameSymbolC*
*:TsuRenameSymbolC*
:TsuquyomiRenameSymbolC
カーソル直下の識別子を別名に変更します.
コメントに含まれている識別子も含めて変更されます.
*:TsuquyomiReloadProject*
*:TsuReloadProject*
:TsuquyomiReloadProject
TSServerで開かれている全てのファイルを一度閉じ,
再度開きます.
`*.ts`をバッファに開いた後に, `tsconfig.json` を編集した場合,
このコマンドを実行する必要があります.
このコマンドを実行すると, `tsconfig.json` の変更が
TSServerに反映されます.
*:TsuquyomiGeterr*
*:TsuGeterr*
:TsuquyomiGeterr
カレントバッファのコンパイルエラー情報をQuickFixウィンドウへ
表示します.
*:TsuquyomiGeterrProject*
*:TsuGeterrProject*
:TsuquyomiGeterrProject
プロジェクトのコンパイルエラー情報全てをQuickFixウィンドウへ
表示します.
このコマンドにはTypeScript@v1.6.0以上が必要です.
*:TsuquyomiAsyncGeterr*
*:TsuAsyncGeterr*
:TsuquyomiAsyncGeterr
カレントバッファのコンパイルエラー情報を非同期で
QuickFixウィンドウへ表示します.
*:TsuquyomiSeaarch*
*:TsuSearch*
:TsuquyomiSearch {keyword}
プロジェクトで {keyword} を含んでいる箇所を検索します.
*:TsuquyomiQuickFix*
*:TsuQuickFix*
:TsuquyomiQuickFix
カーソル配下にエラーが存在し、且つ適用可能なQuickFixが
存在する場合に、このQuickFixを適用します.
このコマンドにはTypeScript@v2.1.0以上が必要です.
*:TsuquyomiSignatureHelp*
*:TsuSignatureHelp*
:TsuquyomiSignatureHelp
カーソル上のメソッドのシグネチャを、プレビューウィンドウへ表示
します。もし複数のオーバーロードされた候補が存在する場合は、
それらをドキュメントと共に表示します。
Note
カーソルはメソッド呼び出しのカッコの間にあるか、少なくとも書き
かけのメソッド呼び出しの開きカッコの後になければいけません。
------------------------------------------------------------------------------
オプション *tsuquyomi-variables*
*g:tsuquyomi_auto_open*
g:tsuquyomi_auto_open (デフォルト値 1)
Vimでバッファを開く際にTSServerでそのファイルを開くかどうか.
この値を0とした場合, バッファを開いた後に|:TsuquyomiOpen|
コマンドを実行すると必要があります.
*g:tsuquyomi_use_local_typescript*
g:tsuquyomi_use_local_typescript (default 1)
プロジェクト用にローカルインストールした`tsserver.js` を
利用するかどうか.
* 0: tsuquyomiは `tsserver.js`の探索を行いません.
* 1: tsuquyomiはカレントディレクトリを起点にプロジェクトの
ルートディレクトリ(`package.json`を含むディレクトリ)を
探索します. 次にプロジェクトルートから
`node_modules/typescript/bin/tsserver.js`
を探索します. 見つからない場合は,
|g:tsuquyomi_use_dev_node_module| に従って`tsserver.js`の
パスを決定します.
*g:tsuquyomi_use_dev_node_module*
g:tsuquyomi_use_dev_node_module (デフォルト値 0)
手動でインストールした`tsserver.js` を利用するかどうか.
* 0: tsuquyomi はグローバルインストールされた
`tsserver` コマンドを利用します(デフォルト).
* 1: tsuquyomiは
`tsuquyomi/node_modules/typescript/bin/tsserver.js` .
を利用します.
このモードを利用する場合, 下記を実行してください:
>
:cd ~/.vim/bundle/tsuquyomi
:!npm install
<
* 2: tsuquyomiは|g:tsuquyomi_tsserver_path| で指定された
`tsserver.js` を利用します.
*g:tsuquyomi_tsserver_path*
g:tsuquyomi_tsserver_path (デフォルト値 `''`)
|g:tsuquyomi_use_dev_node_module|を`2`とした場合,
このオプションで`tsserver.js`のパスを設定する必要があります.
設定例:
>
let g:tsuquyomi_use_dev_node_module = 2
let g:tsuquyomi_tsserver_path =
\ '~/typescript/built/local/tsserver.js'
<
*g:tsuquyomi_nodejs_path*
g:tsuquyomi_nodejs_path (デフォルト値 "node")
Node.js の実行パス.
*g:tsuquyomi_definition_split*
g:tsuquyomi_definition_split (デフォルト値 0)
|:TsuquyomiDefinition| にて別ファイル定義への遷移時,
ウィンドウをどのように開くか.
* 0: |:edit|
* 1: |:split|
* 2: |:vsplit|
* 3: |:tabedit|
*g:tsuquyomi_completion_detail*
g:tsuquyomi_completion_detail (デフォルト値 0)
|tsuquyomi#complete|実行時に詳細な補完メニューを作成するかどうか.
このオプションは 'completeopt' に "menu" が含まれるときにのみ
意味を持つ.
NOTE: このオプションを1に設定した場合, 補完速度は遅くなる
*g:tsuquyomi_completion_case_sensitive*
g:tsuquyomi_completion_case_sensitive (デフォルト値 0)
|tsuquyomi#complete|実行時に補完候補をcase-sensitiveに
探索するかどうか.
*g:tsuquyomi_case_sensitive_imports*
g:tsuquyomi_case_sensitive_imports (default 0)
|tsuquyomi#es6import#complete|実行時に補完候補をcase-sensitive
に探索するかどうか.
*g:tsuquyomi_completion_preview*
g:tsuquyomi_completion_preview (デフォルト値 0)
|tsuquyomi#complete|実行時にシグネチャの情報をプレビューに表示
するかどうか.
このオプションは 'completeopt' に "menu" と "preview" が含まれ
るときにのみ意味を持つ.
NOTE: このオプションを1に設定した場合, 補完速度は少し遅くなる
*g:tsuquyomi_disable_default_mappings*
g:tsuquyomi_disable_default_mappings (デフォルト値 0)
デフォルトキーマッピングを適用するかどうか.
*g:tsuquyomi_disable_quickfix*
g:tsuquyomi_disable_quickfix (デフォルト値 0)
値をセットするとバッファ保存時に |:TsuquyomiGeterr| が
実行されなくなる.
*g:tsuquyomi_save_onrename*
g:tsuquyomi_save_onrename (デフォルト値 0)
値をセットすると |:TsuquyomiRenameSymbol| の実行時に書き換え
られたファイルが自動で保存される.
値がセットされていない場合は, 書き換えられたファイルを|:split|
で表示すため, ユーザは変更箇所を確認してから, ファイルを
|:wa| 等で保存すればよい.
*g:tsuquyomi_single_quote_import*
g:tsuquyomi_single_quote_import (デフォルト値 0)
値をセットすると、|:TsuquyomiImport| 利用時にダブルクォートで
はなくシングルクォートを使って import blockが生成される.
g:tsuquyomi_semicolon_import (default 1)
値をセットすると、|:TsuquyomiImport| 利用時に行末にセミコロン
を追加して import blockが生成される.
*g:tsuquyomi_baseurl_import_path*
g:tsuquyomi_baseurl_import_path (default 0)
値をセットすると、|:TsuquyomiImport|が tsconfig.jsonで指定され
た`baseUrl`からの相対パスをモジュールのパスとして使用するよう
になる.
*g:tsuquyomi_javascript_support*
g:tsuquyomi_javascript_support (デフォルト値 0)
値をセットすると、JavaScriptファイルに対しても動作するようになる
tsconfig.jsonやjsconfig.jsonによる詳細設定が可能。
*g:tsuquyomi_ignore_missing_modules*
g:tsuquyomi_ignore_missing_modules (デフォルト値 0)
値をセットすると、"Cannot find module"で始まる
エラーメッセージを無視するようになる。
import対象の型定義ファイルが存在していないケースで有用。
*g:tsuquyomi_use_vimproc*
g:tsuquyomi_use_vimproc (default 0)
値をセットすると、Vim8のjob機能の代わりに |vimproc| を使用する.
*g:tsuquyomi_locale*
g:tsuquyomi_locale (デフォルト値 "en")
エラーメッセージのLCID。
------------------------------------------------------------------------------
キーマッピング *tsuquyomi-key-mappings*
ノーマルモードのキーマッピング:
*<Plug>(TsuquyomiDefinition)*
<Plug>(TsuquyomiDefinition)
カーソル直下のシンボルが定義された箇所へ遷移.
|:TsuquyomiDefinition|を参照のこと.
*<Plug>(TsuquyomiSplitDefinition)*
<Plug>(TsuquyomiSplitDefinition)
カーソル直下のシンボルが定義された箇所へ遷移.
|:TsuquyomiSplitDefinition|を参照のこと.
*<Plug>(TsuquyomiTypeDefinition)*
<Plug>(TsuquyomiTypeDefinition)
カーソル直下のシンボルの型定義箇所へ遷移.
|:TsuquyomiTypeDefinition|を参照のこと.
*<Plug>(TsuquyomiGoBack)*
<Plug>(TsuquyomiGoBack)
|:TsuquyomiGoBack|を参照のこと.
*<Plug>(TsuquyomiImplementation)*
<Plug>(TsuquyomiImplementation)
カーソル直下のinerfaceが実装されている場所の一覧を表示.
|:TsuquyomiImplementation|を参照のこと.
*<Plug>(TsuquyomiReferences)*
<Plug>(TsuquyomiReferences)
カーソル直下のシンボルが参照されている場所の一覧を表示.
|:TsuquyomiReferences| を参照のこと.
*<Plug>(TsuquyomiRenameSymbol)*
<Plug>(TsuquyomiRenameSymbol)
カーソル直下のシンボルを別名に変更する.
|:TsuquyomiRenameSymbol|を参照のこと.
*<Plug>(TsuquyomiRenameSymbolC)*
<Plug>(TsuquyomiRenameSymbolC)
カーソル直下のシンボルをコメントも含めて別名に変更する.
|:TsuquyomiRenameSymbolC|を参照のこと.
*<Plug>(TsuquyomiQuickFix)*
<Plug>(TsuquyomiQuickFix)
カーソル直下でQuickFixが適用可能な場合に、これを適用する.
|:TsuquyomiQuickFix|を参照のこと.
*<Plug>(TsuquyomiImport)*
<Plug>(TsuquyomiImport)
カーソル直下のシンボルに対応するES6 import文を生成する.
|:TsuquyomiImport|を参照のこと.
*<Plug>(TsuquyomiSignatureHelp)*
<Plug>(TsuquyomiSignatureHelp)
カーソル下のメソッド引数のシグネチャを表示.
|:TsuquyomiSignatureHelp|を参照のこと.
デフォルトキーマップは下記の通り:
ノーマルモード:
{lhs} {rhs}
-------- -----------------------------
<C-]> <Plug>(TsuquyomiDefinition)
<C-W>] <Plug>(TsuquyomiSplitDefinition)
<C-t> <Plug>(TsuquyomiGoBack)
<C-^> <Plug>(TsuquyomiReferences)
------------------------------------------------------------------------------
関数 *tsuquyomi-functions*
tsuquyomi#complete *tsuquyomi#complete*
'completefunc' や 'omnifunc' オプションに適用可能な関数.
デフォルトでは, 'omnifunc' オプションにこの関数をセットしてい
る補完の挙動をカスタマイズする場合,
|tsuquyomi-examples-complete| を参照の事.
tsuquyomi#balloonexpr *tsuquyomi#balloonexpr*
マウスカーソル直下のシンボルについて, ツールチップにシンボルの
情報を表示させる. 'balloonexpr' にセットして利用する.
設定例:
>
set ballooneval
autocmd BufNewFile,BufRead *.ts
\ setlocal balloonexpr=tsuquyomi#balloonexpr()
<
Note: この関数はVimが|+balloon_eval| オプション付きでコンパイル
されている場合のみ利用可能である.
tsuquyomi#hint *tsuquyomi#hint*
カーソル上のシンボルの情報を返却する. |tsuquyomi#balloonexpr|
と似ているが, この関数はGVim, 端末上のVimのどちらでも動作する.
設定例:
>
autocmd FileType typescript nmap <buffer> <Leader>t :
\ <C-u>echo tsuquyomi#hint()<CR>
<
Note: This function works in not only GVim but also
terminal Vim.
tsuquyomi#getSupportedCodeFixes *tsuquyomi#getSupportedCodeFixes*
QuickFixが利用可能なコード値のリストを返却する.
このリストに属するコードのエラーは|:TsuquyomiQuickFix|により
修正可能である.
Todo
==============================================================================
EXAMPLES *tsuquyomi-examples*
*tsuquyomi-examples-complete*
補完の挙動は 'completeopt' オプションで設定する.
メソッド呼び出しの補完時, メソッド定義をプレビューウィンドウに表示.
>
autocmd FileType typescript setlocal completeopt+=preview
<
補完時, ポップアップメニューを表示しない.
>
autocmd FileType typescript setlocal completeopt-=menu
<
*tsuquyomi-examples-async*
Vim8 の Job / Channel を利用し非同期でカレントバッファのコンパイルエラー情報を
QuickFixウィンドウへ表示します.
>
autocmd InsertLeave,BufWritePost *.ts,*.tsx call tsuquyomi#asyncGeterr()
<
==============================================================================
vim:tw=78:ts=8:ft=help:norl:noet:fen:

@ -0,0 +1,511 @@
*tsuquyomi* is a Vim plugin for TypeScript.
Version: 0.6.0
Author : Quramy <yosuke.kurami@gmail.com>
License: MIT license {{{
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}}}
CONTENTS *tsuquyomi-contents*
Introduction |tsuquyomi-introduction|
Install |tsuquyomi-install|
Interface |tsuquyomi-interface|
Commands |tsuquyomi-commands|
Variables |tsuquyomi-variables|
Key Mappings |tsuquyomi-key-mappings|
Functions |tsuquyomi-functions|
Examples |tsuquyomi-examples|
==============================================================================
INTRODUCTION *tsuquyomi-introduction*
Tsuquyomi works as a client for TSServer(which is an editor service bundled into TypeScript).
This plugin provides the following functions:
+ Functions to fetch information of your source codes from TSServer.
(compeletions, locations of definition or references, error, etc...)
+ Functions to start or stop the TSServer process.
==============================================================================
INSTALL *tsuquyomi-install*
Tsuquyomi requires the following:
* Vim (v.7.4.0 or later)
* Shougo/vimproc.vim (https://github.com/Shougo/vimproc.vim)
(Not required if you use vim8 or later)
* Node.js and TypeScript (v.1.5.0 or later)
First of all, if you haven't installed |vimproc|, please install it.
(Please see https://github.com/Shougo/vimproc.vim)
Secondly, install the latest version of TypeScript.
Prompt:
>
$ npm -g install typescript
<
Thirdly, locate Tsuquyomi to your Vim plugin directory.
If you use |NeoBundle| for plugin management, append the following to `.vimrc` .
>
NeoBundle 'Shougo/vimproc.vim', {
\ 'build' : {
\ 'windows' : 'tools\\update-dll-mingw',
\ 'cygwin' : 'make -f make_cygwin.mak',
\ 'mac' : 'make -f make_mac.mak',
\ 'linux' : 'make',
\ 'unix' : 'gmake',
\ },
\ }
NeoBundle 'Quramy/tsuquyomi'
>
And execute the Ex command |:NeoBundleInstall| .
Also, Tsuquyomi is an extension of |unite-outline|.
If you have installed |unite| and |unite-outline| , you can get the outline of
the current buffer calling ':Unite outline'.
==============================================================================
INTERFACE *tsuquyomi-interface*
------------------------------------------------------------------------------
COMMANDS *tsuquyomi-commands*
*:TsuquyomiStartServer*
*:TsuStartServer*
:TsuquyomiStartServer
Start TSServer process.
If the process is already running, this command doesn't
do anything.
*:TsuquyomiStopServer*
*:TsuStopServer*
:TsuquyomiStopServer
Stop TSServer process if it exists.
*:TsuquyomiStatusServer*
*:TsuStatusServer*
:TsuquyomiStatusServer
Show TSServer status.
When TSServer is running, the message "reading" will be
displayed.
*:TsuquyomiOpen*
*:TsuOpen*
:TsuquyomiOpen [{file} ...]
Let TSServer open {file}s.
Once TSServer opens files, you can start a variety of
operations on them.
If you omit the argument {file}, TSServer opens the current
buffer.
By default, |g:tsuquyomi_auto_open| is enabled,
so usually users don't need to exec this command manually.
*:TsuquyomiClose*
*:TsuClose*
:TsuquyomiClose [{file} ...]
Let TSServer release opened {file}s.
If you omit the argument {file}, TSServer closes the file
related to the current buffer.
*:TsuquyomiReload*
*:TsuReload*
:TsuquyomiReload [{file} ...]
Let TSServer reload files.
If you omit the argument {file}, TSServer reloads the current
buffer.
*:TsuquyomiDump*
*:TsuDump*
:TsuquyomiDump [{file} ...]
Save TSServer's buffer to files.
This command can be executed only for debugging purposes.
If you omit the argument {file}, TSServer saves the file
related to the current buffer.
Note: Dumped filename is `originalFileName` + `'.dump'` .
*:TsuquyomiDefinition*
*:TsuDefinition*
:TsuquyomiDefinition
Navigate to the location where the symbol is defined.
*:TsuquyomiSplitDefinition*
*:TsuSplitDefinition*
:TsuquyomiSplitDefinition
Split current window in two. Navigate to the location where
the symbol is defined.
*:TsuquyomiTypeDefinition*
*:TsuTypeDefinition*
:TsuquyomiTypeDefinition
Navigate to the location where the type of the symbol is
defined.
*:TsuquyomiGoBack*
*:TsuGoBack*
:TsuquyomiGoBack
Move the cursor position to the location where
the last |:TsuquyomiDefinition| or |:TsuquyomiTypeDefinition|
was called.
*:TsuquyomiImplementation*
*:TsuImplementation*
:TsuquyomiImplementation
Navigate to the locations where an interface is implemented.
The result is loaded into the |location-list| .
*:TsuquyomiReferences*
*:TsuReferences*
:TsuquyomiReferences
Navigate to the locations where the symbol is referenced.
The result is loaded into the |location-list| .
*:TsuquyomiRenameSymbol*
*:TsuRenameSymbol*
:TsuquyomiRenameSymbol
Rename the identifier under the cursor to a new name.
If the identifier is referenced by other .ts files,
you should open them before executing this command.
*:TsuquyomiRenameSymbolC*
*:TsuRenameSymbolC*
:TsuquyomiRenameSymbolC
Rename the identifier under the cursor to a new name.
This command renames the identifiers including comments.
*:TsuquyomiReloadProject*
*:TsuReloadProject*
:TsuquyomiReloadProject
Close and re-open all files opened by |:TsuquyomiOpen|.
If you change your `tsconfig.json` after opening any `*.ts` files,
you should execute this command so the changes of `tsconfig.json`
are reflected in the TSServer.
*:TsuquyomiGeterr*
*:TsuGeterr*
:TsuquyomiGeterr
Show compilation errors of current buffer in QuickFix window.
*:TsuquyomiAsynGeterr*
*:TsuAsyncGeterr*
:TsuquyomiAsyncGeterr
Show compilation errors of current buffer in QuickFix window
asynchronous.
*:TsuquyomiGeterrProject*
*:TsuGeterrProject*
:TsuquyomiGeterrProject
Show all compilation errors of your project in QuickFix window.
This command requires TypeScript@v1.6.0 or later.
*:TsuquyomiSeaarch*
*:TsuSearch*
:TsuquyomiSearch {keyword}
Search locations which contain {keyword} from project.
*:TsuquyomiQuickFix*
*:TsuQuickFix*
:TsuquyomiQuickFix
If the cursor is over an error and there is a quick fix is
for it, apply it.
This command requires TypeScript@v2.1.0 or later.
*:TsuquyomiSignatureHelp*
*:TsuSignatureHelp*
:TsuquyomiSignatureHelp
Display signature help for the method signature the cursor is
currently inside, and display the output in the preview
window. If there are multiple, overloads they will be
displayed along with any documentation.
Note that the cursor must be between the method parentheses,
or at least after the opening parenthesis of a partially
written method call.
------------------------------------------------------------------------------
VARIABLES *tsuquyomi-variables*
*g:tsuquyomi_auto_open*
g:tsuquyomi_auto_open (default 1)
Whether TSServer automatically opens the file of the current
buffer when the buffer is opened in Vim.
If you set this option to 0, you should manually exec the
|:TsuquyomiOpen| command after opening buffers.
*g:tsuquyomi_use_local_typescript*
g:tsuquyomi_use_local_typescript (default 1)
Whether to use `tsserver.js`that was installed for project
locally.
* 0: Tsuquyomi does not search `tsserver.js`.
* 1: Tsuquyomi searches the root directory(which has
`package.json`) of the project from the current directory.
Then, it searches
`node_modules/typescript/bin/tsserver.js` from the
project root.
If not hit, Tsuquyomi determines `tsserver.js` path from
|g:tsuquyomi_use_dev_node_module| option.
*g:tsuquyomi_use_dev_node_module*
g:tsuquyomi_use_dev_node_module (default 0)
Whether to use `tsserver.js` that was installed manually.
* 0: Tsuquyomi uses global installed `tsserver` command(default).
* 1: Tsuquyomi uses
`tsuquyomi/node_modules/typescript/bin/tsserver.js` .
If you use this mode, exec the following:
>
:cd ~/.vim/bundle/tsuquyomi
:!npm install
<
* 2: Tsuquyomi uses `tsserver.js` whose path is
|g:tsuquyomi_tsserver_path| .
*g:tsuquyomi_tsserver_path*
g:tsuquyomi_tsserver_path (default `''`)
If |g:tsuquyomi_use_dev_node_module| equals `2`,
user should set path of `tsserver.js` to this variable.
For example,
>
let g:tsuquyomi_use_dev_node_module = 2
let g:tsuquyomi_tsserver_path =
\ '~/typescript/built/local/tsserver.js'
<
*g:tsuquyomi_nodejs_path*
g:tsuquyomi_nodejs_path (default "node")
A path to Node.js.
*g:tsuquyomi_definition_split*
g:tsuquyomi_definition_split (default 0)
A way to open a target file when navigating with
|:TsuquyomiDefinition| or |:TsuquyomiTypeDefinition|.
* 0: |:edit|
* 1: |:split|
* 2: |:vsplit|
* 3: |:tabedit|
*g:tsuquyomi_completion_detail*
g:tsuquyomi_completion_detail (default 0)
Whether to show details of complete menu when |tsuquyomi#complete|.
This option makes sense when 'completeopt' includes "menu".
NOTE: If it's set, completions get slow.
*g:tsuquyomi_completion_case_sensitive*
g:tsuquyomi_completion_case_sensitive (default 0)
Whether to search completion case-sensitively when
|tsuquyomi#complete|.
*g:tsuquyomi_case_sensitive_imports*
g:tsuquyomi_case_sensitive_imports (default 0)
Whether to search imports case-sensitively when
|tsuquyomi#es6import#complete|.
*g:tsuquyomi_completion_preview*
g:tsuquyomi_completion_preview (default 0)
Whether to show signature in preview when |tsuquyomi#complete|.
This option makes sense when 'completeopt' includes "menu"
and "preview".
NOTE: If it's set, completions get a little slow.
*g:tsuquyomi_disable_default_mappings*
g:tsuquyomi_disable_default_mappings (default 0)
If set no keys are mapped.
*g:tsuquyomi_disable_quickfix*
g:tsuquyomi_disable_quickfix (default 0)
If set, |:TsuquyomiGeterr| isn't called when you save buffers.
*g:tsuquyomi_save_onrename*
g:tsuquyomi_save_onrename (default 0)
If set, Tsuquyomi automatically saves files in which symbols
are replaced when |:TsuquyomiRenameSymbol| is used.
If not set, Tsuquyomi shows replaced files with |:split|.
You can save them with |:wa| after your review changes.
*g:tsuquyomi_single_quote_import*
g:tsuquyomi_single_quote_import (default 0)
If set, Tsuquyomi generates import blocks using single quotes
instead of double quotes when |:TsuquyomiImport|.
*g:tsuquyomi_semicolon_import*
g:tsuquyomi_semicolon_import (default 1)
If set, Tsuquyomi generates import block using a semicolon at
the end when |:TsuquyomiImport|.
*g:tsuquyomi_baseurl_import_path*
g:tsuquyomi_baseurl_import_path (default 0)
If set, |:TsuquyomiImport| will use module paths relative to
`baseUrl` setting in tsconfig.json.
*g:tsuquyomi_javascript_support*
g:tsuquyomi_javascript_support (default 0)
If set, Tsuquyomi will also be enabled for JavaScript files.
You can further configure tsserver's behavior using
tsconfig.json or jsconfig.json in your project.
*g:tsuquyomi_ignore_missing_modules*
g:tsuquyomi_ignore_missing_modules (default 0)
If set, Tsuquyomi will ignore any error that starts with
"Cannot find module". Useful when you don't have type
definitions for all of your imports.
*g:tsuquyomi_use_vimproc*
g:tsuquyomi_use_vimproc (default 0)
If set, Tsuquyomi will use |vimproc| instead of vim8 default
jobs.
*g:tsuquyomi_locale*
g:tsuquyomi_locale (default "en")
LCID for deagonistics localization.
------------------------------------------------------------------------------
KEY MAPPINGS *tsuquyomi-key-mappings*
Normal mode key mappings.
*<Plug>(TsuquyomiDefinition)*
<Plug>(TsuquyomiDefinition)
Navigate to the location where the symbol under the cursor is
defined. See |:TsuquyomiDefinition|.
*<Plug>(TsuquyomiSplitDefinition)*
<Plug>(TsuquyomiSplitDefinition)
Split current window in two. Navigate to the location where
the symbol under the cursor is defined. See
|:TsuquyomiSplitDefinition|.
*<Plug>(TsuquyomiTypeDefinition)*
<Plug>(TsuquyomiTypeDefinition)
Navigate to the location where the type of the symbol under the
cursor is defined. See |:TsuquyomiTypeDefinition|.
<Plug>(TsuquyomiGoBack) *<Plug>(TsuquyomiGoBack)*
See |:TsuquyomiGoBack|.
*<Plug>(TsuquyomiImplementation)*
<Plug>(TsuquyomiImplementation)
Show a list of locations where the interface under the cursor is
implemented. See |:TsuquyomiImplementation|.
*<Plug>(TsuquyomiReferences)*
<Plug>(TsuquyomiReferences)
Show a list of locations where the symbol under the cursor is
referenced. See |:TsuquyomiReferences|.
*<Plug>(TsuquyomiRenameSymbol)*
<Plug>(TsuquyomiRenameSymbol)
Rename the identifier under the cursor to a new name.
See |:TsuquyomiRenameSymbol|.
*<Plug>(TsuquyomiRenameSymbolC)*
<Plug>(TsuquyomiRenameSymbolC)
Rename the identifier under the cursor to a new name.
See |:TsuquyomiRenameSymbolC|.
*<Plug>(TsuquyomiQuickFix)*
<Plug>(TsuquyomiQuickFix)
Apply quick fix under the cursor if it's available.
See |:TsuquyomiQuickFix|.
*<Plug>(TsuquyomiImport)*
<Plug>(TsuquyomiImport)
Generate ES6 import declaration whose alias is under
the cursor. See |:TsuquyomiImport|.
*<Plug>(TsuquyomiSignatureHelp)*
<Plug>(TsuquyomiSignatureHelp)
Display signature help for the method arguments under the
cursor. See |:TsuquyomiSignatureHelp|.
The following keymappings are default keymappings.
Normal mode default mappings.
{lhs} {rhs}
-------- -----------------------------
<C-]> <Plug>(TsuquyomiDefinition)
<C-W>] <Plug>(TsuquyomiSplitDefinition)
<C-W><C-]> <Plug>(TsuquyomiSplitDefinition)
<C-t> <Plug>(TsuquyomiGoBack)
<C-^> <Plug>(TsuquyomiReferences)
------------------------------------------------------------------------------
FUNCTIONS *tsuquyomi-functions*
tsuquyomi#complete *tsuquyomi#complete*
It's applicable for the 'completefunc' or 'omnifunc' option.
By default, Tsuquyomi sets this function to the 'omnifunc'
option.
If you want to customize completions, see
|tsuquyomi-examples-complete|.
tsuquyomi#balloonexpr *tsuquyomi#balloonexpr*
Returns information about the symbol under the mouse cursor for
tooltip popover window.
This is used as the rhs for 'balloonexpr'.
For example,
>
set ballooneval
autocmd BufNewFile,BufRead *.ts
\ setlocal balloonexpr=tsuquyomi#balloonexpr()
<
Note: This function works only if your Vim is compiled with
|+balloon_eval|.
tsuquyomi#hint *tsuquyomi#hint*
Returns information about the symbol under the cursor. This
function is similar to |tsuquyomi#balloonexpr|, but
works not only in GVim but also in terminal Vim.
For example,
>
autocmd FileType typescript nmap <buffer> <Leader>t :
\ <C-u>echo tsuquyomi#hint()<CR>
<
tsuquyomi#getSupportedCodeFixes *tsuquyomi#getSupportedCodeFixes*
Returns a list of all code quick fixes available.
An error whose code is in this list can be fixed via
|:TsuquyomiQuickFix| command.
Todo
==============================================================================
EXAMPLES *tsuquyomi-examples*
*tsuquyomi-examples-complete*
You can configure tsuquyomi completion, using 'completeopt' .
When completion in call of some method, show the method signature to
the preview window.
>
autocmd FileType typescript setlocal completeopt+=preview
<
When completion, don't show the popup menu of completions.
>
autocmd FileType typescript setlocal completeopt-=menu
<
Show compile errors to QuickFix window asynchronous by Vim8's Job/Channel
feature.
>
autocmd InsertLeave,BufWritePost *.ts,*.tsx call tsuquyomi#asyncGeterr()
<
==============================================================================
vim:tw=78:ts=8:ft=help:norl:noet:fen:

@ -0,0 +1 @@
autocmd BufNewFile,BufRead *.ts,*.tsx setfiletype typescript

@ -0,0 +1,16 @@
"============================================================================
" FILE: ftplugin/javascript.vim
" AUTHOR: Quramy <yosuke.kurami@gmail.com>
"============================================================================
scriptencoding utf-8
if !g:tsuquyomi_javascript_support
finish
endif
if !tsuquyomi#config#preconfig()
finish
endif
call tsuquyomi#config#initBuffer({ 'pattern': '*.js,*.jsx' })

@ -0,0 +1,12 @@
"============================================================================
" FILE: ftplugin/typescript.vim
" AUTHOR: Quramy <yosuke.kurami@gmail.com>
"============================================================================
scriptencoding utf-8
if !tsuquyomi#config#preconfig()
finish
endif
call tsuquyomi#config#initBuffer({ 'pattern': '*.ts,*.tsx' })

@ -0,0 +1,17 @@
{
"name": "tsuquyomi",
"version": "0.7.0",
"description": "Vim plugin for typescript",
"directories": {
"test": "vest"
},
"repository": "https://github.com/Quramy/tsuquyomi.git",
"devDependencies": {
"typescript": "~3.0.1"
},
"scripts": {
"test": "bash runtest.sh"
},
"author": "Quramy",
"license": "MIT"
}

@ -0,0 +1,94 @@
"============================================================================
" FILE: tsuquyomi.vim
" AUTHOR: Quramy <yosuke.kurami@gmail.com>
"============================================================================
scriptencoding utf-8
" Preprocessing {{{
if exists('g:loaded_tsuquyomi')
finish
elseif v:version < 704
echoerr 'Tsuquyomi does not work this version of Vim "' . v:version . '".'
finish
endif
let g:loaded_tsuquyomi = 1
let s:save_cpo = &cpo
set cpo&vim
" Preprocessing }}}
" Global options defintion. {{{
let g:tsuquyomi_auto_open =
\ get(g:, 'tsuquyomi_auto_open', 1)
let g:tsuquyomi_use_local_typescript =
\ get(g:, 'tsuquyomi_use_local_typescript', 1)
let g:tsuquyomi_use_dev_node_module =
\ get(g:, 'tsuquyomi_use_dev_node_module', 0)
let g:tsuquyomi_tsserver_path =
\ get(g:, 'tsuquyomi_tsserver_path', '')
let g:tsuquyomi_debug =
\ get(g:, 'tsuquyomi_debug', 0)
let g:tsuquyomi_tsserver_debug =
\ get(g:, 'tsuquyomi_tsserver_debug', 0)
let g:tsuquyomi_nodejs_path =
\ get(g:, 'tsuquyomi_nodejs_path', 'node')
let g:tsuquyomi_waittime_after_open =
\ get(g:, 'tsuquyomi_waittime_after_open', str2float("0.01"))
let g:tsuquyomi_completion_chunk_size =
\ get(g:, 'tsuquyomi_completion_chunk_size', 20)
let g:tsuquyomi_completion_detail =
\ get(g:, 'tsuquyomi_completion_detail', 0)
let g:tsuquyomi_completion_case_sensitive =
\ get(g:, 'tsuquyomi_completion_case_sensitive', 0)
let g:tsuquyomi_case_sensitive_imports =
\ get(g:, 'tsuquyomi_case_sensitive_imports', 0)
let g:tsuquyomi_completion_preview =
\ get(g:, 'tsuquyomi_completion_preview', 0)
let g:tsuquyomi_definition_split =
\ get(g:, 'tsuquyomi_definition_split', 0)
let g:tsuquyomi_disable_quickfix =
\ get(g:, 'tsuquyomi_disable_quickfix', 0)
let g:tsuquyomi_save_onrename =
\ get(g:, 'tsuquyomi_save_onrename', 0)
let g:tsuquyomi_single_quote_import =
\ get(g:, 'tsuquyomi_single_quote_import', 0)
let g:tsuquyomi_semicolon_import =
\ get(g:, 'tsuquyomi_semicolon_import', 1)
let g:tsuquyomi_import_curly_spacing =
\ get(g:, 'tsuquyomi_import_curly_spacing', 1)
let g:tsuquyomi_javascript_support =
\ get(g:, 'tsuquyomi_javascript_support', 0)
let g:tsuquyomi_ignore_missing_modules =
\ get(g:, 'tsuquyomi_ignore_missing_modules', 0)
let g:tsuquyomi_shortest_import_path =
\ get(g:, 'tsuquyomi_shortest_import_path', 0)
let g:tsuquyomi_baseurl_import_path =
\ get(g:, 'tsuquyomi_baseurl_import_path', 0)
let g:tsuquyomi_use_vimproc =
\ get(g:, 'tsuquyomi_use_vimproc', 0)
let g:tsuquyomi_locale =
\ get(g:, 'tsuquyomi_locale', 'en')
let g:tsuquyomi_search_term_min_length =
\ get(g:, 'tsuquyomi_search_term_min_length', 3)
" Global options defintion. }}}
" augroup tsuquyomi_global_command_group
" autocmd!
" augroup END
" Define commands to operate TSServer
command! TsuquyomiStartServer : call tsuquyomi#startServer()
command! TsuStartServer : call tsuquyomi#startServer()
command! TsuquyomiStatusServer : echom tsuquyomi#statusServer()
command! TsuStatusServer : echom tsuquyomi#statusServer()
command! TsuquyomiStopServer : call tsuquyomi#stopServer()
command! TsuStopServer : call tsuquyomi#stopServer()
" Close and re-open all buffers
command! TsuquyomiReloadProject : call tsuquyomi#reloadProject()
command! TsuReloadProject : call tsuquyomi#reloadProject()
let &cpo = s:save_cpo
unlet s:save_cpo

@ -0,0 +1,17 @@
#!/bin/bash
set -xe
if [ "${VERSION}" == "" ]; then
VERSION=3.2
fi
TSSERVER_PATH="$(pwd)/test/node_modules/typescript-${VERSION}/bin/tsserver"
vim -u test/.vimrc \
-c 'let g:tsuquyomi_use_local_typescript = 0' \
-c 'let g:tsuquyomi_use_dev_node_module = 2' \
-c "let g:tsuquyomi_tsserver_path = \"${TSSERVER_PATH}\"" \
-c 'call vesting#load()' \
-c 'call vesting#init()' \
-c "so $1" -c 'echom string(vesting#get_result())'

@ -0,0 +1,16 @@
#!/bin/bash
set -xe
VERSION=2.0 ./runtest.sh
VERSION=2.1 ./runtest.sh
VERSION=2.2 ./runtest.sh
VERSION=2.3 ./runtest.sh
VERSION=2.4 ./runtest.sh
VERSION=2.5 ./runtest.sh
VERSION=2.6 ./runtest.sh
VERSION=2.7 ./runtest.sh
VERSION=2.8 ./runtest.sh
VERSION=2.9 ./runtest.sh
VERSION=3.0 ./runtest.sh
VERSION=3.1 ./runtest.sh
VERSION=3.2 ./runtest.sh

@ -0,0 +1,104 @@
#!/bin/bash
VIMRC_FILE="test/.vimrc"
DRIVER_FILE="test/_runner"
RESULT_FILE="test/test_result.log"
VIM_BUILD=1
VIM_INSTALL_DIR=`pwd`/local
if [ "${VERSION}" == "" ]; then
VERSION=3.2
fi
TSSERVER_PATH="$(pwd)/test/node_modules/typescript-${VERSION}/bin/tsserver"
echo "Run test with ${TSSERVER_PATH}"
if [ "${VIM_BUILD}" -eq 1 ]; then
echo "`date "+[%Y-%m-%dT%H:%M:%S]"` Use local Vim."
if [ ! -d "./local" ]; then
echo "`date "+[%Y-%m-%dT%H:%M:%S]"` Installing Vim"
if [ ! -d "./vim" ]; then
echo "`date "+[%Y-%m-%dT%H:%M:%S]"` Clonning Vim source from Github"
git clone --depth 1 https://github.com/vim/vim.git
fi
cd vim
./configure --prefix=${VIM_INSTALL_DIR}
if [ ! $? -eq 0 ]; then
exit $?
fi
make
if [ ! $? -eq 0 ]; then
exit $?
fi
make install
if [ ! $? -eq 0 ]; then
exit $?
fi
echo "`date "+[%Y-%m-%dT%H:%M:%S]"` Vim was created successfully."
cd ..
fi
VIM_CMD="${VIM_INSTALL_DIR}/bin/vim"
else
echo "`date "+[%Y-%m-%dT%H:%M:%S]"` Use system Vim."
VIM_CMD="vim"
fi
${VIM_CMD} --version
if [ ! -d "./neobundle.vim" ]; then
echo "`date "+[%Y-%m-%dT%H:%M:%S]"` Installing neobundle"
git clone --depth 1 https://github.com/Shougo/neobundle.vim
fi
if [ "${HIDE_VIM}" == "" ]; then
${VIM_CMD} -u ${VIMRC_FILE} -c NeoBundleInstall -c q
else
${VIM_CMD} -u ${VIMRC_FILE} -c NeoBundleInstall -c q > /dev/null
fi
if [ -f "${RESULT_FILE}" ]; then
rm ${RESULT_FILE}
fi
echo "`date "+[%Y-%m-%dT%H:%M:%S]"` Run vesting."
# In CI, displaying Vim UI is meaningless and it makes CI logs dirty.
# So hide Vim UI.
if [ "${HIDE_VIM}" == "" ]; then
${VIM_CMD} \
-c 'let g:tsuquyomi_use_local_typescript = 0' \
-c 'let g:tsuquyomi_use_dev_node_module = 2' \
-c "let g:tsuquyomi_tsserver_path = \"${TSSERVER_PATH}\"" \
-u ${VIMRC_FILE} \
-s ${DRIVER_FILE}
else
${VIM_CMD} \
-c 'let g:tsuquyomi_use_local_typescript = 0' \
-c 'let g:tsuquyomi_use_dev_node_module = 2' \
-c "let g:tsuquyomi_tsserver_path = \"${TSSERVER_PATH}\"" \
-u ${VIMRC_FILE} \
-s ${DRIVER_FILE} > /dev/null
fi
if [ $? -ne 0 ]; then
echo "Vim exited with non-zero status."
exit 1
fi
echo "`date "+[%Y-%m-%dT%H:%M:%S]"` Done."
echo "`date "+[%Y-%m-%dT%H:%M:%S]"` Result: (${RESULT_FILE})"
cat ${RESULT_FILE}
grep -E "\[Fail\]" ${RESULT_FILE} > /dev/null
if [ $? -eq 0 ]; then
echo "`date "+[%Y-%m-%dT%H:%M:%S]"` Test was failed."
exit 1
fi
grep -E "\[Error\]" ${RESULT_FILE} > /dev/null
if [ $? -eq 0 ]; then
echo "`date "+[%Y-%m-%dT%H:%M:%S]"` Test was failed."
exit 1
fi
echo "`date "+[%Y-%m-%dT%H:%M:%S]"` Test was succeeded."
exit 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

@ -0,0 +1,36 @@
"============================================================================
" FILE: syntax_checkers/typescript/tsuquyomi.vim
" AUTHOR: Quramy <yosuke.kurami@gmail.com>
"============================================================================
" Preprocessing {{{
scriptencoding utf-8
if exists('g:loaded_syntastic_tsuquyomi_syntax_checker')
finish
endif
let g:loaded_syntastic_tsuquyomi_syntax_checker = 1
let s:save_cpo = &cpo
set cpo&vim
" Preprocessing }}}
function! SyntaxCheckers_typescript_tsuquyomi_IsAvailable() dict abort
return 1
endfunction
function! SyntaxCheckers_typescript_tsuquyomi_GetLocList() dict abort
let quickfix_list = tsuquyomi#createFixlist()
for qf in quickfix_list
let qf.valid = 1
let qf.bufnr = bufnr('%')
endfor
return quickfix_list
endfunction
call g:SyntasticRegistry.CreateAndRegisterChecker({
\ 'filetype': 'typescript',
\ 'name': 'tsuquyomi'
\ })
let &cpo = s:save_cpo
unlet s:save_cpo

@ -0,0 +1,27 @@
set nocompatible
let s:basedir = expand('<sfile>:p:h').'/../'
execute('set runtimepath+='.s:basedir)
execute('set runtimepath+='.s:basedir.'neobundle.vim')
call neobundle#begin(expand(s:basedir.'bundle'))
NeoBundle 'Shougo/unite.vim'
NeoBundle 'Shougo/vesting'
NeoBundle 'Shougo/vimproc.vim', {
\ 'build' : {
\ 'windows' : 'tools\\update-dll-mingw',
\ 'cygwin' : 'make -f make_cygwin.mak',
\ 'mac' : 'make -f make_mac.mak',
\ 'linux' : 'make',
\ 'unix' : 'gmake',
\ },
\ }
call neobundle#end()
let g:tsuquyomi_use_dev_node_module = 1
source plugin/tsuquyomi.vim

@ -0,0 +1,29 @@
:Unite vesting:./test/tsClient
*
:call unite#action#do('start')
k
ygg
:winc j
p
:%s/\n\s*\[OK\].*\s/+/g
:%s/\s\+/ /g
:%s/^\s\+\[Vest\]/[Vest]/g
:w! test/test_result.log
gg
dG
:winc k
q
q
:Unite vesting:./test/es6import
*
:call unite#action#do('start')
k
ygg
:winc j
p
:%s/\n\s*\[OK\].*\s/+/g
:%s/\s\+/ /g
:%s/^\s\+\[Vest\]/[Vest]/g
:w! >> test/test_result.log
:quitall!
vim:ft=vim

@ -0,0 +1,46 @@
scriptencoding utf-8
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = s:Filepath.join(tsuquyomi#rootDir(), 'test/es6import/vest')
" FIXME
" Context tsuquyomi#es6import#checkExternalModule(moduleName, file, no_use_cache)
" let s:input_file = s:Filepath.join(s:script_dir, 'resources/variousModules.d.ts')
"
" It returns 0 when the file does not have the given module
" call tsuquyomi#tsClient#tsOpen(s:input_file)
" let code = tsuquyomi#es6import#checkExternalModule('__NO_MODULE__', s:input_file, 1)
" Should code == 0
" call tsuquyomi#tsClient#stopTssSync()
" End
"
" It returns 1 when the file has single-quated module
" call tsuquyomi#tsClient#tsOpen(s:input_file)
" let code = tsuquyomi#es6import#checkExternalModule('external-module', s:input_file, 1)
" Should code == 1
" call tsuquyomi#tsClient#stopTssSync()
" End
"
" It returns 1 when the file has a double-quated module
" call tsuquyomi#tsClient#tsOpen(s:input_file)
" let code = tsuquyomi#es6import#checkExternalModule('external-module/alt', s:input_file, 1)
" Should code == 1
" call tsuquyomi#tsClient#stopTssSync()
" End
"
" It returns 0 when the file has a namespace
" call tsuquyomi#tsClient#tsOpen(s:input_file)
" let code = tsuquyomi#es6import#checkExternalModule('NS', s:input_file, 1)
" Should code == 0
" call tsuquyomi#tsClient#stopTssSync()
" End
"
" It returns 0 when the file has an internal module
" call tsuquyomi#tsClient#tsOpen(s:input_file)
" let code = tsuquyomi#es6import#checkExternalModule('InternalModule', s:input_file, 1)
" Should code == 0
" call tsuquyomi#tsClient#stopTssSync()
" End
"
" End

@ -0,0 +1,125 @@
scriptencoding utf-8
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = s:Filepath.join(tsuquyomi#rootDir(), 'test/es6import/vest')
" FIXME
" Context tsuquyomi#es6import#getImportDeclarations(file)
" let s:resource_dir = s:Filepath.join(s:script_dir, 'resources/importDecPatterns')
"
" " It returns 'no_nav_bar' reason when input files is empty
" " let s:file = s:Filepath.join(s:resource_dir, 'empty.ts')
" " call tsuquyomi#tsClient#tsOpen(s:file)
" " let [result_list, position, reason] = tsuquyomi#es6import#getImportDeclarations(s:file, readfile(s:file))
" " Should reason ==# 'no_module_info'
" " call tsuquyomi#tsClient#stopTssSync()
" " End
"
" " It returns 'no_module_info' reason and position info when input file doesn't have aliases
" " let s:file = s:Filepath.join(s:resource_dir, 'noDec.ts')
" " call tsuquyomi#tsClient#tsOpen(s:file)
" " let [result_list, position, reason] = tsuquyomi#es6import#getImportDeclarations(s:file, readfile(s:file))
" " Should reason ==# 'no_module_info'
" " Should position.start.line == 1
" " Should position.end.line == 3
" " call tsuquyomi#tsClient#stopTssSync()
" " End
"
" It returns position when input file has import declaration and other declarations
" let s:file = s:Filepath.join(s:resource_dir, 'decAndOther.ts')
" call tsuquyomi#tsClient#tsOpen(s:file)
" let [result_list, position, reason] = tsuquyomi#es6import#getImportDeclarations(s:file, readfile(s:file))
" Should reason ==# ''
" Should position.start.line == 1
" Should position.end.line == 1
" call tsuquyomi#tsClient#stopTssSync()
" End
"
" It returns position when input file has import declaration and expression
" let s:file = s:Filepath.join(s:resource_dir, 'decAndFunction.ts')
" call tsuquyomi#tsClient#tsOpen(s:file)
" let [result_list, position, reason] = tsuquyomi#es6import#getImportDeclarations(s:file, readfile(s:file))
" Should reason ==# ''
" Should position.start.line == 1
" Should position.end.line == 1
" call tsuquyomi#tsClient#stopTssSync()
" End
"
" It returns declaration_info list
" let s:file = s:Filepath.join(s:resource_dir, 'simple.ts')
" call tsuquyomi#tsClient#tsOpen(s:file)
" let [result_list, position, reason] = tsuquyomi#es6import#getImportDeclarations(s:file, readfile(s:file))
" Should reason ==# ''
" Should position.start.line == 1
" Should position.end.line == 1
" Should len(result_list) == 1
" Should result_list[0].is_oneliner == 1
" Should result_list[0].module.name ==# './some-module'
" Should result_list[0].module.start.line == 1
" Should result_list[0].module.end.line == 1
" Should result_list[0].has_brace == 1
" Should result_list[0].brace.end.line == 1
" Should result_list[0].brace.end.offset == 18
" Should result_list[0].has_from == 1
" Should result_list[0].from_span.start.offset == 20
" Should result_list[0].from_span.start.line == 1
" Should result_list[0].from_span.end.offset == 23
" Should result_list[0].from_span.end.line == 1
" call tsuquyomi#tsClient#stopTssSync()
" End
"
" It returns a info whose 'is_oneliner' is 0 when input declaration contains multipule lines
" let s:file = s:Filepath.join(s:resource_dir, 'multiline.ts')
" call tsuquyomi#tsClient#tsOpen(s:file)
" let [result_list, position, reason] = tsuquyomi#es6import#getImportDeclarations(s:file, readfile(s:file))
" Should reason ==# ''
" Should position.start.line == 1
" Should position.end.line == 7
" Should result_list[0].is_oneliner == 0
" Should result_list[0].module.start.line == 7
" Should result_list[0].module.end.line == 7
" Should result_list[0].has_brace == 1
" Should result_list[0].brace.end.line == 3
" Should result_list[0].brace.end.offset == 13
" Should result_list[0].has_from == 1
" Should result_list[0].from_span.start.offset == 1
" Should result_list[0].from_span.start.line == 5
" Should result_list[0].from_span.end.offset == 4
" Should result_list[0].from_span.end.line == 5
" call tsuquyomi#tsClient#stopTssSync()
" End
"
" It returns the list whoes has multiple module infos when input declaration contains multiple aliases in one module
" let s:file = s:Filepath.join(s:resource_dir, 'multiAlias.ts')
" call tsuquyomi#tsClient#tsOpen(s:file)
" let [result_list, position, reason] = tsuquyomi#es6import#getImportDeclarations(s:file, readfile(s:file))
" Should len(result_list) == 2
" Should result_list[0].alias_info.text ==# 'altVar'
" Should result_list[1].alias_info.text ==# 'someVar'
" call tsuquyomi#tsClient#stopTssSync()
" End
"
" It returns the list whoes has multiple module infos when input has 2 declaration
" let s:file = s:Filepath.join(s:resource_dir, 'multiDec.ts')
" call tsuquyomi#tsClient#tsOpen(s:file)
" let [result_list, position, reason] = tsuquyomi#es6import#getImportDeclarations(s:file, readfile(s:file))
" Should len(result_list) == 2
" Should result_list[0].alias_info.text ==# 'altVar'
" Should result_list[1].alias_info.text ==# 'someVar'
" call tsuquyomi#tsClient#stopTssSync()
" End
"
" It returns explict alias info when declarations use 'as' keyword
" let s:file = s:Filepath.join(s:resource_dir, 'explictAlias.ts')
" call tsuquyomi#tsClient#tsOpen(s:file)
" let [result_list, position, reason] = tsuquyomi#es6import#getImportDeclarations(s:file, readfile(s:file))
" Should len(result_list) == 2
" Should result_list[0].alias_info.text ==# '$var'
" Should result_list[0].has_brace == 0
" Should result_list[1].alias_info.text ==# '_var'
" Should result_list[1].has_brace == 1
" call tsuquyomi#tsClient#stopTssSync()
" End
"
" End

@ -0,0 +1,2 @@
import {someVar} from './some-module';
document.getElementById('myApp');

@ -0,0 +1,4 @@
import { someVar } from './some-module';
class Hoge {
private hoge: string;
}

@ -0,0 +1,2 @@
import { someVar as _var } from './some-module';
import * as $var from './some-module';

@ -0,0 +1,2 @@
import { someVar } from './some-module';
import { altVar } from './some-module';

@ -0,0 +1,5 @@
///<reference path="./some-module.ts" />
"use strict";
class TestClass {
}

@ -0,0 +1,2 @@
export var someVar: any = null;
export var altVar: any = null;

@ -0,0 +1,19 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"noImplicitAny": false,
"sourceMap": false
},
"files": [
"decAndFunction.ts",
"decAndOther.ts",
"empty.ts",
"explictAlias.ts",
"multiAlias.ts",
"multiline.ts",
"noDec.ts",
"simple.ts",
"some-module.ts"
]
}

@ -0,0 +1,19 @@
declare namespace NS {
namespace nestedNS {
}
}
declare namespace NS.nestedNS {
}
declare module InternalModule {
}
declare module InternalModule.SubModule {
}
declare module 'external-module' {
}
declare module "external-module/alt" {
}

@ -0,0 +1,22 @@
{
"name": "tsuquyomi-test",
"version": "0.0.0",
"description": "package.json file for testing tsuquyomi",
"main": "index.js",
"license": "MIT",
"dependencies": {
"typescript-2.0": "npm:typescript@2.0",
"typescript-2.1": "npm:typescript@2.1",
"typescript-2.2": "npm:typescript@2.2",
"typescript-2.3": "npm:typescript@2.3",
"typescript-2.4": "npm:typescript@2.4",
"typescript-2.5": "npm:typescript@2.5",
"typescript-2.6": "npm:typescript@2.6",
"typescript-2.7": "npm:typescript@2.7",
"typescript-2.8": "npm:typescript@2.8",
"typescript-2.9": "npm:typescript@2.9",
"typescript-3.0": "npm:typescript@3.0",
"typescript-3.1": "npm:typescript@3.1",
"typescript-3.2": "npm:typescript@3.2"
}
}

@ -0,0 +1,24 @@
module SimpleModule {
export interface Runnable {
run: ()=> any;
}
export class MyClass implements Runnable{
name: string;
greeting: string;
constructor (options?: {name?: string; priority?: number}) {
}
say (): string {
return this.greeting;
}
run () {
this.say()
}
}
var myObj = new MyClass();
export function main (): void {
myObj.say();
return;
}
}

@ -0,0 +1,24 @@
module SimpleModule {
export interface Runnable {
run: ()=> any;
}
export class MyClass implements Runnable{
name: string;
greeting: string;
constructor (options?: {name?: string; priority?: number}) {
}
say (): string {
return this.greeting;
}
run () {
this.say()
}
}
var myObj = new MyClass();
myObj.s
export function main (): void {
myObj.say();
return;
}
}

@ -0,0 +1,8 @@
class Hoge {
constructor() { }
}
class Foo extends Hoge {
constructor() {
}
}

@ -0,0 +1,7 @@
module ReferenceTest {
export class SomeClass {
}
export class OtherClass extends SomeClass{
}
}

@ -0,0 +1,6 @@
/// <reference path="referencesTestA.ts" />
module ReferenceTest {
export class AnotherClass extends SomeClass{
}
}

@ -0,0 +1,20 @@
module Test {
var hoge = 1, foo = hoge;
var x = hoge;
/**
*
* @param bar An argument
*
**/
var someFunc = (bar: string) => { };
var otherFunc = () => {
var prefix;
console.log(' prefix ');
console.log(" prefix ");
console.log(` prefix `);
}
}

@ -0,0 +1,5 @@
interface Hoge {
id: any;
// syntaxerror
foo: {
}

@ -0,0 +1,7 @@
{
"compilerOptions": {
"noImplicitAny": true,
"module": "commonjs"
},
"files": ["main.ts", "sub.ts"]
}

@ -0,0 +1,5 @@
module main {
interface IBase {}
class BaseClazz implements IBase {}
}

@ -0,0 +1,6 @@
{
"compilerOptions": {
"noImplicitAny": true
},
"files": ["main.ts"]
}

@ -0,0 +1,10 @@
module SignatureHelpTest {
function add (a:number, b:number): number;
function add (a:string, b:string): string;
function add (a:any, b:any): any {
return a + b;
}
var sum = add(1,
}

@ -0,0 +1,20 @@
module SignatureHelpTest {
export class Calc {
/**
*
* An add method.
* It's is useful for calc.
* @param a A operand.
* It is the first operand.
* @param b Another operand.
* @returns Summation of a and b.
*
**/
add(a: number, b: number): number {
return a + b;
}
}
var calc = new Calc();
calc.add(
}

@ -0,0 +1,24 @@
scriptencoding utf-8
Context Vesting.run()
It checks to sendCommandOneWay successfully
Should tsuquyomi#tsClient#sendCommandOneWay('open', {'file': 'myApp.ts'}) == []
call tsuquyomi#tsClient#stopTssSync()
End
It checks to sendCommandSyncResponse successfully
let res_list = tsuquyomi#tsClient#sendCommandSyncResponse('completions', {})
Should len(res_list) == 1
call tsuquyomi#tsClient#stopTssSync()
End
It checks to sendCommandSyncResponse successfully
let res_list = tsuquyomi#tsClient#sendCommandSyncEvents('geterr', {'files': ['hoge'], 'delay': 50}, str2float("0.01"), 0)
Should len(res_list) == 0
call tsuquyomi#tsClient#stopTssSync()
End
End
Fin

@ -0,0 +1,10 @@
scriptencoding utf-8
Context Vesting.run()
It checks to sendRequest successfully
let res = tsuquyomi#tsClient#sendRequest('{"command": "open", "arguments": { "file": "test/resrouces/SimpleModule.ts"}}', str2float("0.01"), 0, 0)
Should res == []
call tsuquyomi#tsClient#stopTssSync()
End
End
Fin

@ -0,0 +1,19 @@
scriptencoding utf-8
Context Vesting.run()
It checks TSServer's status after startTss.
call tsuquyomi#tsClient#startTss()
Should tsuquyomi#tsClient#statusTss() == 'run'
call tsuquyomi#tsClient#stopTssSync()
End
It checks TSServer's status after stopTssSync
call tsuquyomi#tsClient#startTss()
call tsuquyomi#tsClient#startTss()
call tsuquyomi#tsClient#stopTssSync()
Should tsuquyomi#tsClient#statusTss() == 'dead'
End
End
Fin

@ -0,0 +1,28 @@
scriptencoding utf-8
Context Vesting.run()
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = tsuquyomi#rootDir()
It checks interface of responce of 'completionEntryDetails' command.
let file = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/SimpleModule_writing.ts')
call tsuquyomi#tsClient#tsOpen(file)
let res_list = tsuquyomi#tsClient#tsCompletionEntryDetails(file, 19, 9, ['say', 'greeting'])
Should len(res_list) == 2
let display_texts = []
for result in res_list
let display = ''
for part in result.displayParts
let display = display.part.text
endfor
call add(display_texts, display)
endfor
Should display_texts[0] == '(method) SimpleModule.MyClass.say(): string'
Should display_texts[1] == '(property) SimpleModule.MyClass.greeting: string'
call tsuquyomi#tsClient#stopTssSync()
End
End
Fin

@ -0,0 +1,27 @@
scriptencoding utf-8
Context Vesting.run()
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = tsuquyomi#rootDir()
It checks interface of responce of 'completions' command.
let file = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/SimpleModule.ts')
call tsuquyomi#tsClient#tsOpen(file)
let res_list = tsuquyomi#tsClient#tsCompletions(file, 17, 0, 'setT')
Should len(res_list) == 1
Should res_list[0].name == 'setTimeout'
call tsuquyomi#tsClient#stopTssSync()
End
It checks interface of responce of 'completions' command with non-existing keyword.
let file = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/SimpleModule.ts')
call tsuquyomi#tsClient#tsOpen(file)
let res_list = tsuquyomi#tsClient#tsCompletions(file, 11, 0, 'NO_EXSIT_KEYWORD_XXXXXXX')
Should len(res_list) == 0
call tsuquyomi#tsClient#stopTssSync()
End
End
Fin

@ -0,0 +1,32 @@
scriptencoding utf-8
Context Vesting.run()
let s:V = vital#of('tsuquyomi')
let b:Filepath = s:V.import('System.Filepath')
let s:script_dir = tsuquyomi#rootDir()
let s:result = []
It checks interface of responce of 'definition' command.
let file = b:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/definitionTest.ts')
call tsuquyomi#tsClient#tsOpen(file)
let b:result= tsuquyomi#tsClient#tsDefinition(file, 2, 15)
Should len(b:result) == 1
Should b:Filepath.basename(b:result[0].file) == 'definitionTest.ts'
Should has_key(b:result[0], 'start') != 0
Should has_key(b:result[0].start, 'line')
Should has_key(b:result[0].start, 'offset') != 0
Should has_key(b:result[0], 'end') != 0
Should has_key(b:result[0].end, 'line') != 0
Should has_key(b:result[0].end, 'offset') != 0
call tsuquyomi#tsClient#stopTssSync()
End
It checkes no definition at no symbol
let file = b:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/definitionTest.ts')
call tsuquyomi#tsClient#tsOpen(file)
let b:result = tsuquyomi#tsClient#tsDefinition(file, 3, 1)
Should b:result == []
End
End
Fin

@ -0,0 +1,28 @@
scriptencoding utf-8
Context Vesting.run()
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = tsuquyomi#rootDir()
let s:ver = tsuquyomi#config#getVersion()
It checks interface of responce of 'getSupportedCodeFixes' command.
if s:ver.major == 2 && s:ver.minor == 0
echo "This test is pending on TypeScript 2.0. Please fix this test case!"
else
let file = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/codeFixTest.ts')
call tsuquyomi#tsClient#tsOpen(file)
let result_list = tsuquyomi#tsClient#tsGetCodeFixes(file, 6, 5, 6, 5, [2377])
" echo result_list
Should len(result_list)
Should has_key(result_list[0], 'changes')
Should len(result_list[0].changes)
Should has_key(result_list[0].changes[0], 'textChanges')
Should len(result_list[0].changes[0].textChanges)
Should result_list[0].changes[0].textChanges[0].newText =~ 'super();'
call tsuquyomi#tsClient#stopTssSync()
endif
End
End
Fin

@ -0,0 +1,23 @@
scriptencoding utf-8
Context Vesting.run()
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = tsuquyomi#rootDir()
let s:ver = tsuquyomi#config#getVersion()
It checks interface of responce of 'getSupportedCodeFixes' command.
if s:ver.major == 2 && s:ver.minor == 0
echo "This test is pending on TypeScript 2.0. Please fix this test case!"
else
let file = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/SimpleModule_writing.ts')
call tsuquyomi#tsClient#tsOpen(file)
let result_list = tsuquyomi#tsClient#tsGetSupportedCodeFixes()
Should len(result_list)
call tsuquyomi#tsClient#stopTssSync()
endif
End
End
Fin

@ -0,0 +1,36 @@
scriptencoding utf-8
Context Vesting.run()
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = tsuquyomi#rootDir()
let s:ver = tsuquyomi#config#getVersion()
It checks interface of responce of 'geterr' command.
if s:ver.major == 2 && s:ver.minor < 8
echo "This test is pending in between TypeScript 2.0 and 2.7. Please fix this test case!"
else
let file = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/SimpleModule_writing.ts')
call tsuquyomi#tsClient#tsOpen(file)
let files = [file]
let result_list = tsuquyomi#tsClient#tsGeterr(files, 10)
Should len(result_list) == 3
let semanticDiagDict = filter(copy(result_list), 'v:val.event == "semanticDiag"')[0].body
let syntaxDiagDict = filter(copy(result_list), 'v:val.event == "syntaxDiag"')[0].body
Should has_key(semanticDiagDict, 'diagnostics')
Should has_key(semanticDiagDict, 'file')
Should len(semanticDiagDict.diagnostics) > 0
Should has_key(semanticDiagDict.diagnostics[0], 'text')
Should has_key(semanticDiagDict.diagnostics[0], 'start')
Should has_key(semanticDiagDict.diagnostics[0].start, 'line')
Should has_key(semanticDiagDict.diagnostics[0].start, 'offset')
Should has_key(semanticDiagDict.diagnostics[0], 'end')
Should has_key(semanticDiagDict.diagnostics[0].end, 'line')
Should has_key(semanticDiagDict.diagnostics[0].end, 'offset')
call tsuquyomi#tsClient#stopTssSync()
endif
End
End
Fin

@ -0,0 +1,26 @@
scriptencoding utf-8
Context Vesting.run()
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = tsuquyomi#rootDir()
let s:ver = tsuquyomi#config#getVersion()
It checks interface of responce of 'geterr' command.
if (s:ver.major == 2 && s:ver.minor < 8) ||
\ (s:ver.major == 3 && s:ver.minor == 2)
echo "This test is pending in between TypeScript 2.0 and 2.7, or 3.2. Please fix this test case!"
else
let file = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/samplePrjs/errorPrj/main.ts')
let sub_file = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/samplePrjs/errorPrj/sub.ts')
call tsuquyomi#tsClient#tsOpen(file)
let result_list = tsuquyomi#tsClient#tsGeterrForProject(file, 10, 2)
Should len(result_list) == 6
Should sort(map(copy(result_list), 'v:val.body.file')) == [file, file, file, sub_file, sub_file, sub_file]
call tsuquyomi#tsClient#stopTssSync()
endif
End
End
Fin

@ -0,0 +1,38 @@
scriptencoding utf-8
Context Vesting.run()
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = tsuquyomi#rootDir()
let s:ver = tsuquyomi#config#getVersion()
It checks interface of responce of 'navbar' command.
if s:ver.major == 3 && s:ver.minor == 2
echo "This test is pending on TypeScript 3.2. Please fix this test case!"
else
let file = substitute(s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/SimpleModule.ts'), '\\', '/', 'g')
call tsuquyomi#tsClient#tsOpen(file)
let res_list = tsuquyomi#tsClient#tsNavBar(file)
" echo res_list
Should len(res_list) > 0
Should has_key(res_list[1], 'text')
Should res_list[1].text == 'SimpleModule'
Should has_key(res_list[1], 'kind')
Should res_list[1].kind == 'module'
Should has_key(res_list[1], 'kindModifiers')
Should has_key(res_list[1], 'spans')
Should len(res_list[1].spans) > 0
Should has_key(res_list[1].spans[0], 'start')
Should has_key(res_list[1].spans[0].start, 'line')
Should has_key(res_list[1].spans[0].start, 'offset')
Should has_key(res_list[1].spans[0], 'end')
Should has_key(res_list[1].spans[0].end, 'line')
Should has_key(res_list[1].spans[0].end, 'offset')
Should has_key(res_list[1], 'childItems')
Should len(res_list[1].childItems) > 0
call tsuquyomi#tsClient#stopTssSync()
endif
End
End
Fin

@ -0,0 +1,30 @@
scriptencoding utf-8
Context Vesting.run()
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = tsuquyomi#rootDir()
let s:ver = tsuquyomi#config#getVersion()
It checks interface of responce of 'navbar' command.
if s:ver.major == 3 && s:ver.minor == 2
echo "This test is pending on TypeScript 3.2. Please fix this test case!"
else
let file = substitute(s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/SimpleModule.ts'), '\\', '/', 'g')
call tsuquyomi#tsClient#tsOpen(file)
let res_list = tsuquyomi#tsClient#tsNavto(file, 'encodeURIComponent', 100)
" echo res_list
Should len(res_list) > 0
Should has_key(res_list[0], 'file')
Should has_key(res_list[0], 'name')
Should res_list[0].name == 'encodeURIComponent'
Should has_key(res_list[0], 'kind')
Should res_list[0].kind == 'function'
Should has_key(res_list[0], 'matchKind')
Should res_list[0].matchKind == 'exact'
call tsuquyomi#tsClient#stopTssSync()
endif
End
End
Fin

@ -0,0 +1,24 @@
scriptencoding utf-8
Context Vesting.run()
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = tsuquyomi#rootDir()
let s:ver = tsuquyomi#config#getVersion()
It checks interface of response of 'projectInfo' command
if s:ver.major == 3 && s:ver.minor == 2
echo "This test is pending on TypeScript 3.2. Please fix this test case!"
else
let file = substitute(s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/samplePrjs/prj001/main.ts'), '\\', '/', 'g')
call tsuquyomi#tsClient#tsOpen(file)
let res_projectInfo_dict = tsuquyomi#tsClient#tsProjectInfo(file, 1)
Should has_key(res_projectInfo_dict, 'configFileName')
Should has_key(res_projectInfo_dict, 'fileNames')
call tsuquyomi#tsClient#stopTssSync()
endif
End
End
Fin

@ -0,0 +1,26 @@
scriptencoding utf-8
Context Vesting.run()
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = tsuquyomi#rootDir()
It checks interface of responce of 'quickinfo' command.
let file = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/SimpleModule.ts')
call tsuquyomi#tsClient#tsOpen(file)
let res_dict = tsuquyomi#tsClient#tsQuickinfo(file, 14, 13)
Should has_key(res_dict, 'start')
Should has_key(res_dict.start, 'line')
Should has_key(res_dict.start, 'offset')
Should has_key(res_dict, 'end')
Should has_key(res_dict.end, 'line')
Should has_key(res_dict.end, 'offset')
Should has_key(res_dict, 'displayString')
Should has_key(res_dict, 'kind')
Should has_key(res_dict, 'kindModifiers')
Should res_dict.displayString == '(method) SimpleModule.MyClass.say(): string'
call tsuquyomi#tsClient#stopTssSync()
End
End
Fin

@ -0,0 +1,56 @@
scriptencoding utf-8
Context Vesting.run()
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = tsuquyomi#rootDir()
It checks interface of responce of 'references' command.
let file = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/referencesTestA.ts')
call tsuquyomi#tsClient#tsOpen(file)
let res_reference_list = tsuquyomi#tsClient#tsReferences(file, 2, 16)
Should has_key(res_reference_list, 'refs')
" res_reference_list.refs contains self definition.
Should len(res_reference_list.refs) == 2
Should has_key(res_reference_list.refs[0], 'file')
Should has_key(res_reference_list.refs[0], 'isWriteAccess')
Should has_key(res_reference_list.refs[0], 'lineText')
Should has_key(res_reference_list.refs[0], 'start')
Should has_key(res_reference_list.refs[0].start, 'line')
Should has_key(res_reference_list.refs[0].start, 'offset')
Should has_key(res_reference_list.refs[0], 'end')
Should has_key(res_reference_list.refs[0].end, 'line')
Should has_key(res_reference_list.refs[0].end, 'offset')
Should has_key(res_reference_list, 'symbolName')
Should res_reference_list.symbolName == 'SomeClass'
Should has_key(res_reference_list, 'symbolDisplayString')
call tsuquyomi#tsClient#stopTssSync()
End
It checks the reference from other files
let fileA = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/referencesTestA.ts')
let fileB = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/referencesTestB.ts')
call tsuquyomi#tsClient#tsOpen(fileA)
call tsuquyomi#tsClient#tsOpen(fileB)
let res_reference_list = tsuquyomi#tsClient#tsReferences(fileA, 2, 16)
Should has_key(res_reference_list, 'refs')
" res_reference_list.refs contains self definition , fileA reference and fileB reference.
Should len(res_reference_list.refs) == 3
Should has_key(res_reference_list.refs[0], 'file')
Should has_key(res_reference_list.refs[0], 'isWriteAccess')
Should has_key(res_reference_list.refs[0], 'lineText')
Should has_key(res_reference_list.refs[0], 'start')
Should has_key(res_reference_list.refs[0].start, 'line')
Should has_key(res_reference_list.refs[0].start, 'offset')
Should has_key(res_reference_list.refs[0], 'end')
Should has_key(res_reference_list.refs[0].end, 'line')
Should has_key(res_reference_list.refs[0].end, 'offset')
Should has_key(res_reference_list, 'symbolName')
Should res_reference_list.symbolName == 'SomeClass'
Should has_key(res_reference_list, 'symbolDisplayString')
call tsuquyomi#tsClient#stopTssSync()
End
End
Fin

@ -0,0 +1,22 @@
scriptencoding utf-8
Context Vesting.run()
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = tsuquyomi#rootDir()
let s:ver = tsuquyomi#config#getVersion()
It checks interface of responce of 'reload' command.
if v:true
echo 'this test fails with all TypeScript versions. Please fix this test!'
else
let file = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/SimpleModule.ts')
call tsuquyomi#tsClient#tsOpen(file)
Should tsuquyomi#tsClient#tsReload(file, file) == 1
call tsuquyomi#tsClient#stopTssSync()
endif
End
End
Fin

@ -0,0 +1,98 @@
scriptencoding utf-8
Context Vesting.run()
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = tsuquyomi#rootDir()
let s:ver = tsuquyomi#config#getVersion()
It checks interface of responce of 'rename' command.
if s:ver.major == 3
echo "This test is pending in between TypeScript 3. Please fix this test case!"
else
let file = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/SimpleModule.ts')
call tsuquyomi#tsClient#tsOpen(file)
let result_rename_dict = tsuquyomi#tsClient#tsRename(file, 5, 16, 0, 0)
Should has_key(result_rename_dict, 'info')
Should has_key(result_rename_dict.info, 'canRename')
Should has_key(result_rename_dict.info, 'displayName')
Should result_rename_dict.info.displayName == 'MyClass'
Should has_key(result_rename_dict.info, 'fullDisplayName')
Should has_key(result_rename_dict.info, 'kind')
Should result_rename_dict.info.kind == 'class'
Should has_key(result_rename_dict.info, 'triggerSpan')
Should has_key(result_rename_dict.info.triggerSpan, 'start')
Should has_key(result_rename_dict.info.triggerSpan, 'length')
Should has_key(result_rename_dict, 'locs')
Should len(result_rename_dict.locs) == 1
Should has_key(result_rename_dict.locs[0], 'file')
Should result_rename_dict.locs[0].file != 'test/tsClient/vest/resources/SimpleModule.ts'
Should stridx(result_rename_dict.locs[0].file, 'test/tsClient/vest/resources/SimpleModule.ts')
Should has_key(result_rename_dict.locs[0], 'locs')
Should len(result_rename_dict.locs[0].locs) == 2
Should has_key(result_rename_dict.locs[0].locs[0], 'start')
Should has_key(result_rename_dict.locs[0].locs[0].start, 'line')
Should has_key(result_rename_dict.locs[0].locs[0].start, 'offset')
Should has_key(result_rename_dict.locs[0].locs[0], 'end')
Should has_key(result_rename_dict.locs[0].locs[0].end, 'line')
Should has_key(result_rename_dict.locs[0].locs[0].end, 'offset')
call tsuquyomi#tsClient#stopTssSync()
endif
End
It checks rename command within symbol occurred across multiple files.
let fileA = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/referencesTestA.ts')
let fileB = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/referencesTestB.ts')
call tsuquyomi#tsClient#tsOpen(fileA)
call tsuquyomi#tsClient#tsOpen(fileB)
let result_rename_dict = tsuquyomi#tsClient#tsRename(fileA, 2, 16, 0, 0)
Should len(result_rename_dict.locs) == 2
Should stridx(result_rename_dict.locs[0].file, 'test/tsClient/vest/resources/referencesTestA.ts')
Should stridx(result_rename_dict.locs[1].file, 'test/tsClient/vest/resources/referencesTestB.ts')
call tsuquyomi#tsClient#stopTssSync()
End
It can rename when a line has two symbols. Should to that the result is sorted by reverse order.
if s:ver.major == 3
echo "This test is pending in between TypeScript 3. Please fix this test case!"
else
let file = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/renameTest.ts')
call tsuquyomi#tsClient#tsOpen(file)
let result_rename_dict = tsuquyomi#tsClient#tsRename(file, 3, 9, 0, 0)
Should len(result_rename_dict.locs[0].locs) == 3
Should result_rename_dict.locs[0].locs[0].start.line == 4
Should result_rename_dict.locs[0].locs[0].start.offset == 13
Should result_rename_dict.locs[0].locs[1].start.line == 3
Should result_rename_dict.locs[0].locs[1].start.offset == 25
Should result_rename_dict.locs[0].locs[2].start.line == 3
Should result_rename_dict.locs[0].locs[2].start.offset == 9
call tsuquyomi#tsClient#stopTssSync()
endif
End
It can rename variables in comments.
if (s:ver.major == 2 && (s:ver.minor == 4 || s:ver.minor == 5)) ||
\ (s:ver.major == 3)
echo "This test is pending in between TypeScript 2.4 and 2.5, or TypeScript 3. Please fix this test case!"
else
let file = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/renameTest.ts')
call tsuquyomi#tsClient#tsOpen(file)
let result_rename_dict = tsuquyomi#tsClient#tsRename(file, 11, 21, 1, 0)
Should len(result_rename_dict.locs[0].locs) == 2
Should result_rename_dict.locs[0].locs[1].start.line == 8
Should result_rename_dict.locs[0].locs[1].start.offset == 15
endif
End
" It can rename identifiers in strings.
" let file = s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/renameTest.ts')
" call tsuquyomi#tsClient#tsOpen(file)
" let result_rename_dict = tsuquyomi#tsClient#tsRename(file, 14, 13, 0, 1)
" Should len(result_rename_dict.locs[0].locs) == 4
" End
End
Fin

@ -0,0 +1,70 @@
scriptencoding utf-8
Context Vesting.run()
let s:V = vital#of('tsuquyomi')
let s:Filepath = s:V.import('System.Filepath')
let s:script_dir = tsuquyomi#rootDir()
It checks interface of responce of 'signatureHelp' command.
let file = substitute(s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/signatureHelpTest_writing.ts'), '\\', '/', 'g')
"echo l:file
call tsuquyomi#tsClient#tsOpen(file)
let res_signatureHelp_dict = tsuquyomi#tsClient#tsSignatureHelp(file, 19, 12)
"echo res_signatureHelp_dict
Should has_key(res_signatureHelp_dict, 'selectedItemIndex')
Should has_key(res_signatureHelp_dict, 'argumentCount')
Should has_key(res_signatureHelp_dict, 'argumentIndex')
Should has_key(res_signatureHelp_dict, 'applicableSpan')
Should has_key(res_signatureHelp_dict.applicableSpan, 'start')
Should has_key(res_signatureHelp_dict.applicableSpan.start, 'line')
Should has_key(res_signatureHelp_dict.applicableSpan.start, 'offset')
Should has_key(res_signatureHelp_dict.applicableSpan, 'end')
Should has_key(res_signatureHelp_dict.applicableSpan.end, 'line')
Should has_key(res_signatureHelp_dict.applicableSpan.end, 'offset')
Should has_key(res_signatureHelp_dict, 'items')
Should len(res_signatureHelp_dict.items)
Should has_key(res_signatureHelp_dict.items[0], 'separatorDisplayParts')
Should len(res_signatureHelp_dict.items[0].separatorDisplayParts)
Should has_key(res_signatureHelp_dict.items[0].separatorDisplayParts[0], 'kind')
Should has_key(res_signatureHelp_dict.items[0].separatorDisplayParts[0], 'text')
Should has_key(res_signatureHelp_dict.items[0], 'parameters')
Should len(res_signatureHelp_dict.items[0].parameters)
Should has_key(res_signatureHelp_dict.items[0].parameters[0], 'isOptional')
Should has_key(res_signatureHelp_dict.items[0].parameters[0], 'name')
Should res_signatureHelp_dict.items[0].parameters[0].name == 'a'
Should has_key(res_signatureHelp_dict.items[0].parameters[0], 'documentation')
Should len(res_signatureHelp_dict.items[0].parameters[0].documentation)
Should has_key(res_signatureHelp_dict.items[0].parameters[0].documentation[0], 'kind')
Should has_key(res_signatureHelp_dict.items[0].parameters[0].documentation[0], 'text')
Should res_signatureHelp_dict.items[0].parameters[0].documentation[0].text =~ 'A operand.'
Should has_key(res_signatureHelp_dict.items[0].parameters[0], 'displayParts')
Should len(res_signatureHelp_dict.items[0].parameters[0].displayParts)
Should has_key(res_signatureHelp_dict.items[0].parameters[0].displayParts[0], 'kind')
Should has_key(res_signatureHelp_dict.items[0].parameters[0].displayParts[0], 'text')
Should has_key(res_signatureHelp_dict.items[0], 'prefixDisplayParts')
Should len(res_signatureHelp_dict.items[0].prefixDisplayParts)
Should has_key(res_signatureHelp_dict.items[0].prefixDisplayParts[0], 'kind')
Should has_key(res_signatureHelp_dict.items[0].prefixDisplayParts[0], 'text')
Should has_key(res_signatureHelp_dict.items[0], 'suffixDisplayParts')
Should has_key(res_signatureHelp_dict.items[0].suffixDisplayParts[0], 'kind')
Should has_key(res_signatureHelp_dict.items[0].suffixDisplayParts[0], 'text')
Should has_key(res_signatureHelp_dict.items[0], 'documentation')
Should len(res_signatureHelp_dict.items[0].documentation)
Should has_key(res_signatureHelp_dict.items[0].documentation[0], 'kind')
Should has_key(res_signatureHelp_dict.items[0].documentation[0], 'text')
call tsuquyomi#tsClient#stopTssSync()
End
It returns two items when the method is overridden.
let file = substitute(s:Filepath.join(s:script_dir, 'test/tsClient/vest/resources/signatureHelpTest_overload.ts'), '\\', '/', 'g')
"echo l:file
call tsuquyomi#tsClient#tsOpen(file)
let res_signatureHelp_dict = tsuquyomi#tsClient#tsSignatureHelp(file, 9, 19)
"echo res_signatureHelp_dict
Should len(res_signatureHelp_dict.items) == 2
call tsuquyomi#tsClient#stopTssSync()
End
End
Fin

@ -0,0 +1,68 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"typescript-2.0@npm:typescript@2.0":
version "2.0.10"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.0.10.tgz#ccdd4ed86fd5550a407101a0814012e1b3fac3dd"
integrity sha1-zN1O2G/VVQpAcQGggUAS4bP6w90=
"typescript-2.1@npm:typescript@2.1":
version "2.1.6"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.1.6.tgz#40c7e6e9e5da7961b7718b55505f9cac9487a607"
integrity sha1-QMfm6eXaeWG3cYtVUF+crJSHpgc=
"typescript-2.2@npm:typescript@2.2":
version "2.2.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.2.2.tgz#606022508479b55ffa368b58fee963a03dfd7b0c"
integrity sha1-YGAiUIR5tV/6NotY/uljoD39eww=
"typescript-2.3@npm:typescript@2.3":
version "2.3.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.3.4.tgz#3d38321828231e434f287514959c37a82b629f42"
integrity sha1-PTgyGCgjHkNPKHUUlZw3qCtin0I=
"typescript-2.4@npm:typescript@2.4":
version "2.4.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.2.tgz#f8395f85d459276067c988aa41837a8f82870844"
integrity sha1-+DlfhdRZJ2BnyYiqQYN6j4KHCEQ=
"typescript-2.5@npm:typescript@2.5":
version "2.5.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.3.tgz#df3dcdc38f3beb800d4bc322646b04a3f6ca7f0d"
integrity sha512-ptLSQs2S4QuS6/OD1eAKG+S5G8QQtrU5RT32JULdZQtM1L3WTi34Wsu48Yndzi8xsObRAB9RPt/KhA9wlpEF6w==
"typescript-2.6@npm:typescript@2.6":
version "2.6.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4"
integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=
"typescript-2.7@npm:typescript@2.7":
version "2.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836"
integrity sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==
"typescript-2.8@npm:typescript@2.8":
version "2.8.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.8.4.tgz#0b1db68e6bdfb0b767fa2ab642136a35b059b199"
integrity sha512-IIU5cN1mR5J3z9jjdESJbnxikTrEz3lzAw/D0Tf45jHpBp55nY31UkUvmVHoffCfKHTqJs3fCLPDxknQTTFegQ==
"typescript-2.9@npm:typescript@2.9":
version "2.9.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==
"typescript-3.0@npm:typescript@3.0":
version "3.0.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.3.tgz#4853b3e275ecdaa27f78fda46dc273a7eb7fc1c8"
integrity sha512-kk80vLW9iGtjMnIv11qyxLqZm20UklzuR2tL0QAnDIygIUIemcZMxlMWudl9OOt76H3ntVzcTiddQ1/pAAJMYg==
"typescript-3.1@npm:typescript@3.1":
version "3.1.6"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.6.tgz#b6543a83cfc8c2befb3f4c8fba6896f5b0c9be68"
integrity sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==
"typescript-3.2@npm:typescript@3.2":
version "3.2.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.2.tgz#fe8101c46aa123f8353523ebdcf5730c2ae493e5"
integrity sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==

@ -0,0 +1,7 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
typescript@~3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.1.tgz#43738f29585d3a87575520a4b93ab6026ef11fdb"
Loading…
Cancel
Save