Merge commit '19b843f85139130344e4238488a9b94aa4a5ff51' as 'vim/bundle/tsuquyomi'
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,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 @@
|
||||
|
@ -0,0 +1,2 @@
|
||||
import { someVar as _var } from './some-module';
|
||||
import * as $var from './some-module';
|
@ -0,0 +1,4 @@
|
||||
import {
|
||||
someVar,
|
||||
altVar
|
||||
} from './some-module';
|
@ -0,0 +1,2 @@
|
||||
import { someVar } from './some-module';
|
||||
import { altVar } from './some-module';
|
@ -0,0 +1,7 @@
|
||||
import {
|
||||
someVar
|
||||
}
|
||||
|
||||
from
|
||||
|
||||
'./some-module';
|
@ -0,0 +1,5 @@
|
||||
///<reference path="./some-module.ts" />
|
||||
|
||||
"use strict";
|
||||
class TestClass {
|
||||
}
|
@ -0,0 +1 @@
|
||||
import { someVar } from './some-module';
|
@ -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,3 @@
|
||||
class App { }
|
||||
var app = new App();
|
||||
|
@ -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 @@
|
||||
class Foo extends InvalidBaseClass {}
|
@ -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…
Reference in New Issue