commit 9420badb70128679b216818c2c34c5fd4bb5b592 Author: zoe Date: Sat Apr 2 20:23:20 2022 +0200 first commit diff --git a/.netrwhist b/.netrwhist new file mode 100644 index 0000000..6f6e496 --- /dev/null +++ b/.netrwhist @@ -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' diff --git a/.vimrc b/.vimrc new file mode 100644 index 0000000..6b0e6af --- /dev/null +++ b/.vimrc @@ -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 pumvisible() ? coc#_select_confirm() + \: "\u\\=coc#on_enter()\" +vmap a (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 diff --git a/coc-settings.json b/coc-settings.json new file mode 100644 index 0000000..efed405 --- /dev/null +++ b/coc-settings.json @@ -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" + ] +} diff --git a/init.vim b/init.vim new file mode 100644 index 0000000..35fb420 --- /dev/null +++ b/init.vim @@ -0,0 +1,3 @@ +set runtimepath+=~/.vim,~/.vim/after +set packpath+=~/.vim +source ~/.config/nvim/.vimrc diff --git a/pack/kite/start/vim-plugin/.travis.yml b/pack/kite/start/vim-plugin/.travis.yml new file mode 100644 index 0000000..2ab9a27 --- /dev/null +++ b/pack/kite/start/vim-plugin/.travis.yml @@ -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 + diff --git a/pack/kite/start/vim-plugin/DEVELOPMENT.md b/pack/kite/start/vim-plugin/DEVELOPMENT.md new file mode 100644 index 0000000..a4e7981 --- /dev/null +++ b/pack/kite/start/vim-plugin/DEVELOPMENT.md @@ -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. diff --git a/pack/kite/start/vim-plugin/LICENSE b/pack/kite/start/vim-plugin/LICENSE new file mode 100644 index 0000000..5d27330 --- /dev/null +++ b/pack/kite/start/vim-plugin/LICENSE @@ -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. diff --git a/pack/kite/start/vim-plugin/LSP.md b/pack/kite/start/vim-plugin/LSP.md new file mode 100644 index 0000000..bd8fe52 --- /dev/null +++ b/pack/kite/start/vim-plugin/LSP.md @@ -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 +``` diff --git a/pack/kite/start/vim-plugin/README.md b/pack/kite/start/vim-plugin/README.md new file mode 100644 index 0000000..5b47454 --- /dev/null +++ b/pack/kite/start/vim-plugin/README.md @@ -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 [Kite’s 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 ``. 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 ``. If you'd like to use `` 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 `` (forward) and `` (backward), even after you have typed over the original placeholder text. + +To change these keys: + +```viml +let g:kite_previous_placeholder = '' +let g:kite_next_placeholder = '` +``` + + +### 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 gK (kite-docs) +``` + +By default you need to type `K` (or whatever you have mapped to `(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 `` 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/). diff --git a/pack/kite/start/vim-plugin/VERSION b/pack/kite/start/vim-plugin/VERSION new file mode 100644 index 0000000..7260243 --- /dev/null +++ b/pack/kite/start/vim-plugin/VERSION @@ -0,0 +1 @@ +1.0.84 diff --git a/pack/kite/start/vim-plugin/autoload/kite.vim b/pack/kite/start/vim-plugin/autoload/kite.vim new file mode 100644 index 0000000..01ab8bf --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite.vim @@ -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! * + + autocmd CursorHold,CursorHoldI call kite#events#event('selection') + autocmd TextChanged,TextChangedI call kite#events#event('edit') + autocmd FocusGained call kite#events#event('focus') + + if g:kite_completions + autocmd InsertCharPre call kite#completion#insertcharpre() + autocmd TextChangedI call kite#completion#autocomplete() + + autocmd CompleteDone call kite#completion#replace_range() + + if &ft == 'go' + autocmd CompleteDone call kite#completion#expand_newlines() + endif + if &ft == 'python' + autocmd CompleteDone call kite#snippet#complete_done() + endif + endif + + if exists('g:kite_documentation_continual') && g:kite_documentation_continual + autocmd CursorHold,CursorHoldI call kite#docs#docs() + endif + augroup END +endfunction + + +function! s:setup_mappings() + if exists('g:kite_tab_complete') && g:kite_completions + imap pumvisible() ? "\" : "\" + endif + + if empty(maparg('K', 'n')) && !hasmapto('(kite-docs)', 'n') + nmap K (kite-docs) + endif + + if empty(maparg('', 'n')) + nmap :KiteGotoDefinition + 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 + diff --git a/pack/kite/start/vim-plugin/autoload/kite/async.vim b/pack/kite/start/vim-plugin/autoload/kite/async.vim new file mode 100644 index 0000000..167b7f3 --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite/async.vim @@ -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 diff --git a/pack/kite/start/vim-plugin/autoload/kite/client.vim b/pack/kite/start/vim-plugin/autoload/kite/client.vim new file mode 100644 index 0000000..bf37bf2 --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite/client.vim @@ -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 diff --git a/pack/kite/start/vim-plugin/autoload/kite/codenav.vim b/pack/kite/start/vim-plugin/autoload/kite/codenav.vim new file mode 100644 index 0000000..f68ac3d --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite/codenav.vim @@ -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 diff --git a/pack/kite/start/vim-plugin/autoload/kite/completion.vim b/pack/kite/start/vim-plugin/autoload/kite/completion.vim new file mode 100644 index 0000000..5f64280 --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite/completion.vim @@ -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("\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("\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("\\") + 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("\") + 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 diff --git a/pack/kite/start/vim-plugin/autoload/kite/docs.vim b/pack/kite/start/vim-plugin/autoload/kite/docs.vim new file mode 100644 index 0000000..8557df7 --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite/docs.vim @@ -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('')) | 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 + diff --git a/pack/kite/start/vim-plugin/autoload/kite/document.vim b/pack/kite/start/vim-plugin/autoload/kite/document.vim new file mode 100644 index 0000000..5348b4f --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite/document.vim @@ -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 + diff --git a/pack/kite/start/vim-plugin/autoload/kite/events.vim b/pack/kite/start/vim-plugin/autoload/kite/events.vim new file mode 100644 index 0000000..a662884 --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite/events.vim @@ -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 + diff --git a/pack/kite/start/vim-plugin/autoload/kite/hover.vim b/pack/kite/start/vim-plugin/autoload/kite/hover.vim new file mode 100644 index 0000000..5203aa0 --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite/hover.vim @@ -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 diff --git a/pack/kite/start/vim-plugin/autoload/kite/languages.vim b/pack/kite/start/vim-plugin/autoload/kite/languages.vim new file mode 100644 index 0000000..dedf7cb --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite/languages.vim @@ -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 diff --git a/pack/kite/start/vim-plugin/autoload/kite/metrics.vim b/pack/kite/start/vim-plugin/autoload/kite/metrics.vim new file mode 100644 index 0000000..868c72c --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite/metrics.vim @@ -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 + diff --git a/pack/kite/start/vim-plugin/autoload/kite/onboarding.vim b/pack/kite/start/vim-plugin/autoload/kite/onboarding.vim new file mode 100644 index 0000000..4313a10 --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite/onboarding.vim @@ -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 + silent! nunmap + silent! nunmap j + silent! nunmap k + silent! nunmap x +endfunction diff --git a/pack/kite/start/vim-plugin/autoload/kite/signature.vim b/pack/kite/start/vim-plugin/autoload/kite/signature.vim new file mode 100644 index 0000000..c9a0872 --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite/signature.vim @@ -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 diff --git a/pack/kite/start/vim-plugin/autoload/kite/snippet.vim b/pack/kite/start/vim-plugin/autoload/kite/snippet.vim new file mode 100644 index 0000000..a327228 --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite/snippet.vim @@ -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\" + if mode() ==# 'S' + execute "normal! \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 = [ + \ '', + \ '', + \ '', + \ '', + \ '', + \ '', + \ '', + \ '', + \ '' + \ ] + + " Get a list of maps active in select mode. + for scope in ['', ''] + 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. + 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 ? ' ' : '') + \ . (mapping.expr ? ' ' : '') + \ . (mapping.nowait ? ' ' : '') + \ . (mapping.silent ? ' ' : '') + \ . mapping.lhs . ' ' + \ . substitute(mapping.rhs, '', ''.mapping.sid.'_', 'g') + endfor + + unlet! b:kite_maps +endfunction + + +function! s:setup_maps() + execute 'inoremap ' g:kite_next_placeholder 'pumvisible() ? "" : ":call kite#snippet#next_placeholder()"' + execute 'inoremap ' g:kite_previous_placeholder 'pumvisible() ? ":call kite#snippet#previous_placeholder(2)" : ":call kite#snippet#previous_placeholder()"' + execute 'snoremap ' g:kite_next_placeholder ':call kite#snippet#next_placeholder()' + execute 'snoremap ' g:kite_previous_placeholder ':call kite#snippet#previous_placeholder()' + + call s:remove_smaps_for_printable_characters() +endfunction + + +function! kite#snippet#teardown_maps() + execute 'silent! iunmap ' g:kite_next_placeholder + execute 'silent! iunmap ' g:kite_previous_placeholder + execute 'silent! sunmap ' g:kite_next_placeholder + execute 'silent! sunmap ' g:kite_previous_placeholder +endfunction + + +function! s:setup_autocmds() + augroup KiteSnippets + autocmd! * + + autocmd CursorMovedI + \ call s:update_placeholder_locations() | + \ call s:clear_all_placeholder_highlights() | + \ call s:highlight_current_level_placeholders() + autocmd CursorMoved,CursorMovedI call s:cursormoved() + autocmd InsertLeave call s:insertleave() + augroup END +endfunction + + +function! s:teardown_autocmds() + autocmd! KiteSnippets * +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 diff --git a/pack/kite/start/vim-plugin/autoload/kite/status.vim b/pack/kite/start/vim-plugin/autoload/kite/status.vim new file mode 100644 index 0000000..202c6c5 --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite/status.vim @@ -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 + diff --git a/pack/kite/start/vim-plugin/autoload/kite/utils.vim b/pack/kite/start/vim-plugin/autoload/kite/utils.vim new file mode 100644 index 0000000..0484ff2 --- /dev/null +++ b/pack/kite/start/vim-plugin/autoload/kite/utils.vim @@ -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(':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! \" + + 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! \" + + 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 + diff --git a/pack/kite/start/vim-plugin/doc/kite.txt b/pack/kite/start/vim-plugin/doc/kite.txt new file mode 100644 index 0000000..7b6d8eb --- /dev/null +++ b/pack/kite/start/vim-plugin/doc/kite.txt @@ -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 diff --git a/pack/kite/start/vim-plugin/lib/linux/kite-http b/pack/kite/start/vim-plugin/lib/linux/kite-http new file mode 100755 index 0000000..bfd3ced Binary files /dev/null and b/pack/kite/start/vim-plugin/lib/linux/kite-http differ diff --git a/pack/kite/start/vim-plugin/lib/macos/kite-http b/pack/kite/start/vim-plugin/lib/macos/kite-http new file mode 100755 index 0000000..0bed133 Binary files /dev/null and b/pack/kite/start/vim-plugin/lib/macos/kite-http differ diff --git a/pack/kite/start/vim-plugin/lib/windows/kite-http.exe b/pack/kite/start/vim-plugin/lib/windows/kite-http.exe new file mode 100755 index 0000000..69b4611 Binary files /dev/null and b/pack/kite/start/vim-plugin/lib/windows/kite-http.exe differ diff --git a/pack/kite/start/vim-plugin/lib/windows/md5Sum.exe b/pack/kite/start/vim-plugin/lib/windows/md5Sum.exe new file mode 100755 index 0000000..eab8024 Binary files /dev/null and b/pack/kite/start/vim-plugin/lib/windows/md5Sum.exe differ diff --git a/pack/kite/start/vim-plugin/plugin/kite.vim b/pack/kite/start/vim-plugin/plugin/kite.vim new file mode 100644 index 0000000..4ef2c34 --- /dev/null +++ b/pack/kite/start/vim-plugin/plugin/kite.vim @@ -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 = '' +endif + +if !exists('g:kite_next_placeholder') + let g:kite_next_placeholder = '' +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 (kite-docs) :call kite#docs#docs() + +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() diff --git a/pack/kite/start/vim-plugin/syntax/kite.vim b/pack/kite/start/vim-plugin/syntax/kite.vim new file mode 100644 index 0000000..781d562 --- /dev/null +++ b/pack/kite/start/vim-plugin/syntax/kite.vim @@ -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' + diff --git a/texput.log b/texput.log new file mode 100644 index 0000000..dae24be --- /dev/null +++ b/texput.log @@ -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!