first commit

This commit is contained in:
zoe 2022-04-02 20:23:20 +02:00
commit 9420badb70
34 changed files with 3571 additions and 0 deletions

4
.netrwhist Normal file
View file

@ -0,0 +1,4 @@
let g:netrw_dirhistmax =10
let g:netrw_dirhistcnt =2
let g:netrw_dirhist_2='/mnt/296a894f-4ca1-4eb8-8a77-9940196b59c9/Nextcloud/Documents/Code/Rust/guessing_game/src'
let g:netrw_dirhist_1='/mnt/296a894f-4ca1-4eb8-8a77-9940196b59c9/Nextcloud/Documents/Code/Rust/guessing_game'

157
.vimrc Normal file
View file

@ -0,0 +1,157 @@
syntax on
set conceallevel=2
set mouse=a
set hlsearch
set autoread
set ignorecase
set number
set showmatch
set cursorline
set laststatus=2
set errorbells
set visualbell
set ruler
set linebreak
syntax enable
set ruler
set virtualedit=onemore
set list
filetype plugin indent on
set tabstop=8
set softtabstop=4
set shiftwidth=4
set expandtab
" By default timeoutlen is 1000 ms
set timeoutlen=500
" show existing tab with 4 spaces width
set tabstop=4
" when indenting with '>', use 4 spaces width
set shiftwidth=4
" On pressing tab, insert 4 spaces
"set expandtab
syntax match Tab /\t/
hi Tab gui=underline
" highlight on yank
au TextYankPost * lua vim.highlight.on_yank {higroup="IncSearch", timeout=150, on_visual=true}
" KEY MAPPINGS
" leader key
let mapleaderkey = ","
" install vim-plug if it isn't installed yet
if empty(glob('~/.vim/autoload/plug.vim'))
silent !curl -fLo ~/.vim/autoload/plug.vim --create-dirs
\ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
autocmd VimEnter * PlugInstall --sync | source $MYVIMRC
endif
" Run PlugInstall if there are missing plugins
autocmd VimEnter * if len(filter(values(g:plugs), '!isdirectory(v:val.dir)'))
\| PlugInstall --sync | source $MYVIMRC
\| endif
call plug#begin('~/.vim/plugged')
"Treesitter
Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'}
"dracula theme
Plug 'Mofiqul/dracula.nvim'
let g:dracula_transparent_bg = 'true'
let g:termguicolors = 'true'
"Language tool
Plug 'vigoux/LanguageTool.nvim'
" Conquer Of Completion
Plug 'neoclide/coc.nvim', {'branch': 'release'}
inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm()
\: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
vmap <leader>a <Plug>(coc-codeaction-selected)
highlight CocFloating ctermbg=0 guibg=#44475a
command! -nargs=0 Prettier :CocCommand prettier.formatFile "prettier
"Polyglot Language Packs
Plug 'sheerun/vim-polyglot'
" ale
Plug 'dense-analysis/ale'
let g:ale_fix_on_save = 1
let g:ale_fixers = {
\ 'javascript': ['prettier'],
\ 'css': ['prettier'],
\ 'html': ['prettier'],
\ 'markdown': ['prettier'],
\}
" arduino
Plug 'stevearc/vim-arduino'
" Autopairs
Plug 'jiangmiao/auto-pairs'
" Lualine
Plug 'nvim-lualine/lualine.nvim'
Plug 'kyazdani42/nvim-web-devicons'
Plug 'kdheepak/tabline.nvim'
" Vim-which-key
Plug 'https://github.com/liuchengxu/vim-which-key.git'
" Start screen
Plug 'mhinz/vim-startify'
" Graphviz
Plug 'liuchengxu/graphviz.vim'
"Spelunker
Plug 'kamykn/spelunker.vim'
let g:enable_spelunker_vim = 0
"popup menu
Plug 'kamykn/popup-menu.nvim'
"Tex Conceal
Plug 'KeitaNakamura/tex-conceal.vim', {'for': 'tex'}
let g:tex_flavor = 'latex'
let g:tex_conceal='abdmg'
let g:tex_conceal_frac=1
"Vim markdown
"Plug 'plasticboy/vim-markdown'
"let g:vim_markdown_toc_autofit = 0
"let g:vim_markdown_conceal = 1
"let g:markdown_folding = 0
"Initialize Plugin System
call plug#end()
" activate colorscheme
colorscheme dracula
"lua settings
" lualine"
lua << END
require('lualine').setup({
options = {
theme = 'dracula',
component_separators = {left = '', right = ''},
section_separators = {left = '', right = ''},},
sections = {
lualine_a = {'mode'},
lualine_b = {'branch', 'diff',{'diagnostics', sources={'nvim_diagnostic', 'coc'}}},
lualine_c = {'filename'},
lualine_x = {'filetype'},
lualine_y = {'progress'},
lualine_z = {'location'}
}
})
END
lua << END
require('tabline').setup()
END

16
coc-settings.json Normal file
View file

@ -0,0 +1,16 @@
{
"suggest.noselect": false,
"coc.preferences.formatOnSaveFiletypes": [
"javascript",
"typescript",
"typescriptreact",
"json",
"javascriptreact",
"typescript.tsx",
"graphql"
],
"rust-analyzer.updates.channel": "nightly",
"rust-analyzer.diagnostics.disabled": [
"unresolved-macro-call"
]
}

3
init.vim Normal file
View file

@ -0,0 +1,3 @@
set runtimepath+=~/.vim,~/.vim/after
set packpath+=~/.vim
source ~/.config/nvim/.vimrc

View file

@ -0,0 +1,36 @@
dist: trusty
env:
global:
- DEPS=$HOME/deps
- PATH=$DEPS/bin:$PATH
before_install:
- sudo apt-get install python-pip
- pip install --user awscli
- export PATH="$PATH:$HOME/.local/bin"
- curl --silent -L "https://s3-us-west-1.amazonaws.com/kite-data/tensorflow/libtensorflow-cpu-linux-x86_64-1.9.0.tar.gz" | tar -C $HOME -xz
- curl --silent --compressed --output "$HOME/kited-test" "https://s3-us-west-1.amazonaws.com/kited-test/linux/kited-test"
- chmod u+x "$HOME/kited-test"
install: |
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
C_OPTS="--prefix=$DEPS --with-features=huge --disable-gui "
git clone --depth 1 https://github.com/vim/vim
cd vim
export PATH=/usr/bin:$PATH
./configure $C_OPTS
make
make install
cd -
export PATH=$DEPS/bin:$PATH
export VIM="$(which vim)"
script:
- vim --version
- cd test && bash test
- export LD_LIBRARY_PATH="$HOME/lib:$LD_LIBRARY_PATH"
- echo "Running tests with kited-local --------------" && $HOME/kited-test > ~/kite.log 2>&1 & sleep 20 && bash editor_tests && killall kited-test

View file

@ -0,0 +1,30 @@
# Installation
## Manual installation
### Vim
Assuming your Vim configuration is in `~/.vim/`:
```sh
$ mkdir -p ~/.vim/pack/kite/start/kite
$ git clone https://github.com/kiteco/vim-plugin.git ~/.vim/pack/kite/start/kite/
```
Restart Vim.
### Neovim
Assuming your Neovim configuration is in `~/.config/nvim`:
```sh
$ mkdir -p ~/.config/nvim/pack/kite/start/kite
$ git clone https://github.com/kiteco/vim-plugin.git ~/.config/nvim/pack/kite/start/kite/
```
Restart Neovim.
# Development
When working on the plugin, ensure the file `~/.kite/vim-development` (or `$LOCALAPPDATA$\Kite\vim-development` on Windows) is present. This tells the plugin to use development mode, i.e. to use the non-production key when POSTing metrics to Segment.

View file

@ -0,0 +1,4 @@
Copyright (c) 2017 Manhattan Engineering, Inc - All Rights Reserved
Reproduction of this material is strictly forbidden unless prior written
permission is obtained from Manhattan Engineering, Inc.

View file

