├── .github ├── ISSUE_TEMPLATE.md └── ISSUE_TEMPLATE │ ├── bug.md │ ├── feature_request.md │ └── question.md ├── .gitignore ├── README.md ├── after └── plugin │ └── nvim_typescript.vim ├── autoload ├── airline │ └── extensions │ │ └── nvim_typescript.vim ├── cm │ └── sources │ │ └── typescript.vim ├── health │ └── nvim_typescript.vim ├── ncm2 │ └── nvim_typescript.vim └── nvim_typescript.vim ├── deoplete-tss.gif ├── doc └── nvim-typescript.txt ├── install.sh ├── ncm2-plugin └── nvim_typescript.vim ├── plugin └── nvim_typescript.vim ├── rplugin ├── node │ └── nvim_typescript │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── rollup.config.js │ │ ├── src │ │ ├── client.ts │ │ ├── codeActions.ts │ │ ├── diagnostic.ts │ │ ├── floatingWindow.ts │ │ ├── index.ts │ │ ├── types.d.ts │ │ └── utils.ts │ │ ├── tsconfig.json │ │ └── tslint.json └── python3 │ ├── denite │ └── source │ │ ├── TSDocumentSymbol.py │ │ ├── TSProjectFiles.py │ │ └── TSWorkspaceSymbol.py │ └── deoplete │ └── sources │ └── typescript.py └── syntax └── nvimtypescript.vim /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Warning: I will close the issue without the minimal vimrc and the reproduce ways.** 2 | 3 | # Problems summary 4 | 5 | 6 | ## Expected 7 | 8 | 9 | ## Environment Information 10 | 11 | * terminal: 12 | * vim/nvim version: 13 | 14 | 15 | 16 | ## Provide a minimal vim rc with less than 50 lines (Required!) 17 | 18 | ```vim 19 | " Your minimal init.vim 20 | set runtimepath+=~/path/to/nvim-typescript 21 | ``` 22 | 23 | 24 | ## The reproduce ways from neovim starting 25 | 26 | 1. foo 27 | 2. bar 28 | 3. baz 29 | 30 | ## Example project repo? 31 | 32 | Most common errors are related to a project not being set up correctly. 33 | Please provid a minimal project example to reduce back-and-forth time. 34 | This makes debugging MUCH easier 35 | 36 | 37 | ## Screen shot (if possible) 38 | 39 | 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | **Warning: I will close the issue without the minimal vimrc and the reproduce ways.** 2 | 3 | # Problems summary 4 | 5 | 6 | ## Expected 7 | 8 | 9 | ## Environment Information 10 | 11 | * terminal: 12 | * vim/nvim version: 13 | 14 | 15 | 16 | ## Provide a minimal vim rc with less than 50 lines (Required!) 17 | 18 | ```vim 19 | " Your minimal init.vim 20 | set runtimepath+=~/path/to/nvim-typescript 21 | ``` 22 | 23 | 24 | ## The reproduce ways from neovim starting 25 | 26 | 1. foo 27 | 2. bar 28 | 3. baz 29 | 30 | ## Example project repo? 31 | 32 | Most common errors are related to a project not being set up correctly. 33 | Please provid a minimal project example to reduce back-and-forth time. 34 | This makes debugging MUCH easier 35 | 36 | 37 | ## Screen shot (if possible) 38 | 39 | 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | ### What feature would you like to see? 2 | 3 | ### Pre-existing example in the wild? (From VSCode/WebStorm) 4 | 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | .vscode/ 6 | .idea/ 7 | # C extensions 8 | *.so 9 | tags 10 | lib 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | .DS_Store 24 | var/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | pythonx/ 29 | node_modules/ 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | .hypothesis/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | 58 | # Sphinx documentation 59 | docs/_build/ 60 | 61 | # PyBuilder 62 | target/ 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nvim-Typescript 2 | 3 | ## DEPRECATED 4 | 5 | The time has finally come. Since Neovim 0.5 is out, and LSP is officially supported, I'd suggest you migrate over to it. I have been using it instead of this plugin for a while and it solves many pain points that I've not been able to. This will stay around for older neovim release, but everyone should upgrade. Thanks for the support and for using this plugin 🚀 6 | 7 | 8 | 9 | nvim language service plugin for typescript 10 | 11 | ![](https://github.com/mhartington/nvim-typescript/blob/master/deoplete-tss.gif) 12 | 13 | 14 | ## Installation 15 | 16 | First make sure you have Neovim 0.3.8 or higher. 17 | This includes the node-host that is required for this plugin. 18 | 19 | You will need a global install of the neovim client as well. 20 | This will make sure that neovim and node can communicate. 21 | 22 | 23 | ```bash 24 | npm install -g neovim 25 | ``` 26 | 27 | After installing the neovim client, you will have to run `:UpdateRemotePlugins`. 28 | 29 | You might want to also have typescript install globally. 30 | By default, this plugin will look in your `node_modules` folder first for typescript, but if that does not exist, it will use the global install. 31 | 32 | ```bash 33 | npm -g install typescript 34 | ``` 35 | 36 | Then add the following plugins. This example shows [Dein.vim](https://github.com/Shougo/dein.vim) and [Plug.vim](https://github.com/junegunn/vim-plug), but any plugin manager will work. 37 | 38 | ```viml 39 | " Dein 40 | # REQUIRED: Add a syntax file. YATS is the best 41 | call dein#add('HerringtonDarkholme/yats.vim') 42 | call dein#add('mhartington/nvim-typescript', {'build': './install.sh'}) 43 | " For async completion 44 | call dein#add('Shougo/deoplete.nvim') 45 | " For Denite features 46 | call dein#add('Shougo/denite.nvim') 47 | 48 | 49 | " Vim-Plug 50 | # REQUIRED: Add a syntax file. YATS is the best 51 | Plug 'HerringtonDarkholme/yats.vim' 52 | Plug 'mhartington/nvim-typescript', {'do': './install.sh'} 53 | " For async completion 54 | Plug 'Shougo/deoplete.nvim' 55 | " For Denite features 56 | Plug 'Shougo/denite.nvim' 57 | 58 | 59 | " Enable deoplete at startup 60 | 61 | let g:deoplete#enable_at_startup = 1 62 | ``` 63 | 64 | If errors occur after installing, make sure to run `./install.sh` in the plugin 65 | directory. And try to run `:UpdateRemotePlugins` if you haven't already. 66 | 67 | ## Limitation 68 | 69 | If no completion is happening, please be sure to have a Typescript syntax file in your RTP. Older versions of Neovim do not include a default syntax for Typescript, so be sure to include one. A popular syntax file for Typescript is [yats.vim](https://github.com/HerringtonDarkholme/yats.vim). As of v0.4.3, Neovim includes a default Typescript syntax file that is based off yats. Running nvim-typescript with no syntax file could lead to unexpected behavior. 70 | 71 | ## Open Open Source, or how to make this everyone's code 72 | 73 | If you happened to build something and would love to make a PR, I would be more than happy to add contributors. 74 | If something you do add happens to get merged (most likely it will :grin: ) you'll get a collaborator request. This has worked out very well in the Node community and I want it to happen here. This is as much my code as it is your code. 75 | 76 | See: 77 | - [this site](https://openopensource.github.io/) 78 | - [this talk](https://youtu.be/wIUkWpg9FDY?t=5m10s) 79 | 80 | ## Debugging 81 | 82 | There are a few things you'll have to modify in your nvim config in order to be able to effectively work on this plugin: 83 | 84 | ```viml 85 | call dein#local('~/GitHub', {},['nvim-typescript']) 86 | let $NVIM_NODE_LOG_FILE='nvim-node.log' 87 | let $NVIM_NODE_LOG_LEVEL='warn' 88 | 89 | ``` 90 | This plug will try to log most things to warn as the node-client logs a lot of verbose output to debug/info. 91 | You will now be able to `tail -f /PATH_TO/nvim-node.log`, and see debug output appear. 92 | 93 | 94 | ## TODOS 95 | 96 | If there's a feature that you would like to see, feel free to open an issue or send a PR. 97 | 98 | 99 | Like this plugin? Buy me a coffee on [KoFI](http://ko-fi.com/mhartington) 100 | -------------------------------------------------------------------------------- /after/plugin/nvim_typescript.vim: -------------------------------------------------------------------------------- 1 | " Don't do anything if FZF is not installed/loaded 2 | if !exists('g:loaded_fzf') 3 | finish 4 | endif 5 | 6 | augroup nvim-typescript "{{{ 7 | function! s:OpenResult(line) 8 | let fileAndLine = split(a:line, ':') 9 | execute 'edit' l:fileAndLine[1] 10 | execute l:fileAndLine[2] 11 | endfunction 12 | 13 | function! s:FormatResults(query) 14 | let symbols = TSGetWorkspaceSymbolsFunc(a:query, expand("%:.")) 15 | let formattedResults = [] 16 | for item in (l:symbols) 17 | if exists('g:loaded_webdevicons') 18 | let icon = WebDevIconsGetFileTypeSymbol(item['filename'], isdirectory(item['filename'])) 19 | else 20 | let icon = '' 21 | endif 22 | call add( 23 | \ l:formattedResults, 24 | \ icon . ' : ' . fnamemodify(item['filename'], ":.") . ' : ' . item['lnum'] . ' : ' . item['text'] 25 | \) 26 | endfor 27 | return l:formattedResults 28 | endfunction 29 | 30 | 31 | function! s:SearchForSymbolsWithPattern(pattern) 32 | let resultsGetter = s:FormatResults(a:pattern) 33 | try 34 | call fzf#run({ 35 | \ 'source': resultsGetter, 36 | \ 'down': '40%', 37 | \ 'sink': function('s:OpenResult'), 38 | \ 'options': '--ansi --color=16'}) 39 | catch 40 | echohl WarningMsg 41 | echom v:exception 42 | echohl None 43 | endtry 44 | endfunction 45 | 46 | command! -nargs=1 TSSearchFZF call s:SearchForSymbolsWithPattern() 47 | augroup end "}}} 48 | -------------------------------------------------------------------------------- /autoload/airline/extensions/nvim_typescript.vim: -------------------------------------------------------------------------------- 1 | let s:error_symbol = get(g:, 'airline#extensions#nvim_typescript#error_symbol', 'E:') 2 | let s:warning_symbol = get(g:, 'airline#extensions#nvim_typescript#warning_symbol', 'W:') 3 | 4 | function! airline#extensions#nvim_typescript#get_warning() "{{{ 5 | return airline#extensions#nvim_typescript#get('warning') 6 | endfunction "}}} 7 | 8 | function! airline#extensions#nvim_typescript#get_error() "{{{ 9 | return airline#extensions#nvim_typescript#get('error') 10 | endfunction "}}} 11 | 12 | function! airline#extensions#nvim_typescript#get(type) "{{{ 13 | let is_err = (a:type is# 'error') 14 | let info = get(b:, 'nvim_typescript_diagnostic_info', []) 15 | if empty(info) 16 | return '' 17 | else 18 | let formatted = filter(copy(info),"v:val['category'] == a:type") 19 | if empty(formatted) | return '' | endif 20 | return (is_err ? s:error_symbol : s:warning_symbol).len(formatted) 21 | endif 22 | endfunction "}}} 23 | 24 | function! airline#extensions#nvim_typescript#init(ext) "{{{ 25 | call airline#parts#define_raw('nvim_typescript_error_count', '%{airline#extensions#nvim_typescript#get_warning}') 26 | call airline#parts#define_raw('nvim_typescript_warning_count', '%{airline#extensions#nvim_typescript#get_error}') 27 | 28 | call a:ext.add_statusline_func('airline#extensions#nvim_typescript#apply') 29 | endfunction "}}} 30 | function! airline#extensions#nvim_typescript#apply(...) "{{{ 31 | if get(g:, 'nvim_typescript#diagnostics_enable', 1) 32 | \ && &filetype == "typescript" 33 | \ || &filetype == "typescript.tsx" 34 | let w:airline_section_warning = get(w:, 'airline_section_warning', g:airline_section_warning) 35 | let w:airline_section_warning .= '%{airline#extensions#nvim_typescript#get_warning()}' 36 | 37 | let w:airline_section_error = get(w:, 'airline_section_error', g:airline_section_error) 38 | let w:airline_section_error .= '%{airline#extensions#nvim_typescript#get_error()}' 39 | endif 40 | endfunction "}}} 41 | -------------------------------------------------------------------------------- /autoload/cm/sources/typescript.vim: -------------------------------------------------------------------------------- 1 | " nvim-completion-manager source 2 | func! cm#sources#typescript#register() 3 | let scopes = ['typescript', 'tsx', 'typescript.tsx'] 4 | if g:nvim_typescript#javascript_support 5 | call extend(scopes, ['javascript', 'jsx', 'javascript.jsx']) 6 | endif 7 | if g:nvim_typescript#vue_support 8 | call insert(scopes, 'vue') 9 | endif 10 | " the omnifunc pattern is PCRE 11 | call cm#register_source({'name' : 'typescript', 12 | \ 'priority': 9, 13 | \ 'scopes': scopes, 14 | \ 'abbreviation': g:nvim_typescript#completion_mark, 15 | \ 'cm_refresh_patterns':['\.', '::'], 16 | \ 'cm_refresh': 'cm#sources#typescript#refresh', 17 | \ }) 18 | 19 | endfunc 20 | 21 | 22 | func! cm#sources#typescript#refresh(info, ctx) 23 | call TSCmRefresh(a:info, a:ctx) 24 | endfunc 25 | 26 | -------------------------------------------------------------------------------- /autoload/health/nvim_typescript.vim: -------------------------------------------------------------------------------- 1 | let s:local_tss = 0 2 | 3 | function! s:check_node() abort "{{{ 4 | call health#report_start('Find Node') 5 | if executable('node') 6 | call health#report_ok('node is in $PATH') 7 | else 8 | call health#report_error('node is not in $PATH', 9 | \ 'Typescript requires node') 10 | endif 11 | endfunction "}}} 12 | 13 | function! s:check_local_tsserver() abort "{{{ 14 | call health#report_start('Find Local Typescript') 15 | let l:localServer = getcwd().'/node_modules/.bin/tsserver' 16 | if executable(l:localServer) 17 | let s:local_tss = 1 18 | call health#report_ok('local tsserver is found') 19 | else 20 | call health#report_warn('No local server found, using global', 21 | \"Install typescript locally for more accurate tooling\n". 22 | \'$ npm install typescript@latest --save-dev') 23 | endif 24 | endfunction "}}} 25 | 26 | function! s:check_global_tsserver() abort "{{{ 27 | call health#report_start('Find Global Typescript') 28 | if executable('tsserver') 29 | call health#report_ok('global tsserver is found') 30 | elseif s:local_tss 31 | call health#report_ok('No global server found but local server found') 32 | else 33 | call health#report_error('No global server found and no local server', 34 | \"Install typescript globally or locally\n". 35 | \"$ npm install -g typescript\n". 36 | \'$ npm install typescript@latest --save-dev') 37 | endif 38 | endfunction "}}} 39 | 40 | function! s:check_required_node_for_nvim_typescript() abort "{{{ 41 | call health#report_start('Check for node bindings') 42 | if has('nvim-0.3.8') 43 | call health#report_ok('node bindings found') 44 | else 45 | call health#report_error('node bindings were not found', [ 46 | \ 'Please update to a newer version of neovim.', 47 | \ ]) 48 | endif 49 | endfunction "}}} 50 | 51 | function! health#nvim_typescript#check() abort 52 | call s:check_node() 53 | call s:check_local_tsserver() 54 | call s:check_global_tsserver() 55 | call s:check_required_node_for_nvim_typescript() 56 | endfunction 57 | 58 | 59 | -------------------------------------------------------------------------------- /autoload/ncm2/nvim_typescript.vim: -------------------------------------------------------------------------------- 1 | " nvim-completion-manager-2 source 2 | func! ncm2#nvim_typescript#init() 3 | let scope = ['typescript', 'tsx', 'typescript.tsx', 'typescriptreact'] 4 | if g:nvim_typescript#javascript_support 5 | call extend(scope, ['javascript', 'jsx', 'javascript.jsx']) 6 | endif 7 | if g:nvim_typescript#vue_support 8 | call insert(scope, 'vue') 9 | endif 10 | " the omnifunc pattern is PCRE 11 | call ncm2#register_source({'name' : 'typescript', 12 | \ 'priority': 9, 13 | \ 'scope': scope, 14 | \ 'mark': g:nvim_typescript#completion_mark, 15 | \ 'complete_pattern':['\.', '::'], 16 | \ 'on_complete': 'ncm2#nvim_typescript#on_complete', 17 | \ }) 18 | endfunc 19 | 20 | func! ncm2#nvim_typescript#on_complete(ctx) 21 | call TSNcm2OnComplete(a:ctx) 22 | endfunc 23 | 24 | -------------------------------------------------------------------------------- /autoload/nvim_typescript.vim: -------------------------------------------------------------------------------- 1 | function! nvim_typescript#DefaultKeyMap() 2 | if get(g:, 'nvim_typescript#default_mappings', 1) 3 | execute 'nnoremap K' ':TSDoc' 4 | execute 'nnoremap tdp' ':TSDefPreview' 5 | execute 'nnoremap ' ':TSTypeDef' 6 | endif 7 | endfunction 8 | -------------------------------------------------------------------------------- /deoplete-tss.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhartington/nvim-typescript/44f3db3331632003ed95d963b0753ef94abc8b90/deoplete-tss.gif -------------------------------------------------------------------------------- /doc/nvim-typescript.txt: -------------------------------------------------------------------------------- 1 | *nvim-typescript.txt* Typescript development plugin for neovim 2 | 3 | *nvim-typescript* 4 | 5 | 6 | CONTENTS *typescript-contents* 7 | 8 | 1. Intro........................................|typescript-intro| 9 | 2. Install......................................|typescript-install| 10 | 3. Commands.....................................|typescript-commands| 11 | 4. Variables....................................|typescript-variables| 12 | 5. Mappings.....................................|typescript-mappings| 13 | 7. Functions....................................|typescript-functions| 14 | 8. Troubleshooting..............................|typescript-troubleshooting| 15 | 16 | =============================================================================== 17 | INTRO *typescript-intro* 18 | 19 | Typescript support for Vim. Nvim-typescript provide standard IDE-like features 20 | like auto-completion, viewing of documentation, type-signature, go to 21 | definition, and reference finder. 22 | 23 | =============================================================================== 24 | INSTALL *typescript-install* 25 | 26 | To install, use your typical plugin manager and add a reference in your 27 | init.vim. If you'd like completion support, install Deoplete.nvim as well. 28 | 29 | * https://github.com/junegunn/vim-plug > 30 | 31 | Plug 'mhartington/nvim-typescript', { 'do': './install.sh' } 32 | " For async completion 33 | Plug 'Shougo/deoplete.nvim' 34 | < 35 | 36 | * https://github.com/Shougo/dein.vim > 37 | 38 | call dein#add('mhartington/nvim-typescript', {'build': './install'} 39 | " For async completion 40 | call dein#add('Shougo/deoplete.nvim') 41 | 42 | < 43 | You also need the node-client for neovim installed globally. > 44 | 45 | npm install -g neovim 46 | 47 | < 48 | 49 | In you install Deoplete as well, make sure you also have the python3 bindings 50 | to Neovim. > 51 | 52 | pip3 install pynvim 53 | 54 | 55 | Then run `UpdateRemotePlugins`. 56 | 57 | 58 | =============================================================================== 59 | COMMANDS *typescript-commands* 60 | 61 | *:TSDef* 62 | :TSDef 63 | 64 | Opens the definition file for symbol under the cursor. 65 | 66 | *:TSDefPreview* 67 | :TSDefPreview 68 | 69 | Open the definition file for the symbol under the cursor but in a preview 70 | window 71 | 72 | *:TSRefs* 73 | :TSRefs 74 | 75 | Gathers all reference of the symbol under the cursor and displays them in 76 | a quick-fix window. 77 | 78 | *:TSDoc* 79 | :TSDoc 80 | 81 | Prints the documentation for the symbol under the cursor and displays it in 82 | a split below. 83 | 84 | *:TSStart* 85 | :TSStart 86 | 87 | Starts the typescript client. This is called automatically. 88 | 89 | *:TSStop* 90 | :TSStop 91 | 92 | Stops the typescript client 93 | 94 | *:TSRestart* 95 | :TSRestart 96 | 97 | Restarts the client. This is useful if changes to the `tsconfig.json` are 98 | made during the current nvim session. 99 | 100 | *:TSType* 101 | :TSType 102 | 103 | Prints the type signature for the symbol under the cursor. 104 | *:TSRename* 105 | :TSRename [word] 106 | 107 | Renames the symbol currently under the cursor. Pass a new name as an arg, 108 | if no arg is passed, user will be prompted. 109 | 110 | 111 | *:TSGetCodeFix* 112 | :TSGetCodeFix 113 | 114 | Gets a fix for an error under the cursor. 115 | 116 | *:TSTypeDef* 117 | :TSTypeDef 118 | 119 | Goes to the file where the type for the symbol is defined. 120 | 121 | *:TSEditConfig* 122 | :TSEditConfig 123 | 124 | Opens the config file for the current file. 125 | 126 | *:TSOrganizeImports* 127 | :TSOrganizeImports 128 | 129 | Organizes imports for the current file. 130 | 131 | *TSGetDiagnostics* 132 | :TSGetDiagnostics 133 | 134 | Get any syntax or semantic errors for the current file. 135 | 136 | =============================================================================== 137 | VARIABLES *typescript-variables* 138 | *g:nvim_typescript#server_path* 139 | 140 | g:nvim_typescript#server_path 141 | Values: string 142 | Default: ''./node_modules/.bin/tsserver' 143 | 144 | Set the path of the tsserver. By default, this will be your projects local 145 | copy in node_modules. If you would like to point this elsewhere, set this 146 | variable to the path you want. If that path is invalid, it will default to 147 | the global installed version of typescript 148 | 149 | 150 | *g:nvim_typescript#javascript_support* 151 | g:nvim_typescript#javascript_support 152 | Values: 0 or 1 153 | Default: 0 154 | 155 | Enables javascript support via the typescript client. Requires a 156 | `jsconfig.json` to be present. If one is not found, a boilerplate one will 157 | be created in your current working directory. 158 | 159 | 160 | *g:nvim_typescript#vue_support* 161 | g:nvim_typescript#vue_support 162 | Values: 0 or 1 163 | Default: 0 164 | 165 | Enables vue support. If set to 1, the completion will be enabled 166 | in single-file components with a .vue extension. 167 | 168 | 169 | *g:nvim_typescript#max_completion_detail* 170 | g:nvim_typescript#max_completion_detail 171 | Values: Any natural number 172 | Default: 25 173 | 174 | If the completion returned by the client exceed this number, the deoplete 175 | source will return less detail complete. This is for performance reasons. 176 | This can be bumped up to 100 without any real performance issues, but is 177 | set to 25 as a safe default. 178 | 179 | 180 | *g:nvim_typescript#type_info_on_hold* 181 | g:nvim_typescript#type_info_on_hold 182 | Values: 0 or 1 183 | Default: 0 184 | 185 | If set to 1, the type info for the symbol under the cursor will be 186 | displayed in the echo area. 187 | 188 | 189 | *g:nvim_typescript#signature_complete* 190 | g:nvim_typescript#signature_complete 191 | Values: 0 or 1 192 | Default: 0 193 | 194 | If set to 1, the function signature will be printed to the echo area. Since 195 | this plugin integrates with Deoplete, this is disabled in favor of echodoc. 196 | * https://github.com/Shougo/echodoc.vim 197 | 198 | 199 | g:nvim_typescript#default_mappings *g:nvim_typescript#default_mappings* 200 | Values: 0 or 1 201 | Default: 0 202 | 203 | Enable default mappings for select functions. 204 | 205 | 206 | g:nvim_typescript#completion_mark *g:nvim_typescript#completion_mark* 207 | Values: String 208 | Default: 'TS' 209 | 210 | Sets the default mark to display during omni-func or deoplete completion. 211 | Use this vs setting from deoplete. 212 | 213 | 214 | g:nvim_typescript#server_options *g:nvim_typescript#server_options* 215 | Values: List 216 | Default: '[]' 217 | 218 | Sets options for the typescript server. For example, locale can be set to 219 | French using ['--locale', 'fr']. 220 | 221 | g:nvim_typescript#diagnostics_enable *g:nvim_typescript#diagnostics_enable* 222 | Values: 0 or 1 223 | Default: 1 224 | 225 | If set to 1, the linting errors will be shown. 226 | 227 | g:nvim_typescript#quiet_startup *g:nvim_typescript#quiet_startup* 228 | Values: 0 or 1 229 | Default: 0 230 | 231 | If set to 1, the "starting server" and "server started" messages won't 232 | be shown. 233 | 234 | *g:nvim_typescript#suggestions_enabled* 235 | g:nvim_typescript#suggestions_enabled 236 | Values: 0 or 1 237 | Default: 1 238 | 239 | Enables display of suggestions as well as errors. If set to 1, you will 240 | be shown hints even for typescript errors that have been disabled in your 241 | tsconfig.json. 242 | 243 | =============================================================================== 244 | MAPPINGS *typescript-mappings* 245 | 246 | There are some mappings set, but are not enabled by default. Set 247 | |g:nvim_typescript#default_mappings| to |1| to enable. 248 | 249 | Mapping Action 250 | ------------- -------------- 251 | K :TSDoc 252 | tdp :TSDefPreview 253 | :TSTypeDef 254 | 255 | 256 | =============================================================================== 257 | FUNCTIONS *typescript-functions* 258 | 259 | 260 | *TSOnBufEnter* 261 | TSOnBufEnter 262 | 263 | Function called when a typescript file is opened. This starts the server 264 | client. 265 | 266 | *TSOnBufSave* 267 | TSOnBufSave 268 | 269 | Function called when a typescript file is saved. This send a reload event 270 | to the client, notifying it of changes made to the project. 271 | 272 | *TSGetServerPath* 273 | TSGetServerPath 274 | 275 | Function for checking the path of `tsserver`. By default, we'll use the 276 | binary in node_modules, but will switch to global when that fails. 277 | 278 | =============================================================================== 279 | TROUBLESHOOTING *typescript-troubleshooting* 280 | 281 | 282 | Q: No completions show up on typing. 283 | 284 | A: Make sure you have Deoplete installed and have it enabled on startup. 285 | 286 | > 287 | let g:deoplete#enable_at_startup=1 288 | < 289 | 290 | Q: I get errors when using this plugin. 291 | 292 | A: This could happen for a number of reasons 293 | 1. No valid tsconfig.json in your project 294 | 2. No installation of typescript on your system. 295 | 3. `UpdateRemotePlugins` has not been called since installation. 296 | 4. Plugin add did not run `install.sh` 297 | 298 | Q: The plugin does not recognize my file as typescript. 299 | 300 | A: This file does not include a syntax file for typescript. I do not want to 301 | write one. Please install *https://github.com/HerringtonDarkholme/yats.vim* 302 | or *https://github.com/leafgarland/typescript-vim* 303 | 304 | Q: Why better than tsuquyomi? Why is this better than other plugins? 305 | 306 | A: This plugin does not claim to be better than any other plugin, and is more 307 | for my own needs. Maybe you would like it too? The plugin is all 308 | asynchronous and is lag free. Other plugins are not always async or require 309 | additional plugins that need to be built. If you are using regular Vim, I 310 | highly suggest Tsuquyomi, especially with Vim 8. 311 | 312 | Q: I want to disable automatic diagnostic checks from this plugin and trigger 313 | it on save or some other event. 314 | 315 | A: Use the |g:nvim_typescript#diagnostics_enable| setting to disable automatic 316 | diagnostic checks and manually add the autocmd hooks in your rc file. 317 | 318 | > 319 | autocmd BufWrite *.ts,*.tsx TSGetDiagnostics 320 | < 321 | 322 | This is not a feature I want to add, but is easy to add yourself. 323 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd "$(dirname "$0")" 4 | if which npm >/dev/null; then 5 | cd rplugin/node/nvim_typescript && npm install && npm run build 6 | else 7 | if which yarn >/dev/null; then 8 | cd rplugin/node/nvim_typescript && yarn && yarn build 9 | else 10 | echo "You must have NPM or Yarn installed" 11 | exit 1 12 | fi 13 | fi 14 | -------------------------------------------------------------------------------- /ncm2-plugin/nvim_typescript.vim: -------------------------------------------------------------------------------- 1 | " ncm2 source 2 | 3 | call ncm2#nvim_typescript#init() 4 | 5 | -------------------------------------------------------------------------------- /plugin/nvim_typescript.vim: -------------------------------------------------------------------------------- 1 | if exists('g:nvim_typescript#loaded') 2 | finish 3 | endif 4 | 5 | " Some settings {{{ 6 | let g:nvim_typescript#loaded = 1 7 | let g:airline#extensions#nvim_typescript#enabled = 8 | \ get(g:, 'airline#extensions#nvim_typescript#enabled', 1) 9 | let g:nvim_typescript#completion_res = [] 10 | let g:nvim_typescript#javascript_support = 11 | \ get(g:, 'nvim_typescript#javascript_support', 0) 12 | let g:nvim_typescript#vue_support = 13 | \ get(g:, 'nvim_typescript#vue_support', 0) 14 | let g:nvim_typescript#server_path = 15 | \ get(g:, 'nvim_typescript#server_path', 'node_modules/.bin/tsserver') 16 | let g:nvim_typescript#max_completion_detail = 17 | \ get(g:, 'nvim_typescript#max_completion_detail', 25) 18 | let g:nvim_typescript#type_info_on_hold = 19 | \ get(g:, 'nvim_typescript#type_info_on_hold', 0) 20 | let g:nvim_typescript#signature_complete = 21 | \ get(g:, 'nvim_typescript#signature_complete', 0) 22 | let g:nvim_typescript#default_mappings = 23 | \ get(g:, 'nvim_typescript#default_mappings', 0) 24 | let g:nvim_typescript#completion_mark = 25 | \ get(g:, 'nvim_typescript#completion_mark', 'TS') 26 | let g:nvim_typescript#debug_enabled = 27 | \ get(g:, 'nvim_typescript#debug_enabled', 0) 28 | let g:nvim_typescript#debug_settings = 29 | \ get(g:, 'nvim_typescript#debug_settings', {'file': 'nvim-typescript-tsserver.log', 'level': 'normal'}) 30 | let g:nvim_typescript#diagnostics_enable = 31 | \ get(g:, 'nvim_typescript#diagnostics_enable', 1) 32 | let g:nvim_typescript#quiet_startup = 33 | \ get(g:, 'nvim_typescript#quiet_startup', 0) 34 | let g:nvim_typescript#server_options = 35 | \ get(g:, 'nvim_typescript#server_options', []) 36 | let g:nvim_typescript#expand_snippet = 37 | \ get(g:, 'nvim_typescript#expand_snippet', 0) 38 | let g:nvim_typescript#follow_dir_change = 39 | \ get(g:, 'nvim_typescript#follow_dir_change', 0) 40 | let g:nvim_typescript#suggestions_enabled = 41 | \ get(g:, 'nvim_typescript#suggestions_enabled', 0) 42 | let s:kind_symbols = { 43 | \ 'keyword': 'keyword', 44 | \ 'class': 'class', 45 | \ 'interface': 'interface', 46 | \ 'script': 'script', 47 | \ 'module': 'module', 48 | \ 'local class': 'local class', 49 | \ 'type': 'type', 50 | \ 'enum': 'enum', 51 | \ 'alias': 'alias', 52 | \ 'type parameter': 'type param', 53 | \ 'primitive type': 'primitive type', 54 | \ 'var': 'var', 55 | \ 'local var': 'local var', 56 | \ 'property': 'prop', 57 | \ 'let': 'let', 58 | \ 'const': 'const', 59 | \ 'label': 'label', 60 | \ 'parameter': 'param', 61 | \ 'index': 'index', 62 | \ 'function': 'function', 63 | \ 'local function': 'local function', 64 | \ 'method': 'method', 65 | \ 'getter': 'getter', 66 | \ 'setter': 'setter', 67 | \ 'call': 'call', 68 | \ 'constructor': 'constructor' 69 | \} 70 | 71 | let g:nvim_typescript#kind_symbols = 72 | \ get(g:, 'nvim_typescript#kind_symbols', s:kind_symbols) 73 | 74 | let g:nvim_typescript#default_signs = 75 | \ get(g:, 'nvim_typescript#default_signs', [ 76 | \ { 77 | \ 'TSerror': { 78 | \ 'texthl': 'SpellBad', 79 | \ 'signText': '•', 80 | \ 'signTexthl': 'NeomakeErrorSign' 81 | \ } 82 | \}, 83 | \{ 84 | \ 'TSwarning': { 85 | \ 'texthl': 'SpellBad', 86 | \ 'signText': '•', 87 | \ 'signTexthl': 'NeomakeWarningSign' 88 | \ } 89 | \}, 90 | \{ 91 | \ 'TSsuggestion': { 92 | \ 'texthl': 'SpellBad', 93 | \ 'signText': '•', 94 | \ 'signTexthl': 'NeomakeInfoSign' 95 | \ } 96 | \}, 97 | \{ 98 | \ 'TShint': { 99 | \ 'texthl': 'SpellBad', 100 | \ 'signText': '?', 101 | \ 'signTexthl': 'NeomakeInfoSign' 102 | \ } 103 | \} 104 | \]) 105 | 106 | hi default link nvimtypescriptPopupNormal Pmenu 107 | 108 | "}}} 109 | 110 | augroup nvim-typescript "{{{ 111 | autocmd! 112 | 113 | "FZF stuff 114 | function! s:TSSearch(query) "{{{ 115 | let l:symbols = TSGetWorkspaceSymbolsFunc(a:query, expand('%')) 116 | call setloclist(0, l:symbols, 'r', 'Symbols') 117 | lopen 118 | endfunction 119 | command! -nargs=1 TSSearch call s:TSSearch() "}}} 120 | 121 | " Regular JS support {{{ 122 | if get(g:, 'nvim_typescript#javascript_support', 1) 123 | 124 | autocmd BufEnter *.js,*.jsx call nvim_typescript#DefaultKeyMap() 125 | autocmd BufEnter *.js,*.jsx call TSOnBufEnter() 126 | autocmd BufUnload *.js,*.jsx call TSOnBufLeave(expand('%:p')) 127 | autocmd BufLeave *.js,*jsx call TSCloseWindow() 128 | autocmd BufWritePost *.js,*.jsx call TSOnBufSave() 129 | " if get(g:, 'nvim_typescript#signature_complete', 1) 130 | " autocmd CompleteDone *.js,*.jsx TSSig 131 | " endif 132 | if get(g:, 'nvim_typescript#type_info_on_hold', 1) 133 | autocmd CursorHold *.js,*.jsx TSType 134 | endif 135 | if get(g:, 'nvim_typescript#follow_dir_change', 1) 136 | autocmd DirChanged * call TSOnBufSave() 137 | endif 138 | if get(g:, 'nvim_typescript#diagnostics_enable', 1) 139 | autocmd CursorHold,CursorHoldI *.js,*.jsx call TSEchoMessage() 140 | endif 141 | autocmd CursorMoved,CursorMovedI,InsertLeave *.js,*.jsx call TSCloseWindow() 142 | endif "}}} 143 | 144 | " Vue Support {{{ 145 | if get(g:, 'nvim_typescript#vue_support', 1) 146 | 147 | autocmd BufEnter *.vue call nvim_typescript#DefaultKeyMap() 148 | autocmd BufEnter *.vue call TSOnBufEnter() 149 | autocmd BufWritePost *.vue call TSOnBufSave() 150 | autocmd BufUnload *.vue call TSOnBufLeave(expand('%:p')) 151 | " if get(g:, 'nvim_typescript#signature_complete', 1) 152 | " autocmd CompleteDone,Filetype vue TSSig 153 | " autocmd CompleteDone *.vue TSSig 154 | " endif 155 | if get(g:, 'nvim_typescript#type_info_on_hold', 1) 156 | autocmd CursorHold *.vue TSType 157 | endif 158 | if get(g:, 'nvim_typescript#diagnostics_enable', 1) 159 | autocmd CursorHold,CursorHoldI *.vue call TSEchoMessage() 160 | endif 161 | autocmd CursorMoved,CursorMovedI,InsertLeave *.vue call TSCloseWindow() 162 | endif "}}} 163 | 164 | " Core {{{ 165 | autocmd BufEnter *.ts,*.tsx call nvim_typescript#DefaultKeyMap() 166 | autocmd BufEnter *.ts,*.tsx call TSOnBufEnter(expand('%:p')) 167 | autocmd BufUnload *.ts,*.tsx call TSOnBufLeave(expand('%:p')) 168 | autocmd BufUnload *.ts,*tsx call TSCloseWindow() 169 | autocmd BufWritePost *.ts,*.tsx call TSOnBufSave() 170 | " if get(g:, 'nvim_typescript#signature_complete', 1) "{{{ 171 | " autocmd CompleteDone *.ts,*.tsx TSSig 172 | " endif "}}} 173 | if get(g:, 'nvim_typescript#type_info_on_hold', 1) "{{{ 174 | autocmd CursorHold *.ts,*.tsx TSType 175 | endif "}}} 176 | if get(g:, 'nvim_typescript#follow_dir_change', 1) "{{{ 177 | autocmd DirChanged * call TSOnBufSave() 178 | endif ""}}} 179 | if get(g:, 'nvim_typescript#diagnostics_enable', 1) "{{{ 180 | autocmd CursorHold *.ts,*.tsx call TSEchoMessage() 181 | " autocmd CursorHoldI *.ts,*.tsx call TSEchoMessage() 182 | endif "}}} 183 | 184 | autocmd CursorMoved *.ts,*.tsx call TSCloseWindow() 185 | " autocmd CursorMovedI *.ts,*.tsx call TSCloseWindow() 186 | autocmd InsertLeave *.ts,*.tsx call TSCloseWindow() 187 | autocmd BufWritePost tsconfig.json TSReloadProject 188 | autocmd User CmSetup call cm#sources#typescript#register() 189 | " Cleanup required to prevent hanging on Windows exit 190 | autocmd VimLeavePre * TSStop 191 | "}}} 192 | 193 | augroup end "}}} 194 | 195 | -------------------------------------------------------------------------------- /rplugin/node/nvim_typescript/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "neovim-test", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.10.4", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", 10 | "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.4" 14 | } 15 | }, 16 | "@babel/helper-validator-identifier": { 17 | "version": "7.10.4", 18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", 19 | "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", 20 | "dev": true 21 | }, 22 | "@babel/highlight": { 23 | "version": "7.10.4", 24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", 25 | "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", 26 | "dev": true, 27 | "requires": { 28 | "@babel/helper-validator-identifier": "^7.10.4", 29 | "chalk": "^2.0.0", 30 | "js-tokens": "^4.0.0" 31 | } 32 | }, 33 | "@msgpack/msgpack": { 34 | "version": "1.12.2", 35 | "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-1.12.2.tgz", 36 | "integrity": "sha512-Vwhc3ObxmDZmA5hY8mfsau2rJ4vGPvzbj20QSZ2/E1GDPF61QVyjLfNHak9xmel6pW4heRt3v1fHa6np9Ehfeg==" 37 | }, 38 | "@rollup/plugin-node-resolve": { 39 | "version": "8.4.0", 40 | "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.4.0.tgz", 41 | "integrity": "sha512-LFqKdRLn0ShtQyf6SBYO69bGE1upV6wUhBX0vFOUnLAyzx5cwp8svA0eHUnu8+YU57XOkrMtfG63QOpQx25pHQ==", 42 | "dev": true, 43 | "requires": { 44 | "@rollup/pluginutils": "^3.1.0", 45 | "@types/resolve": "1.17.1", 46 | "builtin-modules": "^3.1.0", 47 | "deep-freeze": "^0.0.1", 48 | "deepmerge": "^4.2.2", 49 | "is-module": "^1.0.0", 50 | "resolve": "^1.17.0" 51 | }, 52 | "dependencies": { 53 | "@types/resolve": { 54 | "version": "1.17.1", 55 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", 56 | "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", 57 | "dev": true, 58 | "requires": { 59 | "@types/node": "*" 60 | } 61 | }, 62 | "resolve": { 63 | "version": "1.17.0", 64 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", 65 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", 66 | "dev": true, 67 | "requires": { 68 | "path-parse": "^1.0.6" 69 | } 70 | } 71 | } 72 | }, 73 | "@rollup/plugin-typescript": { 74 | "version": "5.0.2", 75 | "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-5.0.2.tgz", 76 | "integrity": "sha512-CkS028Itwjqm1uLbFVfpJgtVtnNvZ+og/m6UlNRR5wOOnNTWPcVQzOu5xGdEX+WWJxdvWIqUq2uR/RBt2ZipWg==", 77 | "dev": true, 78 | "requires": { 79 | "@rollup/pluginutils": "^3.0.1", 80 | "resolve": "^1.14.1" 81 | }, 82 | "dependencies": { 83 | "resolve": { 84 | "version": "1.17.0", 85 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", 86 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", 87 | "dev": true, 88 | "requires": { 89 | "path-parse": "^1.0.6" 90 | } 91 | } 92 | } 93 | }, 94 | "@rollup/pluginutils": { 95 | "version": "3.1.0", 96 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", 97 | "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", 98 | "dev": true, 99 | "requires": { 100 | "@types/estree": "0.0.39", 101 | "estree-walker": "^1.0.1", 102 | "picomatch": "^2.2.2" 103 | }, 104 | "dependencies": { 105 | "estree-walker": { 106 | "version": "1.0.1", 107 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", 108 | "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", 109 | "dev": true 110 | } 111 | } 112 | }, 113 | "@types/estree": { 114 | "version": "0.0.39", 115 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", 116 | "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", 117 | "dev": true 118 | }, 119 | "@types/lodash": { 120 | "version": "4.14.138", 121 | "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.138.tgz", 122 | "integrity": "sha512-A4uJgHz4hakwNBdHNPdxOTkYmXNgmUAKLbXZ7PKGslgeV0Mb8P3BlbYfPovExek1qnod4pDfRbxuzcVs3dlFLg==" 123 | }, 124 | "@types/lodash-es": { 125 | "version": "4.17.3", 126 | "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.3.tgz", 127 | "integrity": "sha512-iHI0i7ZAL1qepz1Y7f3EKg/zUMDwDfTzitx+AlHhJJvXwenP682ZyGbgPSc5Ej3eEAKVbNWKFuwOadCj5vBbYQ==", 128 | "dev": true, 129 | "requires": { 130 | "@types/lodash": "*" 131 | } 132 | }, 133 | "@types/node": { 134 | "version": "12.19.5", 135 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.5.tgz", 136 | "integrity": "sha512-Wgdl27uw/jUYUFyajUGKSjDNGxmJrZi9sjeG6UJImgUtKbJoO9aldx+1XODN1EpNDX9DirvbvHHmTsNlb8GwMA==", 137 | "dev": true 138 | }, 139 | "@types/tmp": { 140 | "version": "0.1.0", 141 | "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.1.0.tgz", 142 | "integrity": "sha512-6IwZ9HzWbCq6XoQWhxLpDjuADodH/MKXRUIDFudvgjcVdjFknvmR+DNsoUeer4XPrEnrZs04Jj+kfV9pFsrhmA==", 143 | "dev": true 144 | }, 145 | "ansi-styles": { 146 | "version": "3.2.1", 147 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 148 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 149 | "dev": true, 150 | "requires": { 151 | "color-convert": "^1.9.0" 152 | } 153 | }, 154 | "async": { 155 | "version": "2.6.3", 156 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", 157 | "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", 158 | "requires": { 159 | "lodash": "^4.17.14" 160 | } 161 | }, 162 | "balanced-match": { 163 | "version": "1.0.0", 164 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 165 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 166 | }, 167 | "brace-expansion": { 168 | "version": "1.1.11", 169 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 170 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 171 | "requires": { 172 | "balanced-match": "^1.0.0", 173 | "concat-map": "0.0.1" 174 | } 175 | }, 176 | "buffer-from": { 177 | "version": "1.1.1", 178 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 179 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 180 | "dev": true 181 | }, 182 | "builtin-modules": { 183 | "version": "3.1.0", 184 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", 185 | "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", 186 | "dev": true 187 | }, 188 | "chalk": { 189 | "version": "2.4.2", 190 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 191 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 192 | "dev": true, 193 | "requires": { 194 | "ansi-styles": "^3.2.1", 195 | "escape-string-regexp": "^1.0.5", 196 | "supports-color": "^5.3.0" 197 | } 198 | }, 199 | "color": { 200 | "version": "3.0.0", 201 | "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", 202 | "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", 203 | "requires": { 204 | "color-convert": "^1.9.1", 205 | "color-string": "^1.5.2" 206 | } 207 | }, 208 | "color-convert": { 209 | "version": "1.9.3", 210 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 211 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 212 | "requires": { 213 | "color-name": "1.1.3" 214 | } 215 | }, 216 | "color-name": { 217 | "version": "1.1.3", 218 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 219 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 220 | }, 221 | "color-string": { 222 | "version": "1.5.4", 223 | "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", 224 | "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", 225 | "requires": { 226 | "color-name": "^1.0.0", 227 | "simple-swizzle": "^0.2.2" 228 | } 229 | }, 230 | "colornames": { 231 | "version": "1.1.1", 232 | "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", 233 | "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" 234 | }, 235 | "colors": { 236 | "version": "1.4.0", 237 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", 238 | "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" 239 | }, 240 | "colorspace": { 241 | "version": "1.1.2", 242 | "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", 243 | "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", 244 | "requires": { 245 | "color": "3.0.x", 246 | "text-hex": "1.0.x" 247 | } 248 | }, 249 | "commander": { 250 | "version": "2.20.3", 251 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 252 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 253 | "dev": true 254 | }, 255 | "concat-map": { 256 | "version": "0.0.1", 257 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 258 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 259 | }, 260 | "core-util-is": { 261 | "version": "1.0.2", 262 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 263 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 264 | }, 265 | "deep-freeze": { 266 | "version": "0.0.1", 267 | "resolved": "https://registry.npmjs.org/deep-freeze/-/deep-freeze-0.0.1.tgz", 268 | "integrity": "sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ=", 269 | "dev": true 270 | }, 271 | "deepmerge": { 272 | "version": "4.2.2", 273 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", 274 | "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", 275 | "dev": true 276 | }, 277 | "diagnostics": { 278 | "version": "1.1.1", 279 | "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", 280 | "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", 281 | "requires": { 282 | "colorspace": "1.1.x", 283 | "enabled": "1.0.x", 284 | "kuler": "1.0.x" 285 | } 286 | }, 287 | "enabled": { 288 | "version": "1.0.2", 289 | "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", 290 | "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", 291 | "requires": { 292 | "env-variable": "0.0.x" 293 | } 294 | }, 295 | "env-variable": { 296 | "version": "0.0.6", 297 | "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.6.tgz", 298 | "integrity": "sha512-bHz59NlBbtS0NhftmR8+ExBEekE7br0e01jw+kk0NDro7TtZzBYZ5ScGPs3OmwnpyfHTHOtr1Y6uedCdrIldtg==" 299 | }, 300 | "escape-string-regexp": { 301 | "version": "1.0.5", 302 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 303 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 304 | "dev": true 305 | }, 306 | "fast-safe-stringify": { 307 | "version": "2.0.7", 308 | "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", 309 | "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" 310 | }, 311 | "fecha": { 312 | "version": "4.2.0", 313 | "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", 314 | "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" 315 | }, 316 | "fs.realpath": { 317 | "version": "1.0.0", 318 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 319 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 320 | }, 321 | "fsevents": { 322 | "version": "2.1.3", 323 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", 324 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", 325 | "dev": true, 326 | "optional": true 327 | }, 328 | "glob": { 329 | "version": "7.1.4", 330 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", 331 | "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", 332 | "requires": { 333 | "fs.realpath": "^1.0.0", 334 | "inflight": "^1.0.4", 335 | "inherits": "2", 336 | "minimatch": "^3.0.4", 337 | "once": "^1.3.0", 338 | "path-is-absolute": "^1.0.0" 339 | } 340 | }, 341 | "has-flag": { 342 | "version": "3.0.0", 343 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 344 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 345 | "dev": true 346 | }, 347 | "inflight": { 348 | "version": "1.0.6", 349 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 350 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 351 | "requires": { 352 | "once": "^1.3.0", 353 | "wrappy": "1" 354 | } 355 | }, 356 | "inherits": { 357 | "version": "2.0.4", 358 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 359 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 360 | }, 361 | "is-arrayish": { 362 | "version": "0.3.2", 363 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", 364 | "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" 365 | }, 366 | "is-module": { 367 | "version": "1.0.0", 368 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", 369 | "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", 370 | "dev": true 371 | }, 372 | "is-stream": { 373 | "version": "1.1.0", 374 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 375 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 376 | }, 377 | "isarray": { 378 | "version": "1.0.0", 379 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 380 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 381 | }, 382 | "jest-worker": { 383 | "version": "26.2.1", 384 | "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.2.1.tgz", 385 | "integrity": "sha512-+XcGMMJDTeEGncRb5M5Zq9P7K4sQ1sirhjdOxsN1462h6lFo9w59bl2LVQmdGEEeU3m+maZCkS2Tcc9SfCHO4A==", 386 | "dev": true, 387 | "requires": { 388 | "@types/node": "*", 389 | "merge-stream": "^2.0.0", 390 | "supports-color": "^7.0.0" 391 | }, 392 | "dependencies": { 393 | "has-flag": { 394 | "version": "4.0.0", 395 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 396 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 397 | "dev": true 398 | }, 399 | "supports-color": { 400 | "version": "7.1.0", 401 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", 402 | "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", 403 | "dev": true, 404 | "requires": { 405 | "has-flag": "^4.0.0" 406 | } 407 | } 408 | } 409 | }, 410 | "js-tokens": { 411 | "version": "4.0.0", 412 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 413 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 414 | "dev": true 415 | }, 416 | "kuler": { 417 | "version": "1.0.1", 418 | "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", 419 | "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", 420 | "requires": { 421 | "colornames": "^1.1.1" 422 | } 423 | }, 424 | "lodash": { 425 | "version": "4.17.20", 426 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", 427 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" 428 | }, 429 | "lodash-es": { 430 | "version": "4.17.15", 431 | "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz", 432 | "integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==" 433 | }, 434 | "lodash.defaults": { 435 | "version": "4.2.0", 436 | "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", 437 | "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" 438 | }, 439 | "lodash.omit": { 440 | "version": "4.5.0", 441 | "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", 442 | "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=" 443 | }, 444 | "logform": { 445 | "version": "2.2.0", 446 | "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", 447 | "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", 448 | "requires": { 449 | "colors": "^1.2.1", 450 | "fast-safe-stringify": "^2.0.4", 451 | "fecha": "^4.2.0", 452 | "ms": "^2.1.1", 453 | "triple-beam": "^1.3.0" 454 | } 455 | }, 456 | "merge-stream": { 457 | "version": "2.0.0", 458 | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", 459 | "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", 460 | "dev": true 461 | }, 462 | "minimatch": { 463 | "version": "3.0.4", 464 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 465 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 466 | "requires": { 467 | "brace-expansion": "^1.1.7" 468 | } 469 | }, 470 | "ms": { 471 | "version": "2.1.2", 472 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 473 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 474 | }, 475 | "neovim": { 476 | "version": "4.9.0", 477 | "resolved": "https://registry.npmjs.org/neovim/-/neovim-4.9.0.tgz", 478 | "integrity": "sha512-48hDy0Dheo5qFF+cwhj7qaWoXfbiKOQ0CLNE0/aiA41rhn/Z1m0OKQqlp9SqbSMr/PnY5QdiLdbs0xh2UudEfA==", 479 | "requires": { 480 | "@msgpack/msgpack": "^1.9.3", 481 | "lodash.defaults": "^4.2.0", 482 | "lodash.omit": "^4.5.0", 483 | "semver": "^7.1.1", 484 | "winston": "3.2.1" 485 | } 486 | }, 487 | "once": { 488 | "version": "1.4.0", 489 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 490 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 491 | "requires": { 492 | "wrappy": "1" 493 | } 494 | }, 495 | "one-time": { 496 | "version": "0.0.4", 497 | "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", 498 | "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" 499 | }, 500 | "path-is-absolute": { 501 | "version": "1.0.1", 502 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 503 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 504 | }, 505 | "path-parse": { 506 | "version": "1.0.6", 507 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 508 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 509 | "dev": true 510 | }, 511 | "picomatch": { 512 | "version": "2.2.2", 513 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", 514 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", 515 | "dev": true 516 | }, 517 | "process-nextick-args": { 518 | "version": "2.0.1", 519 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 520 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 521 | }, 522 | "randombytes": { 523 | "version": "2.1.0", 524 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 525 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 526 | "dev": true, 527 | "requires": { 528 | "safe-buffer": "^5.1.0" 529 | } 530 | }, 531 | "readable-stream": { 532 | "version": "3.6.0", 533 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 534 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 535 | "requires": { 536 | "inherits": "^2.0.3", 537 | "string_decoder": "^1.1.1", 538 | "util-deprecate": "^1.0.1" 539 | } 540 | }, 541 | "rimraf": { 542 | "version": "2.7.1", 543 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 544 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 545 | "requires": { 546 | "glob": "^7.1.3" 547 | } 548 | }, 549 | "rollup": { 550 | "version": "2.33.3", 551 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.33.3.tgz", 552 | "integrity": "sha512-RpayhPTe4Gu/uFGCmk7Gp5Z9Qic2VsqZ040G+KZZvsZYdcuWaJg678JeDJJvJeEQXminu24a2au+y92CUWVd+w==", 553 | "dev": true, 554 | "requires": { 555 | "fsevents": "~2.1.2" 556 | } 557 | }, 558 | "rollup-plugin-terser": { 559 | "version": "6.1.0", 560 | "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-6.1.0.tgz", 561 | "integrity": "sha512-4fB3M9nuoWxrwm39habpd4hvrbrde2W2GG4zEGPQg1YITNkM3Tqur5jSuXlWNzbv/2aMLJ+dZJaySc3GCD8oDw==", 562 | "dev": true, 563 | "requires": { 564 | "@babel/code-frame": "^7.8.3", 565 | "jest-worker": "^26.0.0", 566 | "serialize-javascript": "^3.0.0", 567 | "terser": "^4.7.0" 568 | } 569 | }, 570 | "safe-buffer": { 571 | "version": "5.2.1", 572 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 573 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 574 | }, 575 | "semver": { 576 | "version": "7.3.2", 577 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", 578 | "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" 579 | }, 580 | "serialize-javascript": { 581 | "version": "3.1.0", 582 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", 583 | "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", 584 | "dev": true, 585 | "requires": { 586 | "randombytes": "^2.1.0" 587 | } 588 | }, 589 | "simple-swizzle": { 590 | "version": "0.2.2", 591 | "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", 592 | "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", 593 | "requires": { 594 | "is-arrayish": "^0.3.1" 595 | } 596 | }, 597 | "source-map": { 598 | "version": "0.6.1", 599 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 600 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 601 | "dev": true 602 | }, 603 | "source-map-support": { 604 | "version": "0.5.19", 605 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", 606 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", 607 | "dev": true, 608 | "requires": { 609 | "buffer-from": "^1.0.0", 610 | "source-map": "^0.6.0" 611 | } 612 | }, 613 | "stack-trace": { 614 | "version": "0.0.10", 615 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 616 | "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" 617 | }, 618 | "string_decoder": { 619 | "version": "1.3.0", 620 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 621 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 622 | "requires": { 623 | "safe-buffer": "~5.2.0" 624 | } 625 | }, 626 | "supports-color": { 627 | "version": "5.5.0", 628 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 629 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 630 | "dev": true, 631 | "requires": { 632 | "has-flag": "^3.0.0" 633 | } 634 | }, 635 | "terser": { 636 | "version": "4.8.0", 637 | "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", 638 | "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", 639 | "dev": true, 640 | "requires": { 641 | "commander": "^2.20.0", 642 | "source-map": "~0.6.1", 643 | "source-map-support": "~0.5.12" 644 | } 645 | }, 646 | "text-hex": { 647 | "version": "1.0.0", 648 | "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", 649 | "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" 650 | }, 651 | "tmp": { 652 | "version": "0.1.0", 653 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", 654 | "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", 655 | "requires": { 656 | "rimraf": "^2.6.3" 657 | } 658 | }, 659 | "triple-beam": { 660 | "version": "1.3.0", 661 | "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", 662 | "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" 663 | }, 664 | "tslib": { 665 | "version": "1.10.0", 666 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", 667 | "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" 668 | }, 669 | "typescript": { 670 | "version": "3.9.7", 671 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", 672 | "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", 673 | "dev": true 674 | }, 675 | "util-deprecate": { 676 | "version": "1.0.2", 677 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 678 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 679 | }, 680 | "winston": { 681 | "version": "3.2.1", 682 | "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", 683 | "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", 684 | "requires": { 685 | "async": "^2.6.1", 686 | "diagnostics": "^1.1.1", 687 | "is-stream": "^1.1.0", 688 | "logform": "^2.1.1", 689 | "one-time": "0.0.4", 690 | "readable-stream": "^3.1.1", 691 | "stack-trace": "0.0.x", 692 | "triple-beam": "^1.3.0", 693 | "winston-transport": "^4.3.0" 694 | } 695 | }, 696 | "winston-transport": { 697 | "version": "4.4.0", 698 | "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", 699 | "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", 700 | "requires": { 701 | "readable-stream": "^2.3.7", 702 | "triple-beam": "^1.2.0" 703 | }, 704 | "dependencies": { 705 | "readable-stream": { 706 | "version": "2.3.7", 707 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 708 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 709 | "requires": { 710 | "core-util-is": "~1.0.0", 711 | "inherits": "~2.0.3", 712 | "isarray": "~1.0.0", 713 | "process-nextick-args": "~2.0.0", 714 | "safe-buffer": "~5.1.1", 715 | "string_decoder": "~1.1.1", 716 | "util-deprecate": "~1.0.1" 717 | } 718 | }, 719 | "safe-buffer": { 720 | "version": "5.1.2", 721 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 722 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 723 | }, 724 | "string_decoder": { 725 | "version": "1.1.1", 726 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 727 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 728 | "requires": { 729 | "safe-buffer": "~5.1.0" 730 | } 731 | } 732 | } 733 | }, 734 | "wrappy": { 735 | "version": "1.0.2", 736 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 737 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 738 | } 739 | } 740 | } 741 | -------------------------------------------------------------------------------- /rplugin/node/nvim_typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "neovim-test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./lib/index.js", 6 | "module": "./lib/index.esm.js", 7 | "scripts": { 8 | "build": "rollup -c", 9 | "watch": "rollup -cw" 10 | }, 11 | "keywords": [], 12 | "author": "Mike Hartington ", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@rollup/plugin-node-resolve": "^8.4.0", 16 | "@rollup/plugin-typescript": "^5.0.2", 17 | "@types/lodash-es": "4.17.3", 18 | "@types/node": "^12.19.5", 19 | "@types/tmp": "0.1.0", 20 | "rollup": "^2.33.3", 21 | "rollup-plugin-terser": "^6.1.0", 22 | "typescript": "^3.9.7" 23 | }, 24 | "dependencies": { 25 | "@types/lodash": "4.14.138", 26 | "lodash": "^4.17.20", 27 | "lodash-es": "^4.17.15", 28 | "neovim": "^4.9.0", 29 | "tmp": "0.1.0", 30 | "tslib": "1.10.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rplugin/node/nvim_typescript/rollup.config.js: -------------------------------------------------------------------------------- 1 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 2 | import typescript from '@rollup/plugin-typescript'; 3 | import { terser } from 'rollup-plugin-terser'; 4 | 5 | import pkg from './package.json'; 6 | 7 | export default { 8 | input: 'src/index.ts', 9 | external: [ 10 | 'fs', 11 | 'child_process', 12 | '@neovim/decorators', 13 | 'tmp', 14 | 'path', 15 | 'events', 16 | 'os', 17 | 'readline', 18 | ], 19 | output: [ 20 | { file: pkg.main, format: 'cjs' }, 21 | { file: pkg.module, format: 'esm' }, 22 | ], 23 | plugins: [ 24 | typescript(), 25 | nodeResolve(), 26 | terser({ 27 | output: { comments: false }, 28 | compress: { 29 | keep_infinity: true, 30 | pure_getters: true, 31 | passes: 10, 32 | }, 33 | ecma: 5, 34 | warnings: true, 35 | }), 36 | ], 37 | }; 38 | -------------------------------------------------------------------------------- /rplugin/node/nvim_typescript/src/client.ts: -------------------------------------------------------------------------------- 1 | import { ChildProcess, execSync, spawn, SpawnOptions } from 'child_process'; 2 | import { EventEmitter } from 'events'; 3 | import { existsSync } from 'fs'; 4 | import { EOL, platform } from 'os'; 5 | import { normalize } from 'path'; 6 | import { createInterface } from 'readline'; 7 | import protocol from 'typescript/lib/protocol'; 8 | import { trim } from './utils'; 9 | 10 | export class Client extends EventEmitter { 11 | public serverHandle: ChildProcess = null; 12 | public _rl: any; 13 | public _seqNumber = 0; 14 | public _seqToPromises = {}; 15 | public _cwd = process.cwd(); 16 | public _env = process.env; 17 | public serverPath = 'tsserver'; 18 | public serverOptions: string[] = []; 19 | public logFunc: Function = null; 20 | public completionCommand = 'completionInfo'; 21 | public tsConfigVersion: { 22 | major: number; 23 | minor: number; 24 | patch: number; 25 | } = null; 26 | private getErrRes = []; 27 | // Get server, set server 28 | getServerPath() { 29 | return this.serverPath; 30 | } 31 | setServerPath(val: string) { 32 | const normalizedPath = normalize(val); 33 | if (existsSync(normalizedPath)) { 34 | this.serverPath = normalizedPath; 35 | } 36 | } 37 | 38 | // Start the Proc 39 | startServer(): Promise { 40 | return new Promise((res) => { 41 | // _env['TSS_LOG'] = "-logToFile true -file ./server.log" 42 | let args = [...this.serverOptions, '--disableAutomaticTypingAcquisition'] 43 | let options: SpawnOptions = { 44 | stdio: 'pipe', 45 | cwd: this._cwd, 46 | env: this._env, 47 | detached: true, 48 | shell: false 49 | } 50 | let cmd = this.serverPath; 51 | if (platform() === 'win32') { 52 | // detached must be false for windows to avoid child window 53 | // https://nodejs.org/api/child_process.html#child_process_options_detached 54 | options.detached = false; 55 | args = ['/c', this.serverPath, ...args]; 56 | cmd = 'cmd'; 57 | } 58 | 59 | this.serverHandle = spawn(cmd, args, options); 60 | 61 | this._rl = createInterface({ 62 | input: this.serverHandle.stdout, 63 | output: this.serverHandle.stdin, 64 | terminal: false 65 | }); 66 | 67 | this.serverHandle.stderr.on('data', (_data: string, _err: any) => { 68 | // console.error('Error from tss: ' + data); 69 | }); 70 | 71 | this.serverHandle.on('error', _data => { 72 | // console.log(`ERROR Event: ${data}`); 73 | }); 74 | 75 | this.serverHandle.on('exit', _data => { 76 | // console.log(`exit Event: ${data}`); 77 | }); 78 | 79 | this.serverHandle.on('close', _data => { 80 | // console.log(`Close Event: ${data}`); 81 | }); 82 | 83 | this._rl.on('line', (msg: string) => { 84 | if (msg.indexOf('{') === 0) { 85 | this.parseResponse(msg); 86 | } 87 | }); 88 | return res(); 89 | 90 | }) 91 | 92 | } 93 | stopServer() { 94 | this.serverHandle.kill('SIGINT'); 95 | this.serverHandle = null 96 | } 97 | setTSConfigVersion() { 98 | const command = this.serverPath.replace('tsserver', 'tsc'); 99 | const rawOutput = execSync(`${command} --version`).toString(); 100 | const [major, minor, patch] = trim(rawOutput) 101 | .split(' ') 102 | .pop() 103 | .split('-')[0] 104 | .split('.'); 105 | this.tsConfigVersion = { 106 | major: parseInt(major), 107 | minor: parseInt(minor), 108 | patch: parseInt(patch) 109 | }; 110 | 111 | this.completionCommand = this.isCurrentVersionHighter(300) ? 'completionInfo' : 'completions'; 112 | 113 | } 114 | isCurrentVersionHighter(val: number) { 115 | const local = 116 | this.tsConfigVersion.major * 100 + 117 | this.tsConfigVersion.minor * 10 + 118 | this.tsConfigVersion.patch; 119 | return local >= val; 120 | } 121 | 122 | // LangServer Commands 123 | openFile(args: protocol.OpenRequestArgs) { this._makeNoResponseRequest('open', args); } 124 | closeFile(args: protocol.FileRequestArgs) { this._makeNoResponseRequest('close', args); } 125 | reloadProject() { this._makeNoResponseRequest('reloadProjects', null); } 126 | 127 | updateFile(args: protocol.ReloadRequestArgs): Promise { return this._makeTssRequest('reload', args); } 128 | quickInfo(args: protocol.FileLocationRequestArgs): Promise { return this._makeTssRequest('quickinfo', args); } 129 | getDef(args: protocol.FileLocationRequestArgs): Promise { return this._makeTssRequest('definition', args); } 130 | getCompletions(args: protocol.CompletionsRequestArgs): Promise { return this._makeTssRequest(this.completionCommand, args); } 131 | getCompletionDetails(args: protocol.CompletionDetailsRequestArgs): Promise { return this._makeTssRequest('completionEntryDetails', args); } 132 | getProjectInfo(args: protocol.ProjectInfoRequestArgs): Promise { return this._makeTssRequest('projectInfo', args); } 133 | getSymbolRefs(args: protocol.FileLocationRequestArgs): Promise { return this._makeTssRequest('references', args); } 134 | getSignature(args: protocol.FileLocationRequestArgs): Promise { return this._makeTssRequest('signatureHelp', args); } 135 | renameSymbol(args: protocol.RenameRequestArgs): Promise { return this._makeTssRequest('rename', args); } 136 | getTypeDef(args: protocol.FileLocationRequestArgs): Promise { return this._makeTssRequest('typeDefinition', args); } 137 | getDocumentSymbols(args: protocol.FileRequestArgs): Promise { return this._makeTssRequest('navtree', args); } 138 | getWorkspaceSymbols(args: protocol.NavtoRequestArgs): Promise { return this._makeTssRequest('navto', args); } 139 | getSemanticDiagnosticsSync(args: protocol.SemanticDiagnosticsSyncRequestArgs): Promise { return this._makeTssRequest('semanticDiagnosticsSync', args); } 140 | getSyntacticDiagnosticsSync(args: protocol.SyntacticDiagnosticsSyncRequestArgs): Promise { return this._makeTssRequest('syntacticDiagnosticsSync', args); } 141 | getSuggestionDiagnosticsSync(args: protocol.SuggestionDiagnosticsSyncRequestArgs): Promise { return this._makeTssRequest('suggestionDiagnosticsSync', args); } 142 | getCodeFixes(args: protocol.CodeFixRequestArgs): Promise { return this._makeTssRequest('getCodeFixes', args); } 143 | getApplicableRefactors(args: protocol.GetApplicableRefactorsRequestArgs): Promise { return this._makeTssRequest('getApplicableRefactors', args); } 144 | getSupportedCodeFixes(): Promise { return this._makeTssRequest('getSupportedCodeFixes', null); } 145 | getCombinedCodeFix(args: protocol.GetCombinedCodeFixRequestArgs): Promise { return this._makeTssRequest('getCombinedCodeFix', args); } 146 | getOrganizedImports(args: protocol.OrganizeImportsRequestArgs): Promise { return this._makeTssRequest('organizeImports', args); } 147 | getProjectError(args: protocol.GeterrForProjectRequestArgs): void { this._makeTssRequest('geterrForProject', args) } 148 | getEditsForFileRename(args: protocol.GetEditsForFileRenameRequestArgs): Promise { return this._makeTssRequest('getEditsForFileRename', args) } 149 | 150 | // Server communication 151 | _makeTssRequest(commandName: string, args?: any): Promise { 152 | const seq = this._seqNumber++; 153 | const payload = { 154 | seq, 155 | type: 'request', 156 | arguments: args, 157 | command: commandName 158 | }; 159 | const ret = this.createDeferredPromise(); 160 | this._seqToPromises[seq] = ret; 161 | this.serverHandle.stdin.write(JSON.stringify(payload) + EOL); 162 | return ret.promise; 163 | } 164 | _makeNoResponseRequest(commandName?: string, args?: any) { 165 | const seq = this._seqNumber++; 166 | const payload = { 167 | seq, 168 | type: 'request', 169 | arguments: args 170 | }; 171 | if (commandName) { 172 | payload['command'] = commandName; 173 | } 174 | this.serverHandle.stdin.write(JSON.stringify(payload) + EOL); 175 | } 176 | parseResponse(returnedData: string): void { 177 | const response = JSON.parse(returnedData); 178 | // console.warn(returnedData) 179 | const seq = response.request_seq; 180 | const success = response.success; 181 | if (typeof seq === 'number') { 182 | if (success) { 183 | // console.warn(JSON.stringify(response)) 184 | this._seqToPromises[seq].resolve(response.body); 185 | } else { 186 | this._seqToPromises[seq].reject(response.message); 187 | } 188 | } else { 189 | // If a sequence wasn't specified, it might be a call that returns multiple results 190 | // Like 'geterr' - returns both semanticDiag and syntaxDiag 191 | if (response.type && response.type === 'event') { 192 | if (response.event && response.event === 'telemetry') { 193 | } 194 | if (response.event && response.event === 'projectsUpdatedInBackground') { 195 | // console.warn('projectsUpdatedInBackground: ', JSON.stringify(response.body)) 196 | } 197 | if (response.event && response.event === 'projectLoadingFinish') { 198 | this.emit('projectLoadingFinish') 199 | } 200 | if (response.event && (response.event === 'semanticDiag' || response.event === 'syntaxDiag' || response.event === 'suggestionDiag')) { 201 | this.getErrRes.push(response.body); 202 | } 203 | if (response.event && response.event === 'requestCompleted') { 204 | this.getErrCompleted() 205 | } 206 | } 207 | } 208 | } 209 | getErrCompleted() { 210 | this.emit('getErrCompleted', this.getErrRes) 211 | this.getErrRes = []; 212 | } 213 | createDeferredPromise(): any { 214 | let resolve: Function; 215 | let reject: Function; 216 | const promise = new Promise((res, rej) => { 217 | resolve = res; 218 | reject = rej; 219 | }); 220 | return { 221 | resolve, 222 | reject, 223 | promise 224 | }; 225 | } 226 | } 227 | 228 | export const TSServer = new Client(); 229 | -------------------------------------------------------------------------------- /rplugin/node/nvim_typescript/src/codeActions.ts: -------------------------------------------------------------------------------- 1 | import { Neovim } from 'neovim'; 2 | import { FileCodeEdits, CodeAction, CodeEdit } from 'typescript/lib/protocol'; 3 | import { padString } from './utils'; 4 | 5 | const leadingNewLineRexeg = /^\n/; 6 | const leadingAndTrailingNewLineRegex = /^\n|\n$/; 7 | 8 | export async function promptForSelection(options: CodeAction[], nvim: Neovim): Promise { 9 | const changeDescriptions = options.map(change => change.description); 10 | const candidates = changeDescriptions.map((change, idx) => `${idx + 1}: ${change}`); 11 | return new Promise(async (res, rej) => { 12 | const input = await nvim.call('inputlist', [['nvim-ts: Please Select from the following options:', ...candidates]]); 13 | if (!input) return rej('Nothing selected'); 14 | if (parseInt(input) > options.length) return rej('Not a valid options'); 15 | return res(options[parseInt(input)-1].changes); 16 | }); 17 | } 18 | export async function applyCodeFixes(fixes: ReadonlyArray, nvim: Neovim) { 19 | const cursorPos = await nvim.window.cursor; 20 | let commands = []; 21 | for (let fix of fixes) { 22 | for (let textChange of fix.textChanges.reverse()) { 23 | 24 | // SAME LINE EDIT 25 | // inserting new text or modifying a line 26 | if (textChange.start.line === textChange.end.line) { 27 | // MAKE EDIT AT THE START OF THE LINE 28 | if (textChange.start.offset === 1 && textChange.start.offset === textChange.end.offset) { 29 | commands.concat(await sameLineInsertEdit(textChange, nvim)); 30 | } 31 | // EDIT HAS NEWLINE 32 | else if (textChange.newText.match(leadingNewLineRexeg)) { 33 | commands.concat(await sameLineNewLinesEdit(textChange, nvim)); 34 | } 35 | // EDIT IS SOMEWHERE IN A LINE 36 | else { 37 | commands.concat(await midLineEdit(nvim, textChange)) 38 | } 39 | } 40 | // DIFFERENT LINE EDIT 41 | else { 42 | commands.concat(await spanLineEdit(textChange, nvim)); 43 | } 44 | } 45 | } 46 | await nvim.callAtomic(commands) 47 | // .catch(err => console.warn("err", err)); 48 | nvim.window.cursor = cursorPos; 49 | 50 | } 51 | async function sameLineInsertEdit(fix: CodeEdit, nvim: Neovim) { 52 | let newText = fix.newText.replace(leadingAndTrailingNewLineRegex, ''); 53 | const buffer = await nvim.buffer; 54 | 55 | let tsVersion = await nvim.call('TSGetVersion'); 56 | if (tsVersion.major < 3) { 57 | newText = newText.replace(/(\.\.\/)*node_modules\//, ''); 58 | } 59 | const textToArray = newText.split('\n'); 60 | 61 | return await buffer.insert(textToArray, fix.start.line - 1); 62 | }; 63 | async function sameLineNewLinesEdit(fix: CodeEdit, nvim: Neovim) { 64 | const buffer = await nvim.buffer; 65 | const textArray = fix.newText.split('\n').filter(e => e !== ''); 66 | return await buffer.insert(textArray, fix.start.line); 67 | }; 68 | async function spanLineEdit(fix: CodeEdit, nvim: Neovim): Promise> { 69 | // Code fix spans multiple lines 70 | // Chances are this is removing text. 71 | // Need to confirm though 72 | const commands: any[] = []; 73 | const buffer = await nvim.buffer; 74 | 75 | if (!fix.newText) { 76 | // No New text, we're removing something prob 77 | commands.push(await buffer.remove(fix.start.line - 1, fix.end.line - 1, true)); 78 | } 79 | else { 80 | // There is new text, let's just call set lines 81 | const text = fix.newText.split('\n').filter(e => e.trim() != ''); 82 | if (fix.start.offset > 0) { 83 | text[0] = padString(text[0], fix.start.offset - 1); 84 | } 85 | commands.push(await buffer.setLines(text, { start: fix.start.line - 1, end: fix.end.line, strictIndexing: true })); 86 | } 87 | 88 | return commands; 89 | }; 90 | async function midLineEdit(nvim: Neovim, textChange: CodeEdit) { 91 | let commands = []; 92 | const startLine = await nvim.buffer.getLines({ start: textChange.start.line - 1, end: textChange.start.line, strictIndexing: true }); 93 | const endLine = await nvim.buffer.getLines({ start: textChange.end.line - 1, end: textChange.end.line, strictIndexing: true }); 94 | const addingTrailingComma = textChange.newText.match(/^,$/) ? true : false; 95 | const lineAlreadyHasTrailingComma = startLine[0].match(/^.*,\s*$/) ? true : false; 96 | const preSpan = startLine[0].substring(0, textChange.start.offset - 1); 97 | const postSpan = endLine[0].substring(textChange.end.offset - 1); 98 | const repList = `${preSpan}${textChange.newText}${postSpan}`.split('\n'); 99 | let count = textChange.start.line; 100 | 101 | repList.forEach(async (line) => { 102 | if (count <= textChange.end.line) { 103 | if (addingTrailingComma && lineAlreadyHasTrailingComma) { 104 | // console.warn('LINE HAS A COMMA'); 105 | return; 106 | } 107 | commands.push(nvim.buffer.setLines(line, { start: count - 1, end: count, strictIndexing: true })); 108 | } 109 | else { 110 | commands.push(nvim.buffer.insert(line, count)); 111 | } 112 | count += 1; 113 | }); 114 | return commands; 115 | } 116 | 117 | -------------------------------------------------------------------------------- /rplugin/node/nvim_typescript/src/diagnostic.ts: -------------------------------------------------------------------------------- 1 | import { Neovim } from 'neovim'; 2 | import { Diagnostic } from 'typescript/lib/protocol'; 3 | import { createLocList, getBufferFromFile } from './utils'; 4 | 5 | interface SignStoreSign extends Diagnostic { 6 | id: number; 7 | } 8 | const name = 'TSDiagnostics'; 9 | class DiagnosticProvider { 10 | public signStore: Array<{ file: string; signs: Array }> = []; 11 | public nvim: Neovim; 12 | public signID = 1; 13 | private diagnosticSigns = []; 14 | private namespaceId: number; 15 | 16 | async defineSigns(defaults: any) { 17 | this.diagnosticSigns = defaults; 18 | return Promise.all( 19 | defaults.map(async (sign: any) => { 20 | let name = Object.keys(sign)[0]; 21 | let data = sign[name]; 22 | // await this.nvim.command(`sign define ${name} text=${data.signText} texthl=${data.signTexthl}`) 23 | await this.nvim.call('sign_define', [name, { text: data.signText, texthl: data.signTexthl }]); 24 | }) 25 | ); 26 | } 27 | async createNamespace() { 28 | this.namespaceId = await this.nvim.createNamespace(name) 29 | } 30 | async placeSigns(incomingSigns: Diagnostic[], file: string) { 31 | const locList = []; 32 | 33 | // Get the current file 34 | if (!this.signStore.find(entry => entry.file === file)) { 35 | this.signStore.push({ file, signs: [] }) 36 | } 37 | const current = this.signStore.find(entry => entry.file === file); 38 | 39 | // Clear current sings for now. 40 | await this.clearSigns(current); 41 | 42 | // Normalize signs 43 | const normSigns = this.normalizeSigns(incomingSigns); 44 | current.signs = []; 45 | current.signs = normSigns; 46 | // console.warn(JSON.stringify(current.signs)) 47 | // Set buffer var for airline 48 | await this.nvim.buffer.setVar('nvim_typescript_diagnostic_info', current.signs); 49 | // console.warn("NOW SETTING SIGN") 50 | await Promise.all( 51 | current.signs.map(async sign => { 52 | await this.nvim.call('sign_place', [sign.id, name, `TS${sign.category}`, current.file, { lnum: sign.start.line, priority: 90 }]); 53 | locList.push({ filename: current.file, lnum: sign.start.line, col: sign.start.offset, text: sign.text, code: sign.code, type: sign.category[0].toUpperCase() }); 54 | }) 55 | ) 56 | 57 | await this.highlightLine(current); 58 | createLocList(this.nvim, locList, 'Errors', false); 59 | } 60 | normalizeSigns(signs: Diagnostic[]) { 61 | return signs.map(sign => ({ ...sign, id: this.signID++ })); 62 | } 63 | async clearSigns(current: { file: string, signs: SignStoreSign[] }) { 64 | if (current.signs.length > 0) { 65 | await this.clearHighlight(); 66 | await this.nvim.call('sign_unplace', [name, { buffer: current.file }]) 67 | } 68 | } 69 | async clearHighlight() { 70 | const buffer = await this.nvim.buffer; 71 | buffer.clearNamespace({ 72 | nsId: this.namespaceId, 73 | lineStart: 0, 74 | lineEnd: -1, 75 | }) 76 | } 77 | getSign(file: string, line: number, offset: number): SignStoreSign { 78 | const current = this.signStore.find(entry => entry.file === file); 79 | if (current) { 80 | let signs = current.signs; 81 | for (let i = 0; i < signs.length; i++) { 82 | if ( 83 | signs[i].start.line === line && 84 | signs[i].start.offset <= offset && 85 | signs[i].end.offset > offset 86 | ) { 87 | return signs[i]; 88 | } 89 | } 90 | } 91 | } 92 | async highlightLine(current: { file: string, signs: SignStoreSign[] }) { 93 | const buffer = await getBufferFromFile(this.nvim, current.file); 94 | await Promise.all([ 95 | current.signs.map(async sign => { 96 | let hlGroup = this.getSignHighlight(sign); 97 | await buffer.addHighlight({ 98 | srcId: this.namespaceId, 99 | hlGroup, 100 | line: sign.start.line - 1, 101 | colStart: sign.start.offset - 1, 102 | colEnd: sign.end.offset - 1 103 | }); 104 | }) 105 | ]) 106 | } 107 | getSignHighlight(sign: SignStoreSign) { 108 | for (let entry of this.diagnosticSigns) { 109 | let name = Object.keys(entry)[0]; 110 | let data = entry[name]; 111 | if (name === `TS${sign.category}`) { 112 | return data.texthl; 113 | } 114 | } 115 | } 116 | async clearAllHighlights(file: string) { 117 | const current = this.signStore.find(entry => entry.file === file); 118 | // console.log(current) 119 | await this.clearHighlight(); 120 | } 121 | } 122 | export const DiagnosticHost = new DiagnosticProvider(); 123 | -------------------------------------------------------------------------------- /rplugin/node/nvim_typescript/src/floatingWindow.ts: -------------------------------------------------------------------------------- 1 | import { Buffer, Neovim, Window } from 'neovim'; 2 | import { padString, print } from './utils'; 3 | import { OpenWindowOptions } from 'neovim/lib/api/Neovim'; 4 | 5 | const col = async (nvim: Neovim) => await nvim.window.width 6 | const createBuffer = async (nvim: Neovim) => await nvim.createBuffer(false, true); 7 | 8 | function processHoverText(symbol: protocol.QuickInfoResponseBody){ 9 | const text = symbol.displayString.split(/\r|\n/g).map(e => padString(e, 1, true)); 10 | let sepLength = text[0].length; 11 | if (symbol.documentation !== '') { 12 | const docs = symbol.documentation.split(/\r|\n/g).map(e => padString(e, 1, true)); 13 | let separatorLength = docs.reduce((a, b) => a.length > b.length ? a : b).length; 14 | if (separatorLength > sepLength) { 15 | sepLength = separatorLength 16 | } 17 | const sep = '-'.repeat(sepLength); 18 | text.push(sep, ...docs) 19 | } 20 | if (symbol.tags && symbol.tags.length > 0) { 21 | let tags = [] 22 | 23 | symbol.tags.forEach(tag => { 24 | if (tag.name) { 25 | tags.push(`${padString('@' + tag.name, 1)}`); 26 | } 27 | if (tag.text) { 28 | tags.push(...tag.text.split(/\r|\n/g).filter(Boolean).map(e => padString(e, 1))) 29 | } 30 | }) 31 | 32 | let separatorLength = tags.reduce((a, b) => a.length > b.length ? a : b).length; 33 | if (separatorLength > sepLength) { 34 | sepLength = separatorLength 35 | } 36 | const sep = '—'.repeat(sepLength); 37 | text.push(sep, ...tags) 38 | } 39 | return text; 40 | } 41 | const processErrorText = (symbol: protocol.Diagnostic) => { 42 | return symbol.text.split('\n').map((e: string, idx: number) => { 43 | if (idx === 0) { 44 | return padString( 45 | `${symbol.source ? '[' + symbol.source + ']: ' : ''}${e}`, 46 | 1 47 | ); 48 | } 49 | return padString(e, 1); 50 | }); 51 | } 52 | async function popupWindowSize(nvim: Neovim, contents: string[]): Promise<[number, number]> { 53 | let width = 0; 54 | let colsize = await col(nvim); 55 | let max_width = Math.min(colsize, 20) 56 | let max_height = 20; 57 | let height = 0; 58 | for (let line of contents) { 59 | let lw = line.length; 60 | if (lw > width) { 61 | if (lw > max_width) { 62 | width = max_width; 63 | } 64 | width = lw; 65 | } 66 | height += 1; 67 | if (height > max_height) { 68 | height = max_height; 69 | } 70 | } 71 | 72 | return [Math.round(width), Math.round(height)]; 73 | }; 74 | async function getWindowPos(nvim: Neovim, width: number, height: number, errorStart: protocol.Location): Promise { 75 | const [line, offset] = await nvim.window.cursor; 76 | const lastLine = await nvim.window.height; 77 | const coluns = await nvim.window.width; 78 | 79 | let vert = ''; 80 | let hor = ''; 81 | let row = 0; 82 | let col = 0; 83 | if (line + height <= lastLine) { 84 | vert = 'N'; 85 | row = 1; 86 | } else { 87 | vert = 'S'; 88 | row = 0; 89 | } 90 | if (offset + width <= coluns) { 91 | hor = 'W'; 92 | } else { 93 | hor = 'E'; 94 | } 95 | col = errorStart.offset - offset - 1; 96 | const anchor = vert + hor; 97 | 98 | return { 99 | relative: 'cursor', 100 | anchor: anchor, 101 | row: row, 102 | col: col 103 | }; 104 | }; 105 | async function lockBuffer(window: Window, buffer: Buffer) { 106 | return Promise.all([ 107 | window.setOption('winhl', 'Normal:nvimTypescriptPopupNormal,EndOfBuffer:nvimTypescriptEndOfBuffer'), 108 | buffer.setOption('filetype', 'markdown'), 109 | buffer.setOption('buftype', 'nofile'), 110 | buffer.setOption('bufhidden', 'wipe'), 111 | window.setOption('wrap', true), 112 | window.setOption('foldenable', false), 113 | window.setOption('spell', false), 114 | window.setOption('listchars', ''), 115 | buffer.clearNamespace({ nsId: -1 }), 116 | buffer.setOption('modified', false), 117 | buffer.setOption('modifiable', false), 118 | ]); 119 | }; 120 | async function unlockBuffer(buffer: Buffer) { 121 | return Promise.all([ 122 | buffer.setOption('modifiable', true), 123 | ]); 124 | }; 125 | export async function createFloatingWindow(nvim: Neovim, symbol: any, type: "Error" | "Type"): Promise { 126 | const buffer = (await createBuffer(nvim)) as Buffer; 127 | let text: string[]; 128 | if (type === "Error") { 129 | text = processErrorText(symbol); 130 | } 131 | if (type === "Type") { 132 | text = processHoverText(symbol); 133 | } 134 | const [width, height] = await popupWindowSize(nvim, text); 135 | const windowPos = await getWindowPos(nvim, width, height, symbol.start); 136 | const options: OpenWindowOptions = { ...windowPos, height, width, focusable: true, style: 'minimal'}; 137 | await buffer.setLines(text, { start: 0, end: -1, strictIndexing: false }) 138 | const floatingWindow = (await nvim.openWindow( 139 | buffer, 140 | false, 141 | options 142 | )) as Window; 143 | await lockBuffer(floatingWindow, buffer); 144 | return floatingWindow; 145 | }; 146 | 147 | export async function updateFloatingWindow(nvim: Neovim, window: Window, symbol: any, type: "Error" | "Type"): Promise { 148 | const refText = await window.buffer.lines 149 | let text: string[]; 150 | if (type === 'Error') { 151 | text = processErrorText(symbol); 152 | } else { 153 | text = processHoverText(symbol); 154 | } 155 | let sep = '-'.repeat(Math.max(text.reduce((a, b) => a.length > b.length ? a : b).length, refText.reduce((a, b) => a.length > b.length ? a : b).length)) 156 | 157 | if (checkArrays(refText, text)) { 158 | return window 159 | } 160 | let newText: string[]; 161 | if (type === 'Error') { 162 | newText = [...refText, sep, ...text]; 163 | } else { 164 | newText = [...text, sep, ...refText]; 165 | } 166 | const [width, height] = await popupWindowSize(nvim, newText); 167 | console.warn(width, height) 168 | await unlockBuffer(await window.buffer) 169 | await window.buffer.replace(newText, 0) 170 | await nvim.windowConfig(window, { width, height }); 171 | await lockBuffer(window, await window.buffer); 172 | return window 173 | } 174 | function checkArrays(arrA: string[], arrB: string[]) { return arrB.map(entry => arrA.includes(entry))[0] }; 175 | -------------------------------------------------------------------------------- /rplugin/node/nvim_typescript/src/index.ts: -------------------------------------------------------------------------------- 1 | import { statSync, writeFileSync } from 'fs'; 2 | import { debounce } from 'lodash-es'; 3 | import { Neovim, NvimPlugin, Window } from 'neovim'; 4 | import { fileSync } from 'tmp'; 5 | import protocol from 'typescript/lib/protocol'; 6 | import { TSServer } from './client'; 7 | import { applyCodeFixes, promptForSelection } from './codeActions'; 8 | import { DiagnosticHost } from './diagnostic'; 9 | import { 10 | createFloatingWindow, 11 | updateFloatingWindow, 12 | } from './floatingWindow'; 13 | import { 14 | convertDetailEntry, 15 | convertEntry, 16 | createLocList, 17 | createQuickFixList, 18 | getKind, 19 | isRenameSuccess, 20 | printHighlight, 21 | reduceByPrefix, 22 | triggerChar, 23 | trim, 24 | truncateMsg, 25 | processErrors, 26 | } from './utils'; 27 | 28 | module.exports = (plugin: NvimPlugin) => { 29 | const nvim: Neovim = plugin.nvim; 30 | 31 | const client = TSServer; 32 | const diagnosticHost = DiagnosticHost; 33 | 34 | let windowRef: Window = null; 35 | let maxCompletion: number = 50; 36 | let expandSnippet: boolean = false; 37 | let enableDiagnostics: boolean = false; 38 | let quietStartup: boolean = false; 39 | let openFiles = []; 40 | 41 | let suggestionsEnabled: any = false; 42 | let updateTime: number = 0; 43 | let windowLock = false; 44 | 45 | const showInWindow = async(symbol: any, type: "Error" | "Type") => { 46 | if (windowLock) { 47 | await new Promise((resolve) => { 48 | const intervalId = setInterval(() => { 49 | if (!windowLock) { 50 | clearInterval(intervalId); 51 | resolve(); 52 | } 53 | }, 100); 54 | }); 55 | } 56 | windowLock = true; 57 | 58 | try { 59 | if (!windowRef) { 60 | windowRef = await createFloatingWindow(nvim, symbol, type) 61 | } else { 62 | windowRef = await updateFloatingWindow(nvim, windowRef, symbol, type); 63 | } 64 | } 65 | 66 | 67 | catch { 68 | // Probably window was closed by the user 69 | windowRef = null; 70 | await showInWindow(symbol, type); 71 | } 72 | windowLock = false; 73 | } 74 | 75 | // Utils 76 | const init = async () => { 77 | diagnosticHost.nvim = nvim; 78 | await diagnosticHost.createNamespace(); 79 | const [ 80 | maxCompletionVar, 81 | serverPathVar, 82 | serverOptionsVar, 83 | defaultSignsVar, 84 | expandSnippetVar, 85 | enableDiagnosticsVar, 86 | quietStartupVar, 87 | channelIDVar, 88 | suggestionsEnabledVar, 89 | redrawTimeVar, 90 | ] = await Promise.all([ 91 | nvim.getVar('nvim_typescript#max_completion_detail'), 92 | nvim.getVar('nvim_typescript#server_path'), 93 | nvim.getVar('nvim_typescript#server_options'), 94 | nvim.getVar('nvim_typescript#default_signs'), 95 | nvim.getVar('nvim_typescript#expand_snippet'), 96 | nvim.getVar('nvim_typescript#diagnostics_enable'), 97 | nvim.getVar('nvim_typescript#quiet_startup'), 98 | nvim.apiInfo, 99 | nvim.getVar('nvim_typescript#suggestions_enabled'), 100 | nvim.getOption('redrawtime') as Promise, 101 | ]); 102 | 103 | updateTime = parseInt(redrawTimeVar as string); 104 | await nvim.setVar('nvim_typescript#channel_id', channelIDVar[0]); 105 | enableDiagnostics = !!enableDiagnosticsVar; 106 | quietStartup = !!quietStartupVar; 107 | maxCompletion = parseFloat(maxCompletionVar as string); 108 | expandSnippet = expandSnippetVar as boolean; 109 | client.setServerPath(serverPathVar as string); 110 | client.serverOptions = serverOptionsVar as string[]; 111 | suggestionsEnabled = suggestionsEnabledVar; 112 | 113 | await diagnosticHost.defineSigns(defaultSignsVar); 114 | client.setTSConfigVersion(); 115 | 116 | // nvim.on('changedtick', () => printHighlight(nvim,'test')); 117 | client.on('projectLoadingFinish', async () => { 118 | if (!quietStartup) { 119 | await printHighlight(nvim, `Server started`, 'MoreMsg'); 120 | } 121 | }); 122 | client.on( 123 | 'getErrCompleted', 124 | async (res) => await processProjectErrorRes(res) 125 | ); 126 | }; 127 | const reloadFile = async () => { 128 | const file = await getCurrentFile(); 129 | const buffer = await nvim.buffer; 130 | const bufContent = (await buffer.getOption('endofline')) 131 | ? [...(await buffer.lines), '\n'] 132 | : await buffer.lines; 133 | 134 | const contents = bufContent.join('\n'); 135 | 136 | const temp = fileSync(); 137 | writeFileSync(temp.name, contents, 'utf8'); 138 | await client.updateFile({ file, tmpfile: temp.name }); 139 | temp.removeCallback(); 140 | }; 141 | const getCurrentFile = async () => await nvim.buffer.name; 142 | const getCursorPos = async () => await nvim.window.cursor; 143 | const getCommonData = async () => { 144 | let file = await getCurrentFile(); 145 | let cursorPos = await getCursorPos(); 146 | return { 147 | file, 148 | line: cursorPos[0], 149 | offset: cursorPos[1] + 1, 150 | }; 151 | }; 152 | 153 | const tsstart = async () => { 154 | await init(); 155 | if (!quietStartup) { 156 | await printHighlight(nvim, `Starting Server...`, 'Question'); 157 | } 158 | client.startServer(); 159 | await onBufEnter(); 160 | }; 161 | const tsstop = async () => { 162 | if (client.serverHandle != null) { 163 | client.stopServer(); 164 | await printHighlight(nvim, `Server stopped`, 'ErrorMsg'); 165 | } 166 | }; 167 | const reloadProject = async () => { 168 | client.reloadProject(); 169 | await getDiagnostics(); 170 | }; 171 | 172 | 173 | const getSematicErrors = async (file: string) => await client.getSemanticDiagnosticsSync({ file }); 174 | const getSyntaxErrors = async (file: string) => await client.getSyntacticDiagnosticsSync({ file }); 175 | const getSuggested = async (file: string) => await client.getSuggestionDiagnosticsSync({ file }); 176 | const openBufferOrWindow = async ( file: string, lineNumber: number, offset: number) => { 177 | const currentFile = await getCurrentFile(); 178 | const fileIsAlreadyFocused = file === currentFile; 179 | 180 | if (fileIsAlreadyFocused) { 181 | await nvim.command(`call cursor(${lineNumber}, ${offset})`); 182 | return; 183 | } 184 | const windowNumber = await nvim.call('bufwinnr', file); 185 | if (windowNumber != -1) { 186 | await nvim.command(`${windowNumber}wincmd w`); 187 | } else { 188 | await nvim.command(`e ${file}`); 189 | } 190 | await nvim.command(`call cursor(${lineNumber}, ${offset})`); 191 | }; 192 | 193 | const tsBuild = async () => { 194 | await printHighlight(nvim, `Building...`, 'Question'); 195 | await reloadFile(); 196 | const file = await getCurrentFile(); 197 | client.getProjectError({ file, delay: 0 }); 198 | }; 199 | const processProjectErrorRes = async (res: any[]) => { 200 | const fmtErrors = processErrors(res); 201 | await printHighlight(nvim, `Build done`, 'MoreMsg'); 202 | await createQuickFixList(nvim, fmtErrors, 'Errors'); 203 | }; 204 | 205 | //Server Utils 206 | const tsGetServerPath = () => client.serverPath; 207 | const tsGetVersion = () => client.tsConfigVersion; 208 | 209 | //Buffer Events 210 | const onBufEnter = async (arg?: [string]) => { 211 | if (client.serverHandle == null) { 212 | await tsstart(); 213 | } else { 214 | const file = await getCurrentFile(); 215 | if (arg && arg[0] !== file) { 216 | return; 217 | } 218 | if (!openFiles.includes(file)) { 219 | openFiles.push(file); 220 | const buffer = await nvim.buffer; 221 | const bufContent = (await buffer.getOption('endofline')) 222 | ? [...(await buffer.lines), '\n'] 223 | : await buffer.lines; 224 | const fileContent = bufContent.join('\n'); 225 | client.openFile({ file, fileContent }); 226 | if (enableDiagnostics) { 227 | await closeFloatingWindow(); 228 | await getDiagnostics(); 229 | nvim.buffer.listen( 230 | 'lines', 231 | debounce(() => { 232 | // if(!doingCompletion){ 233 | getDiagnostics(); 234 | // } 235 | }, 1000) 236 | ); 237 | } 238 | } else { 239 | await closeFloatingWindow(); 240 | await getDiagnostics(); 241 | } 242 | // console.warn('OPENED FILES', JSON.stringify(openFiles)); 243 | } 244 | }; 245 | const onBufSave = async () => await reloadFile(); 246 | const onBufLeave = (arg: [string]) => { 247 | const [file] = arg; 248 | if (client.serverHandle && file) { 249 | openFiles = openFiles.filter((e) => e != file); 250 | client.closeFile({ file }); 251 | } 252 | }; 253 | 254 | const tsGetType = async () => { 255 | await reloadFile(); 256 | const args = await getCommonData(); 257 | try { 258 | const typeInfo = await client.quickInfo(args); 259 | if (Object.getOwnPropertyNames(typeInfo).length > 0) { 260 | try { 261 | showInWindow(typeInfo, 'Type') 262 | } catch (e) { 263 | await printHighlight(nvim, await truncateMsg(nvim, typeInfo.displayString), 'MoreMsg', 'Function'); 264 | } 265 | } 266 | } catch (err) { 267 | // console.warn('in catch', JSON.stringify(err)); 268 | } 269 | }; 270 | const tsTypeDef = async () => { 271 | await reloadFile(); 272 | const args = await getCommonData(); 273 | const typeDefRes = await client.getTypeDef(args); 274 | 275 | if (typeDefRes && typeDefRes.length > 0) { 276 | const defFile = typeDefRes[0].file; 277 | const defLine = typeDefRes[0].start.line; 278 | const defOffset = typeDefRes[0].start.offset; 279 | await openBufferOrWindow(defFile, defLine, defOffset); 280 | } 281 | }; 282 | 283 | const tsImport = async () => { 284 | await printHighlight( 285 | nvim, 286 | 'TSImport is depreciated, please use TSGetCodeFix' 287 | ); 288 | await getCodeFix(); 289 | }; 290 | 291 | const tsGetDef = async () => { 292 | const definition = await getDefFunc(); 293 | if (definition) { 294 | const defFile = definition[0].file; 295 | const defLine = definition[0].start.line; 296 | const defOffset = definition[0].start.offset; 297 | await openBufferOrWindow(defFile, defLine, defOffset); 298 | } 299 | }; 300 | 301 | const getDefPreview = async () => { 302 | const definition = await getDefFunc(); 303 | if (definition) { 304 | await nvim.command( 305 | `silent pedit! +${definition[0].start.line} ${definition[0].file}` 306 | ); 307 | await nvim.command('wincmd P'); 308 | } 309 | }; 310 | const getDefFunc = async () => { 311 | await reloadFile(); 312 | const args = await getCommonData(); 313 | return client.getDef(args); 314 | }; 315 | 316 | const tsGetDoc = async () => { 317 | await reloadFile(); 318 | const args = await getCommonData(); 319 | const info = await client.quickInfo(args); 320 | if (info) { 321 | const displayString = info.displayString.split('\n'); 322 | const doc = info.documentation.split('\n'); 323 | const message = displayString.concat(doc); 324 | await printInSplit(message); 325 | } 326 | }; 327 | 328 | const tsRename = async (args: string[]) => { 329 | const symbol = await nvim.eval('expand("")'); 330 | 331 | let newName: string; 332 | 333 | if (args.length > 0) { 334 | newName = args[0]; 335 | } else { 336 | const input = await nvim.call('input', [`nvim-ts: rename to `, symbol]); 337 | if (!input) { 338 | await printHighlight(nvim, 'Rename canceled', 'ErrorMsg'); 339 | return; 340 | } else { 341 | newName = input; 342 | } 343 | } 344 | let changedFiles = []; 345 | await reloadFile(); 346 | const renameArgs = await getCommonData(); 347 | const buffNum = await nvim.call('bufnr', '%'); 348 | const renameResults = await client.renameSymbol({ 349 | ...renameArgs, 350 | findInComments: false, 351 | findInStrings: false, 352 | }); 353 | 354 | if (renameResults) { 355 | if (isRenameSuccess(renameResults.info)) { 356 | let changeCount = 0; 357 | for (let fileLocation of renameResults.locs) { 358 | let defFile = fileLocation.file; 359 | 360 | if (defFile !== renameArgs.file) { 361 | await nvim.command(`keepjumps keepalt edit ${defFile}`); 362 | } 363 | const commands = []; 364 | for (let rename of fileLocation.locs.reverse()) { 365 | let { line, offset } = rename.start; 366 | const editLine = await nvim.buffer.getLines({ 367 | start: line - 1, 368 | end: line, 369 | strictIndexing: true, 370 | }); 371 | 372 | const newLine = editLine[0].replace(symbol as string, newName); 373 | commands.concat( 374 | await nvim.buffer.setLines(newLine, { 375 | start: line - 1, 376 | end: line, 377 | strictIndexing: true, 378 | }) 379 | ); 380 | 381 | changedFiles.push({ 382 | filename: defFile, 383 | lnum: line, 384 | col: offset, 385 | text: `Replaced ${symbol} with ${newName}`, 386 | }); 387 | 388 | changeCount += 1; 389 | } 390 | await nvim.callAtomic(commands); 391 | } 392 | 393 | await nvim.command(`buffer ${buffNum}`); 394 | await nvim.call('cursor', [ 395 | renameResults.info.triggerSpan.start.line, 396 | renameResults.info.triggerSpan.start.offset, 397 | ]); 398 | 399 | await createQuickFixList(nvim, changedFiles, 'Renames', false); 400 | await printHighlight( 401 | nvim, 402 | `Replaced ${changeCount} in ${renameResults.locs.length} files` 403 | ); 404 | await getDiagnostics(); 405 | } else { 406 | printHighlight( 407 | nvim, 408 | renameResults.info.localizedErrorMessage, 409 | 'ErrorMsg' 410 | ); 411 | } 412 | } 413 | }; 414 | 415 | const tsGetSig = async () => { 416 | // console.warn("IMHERE") 417 | await reloadFile(); 418 | 419 | // const file = await getCurrentFile(); 420 | // const [line, offset] = await getCursorPos(); 421 | // const info = await client.getSignature({ file, line, offset }); 422 | // console.warn('INFO', info); 423 | 424 | // const signatureHelpItems = info.items.map(item => { 425 | // return { 426 | // variableArguments: item.isVariadic, 427 | // prefix: convertToDisplayString(item.prefixDisplayParts), 428 | // suffix: convertToDisplayString(item.suffixDisplayParts), 429 | // separator: convertToDisplayString(item.separatorDisplayParts), 430 | // parameters: item.parameters.map(p => { 431 | // return { 432 | // text: convertToDisplayString(p.displayParts), 433 | // documentation: convertToDisplayString(p.documentation) 434 | // }; 435 | // }) 436 | // }; 437 | // }); 438 | // const params = getParams(signatureHelpItems[0].parameters, signatureHelpItems[0].separator); 439 | // printHighlight(nvim, params); 440 | // } 441 | // catch (err) { 442 | // console.warn('in catch', JSON.stringify(err)); 443 | // } 444 | }; 445 | 446 | const tsRefs = async () => { 447 | await reloadFile(); 448 | const args = await getCommonData(); 449 | const symbolRefRes = await client.getSymbolRefs(args); 450 | 451 | if (!symbolRefRes || (symbolRefRes && symbolRefRes.refs.length === 0)) { 452 | printHighlight(nvim, 'References not found', 'ErrorMsg'); 453 | return; 454 | } 455 | 456 | const refList = symbolRefRes.refs; 457 | const locationList = refList.map((ref) => { 458 | return { 459 | filename: ref.file, 460 | lnum: ref.start.line, 461 | col: ref.start.offset, 462 | text: trim(ref.lineText), 463 | }; 464 | }); 465 | // Uses QuickFix list as refs can span multiple files. QFList is better. 466 | createQuickFixList(nvim, locationList, 'References', true); 467 | }; 468 | 469 | const tsEditConfig = async () => { 470 | await reloadFile(); 471 | const projectInfo = await getProjectInfoFunc(); 472 | if (projectInfo) { 473 | if (statSync(projectInfo.configFileName).isFile()) { 474 | nvim.command(`e ${projectInfo.configFileName}`); 475 | } else { 476 | printHighlight( 477 | nvim, 478 | `Can't edit config, in an inferred project`, 479 | 'ErrorMsg' 480 | ); 481 | } 482 | } 483 | }; 484 | 485 | const tsOmniFunc = async (args: [number, string]) => { 486 | await reloadFile(); 487 | if (!!args[0]) { 488 | let currentLine = await nvim.line; 489 | let [, col] = await getCursorPos(); 490 | const lineToCursor = currentLine.substring(0, col); 491 | let textMatch = await nvim.call('match', [lineToCursor, '\\k*$']); 492 | return textMatch; 493 | } else { 494 | // Args[1] is good. 495 | return await tsOmniComplete(args[1]); 496 | } 497 | }; 498 | const tsOmniComplete = async (args: string) => { 499 | await reloadFile(); 500 | let file = await getCurrentFile(); 501 | let cursorPos = await nvim.window.cursor; 502 | let line = cursorPos[0]; 503 | let prefix = args; 504 | let offset = cursorPos[1] + 1; 505 | 506 | // returns the detailed result as well as sets the vim var 507 | return complete( file, prefix, offset, line, 'nvim_typescript#completionRes'); 508 | }; 509 | const tsDeoplete = async (args: [string, number]) => { 510 | reloadFile(); 511 | let file = await getCurrentFile(); 512 | let cursorPos = await nvim.window.cursor; 513 | let line = cursorPos[0]; 514 | 515 | let [prefix, offset] = args; 516 | // sets the vim var, but doesn't need to return anything 517 | complete(file, prefix, offset, line, 'nvim_typescript#completion_res'); 518 | }; 519 | const complete = async ( file: string, prefix: string, offset: number, line: number, nvimVar: string) => { 520 | await closeFloatingWindow(); 521 | // console.warn('didClose', didClose); 522 | const currentLine = await nvim.line; 523 | let completeArgs: protocol.CompletionsRequestArgs = { 524 | file, 525 | line, 526 | offset, 527 | prefix, 528 | includeInsertTextCompletions: false, 529 | includeExternalModuleExports: false, 530 | }; 531 | if (client.isCurrentVersionHighter(300)) { 532 | completeArgs.triggerCharacter = triggerChar(currentLine); 533 | } 534 | 535 | let completions: any; 536 | if (client.isCurrentVersionHighter(300)) { 537 | try { 538 | let { isMemberCompletion, entries } = await client.getCompletions( completeArgs); 539 | // - global completions are sorted by TSServer so that `f` will return a wider set than `foo` 540 | // - member completions are however returned in a static bunch so that `foo.ba` will return 541 | // all members of foo regardless of the prefix. 542 | // - if there n > maxCompletions members of foo then the code will never make it to the detailed 543 | // completions 544 | // - lets run a regex on the completions so that as the user narrows down the range of possibilities 545 | // they will eventually see detailed completions for the member 546 | completions = isMemberCompletion && prefix ? reduceByPrefix(prefix, entries) : entries; 547 | 548 | } 549 | catch(e){ 550 | await nvim.setVar(nvimVar, []); 551 | return []; 552 | 553 | } 554 | } 555 | else { 556 | try { 557 | completions = await client.getCompletions(completeArgs); 558 | } 559 | catch(e){ 560 | 561 | await nvim.setVar(nvimVar, []); 562 | return []; 563 | } 564 | } 565 | if (completions.length > 0) { 566 | if (completions.length > maxCompletion) { 567 | let completionRes = await Promise.all( 568 | completions.map( 569 | async (entry: protocol.CompletionEntry) => 570 | await convertEntry(nvim, entry) 571 | ) 572 | ); 573 | await nvim.setVar(nvimVar, completionRes); 574 | return completionRes; 575 | } 576 | else { 577 | const entryNames = completions.map((v: { name: any }) => v.name); 578 | let detailedCompletions = await client.getCompletionDetails({ 579 | file, 580 | line, 581 | offset, 582 | entryNames, 583 | }); 584 | // console.warn(JSON.stringify(detailedCompletions)); 585 | let completionResDetailed = await Promise.all( 586 | detailedCompletions.map( 587 | async (entry) => 588 | await convertDetailEntry(nvim, entry, expandSnippet) 589 | ) 590 | ); 591 | 592 | await nvim.setVar(nvimVar, completionResDetailed); 593 | return completionResDetailed; 594 | } 595 | } 596 | else { 597 | await nvim.setVar(nvimVar, []); 598 | return []; 599 | } 600 | }; 601 | 602 | const tsGetdocsymbols = async () => { 603 | const file = await getCurrentFile(); 604 | const docSysmbols = await getDocSymbolsFunc(); 605 | let docSysmbolsLoc = []; 606 | const symbolList = docSysmbols.childItems; 607 | if (symbolList.length > 0) { 608 | for (let symbol of symbolList) { 609 | docSysmbolsLoc.push({ 610 | filename: file, 611 | lnum: symbol.spans[0].start.line, 612 | col: symbol.spans[0].start.offset, 613 | text: symbol.text, 614 | }); 615 | if (symbol.childItems && symbol.childItems.length > 0) { 616 | for (let childSymbol of symbol.childItems) { 617 | docSysmbolsLoc.push({ 618 | filename: file, 619 | lnum: childSymbol.spans[0].start.line, 620 | col: childSymbol.spans[0].start.offset, 621 | text: childSymbol.text, 622 | }); 623 | } 624 | } 625 | } 626 | createLocList(nvim, docSysmbolsLoc, 'Symbols'); 627 | } 628 | }; 629 | const getDocSymbolsFunc = async () => { 630 | await reloadFile(); 631 | const file = await getCurrentFile(); 632 | const symbols = await client.getDocumentSymbols({ file }); 633 | // console.warn(JSON.stringify(symbols)); 634 | return symbols; 635 | }; 636 | 637 | const getWorkspaceSymbols = async (args: any[]) => { 638 | await reloadFile(); 639 | const file = await getCurrentFile(); 640 | const funcArgs = [...args, file]; 641 | 642 | const results = await getWorkspaceSymbolsFunc(funcArgs); 643 | if (results) { 644 | await createLocList(nvim, results, 'WorkspaceSymbols'); 645 | } 646 | }; 647 | 648 | const getWorkspaceSymbolsFunc = async (args: any[]) => { 649 | const searchValue = args.length > 0 ? args[0] : ''; 650 | const maxResultCount = 50; 651 | const results = await client.getWorkspaceSymbols({ 652 | file: args[1], 653 | searchValue, 654 | maxResultCount, 655 | }); 656 | 657 | const symbolsRes = await Promise.all( 658 | results.map(async (symbol) => { 659 | return { 660 | filename: symbol.file, 661 | lnum: symbol.start.line, 662 | col: symbol.start.offset, 663 | text: `${await getKind(nvim, symbol.kind)}\t ${symbol.name}`, 664 | }; 665 | }) 666 | ); 667 | 668 | return symbolsRes; 669 | }; 670 | 671 | const organizeImports = async () => { 672 | await reloadFile(); 673 | const file = await getCurrentFile(); 674 | const scopes = await client.getOrganizedImports({ 675 | scope: { 676 | type: 'file', 677 | args: { file }, 678 | }, 679 | }); 680 | if (scopes) { 681 | await applyCodeFixes(scopes, nvim); 682 | } else { 683 | printHighlight(nvim, 'No changes needed'); 684 | } 685 | }; 686 | 687 | const getDiagnostics = async () => { 688 | if (enableDiagnostics) { 689 | // console.warn('GETTING DiagnosticHost'); 690 | await reloadFile(); 691 | const file = await getCurrentFile(); 692 | const sematicErrors = await getSematicErrors(file); 693 | const syntaxErrors = await getSyntaxErrors(file); 694 | let res = [...sematicErrors, ...syntaxErrors]; 695 | if (suggestionsEnabled) { 696 | const suggestionErrors = await getSuggested(file); 697 | res = [...res, ...suggestionErrors]; 698 | } 699 | await diagnosticHost.placeSigns(res, file); 700 | await closeFloatingWindow(); 701 | await handleCursorMoved(); 702 | } 703 | }; 704 | 705 | const getProjectInfoFunc = async () => { 706 | const file = await getCurrentFile(); 707 | return await client.getProjectInfo({ file, needFileNameList: true }); 708 | }; 709 | 710 | const closeFloatingWindow = async () => { 711 | try { 712 | await windowRef.close(true); 713 | windowRef = null 714 | } catch (error) {} 715 | return; 716 | }; 717 | 718 | const handleCursorMoved = async () => { 719 | const buftype = await nvim.eval('&buftype'); 720 | if (buftype !== '') return; 721 | 722 | const { file, line, offset } = await getCommonData(); 723 | const errorSign = diagnosticHost.getSign(file, line, offset); 724 | 725 | if (errorSign) { 726 | debounce(async () => { 727 | // print(nvim,`ERROR${JSON.stringify(errorSign)}`); 728 | await showInWindow(errorSign, 'Error'); 729 | }, updateTime + 200)(); 730 | } 731 | }; 732 | 733 | const getErrFull = async () => { 734 | const { file, line, offset } = await getCommonData(); 735 | const buftype = await nvim.eval('&buftype'); 736 | if (buftype !== '') return; 737 | const errorSign = diagnosticHost.getSign(file, line, offset); 738 | if (errorSign) { 739 | await printInSplit(errorSign.text, '__error__'); 740 | } 741 | }; 742 | 743 | const printInSplit = async ( message: string | string[], bufname = '__doc__') => { 744 | const buf: number = await nvim.call('bufnr', bufname); 745 | 746 | if (buf > 0) { 747 | const pageNr = await nvim.tabpage.number; 748 | const pageList: number[] = await nvim.call('tabpagebuflist', pageNr); 749 | const wi: number = await nvim.call(`index`, [pageList, buf]); 750 | if (wi > 0) { 751 | await nvim.command(`${wi + 1} wincmd w`); 752 | } else { 753 | await nvim.command(`sbuffer ${buf}`); 754 | } 755 | } else { 756 | await nvim.command('botright 10split __doc__'); 757 | } 758 | await nvim.callAtomic([ 759 | await nvim.buffer.setOption('modifiable', true), 760 | await nvim.command('sil normal! ggdG'), 761 | await nvim.command('resize 10'), 762 | await nvim.buffer.setOption('swapfile', false), 763 | await nvim.window.setOption('number', false), 764 | await nvim.buffer.setOption('buftype', 'nofile'), 765 | await nvim.buffer.insert(message, 0), 766 | await nvim.command('sil normal! gg'), 767 | await nvim.buffer.setOption('modifiable', false), 768 | ]); 769 | }; 770 | 771 | const getCodeFix = async () => { 772 | await reloadFile(); 773 | const { file, line, offset } = await getCommonData(); 774 | const errorAtCursor = diagnosticHost.getSign(file, line, offset); 775 | if (errorAtCursor) { 776 | // scope: { 777 | // type: 'file', 778 | // args: { file } 779 | // } 780 | // const combinedFixes = await client.getCombinedCodeFix( 781 | // { 782 | // scope: { type: 'file', args: {file} }, 783 | // fixId: {}}); 784 | // console.warn('COMBO FIXES: ', JSON.stringify(combinedFixes)) 785 | 786 | const fixes = await client.getCodeFixes({ 787 | file, 788 | startLine: errorAtCursor.start.line, 789 | startOffset: errorAtCursor.start.offset, 790 | endLine: errorAtCursor.end.line, 791 | endOffset: errorAtCursor.end.offset, 792 | errorCodes: [errorAtCursor.code], 793 | }); 794 | if (fixes.length !== 0) { 795 | const promptSel = await promptForSelection(fixes, nvim); 796 | await diagnosticHost.clearAllHighlights(file); 797 | await applyCodeFixes(promptSel, nvim); 798 | await getDiagnostics(); 799 | } else { 800 | await printHighlight(nvim, 'No fix'); 801 | } 802 | } 803 | }; 804 | 805 | const onCMRefresh = async (args: any[]) => { 806 | const info = args[0]; 807 | const ctx = args[1]; 808 | 809 | const line = ctx['lnum']; 810 | const offset = ctx['col']; 811 | const prefix = ctx['base']; 812 | const startcol = ctx['startcol']; 813 | // recheck 814 | if (await nvim.call('cm#context_changed', ctx)) return; 815 | await reloadFile(); 816 | const file = await getCurrentFile(); 817 | const data = await client.getCompletions({ 818 | file, 819 | line, 820 | offset, 821 | prefix, 822 | includeInsertTextCompletions: false, 823 | includeExternalModuleExports: false, 824 | }); 825 | if (data.entries.length === 0) return []; 826 | 827 | if (data.entries.length > maxCompletion) { 828 | const completions = await Promise.all( 829 | data.entries.map(async (entry) => await convertEntry(nvim, entry)) 830 | ); 831 | await nvim.call('cm#complete', [info, ctx, startcol, completions]); 832 | return; 833 | } 834 | 835 | let entryNames = data.entries.map((v) => v.name); 836 | const detailedCompletions = await client.getCompletionDetails({ 837 | file, 838 | line, 839 | offset, 840 | entryNames, 841 | }); 842 | const detailedEntries = await Promise.all( 843 | detailedCompletions.map( 844 | async (entry) => await convertDetailEntry(nvim, entry) 845 | ) 846 | ); 847 | await nvim.call('cm#complete', [info, ctx, startcol, detailedEntries]); 848 | }; 849 | 850 | const onNcm2Complete = async (args: any[]) => { 851 | const ctx = args[0]; 852 | 853 | const line = ctx['lnum']; 854 | const offset = ctx['ccol']; 855 | const prefix = ctx['base']; 856 | const startccol = ctx['startccol']; 857 | await reloadFile(); 858 | const file = await getCurrentFile(); 859 | const data = await client.getCompletions({ 860 | file, 861 | line, 862 | offset, 863 | prefix, 864 | includeInsertTextCompletions: false, 865 | includeExternalModuleExports: false, 866 | }); 867 | if (data.entries.length === 0) return []; 868 | 869 | if (data.entries.length > maxCompletion) { 870 | const completions = await Promise.all( 871 | data.entries.map(async (entry) => await convertEntry(nvim, entry)) 872 | ); 873 | await nvim.call('ncm2#complete', [ctx, startccol, completions]); 874 | return; 875 | } 876 | 877 | let entryNames = data.entries.map((v) => v.name); 878 | const detailedCompletions = await client.getCompletionDetails({ 879 | file, 880 | line, 881 | offset, 882 | entryNames, 883 | }); 884 | const detailedEntries = await Promise.all( 885 | detailedCompletions.map( 886 | async (entry) => await convertDetailEntry(nvim, entry) 887 | ) 888 | ); 889 | await nvim.call('ncm2#complete', [ctx, startccol, detailedEntries]); 890 | }; 891 | 892 | plugin.registerCommand('TSRename', tsRename, { nargs: '*' }); 893 | plugin.registerCommand('TSGetWorkspaceSymbols', getWorkspaceSymbols, { nargs: '*', }); 894 | plugin.registerCommand('TSSig', tsGetSig); 895 | plugin.registerCommand('TSDefPreview', getDefPreview); 896 | plugin.registerCommand('TSDoc', tsGetDoc); 897 | plugin.registerCommand('TSDef', tsGetDef); 898 | plugin.registerCommand('TSImport', tsImport); 899 | plugin.registerCommand('TSRefs', tsRefs); 900 | plugin.registerCommand('TSEditConfig', tsEditConfig); 901 | plugin.registerCommand('TSStart', tsstart); 902 | plugin.registerCommand('TSStop', tsstop); 903 | plugin.registerCommand('TSReloadProject', reloadProject); 904 | plugin.registerCommand('TSBuild', tsBuild); 905 | plugin.registerCommand('TSTypeDef', tsTypeDef); 906 | plugin.registerCommand('TSType', tsGetType); 907 | plugin.registerCommand('TSGetDocSymbols', tsGetdocsymbols); 908 | plugin.registerCommand('TSOrganizeImports', organizeImports); 909 | plugin.registerCommand('TSGetDiagnostics', getDiagnostics); 910 | plugin.registerCommand('TSGetErrorFull', getErrFull); 911 | plugin.registerCommand('TSGetCodeFix', getCodeFix); 912 | 913 | plugin.registerFunction('TSGetProjectInfoFunc', getProjectInfoFunc, { sync: true, }); 914 | plugin.registerFunction('TSGetWorkspaceSymbolsFunc', getWorkspaceSymbolsFunc, { sync: true }); 915 | plugin.registerFunction('TSGetDocSymbolsFunc', getDocSymbolsFunc, { sync: true, }); 916 | plugin.registerFunction('TSDeoplete', tsDeoplete, { sync: false }); 917 | plugin.registerFunction('TSOmniFunc', tsOmniFunc, { sync: true }); 918 | plugin.registerFunction('TSGetServerPath', tsGetServerPath, { sync: true }); 919 | plugin.registerFunction('TSGetVersion', tsGetVersion, { sync: true }); 920 | plugin.registerFunction('TSNcm2OnComplete', onNcm2Complete); 921 | plugin.registerFunction('TSCmRefresh', onCMRefresh); 922 | plugin.registerFunction('TSCloseWindow', closeFloatingWindow); 923 | plugin.registerFunction('TSEchoMessage', handleCursorMoved); 924 | plugin.registerFunction('TSOnBufEnter', onBufEnter); 925 | plugin.registerFunction('TSOnBufLeave', onBufLeave); 926 | plugin.registerFunction('TSOnBufSave', onBufSave); 927 | }; 928 | 929 | module.exports.default = module.exports; 930 | -------------------------------------------------------------------------------- /rplugin/node/nvim_typescript/src/types.d.ts: -------------------------------------------------------------------------------- 1 | export type CompletionItem = { 2 | word: string; 3 | menu: string; 4 | user_data: string; 5 | info: string; 6 | kind: string; 7 | abbr: string; 8 | }; 9 | 10 | export type CompletionChangeEvent = { 11 | col: number; 12 | row: number; 13 | scrollbar: boolean; 14 | completed_item: CompletionItem; 15 | width: number; 16 | height: number; 17 | size: number; 18 | }; 19 | -------------------------------------------------------------------------------- /rplugin/node/nvim_typescript/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { Neovim } from 'neovim'; 2 | import protocol from 'typescript/lib/protocol'; 3 | import { Client } from './client'; 4 | 5 | export function trim(s: string) { 6 | return (s || '').replace(/^\s+|\s+$/g, ''); 7 | } 8 | 9 | export async function getBufferFromFile(nvim: Neovim, file: string) { 10 | const buffers = await nvim.buffers; 11 | const buf = buffers.find(async buffer => { 12 | let bufname = await buffer.name; 13 | if (bufname === file) { 14 | return buf 15 | } 16 | }); 17 | return buf 18 | } 19 | 20 | export function convertToDisplayString(displayParts?: any[]) { 21 | let ret = ''; 22 | if (!displayParts) return ret; 23 | for (let dp of displayParts) { 24 | ret += dp['text']; 25 | } 26 | return ret; 27 | } 28 | 29 | export function getParams(members: Array<{ text: string; documentation: string }>, separator: string): string { 30 | let ret = ''; 31 | members.forEach((member, idx) => { 32 | if (idx === members.length - 1) { 33 | ret += member.text; 34 | } else { 35 | ret += member.text + separator; 36 | } 37 | }); 38 | return ret; 39 | } 40 | 41 | export async function getCurrentImports(client: Client, file: string) { 42 | const documentSymbols = await client.getDocumentSymbols({ file }); 43 | if (documentSymbols.childItems) { 44 | return Promise.all( 45 | documentSymbols.childItems 46 | .filter(item => item.kind === 'alias') 47 | .map(item => item.text) 48 | ); 49 | } else { 50 | return; 51 | } 52 | } 53 | 54 | export async function convertEntry(nvim: Neovim, entry: protocol.CompletionEntry): Promise { 55 | let kind = await getKind(nvim, entry.kind); 56 | return { 57 | word: entry.name, 58 | kind: kind 59 | }; 60 | } 61 | 62 | export async function convertDetailEntry(nvim: Neovim, entry: protocol.CompletionEntryDetails, expandSnippet = false): Promise { 63 | let displayParts = entry.displayParts; 64 | let signature = ''; 65 | for (let p of displayParts) { 66 | signature += p.text; 67 | } 68 | signature = signature.replace(/\s+/gi, ' '); 69 | const menu = signature.replace( 70 | /^(var|let|const|class|\(method\)|\(property\)|enum|namespace|function|import|interface|type)\s+/gi, 71 | '' 72 | ); 73 | const word = !!expandSnippet ? `${entry.name}${getAbbr(entry)}` : entry.name; 74 | const info = entry.documentation ? entry.documentation.map(d => d.text).join('\n') : ''; 75 | let kind = await getKind(nvim, entry.kind); 76 | 77 | return { 78 | word: word, 79 | kind: kind, 80 | abbr: entry.name, 81 | menu: menu, 82 | info: info 83 | }; 84 | } 85 | 86 | function getAbbr(entry: protocol.CompletionEntryDetails) { 87 | return entry.displayParts 88 | .filter(e => (e.kind === 'parameterName' ? e : null)) 89 | .map(e => e.text) 90 | .map((e, idx) => '<`' + idx + ':' + e + '`>') 91 | .join(', '); 92 | } 93 | 94 | export async function getKind(nvim: any, kind: string): Promise { 95 | const icons = await nvim.getVar('nvim_typescript#kind_symbols'); 96 | if (kind in icons) return icons[kind]; 97 | return kind; 98 | } 99 | 100 | function toTitleCase(str: string) { 101 | return str.replace(/\w\S*/g, function (txt) { 102 | return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); 103 | }); 104 | } 105 | 106 | export async function createLocList(nvim: Neovim, list: Array<{ filename: string; lnum: number; col: number; text: string }>, title: string, autoOpen = false): Promise { 107 | return new Promise( 108 | async (resolve: any): Promise => { 109 | await nvim.call('setloclist', [0, list, 'r', title]); 110 | if (autoOpen) { 111 | await nvim.command('lwindow'); 112 | } 113 | resolve(); 114 | } 115 | ); 116 | } 117 | 118 | export async function createQuickFixList(nvim: Neovim, list: Array<{ filename: string; lnum: number; col: number; text: string; nr?: number; type?: string; }>, title: string, autoOpen = false): Promise { 119 | await nvim.call('setqflist', [list, 'r', title]); 120 | if (autoOpen) await nvim.command('botright copen'); 121 | } 122 | 123 | export async function truncateMsg(nvim: Neovim, message: string): Promise { 124 | /** 125 | * Print as much of msg as possible without triggering "Press Enter" 126 | * Inspired by neomake, which is in turn inspired by syntastic. 127 | */ 128 | const columns = (await nvim.getOption('columns')) as number; 129 | let msg = message.replace(/(\r\n|\n|\r|\t|\s+)/gm, ' ').trim(); 130 | if (msg.length > columns - 9) return msg.substring(0, columns - 11) + '…'; 131 | return msg; 132 | } 133 | 134 | export async function print(nvim: Neovim, message: string){ 135 | await nvim.outWrite(`${message} \n`) 136 | } 137 | export async function printHighlight(nvim: Neovim, message: string | Promise, statusHL = 'None', messageHL = 'NormalNC'): Promise { 138 | const ruler = (await nvim.getOption('ruler')) as boolean; 139 | const showCmd = (await nvim.getOption('showcmd')) as boolean; 140 | const msg = (message as string).replace(/\"/g, '\\"'); 141 | await nvim.setOption('ruler', false); 142 | await nvim.setOption('showcmd', false); 143 | await nvim.command( 144 | `redraw | echohl ${statusHL} | echon "nvim-ts: " | echohl None | echohl ${messageHL} | echon "${msg}" | echohl None | redraw` 145 | ); 146 | await nvim.setOption('ruler', ruler); 147 | await nvim.setOption('showcmd', showCmd); 148 | } 149 | 150 | // ReduceByPrefix takes a list of basic complettions and a prefix and eliminates things 151 | // that don't match the prefix 152 | export const reduceByPrefix = (prefix: string, c: ReadonlyArray): protocol.CompletionEntry[] => { 153 | const re = new RegExp(prefix, 'i'); 154 | return c.filter(v => re.test(v.name)); 155 | }; 156 | 157 | // for testing rename results to guarantee the object type 158 | export const isRenameSuccess = ( 159 | obj: protocol.RenameInfoSuccess | protocol.RenameInfoFailure 160 | ): obj is protocol.RenameInfoSuccess => obj.canRename; 161 | 162 | // trigger char is for complete requests to the TSServer, there are special completions when the 163 | // trigger character is ., ", /, etc.. 164 | const chars = ['.', '"', "'", '`', '/', '@', '<']; 165 | export const triggerChar = ( 166 | p: string 167 | ): protocol.CompletionsTriggerCharacter | undefined => { 168 | const char = p[p.length - 1]; 169 | return chars.includes(char) 170 | ? (char as protocol.CompletionsTriggerCharacter) 171 | : undefined; 172 | }; 173 | 174 | export function padString(str: string, len: number, padRight = false, ch = ' ') { 175 | var lpad = ''; 176 | var rpad = ''; 177 | while (lpad.length < len) { 178 | lpad += ch; 179 | } 180 | if (padRight) { 181 | while (rpad.length < len) { 182 | rpad += ch; 183 | } 184 | } 185 | return lpad + str + rpad; 186 | } 187 | 188 | export function processErrors(res: Array<{ file: string; diagnostics: protocol.Diagnostic[] }>) { 189 | return res 190 | .filter(e => !!e.diagnostics.length) 191 | .filter(e => !e.file.includes('node_module')) 192 | .flatMap(({ file, diagnostics }) => 193 | diagnostics.map((o: protocol.Diagnostic) => ({ 194 | filename: file, 195 | lnum: o.start.line, 196 | col: o.start.offset, 197 | text: o.text, 198 | type: o.category[0].toUpperCase() 199 | })) 200 | ); 201 | } 202 | -------------------------------------------------------------------------------- /rplugin/node/nvim_typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "allowUnreachableCode": false, 8 | "lib": ["es2019", "dom"], 9 | "module": "ES2015", 10 | "moduleResolution": "node", 11 | "sourceMap": false, 12 | "target": "ESNext", 13 | "rootDir": "src", 14 | "incremental": false 15 | }, 16 | "include": ["src"] 17 | } 18 | -------------------------------------------------------------------------------- /rplugin/node/nvim_typescript/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "jsRules": {}, 4 | "rules": {}, 5 | "rulesDirectory": [] 6 | } -------------------------------------------------------------------------------- /rplugin/python3/denite/source/TSDocumentSymbol.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | from operator import itemgetter 4 | from denite.base.source import Base 5 | 6 | 7 | class Source(Base): 8 | 9 | def __init__(self, vim): 10 | super().__init__(vim) 11 | self.vim = vim 12 | self.name = 'TSDocumentSymbol' 13 | self.kind = 'file' 14 | 15 | def getKind(self, kind): 16 | if kind in self.vim.vars["nvim_typescript#kind_symbols"].keys(): 17 | return self.vim.vars["nvim_typescript#kind_symbols"][kind] 18 | else: 19 | return kind 20 | 21 | def convertToCandidate(self, symbols): 22 | candidates = [] 23 | for symbol in symbols['childItems']: 24 | 25 | if 'alias' not in symbol.values(): 26 | candidates.append({ 27 | 'text': '{0} {1}'.format(self.getKind(symbol['kind']), symbol['text']), 28 | 'lnum': symbol['spans'][0]['start']['line'], 29 | 'col': symbol['spans'][0]['start']['offset'] 30 | }) 31 | if 'childItems' in symbol and len(symbol['childItems']) > 0: 32 | for childSymbol in symbol['childItems']: 33 | candidates.append({ 34 | 'text': '\t {0} {1}'.format(self.getKind(childSymbol['kind']), childSymbol['text']), 35 | 'lnum': childSymbol['spans'][0]['start']['line'], 36 | 'col': childSymbol['spans'][0]['start']['offset'] 37 | }) 38 | if 'childItems' in childSymbol and len(childSymbol['childItems']) > 0: 39 | for subSymbol in childSymbol['childItems']: 40 | candidates.append({ 41 | 'text': '\t\t {0} {1}'.format(self.getKind(subSymbol['kind']), subSymbol['text']), 42 | 'lnum': subSymbol['spans'][0]['start']['line'], 43 | 'col': subSymbol['spans'][0]['start']['offset'] 44 | }) 45 | return candidates 46 | 47 | def gather_candidates(self, context): 48 | bufname = self.vim.current.buffer.name 49 | response = self.vim.funcs.TSGetDocSymbolsFunc() 50 | if response is None: 51 | return [] 52 | 53 | candidates = self.convertToCandidate(response) 54 | values = list(map(lambda symbol: { 55 | 'abbr': "{0}".format(symbol['text']), 56 | 'word': symbol['text'], 57 | 'action__line': symbol['lnum'], 58 | "action__path": bufname, 59 | "action__col": symbol['col'], 60 | }, candidates)) 61 | return sorted(values, key=itemgetter('action__line')) 62 | -------------------------------------------------------------------------------- /rplugin/python3/denite/source/TSProjectFiles.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | from denite.base.source import Base 4 | 5 | 6 | class Source(Base): 7 | 8 | def __init__(self, vim): 9 | super().__init__(vim) 10 | self.vim = vim 11 | self.name = 'TSProjectFiles' 12 | self.kind = 'file' 13 | 14 | def convertToCandidate(self, symbols): 15 | return list(map(lambda symbol: { 16 | 'text': symbol, 17 | }, symbols)) 18 | 19 | def gather_candidates(self, context): 20 | response = self.vim.funcs.TSGetProjectInfoFunc() 21 | if response is None: 22 | return [] 23 | candidates = self.convertToCandidate(response['fileNames']) 24 | return list(map(lambda symbol: { 25 | 'word': symbol['text'], 26 | 'action__path': symbol['text'] 27 | }, candidates)) 28 | -------------------------------------------------------------------------------- /rplugin/python3/denite/source/TSWorkspaceSymbol.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | from operator import itemgetter 4 | from denite.base.source import Base 5 | 6 | 7 | class Source(Base): 8 | 9 | def __init__(self, vim): 10 | super().__init__(vim) 11 | self.vim = vim 12 | self.name = 'TSWorkspaceSymbol' 13 | self.kind = 'file' 14 | 15 | def on_init(self, context): 16 | context['is_interactive'] = True 17 | context['is_async'] = False 18 | context['file'] = self.vim.current.buffer.name 19 | 20 | def gather_candidates(self, context): 21 | if context['input']: 22 | res = self.vim.call('TSGetWorkspaceSymbolsFunc', 23 | context['input'], context['file']) 24 | if res is None: 25 | return [] 26 | if res: 27 | self.vim.out_write('{} \n'.format(context['input'])) 28 | values = list(map(lambda s: { 29 | 'abbr': " {0}\t{1}".format(s['text'], s['filename']), 30 | 'word': s['text'], 31 | 'action__line': s['lnum'], 32 | "action__path": s['filename'], 33 | "action__col": s['col'], 34 | }, res)) 35 | return sorted(values, key=itemgetter('action__line')) 36 | return [] 37 | else: 38 | return [] 39 | -------------------------------------------------------------------------------- /rplugin/python3/deoplete/sources/typescript.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | import re 3 | from deoplete.source.base import Base 4 | 5 | class Source(Base): 6 | 7 | # Base options 8 | def __init__(self, vim): 9 | Base.__init__(self, vim) 10 | self.name = "typescript" 11 | self.mark = self.vim.vars['nvim_typescript#completion_mark'] 12 | self.rank = 1000 13 | self.min_pattern_length = 1 14 | self.max_abbr_width = 0 15 | self.max_kind_width = 0 16 | self.max_menu_width = 0 17 | self.input_pattern = r'.' 18 | # self.input_pattern = r'\.|\"\\|\s|\/|\@' 19 | # self.input_pattern = r'(\.|::)\w*' 20 | self.filetypes = ["typescript", "tsx", "typescript.tsx", "typescriptreact"] 21 | if self.vim.vars["nvim_typescript#javascript_support"]: 22 | self.filetypes.extend(["javascript", "jsx", "javascript.jsx"]) 23 | if self.vim.vars["nvim_typescript#vue_support"]: 24 | self.filetypes.extend(["vue"]) 25 | 26 | def log(self, message): 27 | self.warn('************') 28 | # self.vim.out_write('\n {}'.format(message)) 29 | self.warn('{}'.format(message)) 30 | self.warn('************') 31 | 32 | def get_complete_position(self, context): 33 | m = re.search(r"\w*$", context["input"]) 34 | return m.start() if m else -1 35 | 36 | def reset_var(self): 37 | self.vim.vars["nvim_typescript#completion_res"] = [] 38 | 39 | def gather_candidates(self, context): 40 | try: 41 | if context["is_async"]: 42 | res = self.vim.vars["nvim_typescript#completion_res"] 43 | if res: 44 | context["is_async"] = False 45 | self.reset_var() 46 | return res 47 | else: 48 | self.vim.call('TSCloseWindow') 49 | context["is_async"] = True 50 | offset = context["complete_position"] + 1 51 | prefix = context["complete_str"] 52 | self.log(self.vim.vars["nvim_typescript#completion_res"]) 53 | self.reset_var() 54 | self.vim.funcs.TSDeoplete(prefix, offset) 55 | return [] 56 | except BaseException: 57 | return [] 58 | -------------------------------------------------------------------------------- /syntax/nvimtypescript.vim: -------------------------------------------------------------------------------- 1 | 2 | --------------------------------------------------------------------------------