diff --git a/vim/bundle/tsuquyomi/.gitignore b/vim/bundle/tsuquyomi/.gitignore new file mode 100644 index 0000000..7d0ce41 --- /dev/null +++ b/vim/bundle/tsuquyomi/.gitignore @@ -0,0 +1,11 @@ +*.swp +*.swo +*.un~ +node_modules/ +NUL +.tmp* +*.log +neobundle.vim/ +bundle/ +vim/ +local/ diff --git a/vim/bundle/tsuquyomi/.travis.yml b/vim/bundle/tsuquyomi/.travis.yml new file mode 100644 index 0000000..d8d9373 --- /dev/null +++ b/vim/bundle/tsuquyomi/.travis.yml @@ -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 diff --git a/vim/bundle/tsuquyomi/README.md b/vim/bundle/tsuquyomi/README.md new file mode 100644 index 0000000..b467f8d --- /dev/null +++ b/vim/bundle/tsuquyomi/README.md @@ -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 ` ` 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 `` 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 `` , Tsuquyomi moves the cursor to the location where the last `` 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 `` 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 e (TsuquyomiRenameSymbol) +autocmd FileType typescript nmap E (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 t : echo tsuquyomi#hint() +``` + +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 diff --git a/vim/bundle/tsuquyomi/autoload/tsuquyomi.vim b/vim/bundle/tsuquyomi/autoload/tsuquyomi.vim new file mode 100644 index 0000000..3ae388f --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/tsuquyomi.vim @@ -0,0 +1,1205 @@ +"============================================================================ +" FILE: tsuquyomi.vim +" AUTHOR: Quramy +"============================================================================ + +scriptencoding utf-8 + +let s:save_cpo = &cpo +set cpo&vim + +let s:V = vital#of('tsuquyomi') +let s:Filepath = s:V.import('System.Filepath') +let s:script_dir = expand(':p:h') +"let s:root_dir = s:Filepath.join(s:script_dir, '../') +let s:root_dir = s:Filepath.dirname(s:Filepath.dirname(s:Filepath.remove_last_separator(s:Filepath.join(s:script_dir, '../')))) +" +" ### Utilites {{{ +function! s:error(msg) + echom (a:msg) + throw 'tsuquyomi: '.a:msg +endfunction + +function! s:normalizePath(path) + return substitute(a:path, '\\', '/', 'g') +endfunction + +function! s:joinParts(displayParts) + return join(map(a:displayParts, 'v:val.text'), '') +endfunction + +function! s:joinPartsIgnoreBreak(displayParts, replaceString) + let l:display = '' + for part in a:displayParts + if part.kind == 'lineBreak' + let l:display = l:display.a:replaceString + break + endif + let l:display = l:display.part.text + endfor + return l:display +endfunction + +" Check whether files are opened. +" Found not opend file, show message. +function! s:checkOpenAndMessage(filelist) + if tsuquyomi#tsClient#statusTss() == 'dead' + return [[], a:filelist] + endif + let opened = [] + let not_opend = [] + for file in a:filelist + if tsuquyomi#bufManager#isOpened(file) + call add(opened, file) + else + call add(not_opend, file) + endif + endfor + if len(not_opend) + for file in not_opend + if tsuquyomi#bufManager#isNotOpenable(file) + echom '[Tsuquyomi] The buffer "'.file.'" is not valid filepath, so tusuqoymi cannot open this buffer.' + return [opened, not_opend] + endif + endfor + echom '[Tsuquyomi] Buffers ['.join(not_opend, ', ').'] are not opened by TSServer. Please exec command ":TsuquyomiOpen '.join(not_opend).'" and retry.' + endif + return [opened, not_opend] +endfunction + +" Save current buffer to a temp file, and emit to reload TSServer. +" This function may be called for conversation with TSServer after user's change buffer. +function! s:flush() + if tsuquyomi#bufManager#isDirty(expand('%:p')) + let file_name = expand('%:p') + call tsuquyomi#bufManager#saveTmp(file_name) + call tsuquyomi#tsClient#tsReload(file_name, tsuquyomi#bufManager#tmpfile(file_name)) + call tsuquyomi#bufManager#setDirty(file_name, 0) + endif +endfunction + +function! s:is_valid_identifier(symbol_str) + return a:symbol_str =~ '^[A-Za-z_\$][A-Za-z_\$0-9]*$' +endfunction + +" Manually write content to the preview window. +" Opens a preview window to a scratch buffer named '__TsuquyomiScratch__' +function! s:writeToPreview(content) + silent pedit __TsuquyomiScratch__ + silent wincmd P + setlocal modifiable noreadonly + setlocal nobuflisted buftype=nofile bufhidden=wipe ft=typescript + put =a:content + 0d_ + setlocal nomodifiable readonly + silent wincmd p +endfunction + +function! s:setqflist(quickfix_list, ...) + " 0: Do not close cwindow automatically + " 1: Close cwindow automatically + let auto_close = len(a:000) ? a:0 : 0 + call setqflist(a:quickfix_list, 'r') + if len(a:quickfix_list) > 0 + cwindow + else + if auto_close != 0 + cclose + endif + endif +endfunction + +let s:diagnostics_queue = [] +let s:diagnostics_timer = -1 +function! s:addDiagnosticsQueue(delay, bufnum) + if index(s:diagnostics_queue, a:bufnum) != -1 + return + endif + + if s:diagnostics_timer != -1 + call timer_stop(s:diagnostics_timer) + let s:diagnostics_timer = -1 + endif + + call add(s:diagnostics_queue, a:bufnum) + + let s:diagnostics_timer = timer_start( + \ a:delay, + \ function('s:sendDiagnosticsQueue') + \ ) +endfunction + +function! s:sendDiagnosticsQueue(timer) abort + for l:bufnum in s:diagnostics_queue + if !bufexists(l:bufnum) + continue + endif + let l:file = tsuquyomi#emitChange(l:bufnum) + let l:delayMsec = 50 "TODO export global option + call tsuquyomi#tsClient#tsAsyncGeterr([l:file], l:delayMsec) + endfor + let s:diagnostics_queue = [] +endfunction + +" ### Utilites }}} + +" ### Public functions {{{ +" +function! tsuquyomi#rootDir() + return s:root_dir +endfunction + +" #### Server operations {{{ +function! tsuquyomi#startServer() + return tsuquyomi#tsClient#startTss() +endfunction + +function! tsuquyomi#stopServer() + call tsuquyomi#bufManager#clearMap() + return tsuquyomi#tsClient#stopTss() +endfunction + +function! tsuquyomi#statusServer() + return tsuquyomi#tsClient#statusTss() +endfunction + +" #### Server operations }}} + +" #### Notify changed {{{ +function! tsuquyomi#letDirty() + return tsuquyomi#bufManager#setDirty(expand('%:p'), 1) +endfunction + +function! tsuquyomi#flush() + call s:flush() +endfunction +" #### Notify changed }}} + +" #### File operations {{{ +function! tsuquyomi#open(...) + let filelist = a:0 ? map(range(1, a:{0}), 'expand(a:{v:val})') : [expand('%:p')] + return s:openFromList(filelist) +endfunction + +function! s:openFromList(filelist) + for file in a:filelist + if file == '' || tsuquyomi#bufManager#isNotOpenable(file) ||tsuquyomi#bufManager#isOpened(file) + continue + endif + call tsuquyomi#tsClient#tsOpen(file) + call tsuquyomi#bufManager#open(file) + endfor + return 1 +endfunction + +function! tsuquyomi#close(...) + let filelist = a:0 ? map(range(1, a:{0}), 'expand(a:{v:val})') : [expand('%:p')] + return s:closeFromList(filelist) +endfunction + +function! s:closeFromList(filelist) + let file_count = 0 + for file in a:filelist + if tsuquyomi#bufManager#isOpened(file) + call tsuquyomi#tsClient#tsClose(file) + call tsuquyomi#bufManager#close(file) + let file_count = file_count + 1 + endif + endfor + return file_count +endfunction + +function! s:reloadFromList(filelist) + let file_count = 0 + for file in a:filelist + if tsuquyomi#bufManager#isOpened(file) + call tsuquyomi#tsClient#tsReload(file, file) + else + call tsuquyomi#tsClient#tsOpen(file) + call tsuquyomi#bufManager#open(file) + endif + call tsuquyomi#bufManager#setDirty(file, 0) + let file_count = file_count + 1 + endfor + return file_count +endfunction + +function! tsuquyomi#reload(...) + let filelist = a:0 ? map(range(1, a:{0}), 'expand(a:{v:val})') : [expand('%:p')] + return s:reloadFromList(filelist) +endfunction + +function! tsuquyomi#reloadProject() + if tsuquyomi#config#isHigher(160) + call tsuquyomi#tsClient#tsReloadProjects() + else + let filelist = values(map(tsuquyomi#bufManager#openedFiles(), 'v:val.bufname')) + if len(filelist) + call s:closeFromList(filelist) + call s:openFromList(filelist) + endif + endif +endfunction + +function! tsuquyomi#dump(...) + let filelist = a:0 ? map(range(1, a:{0}), 'expand(a:{v:val})') : [expand('%:p')] + let [opend, not_opend] = s:checkOpenAndMessage(filelist) + + for file in opend + call tsuquyomi#tsClient#tsSaveto(file, file.'.dump') + endfor +endfunction +" #### File operations }}} + +" #### Project information {{{ +function! tsuquyomi#projectInfo(file) + if !tsuquyomi#config#isHigher(160) + echom '[Tsuquyomi] This feature requires TypeScript@1.6.0 or higher' + return {} + endif + if len(s:checkOpenAndMessage([a:file])[1]) + return {} + endif + let l:result = tsuquyomi#tsClient#tsProjectInfo(a:file, 1) + let l:result.filteredFileNames = [] + if has_key(l:result, 'fileNames') + for fileName in l:result.fileNames + if fileName =~ 'typescript/lib/lib.d.ts$' + else + call add(l:result.filteredFileNames, fileName) + endif + endfor + endif + return l:result +endfunction +" }}} + +" #### Complete {{{ +" +function! tsuquyomi#setPreviewOption() + " issue #41 + " I'll consider how to highlighting preview window without setting filetype. + " + " if &previewwindow + " setlocal ft=typescript + " endif +endfunction + +function! tsuquyomi#makeCompleteMenu(file, line, offset, entryNames) + call tsuquyomi#perfLogger#record('tsCompletionEntryDetail') + let res_list = tsuquyomi#tsClient#tsCompletionEntryDetails(a:file, a:line, a:offset, a:entryNames) + call tsuquyomi#perfLogger#record('tsCompletionEntryDetail_done') + let display_texts = [] + for result in res_list + call add(display_texts, s:joinPartsIgnoreBreak(result.displayParts, '{...}')) + endfor + return display_texts +endfunction + +" Get signature help information for preview window. +function! tsuquyomi#getSignatureHelp(file, line, offset) + + if stridx(&completeopt, 'preview') == -1 + return [0, ''] + endif + + let l:sig_dict = tsuquyomi#tsClient#tsSignatureHelp(a:file, a:line, a:offset) + let has_info = 0 + if has_key(l:sig_dict, 'items') && len(l:sig_dict.items) + let has_info = 1 + let info_lines = [] + + for sigitem in l:sig_dict.items + let siginfo_list = [] + let dispText = s:joinParts(sigitem.prefixDisplayParts) + let params_list = [] + for paramInfo in sigitem.parameters + let param_text = s:joinParts(paramInfo.displayParts) + if len(paramInfo.documentation) + let param_text = param_text.'/* '.s:joinPartsIgnoreBreak(paramInfo.documentation, ' ...').' */' + endif + call add(params_list, param_text) + endfor + let dispText = dispText.join(params_list, ', ').s:joinParts(sigitem.suffixDisplayParts) + if len(sigitem.documentation) + let dispText = dispText.'/* '.s:joinPartsIgnoreBreak(sigitem.documentation, ' ...').' */' + endif + call add(info_lines, dispText) + endfor + + let sigitem = l:sig_dict.items[0] + return [has_info, join(info_lines, "\n\n")] + endif + + return [has_info, ''] +endfunction + +" Comparator comparing on TypeScript CompletionEntry's 'sortText' property +" See https://github.com/Microsoft/TypeScript/blob/master/src/server/protocol.ts#L1483 +function! s:sortTextComparator(entry1, entry2) + if a:entry1.sortText < a:entry2.sortText + return -1 + elseif a:entry1.sortText > a:entry2.sortText + return 1 + else + return 0 + endif +endfunction + +function! tsuquyomi#signatureHelp() + pclose + + if len(s:checkOpenAndMessage([expand('%:p')])[1]) + return + endif + + call s:flush() + + let l:file = expand('%:p') + let l:line = line('.') + let l:offset = col('.') + let [has_info, siginfo] = tsuquyomi#getSignatureHelp(l:file, l:line, l:offset) + if has_info + call s:writeToPreview(siginfo) + endif +endfunction + +function! tsuquyomi#complete(findstart, base) + if len(s:checkOpenAndMessage([expand('%:p')])[1]) + return + endif + + let l:line_str = getline('.') + let l:line = line('.') + let l:offset = col('.') + + " search backwards for start of identifier (iskeyword pattern) + let l:start = l:offset + while l:start > 0 && l:line_str[l:start-2] =~ "\\k" + let l:start -= 1 + endwhile + + if(a:findstart) + call tsuquyomi#perfLogger#record('before_flush') + call s:flush() + call tsuquyomi#perfLogger#record('after_flush') + return l:start - 1 + else + let l:file = expand('%:p') + let l:res_dict = {'words': []} + call tsuquyomi#perfLogger#record('before_tsCompletions') + " By default the result list will be sorted by the 'name' properly alphabetically + let l:alpha_sorted_res_list = tsuquyomi#tsClient#tsCompletions(l:file, l:line, l:start, a:base) + call tsuquyomi#perfLogger#record('after_tsCompletions') + + let is_javascript = (&filetype == 'javascript') || (&filetype == 'jsx') || (&filetype == 'javascript.jsx') + if is_javascript + " Sort the result list according to how TypeScript suggests entries to be sorted + let l:res_list = sort(copy(l:alpha_sorted_res_list), 's:sortTextComparator') + else + let l:res_list = l:alpha_sorted_res_list + endif + + let enable_menu = stridx(&completeopt, 'menu') != -1 + let length = strlen(a:base) + if enable_menu + call tsuquyomi#perfLogger#record('start_menu') + if g:tsuquyomi_completion_preview + let [has_info, siginfo] = tsuquyomi#getSignatureHelp(l:file, l:line, l:start) + else + let [has_info, siginfo] = [0, ''] + endif + + let size = g:tsuquyomi_completion_chunk_size + let j = 0 + while j * size < len(l:res_list) + let entries = [] + let items = [] + let upper = min([(j + 1) * size, len(l:res_list)]) + for i in range(j * size, upper - 1) + let info = l:res_list[i] + if !length + \ || !g:tsuquyomi_completion_case_sensitive && info.name[0:length - 1] == a:base + \ || g:tsuquyomi_completion_case_sensitive && info.name[0:length - 1] ==# a:base + let l:item = {'word': info.name, 'menu': info.kind } + if has_info + let l:item.info = siginfo + endif + if is_javascript && info.kind == 'warning' + let l:item.menu = '' " Make display cleaner by not showing 'warning' as the type + endif + if !g:tsuquyomi_completion_detail + call complete_add(l:item) + else + " if file is TypeScript, then always add to entries list to + " fetch details. Or in the case of JavaScript, avoid adding to + " entries list if ScriptElementKind is 'warning'. Because those + " entries are just random identifiers that occur in the file. + if !is_javascript || info.kind != 'warning' + call add(entries, info.name) + endif + call add(items, l:item) + endif + endif + endfor + if g:tsuquyomi_completion_detail + call tsuquyomi#perfLogger#record('before_completeMenu'.j) + let menus = tsuquyomi#makeCompleteMenu(l:file, l:line, l:start, entries) + call tsuquyomi#perfLogger#record('after_completeMenu'.j) + let idx = 0 + for menu in menus + let items[idx].menu = menu + let items[idx].info = menu + call complete_add(items[idx]) + let idx = idx + 1 + endfor + " For JavaScript completion, there are entries whose + " ScriptElementKind is 'warning'. tsserver won't have any details + " returned for them, but they still need to be added at the end. + for i in range(idx, len(items) - 1) + call complete_add(items[i]) + endfor + endif + if complete_check() + break + endif + let j = j + 1 + endwhile + return [] + else + return filter(map(l:res_list, 'v:val.name'), 'stridx(v:val, a:base) == 0') + endif + + endif +endfunction +" ### Complete }}} + +" #### Definition {{{ +function! tsuquyomi#gotoDefinition(tsClientFunction, splitMode) + if len(s:checkOpenAndMessage([expand('%:p')])[1]) + return + endif + + call s:flush() + + let l:file = s:normalizePath(expand('%:p')) + let l:line = line('.') + let l:offset = col('.') + let l:res_list = a:tsClientFunction(l:file, l:line, l:offset) + let l:definition_split = a:splitMode > 0 ? a:splitMode : g:tsuquyomi_definition_split + + if(len(l:res_list) > 0) + " If get result, go to last location. + let l:info = l:res_list[len(l:res_list) - 1] + if a:splitMode == 0 && l:file == l:info.file + " Same file without split + call tsuquyomi#bufManager#winPushNavDef(bufwinnr(bufnr('%')), l:file, {'line': l:line, 'col': l:offset}) + call cursor(l:info.start.line, l:info.start.offset) + elseif l:definition_split == 0 + call tsuquyomi#bufManager#winPushNavDef(bufwinnr(bufnr('%')), l:file, {'line': l:line, 'col': l:offset}) + execute 'edit +call\ cursor('.l:info.start.line.','.l:info.start.offset.') '.l:info.file + elseif l:definition_split == 1 + execute 'split +call\ cursor('.l:info.start.line.','.l:info.start.offset.') '.l:info.file + elseif l:definition_split == 2 + execute 'vsplit +call\ cursor('.l:info.start.line.','.l:info.start.offset.') '.l:info.file + elseif l:definition_split == 3 + execute 'tabedit +call\ cursor('.l:info.start.line.','.l:info.start.offset.') '.l:info.file + endif + else + " If don't get result, do nothing. + endif +endfunction + +function! tsuquyomi#definition() + call tsuquyomi#gotoDefinition(function('tsuquyomi#tsClient#tsDefinition'), 0) +endfunction + +function! tsuquyomi#splitDefinition() + call tsuquyomi#gotoDefinition(function('tsuquyomi#tsClient#tsDefinition'), 1) +endfunction + +function! tsuquyomi#typeDefinition() + call tsuquyomi#gotoDefinition(function('tsuquyomi#tsClient#tsTypeDefinition'), 0) +endfunction + +function! tsuquyomi#goBack() + let [type, result] = tsuquyomi#bufManager#winPopNavDef(bufwinnr(bufnr('%'))) + if !type + echom '[Tsuquyomi] No items in navigation stack...' + return + endif + let [file_name, loc] = [result.file_name, result.loc] + if expand('%:p') == file_name + call cursor(loc.line, loc.col) + else + execute 'edit +call\ cursor('.loc.line.','.loc.col.') '.file_name + endif +endfunction + +" #### Definition }}} + +" #### References {{{ +" Show reference on a location window. +function! tsuquyomi#getLocations(tsClientFunction, functionTitle) + if len(s:checkOpenAndMessage([expand('%:p')])[1]) + return + endif + + call s:flush() + + let l:file = expand('%:p') + let l:line = line('.') + let l:offset = col('.') + + " 1. Fetch reference information. + let l:res = a:tsClientFunction(l:file, l:line, l:offset) + + let l:references = [] + if type(l:res) == v:t_dict && has_key(l:res, 'refs') + let l:references = l:res.refs + elseif type(l:res) == v:t_list + let l:references = l:res + endif + + if len(l:references) != 0 + let l:location_list = [] + " 2. Make a location list for `setloclist` + for reference in l:references + if has_key(reference, 'lineText') + let l:location_info = { + \'filename': fnamemodify(reference.file, ':~:.'), + \'lnum': reference.start.line, + \'col': reference.start.offset, + \'text': reference.lineText + \} + else + let l:location_info = { + \'filename': fnamemodify(reference.file, ':~:.'), + \'lnum': reference.start.line, + \'col': reference.start.offset + \} + endif + call add(l:location_list, l:location_info) + endfor + if len(l:location_list) > 0 + call setloclist(0, l:location_list, 'r') + "3. Open location window. + lwindow + endif + else + echom '[Tsuquyomi] '.a:functionTitle.': Not found...' + endif +endfunction + +function! tsuquyomi#references() + call tsuquyomi#getLocations(function('tsuquyomi#tsClient#tsReferences'), 'References') +endfunction + +function! tsuquyomi#implementation() + call tsuquyomi#getLocations(function('tsuquyomi#tsClient#tsImplementation'), 'Implementation') +endfunction + +" #### References }}} + +" #### Geterr {{{ + +function! tsuquyomi#asyncGeterr(...) + if g:tsuquyomi_is_available == 1 + call tsuquyomi#registerNotify(function('s:setqflist'), 'diagnostics') + + let l:delay = len(a:000) ? a:1 : 0 + call tsuquyomi#asyncCreateFixlist(l:delay) + endif +endfunction + +function! tsuquyomi#parseDiagnosticEvent(event, supportedCodes) + let quickfix_list = [] + let codes = len(a:supportedCodes) > 0 ? a:supportedCodes : s:supportedCodeFixes + if has_key(a:event, 'type') && a:event.type ==# 'event' && (a:event.event ==# 'syntaxDiag' || a:event.event ==# 'semanticDiag') + for diagnostic in a:event.body.diagnostics + if diagnostic.text =~ "Cannot find module" && g:tsuquyomi_ignore_missing_modules == 1 + continue + endif + let item = {} + let item.filename = a:event.body.file + let item.lnum = diagnostic.start.line + if(has_key(diagnostic.start, 'offset')) + let item.col = diagnostic.start.offset + endif + let item.text = diagnostic.text + if !has_key(diagnostic, 'code') + continue + endif + let item.code = diagnostic.code + let l:cfidx = index(codes, (diagnostic.code . '')) + if l:cfidx >= 0 + let l:qfmark = '[QF available]' + let item.text = diagnostic.code . l:qfmark . ': ' . item.text + endif + let item.availableCodeFix = l:cfidx >= 0 + let item.type = 'E' + call add(quickfix_list, item) + endfor + endif + return quickfix_list +endfunction + +function! tsuquyomi#registerNotify(callback, eventName) + call tsuquyomi#tsClient#registerNotify(a:callback, a:eventName) +endfunction + +function! tsuquyomi#emitChange(bufnum) + let l:input = join(getbufline(a:bufnum, 1, '$'), "\n") . "\n" + let l:file = expand('%:p') + + " file, line, offset, endLine, endOffset, insertString + call tsuquyomi#tsClient#tsAsyncChange(l:file, 1, 1, len(l:input), 1, l:input) + + return l:file +endfunction + +function! tsuquyomi#asyncCreateFixlist(...) + " Works only Vim8(+channel, +job) + " We must register callbacks(handler and callback) before execute this. + " See `tsuquyomi#config#initBuffer()` + if len(s:checkOpenAndMessage([expand('%:p')])[1]) + return [] + endif + + let l:delay = len(a:000) ? a:1 : 0 + let l:bufnum = bufnr('%') + + " Tell TSServer to change for get syntaxDiag and semanticDiag errors. + if delay > 0 + " Debunce request for Textchanged autocmd. + call s:addDiagnosticsQueue(l:delay, l:bufnum) + else + " Cancel current timer + if s:diagnostics_timer != -1 + call timer_stop(s:diagnostics_timer) + let s:diagnostics_timer = -1 + endif + + let l:file = tsuquyomi#emitChange(l:bufnum) + let l:delayMsec = 50 "TODO export global option + call tsuquyomi#tsClient#tsAsyncGeterr([l:file], l:delayMsec) + endif +endfunction + +function! tsuquyomi#createQuickFixListFromEvents(event_list) + if !len(a:event_list) + return [] + endif + let quickfix_list = [] + let supportedCodes = tsuquyomi#getSupportedCodeFixes() + for event_item in a:event_list + let items = tsuquyomi#parseDiagnosticEvent(event_item, supportedCodes) + let quickfix_list = quickfix_list + items + endfor + return quickfix_list +endfunction + +function! tsuquyomi#createFixlist() + if len(s:checkOpenAndMessage([expand('%:p')])[1]) + return [] + endif + call s:flush() + + let l:files = [expand('%:p')] + let l:delayMsec = 50 "TODO export global option + + " 1. Fetch error information from TSServer. + let result = tsuquyomi#tsClient#tsGeterr(l:files, l:delayMsec) + + " 2. Make a quick fix list for `setqflist`. + return tsuquyomi#createQuickFixListFromEvents(result) +endfunction + +function! tsuquyomi#geterr() + let quickfix_list = tsuquyomi#createFixlist() + + call s:setqflist(quickfix_list, 1) +endfunction + +function! tsuquyomi#geterrProject() + + if !tsuquyomi#config#isHigher(160) + echom '[Tsuquyomi] This feature requires TypeScript@1.6.0 or higher' + return + endif + + if len(s:checkOpenAndMessage([expand('%:p')])[1]) + return + endif + + call s:flush() + let l:file = expand('%:p') + + " 1. Fetch Project info for event count. + let l:pinfo = tsuquyomi#projectInfo(l:file) + if !has_key(l:pinfo, 'filteredFileNames') || !len(l:pinfo.filteredFileNames) + return + endif + + " 2. Fetch error information from TSServer. + let l:delayMsec = 50 "TODO export global option + let l:result = tsuquyomi#tsClient#tsGeterrForProject(l:file, l:delayMsec, len(l:pinfo.filteredFileNames)) + + " 3. Make a quick fix list for `setqflist`. + let quickfix_list = tsuquyomi#createQuickFixListFromEvents(result) + + call s:setqflist(quickfix_list, 1) +endfunction + +function! tsuquyomi#reloadAndGeterr() + if tsuquyomi#tsClient#statusTss() != 'dead' + return tsuquyomi#geterr() + endif +endfunction + +" #### Geterr }}} + +" #### Balloon {{{ +function! tsuquyomi#balloonexpr() + call s:flush() + let res = tsuquyomi#tsClient#tsQuickinfo(fnamemodify(buffer_name(v:beval_bufnr),":p"), v:beval_lnum, v:beval_col) + if has_key(res, 'displayString') + if (has_key(res, 'documentation') && res.documentation != '') + return join([res.documentation, res.displayString], "\n\n") + endif + + return res.displayString + endif +endfunction + +function! tsuquyomi#hint() + call s:flush() + let res = tsuquyomi#tsClient#tsQuickinfo(expand('%:p'), line('.'), col('.')) + if has_key(res, 'displayString') + if (has_key(res, 'documentation') && res.documentation != '') + return join([res.documentation, res.displayString], "\n\n") + endif + + return res.displayString + else + return '[Tsuquyomi] There is no hint at the cursor.' + endif +endfunction + +" #### Balloon }}} + +" #### Rename {{{ +function! tsuquyomi#renameSymbol() + return s:renameSymbolWithOptions(0, 0) +endfunction + +function! tsuquyomi#renameSymbolWithComments() + return s:renameSymbolWithOptions(1, 0) +endfunction + +function! tsuquyomi#renameSymbolWithStrings() + return s:renameSymbolWithOptions(0, 1) +endfunction + +function! tsuquyomi#renameSymbolWithCommentsStrings() + return s:renameSymbolWithOptions(1, 1) +endfunction + +function! s:renameSymbolWithOptions(findInComments, findInString) + + if len(s:checkOpenAndMessage([expand('%:p')])[1]) + return + endif + + call s:flush() + + let l:filename = expand('%:p') + let l:line = line('.') + let l:offset = col('.') + + " * Make a list of locations of symbols to be replaced. + let l:res_dict = tsuquyomi#tsClient#tsRename(l:filename, l:line, l:offset, a:findInComments, a:findInString) + + " * Check the symbol is renameable + if !has_key(l:res_dict, 'info') + echom '[Tsuquyomi] No symbol to be renamed' + return + elseif !l:res_dict.info.canRename + echom '[Tsuquyomi] '.l:res_dict.info.localizedErrorMessage + return + endif + + " * Check affection only current buffer. + if len(l:res_dict.locs) != 1 || s:normalizePath(expand('%:p')) != l:res_dict.locs[0].file + let file_list = map(copy(l:res_dict.locs), 'v:val.file') + let dirty_file_list = tsuquyomi#bufManager#whichDirty(file_list) + call s:reloadFromList(dirty_file_list) + endif + + " * Question user what new symbol name. + echohl String + let renameTo = input('[Tsuquyomi] New symbol name : ') + echohl none + if !s:is_valid_identifier(renameTo) + echo ' ' + echom '[Tsuquyomi] It is a not valid identifer.' + return + endif + + let s:locs_dict = {} + let s:rename_to = renameTo + let s:other_buf_list = [] + + " * Execute to replace symbols by location, by buffer + for fileLoc in l:res_dict.locs + let is_open = tsuquyomi#bufManager#isOpened(fileLoc.file) + if !is_open + let s:locs_dict[s:normalizePath(fileLoc.file)] = fileLoc.locs + call add(s:other_buf_list, s:normalizePath(fileLoc.file)) + continue + endif + let buffer_name = tsuquyomi#bufManager#bufName(fileLoc.file) + let s:locs_dict[buffer_name] = fileLoc.locs + "echom 'fileLoc.file '.fileLoc.file.', '.buffer_name + let changed_count = 0 + if buffer_name != expand('%:p') + call add(s:other_buf_list, buffer_name) + continue + endif + endfor + + if !g:tsuquyomi_save_onrename + let changed_count = s:renameLocal(0) + echohl String + echo ' ' + echo 'Changed '.changed_count.' locations.' + echohl none + for otherbuf in s:other_buf_list + execute('silent split +call\ s:renameLocal(0) '.otherbuf) + endfor + else + echohl String + let l:confirm = input('[Tsuquyomi] The symbol is located in '.(len(s:other_buf_list) + 1).' files. Really replace them? [Y/n]') + echohl none + if l:confirm != 'n' && l:confirm != 'no' + call s:renameLocalSeq(-1) + endif + endif +endfunction + +function! s:renameLocal(should_save) + let changed_count = 0 + let filename = expand('%:p') + let locations_in_buf = s:locs_dict[expand('%:p')] + let renameTo = s:rename_to + for span in locations_in_buf + if span.start.line != span.end.line + echom '[Tsuquyomi] this span is across multiple lines. ' + return + endif + + let lineidx = span.start.line + let linestr = getline(lineidx) + if span.start.offset - 1 + let pre = linestr[:(span.start.offset - 2)] + let post = linestr[(span.end.offset - 1):] + let linestr = pre.renameTo.post + else + let post = linestr[(span.end.offset - 1):] + let linestr = renameTo.post + endif + call setline(lineidx, linestr) + let changed_count = changed_count + 1 + endfor + call tsuquyomi#reload() + if a:should_save + write + endif + return changed_count +endfunction + +function! s:renameLocalSeq(index) + call s:renameLocal(1) + if a:index + 1 < len(s:other_buf_list) + let l:next = s:other_buf_list[a:index + 1] + execute('silent edit +call\ s:renameLocalSeq('.(a:index + 1).') '.l:next) + else + echohl String + echo ' ' + echo 'Changed '.(a:index + 2).' files successfuly.' + echohl none + endif +endfunction +" #### Rename }}} + +" #### NavBar {{{ +function! tsuquyomi#navBar() + if len(s:checkOpenAndMessage([expand('%:p')])[1]) + return [[], 0] + endif + + call s:flush() + + let l:filename = expand('%:p') + + let result_list = tsuquyomi#tsClient#tsNavBar(tsuquyomi#bufManager#normalizePath(l:filename)) + + if len(result_list) + return [result_list, 1] + else + return [[], 0] + endif + +endfunction +" #### NavBar }}} + +" #### Navto {{{ +function! tsuquyomi#navto(term, kindModifiers, matchKindType) + + if len(a:term) < g:tsuquyomi_search_term_min_length + echom "[Tsuquyomi] search term's length should be greater than ".g:tsuquyomi_search_term_min_length."." + return [[], 0] + endif + + if len(s:checkOpenAndMessage([expand('%:p')])[1]) + return [[], 0] + endif + + call s:flush() + + let l:filename = expand('%:p') + + let result_list = tsuquyomi#tsClient#tsNavto(tsuquyomi#bufManager#normalizePath(l:filename), a:term, 100) + + if len(result_list) + let list = [] + for result in result_list + let flg = 1 + if a:matchKindType == 1 + let flg = flg && (result.matchKind=='prefix' || result.matchKind=='exact') + elseif a:matchKindType == 2 + let flg = flg && (result.matchKind=='exact') + endif + if a:kindModifiers != '' + let flg = flg && has_key(result, 'kindModifiers') && result.kindModifiers=~a:kindModifiers + endif + if flg + call add(list, result) + endif + endfor + return [list, 1] + else + echohl Error + echom "[Tsuquyomi] Nothing was hit." + echohl none + return [[], 0] + endif + +endfunction + +function! tsuquyomi#navtoByLoclist(term, kindModifiers, matchKindType) + let [result_list, res_code] = tsuquyomi#navto(a:term, a:kindModifiers, a:matchKindType) + if res_code + let l:location_list = [] + for navtoItem in result_list + let text = navtoItem.kind.' '.navtoItem.name + if has_key(navtoItem, 'kindModifiers') + let text = navtoItem.kindModifiers.' '.text + endif + if has_key(navtoItem, 'containerName') + if has_key(navtoItem, 'containerKind') + let text = text.' in '.navtoItem.containerKind.' '.navtoItem.containerName + else + let text = text.' in '.navtoItem.containerName + endif + endif + let l:location_info = { + \'filename': navtoItem.file, + \'lnum': navtoItem.start.line, + \'col': navtoItem.start.offset, + \'text': text + \} + call add(l:location_list, l:location_info) + endfor + if(len(l:location_list) > 0) + call setloclist(0, l:location_list, 'r') + lwindow + endif + endif +endfunction + +function! tsuquyomi#navtoByLoclistContain(term) + call tsuquyomi#navtoByLoclist(a:term, '', 0) +endfunction + +function! tsuquyomi#navtoByLoclistPrefix(term) + call tsuquyomi#navtoByLoclist(a:term, '', 1) +endfunction + +function! tsuquyomi#navtoByLoclistExact(term) + call tsuquyomi#navtoByLoclist(a:term, '', 2) +endfunction + +" #### Navto }}} + +" #### Configure {{{ +function! tsuquyomi#sendConfigure() + let l:file = expand('%:p') + let l:hostInfo = &viminfo + let l:formatOptions = { } + let l:extraFileExtensions = [] + if exists('&shiftwidth') + let l:formatOptions.baseIndentSize = &shiftwidth + let l:formatOptions.indentSize = &shiftwidth + endif + if exists('&expandtab') + let l:formatOptions.convertTabsToSpaces = &expandtab + endif + call tsuquyomi#tsClient#tsConfigure(l:file, l:hostInfo, l:formatOptions, l:extraFileExtensions) +endfunction +" #### }}} + +" #### CodeFixes {{{ + +function! s:sortQfItemByColdiff(a, b) + if a:a.coldiff < a:b.coldiff + return -1 + endif + if a:a.coldiff == a:b.coldiff + return 0 + endif + if a:a.coldiff > a:b.coldiff + return 1 + endif +endfunction + +let s:supportedCodeFixes = [] +function! tsuquyomi#getSupportedCodeFixes() + if !tsuquyomi#config#isHigher(210) + return [] + endif + if len(s:supportedCodeFixes) + return s:supportedCodeFixes + endif + try + let s:supportedCodeFixes = tsuquyomi#tsClient#tsGetSupportedCodeFixes() + return s:supportedCodeFixes + catch + return [] + endtry +endfunction + +function! tsuquyomi#quickFix() + if !tsuquyomi#config#isHigher(210) + echom '[Tsuquyomi] This feature requires TypeScript@2.1.0 or higher' + return + endif + if len(s:checkOpenAndMessage([expand('%:p')])[1]) + return + endif + call s:flush() + let l:file = expand('%:p') + let l:line = line('.') + let l:col = col('.') + let l:qfList = tsuquyomi#createFixlist() + call filter(l:qfList, 'v:val.lnum == l:line') + if !len(l:qfList) + echom '[Tsuquyomi] There is no error to fix' + return + endif + if len(l:qfList) > 1 + let l:temp = [] + for qfItem in qfList + let qfItem.coldiff = abs(qfItem.col - l:col) + call add(l:temp, qfItem) + endfor + call sort(l:temp, function('s:sortQfItemByColdiff')) + let l:target = l:temp[0] + else + let l:target = l:qfList[0] + endif + let l:supportedCodes = copy(tsuquyomi#getSupportedCodeFixes()) + call filter(l:supportedCodes, 'v:val == l:target.code') + if !len(l:supportedCodes) + echom '[Tsuquyomi] '.l:target.code.' has no quick fixes...' + return + endif + let l:result_list = tsuquyomi#tsClient#tsGetCodeFixes(file, l:target.lnum, l:target.col, l:target.lnum, l:target.col, [l:target.code]) + if !len(l:result_list) + echom '[Tsuquyomi] '.l:target.code.' has no quick fixes...' + return + endif + let s:available_qf_descriptions = map(copy(l:result_list), 'v:val.description') + let [description, isSelect] = tsuquyomi#selectQfDescription() + if !isSelect + return + endif + let l:changes = filter(l:result_list, 'v:val.description ==# description')[0].changes + " TODO + " allow other file + for fileChange in l:changes + if tsuquyomi#bufManager#normalizePath(l:file) !=# fileChange.fileName + echom '[Tsuquyomi] Tsuquyomi does not support this code fix...' + return + endif + endfor + call tsuquyomi#applyQfChanges(l:changes) +endfunction + +function! tsuquyomi#applyQfChanges(changes) + for fileChange in a:changes + " TODO + " allow fileChange.fileName + for textChange in fileChange.textChanges + let linesCountForReplacement = textChange.end.line - textChange.start.line + 1 + let preSpan = strpart(getline(textChange.start.line), 0, textChange.start.offset - 1) + let postSpan = strpart(getline(textChange.end.line), textChange.end.offset - 1) + let repList = split(preSpan.textChange.newText.postSpan, '\n') + let l:count = textChange.start.line + for rLine in repList + if l:count <= textChange.end.line + call setline(l:count, rLine) + else + call append(l:count - 1, rLine) + endif + let l:count = l:count + 1 + endfor + endfor + endfor +endfunction + +let s:available_qf_descriptions = [] +function! tsuquyomi#selectQfComplete(arg_lead, cmd_line, cursor_pos) + return join(s:available_qf_descriptions, "\n") +endfunction + +function! tsuquyomi#selectQfDescription() + echohl String + if len(s:available_qf_descriptions) == 1 + let l:yn = input('[Tsuquyomi] Apply: "'.s:available_qf_descriptions[0].'" [y/N]') + echohl none + echo ' ' + if l:yn =~ 'N' + return ['', 0] + else + return [s:available_qf_descriptions[0], 1] + endif + endif + let l:selected_desc = input('[Tsuquyomi] You can apply 2 more than quick fixes. Select one (candidates are shown using TAB): ', '', 'custom,tsuquyomi#selectQfComplete') + echohl none + echo ' ' + if len(filter(copy(s:available_qf_descriptions), 'v:val==#l:selected_desc')) + return [l:selected_desc, 1] + else + echohl Error + echom '[Tsuquyomi] Invalid selection.' + echohl none + return ['', 0] + endif +endfunction +"#### CodeFixes }}} + +" ### Public functions }}} + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/vim/bundle/tsuquyomi/autoload/tsuquyomi/bufManager.vim b/vim/bundle/tsuquyomi/autoload/tsuquyomi/bufManager.vim new file mode 100644 index 0000000..94d471c --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/tsuquyomi/bufManager.vim @@ -0,0 +1,165 @@ +"============================================================================ +" FILE: bufManager.vim +" AUTHOR: Quramy +"============================================================================ + +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 diff --git a/vim/bundle/tsuquyomi/autoload/tsuquyomi/config.vim b/vim/bundle/tsuquyomi/autoload/tsuquyomi/config.vim new file mode 100644 index 0000000..c32a3a8 --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/tsuquyomi/config.vim @@ -0,0 +1,320 @@ +"============================================================================ +" FILE: autoload/tsuquyomi/config.vim +" AUTHOR: Quramy +"============================================================================ +" +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(':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() + command! -buffer -nargs=* -complete=buffer TsuOpen :call tsuquyomi#open() + command! -buffer -nargs=* -complete=buffer TsuquyomiClose :call tsuquyomi#close() + command! -buffer -nargs=* -complete=buffer TsuClose :call tsuquyomi#close() + command! -buffer -nargs=* -complete=buffer TsuquyomiReload :call tsuquyomi#reload() + command! -buffer -nargs=* -complete=buffer TsuReload :call tsuquyomi#reload() + command! -buffer -nargs=* -complete=buffer TsuquyomiDump :call tsuquyomi#dump() + command! -buffer -nargs=* -complete=buffer TsuDump :call tsuquyomi#dump() + command! -buffer -nargs=1 TsuquyomiSearch :call tsuquyomi#navtoByLoclistContain() + command! -buffer -nargs=1 TsuSearch :call tsuquyomi#navtoByLoclistContain() + + 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 (TsuquyomiDefinition) :TsuquyomiDefinition + noremap (TsuquyomiSplitDefinition) :TsuquyomiSplitDefinition + noremap (TsuquyomiTypeDefinition) :TsuquyomiTypeDefinition + noremap (TsuquyomiGoBack) :TsuquyomiGoBack + noremap (TsuquyomiImplementation) :TsuquyomiImplementation + noremap (TsuquyomiReferences) :TsuquyomiReferences + noremap (TsuquyomiRenameSymbol) :TsuquyomiRenameSymbol + noremap (TsuquyomiRenameSymbolC) :TsuquyomiRenameSymbolC + noremap (TsuquyomiQuickFix) :TsuquyomiQuickFix + noremap (TsuquyomiSignatureHelp) :TsuquyomiSignatureHelp + noremap (TsuquyomiImport) :TsuquyomiImport + + " TODO These commands don't work correctly. + noremap (TsuquyomiRenameSymbolS) :TsuquyomiRenameSymbolS + noremap (TsuquyomiRenameSymbolCS) :TsuquyomiRenameSymbolCS +endfunction + +function! tsuquyomi#config#applyBufLocalDefaultMap() + if(!exists('g:tsuquyomi_disable_default_mappings')) + if !hasmapto('(TsuquyomiDefinition)') + map (TsuquyomiDefinition) + endif + if !hasmapto('(TsuquyomiSplitDefinition)') + map ] (TsuquyomiSplitDefinition) + map (TsuquyomiSplitDefinition) + endif + if !hasmapto('(TsuquyomiGoBack)') + map (TsuquyomiGoBack) + endif + if !hasmapto('(TsuquyomiReferences)') + map (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 diff --git a/vim/bundle/tsuquyomi/autoload/tsuquyomi/es6import.vim b/vim/bundle/tsuquyomi/autoload/tsuquyomi/es6import.vim new file mode 100644 index 0000000..a447855 --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/tsuquyomi/es6import.vim @@ -0,0 +1,509 @@ +"============================================================================ +" FILE: autoload/tsuquyomi/es6import.vim +" AUTHOR: Quramy +"============================================================================ + +scriptencoding utf-8 + +let s:save_cpo = &cpo +set cpo&vim + +let s:V = vital#of('tsuquyomi') +let s:Filepath = s:V.import('System.Filepath') +let s:JSON = s:V.import('Web.JSON') + +function! s:normalizePath(path) + return substitute(a:path, '\\', '/', 'g') +endfunction + +function! s:is_valid_identifier(symbol_str) + return a:symbol_str =~ '^[A-Za-z_\$][A-Za-z_\$0-9]*$' +endfunction + +function! s:get_keyword_under_cursor() + let l:line_str = getline('.') + let l:line = line('.') + let l:offset = col('.') + " search backwards for start of identifier (iskeyword pattern) + let l:start = l:offset + let l:end = l:offset + while l:start > 0 && l:line_str[l:start-2] =~ "\\k" + let l:start -= 1 + endwhile + while l:end <= strlen(l:line_str) && l:line_str[l:end] =~ "\\k" + let l:end += 1 + endwhile + return { + \ 'text': l:line_str[l:start-1:l:end-1], + \ 'start': { 'offset': l:start, 'line': l:line }, + \ 'end': { 'offset': l:end, 'line': l:line } + \ } +endfunction + +function! s:relativePath(from, to) + let l:from_parts = s:Filepath.split(s:Filepath.dirname(a:from)) + let l:to_parts = s:Filepath.split(a:to) + let l:count_node_modules = len(filter(copy(l:to_parts), 'v:val==#"node_modules"')) + if l:count_node_modules > 1 + return ['', 0] + elseif l:count_node_modules == 1 + return [substitute(a:to, '^.*\/node_modules\/', '', ''), 1] + endif + let l:idx = 0 + while idx < min([len(l:from_parts), len(l:to_parts)]) && l:from_parts[l:idx] ==# l:to_parts[l:idx] + let l:idx += 1 + endwhile + call remove(l:from_parts, 0, l:idx - 1) + call remove(l:to_parts, 0, l:idx - 1) + if len(l:from_parts) + return [join(map(l:from_parts, '"../"'), '').join(l:to_parts, '/'), 1] + else + return ['./'.join(l:to_parts, '/'), 1] + endif +endfunction + +let s:external_module_cache_dict = {} +function! tsuquyomi#es6import#checkExternalModule(name, file, no_use_cache) + let l:cache = s:external_module_cache_dict + if a:no_use_cache || !has_key(l:cache, a:file) || !has_key(l:cache[a:file], a:name) + if !has_key(l:cache, a:file) + let l:cache[a:file] = {} + endif + let l:result = tsuquyomi#tsClient#tsNavBar(a:file) + let l:modules = map(filter(l:result, 'v:val.kind==#"module"'), 'v:val.text') + let l:cache[a:file][a:name] = 0 + for module_name in l:modules + if module_name[0] ==# '"' || module_name[0] ==# "'" + if module_name[1:-2] ==# a:name + let l:cache[a:file][a:name] = 1 + break + endif + endif + endfor + endif + return l:cache[a:file][a:name] +endfunction + +function! tsuquyomi#es6import#createImportBlock(text) + let l:identifier = a:text + if !s:is_valid_identifier(l:identifier) + return [] + endif + let [l:nav_list, l:hit] = tsuquyomi#navto(l:identifier, 'export', 2) + if !l:hit || !len(l:nav_list) + return [] + endif + let l:from = s:normalizePath(expand('%:p')) + let l:result_list = [] + for nav in l:nav_list + if has_key(nav, 'containerKind') && nav.containerKind ==# 'module' + if tsuquyomi#es6import#checkExternalModule(nav.containerName, nav.file, 0) + let l:importDict = { + \ 'identifier': nav.name, + \ 'path': nav.containerName, + \ 'nav': nav + \ } + call add(l:result_list, l:importDict) + endif + else + let l:to = s:normalizePath(nav.file) + let [l:relative_path, l:result] = s:relativePath(l:from, l:to) + if !l:result + return [] + endif + let l:relative_path = s:removeTSExtensions(l:relative_path) + if g:tsuquyomi_shortest_import_path == 1 + let l:path = s:getShortestImportPath(l:to, l:identifier, l:relative_path) + elseif g:tsuquyomi_baseurl_import_path == 1 + let l:base_url_import_path = s:getBaseUrlImportPath(nav.file) + let l:path = l:base_url_import_path != '' ? l:base_url_import_path : l:relative_path + else + let l:path = l:relative_path + endif + let l:importDict = { + \ 'identifier': nav.name, + \ 'path': l:path, + \ 'nav': nav + \ } + call add(l:result_list, l:importDict) + endif + endfor + + if g:tsuquyomi_case_sensitive_imports == 1 + call filter(l:result_list, 'v:val.identifier ==# l:identifier') + endif + + " Make the possible imports list unique per path + let dictionary = {} + for i in l:result_list + let dictionary[i.path] = i + endfor + + let l:unique_result_list = [] + + if (exists('a:1')) + let l:unique_result_list = sort(values(dictionary), a:1) + else + let l:unique_result_list = sort(values(dictionary)) + endif + + return l:unique_result_list +endfunction + +function! s:removeTSExtensions(path) + let l:path = a:path + let l:path = substitute(l:path, '\.d\.ts$', '', '') + let l:path = substitute(l:path, '\.ts$', '', '') + let l:path = substitute(l:path, '\.tsx$', '', '') + let l:path = substitute(l:path, '^@types/', '', '') + let l:path = substitute(l:path, '/index$', '', '') + return l:path +endfunction + +function! s:getShortestImportPath(absolute_path, module_identifier, relative_path) + let l:splitted_relative_path = split(a:relative_path, '/') + if l:splitted_relative_path[0] == '..' + let l:paths_to_visit = substitute(a:relative_path, '\.\.\/', '', 'g') + let l:path_moves_to_do = len(split(l:paths_to_visit, '/')) + else + let l:path_moves_to_do = len(l:splitted_relative_path) - 1 + endif + let l:shortened_path = l:splitted_relative_path[len(l:splitted_relative_path) - 1] + let l:path_move_count = 0 + let l:splitted_absolute_path = split(a:absolute_path, '/') + while l:path_move_count != l:path_moves_to_do + let l:splitted_absolute_path = l:splitted_absolute_path[0:len(splitted_absolute_path) - 2] + let l:shortened_path = s:getShortenedPath(l:splitted_absolute_path, l:shortened_path, a:module_identifier) + let l:path_move_count += 1 + endwhile + let l:shortened_path = substitute(l:shortened_path, '\[\/\]\*\[\index\]\*', '', 'g') + if l:splitted_relative_path[0] == '.' + return './' . s:getPathWithSkippedRoot(l:shortened_path) + elseif l:splitted_relative_path[0] == '..' + let l:count = 0 + let l:current = '..' + let l:prefix = '' + while l:current == '..' || l:count == len(l:splitted_relative_path) - 1 + let l:current = l:splitted_relative_path[l:count] + if l:current == '..' + let l:prefix = l:prefix . l:current . '/' + endif + let l:count += 1 + endwhile + return l:prefix . s:getPathWithSkippedRoot(l:shortened_path) + endif + return l:shortened_path +endfunction + +function! s:getPathWithSkippedRoot(path) + return join(split(a:path, '/')[1:len(a:path) -1], '/') +endfunction + +function! s:getShortenedPath(splitted_absolute_path, previous_shortened_path, module_identifier) + let l:shortened_path = a:previous_shortened_path + let l:absolute_path_to_search_in = '/' . join(a:splitted_absolute_path, '/') . '/' + let l:found_module_reference = s:findExportingFileForModule(a:module_identifier, l:shortened_path, l:absolute_path_to_search_in) + let l:current_directory_name = a:splitted_absolute_path[len(a:splitted_absolute_path) -1] + let l:path_separator = '/' + while l:found_module_reference != '' + if l:found_module_reference == 'index' + let l:found_module_reference = '[index]*' + let l:path_separator = '[/]*' + else + let l:path_separator = '/' + endif + let l:shortened_path = l:found_module_reference + let l:found_module_reference = s:findExportingFileForModule(a:module_identifier, l:found_module_reference, l:absolute_path_to_search_in) + if l:found_module_reference != '' + let l:shortened_path = l:found_module_reference + endif + endwhile + return l:current_directory_name . l:path_separator . l:shortened_path +endfunction + +function! s:getBaseUrlImportPath(module_absolute_path) + let [l:tsconfig, l:tsconfig_file_path] = s:getTsconfig(a:module_absolute_path) + + if empty(l:tsconfig) || l:tsconfig_file_path == '' + return '' + endif + + let l:project_root_path = fnamemodify(l:tsconfig_file_path, ':h').'/' + " We assume that baseUrl is a path relative to tsconfig.json path. + let l:base_url_config = has_key(l:tsconfig.compilerOptions, 'baseUrl') ? l:tsconfig.compilerOptions.baseUrl : '.' + let l:base_url_path = simplify(l:project_root_path.l:base_url_config) + + return s:removeTSExtensions(substitute(a:module_absolute_path, l:base_url_path, '', '')) +endfunction + +let s:tsconfig = {} +let s:tsconfig_file_path = '' + +function! s:getTsconfig(module_absolute_path) + if empty(s:tsconfig) + let l:project_info = tsuquyomi#tsClient#tsProjectInfo(a:module_absolute_path, 0) + + if has_key(l:project_info, 'configFileName') + let s:tsconfig_file_path = l:project_info.configFileName + else + echom '[Tsuquyomi] Cannot find project’s tsconfig.json to compute baseUrl import path.' + endif + + let l:json = join(readfile(s:tsconfig_file_path),'') + + try + let s:tsconfig = s:JSON.decode(l:json) + catch + echom '[Tsuquyomi] Cannot parse project’s tsconfig.json. Does it have comments?' + endtry + + endif + + return [s:tsconfig, s:tsconfig_file_path] +endfunction + +function! s:findExportingFileForModule(module, current_module_file, module_directory_path) + execute + \"silent! noautocmd vimgrep /export\\s*\\({.*\\(\\s\\|,\\)" + \. a:module + \."\\(\\s\\|,\\)*.*}\\|\\*\\)\\s\\+from\\s\\+\\(\\'\\|\\\"\\)\\.\\\/" + \. substitute(a:current_module_file, '\/', '\\/', '') + \."[\\/]*\\(\\'\\|\\\"\\)[;]*/j " + \. a:module_directory_path + \. "*.ts" + redir => l:grep_result + silent! clist + redir END + if l:grep_result =~ 'No Errors' + return '' + endif + let l:raw_result = split(l:grep_result, ' ')[2] + let l:raw_result = split(l:raw_result, ':')[0] + let l:raw_result_parts = split(l:raw_result, '/') + let l:extracted_file_name = l:raw_result_parts[len(l:raw_result_parts) -1 ] + let l:extracted_file_name = s:removeTSExtensions(l:extracted_file_name) + return l:extracted_file_name +endfunction + +function! s:comp_alias(alias1, alias2) + return a:alias2.spans[0].end.line - a:alias1.spans[0].end.line +endfunction + +function! tsuquyomi#es6import#createImportPosition(nav_bar_list) + if !len(a:nav_bar_list) + return {} + endif + if len(a:nav_bar_list) == 1 + if a:nav_bar_list[0].kind ==# 'module' + if !len(filter(copy(a:nav_bar_list[0].childItems), 'v:val.kind ==#"alias"')) + let l:start_line = a:nav_bar_list[0].spans[0].start.line - 1 + let l:end_line = l:start_line + else + let l:start_line = a:nav_bar_list[0].spans[0].start.line + let l:end_line = a:nav_bar_list[0].spans[0].end.line + endif + else + let l:start_line = a:nav_bar_list[0].spans[0].start.line - 1 + let l:end_line = l:start_line + endif + elseif len(a:nav_bar_list) > 1 + let l:start_line = a:nav_bar_list[0].spans[0].start.line + let l:end_line = a:nav_bar_list[1].spans[0].start.line - 1 + endif + return { 'start': { 'line': l:start_line }, 'end': { 'line': l:end_line } } +endfunction + +function! tsuquyomi#es6import#getImportDeclarations(fileName, content_list) + let l:nav_bar_list = tsuquyomi#tsClient#tsNavBar(a:fileName) + if !len(l:nav_bar_list) + return [[], {}, 'no_nav_bar'] + endif + let l:position = tsuquyomi#es6import#createImportPosition(l:nav_bar_list) + let l:module_infos = filter(copy(l:nav_bar_list), 'v:val.kind ==# "module"') + if !len(l:module_infos) + return [[], l:position, 'no_module_info'] + endif + let l:result_list = [] + let l:alias_list = filter(l:module_infos[0].childItems, 'v:val.kind ==# "alias"') + let l:end_line = position.end.line + let l:last_module_end_line = 0 + for alias in sort(l:alias_list, "s:comp_alias") + let l:hit = 0 + let [l:has_brace, l:brace] = [0, {}] + let [l:has_from, l:from] = [0, { 'start': {}, 'end': {} }] + let [l:has_module, l:module] = [0, { 'name': '', 'start': {}, 'end': {} }] + let l:line = alias.spans[0].start.line + while !l:hit && l:line <= l:end_line + if !len(a:content_list) + let l:line_str = getline(l:line) + else + let l:line_str = a:content_list[l:line - 1] + endif + let l:brace_end_offset = match(l:line_str, "}") + let l:from_offset = match(l:line_str, 'from') + if l:brace_end_offset + 1 && !l:has_brace && !l:has_from + let l:has_brace = 1 + let l:brace = { + \ 'end': { 'offset': l:brace_end_offset + 1, 'line': l:line } + \ } + endif + if l:from_offset + 1 + let l:has_from = 1 + let l:from = { + \ 'start': { 'offset': l:from_offset + 1, 'line': l:line }, + \ 'end': { 'offset': l:from_offset + 4, 'line': l:line } + \ } + endif + if l:has_from + let l:module_name_sq = matchstr(l:line_str, "\\m'\\zs.*\\ze'") + if l:module_name_sq !=# '' + let l:has_module = 1 + let l:module_name = l:module_name_sq + else + let l:module_name_dq = matchstr(l:line_str, '\m"\zs.*\ze"') + if l:module_name_dq !=# '' + let l:has_module = 1 + let l:module_name = l:module_name_dq + endif + endif + endif + if l:has_module + let [l:hit, l:end_line] = [1, l:line] + let l:module = { + \ 'name': l:module_name, + \ 'start': { 'line': l:line }, + \ 'end': { 'line': l:line }, + \ } + if !l:last_module_end_line + let l:last_module_end_line = l:line + endif + else + let l:line += 1 + endif + endwhile + if l:hit + let l:info = { + \ 'module': l:module, + \ 'has_from': l:has_from, + \ 'from_span': l:from, + \ 'has_brace': l:has_brace, + \ 'brace': l:brace, + \ 'alias_info': alias, + \ 'is_oneliner': alias.spans[0].start.line == l:module.end.line + \ } + call add(l:result_list, l:info) + endif + endfor + if l:last_module_end_line + let l:position.end.line = l:last_module_end_line + endif + return [l:result_list, l:position, ''] +endfunction + +let s:importable_module_list = [] +function! tsuquyomi#es6import#moduleComplete(arg_lead, cmd_line, cursor_pos) + return join(s:importable_module_list, "\n") +endfunction + +function! tsuquyomi#es6import#selectModule() + echohl String + let l:selected_module = input("[Tsuquyomi] You can import from 2 or more modules.\n" . join(s:importable_module_list, "\n") . "\nSelect one: ", '', 'custom,tsuquyomi#es6import#moduleComplete') + echohl none + echo ' ' + if len(filter(copy(s:importable_module_list), 'v:val==#l:selected_module')) + return [l:selected_module, 1] + else + echohl Error + echom '[Tsuquyomi] Invalid module path.' + echohl none + return ['', 0] + endif +endfunction + +function! tsuquyomi#es6import#complete() + if !tsuquyomi#bufManager#isOpened(expand('%:p')) + return + end + call tsuquyomi#flush() + let l:identifier_info = s:get_keyword_under_cursor() + let l:list = tsuquyomi#es6import#createImportBlock(l:identifier_info.text) + if len(l:list) > 1 + let s:importable_module_list = map(copy(l:list), 'v:val.path') + let [l:selected_module, l:code] = tsuquyomi#es6import#selectModule() + if !l:code + echohl Error + echom '[Tsuquyomi] No search result.' + echohl none + return + endif + let l:block = filter(l:list, 'v:val.path==#l:selected_module')[0] + elseif len(l:list) == 1 + let l:block = l:list[0] + else + return + endif + let [l:import_list, l:dec_position, l:reason] = tsuquyomi#es6import#getImportDeclarations(expand('%:p'), []) + let l:module_end_line = has_key(l:dec_position, 'end') ? l:dec_position.end.line : 0 + let l:same_path_import_list = filter(l:import_list, 'v:val.has_brace && v:val.module.name ==# l:block.path') + if len(l:same_path_import_list) && len(filter(copy(l:same_path_import_list), 'v:val.alias_info.text ==# l:block.identifier')) + echohl Error + echom '[Tsuquyomi] '.l:block.identifier.' is already imported.' + echohl none + return + endif + + "Replace search keyword to hit result identifer + let l:line = getline(l:identifier_info.start.line) + let l:new_line = l:block.identifier + if l:identifier_info.start.offset > 1 + let l:new_line = l:line[0:l:identifier_info.start.offset - 2].l:new_line + endif + let l:new_line = l:new_line.l:line[l:identifier_info.end.offset: -1] + call setline(l:identifier_info.start.line, l:new_line) + + if g:tsuquyomi_import_curly_spacing == 0 + let l:curly_spacing = '' + else + let l:curly_spacing = ' ' + end + + "Add import declaration + if !len(l:same_path_import_list) + if g:tsuquyomi_semicolon_import + let l:semicolon = ';' + else + let l:semicolon = '' + endif + if g:tsuquyomi_single_quote_import + let l:expression = "import {".l:curly_spacing.l:block.identifier.l:curly_spacing."} from '".l:block.path."'".l:semicolon + else + let l:expression = 'import {'.l:curly_spacing.l:block.identifier.l:curly_spacing.'} from "'.l:block.path.'"'.l:semicolon + endif + call append(l:module_end_line, l:expression) + else + let l:target_import = l:same_path_import_list[0] + if l:target_import.is_oneliner + let l:line = getline(l:target_import.brace.end.line) + let l:injection_position = target_import.brace.end.offset - 2 - strlen(l:curly_spacing) + let l:expression = l:line[0:l:injection_position].', '.l:block.identifier.l:curly_spacing.l:line[l:target_import.brace.end.offset - 1: -1] + call setline(l:target_import.brace.end.line, l:expression) + else + let l:before_line = getline(l:target_import.brace.end.line - 1) + let l:indent = matchstr(l:before_line, '\m^\s*') + let l:before_has_trailing_comma = matchstr(l:before_line, ',\s*$') + if l:before_has_trailing_comma !=# '' + let l:prev_trailing_comma = '' + let l:new_trailing_comma = ',' + else + let l:prev_trailing_comma = ',' + let l:new_trailing_comma = '' + endif + + call setline(l:target_import.brace.end.line - 1, l:before_line.l:prev_trailing_comma) + call append(l:target_import.brace.end.line - 1, l:indent.l:block.identifier.l:new_trailing_comma) + endif + endif +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/vim/bundle/tsuquyomi/autoload/tsuquyomi/perfLogger.vim b/vim/bundle/tsuquyomi/autoload/tsuquyomi/perfLogger.vim new file mode 100644 index 0000000..cdedb21 --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/tsuquyomi/perfLogger.vim @@ -0,0 +1,32 @@ +"============================================================================ +" FILE: perfLogger.vim +" AUTHOR: Quramy +"============================================================================ + +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 + diff --git a/vim/bundle/tsuquyomi/autoload/tsuquyomi/tsClient.vim b/vim/bundle/tsuquyomi/autoload/tsuquyomi/tsClient.vim new file mode 100644 index 0000000..e3ee1be --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/tsuquyomi/tsClient.vim @@ -0,0 +1,940 @@ +"============================================================================ +" FILE: tsuquyomi.vim +" AUTHOR: Quramy +"============================================================================ + +scriptencoding utf-8 + +let s:save_cpo = &cpo +set cpo&vim + +if !tsuquyomi#config#preconfig() + finish +endif + +let s:script_dir = expand(':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} 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} +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} 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} 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} files List of filename +" PARAM: {int} delay Delay time [msec]. +" PARAM: {list} 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} 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} 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} 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 +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} 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} errorCodes Error codes we want to get the fixes for +" RETURNS: {list} +" 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} 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 diff --git a/vim/bundle/tsuquyomi/autoload/unite/sources/outline/typescript.vim b/vim/bundle/tsuquyomi/autoload/unite/sources/outline/typescript.vim new file mode 100644 index 0000000..017e6da --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/unite/sources/outline/typescript.vim @@ -0,0 +1,66 @@ +"============================================================================ +" FILE: autoload/unite/sources/outline/typescript.vim +" AUTHOR: Quramy +"============================================================================ + +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 + diff --git a/vim/bundle/tsuquyomi/autoload/unite/sources/tsproject.vim b/vim/bundle/tsuquyomi/autoload/unite/sources/tsproject.vim new file mode 100644 index 0000000..763c771 --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/unite/sources/tsproject.vim @@ -0,0 +1,66 @@ +"============================================================================ +" FILE: autoload/unite/sources/tsproject.vim +" AUTHOR: Quramy +"============================================================================ + +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 + diff --git a/vim/bundle/tsuquyomi/autoload/vital.vim b/vim/bundle/tsuquyomi/autoload/vital.vim new file mode 100644 index 0000000..f1ba849 --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/vital.vim @@ -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 diff --git a/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi.vim b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi.vim new file mode 100644 index 0000000..9eba177 --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi.vim @@ -0,0 +1,5 @@ +let s:_plugin_name = expand(':t:r') + +function! vital#{s:_plugin_name}#new() abort + return vital#{s:_plugin_name[1:]}#new() +endfunction diff --git a/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/Data/List.vim b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/Data/List.vim new file mode 100644 index 0000000..285b406 --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/Data/List.vim @@ -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(''), '\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('%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: diff --git a/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/Data/String.vim b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/Data/String.vim new file mode 100644 index 0000000..49a3d18 --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/Data/String.vim @@ -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(''), '\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('%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- s +" plus a terminating " +" That's why {stdin} always ends with 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 to the end of +" the last line. +" That's why add a trailing 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- s +" plus a terminating " +" 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- s +" plus a terminating " +" 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 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 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: diff --git a/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/Prelude.vim b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/Prelude.vim new file mode 100644 index 0000000..513b864 --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/Prelude.vim @@ -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(''), '\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('%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: diff --git a/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/Process.vim b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/Process.vim new file mode 100644 index 0000000..679fbb4 --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/Process.vim @@ -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(''), '\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('%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: diff --git a/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/ProcessManager.vim b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/ProcessManager.vim new file mode 100644 index 0000000..5b4c081 --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/ProcessManager.vim @@ -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(''), '\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('%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: diff --git a/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/System/Filepath.vim b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/System/Filepath.vim new file mode 100644 index 0000000..5965842 --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/System/Filepath.vim @@ -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(''), '\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('%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(':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: diff --git a/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/Web/JSON.vim b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/Web/JSON.vim new file mode 100644 index 0000000..37c1945 --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/vital/_tsuquyomi/Web/JSON.vim @@ -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(''), '\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('%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: diff --git a/vim/bundle/tsuquyomi/autoload/vital/tsuquyomi.vim b/vim/bundle/tsuquyomi/autoload/vital/tsuquyomi.vim new file mode 100644 index 0000000..d428cc2 --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/vital/tsuquyomi.vim @@ -0,0 +1,339 @@ +let s:plugin_name = expand(':t:r') +let s:vital_base_dir = expand(':h') +let s:project_root = expand(':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(''), '\zs\d\+\ze__SID$') + endfunction + let s:_s = '' . 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 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(':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 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("\")), '"[\\x" . printf("%0x", char2nr("\"[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%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('%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 diff --git a/vim/bundle/tsuquyomi/autoload/vital/tsuquyomi.vital b/vim/bundle/tsuquyomi/autoload/vital/tsuquyomi.vital new file mode 100644 index 0000000..fd99e5a --- /dev/null +++ b/vim/bundle/tsuquyomi/autoload/vital/tsuquyomi.vital @@ -0,0 +1,7 @@ +tsuquyomi +bd6fd747ba07d5124619347efa32c2f068e22318 + +Web.JSON +ProcessManager +System.Filepath +Prelude diff --git a/vim/bundle/tsuquyomi/capt_comp.png b/vim/bundle/tsuquyomi/capt_comp.png new file mode 100644 index 0000000..23726f2 Binary files /dev/null and b/vim/bundle/tsuquyomi/capt_comp.png differ diff --git a/vim/bundle/tsuquyomi/doc/tsuquyomi.jax b/vim/bundle/tsuquyomi/doc/tsuquyomi.jax new file mode 100644 index 0000000..e40794f --- /dev/null +++ b/vim/bundle/tsuquyomi/doc/tsuquyomi.jax @@ -0,0 +1,519 @@ +*tsuquyomi* はTypeScript向けのVim plugin です. + +Version: 0.5.1 +Author : Quramy +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* + +ノーマルモードのキーマッピング: + + *(TsuquyomiDefinition)* +(TsuquyomiDefinition) + カーソル直下のシンボルが定義された箇所へ遷移. + |:TsuquyomiDefinition|を参照のこと. + + *(TsuquyomiSplitDefinition)* +(TsuquyomiSplitDefinition) + カーソル直下のシンボルが定義された箇所へ遷移. + |:TsuquyomiSplitDefinition|を参照のこと. + + *(TsuquyomiTypeDefinition)* +(TsuquyomiTypeDefinition) + カーソル直下のシンボルの型定義箇所へ遷移. + |:TsuquyomiTypeDefinition|を参照のこと. + + *(TsuquyomiGoBack)* +(TsuquyomiGoBack) + |:TsuquyomiGoBack|を参照のこと. + + *(TsuquyomiImplementation)* +(TsuquyomiImplementation) + カーソル直下のinerfaceが実装されている場所の一覧を表示. + |:TsuquyomiImplementation|を参照のこと. + + + *(TsuquyomiReferences)* +(TsuquyomiReferences) + カーソル直下のシンボルが参照されている場所の一覧を表示. + |:TsuquyomiReferences| を参照のこと. + + *(TsuquyomiRenameSymbol)* +(TsuquyomiRenameSymbol) + カーソル直下のシンボルを別名に変更する. + |:TsuquyomiRenameSymbol|を参照のこと. + + *(TsuquyomiRenameSymbolC)* +(TsuquyomiRenameSymbolC) + カーソル直下のシンボルをコメントも含めて別名に変更する. + |:TsuquyomiRenameSymbolC|を参照のこと. + + *(TsuquyomiQuickFix)* +(TsuquyomiQuickFix) + カーソル直下でQuickFixが適用可能な場合に、これを適用する. + |:TsuquyomiQuickFix|を参照のこと. + + *(TsuquyomiImport)* +(TsuquyomiImport) + カーソル直下のシンボルに対応するES6 import文を生成する. + |:TsuquyomiImport|を参照のこと. + + *(TsuquyomiSignatureHelp)* +(TsuquyomiSignatureHelp) + カーソル下のメソッド引数のシグネチャを表示. + |:TsuquyomiSignatureHelp|を参照のこと. + +デフォルトキーマップは下記の通り: + +ノーマルモード: +{lhs} {rhs} +-------- ----------------------------- + (TsuquyomiDefinition) +] (TsuquyomiSplitDefinition) + (TsuquyomiGoBack) + (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 t : + \ echo tsuquyomi#hint() +< + 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: diff --git a/vim/bundle/tsuquyomi/doc/tsuquyomi.txt b/vim/bundle/tsuquyomi/doc/tsuquyomi.txt new file mode 100644 index 0000000..a2390f1 --- /dev/null +++ b/vim/bundle/tsuquyomi/doc/tsuquyomi.txt @@ -0,0 +1,511 @@ +*tsuquyomi* is a Vim plugin for TypeScript. + +Version: 0.6.0 +Author : Quramy +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. + + *(TsuquyomiDefinition)* +(TsuquyomiDefinition) + Navigate to the location where the symbol under the cursor is + defined. See |:TsuquyomiDefinition|. + + *(TsuquyomiSplitDefinition)* +(TsuquyomiSplitDefinition) + Split current window in two. Navigate to the location where + the symbol under the cursor is defined. See + |:TsuquyomiSplitDefinition|. + + *(TsuquyomiTypeDefinition)* +(TsuquyomiTypeDefinition) + Navigate to the location where the type of the symbol under the + cursor is defined. See |:TsuquyomiTypeDefinition|. + +(TsuquyomiGoBack) *(TsuquyomiGoBack)* + See |:TsuquyomiGoBack|. + + *(TsuquyomiImplementation)* +(TsuquyomiImplementation) + Show a list of locations where the interface under the cursor is + implemented. See |:TsuquyomiImplementation|. + + *(TsuquyomiReferences)* +(TsuquyomiReferences) + Show a list of locations where the symbol under the cursor is + referenced. See |:TsuquyomiReferences|. + + *(TsuquyomiRenameSymbol)* +(TsuquyomiRenameSymbol) + Rename the identifier under the cursor to a new name. + See |:TsuquyomiRenameSymbol|. + + *(TsuquyomiRenameSymbolC)* +(TsuquyomiRenameSymbolC) + Rename the identifier under the cursor to a new name. + See |:TsuquyomiRenameSymbolC|. + + *(TsuquyomiQuickFix)* +(TsuquyomiQuickFix) + Apply quick fix under the cursor if it's available. + See |:TsuquyomiQuickFix|. + + *(TsuquyomiImport)* +(TsuquyomiImport) + Generate ES6 import declaration whose alias is under + the cursor. See |:TsuquyomiImport|. + + *(TsuquyomiSignatureHelp)* +(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} +-------- ----------------------------- + (TsuquyomiDefinition) +] (TsuquyomiSplitDefinition) + (TsuquyomiSplitDefinition) + (TsuquyomiGoBack) + (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 t : + \ echo tsuquyomi#hint() +< +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: diff --git a/vim/bundle/tsuquyomi/ftdetect/typescript.vim b/vim/bundle/tsuquyomi/ftdetect/typescript.vim new file mode 100644 index 0000000..fd73285 --- /dev/null +++ b/vim/bundle/tsuquyomi/ftdetect/typescript.vim @@ -0,0 +1 @@ +autocmd BufNewFile,BufRead *.ts,*.tsx setfiletype typescript diff --git a/vim/bundle/tsuquyomi/ftplugin/javascript/tsuquyomi.vim b/vim/bundle/tsuquyomi/ftplugin/javascript/tsuquyomi.vim new file mode 100644 index 0000000..e821515 --- /dev/null +++ b/vim/bundle/tsuquyomi/ftplugin/javascript/tsuquyomi.vim @@ -0,0 +1,16 @@ +"============================================================================ +" FILE: ftplugin/javascript.vim +" AUTHOR: Quramy +"============================================================================ + +scriptencoding utf-8 + +if !g:tsuquyomi_javascript_support + finish +endif + +if !tsuquyomi#config#preconfig() + finish +endif + +call tsuquyomi#config#initBuffer({ 'pattern': '*.js,*.jsx' }) diff --git a/vim/bundle/tsuquyomi/ftplugin/typescript/tsuquyomi.vim b/vim/bundle/tsuquyomi/ftplugin/typescript/tsuquyomi.vim new file mode 100644 index 0000000..47b0f9a --- /dev/null +++ b/vim/bundle/tsuquyomi/ftplugin/typescript/tsuquyomi.vim @@ -0,0 +1,12 @@ +"============================================================================ +" FILE: ftplugin/typescript.vim +" AUTHOR: Quramy +"============================================================================ + +scriptencoding utf-8 + +if !tsuquyomi#config#preconfig() + finish +endif + +call tsuquyomi#config#initBuffer({ 'pattern': '*.ts,*.tsx' }) diff --git a/vim/bundle/tsuquyomi/package.json b/vim/bundle/tsuquyomi/package.json new file mode 100644 index 0000000..459a12a --- /dev/null +++ b/vim/bundle/tsuquyomi/package.json @@ -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" +} diff --git a/vim/bundle/tsuquyomi/plugin/tsuquyomi.vim b/vim/bundle/tsuquyomi/plugin/tsuquyomi.vim new file mode 100644 index 0000000..473d96d --- /dev/null +++ b/vim/bundle/tsuquyomi/plugin/tsuquyomi.vim @@ -0,0 +1,94 @@ +"============================================================================ +" FILE: tsuquyomi.vim +" AUTHOR: Quramy +"============================================================================ + +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 diff --git a/vim/bundle/tsuquyomi/run-one-test.sh b/vim/bundle/tsuquyomi/run-one-test.sh new file mode 100755 index 0000000..d62d6be --- /dev/null +++ b/vim/bundle/tsuquyomi/run-one-test.sh @@ -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())' diff --git a/vim/bundle/tsuquyomi/runtest-all-ts.sh b/vim/bundle/tsuquyomi/runtest-all-ts.sh new file mode 100755 index 0000000..aad59a6 --- /dev/null +++ b/vim/bundle/tsuquyomi/runtest-all-ts.sh @@ -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 diff --git a/vim/bundle/tsuquyomi/runtest.sh b/vim/bundle/tsuquyomi/runtest.sh new file mode 100755 index 0000000..747dcec --- /dev/null +++ b/vim/bundle/tsuquyomi/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 + diff --git a/vim/bundle/tsuquyomi/screen.gif b/vim/bundle/tsuquyomi/screen.gif new file mode 100644 index 0000000..9609cda Binary files /dev/null and b/vim/bundle/tsuquyomi/screen.gif differ diff --git a/vim/bundle/tsuquyomi/syntax_checkers/typescript/tsuquyomi.vim b/vim/bundle/tsuquyomi/syntax_checkers/typescript/tsuquyomi.vim new file mode 100644 index 0000000..11e0eb5 --- /dev/null +++ b/vim/bundle/tsuquyomi/syntax_checkers/typescript/tsuquyomi.vim @@ -0,0 +1,36 @@ +"============================================================================ +" FILE: syntax_checkers/typescript/tsuquyomi.vim +" AUTHOR: Quramy +"============================================================================ + +" 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 diff --git a/vim/bundle/tsuquyomi/test/.vimrc b/vim/bundle/tsuquyomi/test/.vimrc new file mode 100644 index 0000000..7fccc74 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/.vimrc @@ -0,0 +1,27 @@ + +set nocompatible +let s:basedir = expand(':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 + + diff --git a/vim/bundle/tsuquyomi/test/_runner b/vim/bundle/tsuquyomi/test/_runner new file mode 100644 index 0000000..6aa78c4 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/_runner @@ -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 diff --git a/vim/bundle/tsuquyomi/test/es6import/vest/checkExternalModule.spec.vim b/vim/bundle/tsuquyomi/test/es6import/vest/checkExternalModule.spec.vim new file mode 100644 index 0000000..8ac51ea --- /dev/null +++ b/vim/bundle/tsuquyomi/test/es6import/vest/checkExternalModule.spec.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 diff --git a/vim/bundle/tsuquyomi/test/es6import/vest/getImportDeclarations.spec.vim b/vim/bundle/tsuquyomi/test/es6import/vest/getImportDeclarations.spec.vim new file mode 100644 index 0000000..7338ead --- /dev/null +++ b/vim/bundle/tsuquyomi/test/es6import/vest/getImportDeclarations.spec.vim @@ -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 diff --git a/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/decAndFunction.ts b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/decAndFunction.ts new file mode 100644 index 0000000..86f6186 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/decAndFunction.ts @@ -0,0 +1,2 @@ +import {someVar} from './some-module'; +document.getElementById('myApp'); diff --git a/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/decAndOther.ts b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/decAndOther.ts new file mode 100644 index 0000000..7b17c00 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/decAndOther.ts @@ -0,0 +1,4 @@ +import { someVar } from './some-module'; +class Hoge { + private hoge: string; +} diff --git a/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/empty.ts b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/empty.ts new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/empty.ts @@ -0,0 +1 @@ + diff --git a/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/explictAlias.ts b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/explictAlias.ts new file mode 100644 index 0000000..d3188e8 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/explictAlias.ts @@ -0,0 +1,2 @@ +import { someVar as _var } from './some-module'; +import * as $var from './some-module'; diff --git a/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/multiAlias.ts b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/multiAlias.ts new file mode 100644 index 0000000..c5e30ab --- /dev/null +++ b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/multiAlias.ts @@ -0,0 +1,4 @@ +import { + someVar, + altVar +} from './some-module'; diff --git a/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/multiDec.ts b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/multiDec.ts new file mode 100644 index 0000000..51f796a --- /dev/null +++ b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/multiDec.ts @@ -0,0 +1,2 @@ +import { someVar } from './some-module'; +import { altVar } from './some-module'; diff --git a/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/multiline.ts b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/multiline.ts new file mode 100644 index 0000000..346034d --- /dev/null +++ b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/multiline.ts @@ -0,0 +1,7 @@ +import { + someVar + } + +from + +'./some-module'; diff --git a/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/noDec.ts b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/noDec.ts new file mode 100644 index 0000000..f876f51 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/noDec.ts @@ -0,0 +1,5 @@ +/// + +"use strict"; +class TestClass { +} diff --git a/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/simple.ts b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/simple.ts new file mode 100644 index 0000000..393fc71 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/simple.ts @@ -0,0 +1 @@ +import { someVar } from './some-module'; diff --git a/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/some-module.ts b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/some-module.ts new file mode 100644 index 0000000..11bbaf5 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/some-module.ts @@ -0,0 +1,2 @@ +export var someVar: any = null; +export var altVar: any = null; diff --git a/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/tsconfig.json b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/tsconfig.json new file mode 100644 index 0000000..b1a3a00 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/es6import/vest/resources/importDecPatterns/tsconfig.json @@ -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" + ] +} diff --git a/vim/bundle/tsuquyomi/test/es6import/vest/resources/variousModules.d.ts b/vim/bundle/tsuquyomi/test/es6import/vest/resources/variousModules.d.ts new file mode 100644 index 0000000..dd9ec4b --- /dev/null +++ b/vim/bundle/tsuquyomi/test/es6import/vest/resources/variousModules.d.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" { +} diff --git a/vim/bundle/tsuquyomi/test/package.json b/vim/bundle/tsuquyomi/test/package.json new file mode 100644 index 0000000..904bc21 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/package.json @@ -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" + } +} diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/resources/SimpleModule.ts b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/SimpleModule.ts new file mode 100644 index 0000000..c28a36a --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/SimpleModule.ts @@ -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; + } +} diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/resources/SimpleModule_writing.ts b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/SimpleModule_writing.ts new file mode 100644 index 0000000..1cd373d --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/SimpleModule_writing.ts @@ -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; + } +} diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/resources/codeFixTest.ts b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/codeFixTest.ts new file mode 100644 index 0000000..cbb97b2 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/codeFixTest.ts @@ -0,0 +1,8 @@ +class Hoge { + constructor() { } +} + +class Foo extends Hoge { + constructor() { + } +} diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/resources/definitionTest.ts b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/definitionTest.ts new file mode 100644 index 0000000..6d04b00 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/definitionTest.ts @@ -0,0 +1,3 @@ +class App { } +var app = new App(); + diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/resources/referencesTestA.ts b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/referencesTestA.ts new file mode 100644 index 0000000..cbd7b0c --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/referencesTestA.ts @@ -0,0 +1,7 @@ +module ReferenceTest { + export class SomeClass { + } + + export class OtherClass extends SomeClass{ + } +} diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/resources/referencesTestB.ts b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/referencesTestB.ts new file mode 100644 index 0000000..94d6a35 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/referencesTestB.ts @@ -0,0 +1,6 @@ +/// + +module ReferenceTest { + export class AnotherClass extends SomeClass{ + } +} diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/resources/renameTest.ts b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/renameTest.ts new file mode 100644 index 0000000..15c77e9 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/renameTest.ts @@ -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 `); + } + +} diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/resources/samplePrjs/errorPrj/main.ts b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/samplePrjs/errorPrj/main.ts new file mode 100644 index 0000000..b657ed2 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/samplePrjs/errorPrj/main.ts @@ -0,0 +1,5 @@ +interface Hoge { + id: any; + // syntaxerror + foo: { +} diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/resources/samplePrjs/errorPrj/sub.ts b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/samplePrjs/errorPrj/sub.ts new file mode 100644 index 0000000..6c9e568 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/samplePrjs/errorPrj/sub.ts @@ -0,0 +1 @@ +class Foo extends InvalidBaseClass {} diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/resources/samplePrjs/errorPrj/tsconfig.json b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/samplePrjs/errorPrj/tsconfig.json new file mode 100644 index 0000000..de784c3 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/samplePrjs/errorPrj/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "noImplicitAny": true, + "module": "commonjs" + }, + "files": ["main.ts", "sub.ts"] +} diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/resources/samplePrjs/prj001/main.ts b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/samplePrjs/prj001/main.ts new file mode 100644 index 0000000..07f084a --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/samplePrjs/prj001/main.ts @@ -0,0 +1,5 @@ +module main { + interface IBase {} + + class BaseClazz implements IBase {} +} diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/resources/samplePrjs/prj001/tsconfig.json b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/samplePrjs/prj001/tsconfig.json new file mode 100644 index 0000000..a5d3029 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/samplePrjs/prj001/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "noImplicitAny": true + }, + "files": ["main.ts"] +} diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/resources/signatureHelpTest_overload.ts b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/signatureHelpTest_overload.ts new file mode 100644 index 0000000..2c711c8 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/signatureHelpTest_overload.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, +} diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/resources/signatureHelpTest_writing.ts b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/signatureHelpTest_writing.ts new file mode 100644 index 0000000..83c8e07 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/resources/signatureHelpTest_writing.ts @@ -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( +} diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/sendCommand.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/sendCommand.spec.vim new file mode 100644 index 0000000..e0e2295 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/sendCommand.spec.vim @@ -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 diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/sendRequest.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/sendRequest.spec.vim new file mode 100644 index 0000000..7d91017 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/sendRequest.spec.vim @@ -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 diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/startTss.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/startTss.spec.vim new file mode 100644 index 0000000..baae6ba --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/startTss.spec.vim @@ -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 diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/tsCompletionEntryDetails.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/tsCompletionEntryDetails.spec.vim new file mode 100644 index 0000000..051ace8 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/tsCompletionEntryDetails.spec.vim @@ -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 diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/tsCompletions.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/tsCompletions.spec.vim new file mode 100644 index 0000000..2830502 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/tsCompletions.spec.vim @@ -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 diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/tsDefinition.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/tsDefinition.spec.vim new file mode 100644 index 0000000..cda9ec4 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/tsDefinition.spec.vim @@ -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 diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/tsGetCodeFixes.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/tsGetCodeFixes.vim new file mode 100644 index 0000000..75f2f87 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/tsGetCodeFixes.vim @@ -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 diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/tsGetSupportedCodeFixes.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/tsGetSupportedCodeFixes.vim new file mode 100644 index 0000000..b1c2aa5 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/tsGetSupportedCodeFixes.vim @@ -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 + diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/tsGeterr.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/tsGeterr.spec.vim new file mode 100644 index 0000000..f3027d5 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/tsGeterr.spec.vim @@ -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 + diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/tsGeterrForProject.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/tsGeterrForProject.spec.vim new file mode 100644 index 0000000..86d01ca --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/tsGeterrForProject.spec.vim @@ -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 + diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/tsNavBar.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/tsNavBar.spec.vim new file mode 100644 index 0000000..650641d --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/tsNavBar.spec.vim @@ -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 diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/tsNavto.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/tsNavto.spec.vim new file mode 100644 index 0000000..c13cd5e --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/tsNavto.spec.vim @@ -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 diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/tsProjectInfo.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/tsProjectInfo.spec.vim new file mode 100644 index 0000000..9d494bc --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/tsProjectInfo.spec.vim @@ -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 diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/tsQuickinfo.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/tsQuickinfo.spec.vim new file mode 100644 index 0000000..ffd5a55 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/tsQuickinfo.spec.vim @@ -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 diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/tsReferences.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/tsReferences.spec.vim new file mode 100644 index 0000000..a2d4da9 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/tsReferences.spec.vim @@ -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 + diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/tsReload.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/tsReload.spec.vim new file mode 100644 index 0000000..5849c65 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/tsReload.spec.vim @@ -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 + diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/tsRename.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/tsRename.spec.vim new file mode 100644 index 0000000..e498e2a --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/tsRename.spec.vim @@ -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 diff --git a/vim/bundle/tsuquyomi/test/tsClient/vest/tsSignatureHelp.spec.vim b/vim/bundle/tsuquyomi/test/tsClient/vest/tsSignatureHelp.spec.vim new file mode 100644 index 0000000..7ab542b --- /dev/null +++ b/vim/bundle/tsuquyomi/test/tsClient/vest/tsSignatureHelp.spec.vim @@ -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 + diff --git a/vim/bundle/tsuquyomi/test/yarn.lock b/vim/bundle/tsuquyomi/test/yarn.lock new file mode 100644 index 0000000..77c7927 --- /dev/null +++ b/vim/bundle/tsuquyomi/test/yarn.lock @@ -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== diff --git a/vim/bundle/tsuquyomi/yarn.lock b/vim/bundle/tsuquyomi/yarn.lock new file mode 100644 index 0000000..e9d1036 --- /dev/null +++ b/vim/bundle/tsuquyomi/yarn.lock @@ -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"