@ -0,0 +1,57 @@
## Kite-LSP
Kite comes with a language server adapter that may be used to enable Kite
completions via your favorite LSP client.
Other Kite features than completions are not well-supported via LSP, so you
should still install the Kite plugin for Vim.
The Kite-LSP adapter may be run as follows, depending on your operating system:
```bash
/Applications/Kite.app/Contents/MacOS/kite-lsp --editor=vim
~/.local/share/kite/current/kite-lsp --editor=vim
C:\Program Files\Kite\kite-lsp.exe --editor=vim
```
If you installed Kite to a non-standard location, you may need to appropriately
modify the executable path.
### Neovim-LSP
As today the [LSP integration in neovim](https://github.com/neovim/nvim-lspconfig) is just avalaible in the nightlies.
To enable the support in this case:
```
au User lsp_setup call lsp#register_server({
\ 'name': 'kite',
\ 'cmd': '~/.local/share/kite/current/kite-lsp --editor=vim',
\ 'whitelist': ["php", "javascript", "python", "bash"],
\ })
```
### Coc.nvim
We have tested Kite-LSP with the full-featured
[coc.nvim](https://github.com/neoclide/coc.nvim) completions plugin.
However, it should likely work with most LSP client plugins.
In order to enable Kite-LSP, add the following to your coc.nvim configuration
(`:CocConfig`):
```json
{
"languageserver": {
"kite": {
"command": "/Applications/Kite.app/Contents/MacOS/kite-lsp --editor=vim",
"filetypes": ["python", "go", "javascript"]
}
}
}
```
Add to `"filetypes"` any languages for which you would like to enable Kite
completions.
You should also disable the Kite plugin's completions to avoid getting
duplicate completions:
```viml
let g:kite_completions=0
```

View file

@ -0,0 +1,222 @@
# Kite Python Plugin for Vim/Neovim
Kite is an AI-powered programming assistant that helps you write Python code inside Vim. Kite helps you write code faster by showing you the right information at the right time. Learn more about how Kite helps you while you're using Vim at https://kite.com/integrations/vim/.
At a high level, Kite provides you with:
* 🧠 __[Line-of-Code Completions](#Line-of-Code-Completions)__ powered by machine learning models trained on the entire open source code universe
* 📝 __[Intelligent Snippets](#Intelligent-Snippets)__ that automatically provide context-relevant code snippets for your function calls
* 🔍 __[Instant documentation](#Kite-Copilot-for-Python-Documentation)__ for the symbol underneath your cursor so you save time searching for Python docs
## Requirements
* macOS 10.10+ or Windows 7+ or Linux (Ubuntu, Debian, Fedora, Arch Linux, Linux Mint, openSUSE, KDE, XFCE, Gnome 2, Gnome 3)
* Vim 8 or Neovim
* [Kite Engine](https://kite.com/)
Use another editor? Check out [Kites other editor integrations](https://kite.com/integrations/).
## Installation
### Installing the Kite Engine
The [Kite Engine](https://kite.com/) needs to be installed and running on your computer in order for the package to work properly. The package itself provides the frontend that interfaces with the Kite Engine, which performs all the code analysis and machine learning 100% locally on your computer (no code is sent to a cloud server).
__macOS Instructions__
1. Download the [installer](https://kite.com/download/) and open the downloaded `.dmg` file.
2. Drag the Kite icon into the `Applications` folder.
3. Run `Kite.app` to start the Kite Engine.
__Windows Instructions__
1. Download the [installer](https://kite.com/download/) and run the downloaded `.exe` file.
2. The installer should run the Kite Engine automatically after installation is complete.
__Linux Instructions__
1. Visit https://kite.com/linux/ to learn how to install Kite.
2. The installer should run the Kite Engine automatically after installation is complete.
### Installing the Kite plugin for Vim/Neovim
When running the Kite Engine for the first time, you'll be guided through a setup process which will allow you to install the Vim/Neovim plugin. You can also install or uninstall the Vim plugin at any time using the Kite Engine's [plugin manager](https://help.kite.com/article/62-managing-editor-plugins).
Alternatively, you can follow the instructions in the [DEVELOPMENT.md](https://github.com/kiteco/vim-plugin/blob/master/DEVELOPMENT.md) file to learn how to manually install the Vim/Neovim plugin.
Once installed, the plugin will be automatically updated by Kite when necessary.
### Configuring supported languages
Kite supports 12 languages and counting. By default only Python is enabled. You can configure the languages for which Kite is enabled:
```viml
" Python, JavaScript, Go
let g:kite_supported_languages = ['python', 'javascript', 'go']
" All the languages Kite supports
let g:kite_supported_languages = ['*']
" Turn off Kite
let g:kite_supported_languages = []
```
[Learn more about why Kite is the best autocomplete for Vim.](https://kite.com/integrations/vim/)
## Features
Kite's Vim/Neovim plugin provides a number of features to help you code better and faster.
### Line-of-Code Completions
Kite's ranked completions are integrated with Vim's insert-mode completion, specifically the user-defined completion. Kite shows normal completions or signature-completions as appropriate for the cursor position.
By default Kite's completions will show up automatically as you type. You can opt out via:
```viml
let g:kite_auto_complete=0
```
You can manually invoke the completions in insert mode with `<C-X><C-U>`. See `:h i_CTRL-X_CTRL-U` for details.
You can disable Kite's completions altogether with this in your vimrc:
```viml
let g:kite_completions=0
```
Kite's completions include snippets by default. To opt out of the snippets, add this to your vimrc:
```viml
let g:kite_snippets=0
```
Normally you insert the currently selected completion option with `<C-y>`. If you'd like to use `<Tab>` instead / as well, add this to your vimrc:
```viml
let g:kite_tab_complete=1
```
For any kind of completion you must set 'completopt' as follows:
```viml
set completeopt+=menuone
```
For automatic completion, you also need either:
```viml
set completeopt+=noselect
```
or:
```viml
set completeopt+=noinsert
```
To see documentation in the preview window for each completion option, copy all the lines above into your vimrc and change the preview line to:
```viml
set completeopt+=preview
```
To have the preview window automatically closed once a completion has been inserted:
```viml
autocmd CompleteDone * if !pumvisible() | pclose | endif
```
We also recommend:
```viml
set belloff+=ctrlg " if vim beeps during completion
```
### Intelligent Snippets
Some completions are actually autogenerated code snippets which can be filled in. These will be highlighted with the Underline highlight group.
You can navigate between placeholders with `<CTRL-J>` (forward) and `<CTRL-K>` (backward), even after you have typed over the original placeholder text.
To change these keys:
```viml
let g:kite_previous_placeholder = '<C-H>'
let g:kite_next_placeholder = '<C-L>`
```
### Signatures
Kite can show how other people used the signature you are using. By default this is off to save space.
To turn it on: `:KiteShowPopularPatterns`.
To turn it off: `:KiteHidePopularPatterns`.
### Kite Copilot for Python Documentation
As you edit your code, the [Kite Copilot](https://kite.com/copilot/) will automatically show examples and docs for the code under your cursor.
Alternatively, you can press `K` when the cursor is on a symbol to view its documentation in Kite Copilot.
If you have mapped `K` already, the plugin won't overwrite your mapping. You can set an alternative mapping, e.g. to `gK`, like this:
```viml
nmap <silent> <buffer> gK <Plug>(kite-docs)
```
By default you need to type `K` (or whatever you have mapped to `<Plug>(kite-docs)`) each time you want to see documentation for the keyword under the cursor. To have the documentation continually update itself as you move from keyword to keyword:
```viml
let g:kite_documentation_continual=1
```
### Goto Definition
Use `<C-]>` or `:KiteGotoDefinition` to jump to a method's definition.
### Commands
- `KiteDocsAtCursor` - show documentation for the keyword under the cursor.
- `KiteOpenCopilot` - open the Kite Copilot and focus on it.
- `KiteGeneralSettings` - open Kite's settings in the Copilot.
- `KitePermissions` - open Kite's permission settings in the Copilot.
- `KiteTutorial` - show a tutorial for how to use Kite with Vim.
- `KiteEnableAutoStart` - start Kite automatically when Vim starts.
- `KiteDisableAutoStart` - do not start Kite automatically when Vim starts.
- `KiteGotoDefinition` - jump to a method's definition.
### Statusline
Add `%{kite#statusline()}` to your statusline to get an indicator of what Kite is doing. If you don't have a status line, this one matches the default when `&ruler` is set:
```viml
set statusline=%<%f\ %h%m%r%{kite#statusline()}%=%-14.(%l,%c%V%)\ %P
set laststatus=2 " always display the status line
```
### Debugging
Use `let g:kite_log=1` to switch on logging. Logs are written to `kite-vim.log` in Vim's current working directory.
---
#### About Kite
Kite is built by a team in San Francisco devoted to making programming easier and more enjoyable for all. Follow Kite on
[Twitter](https://twitter.com/kitehq) and get the latest news and programming tips on the
[Kite Blog](https://kite.com/blog/).
Kite has been featured in [Wired](https://www.wired.com/2016/04/kites-coding-asssitant-spots-errors-finds-better-open-source/),
[VentureBeat](https://venturebeat.com/2019/01/28/kite-raises-17-million-for-its-ai-powered-developer-environment/),
[The Next Web](https://thenextweb.com/dd/2016/04/14/kite-plugin/), and
[TechCrunch](https://techcrunch.com/2019/01/28/kite-raises-17m-for-its-ai-driven-code-completion-tool/).

View file

@ -0,0 +1 @@
1.0.84

View file

@ -0,0 +1,264 @@
let s:status_poll_interval = 5 * 1000 " 5sec in milliseconds
let s:timer = -1
let s:watch_timer = -1
if !kite#utils#windows()
let s:kite_symbol = nr2char(printf('%d', '0x27E0'))
else
let s:kite_symbol = '[k]'
endif
let s:inited = 0
let s:kite_auto_launched = 0
function kite#enable_auto_start()
call kite#utils#set_setting('start_kited_at_startup', 1)
call s:launch_kited()
call kite#utils#info('Kite: auto-start enabled')
endfunction
function kite#disable_auto_start()
call kite#utils#set_setting('start_kited_at_startup', 0)
call kite#utils#info('Kite: auto-start disabled')
endfunction
function kite#symbol()
return s:kite_symbol
endfunction
function kite#statusline()
if exists('b:kite_status')
return b:kite_status
else
return ''
endif
endfunction
function! kite#max_file_size()
" Fallback to 1MB
return get(b:, 'kite_max_file_size', 1048576)
endfunction
function! kite#configure_completeopt()
" If the user has configured completeopt, leave it alone.
redir => output
silent verbose set completeopt
redir END
if len(split(output, '\n')) > 1 | return | endif
set completeopt=menuone,noinsert
endfunction
function! s:setup_options()
let s:pumheight = &pumheight
if &pumheight == 0
set pumheight=10
endif
let s:updatetime = &updatetime
if &updatetime == 4000
set updatetime=100
endif
let s:shortmess = &shortmess
set shortmess+=c
if kite#utils#windows()
" Avoid taskbar flashing on Windows when executing system() calls.
let s:shelltemp = &shelltemp
set noshelltemp
endif
endfunction
function! s:restore_options()
if !exists('s:pumheight') | return | endif
let &pumheight = s:pumheight
unlet s:pumheight
let &updatetime = s:updatetime
let &shortmess = s:shortmess
if kite#utils#windows()
let &shelltemp = s:shelltemp
endif
endfunction
function! kite#bufenter()
if kite#languages#supported_by_plugin()
call s:launch_kited()
if !kite#utils#kite_running()
call kite#status#status()
call s:start_status_timer()
call s:start_watching_for_kited()
return
endif
call s:stop_watching_for_kited()
if kite#languages#supported_by_kited()
if g:kite_completions
call s:disable_completion_plugins()
endif
call s:setup_options()
call s:setup_events()
call s:setup_mappings()
call s:set_max_file_size()
if g:kite_completions
setlocal completefunc=kite#completion#complete
endif
call kite#events#event('focus')
call kite#status#status()
call s:start_status_timer()
return
end
end
" Buffer is not a supported language.
call s:restore_options()
call s:stop_status_timer()
endfunction
function s:setup_events()
augroup KiteEvents
autocmd! * <buffer>
autocmd CursorHold,CursorHoldI <buffer> call kite#events#event('selection')
autocmd TextChanged,TextChangedI <buffer> call kite#events#event('edit')
autocmd FocusGained <buffer> call kite#events#event('focus')
if g:kite_completions
autocmd InsertCharPre <buffer> call kite#completion#insertcharpre()
autocmd TextChangedI <buffer> call kite#completion#autocomplete()
autocmd CompleteDone <buffer> call kite#completion#replace_range()
if &ft == 'go'
autocmd CompleteDone <buffer> call kite#completion#expand_newlines()
endif
if &ft == 'python'
autocmd CompleteDone <buffer> call kite#snippet#complete_done()
endif
endif
if exists('g:kite_documentation_continual') && g:kite_documentation_continual
autocmd CursorHold,CursorHoldI <buffer> call kite#docs#docs()
endif
augroup END
endfunction
function! s:setup_mappings()
if exists('g:kite_tab_complete') && g:kite_completions
imap <buffer> <expr> <Tab> pumvisible() ? "\<C-y>" : "\<Tab>"
endif
if empty(maparg('K', 'n')) && !hasmapto('(kite-docs)', 'n')
nmap <silent> <buffer> K <Plug>(kite-docs)
endif
if empty(maparg('<C-]>', 'n'))
nmap <silent> <buffer> <C-]> :KiteGotoDefinition<CR>
endif
endfunction
function! s:set_max_file_size()
let max_file_size = kite#client#max_file_size()
if max_file_size != -1
let b:kite_max_file_size = max_file_size
endif
endfunction
function! s:start_status_timer()
if s:timer == -1
let s:timer = timer_start(s:status_poll_interval,
\ function('kite#status#status'),
\ {'repeat': -1}
\ )
else
call timer_pause(s:timer, 0) " unpause
endif
endfunction
function! s:stop_status_timer()
call timer_pause(s:timer, 1)
endfunction
function! s:launch_kited()
if !s:kite_auto_launched && kite#utils#get_setting('start_kited_at_startup', 1)
call kite#utils#launch_kited()
let s:kite_auto_launched = 1
endif
endfunction
function! s:start_watching_for_kited()
if s:watch_timer == -1
let s:watch_timer = timer_start(s:status_poll_interval,
\ function('kite#activate_when_ready'),
\ {'repeat': -1}
\ )
else
call timer_pause(s:watch_timer, 0) " unpause
endif
endfunction
function! kite#activate_when_ready(...)
if kite#utils#kite_running()
call kite#bufenter()
endif
endfunction
function! s:stop_watching_for_kited()
call timer_pause(s:watch_timer, 1)
endfunction
function! s:disable_completion_plugins()
" coc.nvim
if exists('g:did_coc_loaded')
let b:coc_suggest_disable = 1
" Alternatively:
" autocmd BufEnter *.python :CocDisable
" autocmd BufLeave *.python :CocEnable
call kite#utils#warn("disabling coc.nvim's completions in this buffer")
endif
" Jedi
if exists('*jedi#setup_completion')
" This may not be enough: https://github.com/davidhalter/jedi-vim/issues/614
let g:jedi#completions_enabled = 0
call kite#utils#warn("disabling jedi-vim's completions")
" Alternatively:
" call kite#utils#warn('please uninstall jedi-vim and restart vim/nvim')
" finish
endif
" YouCompleteMe
if exists('g:loaded_youcompleteme') && !exists('g:ycm_filetype_blacklist.python')
let g:ycm_filetype_blacklist.python = 1
call kite#utils#warn("disabling YouCompleteMe's completions for python files")
endif
" Deoplete
if exists('*deoplete#disable')
call deoplete#disable()
call kite#utils#warn("disabling deoplete's completions")
endif
endfunction

View file

@ -0,0 +1,123 @@
let s:async_sync_id = 0
let s:async_sync_outputs = {}
function! s:next_async_sync_id()
let async_sync_id = s:async_sync_id
let s:async_sync_id += 1
return async_sync_id
endfunction
function! s:async_sync_output(async_sync_id, output)
if type(a:output) == v:t_list
" Ensure empty list becomes an empty string.
let output = join(a:output, "\n")
else
" Assume this is a string
let output = a:output
endif
let s:async_sync_outputs[a:async_sync_id] = output " job can now be garbage collected
endfunction
" Executes `cmd` asynchronously but looks synchronous to the caller.
function! kite#async#sync(cmd)
let async_sync_id = s:next_async_sync_id()
let job_handle = kite#async#execute(a:cmd, function('s:async_sync_output', [async_sync_id]))
let s:async_sync_outputs[async_sync_id] = job_handle
let job_type = type(job_handle) " Assume not a string
let vim = !has('nvim')
while type(s:async_sync_outputs[async_sync_id]) == job_type
if vim | call job_status(job_handle) | endif
sleep 5m
endwhile
let output = s:async_sync_outputs[async_sync_id]
unlet s:async_sync_outputs[async_sync_id]
return output
endfunction
" Optional argument is data (JSON) to pass to cmd's stdin.
" Returns the job / job id.
function! kite#async#execute(cmd, handler, ...)
let options = {
\ 'stdoutbuffer': [],
\ 'handler': a:handler,
\ }
let command = s:build_command(a:cmd)
if has('nvim')
let jobid = jobstart(command, extend(options, {
\ 'on_stdout': function('s:on_stdout_nvim'),
\ 'on_exit': function('s:on_exit_nvim')
\ }))
if a:0
call chansend(jobid, a:1)
call chanclose(jobid, 'stdin')
endif
return jobid
else
let job = job_start(command, {
\ 'out_cb': function('s:on_stdout_vim', options),
\ 'exit_cb': function('s:on_exit_vim', options)
\ })
if a:0
let channel = job_getchannel(job)
call ch_sendraw(channel, a:1)
call ch_close_in(channel)
endif
return job
endif
endfunction
function! s:build_command(cmd)
if has('nvim')
if has('unix')
return ['sh', '-c', a:cmd]
elseif has('win64') || has('win32')
return ['cmd.exe', '/c', a:cmd]
else
throw 'unknown os'
endif
else
if has('unix')
return ['sh', '-c', a:cmd]
elseif has('win64') || has('win32')
return 'cmd.exe /c '.a:cmd
else
throw 'unknown os'
endif
endif
endfunction
function! s:on_stdout_vim(_channel, data) dict
" a:data - an output line
call add(self.stdoutbuffer, a:data)
endfunction
function! s:on_exit_vim(job, exit_status) dict
" Allow time for any buffered data to trigger out_cb.
" 5m is an educated guess.
sleep 5m
call self.handler(self.stdoutbuffer)
endfunction
function! s:on_stdout_nvim(_job_id, data, event) dict
if empty(self.stdoutbuffer)
let self.stdoutbuffer = a:data
else
let self.stdoutbuffer = self.stdoutbuffer[:-2] +
\ [self.stdoutbuffer[-1] . a:data[0]] +
\ a:data[1:]
endif
endfunction
function! s:on_exit_nvim(_job_id, _data, _event) dict
call map(self.stdoutbuffer, 'substitute(v:val, "\r$", "", "")')
call self.handler(self.stdoutbuffer)
endfunction

View file

@ -0,0 +1,329 @@
let s:port = empty($KITED_TEST_PORT) ? 46624 : $KITED_TEST_PORT
let s:channel_base = 'localhost:'.s:port
let s:base_url = 'http://127.0.0.1:'.s:port
let s:editor_path = '/clientapi/editor'
let s:onboarding_path = '/clientapi/plugins/onboarding_file?editor=vim'
let s:hover_path = '/api/buffer/vim'
let s:docs_path = 'kite://docs/'
let s:status_path = '/clientapi/status?filename='
let s:languages_path = '/clientapi/languages'
let s:copilot_path = 'kite://home'
let s:counter_path = '/clientapi/metrics/counters'
let s:settings_path = 'kite://settings'
let s:permissions_path = 'kite://settings/permissions'
let s:max_file_size_path = '/clientapi/settings/max_file_size_kb'
let s:codenav_path = '/codenav/editor/related'
function! kite#client#docs(word)
let url = s:docs_path.a:word
call kite#utils#browse(url)
endfunction
function! kite#client#settings()
call kite#utils#browse(s:settings_path)
endfunction
function! kite#client#permissions()
call kite#utils#browse(s:permissions_path)
endfunction
function! kite#client#copilot()
call kite#utils#browse(s:copilot_path)
endfunction
function! kite#client#counter(json, handler)
let path = s:counter_path
if has('channel')
call s:async(function('s:timer_post', [path, g:kite_long_timeout, a:json, a:handler]))
else
call kite#async#execute(s:external_http_cmd(s:base_url.path, g:kite_long_timeout, 1), a:handler, a:json)
endif
endfunction
function! kite#client#onboarding_file(handler)
let path = s:onboarding_path
if has('channel')
let response = s:internal_http(path, g:kite_short_timeout)
else
let response = s:external_http(s:base_url.path, g:kite_short_timeout)
endif
return a:handler(s:parse_response(response))
endfunction
function! kite#client#status(filename, handler)
let path = s:status_path.kite#utils#url_encode(a:filename)
if has('channel')
let response = s:internal_http(path, g:kite_short_timeout)
else
let response = s:external_http(s:base_url.path, g:kite_short_timeout)
endif
return a:handler(s:parse_response(response))
endfunction
function! kite#client#languages(handler)
let path = s:languages_path
if has('channel')
let response = s:internal_http(path, g:kite_short_timeout)
else
let response = s:external_http(s:base_url.path, g:kite_short_timeout)
endif
return a:handler(s:parse_response(response))
endfunction
" Returns max file size in bytes, or -1 if not available.
function! kite#client#max_file_size()
let path = s:max_file_size_path
if has('channel')
let response = s:internal_http(path, g:kite_short_timeout)
else
let response = s:external_http(s:base_url.path, g:kite_short_timeout)
endif
let result = s:parse_response(response)
if result.status == 200
return result.body * 1024
else
return -1
endif
endfunction
function! kite#client#hover(filename, hash, cursor, handler)
call s:wait_for_pending_events()
let path = s:hover_path.'/'.a:filename.'/'.a:hash.'/hover?cursor_runes='.a:cursor
if has('channel')
call s:async(function('s:timer_get', [path, g:kite_long_timeout, a:handler]))
else
call kite#async#execute(s:external_http_cmd(s:base_url.path, g:kite_long_timeout, 0),
\ function('s:parse_and_handle', [a:handler]))
endif
endfunction
function! kite#client#signatures(json, handler)
let path = s:editor_path.'/signatures'
if has('channel')
call s:async(function('s:timer_post', [path, g:kite_long_timeout, a:json, a:handler]))
else
call kite#async#execute(s:external_http_cmd(s:base_url.path, g:kite_long_timeout, 1),
\ function('s:parse_and_handle', [a:handler]), a:json)
endif
endfunction
function! kite#client#completions(json, handler)
let path = s:editor_path.'/complete'
if has('channel')
call s:async(function('s:timer_post', [path, g:kite_long_timeout, a:json, a:handler]))
else
call kite#async#execute(s:external_http_cmd(s:base_url.path, g:kite_long_timeout, 1),
\ function('s:parse_and_handle', [a:handler]), a:json)
endif
endfunction
function! kite#client#request_related(json, handler)
let path = s:codenav_path
let timeout = 10000 "10s
if has('channel')
call s:async(function('s:timer_post', [path, timeout, a:json, a:handler]))
else
call kite#async#execute(s:external_http_cmd(s:base_url.path, timeout, 1),
\ function('s:parse_and_handle', [a:handler]), a:json)
endif
endfunction
function! kite#client#post_event(json, handler)
let path = s:editor_path.'/event'
if has('channel')
call s:async(function('s:timer_post', [path, g:kite_short_timeout, a:json, a:handler]))
else
call kite#async#execute(s:external_http_cmd(s:base_url.path, g:kite_short_timeout, 1),
\ function('s:parse_and_handle', [a:handler]), a:json)
endif
endfunction
function! s:timer_get(path, timeout, handler, timer)
call a:handler(s:parse_response(s:internal_http(a:path, a:timeout)))
endfunction
function! s:timer_post(path, timeout, json, handler, timer)
call a:handler(s:parse_response(s:internal_http(a:path, a:timeout, a:json)))
endfunction
function! s:async(callback)
call timer_start(0, a:callback)
endfunction
function! s:on_std_out(_channel, message) dict
let self.stdoutbuffer .= a:message
endfunction
" Optional argument is json to be posted
function! s:internal_http(path, timeout, ...)
" Use HTTP 1.0 (not 1.1) to avoid having to parse chunked responses.
if a:0
let str = 'POST '.a:path." HTTP/1.0\nHost: localhost\nContent-Type: application/x-www-form-urlencoded\nContent-Length: ".len(a:1)."\n\n".a:1
else
let str = 'GET '.a:path." HTTP/1.0\nHost: localhost\n\n"
endif
call kite#utils#log('')
call kite#utils#log(map(split(str, '\n', 1), '"> ".v:val'))
let options = {'stdoutbuffer': ''}
try
let channel = ch_open(s:channel_base, {
\ 'mode': 'raw',
\ 'callback': function('s:on_std_out', options)
\ })
catch /E898\|E901\|E902/
call kite#utils#log('| Cannot open channel: '.str)
return ''
endtry
try
call ch_sendraw(channel, str)
catch /E630\|E631\|E906/
call kite#utils#log('| Cannot send over channel: '.str)
return ''
endtry
let start = reltime()
while ch_status(channel) !=# 'closed'
if reltimefloat(reltime(start))*1000 > a:timeout
call kite#utils#log('| Timed out waiting for response (timeout: '.a:timeout.'ms)')
try
call ch_close(channel)
catch /E906/
" noop
endtry
return ''
endif
sleep 5m
endwhile
call kite#utils#log('| Received complete response: '.string(reltimefloat(reltime(start))*1000).'ms')
return options.stdoutbuffer
endfunction
" Optional argument is json to be posted
function! s:external_http(url, timeout, ...)
let cmd = s:external_http_cmd(a:url, a:timeout, a:0)
if a:0
return system(cmd, a:1)
else
return system(cmd)
endif
endif
endfunction
" data argument is a boolean
function! s:external_http_cmd(endpoint, timeout, data)
let cmd = s:http_binary
let cmd .= ' --timeout '.a:timeout.'ms'
if a:data
let cmd .= ' -'
endif
let cmd .= ' '.s:shellescape(a:endpoint)
call kite#utils#log('')
call kite#utils#log('> '.cmd)
return cmd
endfunction
function! s:parse_and_handle(handler, out)
call a:handler(s:parse_response(a:out))
endfunction
" Returns the integer HTTP response code and the string body in a dictionary.
"
" lines - either a list (from async commands) or a string (from sync)
function! s:parse_response(lines)
if empty(a:lines)
return {'status': 0, 'body': ''}
endif
if type(a:lines) == v:t_string
let lines = split(a:lines, '\r\?\n', 1)
else
let lines = a:lines
endif
call kite#utils#log(map(copy(lines), '"< ".v:val'))
if type(a:lines) == v:t_string
let lines = split(a:lines, '\r\?\n')
else
let lines = a:lines
endif
" Ignore occasional 100 Continue.
let i = match(lines, '^HTTP/1.[01] [2345]\d\d ')
if i == -1
return {'status': 0, 'body': ''}
endif
let status = split(lines[i], ' ')[1]
let sep = match(lines, '^$', i)
let body = join(lines[sep+1:], "\n")
return {'status': status, 'body': body}
endfunction
function! s:wait_for_pending_events()
while kite#events#any_events_pending()
sleep 5m
endwhile
endfunction
" Only used with NeoVim on not-Windows, in async jobs.
function! s:shellescape(str)
let [_shell, &shell] = [&shell, 'sh']
let escaped = shellescape(a:str)
let &shell = _shell
return escaped
endfunction
let s:http_binary = kite#utils#lib('kite-http')
if !empty($KITED_TEST_PORT)
function! kite#client#request_history()
let ret = json_decode(
\ s:parse_response(
\ s:internal_http('/testapi/request-history', 500)
\ ).body
\ )
if type(ret) != type([])
throw '/testapi/request-history did not return a list (type '.type(ret).')'
endif
return ret
endfunction
function! kite#client#reset_request_history()
call s:internal_http('/testapi/request-history/reset', 500)
endfunction
endif

View file

@ -0,0 +1,39 @@
function! kite#codenav#from_file()
let filepath = kite#utils#filepath(0)
call kite#codenav#request_related(filepath, v:null)
endfunction
function! kite#codenav#from_line()
let filepath = kite#utils#filepath(0)
call kite#codenav#request_related(filepath, line("."))
endfunction
function! kite#codenav#request_related(filename, line)
let json = json_encode({
\ 'editor': 'vim',
\ 'location': {'filename': a:filename, 'line': a:line}
\ })
call kite#client#request_related(json, function('kite#codenav#handler'))
endfunction
function! kite#codenav#handler(response) abort
if a:response.status != 200
if a:response.status == 0
call kite#utils#warn("Kite could not be reached. Please check that Kite Engine is running.")
return
endif
let err = json_decode(a:response.body)
if empty(err) || type(err.message) != v:t_string
call kite#utils#warn("Oops! Something went wrong with Code Finder. Please try again later.")
return
endif
call kite#utils#warn(err.message)
endif
endfunction

View file

@ -0,0 +1,454 @@
let s:should_trigger_completion = 0
let s:completion_counter = 0
let s:begin = 0
let s:end = 0
function! kite#completion#replace_range()
if empty(v:completed_item) | return | endif
if !exists('s:startcol') | return | endif
let startcol = s:startcol
unlet s:startcol
if has_key(v:completed_item, 'user_data') && !empty(v:completed_item.user_data)
let range = json_decode(v:completed_item.user_data).range
let placeholders = json_decode(v:completed_item.user_data).placeholders
elseif exists('b:kite_completions') && has_key(b:kite_completions, v:completed_item.word)
let range = json_decode(b:kite_completions[v:completed_item.word]).range
let placeholders = json_decode(b:kite_completions[v:completed_item.word]).placeholders
else
return
endif
" The range seems to be wrong when placeholders are involved so stop here.
if !empty(placeholders) | return | endif
let col = col('.')
let _col = col
" end of range
let n = range.end - s:offset_before_completion
if n > 0
execute 'normal! "_'.n.'x'
let col -= n
endif
" start of range
let range_begin_col = col('.') - (kite#utils#character_offset() - range.begin)
let n = startcol - range_begin_col
if n > 0
call kite#utils#goto_character(range.begin + 1)
execute 'normal! "_'.n.'x'
let col -= n
endif
" restore cursor position
if col != _col
execute 'normal!' (col+1).'|'
call s:feedkeys("\<Esc>la")
endif
endfunction
function! kite#completion#expand_newlines()
if empty(v:completed_item) | return | endif
if match(v:completed_item.word, '\n') == -1 | return | endif
let parts = split(getline('.'), '\n', 1)
delete _
call append(line('.')-1, parts)
-1
" startinsert! doesn't seem to work with: package main^@import ""^@
call s:feedkeys("\<Esc>A")
endfunction
function! kite#completion#insertcharpre()
let s:should_trigger_completion = 1
" Trigger a fresh completion after every keystroke when the popup menu
" is visible (by calling the function which TextChangedI would call
" (TextChangedI is not triggered when the popup menu is visible)).
if pumvisible()
call kite#utils#log('# Trigger autocomplete because of pumvisible(): '.v:char)
call kite#completion#autocomplete()
endif
endfunction
function! kite#completion#autocomplete()
if !g:kite_auto_complete | return | endif
if exists('b:kite_skip') && b:kite_skip | return | endif
if wordcount().bytes > kite#max_file_size() | return | endif
if s:should_trigger_completion
let s:should_trigger_completion = 0
call s:feedkeys("\<C-X>\<C-U>")
endif
endfunction
" Manual invocation calls this method.
function! kite#completion#complete(findstart, base)
if a:findstart
if !s:completeopt_suitable()
let g:kite_auto_complete = 0
return -3
endif
" Store the buffer contents and cursor position here because when Vim
" calls this function the second time (with a:findstart == 0) Vim has
" already deleted the text between `start` and the cursor position.
let s:cursor = kite#utils#character_offset()
let s:text = kite#utils#buffer_contents()
let s:startcol = s:findstart()
return s:startcol
else
" Leave CTRL-X submode so user can invoke other completion methods.
call s:feedkeys("\<C-e>")
call s:get_completions()
if has('patch-8.1.0716')
return v:none
else
return []
endif
endif
endfunction
function! s:findstart()
let line = getline('.')
let start = col('.') - 1
let s:signature = s:before_function_call_argument(line[:start-1]) && s:begin == 0
if !s:signature
while start > 0 && line[start - 1] =~ '\w'
let start -= 1
endwhile
endif
return start
endfunction
function! s:get_completions()
if s:signature
call kite#signature#increment_completion_counter()
else
let s:completion_counter = s:completion_counter + 1
endif
let filename = kite#utils#filepath(0)
if s:signature
let params = {
\ 'filename': filename,
\ 'editor': 'vim',
\ 'text': s:text,
\ 'cursor_runes': s:cursor,
\ 'offset_encoding': 'utf-32'
\ }
else
let params = {
\ 'no_snippets': (g:kite_snippets ? v:false : v:true),
\ 'no_unicode': (kite#utils#windows() ? v:true : v:false),
\ 'filename': filename,
\ 'editor': 'vim',
\ 'text': s:text,
\ 'position': {
\ 'begin': (s:begin > 0 ? s:begin : s:cursor),
\ 'end': (s:end > 0 ? s:end : s:cursor),
\ },
\ 'offset_encoding': 'utf-32',
\ 'placeholders': []
\ }
let s:begin = 0
let s:end = 0
endif
let json = json_encode(params)
if s:signature
call kite#client#signatures(json, function('kite#signature#handler', [kite#signature#completion_counter(), s:startcol]))
else
call kite#client#completions(json, function('kite#completion#handler', [s:completion_counter, s:startcol]))
endif
endfunction
function! kite#completion#handler(counter, startcol, response) abort
call kite#utils#log('completion: '.a:response.status)
" Ignore old completion results.
if a:counter != s:completion_counter
return
endif
if a:response.status != 200
return
endif
" This should not happen but evidently it sometimes does (#107).
if empty(a:response.body)
return
endif
let json = json_decode(a:response.body)
" API should return 404 status when no completions but it sometimes
" return 200 status and an empty response body, or "completions":"null".
if empty(json) || type(json.completions) != v:t_list
return
endif
" 'display' is the LHS of each option in the completion menu
let max_display_length = s:max_display_length(json.completions, 0)
" 'hint' is the RHS of each option in the completion menu
" Add 1 for leading space we add
let max_hint_length = s:max_hint_length(json.completions) + 1
let available_win_width = s:winwidth() - a:startcol
let max_width = available_win_width > g:kite_completion_max_width
\ ? g:kite_completion_max_width : available_win_width
" pad LHS text gap RHS text gap kite branding pad scrollbar
" | | | | | | | |
let menu_width = 1 + max_display_length + 1 + max_hint_length + 1 + strdisplaywidth(kite#symbol()) + 2 + 1
if menu_width < max_width " no truncation
let lhs_width = max_display_length
let rhs_width = max_hint_length
elseif menu_width - 1 - max_hint_length < max_width " truncate rhs
let lhs_width = max_display_length
let rhs_width = max_width - (1 + max_display_length + 1 + strdisplaywidth(kite#symbol()) + 2 + 1)
else " drop rhs and truncate lhs
let lhs_width = max_width - (1 + 1 + strdisplaywidth(kite#symbol()) + 2 + 1)
let rhs_width = 0
endif
let matches = []
for c in json.completions
call add(matches, s:adapt(c, lhs_width, rhs_width, 0))
if has_key(c, 'children')
for child in c.children
call add(matches, s:adapt(child, lhs_width, rhs_width, 1))
endfor
endif
endfor
if !has('patch-8.0.1493')
let b:kite_completions = {}
for item in filter(copy(matches), 'has_key(v:val, "user_data")')
let b:kite_completions[item.word] = item.user_data
endfor
endif
if mode(1) ==# 'i'
let s:startcol = a:startcol+1
let s:offset_before_completion = kite#utils#character_offset()
call complete(a:startcol+1, matches)
endif
endfunction
function! s:adapt(completion_option, lhs_width, rhs_width, nesting)
let display = s:indent(a:nesting) . a:completion_option.display
let display = kite#utils#truncate(display, a:lhs_width)
" Ensure a minimum separation between abbr and menu of two spaces.
" (Vim lines up the menus so that they are left-aligned 1 space after the longest abbr).
let hint = ' ' . a:completion_option.hint
let hint = kite#utils#ralign(hint, a:rhs_width)
" Add the branding
let hint .= ' '.kite#symbol()
return {
\ 'word': a:completion_option.snippet.text,
\ 'abbr': display,
\ 'info': a:completion_option.documentation.text,
\ 'menu': hint,
\ 'equal': 1,
\ 'user_data': json_encode({'placeholders': a:completion_option.snippet.placeholders, 'range': a:completion_option.replace})
\ }
endfunction
function! s:max_hint_length(completions)
let max = 0
for e in a:completions
let len = strdisplaywidth(e.hint)
if len > max
let max = len
endif
if has_key(e, 'children')
let len = s:max_hint_length(e.children)
if len > max
let max = len
endif
endif
endfor
return max
endfunction
function! s:max_display_length(completions, nesting)
let max = 0
for e in a:completions
let len = strdisplaywidth(s:indent(a:nesting) . e.display)
if len > max
let max = len
endif
if has_key(e, 'children')
let len = s:max_display_length(e.children, a:nesting+1)
if len > max
let max = len
endif
endif
endfor
return max
endfunction
function! s:indent(nesting)
return repeat(' ', a:nesting)
endfunction
" Returns truthy if the cursor is:
"
" - just after an open parenthesis; or
" - just after a comma inside a function call; or
" - just after an equals sign inside a function call.
"
" Note this differs from all the other editor plugins. They can all show both
" a signature popup and a completions popup at the same time, whereas Vim can
" only show one popup. Therefore we need to switch its purpose between
" signature info and completions info at appropriate points inside a function
" call's arguments.
"
" line - the line up to the cursor position
function! s:before_function_call_argument(line)
" Other editors basically do this:
" return a:line =~ '\v[(][^)]*$'
return a:line =~ '\v[(]([^)]+[=,])?\s*$'
endfunction
" Returns the width of the part of the current window which holds the buffer contents.
function! s:winwidth()
let w = winwidth(0)
if &number
let w -= &numberwidth
endif
let w -= &foldcolumn
if &signcolumn == 'yes' || (&signcolumn == 'auto' && s:signs_in_buffer())
" TODO: neovim multiple sign columns
let w -= 2
endif
return w
endfunction
" Returns 1 if the current buffer has any signs, 0 otherwise.
function! s:signs_in_buffer()
let bufinfo = getbufinfo(bufnr(''))[0]
let signs = has_key(bufinfo, 'signs') ? bufinfo.signs : []
return !empty(signs)
endfunction
function! s:completeopt_suitable()
let copts = split(&completeopt, ',')
if g:kite_auto_complete
if index(copts, 'menuone') == -1
call s:popup_warn("Kite: completeopt must contain 'menuone'")
return 0
endif
if index(copts, 'noinsert') == -1 && index(copts, 'noselect') == -1
call s:popup_warn("Kite: completeopt must contain 'noinsert' and/or 'noselect'")
return 0
endif
endif
return 1
endfunction
" feedkeys() by default adds keys to the end of the typeahead buffer. Any
" keys already in the buffer will be processed first and may change Vim's
" state, making the queued keys no longer appropriate (e.g. an insert mode key
" combo being applied in normal mode). To avoid this we use the 'i' flag
" which ensures the keys are processed immediately.
function s:feedkeys(keys)
call feedkeys(a:keys, 'i')
endfunction
function! s:popup_warn(msg)
if exists('*popup_notification')
call popup_notification(a:msg, {
\ 'pos': 'botleft',
\ 'line': 'cursor-1',
\ 'col': 'cursor',
\ 'moved': 'any',
\ 'time': 2000
\ })
elseif exists('*nvim_open_win')
let lines = s:border(a:msg)
let buf = nvim_create_buf(v:false, v:true)
call nvim_buf_set_lines(buf, 0, -1, v:true, lines)
let winid = nvim_open_win(buf, v:false, {
\ 'relative': 'cursor',
\ 'anchor': 'SW',
\ 'row': 0,
\ 'col': 0,
\ 'width': strdisplaywidth(lines[0]),
\ 'height': len(lines),
\ 'focusable': v:false,
\ 'style': 'minimal'
\ })
call nvim_win_set_option(winid, 'winhighlight', 'Normal:WarningMsg')
call timer_start(2000, {-> execute("call nvim_win_close(".winid.", v:true)")})
else
call kite#utils#warn(a:msg)
endif
endfunction
" Converts:
"
" A quick brown fox.
"
" Into:
"
" +--------------------+
" | A quick brown fox. |
" +--------------------+
"
function! s:border(text)
return [
\ '+'.repeat('-', strdisplaywidth(a:text)+2).'+',
\ '| '.a:text.' |',
\ '+'.repeat('-', strdisplaywidth(a:text)+2).'+'
\ ]
endfunction

View file

@ -0,0 +1,24 @@
function! kite#docs#docs()
if &filetype != 'python'
call kite#utils#warn('Docs are only available for Python')
return
endif
if empty(expand('<cword>')) | return | endif
let b:kite_id = ''
call kite#hover#hover()
while b:kite_id == ''
sleep 5m
endwhile
if b:kite_id == -1
call kite#utils#info('No documentation available.')
return
endif
call kite#client#docs(b:kite_id)
endfunction

View file

@ -0,0 +1,42 @@
let kite#document#Document = {}
" NOTE: this has to be called with a g: prefix.
function! kite#document#Document.New(dict)
let newDocument = copy(self)
let newDocument.dict = a:dict
return newDocument
endfunction
" Query the document, returning `default` if `key` does not exist
" or if the value at `key` is not the same type as `default`.
function! kite#document#Document.dig(key, default)
let v = copy(self.dict)
for k in split(a:key, '\.')
let matchlist = matchlist(k, '\v(\w+)\[(-?\d+)\]') " foo[42]
if !empty(matchlist)
let kk = matchlist[1] " foo
if has_key(v, kk)
let v = get(v[kk], str2nr(matchlist[2]), a:default)
else
return a:default
endif
elseif type(v) == v:t_dict && has_key(v, k)
let v = v[k]
else
return a:default
endif
endfor
if type(v) == type(a:default)
return v
endif
return a:default
endfunction

View file

@ -0,0 +1,46 @@
let s:events_pending = 0
function! kite#events#any_events_pending()
return s:events_pending > 0
endfunction
function! kite#events#event(action)
let filename = kite#utils#filepath(0)
if wordcount().bytes < kite#max_file_size()
let action = a:action
let text = kite#utils#buffer_contents()
else
let action = 'skip'
let text = ''
endif
let [sel_start, sel_end] = kite#utils#selected_region_characters()
if [sel_start, sel_end] == [-1, -1]
return
endif
let selections = [{ 'start': sel_start, 'end': sel_end, 'encoding': 'utf-32' }]
let json = json_encode({
\ 'source': 'vim',
\ 'filename': filename,
\ 'text': text,
\ 'action': action,
\ 'selections': selections,
\ 'editor_version': kite#utils#vim_version(),
\ 'plugin_version': kite#utils#plugin_version()
\ })
let s:events_pending += 1
call kite#client#post_event(json, function('kite#events#handler', [bufnr('')]))
endfunction
function! kite#events#handler(bufnr, response)
let s:events_pending -= 1
call setbufvar(a:bufnr, 'kite_skip', (a:response.status == 0 || a:response.status == 403))
endfunction

View file

@ -0,0 +1,66 @@
function! kite#hover#hover()
if exists('b:kite_skip') && b:kite_skip | return | endif
if wordcount().bytes > kite#max_file_size() | return | endif
let filename = kite#utils#filepath(1)
let hash = kite#utils#buffer_md5()
let cursor = kite#utils#cursor_characters()
call kite#client#hover(filename, hash, cursor, function('kite#hover#handler'))
endfunction
function! kite#hover#goto_definition()
if &filetype != 'python'
call kite#utils#warn('Go to definition is only available for Python')
return
endif
if exists('b:kite_skip') && b:kite_skip | return | endif
if wordcount().bytes > kite#max_file_size() | return | endif
let filename = kite#utils#filepath(1)
let hash = kite#utils#buffer_md5()
let cursor = kite#utils#cursor_characters()
call kite#client#hover(filename, hash, cursor, function('kite#hover#goto_definition_handler'))
endfunction
function! kite#hover#handler(response)
if a:response.status == 200
let json = json_decode(a:response.body)
let sym = type(json.symbol) == v:t_list ? json.symbol[0] : json.symbol
let id = sym.id
if empty(id)
let b:kite_id = -1
else
let b:kite_id = id
endif
else
let b:kite_id = -1
endif
endfunction
function! kite#hover#goto_definition_handler(response)
if a:response.status != 200
call kite#utils#warn('unable to find a definition.')
return
endif
let json = json_decode(a:response.body)
let definition = json.report.definition
if type(definition) != type({})
call kite#utils#warn('unable to find a definition.')
return
endif
if definition.filename !=# expand('%:p')
execute 'edit' definition.filename
end
execute definition.line
normal! zz
endfunction

View file

@ -0,0 +1,65 @@
let s:languages_supported_by_kited = []
" Returns true if we want Kite completions for the current buffer, false otherwise.
function! kite#languages#supported_by_plugin()
" Return false if the file extension is not recognised by kited.
let recognised_extensions = [
\ 'c',
\ 'cc',
\ 'cpp',
\ 'cs',
\ 'css',
\ 'go',
\ 'h',
\ 'hpp',
\ 'html',
\ 'java',
\ 'js',
\ 'jsx',
\ 'kt',
\ 'less',
\ 'm',
\ 'php',
\ 'py',
\ 'pyw',
\ 'rb',
\ 'scala',
\ 'sh',
\ 'ts',
\ 'tsx',
\ 'vue',
\ ]
if index(recognised_extensions, expand('%:e')) == -1
return 0
endif
if g:kite_supported_languages == ['*']
return 1
endif
" Return false if the buffer's language is not one for which we want Kite completions.
if index(g:kite_supported_languages, &filetype) == -1
return 0
endif
return 1
endfunction
" Returns true if the current buffer's language is supported by kited, false otherwise.
function! kite#languages#supported_by_kited()
" Only check kited's languages once.
if empty(s:languages_supported_by_kited)
" A list of language names, e.g. ['bash', 'c', 'javascript', 'ruby', ...]
let s:languages_supported_by_kited = kite#client#languages(function('kite#languages#handler'))
endif
return index(s:languages_supported_by_kited, &filetype) != -1
endfunction
function! kite#languages#handler(response)
if a:response.status != 200 | return [] | endif
return json_decode(a:response.body)
endfunction

View file

@ -0,0 +1,29 @@
"
" Editor feature metrics
"
let s:prompted = 0
" Optional argument is value by which to increment named metric.
" Defaults to 1.
function! kite#metrics#requested(name)
call s:increment('vim_'.a:name.'_requested')
endfunction
function! kite#metrics#fulfilled(name)
call s:increment('vim_'.a:name.'_fulfilled')
endfunction
function! s:increment(name)
let json = json_encode({'name': a:name, 'value': 1})
call kite#client#counter(json, function('kite#metrics#handler'))
endfunction
function! kite#metrics#handler(response)
" Noop
endfunction

View file

@ -0,0 +1,77 @@
let s:text = [
\ 'Kite is now integrated with Vim',
\ '',
\ 'Kite is an AI-powered programming assistant',
\ 'that shows you the right information at the right',
\ 'time to keep you in the flow.',
\ '',
\ 'Please choose:',
\ '',
\ 'Learn how to use Kite',
\ 'Hide',
\ 'Hide forever',
\ ]
let s:option = 'onboarding_required'
function! kite#onboarding#call(force)
if !a:force
if !kite#utils#get_setting(s:option, 1)
return
endif
endif
call kite#client#onboarding_file(function('kite#onboarding#handler'))
endfunction
function! kite#onboarding#handler(response) abort
if a:response.status == 200
silent execute 'tabedit' json_decode(a:response.body)
call kite#utils#set_setting(s:option, 0)
else
if exists('*popup_menu')
if !has('patch-8.1.1799')
call s:unmap_menu_keys()
endif
let title = s:text[0]
let winid = popup_menu(s:text[1:], {
\ 'title': ' '.title.' ',
\ 'callback': 'kite#onboarding#popup_callback',
\ })
call win_execute(winid, "normal! ".repeat('j', len(s:text[1:])-3))
else
let s:text[-3] = '1. '.s:text[-3]
let s:text[-2] = '2. '.s:text[-2]
let s:text[-1] = '3. '.s:text[-1]
call s:handle_choice(inputlist(s:text)-1)
endif
endif
endfunction
" Invoked when popup closes.
function! kite#onboarding#popup_callback(_, result)
call s:handle_choice(2 - len(s:text[1:]) + a:result)
endfunction
function! s:handle_choice(index)
if a:index == 0 " learn now
call kite#utils#browse('https://help.kite.com/category/47-vim-integration')
elseif a:index == 2 " hide forever
call kite#utils#set_setting(s:option, 0)
endif
endfunction
function! s:unmap_menu_keys()
silent! nunmap <CR>
silent! nunmap <Space>
silent! nunmap j
silent! nunmap k
silent! nunmap x
endfunction

View file

@ -0,0 +1,209 @@
let s:completion_counter = 0
function! kite#signature#increment_completion_counter()
let s:completion_counter = s:completion_counter + 1
endfunction
function! kite#signature#completion_counter()
return s:completion_counter
endfunction
function! kite#signature#handler(counter, startcol, response) abort
call kite#utils#log('signature: '.a:response.status)
" Ignore old completion results.
if a:counter != s:completion_counter
return
endif
if a:response.status != 200
return
endif
let json = json_decode(a:response.body)
let call = g:kite#document#Document.New(json.calls[0])
let function_name = call.dig('func_name', '')
if empty(function_name)
let function_name = call.dig('callee.repr', '')
endif
let function_name = split(function_name, '\.')[-1]
let spacer = {'word': '', 'empty': 1, 'dup': 1}
let indent = ' '
let completions = []
let wrap_width = 50
"
" Signature
"
let parameters = []
let return_type = ''
let [current_arg, in_kwargs] = [call.dig('arg_index', 0), call.dig('language_details.python.in_kwargs', 0)]
let kind = call.dig('callee.kind', '')
" 1. Name of function with parameters.
if kind ==# 'function'
" 1.b.1. Parameters
for parameter in call.dig('callee.details.function.parameters', [])
" 1.b.1.a. Name
let name = parameter.name
" 1.b.1.b. Default value
if kite#utils#present(parameter.language_details.python, 'default_value')
let name .= '='.parameter.language_details.python.default_value[0].repr
endif
" 2. Highlight current argument
if !in_kwargs && len(parameters) == current_arg
let name = '*'.name.'*'
endif
call add(parameters, name)
endfor
" 1.b.2. vararg indicator
let vararg = call.dig('callee.details.function.language_details.python.vararg', {})
if !empty(vararg)
call add(parameters, '*'.vararg.name)
endif
" 1.b.3. keyword arguments indicator
let kwarg = call.dig('callee.details.function.language_details.python.kwarg', {})
if !empty(kwarg)
call add(parameters, '**'.kwarg.name)
endif
" 1.b.4. Return type
let return_value = call.dig('callee.details.function.return_value', [])
if !empty(return_value)
let return_type = ' -> '.return_value[0].type
endif
elseif kind ==# 'type'
" 1.c.1. Parameters
for parameter in call.dig('callee.details.type.language_details.python.constructor.parameters', [])
" 1.c.1.a. Name
let name = parameter.name
" 1.c.1.b. Default value
if kite#utils#present(parameter.language_details.python, 'default_value')
let name .= '='.parameter.language_details.python.default_value[0].repr
endif
" 2. Highlight current argument
if !in_kwargs && len(parameters) == current_arg
let name = '*'.name.'*'
endif
call add(parameters, name)
endfor
" 1.c.2. vararg indicator
let vararg = call.dig('callee.details.type.language_details.python.constructor.language_details.python.vararg', {})
if !empty(vararg)
call add(parameters, '*'.vararg.name)
endif
" 1.c.3. keyword arguments indicator
let kwarg = call.dig('callee.details.type.language_details.python.constructor.language_details.python.kwarg', {})
if !empty(kwarg)
call add(parameters, '*'.kwarg.name)
endif
" 1.c.4. Return type
let return_type = ' -> '.function_name
endif
" The completion popup does not wrap long lines so we wrap manually.
for line in kite#utils#wrap(kite#symbol().' '.function_name.'('.join(parameters, ', ').')'.return_type, wrap_width, 4)
let completion = {
\ 'word': '',
\ 'abbr': line,
\ 'empty': 1,
\ 'dup': 1
\ }
call add(completions, completion)
endfor
" 3. Keyword arguments
let kwarg_parameters = call.dig('callee.details.function.kwarg_parameters', [])
if !empty(kwarg_parameters)
call add(completions, spacer)
call add(completions, s:heading('**kw'))
for kwarg in kwarg_parameters
let name = kwarg.name
let types = kite#utils#map_join(kwarg.inferred_value, 'repr', '|')
if empty(types)
let types = ''
endif
call add(completions, {
\ 'word': name.'=',
\ 'abbr': indent.name,
\ 'menu': types,
\ 'empty': 1,
\ 'dup': 1
\ })
endfor
endif
" 4. Popular patterns
if kite#signature#should_show_popular_patterns()
let signatures = call.dig('signatures', [])
if len(signatures) > 0
call add(completions, spacer)
call add(completions, s:heading('How Others Used This'))
endif
for signature in signatures
let sigdoc = g:kite#document#Document.New(signature)
" b. Arguments
let arguments = []
for arg in sigdoc.dig('args', [])
call add(arguments, arg.name)
endfor
" c. Keyword arguments
for kwarg in sigdoc.dig('language_details.python.kwargs', [])
let name = kwarg.name
let examples = kite#utils#coerce(kwarg.types[0], 'examples', [])
if len(examples) > 0
let name .= '='.examples[0]
endif
call add(arguments, name)
endfor
for line in kite#utils#wrap(function_name.'('.join(arguments, ', ').')', wrap_width, 2)
let completion = {
\ 'word': '',
\ 'abbr': indent.line,
\ 'empty': 1,
\ 'equal': 1,
\ 'dup': 1
\ }
call add(completions, completion)
endfor
endfor
endif
if mode(1) ==# 'i'
call complete(a:startcol+1, completions)
endif
endfunction
function! kite#signature#should_show_popular_patterns()
return kite#utils#get_setting('show_popular_patterns', 0)
endfunction
function! kite#signature#show_popular_patterns()
call kite#utils#set_setting('show_popular_patterns', 1)
endfunction
function! kite#signature#hide_popular_patterns()
call kite#utils#set_setting('show_popular_patterns', 0)
endfunction
function s:heading(text)
return {'abbr': a:text.':', 'word': '', 'empty': 1, 'dup': 1}
endfunction

View file

@ -0,0 +1,413 @@
function! s:setup_stack()
if exists('b:kite_stack') | return | endif
" stack:
" [
" { index: 0, placeholders: { ... } }, <-- depth 0
" { index: 0, placeholders: { ... } }, <-- depth 1
" ... <-- depth n
" ]
"
" index - the currently active placeholder at that depth
let b:kite_stack = {'stack': []}
function! b:kite_stack.pop()
return remove(self.stack, -1)
endfunction
function! b:kite_stack.peek()
return get(self.stack, -1)
endfunction
function! b:kite_stack.push(item)
call add(self.stack, a:item)
endfunction
function! b:kite_stack.is_empty()
return empty(self.stack)
endfunction
function! b:kite_stack.empty()
let self.stack = []
endfunction
endfunction
function! kite#snippet#complete_done()
if empty(v:completed_item) | return | endif
call s:setup_stack()
if has_key(v:completed_item, 'user_data') && !empty(v:completed_item.user_data)
let placeholders = json_decode(v:completed_item.user_data).placeholders
elseif exists('b:kite_completions') && has_key(b:kite_completions, v:completed_item.word)
let placeholders = json_decode(b:kite_completions[v:completed_item.word]).placeholders
let b:kite_completions = {}
else
return
endif
" Send the edit event. Normally this is sent automatically on TextChanged(I).
" But for some reason this doesn't fire when a completion has a snippet placeholder.
call kite#events#event('edit')
if empty(placeholders)
if b:kite_stack.is_empty()
return
else
call kite#snippet#next_placeholder()
return
endif
endif
let b:kite_linenr = line('.')
let b:kite_line_length = col('$')
call s:setup_maps()
call s:setup_autocmds()
" Calculate column number (col_begin) of start of each placeholder, and placeholder length.
let inserted_text = v:completed_item.word
let insertion_start = col('.') - strdisplaywidth(inserted_text)
let b:kite_insertion_end = col('.')
for ph in placeholders
let ph.col_begin = insertion_start + ph.begin
let ph.length = ph.end - ph.begin
unlet ph.begin ph.end
endfor
" Update placeholder locations.
"
" todo move this into the push() function?
" note this is very similar to s:update_placeholder_locations()
if !b:kite_stack.is_empty()
" current placeholder which has just been completed
let level = b:kite_stack.peek()
let ph = level.placeholders[level.index]
let ph_new_length = col('.') - ph.col_begin
let ph_length_delta = ph_new_length - ph.length
let ph.length = ph_new_length
let marker = ph.col_begin
" following placeholders at same level
for ph in level.placeholders[level.index+1:]
let ph.col_begin += ph_length_delta
endfor
" placeholders at outer levels
for level in b:kite_stack.stack[:-2]
for ph in level.placeholders
if ph.col_begin > marker
let ph.col_begin += ph_length_delta
endif
endfor
endfor
endif
call b:kite_stack.push({'placeholders': placeholders, 'index': 0})
" Move to first placeholder.
call s:placeholder(0)
endfunction
" Go to next placeholder at current level, if there is one, or first placeholder at next level otherwise.
function! kite#snippet#next_placeholder()
call s:update_placeholder_locations()
call s:placeholder(b:kite_stack.peek().index + 1)
endfunction
function! kite#snippet#previous_placeholder(...)
call s:placeholder(b:kite_stack.peek().index - 1 - (a:0 ? a:1 : 0))
endfunction
" Move to the placeholder at index and select its text.
function! s:placeholder(index)
let index = a:index
let level = b:kite_stack.peek()
let placeholders = level.placeholders
" Clear highlights before we pop the stack.
call s:clear_all_placeholder_highlights()
if index < 0
" If no other levels in stack
if len(b:kite_stack.stack) == 1
" Stay with first placeholder and proceed
let index = 0
else
call b:kite_stack.pop()
call s:placeholder(b:kite_stack.peek().index - 1)
return
endif
endif
" if navigating forward from last placeholder of current level
if index == len(placeholders)
" If no other levels in stack
if len(b:kite_stack.stack) == 1
call s:goto_initial_completion_end()
else
call b:kite_stack.pop()
call s:placeholder(b:kite_stack.peek().index + 1)
endif
return
endif
call s:highlight_current_level_placeholders()
let level.index = index
let ph = placeholders[index]
" store line length before placeholder gets changed by user
" let b:kite_line_length = col('$')
if ph.length == 0
normal! h
return
endif
" insert mode -> normal mode
stopinsert
let linenr = line('.')
call setpos("'<", [0, linenr, ph.col_begin])
call setpos("'>", [0, linenr, ph.col_begin + ph.length - (mode() == 'n' ? 1 : 0)])
" normal mode -> visual mode -> select mode
execute "normal! gv\<C-G>"
if mode() ==# 'S'
execute "normal! \<C-O>gh"
endif
endfunction
function! s:goto_initial_completion_end()
" call setpos('.', [0, b:kite_linenr, b:kite_insertion_end + col('$') - b:kite_line_length - 1])
call setpos('.', [0, b:kite_linenr, col('$')])
startinsert!
call s:teardown()
endfunction
" Adjust current and subsequent placeholders for the amount of text entered
" at the placeholder we are leaving.
function! s:update_placeholder_locations()
if !exists('b:kite_line_length') | return | endif
let line_length_delta = col('$') - b:kite_line_length
" current placeholder
let level = b:kite_stack.peek()
let ph = level.placeholders[level.index]
let marker = ph.col_begin
let ph.length += line_length_delta
" subsequent placeholders at current level
for ph in level.placeholders[level.index+1:]
let ph.col_begin += line_length_delta
endfor
" placeholders at outer levels
for level in b:kite_stack.stack[:-2]
for ph in level.placeholders
if ph.col_begin > marker
let ph.col_begin += line_length_delta
endif
endfor
endfor
let b:kite_line_length = col('$')
endfunction
function! s:highlight_current_level_placeholders()
let group = s:highlight_group_for_placeholders()
if empty(group) | return | endif
let linenr = line('.')
for ph in b:kite_stack.peek().placeholders
let ph.matchid = matchaddpos(group, [[linenr, ph.col_begin, ph.length]])
endfor
endfunction
" Clears highlights of placeholders in the stack.
"
" Note: if we need a way to clear highlights of placeholders which are no
" longer in the stack (because they have been popped) we could use a custom
" highlight group (e.g. KiteUnderline linked to Underline), call getmatches(),
" and remove all matches using the custom highlight group.
function! s:clear_all_placeholder_highlights()
for level in b:kite_stack.stack
for ph in level.placeholders
if has_key(ph, 'matchid')
call matchdelete(ph.matchid)
unlet ph.matchid
endif
endfor
endfor
endfunction
" Many plugins use vmap for visual-mode mappings but vmap maps both
" visual-mode and select-mode (they should use xmap instead). Assume any
" visual-mode mappings for printable characters are not wanted and remove them
" (but remember them so we can restore them afterwards). Similarly for map.
" Assume any select-only-mode maps are deliberate.
"
" :help mapmode-s
" :help Select-mode-mapping
function! s:remove_smaps_for_printable_characters()
let b:kite_maps = []
let printable_keycodes = [
\ '<Space>',
\ '<Bslash>',
\ '<Tab>',
\ '<C-Tab>',
\ '<NL>',
\ '<CR>',
\ '<BS>',
\ '<Leader>',
\ '<LocalLeader>'
\ ]
" Get a list of maps active in select mode.
for scope in ['<buffer>', '']
redir => maps | silent execute 'smap' scope | redir END
let mappings = split(maps, "\n")
" 'No mapping found' or localised equivalent (starts with capital letter).
if len(mappings) == 1 && mappings[0][0] =~ '\u' | continue | endif
" Assume select-mode maps are deliberate and ignore them.
call filter(mappings, 'v:val[0:2] !~# "s"')
for mapping in mappings
let lhs = matchlist(mapping, '\v^...(\S+)\s.*')[1]
" ^^^ ^^^
" mode lhs
" Ignore keycodes for non-printable characters, e.g. <Left>
if lhs[0] == '<' && index(printable_keycodes, lhs) == -1 | continue | endif
" Remember the mapping so we can restore it later.
call add(b:kite_maps, maparg(lhs, 's', 0, 1))
" Remove the mapping.
silent! execute 'sunmap' scope lhs
endfor
endfor
endfunction
function! s:restore_smaps()
for mapping in b:kite_maps
silent! execute mapping.mode . (mapping.noremap ? 'nore' : '') . 'map '
\ . (mapping.buffer ? '<buffer> ' : '')
\ . (mapping.expr ? '<expr> ' : '')
\ . (mapping.nowait ? '<nowait> ' : '')
\ . (mapping.silent ? '<silent> ' : '')
\ . mapping.lhs . ' '
\ . substitute(mapping.rhs, '<SID>', '<SNR>'.mapping.sid.'_', 'g')
endfor
unlet! b:kite_maps
endfunction
function! s:setup_maps()
execute 'inoremap <buffer> <silent> <expr>' g:kite_next_placeholder 'pumvisible() ? "<C-Y>" : "<C-\><C-O>:call kite#snippet#next_placeholder()<CR>"'
execute 'inoremap <buffer> <silent> <expr>' g:kite_previous_placeholder 'pumvisible() ? "<C-Y><C-G>:<C-U>call kite#snippet#previous_placeholder(2)<CR>" : "<C-\><C-O>:call kite#snippet#previous_placeholder()<CR>"'
execute 'snoremap <buffer> <silent>' g:kite_next_placeholder '<Esc>:call kite#snippet#next_placeholder()<CR>'
execute 'snoremap <buffer> <silent>' g:kite_previous_placeholder '<Esc>:call kite#snippet#previous_placeholder()<CR>'
call s:remove_smaps_for_printable_characters()
endfunction
function! kite#snippet#teardown_maps()
execute 'silent! iunmap <buffer>' g:kite_next_placeholder
execute 'silent! iunmap <buffer>' g:kite_previous_placeholder
execute 'silent! sunmap <buffer>' g:kite_next_placeholder
execute 'silent! sunmap <buffer>' g:kite_previous_placeholder
endfunction
function! s:setup_autocmds()
augroup KiteSnippets
autocmd! * <buffer>
autocmd CursorMovedI <buffer>
\ call s:update_placeholder_locations() |
\ call s:clear_all_placeholder_highlights() |
\ call s:highlight_current_level_placeholders()
autocmd CursorMoved,CursorMovedI <buffer> call s:cursormoved()
autocmd InsertLeave <buffer> call s:insertleave()
augroup END
endfunction
function! s:teardown_autocmds()
autocmd! KiteSnippets * <buffer>
endfunction
" Called to deactivate all placeholders.
function! s:teardown()
call s:clear_all_placeholder_highlights()
call kite#snippet#teardown_maps()
call s:teardown_autocmds()
call s:restore_smaps()
call b:kite_stack.empty()
unlet! b:kite_linenr b:kite_line_length b:kite_insertion_end
endfunction
function! s:highlight_group_for_placeholders()
for group in ['Special', 'SpecialKey', 'Underline', 'DiffChange']
if hlexists(group)
return group
endif
endfor
return ''
endfunction
function! s:cursormoved()
if !exists('b:kite_linenr') | return | endif
if b:kite_linenr == line('.') | return | endif
" TODO check whether the cursor is outside the bounds of the completion?
call s:teardown()
endfunction
function! s:insertleave()
" Modes established by experimentation.
if mode(1) !=# 's' && mode(1) !=# ((has('patch-8.1.0225') || has('nvim-0.4.0')) ? 'niI' : 'n')
call s:teardown()
endif
endfunction
function! s:debug_stack()
if b:kite_stack.is_empty()
echom 'stack empty'
return
endif
let i = 0
for level in b:kite_stack.stack
echom 'level' i
echom ' index' level.index
for pholder in level.placeholders
echom ' '.string(pholder)
endfor
let i += 1
endfor
endfunction

View file

@ -0,0 +1,62 @@
" Updates the status of the current buffer.
"
" Optional argument is a timer id (when called by a timer).
function! kite#status#status(...)
if !s:status_in_status_line() | return | endif
let buf = bufnr('')
let msg = 'NOT SET'
" Check kited status (installed / running) every 10 file status checks.
let counter = getbufvar(buf, 'kite_status_counter', 0)
if counter == 0
if !kite#utils#kite_running()
let msg = 'Kite: not running'
if !kite#utils#kite_installed()
let msg = 'Kite: not installed'
endif
endif
endif
call setbufvar(buf, 'kite_status_counter', (counter + 1) % 10)
if wordcount().bytes > kite#max_file_size()
let msg = 'Kite: file too large'
endif
if msg !=# 'NOT SET'
if msg !=# getbufvar(buf, 'kite_status')
call setbufvar(buf, 'kite_status', msg)
redrawstatus
endif
return
endif
let filename = kite#utils#filepath(0)
call kite#client#status(filename, function('kite#status#handler', [buf]))
endfunction
function! kite#status#handler(buffer, response)
call kite#utils#log('kite status status: '.a:response.status.', body: '.a:response.body)
if a:response.status != 200 | return | endif
let json = json_decode(a:response.body)
let msg = ''
let suffix = get(json, 'short', 'FIELD MISSING')
if suffix !=# 'FIELD MISSING'
let msg = join(['Kite: ', suffix], '')
endif
if msg !=# getbufvar(a:buffer, 'kite_status')
call setbufvar(a:buffer, 'kite_status', msg)
redrawstatus
endif
endfunction
function! s:status_in_status_line()
return stridx(&statusline, 'kite#statusline()') != -1
endfunction

View file

@ -0,0 +1,611 @@
" Values for s:os are used in plugin directory structure
" and also metric values.
if has('win64') || has('win32') || has('win32unix')
let s:os = 'windows'
else
let s:os = empty(findfile('/sbin/launchd')) ? 'linux' : 'macos'
endif
function! kite#utils#windows()
return s:os ==# 'windows'
endfunction
function! kite#utils#macos()
return s:os ==# 'macos'
endfunction
let s:separator = !exists('+shellslash') || &shellslash ? '/' : '\'
let s:plugin_dir = expand('<sfile>:p:h:h:h')
let s:doc_dir = s:plugin_dir.s:separator.'doc'
let s:lib_dir = s:plugin_dir.s:separator.'lib'
let s:lib_subdir = s:lib_dir.s:separator.(s:os)
let s:vim_version = ''
let s:plugin_version = ''
function! kite#utils#vim_version()
if !empty(s:vim_version)
return s:vim_version
endif
let s:vim_version = kite#utils#normalise_version(execute('version'))
return s:vim_version
endfunction
function! kite#utils#normalise_version(version)
let lines = split(a:version, '\n')
if lines[0] =~ 'NVIM'
" Or use api_info().version.
return lines[0] " e.g. NVIM v0.2.2
else
let [major, minor] = matchlist(lines[0], '\v(\d)\.(\d+)')[1:2]
let patch_line = match(lines, ': \d')
if patch_line == -1
let patches = '0'
else
let patches = substitute(split(lines[patch_line], ': ')[1], ' ', '', 'g')
endif
return join([major, minor, patches], '.') " e.g. 8.1.1-582
endif
endfunction
function! kite#utils#plugin_version()
if !empty(s:plugin_version)
return s:plugin_version
endif
let s:plugin_version = readfile(s:plugin_dir.s:separator.'VERSION')[0]
return s:plugin_version
endfunction
" From tpope/vim-fugitive
function! s:winshell()
return kite#utils#windows() && &shellcmdflag !~# '^-'
endfunction
function! kite#utils#browse(url)
if kite#utils#windows()
let cmd = 'cmd /c start "" "'.a:url.'"'
else
let cmd = 'open "'.a:url.'"'
endif
silent call system(cmd)
endfunction
" From tpope/vim-fugitive
function! s:shellescape(arg)
if a:arg =~ '^[A-Za-z0-9_/.-]\+$'
return a:arg
elseif s:winshell()
return '"'.substitute(substitute(a:arg, '"', '""', 'g'), '%', '"%"', 'g').'"'
else
return shellescape(a:arg)
endif
endfunction
if kite#utils#windows()
let s:settings_dir = join([$LOCALAPPDATA, 'Kite'], s:separator)
else
let s:settings_dir = expand('~/.kite')
endif
if !isdirectory(s:settings_dir)
call mkdir(s:settings_dir, 'p')
endif
let s:settings_path = s:settings_dir.s:separator.'vim-plugin.json'
" Get the value for the given key.
" If the key has not been set, returns the default value if given
" (i.e. the optional argument) or -1 otherwise.
function! kite#utils#get_setting(key, ...)
let settings = s:settings()
return get(settings, a:key, (a:0 ? a:1 : -1))
endfunction
" Sets the value for the key.
function! kite#utils#set_setting(key, value)
let settings = s:settings()
let settings[a:key] = a:value
let json_str = json_encode(settings)
call writefile([json_str], s:settings_path)
endfunction
function! s:settings()
if filereadable(s:settings_path)
let json_str = join(readfile(s:settings_path), '')
return json_decode(json_str)
else
return {}
endif
endfunction
function! kite#utils#os()
return s:os
endfunction
function! kite#utils#lib(filename)
return s:lib_subdir.s:separator.a:filename
endfunction
function! kite#utils#kite_installed()
return !empty(s:kite_install_path())
endfunction
" Returns the kite installation path including the filename, or an empty string if not installed.
function! s:kite_install_path()
if kite#utils#windows()
let output = kite#async#sync('reg query HKEY_LOCAL_MACHINE\Software\Kite\AppData /v InstallPath /s /reg:64')
let lines = filter(split(output, '\n'), 'v:val =~ "InstallPath"')
if empty(lines)
return ''
endif
return substitute(lines[0], '\v^\s+InstallPath\s+REG_\w+\s+', '', '').s:separator.'kited.exe'
elseif kite#utils#macos()
return kite#async#sync('mdfind ''kMDItemCFBundleIdentifier = "com.kite.Kite" || kMDItemCFBundleIdentifier = "enterprise.kite.Kite"''')
else
let path = exepath('/opt/kite/kited')
if !empty(path)
return path
endif
let path = exepath(expand('~/.local/share/kite/kited'))
if !empty(path)
return path
endif
return ''
endif
endfunction
function! kite#utils#kite_running()
if kite#utils#windows()
let [cmd, process] = ['tasklist /FI "IMAGENAME eq kited.exe"', '^kited.exe']
elseif kite#utils#macos()
let [cmd, process] = ['ps -axco command', '^Kite$']
else
let process_name = empty($KITED_TEST_PORT) ? 'kited' : 'kited-test'
let [cmd, process] = ['ps -axco command', '^'.process_name.'$']
endif
return match(split(kite#async#sync(cmd), '\n'), process) > -1
endfunction
function! kite#utils#launch_kited()
if kite#utils#kite_running()
return
endif
let path = s:kite_install_path()
if empty(path)
return
endif
if kite#utils#windows()
let $KITE_SKIP_ONBOARDING = 1
silent execute "!start" s:shellescape(path)
elseif kite#utils#macos()
call system('open -a '.path.' --args "--plugin-launch"')
else
silent execute '!'.path.' --plugin-launch >/dev/null 2>&1 &'
endif
endfunction
" msg - a list or a string
function! kite#utils#log(msg)
if g:kite_log
if type(a:msg) == v:t_string
let msg = [a:msg]
else
let msg = a:msg
endif
call writefile(msg, 'kite-vim.log', 'a')
endif
endfunction
function! kite#utils#warn(msg)
echohl WarningMsg
echom 'Kite: '.a:msg
echohl None
let v:warningmsg = a:msg
endfunction
function! kite#utils#info(msg)
echohl Question
echom a:msg
echohl None
endfunction
" Returns the absolute path to the current file after resolving symlinks.
"
" url_format - when truthy, return the path in a URL-compatible format.
function! kite#utils#filepath(url_format)
let path = resolve(expand('%:p'))
if a:url_format
let path = substitute(path, '[\/]', ':', 'g')
if kite#utils#windows()
let path = substitute(path, '^\(\a\)::', '\1:', '')
let path = ':windows:'.path
endif
let path = kite#utils#url_encode(path)
endif
return path
endfunction
" Returns a 2-element list of 0-based character indices into the buffer.
"
" When no text is selected, both elements are the cursor position.
"
" When text is selected, the elements are the start (inclusive) and
" end (exclusive) of the selection.
"
" Returns [-1, -1] when not in normal, insert, or visual mode.
function! kite#utils#selected_region_characters()
return s:selected_region('c')
endfunction
" Returns a 2-element list of 0-based byte indices into the buffer.
"
" When no text is selected, both elements are the cursor position.
"
" When text is selected, the elements are the start (inclusive) and
" end (exclusive) of the selection.
"
" Returns [-1, -1] when not in normal, insert, or visual mode.
function! kite#utils#selected_region_bytes()
return s:selected_region('b')
endfunction
" Returns a 2-element list of 0-based indices into the buffer.
"
" When no text is selected, both elements are the cursor position.
"
" When text is selected, the elements are the start (inclusive) and
" end (exclusive) of the selection.
"
" Returns [-1, -1] when not in normal, insert, or visual mode.
"
" param type (String) - 'c' for character indices, 'b' for byte indices
"
" NOTE: the cursor is moved during the function (but finishes where it started).
function! s:selected_region(type)
if mode() ==# 'n' || mode() ==# 'i'
if a:type == 'c'
let offset = kite#utils#character_offset()
else
let offset = kite#utils#byte_offset_start()
endif
return [offset, offset]
endif
if mode() ==? 'v'
let pos_start = getpos('v')
let pos_end = getpos('.')
if (pos_start[1] > pos_end[1]) || (pos_start[1] == pos_end[1] && pos_start[2] > pos_end[2])
let [pos_start, pos_end] = [pos_end, pos_start]
endif
" switch to normal mode
execute "normal! \<Esc>"
call setpos('.', pos_start)
if a:type == 'c'
let offset1 = kite#utils#character_offset()
else
let offset1 = kite#utils#byte_offset_start()
endif
call setpos('.', pos_end)
" end position is exclusive
if a:type == 'c'
let offset2 = kite#utils#character_offset() + 1
else
let offset2 = kite#utils#byte_offset_end() + 1
endif
" restore visual selection
normal! gv
return [offset1, offset2]
endif
return [-1, -1]
endfunction
" Returns the 0-based index of the cursor in the buffer.
"
" Returns -1 when the buffer is empty.
function! kite#utils#cursor_characters()
if mode() ==? 'v'
" switch to normal mode
execute "normal! \<Esc>"
let cursor = kite#utils#character_offset()
" restore visual selection
normal! gv
return cursor
endif
return kite#utils#character_offset()
endfunction
" Returns the 0-based index into the buffer of the cursor position.
" Returns -1 when the buffer is empty.
"
" Does not work in visual mode.
function! kite#utils#character_offset()
" wordcount().cursor_chars is 1-based so we need to subtract 1.
let offset = wordcount().cursor_chars - 1
" In insert mode the cursor isn't really between two characters;
" it is actually on the second character, but that's what we want
" anyway.
" If the cursor is just before (i.e. on) the end of the line, and
" the file has dos line endings, wordcount().cursor_chars will
" regard the cursor as on the second character of the \r\n. In this
" case we want the offset of the first, i.e. the \r.
if col('.') == col('$') && &ff ==# 'dos'
let offset -= 1
endif
return offset
endfunction
" Returns the 0-based index into the buffer of the cursor position.
" If the cursor is on a multibyte character, it reports the character's
" first byte.
function! kite#utils#byte_offset_start()
let offset = line2byte(line('.')) - 1 + col('.') - 1
if offset < 0
let offset = 0
endif
return offset
endfunction
" Returns the 0-based index into the buffer of the cursor position.
" If the cursor is on a multibyte character, it reports the character's
" last byte.
function! kite#utils#byte_offset_end()
let offset = wordcount().cursor_bytes - 1
if offset < 0
let offset = 0
endif
return offset
endfunction
function! kite#utils#buffer_contents()
let line_ending = {"unix": "\n", "dos": "\r\n", "mac": "\r"}[&fileformat]
return join(getline(1, '$'), line_ending).(&eol ? line_ending : '')
endfunction
" Similar to the goto command, but for characters.
" index is 1-based, the start of the file.
function! kite#utils#goto_character(index)
call search('\m\%^\_.\{'.a:index.'}', 'es')
" The search() function above counts a newline as 1 character even if it is
" actually 2. Therefore we need to adjust the cursor position when newlines
" are 2 characters.
if &ff ==# 'dos'
let [_whichwrap, &whichwrap] = [&whichwrap, "h,l"]
let delta = wordcount().cursor_chars - a:index
while delta != 0
" Cannot land on a newline character.
if (delta == -1 || delta == -2) && col('.') == col('$') - 1
break
endif
execute "normal! ".delta.(delta > 0 ? 'h' : 'l')
let delta = wordcount().cursor_chars - a:index
endwhile
let &whichwrap = _whichwrap
endif
endfunction
" Returns the MD5 hash of the buffer contents.
function! kite#utils#buffer_md5()
return s:MD5(kite#utils#buffer_contents())
endfunction
" https://github.com/tpope/vim-unimpaired/blob/3a7759075cca5b0dc29ce81f2747489b6c8e36a7/plugin/unimpaired.vim#L327-L329
function! kite#utils#url_encode(str)
return substitute(a:str,'[^A-Za-z0-9_.~-]','\="%".printf("%02X",char2nr(submatch(0)))','g')
endfunction
" Capitalises the first letter of str.
function! kite#utils#capitalize(str)
return substitute(a:str, '^.', '\u\0', '')
endfunction
function! kite#utils#map_join(list, prop, sep)
return join(map(copy(a:list), {_,v -> v[a:prop]}), a:sep)
endfunction
" Returns a list of lines, each no longer than length.
" The last line may be longer than length if it has no spaces.
" Assumes str is a constructor or function call.
"
" Example: json.dumps
"
" dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, encoding, default, sort_keys, *args, **kwargs)
"
" - becomes when wrapped:
"
" dumps(obj, skipkeys, ensure_ascii, check_circular,
" allow_nan, cls, indent, separators, encoding,
" default, sort_keys, *args, **kwargs)
"
function! kite#utils#wrap(str, length, indent)
let lines = []
let str = a:str
let [prefix; str] = split(a:str, '(\zs')
let str = join(str)
while v:true
let line = prefix.str
if len(line) <= a:length
call add(lines, line)
break
endif
let i = strridx(str[0:a:length-len(prefix)], ' ')
if i == -1
call add(lines, line)
break
endif
let line = prefix . str[0:i-1]
call add(lines, line)
let str = str[i+1:]
let prefix = repeat(' ', a:indent)
endwhile
return lines
endfunction
function! kite#utils#coerce(dict, key, default)
if has_key(a:dict, a:key)
let v = a:dict[a:key]
if type(v) == type(a:default) " check type in case of null
return v
endif
endif
return a:default
endfunction
function! kite#utils#present(dict, key)
return has_key(a:dict, a:key) && !empty(a:dict[a:key])
endfunction
" Returns a string of the given length.
"
" If length is 0 or negative, returns an empty string.
"
" If text is less than length, it is padded with leading spaces so that it is
" right-aligned.
"
" If text is greater than length, it is truncated with an ellipsis.
" If there isn't room for an ellipsis, or room for only an ellipsis, empty spaces are used.
function! kite#utils#ralign(text, length)
if a:length <= 0
return ''
endif
let text_width = strdisplaywidth(a:text)
" The required length
if text_width == a:length
return a:text
endif
" Less than the required length: left-pad
if text_width < a:length
return repeat(' ', a:length-text_width) . a:text
endif
" Greater than the required length: truncate
if kite#utils#windows()
let ellipsis = '...'
else
let ellipsis = '…'
endif
let ellipsis_width = strdisplaywidth(ellipsis)
if ellipsis_width >= a:length
return repeat(' ', a:length)
endif
return a:text[: a:length-ellipsis_width-1] . ellipsis
endfunction
function! kite#utils#truncate(text, length)
let text_width = strdisplaywidth(a:text)
if text_width <= a:length
return a:text
endif
if kite#utils#windows()
let ellipsis = '...'
else
let ellipsis = '…'
endif
let ellipsis_width = strdisplaywidth(ellipsis)
if ellipsis_width >= a:length
return a:text[0] . ellipsis[0: a:length-2]
endif
return a:text[: a:length-ellipsis_width-1] . ellipsis
endfunction
function! s:chomp(str)
return substitute(a:str, '\n$', '', '')
endfunction
function! s:md5(text)
return s:chomp(system('md5', a:text))
endfunction
function! s:md5sum(text)
return split(system('md5sum', a:text), ' ')[0]
endfunction
function! s:md5bin(text)
return s:chomp(system(s:md5_binary, a:text))
endfunction
if executable('md5')
let s:MD5 = function('s:md5')
elseif executable('md5sum')
let s:MD5 = function('s:md5sum')
else
let s:md5_binary = kite#utils#lib('md5Sum.exe')
let s:MD5 = function('s:md5bin')
endif

View file

@ -0,0 +1,35 @@
*kite.txt* Kite for VIM
Kite for VIM
============
VIM is now integrated with Kite! To get a taste of what Kite can do, open a
saved Python file and start coding away.
1. Autocompletions
As you code, Kite will provide autocompletion suggestions ranked by popularity
using all the open source code on GitHub.
2. Documentation
Press |K| when your cursor is over a identifier to open a split window with
documentation about the identifier. In addition to documentation, Kite also
provides information about where you've used the identifier in your codebase,
as well as curated code examples showing you how to use the identifier.
3. Goto Definition
Press |C-]| to jump to a method's defintion.
4. Copilot integration
While you code in VIM, the Copilot will automatically show you information
about the code that you're currently working with. To open the Copilot, click
on the Kite menubar icon and select "Open Copilot".
To learn more about Kite and how to use the VIM plugin, visit our [help
page](http://help.kite.com).
vim:tw=78:et:ft=help:norl

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,105 @@
if exists('g:loaded_kite') || &cp
finish
endif
if has('nvim')
if !has('nvim-0.3')
echoerr 'Kite requires Neovim 0.3 or greater'
finish
endif
else
if v:version < 800 || !has('patch-8.0.0027')
echoerr 'Kite requires Vim 8.0.0027 or greater'
finish
endif
endif
let g:loaded_kite = 1
filetype on
" The list of languages / file types for which we want Kite's completions.
if !exists('g:kite_supported_languages')
let g:kite_supported_languages = ['python']
endif
if !exists('g:kite_auto_complete')
let g:kite_auto_complete = 1
endif
if !exists('g:kite_snippets')
let g:kite_snippets = 1
endif
if !exists('g:kite_previous_placeholder')
let g:kite_previous_placeholder = '<C-K>'
endif
if !exists('g:kite_next_placeholder')
let g:kite_next_placeholder = '<C-J>'
endif
if !exists('g:kite_documentation_continual')
let g:kite_documentation_continual = 0
endif
if !exists('g:kite_completions')
let g:kite_completions = 1
endif
if !exists('g:kite_log')
let g:kite_log = 0
endif
if !exists('g:kite_short_timeout')
let g:kite_short_timeout = 120 " ms
endif
if !exists('g:kite_long_timeout')
let g:kite_long_timeout = 400 " ms
endif
if !exists('g:kite_completion_max_width')
let g:kite_completion_max_width = 75
endif
if !(has('nvim') || has('job'))
call kite#utils#warn('disabled - requires nvim or vim with the +job feature')
finish
endif
if !(has('nvim') || has('timers'))
call kite#utils#warn('disabled - requires nvim or vim with the +timers feature')
finish
endif
" Nvim-QT
if exists('g:GuiLoaded')
GuiPopupmenu 0
endif
augroup Kite
autocmd!
autocmd BufEnter * call kite#bufenter()
autocmd VimEnter * if g:kite_completions | call kite#configure_completeopt() | endif
autocmd VimEnter * nested if kite#utils#kite_running() && &filetype !~# '^git' | call kite#onboarding#call(0) | endif
augroup END
nnoremap <silent> <Plug>(kite-docs) :call kite#docs#docs()<CR>
command! KiteDocsAtCursor call kite#docs#docs()
command! KiteOpenCopilot call kite#client#copilot()
command! KiteGeneralSettings call kite#client#settings()
command! KitePermissions call kite#client#permissions()
command! KiteTutorial call kite#onboarding#call(1)
command! KiteDisableAutoStart call kite#disable_auto_start()
command! KiteEnableAutoStart call kite#enable_auto_start()
command! KiteShowPopularPatterns call kite#signature#show_popular_patterns()
command! KiteHidePopularPatterns call kite#signature#hide_popular_patterns()
command! KiteGotoDefinition call kite#hover#goto_definition()
command! KiteFindRelatedCodeFromFileExperimental call kite#codenav#from_file()
command! KiteFindRelatedCodeFromLineExperimental call kite#codenav#from_line()

View file

@ -0,0 +1,27 @@
if exists('b:current_syntax')
finish
endif
" Section headings
syntax match kiteHeading /\v^[A-Z* ]+$/
highlight link kiteHeading String
" Usages / Definitions
syntax include @python syntax/python.vim
syntax region kiteSnippet start=/\v^\[.+:\d+\]/ end=/$/ keepend contains=kiteRef,kiteCode
syntax match kiteRef /\v^\[.+:\d+\]/ contained
syntax region kiteCode start=/ / end=/$/ contains=@python contained
highlight link kiteRef Comment
" Links
syntax region MyLink start=/^-> /hs=e end=/\v(\s\(\a+[.]\a{2,3}\))?$/he=s-1 contains=Domain
syntax match Domain /\v\(\a+[.]\a{2,3}\)/
highlight link MyLink Underlined
highlight link Domain Comment
let b:current_syntax = 'kite'

21
texput.log Normal file
View file

@ -0,0 +1,21 @@
This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2021/Arch Linux) (preloaded format=pdflatex 2021.5.23) 4 OCT 2021 15:07
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
**main.tex
! Emergency stop.
<*> main.tex
End of file on the terminal!
Here is how much of TeX's memory you used:
3 strings out of 478994
111 string characters out of 5864751
283044 words of memory out of 5000000
17591 multiletter control sequences out of 15000+600000
403430 words of font info for 27 fonts, out of 8000000 for 9000
1141 hyphenation exceptions out of 8191
0i,0n,0p,11b,6s stack positions out of 5000i,500n,10000p,200000b,80000s
! ==> Fatal error occurred, no output PDF file produced!