diff --git a/vim/bundle/LanguageClient-neovim/.circleci/config.yml b/vim/bundle/LanguageClient-neovim/.circleci/config.yml new file mode 100644 index 0000000..7ff3c12 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/.circleci/config.yml @@ -0,0 +1,32 @@ +version: 2 +general: + branches: + ignore: + - /binary-.*/ + +jobs: + build: + docker: + - image: autozimu/languageclientneovim + steps: + - checkout + - run: make test && make integration-test + deploy: + docker: + - image: autozimu/rust-cross-for-macos + steps: + - checkout + - run: | + cargo build --target=x86_64-apple-darwin --release + github-release upload --user autozimu --repo LanguageClient-neovim --tag test --name languageclient-x86_64-apple-darwin --file $CARGO_TARGET_DIR/x86_64-apple-darwin/release/languageclient + +# workflows: +# version: 2 +# build-and-deploy: +# jobs: +# - build +# - deploy: +# requires: +# - build + +# vim: tabstop=2 diff --git a/vim/bundle/LanguageClient-neovim/.github/CONTRIBUTING.md b/vim/bundle/LanguageClient-neovim/.github/CONTRIBUTING.md new file mode 100644 index 0000000..83a7b4a --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/.github/CONTRIBUTING.md @@ -0,0 +1,29 @@ +# Prerequisites +- [rust] toolchain + +[rust]: https://www.rust-lang.org + +# Development +Put those settings inside vimrc, +```vim +let g:LanguageClient_devel = 1 " Use rust debug build +let g:LanguageClient_loggingLevel = 'DEBUG' " Use highest logging level +``` + +1. Make necessary changes. +1. Build. `make` to build, format and run [clippy], or `make build` to run build only. +1. Verify changes. +1. Run tests. (See below section) + +[clippy]: https://github.com/rust-lang-nursery/rust-clippy + +# Run tests +(Option 1. Recommended) With docker installed, +```sh +make test && make integration-test-docker +``` + +(Option 2) Refer [`Dockerfile`](Dockerfile) to install tests dependencies. +```sh +make test && make integration-test +``` diff --git a/vim/bundle/LanguageClient-neovim/.github/ISSUE_TEMPLATE/bug_report.md b/vim/bundle/LanguageClient-neovim/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..8f9fdd9 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,46 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +- Did you upgrade to latest plugin version? +- Did you upgrade to/compile latest binary? Run shell command + `bin/languageclient --version` to get its version number. +- (Neovim users only) Did you check output of `:checkhealth LanguageClient`? +- Did you check [troubleshooting]? + +[troubleshooting]: https://github.com/autozimu/LanguageClient-neovim/blob/next/INSTALL.md#6-troubleshooting + +## Describe the bug +A clear and concise description of what the bug is. + +## Environment +- neovim/vim version (`nvim --version` or `vim --version`): +- This plugin version (`git rev-parse --short HEAD`): +- This plugin's binary version (`bin/languageclient --version`): +- Minimal vimrc content (A minimal vimrc is the smallest vimrc that could + reproduce the issue. Refer to an example [here][min-vimrc.vim]): +- Language server link and version: + +[min-vimrc.vim]: https://github.com/autozimu/LanguageClient-neovim/blob/next/min-vimrc.vim + +## To Reproduce +Steps to reproduce the behavior: +1. Create/Fetch example project ... +1. Start vim, `nvim -u min-vimrc.vim` ... +1. Edit ... +1. Execute .... +1. See error + +## Current behavior +A clear and concise description of what's the current behavior. + +## Expected behavior +A clear and concise description of what you expected to happen. + +## Screenshots +If applicable, add screenshots to help explain your problem. + +## Additional context +Add any other context about the problem here. diff --git a/vim/bundle/LanguageClient-neovim/.github/ISSUE_TEMPLATE/feature_request.md b/vim/bundle/LanguageClient-neovim/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..77eebe1 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +## Is your feature request related to a problem? Please describe. +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +## Describe the solution you'd like +A clear and concise description of what you want to happen. + +## Describe alternatives you've considered +A clear and concise description of any alternative solutions or features you've considered. + +## Additional context +Add any other context or screenshots about the feature request here. diff --git a/vim/bundle/LanguageClient-neovim/.gitignore b/vim/bundle/LanguageClient-neovim/.gitignore new file mode 100644 index 0000000..f864d26 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/.gitignore @@ -0,0 +1,7 @@ +doc/tags +__pycache__ +.pytest_cache +.idea +target +rplugin/python3/tests +tests/data/.vim/plugged diff --git a/vim/bundle/LanguageClient-neovim/.travis.yml b/vim/bundle/LanguageClient-neovim/.travis.yml new file mode 100644 index 0000000..03ad474 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/.travis.yml @@ -0,0 +1,56 @@ +# Based on the "trust" template v0.1.1 +# https://github.com/japaric/trust/tree/v0.1.1 + +dist: trusty +sudo: required +services: docker +language: rust + +env: + global: + - CRATE_NAME=languageclient + - secure: eWJeA/cgx+RqY6jny1wtw4LErLy5leXYFvcfmGzwn8jGzDuUYt9S86GpMWamhKXsWZnUq3tdaCUFEThqd72nbQDd5tXhlKDAHQO6imo7zkszERv2mBRbDJwufYnj9J41OSyXk2MrJKerOTkD0HX5RmHXpY/wZf5ESSY5KamWBrqEOCLHdkT0QFD8Yqa9oCrfX3pI16b70rbZF60TSL5sE4neU9ai771fWyl0cGgO6QrpmW+J2fzC+gEtvDuBVoOUsfLlUBAEdylO6FIEEZy7i+HFFBCXYKtJIffdlMWKg9nogDU2ZqEtcPCgbc6HbhTCMPNPm/JlXUPw2zwb6tCO/IlMe3heLMPs+dfzbUdSCxTWicPzdzbZCjA4iUvDE2/vailVdKm72jSbm9ja7Vr9H5Y+IQpH3uncAAyE/q6fpKUh6uVRgKPA/5bULgzOwM0EuwWpyMC2iJvu/SGmrsUYZzztuB61p+G+TGlqrKFw9IfzfzrKlRDkMHYRbbSGQuB7NEYRV2Qn+4VavLjw+bshiK+8BqfjfFBdp/v1u8D06MhE1f4NnK9jh7NUm7qipfTU+HgZUauYN54T8CeljtyaynUeTDy2FNnK+zoA8WfxxZSnN7z8XOeAQESZWCRx3gG6ES6hChkwNxsyub9cU+VGCKvPgikXQzaWZ2EvOzGrpz8= + +matrix: + include: + # Linux + - env: TARGETS=i686-unknown-linux-musl:x86_64-unknown-linux-musl:aarch64-unknown-linux-gnu:i686-pc-windows-gnu:x86_64-pc-windows-gnu:x86_64-unknown-freebsd + + # OSX + - env: TARGETS=x86_64-apple-darwin + os: osx + +if: tag IS present + +branches: + except: + - /binary-.*/ + +script: + - true + +deploy: + # - Create a `public_repo` GitHub token. Go to: https://github.com/settings/tokens/new + # - Encrypt it: `travis encrypt 0123456789012345678901234567890123456789 + # - Paste the output down here + api_key: + secure: Dr/oCr65KsSjcDk21+CS7cZ/UR/ktncwDLsQIF/UyFC0bCP5MFbQ76Cz7Dy460hMtjCiy/X4QukxtiF0oeaOvi1szbo29s1KYjc9xPVhI1roQUyGghkGzo+V5Ncum3pwKF1Mb/kotikh2QSFQJYA3o32HhMNVb+1bApD+bwyAsGlnlAYYWQy+EcIDByhG9B4HB2pScoblyPmbgQPftc/E3ldKkm4iPNiNNa9T/rlXazNOkuiAqbRR/qBI4sSVAFFvfzvEcva4k5X8wzWfdsiDk9HKnNL7iDzdMMExSOoIKoet6jK3E3f2VbwD7tRw+We2cB+EBrW7xtkcck/BbEtnXhiWpt+Gg4qxXjZ6RVXgVw9tH0CGp2FjZ6B2DQ/chtgzIzLE+iuhJwhRqJ+7IW7LgNrnEsi6QelCiNKsm0aqXEpU5BVoBXxBegJHgHhunkKPpqmhYps5KsH4RPzrduqk2LHlYriwaGumcEnOVXzoJJs0NE6RaFzwl5cxmiKxC8tl1/dE4w2WmYM8n3yMmfbCd520bTQbvOe68k/8iV7gqZmOnWX//PLJ6Zf+v7T0yhaFC8Oj8ueWUCCSd7TAC3rHxd52BVZnQF1xXosiiN2PyWGZGbxaO1Qh7UbF1Xk+ot2pwEsP5Q8h155DlYJ/3mMzLvSi6Cp+78YKsoFlpfDy6w= + file_glob: true + file: bin/$CRATE_NAME-$TRAVIS_TAG-* + overwrite: true + on: + tags: true + provider: releases + skip_cleanup: true + +before_deploy: + - bash ci/install.sh + - bash ci/before_deploy.sh + +cache: + cargo: true +before_cache: + # Travis can't cache files that are not readable by "others" + - chmod -R a+r $HOME/.cargo + +# vim: tabstop=2 diff --git a/vim/bundle/LanguageClient-neovim/.vim/settings.json b/vim/bundle/LanguageClient-neovim/.vim/settings.json new file mode 100644 index 0000000..af92717 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/.vim/settings.json @@ -0,0 +1,8 @@ +{ + "rust": { + "clippy_preference": "on", + "all_targets": false, + "build_on_save": true, + "wait_to_build": 0 + } +} diff --git a/vim/bundle/LanguageClient-neovim/.vscode/launch.json b/vim/bundle/LanguageClient-neovim/.vscode/launch.json new file mode 100644 index 0000000..d5cf02f --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach", + "type": "lldb", + "request": "attach", + "program": "${workspaceFolder}/target/debug/languageclient" + } + ] +} diff --git a/vim/bundle/LanguageClient-neovim/.vscode/settings.json b/vim/bundle/LanguageClient-neovim/.vscode/settings.json new file mode 100644 index 0000000..69678f0 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "lldb.sourceLanguages": [ + "rust" + ] +} diff --git a/vim/bundle/LanguageClient-neovim/Cargo.lock b/vim/bundle/LanguageClient-neovim/Cargo.lock new file mode 100644 index 0000000..ca902cf --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/Cargo.lock @@ -0,0 +1,1201 @@ +[[package]] +name = "aho-corasick" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "antidote" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "atty" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bytes" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cc" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "chrono" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "clap" +version = "2.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crossbeam-utils" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "diff" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "dtoa" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "failure" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "filetime" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "flate2" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fsevent" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fsevent-sys" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "glob" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "humantime" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "inotify" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "inotify-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "iovec" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "jsonrpc-core" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "languageclient" +version = "0.1.123" +dependencies = [ + "diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "languageserver-types 0.51.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log4rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "notify 4.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pathdiff 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "languageserver-types" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "num-derive 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazycell" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.43" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "linked-hash-map" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lock_api" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log-mdc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log4rs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log-mdc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde-value 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_yaml 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", + "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "maplit" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz-sys" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio" +version = "0.6.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-extras" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazycell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "notify" +version = "4.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "filetime 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fsevent 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-derive" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-integer" +version = "0.1.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num_cpus" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ordered-float" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "owning_ref" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pathdiff" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quick-error" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "quote" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_syscall" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ryu" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "same-file" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde-value" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_yaml" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "slab" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smallvec" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "stable_deref_trait" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "strsim" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "structopt" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "structopt-derive" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.15.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "termion" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-executor" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-io" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-reactor" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "traitobject" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "typemap" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ucd-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-width" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unsafe-any" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "url" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "url_serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "utf8-ranges" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "walkdir" +version = "2.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "yaml-rust" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a" +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" +"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" +"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" +"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" +"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781" +"checksum bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0ce55bd354b095246fc34caf4e9e242f5297a7fd938b090cadfea6eee614aa62" +"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16" +"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" +"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" +"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19" +"checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" +"checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a" +"checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" +"checksum failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7efb22686e4a466b1ec1a15c2898f91fa9cb340452496dca654032de20ff95b9" +"checksum failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "946d0e98a50d9831f5d589038d2ca7f8f455b1c21028c0db0e84116a12696426" +"checksum filetime 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "da4b9849e77b13195302c174324b5ba73eec9b236b24c221a61000daefb95c5f" +"checksum flate2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4af030962d89d62aa52cd9492083b1cd9b2d1a77764878102a6c0f86b4d5444d" +"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +"checksum fsevent 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "c4bbbf71584aeed076100b5665ac14e3d85eeb31fdbb45fbd41ef9a682b5ec05" +"checksum fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1a772d36c338d07a032d5375a36f15f9a7043bf0cb8ce7cee658e037c6032874" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b" +"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" +"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e" +"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +"checksum inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b54539f3910d6f84fbf9a643efd6e3aa6e4f001426c0329576128255994718" +"checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" +"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" +"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" +"checksum jsonrpc-core 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ddf83704f4e79979a424d1082dd2c1e52683058056c9280efa19ac5f6bc9033c" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum languageserver-types 0.51.0 (registry+https://github.com/rust-lang/crates.io-index)" = "caecadd973c43c93f5ce96fa457da310113d867af28808a8ed74023e9887a39e" +"checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7" +"checksum lazycell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddba4c30a78328befecec92fc94970e53b3ae385827d28620f0f5bb2493081e0" +"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" +"checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" +"checksum lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775751a3e69bde4df9b38dd00a1b5d6ac13791e4223d4a0506577f0dd27cfb7a" +"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f" +"checksum log-mdc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" +"checksum log4rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a1f16090a553200fba94e104310b3e53e71f500fd9db7dc2143055aa3cc7ae63" +"checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43" +"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +"checksum memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4b3629fe9fdbff6daa6c33b90f7c08355c1aca05a3d01fa8063b822fcf185f3b" +"checksum miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "609ce024854aeb19a0ef7567d348aaa5a746b32fb72e336df7fcc16869d7e2b4" +"checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" +"checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +"checksum notify 4.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "873ecfd8c174964ae30f401329d140142312c8e5590719cf1199d5f1717d8078" +"checksum num-derive 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8af1847c907c2f04d7bfd572fb25bbb4385c637fe5be163cf2f8c5d778fe1e7d" +"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" +"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" +"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" +"checksum ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7eb5259643245d3f292c7a146b2df53bba24d7eab159410e648eb73dc164669d" +"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" +"checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" +"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" +"checksum pathdiff 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a3bf70094d203e07844da868b634207e71bfab254fe713171fae9a6e751ccf31" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee" +"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" +"checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5" +"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" +"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" +"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" +"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2069749032ea3ec200ca51e4a31df41759190a88edca0d2d86ee8bedf7073341" +"checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d" +"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7" +"checksum same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f7794e2fda7f594866840e95f5c5962e886e228e68b6505885811a94dd728c" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "84257ccd054dc351472528c8587b4de2dbf0dc0fe2e634030c1a90bfdacebaa9" +"checksum serde-value 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "52903ade2290cbd61a0937a66a268f26cebf246e3ddd7964a8babb297111fb0d" +"checksum serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "31569d901045afbff7a9479f793177fe9259819aff10ab4f89ef69bbc5f567fe" +"checksum serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "43344e7ce05d0d8280c5940cabb4964bea626aa58b1ec0e8c73fa2a8512a38ce" +"checksum serde_yaml 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8099d3df28273c99a1728190c7a9f19d444c941044f64adf986bee7ec53051" +"checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d" +"checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d" +"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" +"checksum structopt 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "d77af7242f18c40fd19cb270985930f239ee1646cfb482050bbae9da1d18743b" +"checksum structopt-derive 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "17ff01fe96de9d16e7372ae5f19dd7ece2c703b51043c3db9ea27f9e393ea311" +"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" +"checksum syn 0.15.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b10ee269228fb723234fce98e9aac0eaed2bd5f1ad2f6930e8d5b93f04445a1a" +"checksum synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb9b7550d063ea184027c9b8c20ac167cd36d3e06b3a40bceb9d746dc1a7b7" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" +"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" +"checksum tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c117b6cf86bb730aab4834f10df96e4dd586eff2c3c27d3781348da49e255bde" +"checksum tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "8b8a85fffbec3c5ab1ab62324570230dcd37ee5996a7859da5caf7b9d45e3e8c" +"checksum tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4b26fd37f1125738b2170c80b551f69ff6fecb277e6e5ca885e53eec2b005018" +"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" +"checksum typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" +"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" +"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +"checksum unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" +"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" +"checksum url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74e7d099f1ee52f823d4bdd60c93c3602043c728f5db3b97bdb548467f7bddea" +"checksum utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd70f467df6810094968e2fce0ee1bd0e87157aceb026a8c083bcf5e25b9efe4" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "af464bc7be7b785c7ac72e266a6b67c4c9070155606f51655a650a6686204e35" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +"checksum yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95acf0db5515d07da9965ec0e0ba6cc2d825e2caeb7303b66ca441729801254e" diff --git a/vim/bundle/LanguageClient-neovim/Cargo.toml b/vim/bundle/LanguageClient-neovim/Cargo.toml new file mode 100644 index 0000000..6e90938 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "languageclient" +version = "0.1.123" +authors = ["Junfeng Li "] +description = "Language Server Protocol (LSP) support for vim and neovim" +publish = false + +[dependencies] +failure = "0" +log = "0" +log4rs = "0" +structopt = "0" +maplit = "1" + +serde = "1" +serde_derive = "1" +serde_json = "1" + +jsonrpc-core = "8" +languageserver-types = "0" +url = "1" +pathdiff = "0" +diff = "0" +regex = "1" +glob = "0" +notify = "4" + +[package.metadata.release] +no-dev-version = true +pre-release-replacements = [ + { file = "install.sh", search = "version=.*", replace = "version={{version}}" }, + { file = "install.ps1", search = "version = '.*'", replace = "version = '{{version}}'" } +] +pre-release-hook = ["cargo", "build"] diff --git a/vim/bundle/LanguageClient-neovim/INSTALL.md b/vim/bundle/LanguageClient-neovim/INSTALL.md new file mode 100644 index 0000000..fc3bc24 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/INSTALL.md @@ -0,0 +1,91 @@ +# 1. Install neovim or vim + +Obviously you need [neovim](https://github.com/neovim/neovim#install-from-package) or [vim](http://www.vim.org/)! + +# 2. Install dependencies + +None. + +# 3. Install this plugin + +> For Windows users, replace `bash install.sh` with `powershell -executionpolicy bypass -File install.ps1` in following +> snippets. + +> If you don't want to use pre-built binaries, specify branch `next` and `make +> release` as post action after plugin installation and update. e.g., `Plug +> 'autozimu/LanguageClient-neovim', {'branch': 'next', 'do': 'make release'}`. + +> Android is not supported using the `install.sh` script. + +Choose steps matching your plugin manager. + +## [vim-plug](https://github.com/junegunn/vim-plug) user +Add following to vimrc +```vim +Plug 'autozimu/LanguageClient-neovim', { + \ 'branch': 'next', + \ 'do': 'bash install.sh', + \ } +``` + +Restart neovim and run `:PlugInstall` to install. + +## [dein.vim](https://github.com/Shougo/dein.vim) user +Add following to vimrc +```vim +call dein#add('autozimu/LanguageClient-neovim', { + \ 'rev': 'next', + \ 'build': 'bash install.sh', + \ }) +``` + +Restart neovim and run `:call dein#install()` to install. + +## Manual +Clone this repo into some place, e.g., `~/.vim-plugins` +```sh +mkdir -p ~/.vim-plugins +cd ~/.vim-plugins +git clone --depth 1 https://github.com/autozimu/LanguageClient-neovim.git +cd LanguageClient-neovim +bash install.sh +``` + +Add this plugin to vim/neovim `runtimepath`, +```vim +set runtimepath+=~/.vim-plugins/LanguageClient-neovim +``` + +# 4. Install language servers +Install language servers if corresponding language servers are not available +yet on your system. Please see and/or + +for list of language servers. + +# 5. Configure this plugin +Example configuration +```vim +" Required for operations modifying multiple buffers like rename. +set hidden + +let g:LanguageClient_serverCommands = { + \ 'rust': ['~/.cargo/bin/rustup', 'run', 'stable', 'rls'], + \ 'javascript': ['/usr/local/bin/javascript-typescript-stdio'], + \ 'javascript.jsx': ['tcp://127.0.0.1:2089'], + \ 'python': ['/usr/local/bin/pyls'], + \ } + +nnoremap K :call LanguageClient#textDocument_hover() +nnoremap gd :call LanguageClient#textDocument_definition() +nnoremap :call LanguageClient#textDocument_rename() +``` + +# 6. Troubleshooting + +1. Backup your vimrc and use [min-vimrc.vim](min-vimrc.vim) as vimrc. +1. Try on [sample projects](tests/data). +1. Execute `:echo &runtimepath` and make sure the plugin path is in the list. +1. Make sure language server could be started when invoked manually from shell. + Also try use absolute path for server commands, as PATH in vim might be different from shell env, especially on macOS. +1. Check content of log file. Also worth noting language server might have + separate log file. diff --git a/vim/bundle/LanguageClient-neovim/LICENSE.txt b/vim/bundle/LanguageClient-neovim/LICENSE.txt new file mode 100644 index 0000000..d00dce2 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Junfeng Li and contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vim/bundle/LanguageClient-neovim/Makefile b/vim/bundle/LanguageClient-neovim/Makefile new file mode 100644 index 0000000..0edc806 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/Makefile @@ -0,0 +1,68 @@ +all: check build fmt clippy + +check: + cargo check + +build: + cargo build + +fmt: + cargo fmt + +clippy: + cargo clippy + +vint: + vint autoload plugin + +release: + cargo build --release + cp -f target/release/languageclient bin/ + chmod a+x bin/languageclient + +bump-version: + [[ `git rev-parse --abbrev-ref HEAD` == "next" ]] || (echo "Not on branch next"; exit 1) + cargo release patch + +test: + cargo test + +integration-test-lint: + mypy --ignore-missing-imports \ + tests \ + rplugin/python3/denite/source \ + rplugin/python3/deoplete/sources + flake8 . + +integration-test: build integration-test-lint + tests/test.sh + +integration-test-docker: + docker image pull autozimu/languageclientneovim + docker run --volume ${CURDIR}:/root/.config/nvim autozimu/languageclientneovim bash -c "\ + export CARGO_TARGET_DIR=/tmp && \ + cd /root/.config/nvim && \ + make integration-test" + +integration-test-docker-debug: + docker image pull autozimu/languageclientneovim + docker run --interactive --tty --volume ${CURDIR}:/root/.config/nvim autozimu/languageclientneovim + +cleanup-binary-tags: + ci/cleanup-binary-tags.py + +clean: + cargo clean + rm -rf bin/languageclient + +DATE := $(shell date +%F) + +build-docker-image: ci/Dockerfile + docker build \ + --tag autozimu/languageclientneovim:latest \ + --tag autozimu/languageclientneovim:$(DATE) \ + ci + +publish-docker-image: + docker push autozimu/languageclientneovim:latest + docker push autozimu/languageclientneovim:$(DATE) diff --git a/vim/bundle/LanguageClient-neovim/README.md b/vim/bundle/LanguageClient-neovim/README.md new file mode 100644 index 0000000..a5bc9c4 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/README.md @@ -0,0 +1,95 @@ +> For legacy python implementation, see [branch master](https://github.com/autozimu/LanguageClient-neovim/tree/master). + +# LanguageClient-neovim +[![CircleCI](https://circleci.com/gh/autozimu/LanguageClient-neovim.svg?style=svg)](https://circleci.com/gh/autozimu/LanguageClient-neovim) + +[Language Server Protocol] (LSP) support for [vim] and [neovim]. + +[Language Server Protocol]: https://github.com/Microsoft/language-server-protocol +[neovim]: https://neovim.io/ +[vim]: http://www.vim.org/ + +![rename](https://cloud.githubusercontent.com/assets/1453551/24251636/2e73a1cc-0fb1-11e7-8a5e-3332e6a5f424.gif) + +More recordings at [Updates, screenshots & GIFs](https://github.com/autozimu/LanguageClient-neovim/issues/35). + +# Features + +- Non-blocking asynchronous calls. +- [Sensible completion](https://github.com/autozimu/LanguageClient-neovim/issues/35#issuecomment-288731936). + Integrated with [deoplete](https://github.com/Shougo/deoplete.nvim) or + [ncm2](https://github.com/ncm2/ncm2). Or with vim built-in `omnifunc`. +- [Realtime diagnostics/compiler/lint message.](https://github.com/autozimu/LanguageClient-neovim/issues/35#issuecomment-288732042) +- [Rename.](https://github.com/autozimu/LanguageClient-neovim/issues/35#issuecomment-288731403) +- [Hover/Get identifier info.](https://github.com/autozimu/LanguageClient-neovim/issues/35#issuecomment-288731665) +- [Goto definition.](https://github.com/autozimu/LanguageClient-neovim/issues/35#issuecomment-288731744) +- Goto reference locations. +- [Workspace/Document symbols query](https://github.com/autozimu/LanguageClient-neovim/issues/35#issuecomment-288731839). +- [Formatting](https://github.com/autozimu/LanguageClient-neovim/issues/35#issuecomment-324497559). +- [Code Action/Quick Fix](https://github.com/autozimu/LanguageClient-neovim/issues/35#issuecomment-331016526). + +# Quick Start + +Using [`vim-plug`](https://github.com/junegunn/vim-plug): + +```vim +Plug 'autozimu/LanguageClient-neovim', { + \ 'branch': 'next', + \ 'do': 'bash install.sh', + \ } + +" (Optional) Multi-entry selection UI. +Plug 'junegunn/fzf' + +Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' } +``` + +Example configuration + +```vim +" Required for operations modifying multiple buffers like rename. +set hidden + +let g:LanguageClient_serverCommands = { + \ 'rust': ['~/.cargo/bin/rustup', 'run', 'stable', 'rls'], + \ 'javascript': ['/usr/local/bin/javascript-typescript-stdio'], + \ 'javascript.jsx': ['tcp://127.0.0.1:2089'], + \ 'python': ['/usr/local/bin/pyls'], + \ } + +nnoremap :call LanguageClient_contextMenu() +" Or map each action separately +nnoremap K :call LanguageClient#textDocument_hover() +nnoremap gd :call LanguageClient#textDocument_definition() +nnoremap :call LanguageClient#textDocument_rename() +``` + +Run command `nvim +PlugInstall +UpdateRemotePlugins +qa` in shell to install +this plugin. Install corresponding language servers. Restart neovim/vim and +language services will be available right away. Happy hacking! + +# Install + +[INSTALL](INSTALL.md) + +# Troubleshooting + +[Troubleshooting](INSTALL.md#6-troubleshooting) + +# Language Servers + +Please see and/or . + +# Documentation + +[`:help LanguageClient`][LanguageClient.txt] for full list of configurations, commands and functions. + +[LanguageClient.txt]: doc/LanguageClient.txt + +# Contributing + +[CONTRIBUTING](.github/CONTRIBUTING.md) + +# License + +The MIT License. diff --git a/vim/bundle/LanguageClient-neovim/TODO.md b/vim/bundle/LanguageClient-neovim/TODO.md new file mode 100644 index 0000000..3e76d11 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/TODO.md @@ -0,0 +1,4 @@ +- Structured, independent, reproducible tests. +- More integration tests. +- Check server capabilities before calling RPC API. +- Replace echodoc functionality. diff --git a/vim/bundle/LanguageClient-neovim/autoload/LSP.vim b/vim/bundle/LanguageClient-neovim/autoload/LSP.vim new file mode 100644 index 0000000..1d17aef --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/autoload/LSP.vim @@ -0,0 +1,40 @@ +function! LSP#filename() abort + return expand('%:p') +endfunction + +function! LSP#text() abort + let l:lines = getline(1, '$') + if l:lines[-1] !=# '' && &fixendofline + let l:lines += [''] + endif + return l:lines +endfunction + +function! LSP#line() abort + return line('.') - 1 +endfunction + +function! LSP#character() abort + return col('.') - 1 +endfunction + +function! LSP#range_start_line() abort + let l:lnum = v:lnum ? v:lnum : getpos("'<")[1] + return l:lnum - 1 +endfunction + +function! LSP#range_end_line() abort + if v:lnum + return v:lnum - 1 + v:count + endif + + return getpos("'>")[1] +endfunction + +function! LSP#visible_line_start() abort + return line('w0') - 1 +endfunction + +function! LSP#visible_line_end() abort + return line('w$') - 1 +endfunction diff --git a/vim/bundle/LanguageClient-neovim/autoload/LanguageClient.vim b/vim/bundle/LanguageClient-neovim/autoload/LanguageClient.vim new file mode 100644 index 0000000..a4e103b --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/autoload/LanguageClient.vim @@ -0,0 +1,1118 @@ +if get(g:, 'LanguageClient_loaded') + finish +endif + +let s:TYPE = { +\ 'string': type(''), +\ 'list': type([]), +\ 'dict': type({}), +\ 'funcref': type(function('call')) +\ } + +function! s:Echo(message) abort + echo a:message +endfunction + +function! s:Ellipsis(message) abort + let l:maxlen = &columns * &cmdheight - 2 + if &showcmd + let maxlen -= 11 + endif + if &ruler + let maxlen -= 18 + endif + if len(a:message) < l:maxlen + let l:message = a:message + else + let l:message = a:message[:l:maxlen - 3] . '...' + endif + return l:message +endfunction + +" `echo` message without trigger |hit-enter| +function! s:EchoEllipsis(message) abort + echo s:Ellipsis(a:message) +endfunction + +" `echomsg` message without trigger |hit-enter| +function! s:EchomsgEllipsis(message) abort + " Credit: ALE, snippets from ale#cursor#TruncatedEcho() + let l:message = a:message + " Change tabs to spaces. + let l:message = substitute(l:message, "\t", ' ', 'g') + " Remove any newlines in the message. + let l:message = substitute(l:message, "\n", '', 'g') + + " We need to remember the setting for shortmess and reset it again. + let l:shortmess_options = &l:shortmess + try + let l:cursor_position = getcurpos() + + " The message is truncated and saved to the history. + setlocal shortmess+=T + exec "norm! :echomsg l:message\n" + + " Reset the cursor position if we moved off the end of the line. + " Using :norm and :echomsg can move the cursor off the end of the + " line. + if l:cursor_position != getcurpos() + call setpos('.', l:cursor_position) + endif + finally + let &l:shortmess = l:shortmess_options + endtry +endfunction + +function! s:Echomsg(message) abort + echomsg a:message +endfunction + +function! s:Echoerr(message) abort + echohl Error | echomsg a:message | echohl None +endfunction + +function! s:Echowarn(message) abort + echohl WarningMsg | echomsg a:message | echohl None +endfunction + +function! s:Debug(message) abort + if !exists('g:LanguageClient_loggingLevel') + return + endif + + if g:LanguageClient_loggingLevel ==? 'INFO' || g:LanguageClient_loggingLevel ==? 'DEBUG' + call s:Echoerr(a:message) + endif +endfunction + +function! s:hasSnippetSupport() abort + if get(g:, 'LanguageClient_hasSnippetSupport', 1) !=# 1 + return 0 + endif + + " https://github.com/SirVer/ultisnips + if exists('g:did_plugin_ultisnips') + return 1 + endif + " https://github.com/Shougo/neosnippet.vim + if exists('g:loaded_neosnippet') + return 1 + endif + " https://github.com/garbas/vim-snipmate + if exists('g:loaded_snips') + return 1 + endif + + return 0 +endfunction + +function! s:IsTrue(v) abort + if type(a:v) ==# type(0) + return a:v ==# 0 ? v:false : v:true + elseif a:v is v:null + return v:false + else + return v:true + endif +endfunction + +function! s:IsFalse(v) abort + return s:IsTrue(a:v) ? v:false : v:true +endfunction + +" Get all listed buffer file names. +function! s:Bufnames() abort + return map(filter(range(0,bufnr('$')), 'buflisted(v:val)'), 'fnamemodify(bufname(v:val), '':p'')') +endfunction + +function! s:getInput(prompt, default) abort + call inputsave() + let l:input = input(a:prompt, a:default) + call inputrestore() + return l:input +endfunction + +function! s:FZF(source, sink) abort + if !get(g:, 'loaded_fzf') + call s:Echoerr('FZF not loaded!') + return + endif + + if exists('LanguageClient_fzfOptions') + let l:options = LanguageClient_fzfOptions + elseif exists('*fzf#vim#with_preview') + let l:options = fzf#vim#with_preview('right:50%:hidden', '?').options + else + let l:options = [] + endif + call fzf#run(fzf#wrap({ + \ 'source': a:source, + \ 'sink': function(a:sink), + \ 'options': l:options, + \ })) + if has('nvim') + call feedkeys('i') + endif +endfunction + +function! s:Edit(action, path) abort + " If editing current file, push current location to jump list. + let l:bufnr = bufnr(a:path) + if l:bufnr == bufnr('%') + execute 'normal m`' + return + endif + + let l:action = a:action + " Avoid the 'not saved' warning. + if l:action ==# 'edit' && l:bufnr != -1 + execute 'buffer' l:bufnr + return + endif + + execute l:action . ' ' . fnameescape(a:path) +endfunction + +" Batch version of `matchdelete()`. +function! s:MatchDelete(ids) abort + for l:id in a:ids + call matchdelete(l:id) + endfor +endfunction + +" Batch version of nvim_buf_add_highlight +function! s:AddHighlights(source, highlights) abort + for hl in a:highlights + call nvim_buf_add_highlight(0, a:source, hl.group, hl.line, hl.character_start, hl.character_end) + endfor +endfunction + +let s:id = 1 +let s:handlers = {} + +" Note: vim execute callback for every line. +let s:content_length = 0 +let s:input = '' +function! s:HandleMessage(job, lines, event) abort + if a:event ==# 'stdout' + while len(a:lines) > 0 + let l:line = remove(a:lines, 0) + + if l:line ==# '' + continue + elseif s:content_length == 0 + let s:content_length = str2nr(substitute(l:line, '.*Content-Length:', '', '')) + continue + endif + + let s:input .= strpart(l:line, 0, s:content_length) + if s:content_length < strlen(l:line) + call insert(a:lines, strpart(l:line, s:content_length), 0) + let s:content_length = 0 + else + let s:content_length = s:content_length - strlen(l:line) + endif + if s:content_length > 0 + continue + endif + + try + let l:message = json_decode(s:input) + if type(l:message) !=# s:TYPE.dict + throw 'Messsage is not dict.' + endif + catch + call s:Debug('Error decoding message: ' . string(v:exception) . + \ ' Message: ' . s:input) + continue + finally + let s:input = '' + endtry + + if has_key(l:message, 'method') + let l:id = get(l:message, 'id', v:null) + let l:method = get(l:message, 'method') + let l:params = get(l:message, 'params') + try + if l:method ==# 'execute' + for l:cmd in l:params + execute l:cmd + endfor + let l:result = 0 + else + let l:params = type(l:params) == s:TYPE.list ? l:params : [l:params] + let l:result = call(l:method, l:params) + endif + if l:id != v:null + call LanguageClient#Write(json_encode({ + \ 'jsonrpc': '2.0', + \ 'id': l:id, + \ 'result': l:result, + \ })) + endif + catch + let l:exception = v:exception + if l:id != v:null + try + call LanguageClient#Write(json_encode({ + \ 'jsonrpc': '2.0', + \ 'id': l:id, + \ 'error': { + \ 'code': -32603, + \ 'message': string(v:exception) + \ } + \ })) + catch + " TODO + endtry + endif + call s:Debug(string(l:exception)) + endtry + elseif has_key(l:message, 'result') || has_key(l:message, 'error') + let l:id = get(l:message, 'id') + let l:Handle = get(s:handlers, l:id) + unlet s:handlers[l:id] + let l:type = type(l:Handle) + if l:type == s:TYPE.funcref || l:type == s:TYPE.string + call call(l:Handle, [l:message]) + elseif l:type == s:TYPE.list + call add(l:Handle, l:message) + elseif l:type == s:TYPE.string && exists(l:Handle) + let l:outputs = eval(l:Handle) + call add(l:outputs, l:message) + else + call s:Echoerr('Unknown Handle type: ' . string(l:Handle)) + endif + else + call s:Echoerr('Unknown message: ' . string(l:message)) + endif + endwhile + elseif a:event ==# 'stderr' + call s:Echoerr('LanguageClient stderr: ' . string(a:lines)) + elseif a:event ==# 'exit' + if type(a:lines) == type(0) && a:lines == 0 + return + endif + call s:Debug('LanguageClient exited with: ' . string(a:lines)) + else + call s:Debug('LanguageClient unknown event: ' . a:event) + endif +endfunction + +function! s:HandleStdoutVim(job, data) abort + return s:HandleMessage(a:job, [a:data], 'stdout') +endfunction + +function! s:HandleStderrVim(job, data) abort + return s:HandleMessage(a:job, [a:data], 'stderr') +endfunction + +function! s:HandleExitVim(job, data) abort + return s:HandleMessage(a:job, [a:data], 'exit') +endfunction + +function! s:HandleOutputNothing(output) abort +endfunction + +function! s:HandleOutput(output, ...) abort + let l:quiet = get(a:000, 0) + + if has_key(a:output, 'result') + " let l:result = string(a:result) + " if l:result !=# 'v:null' + " echomsg l:result + " endif + return get(a:output, 'result') + elseif has_key(a:output, 'error') + let l:error = get(a:output, 'error') + let l:message = get(l:error, 'message') + if !l:quiet + call s:Echoerr(l:message) + endif + return v:null + else + if !l:quiet + call s:Echoerr('Unknown output type: ' . json_encode(a:output)) + endif + return v:null + endif +endfunction + +let s:root = expand(':p:h:h') +function! LanguageClient#binaryPath() abort + let l:filename = 'languageclient' + if has('win32') + let l:filename .= '.exe' + endif + + if exists('g:LanguageClient_devel') + if exists('$CARGO_TARGET_DIR') + let l:path = $CARGO_TARGET_DIR . '/debug/' + else + let l:path = s:root . '/target/debug/' + endif + else + let l:path = s:root . '/bin/' + endif + + return l:path . l:filename +endfunction + +function! s:Launch() abort + let l:binpath = LanguageClient#binaryPath() + + if executable(l:binpath) != 1 + call s:Echoerr('LanguageClient: binary (' . l:binpath . ') doesn''t exists! Please check installation guide.') + return 0 + endif + + if has('nvim') + let s:job = jobstart([l:binpath], { + \ 'on_stdout': function('s:HandleMessage'), + \ 'on_stderr': function('s:HandleMessage'), + \ 'on_exit': function('s:HandleMessage'), + \ }) + if s:job == 0 + call s:Echoerr('LanguageClient: Invalid arguments!') + return 0 + elseif s:job == -1 + call s:Echoerr('LanguageClient: Not executable!') + return 0 + else + return 1 + endif + elseif has('job') + let s:job = job_start([l:binpath], { + \ 'out_cb': function('s:HandleStdoutVim'), + \ 'err_cb': function('s:HandleStderrVim'), + \ 'exit_cb': function('s:HandleExitVim'), + \ }) + if job_status(s:job) !=# 'run' + call s:Echoerr('LanguageClient: job failed to start or died!') + return 0 + else + return 1 + endif + else + echoerr 'Not supported: not nvim nor vim with +job.' + return 0 + endif +endfunction + +function! LanguageClient#Write(message) abort + let l:message = a:message . "\n" + if has('nvim') + " jobsend respond 1 for success. + return !jobsend(s:job, l:message) + elseif has('channel') + return ch_sendraw(s:job, l:message) + else + echoerr 'Not supported: not nvim nor vim with +channel.' + endif +endfunction + +function! LanguageClient#Call(method, params, callback, ...) abort + let l:id = s:id + let s:id = s:id + 1 + if a:callback is v:null + let s:handlers[l:id] = function('s:HandleOutput') + else + let s:handlers[l:id] = a:callback + endif + let l:skipAddParams = get(a:000, 0, v:false) + let l:params = a:params + if type(a:params) == s:TYPE.dict && !skipAddParams + let l:params = extend({ + \ 'buftype': &buftype, + \ 'languageId': &filetype, + \ }, l:params) + endif + return LanguageClient#Write(json_encode({ + \ 'jsonrpc': '2.0', + \ 'id': l:id, + \ 'method': a:method, + \ 'params': l:params, + \ })) +endfunction + +function! LanguageClient#Notify(method, params) abort + let l:params = a:params + if type(params) == s:TYPE.dict + let l:params = extend({ + \ 'buftype': &buftype, + \ 'languageId': &filetype, + \ }, l:params) + endif + return LanguageClient#Write(json_encode({ + \ 'jsonrpc': '2.0', + \ 'method': a:method, + \ 'params': l:params, + \ })) +endfunction + +function! LanguageClient#textDocument_hover(...) abort + let l:Callback = get(a:000, 1, v:null) + let l:params = { + \ 'filename': LSP#filename(), + \ 'text': LSP#text(), + \ 'line': LSP#line(), + \ 'character': LSP#character(), + \ 'handle': s:IsFalse(l:Callback), + \ } + call extend(l:params, get(a:000, 0, {})) + return LanguageClient#Call('textDocument/hover', l:params, l:Callback) +endfunction + +" Meta methods to go to various places. +function! LanguageClient#findLocations(...) abort + let l:Callback = get(a:000, 1, v:null) + let l:params = { + \ 'filename': LSP#filename(), + \ 'text': LSP#text(), + \ 'line': LSP#line(), + \ 'character': LSP#character(), + \ 'gotoCmd': v:null, + \ 'handle': s:IsFalse(l:Callback), + \ } + call extend(l:params, get(a:000, 0, {})) + return LanguageClient#Call('languageClient/findLocations', l:params, l:Callback) +endfunction + +function! LanguageClient#textDocument_definition(...) abort + let l:params = { + \ 'method': 'textDocument/definition', + \ } + call extend(l:params, get(a:000, 0, {})) + return call('LanguageClient#findLocations', [l:params] + a:000[1:]) +endfunction + +function! LanguageClient#textDocument_typeDefinition(...) abort + let l:params = { + \ 'method': 'textDocument/typeDefinition', + \ } + call extend(l:params, get(a:000, 0, {})) + return call('LanguageClient#findLocations', [l:params] + a:000[1:]) +endfunction + +function! LanguageClient#textDocument_implementation(...) abort + let l:params = { + \ 'method': 'textDocument/implementation', + \ } + call extend(l:params, get(a:000, 0, {})) + return call('LanguageClient#findLocations', [l:params] + a:000[1:]) +endfunction + +function! LanguageClient#textDocument_references(...) abort + let l:Callback = get(a:000, 1, v:null) + let l:params = { + \ 'filename': LSP#filename(), + \ 'text': LSP#text(), + \ 'line': LSP#line(), + \ 'character': LSP#character(), + \ 'includeDeclaration': v:true, + \ 'handle': s:IsFalse(l:Callback), + \ 'gotoCmd': v:null, + \ } + call extend(l:params, get(a:000, 0, {})) + return LanguageClient#Call('textDocument/references', l:params, l:Callback) +endfunction + +function! LanguageClient#textDocument_rename(...) abort + let l:params = { + \ 'filename': LSP#filename(), + \ 'text': LSP#text(), + \ 'line': LSP#line(), + \ 'character': LSP#character(), + \ 'cword': expand(''), + \ 'handle': v:true, + \ } + call extend(l:params, a:0 >= 1 ? a:1 : {}) + let l:Callback = a:0 >= 2 ? a:2 : v:null + return LanguageClient#Call('textDocument/rename', l:params, v:null) +endfunction + +function! LanguageClient#textDocument_documentSymbol(...) abort + let l:Callback = get(a:000, 1, v:null) + let l:params = { + \ 'filename': LSP#filename(), + \ 'text': LSP#text(), + \ 'handle': s:IsFalse(l:Callback), + \ } + call extend(l:params, get(a:000, 0, {})) + return LanguageClient#Call('textDocument/documentSymbol', l:params, l:Callback) +endfunction + +function! LanguageClient#workspace_symbol(...) abort + let l:Callback = get(a:000, 2, v:null) + let l:params = { + \ 'filename': LSP#filename(), + \ 'text': LSP#text(), + \ 'query': get(a:000, 0, ''), + \ 'handle': s:IsFalse(l:Callback), + \ } + call extend(l:params, get(a:000, 1, {})) + return LanguageClient#Call('workspace/symbol', l:params, l:Callback) +endfunction + +function! LanguageClient#textDocument_codeAction(...) abort + let l:Callback = get(a:000, 1, v:null) + let l:params = { + \ 'filename': LSP#filename(), + \ 'text': LSP#text(), + \ 'line': LSP#line(), + \ 'character': LSP#character(), + \ 'handle': s:IsFalse(l:Callback), + \ } + call extend(l:params, get(a:000, 0, {})) + return LanguageClient#Call('textDocument/codeAction', l:params, l:Callback) +endfunction + +function! LanguageClient#textDocument_completion(...) abort + " Note: do not add 'text' as it might be huge. + let l:params = { + \ 'filename': LSP#filename(), + \ 'line': LSP#line(), + \ 'character': LSP#character(), + \ 'handle': v:false, + \ } + call extend(l:params, a:0 >= 1 ? a:1 : {}) + let l:Callback = a:0 >= 2 ? a:2 : v:null + return LanguageClient#Call('textDocument/completion', l:params, l:Callback) +endfunction + +function! LanguageClient#textDocument_formatting(...) abort + let l:params = { + \ 'filename': LSP#filename(), + \ 'text': LSP#text(), + \ 'line': LSP#line(), + \ 'character': LSP#character(), + \ 'handle': v:true, + \ } + call extend(l:params, a:0 >= 1 ? a:1 : {}) + let l:Callback = a:0 >= 2 ? a:2 : v:null + return LanguageClient#Call('textDocument/formatting', l:params, l:Callback) +endfunction + +function! LanguageClient#textDocument_rangeFormatting(...) abort + let l:Callback = get(a:000, 1, v:null) + let l:params = { + \ 'filename': LSP#filename(), + \ 'text': LSP#text(), + \ 'line': LSP#line(), + \ 'character': LSP#character(), + \ 'LSP#range_start_line()': LSP#range_start_line(), + \ 'LSP#range_end_line()': LSP#range_end_line(), + \ 'handle': s:IsFalse(l:Callback), + \ } + call extend(l:params, get(a:000, 0, {})) + return LanguageClient#Call('textDocument/rangeFormatting', l:params, l:Callback) +endfunction + +function! LanguageClient#completionItem_resolve(completion_item, ...) abort + let l:Callback = get(a:000, 1, v:null) + let l:params = { + \ 'completionItem': a:completion_item, + \ 'handle': s:IsFalse(l:Callback) + \ } + call extend(l:params, get(a:000, 0, {})) + return LanguageClient#Call('completionItem/resolve', l:params, l:Callback) +endfunction + +function! LanguageClient#textDocument_rangeFormatting_sync(...) abort + let l:result = LanguageClient_runSync('LanguageClient#textDocument_rangeFormatting', { + \ 'handle': v:true, + \ }) + return l:result isnot v:null +endfunction + +function! LanguageClient#rustDocument_implementations(...) abort + let l:params = { + \ 'filename': LSP#filename(), + \ 'text': LSP#text(), + \ 'line': LSP#line(), + \ 'character': LSP#character(), + \ } + call extend(l:params, a:0 >= 1 ? a:1 : {}) + let l:Callback = a:0 >= 2 ? a:2 : v:null + return LanguageClient#Call('rustDocument/implementations', l:params, l:Callback) +endfunction + +function! LanguageClient#textDocument_didOpen() abort + return LanguageClient#Notify('textDocument/didOpen', { + \ 'filename': LSP#filename(), + \ 'text': LSP#text(), + \ }) +endfunction + +function! LanguageClient#textDocument_didChange() abort + " Note: do not add 'text' as it might be huge. + return LanguageClient#Notify('textDocument/didChange', { + \ 'filename': LSP#filename(), + \ }) +endfunction + +function! LanguageClient#textDocument_didSave() abort + return LanguageClient#Notify('textDocument/didSave', { + \ 'filename': LSP#filename(), + \ }) +endfunction + +function! LanguageClient#textDocument_didClose() abort + return LanguageClient#Notify('textDocument/didClose', { + \ 'filename': LSP#filename(), + \ }) +endfunction + +function! LanguageClient#textDocument_documentHighlight(...) abort + let l:Callback = get(a:000, 1, v:null) + let l:params = { + \ 'filename': LSP#filename(), + \ 'text': LSP#text(), + \ 'line': LSP#line(), + \ 'character': LSP#character(), + \ 'handle': s:IsFalse(l:Callback), + \ } + call extend(l:params, get(a:000, 0, {})) + return LanguageClient#Call('textDocument/documentHighlight', l:params, l:Callback) +endfunction + +function! LanguageClient#clearDocumentHighlight() abort + return LanguageClient#Notify('languageClient/clearDocumentHighlight', {}) +endfunction + +function! LanguageClient#getState(callback) abort + return LanguageClient#Call('languageClient/getState', {}, a:callback) +endfunction + +function! LanguageClient#isAlive(callback) abort + return LanguageClient#Call('languageClient/isAlive', {}, a:callback) +endfunction + +function! LanguageClient#startServer(...) abort + let l:params = { + \ 'filename': LSP#filename(), + \ 'cmdargs': [], + \ } + call extend(l:params, a:0 > 0 ? {'cmdargs': a:000} : {}) + return LanguageClient#Call('languageClient/startServer', l:params, v:null) +endfunction + +function! LanguageClient#registerServerCommands(cmds, ...) abort + let l:handle = a:0 > 0 ? a:1 : v:null + return LanguageClient#Call('languageClient/registerServerCommands', a:cmds, l:handle, v:true) +endfunction + +function! LanguageClient#setLoggingLevel(level) abort + let l:params = { + \ 'loggingLevel': a:level, + \ } + return LanguageClient#Call('languageClient/setLoggingLevel', l:params, v:null) +endfunction + +function! LanguageClient#setDiagnosticsList(diagnosticsList) abort + let l:params = { + \ 'diagnosticsList': a:diagnosticsList, + \ } + return LanguageClient#Call('languageClient/setDiagnosticsList', l:params, v:null) +endfunction + +function! LanguageClient#registerHandlers(handlers, ...) abort + let l:handle = a:0 > 0 ? a:1 : v:null + return LanguageClient#Call('languageClient/registerHandlers', a:handlers, l:handle) +endfunction + +function! s:ExecuteAutocmd(event) abort + if exists('#User#' . a:event) + execute 'doautocmd User ' . a:event + endif +endfunction + +function! LanguageClient_runSync(fn, ...) abort + let s:LanguageClient_runSync_outputs = [] + let l:arguments = add(a:000[:], s:LanguageClient_runSync_outputs) + call call(a:fn, l:arguments) + while len(s:LanguageClient_runSync_outputs) == 0 + sleep 100m + endwhile + let l:output = remove(s:LanguageClient_runSync_outputs, 0) + return s:HandleOutput(l:output, v:true) +endfunction + +function! LanguageClient#handleBufNewFile() abort + if &buftype !=# '' || &filetype ==# '' + return + endif + + try + call LanguageClient#Notify('languageClient/handleBufNewFile', { + \ 'filename': LSP#filename(), + \ }) + catch + call s:Debug('LanguageClient caught exception: ' . string(v:exception)) + endtry +endfunction + +function! LanguageClient#handleBufReadPost() abort + if &buftype !=# '' || &filetype ==# '' + return + endif + + try + call LanguageClient#Notify('languageClient/handleBufReadPost', { + \ 'filename': LSP#filename(), + \ }) + catch + call s:Debug('LanguageClient caught exception: ' . string(v:exception)) + endtry +endfunction + +function! LanguageClient#handleTextChanged() abort + if &buftype !=# '' || &filetype ==# '' + return + endif + + try + " Note: do not add 'text' as it might be huge. + call LanguageClient#Notify('languageClient/handleTextChanged', { + \ 'filename': LSP#filename(), + \ }) + catch + call s:Debug('LanguageClient caught exception: ' . string(v:exception)) + endtry +endfunction + +function! LanguageClient#handleBufWritePost() abort + if &buftype !=# '' || &filetype ==# '' + return + endif + + try + call LanguageClient#Notify('languageClient/handleBufWritePost', { + \ 'filename': LSP#filename(), + \ }) + catch + call s:Debug('LanguageClient caught exception: ' . string(v:exception)) + endtry +endfunction + +function! LanguageClient#handleBufDelete() abort + if &buftype !=# '' || &filetype ==# '' + return + endif + + try + call LanguageClient#Notify('languageClient/handleBufDelete', { + \ 'filename': LSP#filename(), + \ }) + catch + call s:Debug('LanguageClient caught exception: ' . string(v:exception)) + endtry +endfunction + +let s:last_cursor_line = -1 +function! LanguageClient#handleCursorMoved() abort + let l:cursor_line = getcurpos()[1] - 1 + if l:cursor_line == s:last_cursor_line + return + endif + let s:last_cursor_line = l:cursor_line + + if &buftype !=# '' || &filetype ==# '' + return + endif + + try + call LanguageClient#Notify('languageClient/handleCursorMoved', { + \ 'buftype': &buftype, + \ 'filename': LSP#filename(), + \ 'line': l:cursor_line, + \ 'LSP#visible_line_start()': LSP#visible_line_start(), + \ 'LSP#visible_line_end()': LSP#visible_line_end(), + \ }) + catch + call s:Debug('LanguageClient caught exception: ' . string(v:exception)) + endtry +endfunction + +function! LanguageClient#handleCompleteDone() abort + let user_data = get(v:completed_item, 'user_data', '') + if user_data ==# '' + return + endif + + try + call LanguageClient#Notify('languageClient/handleCompleteDone', { + \ 'filename': LSP#filename(), + \ 'completed_item': v:completed_item, + \ 'line': LSP#line(), + \ 'character': LSP#character(), + \ }) + catch + call s:Debug('LanguageClient caught exception: ' . string(v:exception)) + endtry +endfunction + +function! LanguageClient#handleVimLeavePre() abort + try + if get(g:, 'LanguageClient_autoStop', 1) + call LanguageClient#exit() + endif + catch + call s:Debug('LanguageClient caught exception: ' . string(v:exception)) + endtry +endfunction + +function! s:LanguageClient_FZFSinkLocation(line) abort + return LanguageClient#Notify('LanguageClient_FZFSinkLocation', [a:line]) +endfunction + +function! LanguageClient_FZFSinkCommand(selection) abort + return LanguageClient#Notify('LanguageClient_FZFSinkCommand', { + \ 'selection': a:selection, + \ }) +endfunction + +function! LanguageClient_NCMRefresh(info, context) abort + return LanguageClient#Call('LanguageClient_NCMRefresh', { + \ 'info': a:info, + \ 'ctx': a:context, + \ }, v:null) +endfunction + +function! LanguageClient_NCM2OnComplete(context) abort + return LanguageClient#Call('LanguageClient_NCM2OnComplete', { + \ 'ctx': a:context, + \ }, v:null) +endfunction + +function! LanguageClient#explainErrorAtPoint(...) abort + if &buftype !=# '' || &filetype ==# '' + return + endif + + let l:Callback = get(a:000, 1, v:null) + let l:params = { + \ 'buftype': &buftype, + \ 'filename': LSP#filename(), + \ 'line': LSP#line(), + \ 'character': LSP#character(), + \ 'handle': s:IsFalse(l:Callback), + \ } + call extend(l:params, get(a:000, 0, {})) + return LanguageClient#Call('languageClient/explainErrorAtPoint', l:params, l:Callback) +endfunction + +let g:LanguageClient_omniCompleteResults = [] +function! LanguageClient#omniComplete(...) abort + try + " Note: do not add 'text' as it might be huge. + let l:params = { + \ 'filename': LSP#filename(), + \ 'line': LSP#line(), + \ 'character': LSP#character(), + \ 'complete_position': v:null, + \ 'handle': v:false, + \ } + call extend(l:params, get(a:000, 0, {})) + let l:Callback = get(a:000, 1, g:LanguageClient_omniCompleteResults) + call LanguageClient#Call('languageClient/omniComplete', l:params, l:Callback) + catch + call add(l:Callback, []) + call s:Debug(string(v:exception)) + endtry +endfunction + +function! LanguageClient#get_complete_start(input) abort + " echomsg a:input + return match(a:input, '\k*$') +endfunction + +function! LanguageClient_filterCompletionItems(item, base) abort + return a:item.word =~# '^' . a:base +endfunction + +let g:LanguageClient_completeResults = [] +function! LanguageClient#complete(findstart, base) abort + if a:findstart + " Before requesting completion, content between l:start and current cursor is removed. + let s:completeText = LSP#text() + + let l:input = getline('.')[:LSP#character() - 1] + let l:start = LanguageClient#get_complete_start(l:input) + return l:start + else + " Magic happens that cursor jumps to the previously found l:start. + let l:result = LanguageClient_runSync( + \ 'LanguageClient#omniComplete', { + \ 'character': LSP#character() + len(a:base), + \ 'complete_position': LSP#character(), + \ 'text': s:completeText, + \ }) + let l:result = l:result is v:null ? [] : l:result + let l:filtered_items = [] + for l:item in l:result + if LanguageClient_filterCompletionItems(l:item, a:base) + call add(l:filtered_items, l:item) + endif + endfor + return filtered_items + endif +endfunction + +function! LanguageClient#textDocument_signatureHelp(...) abort + if &buftype !=# '' || &filetype ==# '' + return + endif + + let l:params = { + \ 'filename': LSP#filename(), + \ 'line': LSP#line(), + \ 'character': LSP#character(), + \ 'handle': v:true, + \ } + call extend(l:params, a:0 >= 1 ? a:1 : {}) + let l:Callback = a:0 >= 2 ? a:2 : v:null + return LanguageClient#Call('textDocument/signatureHelp', l:params, l:Callback) +endfunction + +function! LanguageClient#workspace_applyEdit(...) abort + if &buftype !=# '' || &filetype ==# '' + return + endif + + let l:params = { + \ 'edit': {}, + \ } + call extend(l:params, a:0 >= 1 ? a:1 : {}) + let l:Callback = a:0 >= 2 ? a:2 : v:null + return LanguageClient#Call('workspace/applyEdit', l:params, l:Callback) +endfunction + +function! LanguageClient#workspace_executeCommand(command, ...) abort + if &buftype !=# '' || &filetype ==# '' + return + endif + + let l:params = { + \ 'command': a:command, + \ 'arguments': get(a:000, 0, v:null), + \ } + let l:Callback = get(a:000, 1, v:null) + return LanguageClient#Call('workspace/executeCommand', l:params, l:Callback) +endfunction + +function! LanguageClient#exit() abort + return LanguageClient#Notify('exit', { + \ 'languageId': &filetype, + \ }) +endfunction + +" Set to 1 when the language server is busy (e.g. building the code). +let g:LanguageClient_serverStatus = 0 +let g:LanguageClient_serverStatusMessage = '' + +function! LanguageClient#serverStatus() abort + return g:LanguageClient_serverStatus +endfunction + +function! LanguageClient#serverStatusMessage() abort + return g:LanguageClient_serverStatusMessage +endfunction + +" Example function usable for status line. +function! LanguageClient#statusLine() abort + if g:LanguageClient_serverStatusMessage ==# '' + return '' + endif + + return '[' . g:LanguageClient_serverStatusMessage . ']' +endfunction + +function! LanguageClient#cquery_base(...) abort + let l:params = { + \ 'method': '$cquery/base', + \ } + call extend(l:params, get(a:000, 0, {})) + return call('LanguageClient#findLocations', [l:params] + a:000[1:]) +endfunction + +function! LanguageClient#cquery_callers(...) abort + let l:params = { + \ 'method': '$cquery/callers', + \ } + call extend(l:params, get(a:000, 0, {})) + return call('LanguageClient#findLocations', [l:params] + a:000[1:]) +endfunction + +function! LanguageClient#cquery_vars(...) abort + let l:params = { + \ 'method': '$cquery/vars', + \ } + call extend(l:params, get(a:000, 0, {})) + return call('LanguageClient#findLocations', [l:params] + a:000[1:]) +endfunction + +function! LanguageClient#java_classFileContent(...) abort + if &buftype !=# '' || &filetype ==# '' + return + endif + + let l:params = get(a:000, 0, {}) + let l:Callback = get(a:000, 1, v:null) + return LanguageClient#Call('java/classFileContent', l:params, l:Callback) +endfunction + +function! LanguageClient_contextMenuItems() abort + return { + \ 'Code Action': 'LanguageClient#textDocument_codeAction', + \ 'Definition': 'LanguageClient#textDocument_definition', + \ 'Document Symbol': 'LanguageClient#textDocument_documentSymbol', + \ 'Formatting': 'LanguageClient#textDocument_formatting', + \ 'Hover': 'LanguageClient#textDocument_hover', + \ 'Implementation': 'LanguageClient#textDocument_implementation', + \ 'Range Formatting': 'LanguageClient#textDocument_rangeFormatting', + \ 'References': 'LanguageClient#textDocument_references', + \ 'Rename': 'LanguageClient#textDocument_rename', + \ 'Signature Help': 'LanguageClient#textDocument_signatureHelp', + \ 'Type Definition': 'LanguageClient#textDocument_typeDefinition', + \ 'Document Highlight': 'LanguageClient#textDocument_documentHighlight', + \ 'Workspace Symbol': 'LanguageClient#workspace_symbol', + \ } +endfunction + +function! LanguageClient_handleContextMenuItem(item) abort + let l:items = LanguageClient_contextMenuItems() + silent! exe 'redraw' + return call(l:items[a:item], []) +endfunction + +function! LanguageClient_contextMenu() abort + let l:options = keys(LanguageClient_contextMenuItems()) + + if get(g:, 'loaded_fzf') && get(g:, 'LanguageClient_fzfContextMenu', 1) + return fzf#run(fzf#wrap({ + \ 'source': l:options, + \ 'sink': function('LanguageClient_handleContextMenuItem'), + \ })) + endif + + let l:selections = map(copy(l:options), { key, val -> printf('%d) %s', key + 1, val ) }) + + call inputsave() + let l:selection = inputlist(l:selections) + call inputrestore() + + if !l:selection || l:selection > len(l:selections) + return + endif + + return LanguageClient_handleContextMenuItem(l:options[l:selection - 1]) +endfunction + +function! LanguageClient#debugInfo(...) abort + let l:params = get(a:000, 0, {}) + let l:Callback = get(a:000, 1, v:null) + return LanguageClient#Call('languageClient/debugInfo', l:params, l:Callback) +endfunction + +let g:LanguageClient_loaded = s:Launch() diff --git a/vim/bundle/LanguageClient-neovim/autoload/health/LanguageClient.vim b/vim/bundle/LanguageClient-neovim/autoload/health/LanguageClient.vim new file mode 100644 index 0000000..598c1f5 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/autoload/health/LanguageClient.vim @@ -0,0 +1,25 @@ +function! s:checkJobFeature() abort + if !has('nvim') && !has('job') + call health#report_error('Not supported: not nvim nor vim with +job.') + endif +endfunction + +function! s:checkBinary() abort + let l:path = LanguageClient#binaryPath() + if executable(l:path) ==# 1 + call health#report_ok('binary found: ' . l:path) + else + call health#report_error( + \ 'binary is missing or not executable. ' . + \ 'Try reinstall it with install.sh or install.ps1: ' . + \ l:path) + endif + + let output = system([l:path, '--version']) + call health#report_ok(output) +endfunction + +function! health#LanguageClient#check() abort + call s:checkJobFeature() + call s:checkBinary() +endfunction diff --git a/vim/bundle/LanguageClient-neovim/build.rs b/vim/bundle/LanguageClient-neovim/build.rs new file mode 100644 index 0000000..6c0de07 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/build.rs @@ -0,0 +1,9 @@ +use std::fs::read_to_string; + +fn main() { + let git_hash = option_env!("TRAVIS_COMMIT") + .map(|s| s.into()) + .unwrap_or_else(|| read_to_string(".git/refs/heads/next").unwrap_or_default()); + + println!("cargo:rustc-env=GIT_HASH={}", git_hash); +} diff --git a/vim/bundle/LanguageClient-neovim/ci/Dockerfile b/vim/bundle/LanguageClient-neovim/ci/Dockerfile new file mode 100644 index 0000000..9a5e174 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/ci/Dockerfile @@ -0,0 +1,17 @@ +FROM rust:1.27-slim + +RUN apt-get update \ + && apt-get install --yes curl git neovim python3-pip python3-pytest mypy flake8 \ + && pip3 install neovim vim-vint + +RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - \ + && apt-get install --yes nodejs \ + && npm install -g javascript-typescript-langserver@2.5.5 + +RUN rustup component add rls-preview rust-analysis rust-src \ + && rustc --version \ + && rls --version + +ENV CARGO_TARGET_DIR=/tmp + +CMD /bin/bash diff --git a/vim/bundle/LanguageClient-neovim/ci/before_deploy.sh b/vim/bundle/LanguageClient-neovim/ci/before_deploy.sh new file mode 100644 index 0000000..1fbd4c4 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/ci/before_deploy.sh @@ -0,0 +1,46 @@ +set -ex + +package() { + BIN_NAME_TAG=$CRATE_NAME-$TRAVIS_TAG-$TARGET + if [[ $TARGET =~ .*windows.* ]]; then + BIN_NAME_TAG=$BIN_NAME_TAG.exe + fi + + cp -f target/$TARGET/release/$BIN_NAME bin/$BIN_NAME_TAG +} + +release_tag() { + cp -f target/$TARGET/release/$BIN_NAME bin/ + + git config --global user.email "travis@travis-ci.org" + git config --global user.name "Travis CI" + + git add --force bin/$BIN_NAME + SHA=$(git rev-parse --short HEAD) + git commit --message "Add binary. $SHA. $TRAVIS_TAG-$TARGET." + tagname="binary-$TRAVIS_TAG-$TARGET" + git tag --force "$tagname" + git push --force https://${GITHUB_TOKEN}@github.com/autozimu/LanguageClient-neovim.git "$tagname" + + git reset --hard HEAD^ +} + +TARGETS=(${TARGETS//:/ }) +for TARGET in "${TARGETS[@]}"; do + BIN_NAME=$CRATE_NAME + if [[ $TARGET =~ .*windows.* ]]; then + BIN_NAME=$BIN_NAME.exe + fi + + cross build --release --target $TARGET + release_tag + package +done + +if [[ $TRAVIS_OS_NAME == 'linux' ]]; then + sudo apt-get update + sudo apt-get install --yes python3-pip + sudo pip3 install semver + + ci/cleanup-binary-tags.py +fi diff --git a/vim/bundle/LanguageClient-neovim/ci/cleanup-binary-tags.py b/vim/bundle/LanguageClient-neovim/ci/cleanup-binary-tags.py new file mode 100755 index 0000000..72e8d22 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/ci/cleanup-binary-tags.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +import os +import subprocess +import re +import semver + + +def tag_to_version(tag): + version = re.sub(r'binary-', '', tag) + version = re.sub(r'-[x86|i686].*', '', version) + return version + + +subprocess.check_call('git pull --tags', shell=True) +tags = subprocess.check_output( + 'git tag --list | grep binary', shell=True).decode('UTF-8').splitlines() +versions = sorted(list(set([tag_to_version(tag) for tag in tags])), + key=semver.parse_version_info) +versions_to_delete = versions[:-3] + +cmd_delete_local = 'git tag --delete' +cmd_delete_remote = 'git push --delete ' +GITHUB_TOKEN = os.environ.get('GITHUB_TOKEN') +if GITHUB_TOKEN: + cmd_delete_remote += ( + 'https://{}@github.com/autozimu/LanguageClient-neovim.git' + .format(GITHUB_TOKEN)) +else: + cmd_delete_remote += 'origin' +for tag in tags: + if tag_to_version(tag) in versions_to_delete: + cmd_delete_local += ' ' + tag + cmd_delete_remote += ' ' + tag + +if not cmd_delete_local.endswith('delete'): + subprocess.check_call(cmd_delete_local, shell=True) +if not (cmd_delete_remote.endswith('origin') or + cmd_delete_remote.endswith('.git')): + subprocess.check_call(cmd_delete_remote, shell=True) diff --git a/vim/bundle/LanguageClient-neovim/ci/install.sh b/vim/bundle/LanguageClient-neovim/ci/install.sh new file mode 100644 index 0000000..80e18e4 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/ci/install.sh @@ -0,0 +1,47 @@ +set -ex + +main() { + local target= + if [ $TRAVIS_OS_NAME = linux ]; then + target=x86_64-unknown-linux-musl + sort=sort + else + target=x86_64-apple-darwin + sort=gsort # for `sort --sort-version`, from brew's coreutils. + fi + + # Builds for iOS are done on OSX, but require the specific target to be + # installed. + case $TARGET in + aarch64-apple-ios) + rustup target install aarch64-apple-ios + ;; + armv7-apple-ios) + rustup target install armv7-apple-ios + ;; + armv7s-apple-ios) + rustup target install armv7s-apple-ios + ;; + i386-apple-ios) + rustup target install i386-apple-ios + ;; + x86_64-apple-ios) + rustup target install x86_64-apple-ios + ;; + esac + + # This fetches latest stable release + local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \ + | cut -d/ -f3 \ + | grep -E '^v[0.1.0-9.]+$' \ + | $sort --version-sort \ + | tail -n1) + curl -LSfs https://japaric.github.io/trust/install.sh | \ + sh -s -- \ + --force \ + --git japaric/cross \ + --tag $tag \ + --target $target +} + +main diff --git a/vim/bundle/LanguageClient-neovim/ci/script.sh b/vim/bundle/LanguageClient-neovim/ci/script.sh new file mode 100755 index 0000000..523fcff --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/ci/script.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +set -o nounset +set -o errexit +set -o xtrace + +function main() { + make test + + if command -v docker > /dev/null ; then + make integration-test-docker + fi +} + +# we don't run the "test phase" when doing deploys +if [ -z $TRAVIS_TAG ]; then + main +fi diff --git a/vim/bundle/LanguageClient-neovim/doc/LanguageClient.txt b/vim/bundle/LanguageClient-neovim/doc/LanguageClient.txt new file mode 100644 index 0000000..4561492 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/doc/LanguageClient.txt @@ -0,0 +1,654 @@ +*LanguageClient* Language Server Protocol support for neovim + + +============================================================================== +CONTENTS *LanguageClientContents* + +1. Usage ....................... |LanguageClientUsage| +2. Configuration ............... |LanguageClientConfiguration| +3. Commands .................... |LanguageClientCommands| +4. Functions ................... |LanguageClientFunctions| +5. Events ...................... |LanguageClientEvents| +6. License ..................... |LanguageClientLicense| +7. Bugs ........................ |LanguageClientBugs| +8. Contributing ................ |LanguageClientContributing| + +============================================================================== +1. Usage *LanguageClientUsage* + +Before using of LanguageClient, it is necessary to specify commands that are +going to be used to start language server. See |LanguageClient_serverCommands| +for detail. Here is a simple example config: > + + let g:LanguageClient_serverCommands = { + \ 'rust': ['rustup', 'run', 'stable', 'rls'], + \ } + +After that, open a file with one of the above filetypes, functionalities +provided by language servers should be available out of the box. + +At this point, call any provided function as you like. See +|LanguageClientFunctions| for a complete list of functions. Usually one would +like to map these functions to shortcuts, for example: > + + nnoremap K :call LanguageClient#textDocument_hover() + nnoremap gd :call LanguageClient#textDocument_definition() + nnoremap :call LanguageClient#textDocument_rename() + +You can apply these mappings only for buffers with supported filetypes with a +simple function: > + + function LC_maps() + if has_key(g:LanguageClient_serverCommands, &filetype) + nnoremap K :call LanguageClient#textDocument_hover() + nnoremap gd :call LanguageClient#textDocument_definition() + nnoremap :call LanguageClient#textDocument_rename() + endif + endfunction + + autocmd FileType * call LC_maps() + +If one is using deoplete/nvim-completion-manager at the same time, completion +should work out of the box. Otherwise, completion is available with 'C-X C-O' +('omnifunc'). + +Alternatively, set 'completefunc': > + set completefunc=LanguageClient#complete +< +If the language server supports, diagnostic/lint information will be displayed +via gutter and syntax highlighting with real time editing. At the same time, +those info are populated into quickfix list (or location list), which can be +accessed by regular quickfix/location list operations. + +To use the language server with Vim's formatting operator |gq|, set 'formatexpr': > + set formatexpr=LanguageClient#textDocument_rangeFormatting_sync() +< + +============================================================================== +2. Configuration *LanguageClientConfiguration* + +2.1 g:LanguageClient_serverCommands *g:LanguageClient_serverCommands* + +String to list map. Defines commands to start language server for specific +filetype. For example: > + + let g:LanguageClient_serverCommands = { + \ 'rust': ['rustup', 'run', 'stable', 'rls'], + \ } + +In the example above, 'run', 'stable', and 'rls' are arguments to the +'rustup' command line tool. + +Or tcp connection string to the server, > + let g:LanguageCLient_servercommands = { + \ 'javascript': ['tcp://127.0.0.1:2089'], + \ } + +Note: environmental variables are not supported except home directory alias `~`. + +Default: {} +Valid Option: Map | String> + +2.2 g:LanguageClient_diagnosticsDisplay *g:LanguageClient_diagnosticsDisplay* + +Control how diagnostics messages are displayed. + +Default: > + { + 1: { + "name": "Error", + "texthl": "ALEError", + "signText": "✖", + "signTexthl": "ALEErrorSign", + }, + 2: { + "name": "Warning", + "texthl": "ALEWarning", + "signText": "⚠", + "signTexthl": "ALEWarningSign", + }, + 3: { + "name": "Information", + "texthl": "ALEInfo", + "signText": "ℹ", + "signTexthl": "ALEInfoSign", + }, + 4: { + "name": "Hint", + "texthl": "ALEInfo", + "signText": "➤", + "signTexthl": "ALEInfoSign", + }, + } + +2.3 g:LanguageClient_diagnosticsSignsMax *g:LanguageClient_diagnosticsSignsMax* + +Control how many diagnostics signs are displayed. + +Default: v:null (Show all signs) +Valid options: v:null | number + +2.4 g:LanguageClient_changeThrottle *g:LanguageClient_changeThrottle* + +Interval in seconds during which textDocument_didChange is suppressed. For +example: > + + let g:LanguageClient_changeThrottle = 0.5 + +This will make LanguageClient pause 0.5 second to send text changes to +server after one textDocument_didChange is sent. + +Default: v:null (No throttling) +Valid options: v:null | number + +2.5 g:LanguageClient_autoStart *g:LanguageClient_autoStart* + +Whether to start language servers automatically when opening a file of +associated filetype. + +Default: 1. + +2.6 g:LanguageClient_autoStop *g:LanguageClient_autoStop* + +Whether to stop language servers automatically when closing vim. +associated filetype. + +Default: 1. + +2.7 g:LanguageClient_selectionUI *g:LanguageClient_selectionUI* + +Selection UI used when there are multiple entries. + +Default: If fzf is loaded, use "fzf", otherwise use "location-list". +Valid options: "fzf" | "quickfix" | "location-list" + +2.8 g:LanguageClient_trace *g:LanguageClient_trace* + +Trace setting passed to server. + +Default: "off" +Valid options: "off" | "messages" | "verbose" + +2.9 g:LanguageClient_diagnosticsList *g:LanguageClient_diagnosticsList* + +List used to fill diagnostic messages. + +Default: "Quickfix" +Valid options: "Quickfix" | "Location" | "Disabled" + +2.10 g:LanguageClient_diagnosticsEnable *g:LanguageClient_diagnosticsEnable* + +Whether to handle diagnostic messages, including gutter, highlight and +quickfix/location list. + +Default: 1 +Valid options: 1 | 0 + +2.11 g:LanguageClient_windowLogMessageLevel *g:LanguageClient_windowLogMessageLevel* + +Maximum MessageType to show messages from window/logMessage notifications. + +Default: "Warning" +Valid options: "Error" | "Warning" | "Info" | "Log" + +2.12 g:LanguageClient_settingsPath *g:LanguageClient_settingsPath* + +Path for language server settings. + +Example settings file content: > + { + "rust.clippy_preference": "on" + } + +Default: ".vim/settings.json" + +2.13 g:LanguageClient_loadSettings *g:LanguageClient_loadSettings* + +Whether to load language server settings. + +Default: 1 +Valid options: 1 | 0 + +2.14 g:LanguageClient_loggingFile *g:LanguageClient_loggingFile* + +Log file location. + +Default: null +Valid options: any valid path. + +2.14 g:LanguageClient_loggingLevel *g:LanguageClient_loggingLevel* + +Logging level. + +Default: 'WARN' +Valid options: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' + +2.14 g:LanguageClient_serverStderr *g:LanguageClient_serverStderr* + +Path for language server stderr. + +Default: None +Valid options: any valid path. + +2.15 g:LanguageClient_rootMarkers *g:LanguageClient_rootMarkers* + +Customized project root markers. Generally a heuristic algorithm within this +plugin should be able to detect project root automatically. This option is +provided in case the algorithm failed. + +Example setting 1. List of string array. Shell-like glob is supported. > + let g:LanguageClient_rootMarkers = ['.root', 'project.*'] + +Example setting 2. Map filetype to string array. > + let g:LanguageClient_rootMarkers = { + \ 'javascript': ['project.json'], + \ 'rust': ['Cargo.toml'], + \ } + +Default: v:null +Valid option: Array | Map> + +2.16 g:LanguageClient_fzfOptions *g:LanguageClient_fzfOptions* + +Customize fzf. Check fzf documentation for available options. + +Default: v:null +Valid option: Array | String + +2.17 g:LanguageClient_hasSnippetSupport *g:LanguageClient_hasSnippetSupport* + +Override detection of snippet support. + +Default: 1 +Valid options: 1 | 0 + +2.18 g:LanguageClient_waitOutputTimeout *g:LanguageClient_waitOutputTimeout* + +Duration of time (in seconds) to wait for language server to return output +before timing out. + +Default: 10 +Valid options: number + +2.19 g:LanguageClient_hoverPreview *g:LanguageClient_hoverPreview* + +Controls how hover output is displayed. Must be one of the following: + Never - Never use preview window, always echo hover output + Auto - Use preview window for hover entries longer than one line (default) + Always - Always use preview window, never echo hover output + +Default: "Auto" +Valid options: "Never", "Auto", "Always" + +2.20 g:LanguageClient_fzfContextMenu *g:LanguageClient_fzfContextMenu* + +Should FZF be used for `LanguageClient_contextMenu()`. + +Default: 1 +Valid options: 1 | 0 + +2.21 g:LanguageClient_completionPreferTextEdit +*g:LanguageClient_completionPreferTextEdit* + +(Experimental) Should prefer use textEdit it is present in +CompletionItem. + +Note: textEdit support requires vim >= v8.0.1493, or vim >= v8.1.0039, or +neovim >= 0.3.0. + +Default: 0 +Valid options: 1 | 0 + +2.22 g:LanguageClient_documentHighlightDisplay *g:LanguageClient_documentHighlightDisplay* + +Control how document highlights are displayed. + +Default: > + { + 1: { + "name": "Text", + "texthl": "SpellCap", + }, + 2: { + "name": "Read", + "texthl": "SpellLocal", + }, + 3: { + "name": "Write", + "texthl": "SpellRare", + }, + } + +============================================================================== +3. Commands *LanguageClientCommands* + +3.1 LanguageClientStart *LanguageClientStart* + +Start language server for current buffer. + +3.2 LanguageClientStop *LanguageClientStop* + +Stop current language server. + +============================================================================== +4. Functions *LanguageClientFunctions* + +*LanguageClient#Call()* +*LanguageClient_Call()* +Signature: LanguageClient#Call(method: String, params: Map | List, callback: Funcref | String | List | Null) + +Call a method of current language server. After receiving response, if +callback is a function, it is invoked with response as params, if the callback +is a list, the response is pushed at the end of it, if callback is null, it is +handled by this plugin default handler. + +*LanguageClient#Notify()* +*LanguageClient_Notify()* +Signature: LanguageClient#Notify(method: String, params: Map | List) + +Send a notification to the current language server. + +*LanguageClient_contextMenu()* +Signature: LanguageClient#contextMenu(...) + +Show list of all available actions. + +If optional dependency FZF is installed, actions will be displayed in a FZF +prompt, selecting one of the action will then call the action's function. + +To skip FZF prompt even if FZF is installed and use native numbered list, +add this variable to vimrc: +`let g:LanguageClient_fzfContextMenu = 0` + +For Denite users, a source with name 'contextMenu' is provided. + +*LanguageClient#textDocument_hover()* +*LanguageClient_textDocument_hover()* +Signature: LanguageClient#textDocument_hover(...) + +Show type info (and short doc) of identifier under cursor. + +*LanguageClient#textDocument_definition()* +*LanguageClient_textDocument_definition()* +Signature: LanguageClient#textDocument_definition(...) + +Goto definition under cursor. + +*LanguageClient#textDocument_typeDefinition()* +*LanguageClient_textDocument_typeDefinition()* +Signature: LanguageClient#textDocument_typeDefinition(...) + +Goto type definition under cursor. + +*LanguageClient#textDocument_implementation()* +*LanguageClient_textDocument_implementation()* +Signature: LanguageClient#textDocument_implementation(...) + +Goto implementation under cursor. + +*LanguageClient#textDocument_rename()* +Signature: LanguageClient#textDocument_rename() + LanguageClient#textDocument_rename({"newName": ...}) + +Rename identifier under cursor. + +Accepts an optional dictionary argument, which if passed with the {newName} +property, will rename to the provided value without prompting the user. This +can be useful in situations where {newName} can be wholly determined from the +existing name (e.g. capitalization, camelCase, etc). + + +Example bindings combining with tpope/vim-abolish: +> + " Rename - rn => rename + noremap rn :call LanguageClient#textDocument_rename() + + " Rename - rc => rename camelCase + noremap rc :call LanguageClient#textDocument_rename( + \ {'newName': Abolish.camelcase(expand(''))}) + + " Rename - rs => rename snake_case + noremap rs :call LanguageClient#textDocument_rename( + \ {'newName': Abolish.snakecase(expand(''))}) + + " Rename - ru => rename UPPERCASE + noremap ru :call LanguageClient#textDocument_rename( + \ {'newName': Abolish.uppercase(expand(''))}) +< + +*LanguageClient#textDocument_documentSymbol()* +*LanguageClient_textDocument_documentSymbol()* +Signature: LanguageClient#textDocument_documentSymbol(...) + +List of current buffer's symbols. + +If optional dependency FZF is installed, symbols will be displayed in a FZF +prompt, selecting one of the symbol will then goto the symbol's definition. + +For Denite users, a source with name 'documentSymbol' is provided. + +*LanguageClient#textDocument_references()* +*LanguageClient_textDocument_references()* +Signature: LanguageClient#textDocument_references(...) + +List all references of identifier under cursor. + +If optional dependency FZF is installed, locations will be displayed in a FZF +prompt, selecting one of the entry will then goto the reference location. + +For Denite users, a source with name 'references' is provided. + +*LanguageClient#textDocument_codeAction()* +*LanguageClient_textDocument_codeAction()* +Signature: LanguageClient#textDocument_codeAction(...) + +Show code actions at current location. + +*LanguageClient#textDocument_completion()* +*LanguageClient_textDocument_completion()* +Signature: LanguageClient#textDocument_completion(...) + +Get a list of completion items at current editing location. Note, this is an +synchronous call. + +When using a supported completion manager (deoplete and +nvim-completion-manager are supported), completion should work out of the box. + +*LanguageClient#textDocument_formatting()* +*LanguageClient_textDocument_formatting()* +Signature: LanguageClient#textDocument_formatting(...) + +Format current document. + +*LanguageClient#textDocument_rangeFormatting()* +*LanguageClient_textDocument_rangeFormatting()* +Signature: LanguageClient#textDocument_rangeFormatting(...) + +Format selected lines. + +*LanguageClient#textDocument_documentHighlight()* +*LanguageClient_textDocument_documentHighlight()* +Signature: LanguageClient#textDocument_documentHighlight(...) + +Highlight usages of the symbol under the cursor. + +*LanguageClient#clearDocumentHighlight()* +*LanguageClient_clearDocumentHighlight()* +Signature: LanguageClient#clearDocumentHighlight() + +Clear the symbol usages highlighting. + +*LanguageClient#workspace_symbol()* +*LanguageClient_workspace_symbol()* +Signature: LanguageClient#workspace_symbol([query: String], ...) + +List of project's symbols. + +If optional dependency FZF is installed, symbols will be displayed in a FZF +prompt, selecting one of the symbol will then goto the symbol's definition. + +For Denite users, a source with name 'workspaceSymbol' is provided. + +*LanguageClient#workspace_applyEdit()* +*LanguageClient_workspace_applyEdit()* +Signature: LanguageClient#workspace_applyEdit(params: Dict, callback: Function | List | Null) + +Apply a workspace edit. + +*LanguageClient#workspace_executeCommand()* +*LanguageClient_workspace_executeCommand()* +Signature: LanguageClient#workspace_executeCommand(command: String, [arguments: Any], [callback: Function | List | Null]) + +Execute a workspace command. + +*LanguageClient#setLoggingLevel()* +*LanguageClient_setLoggingLevel()* +Signature: LanguageClient#setLoggingLevel(level: String) + +Set the plugin logging level. + +Valid logging levels are 'ERROR', 'WARN'(default), 'INFO', 'DEBUG'. + +*LanguageClient#setDiagnosticsList()* +Signature: LanguageClient#setDiagnosticsList(diagnosticsList: String) + +Set the destination of diagnostics. + +Valid options are 'Quickfix', 'Location', 'Disabled'. + +*LanguageClient#registerServerCommands()* +*LanguageClient_registerServerCommands()* +Signature: LanguageClient#registerServerCommands(commands: Map) + +Register/Override commands to start language servers. + +*LanguageClient#registerHandlers* +*LanguageClient_registerHandlers* +Signature: LanguageClient#registerHandlers(handlers: Map) + +Register/Override method/notification handlers. + +Example > + function! HandleWindowProgress(params) abort + echomsg json_encode(a:params) + endfunction + + call LanguageClient#registerHandlers({ + \ 'window/progress': 'HandleWindowProgress', + \ }) + +*LanguageClient#serverStatus()* +*LanguageClient_serverStatus()* +Signature: LanguageClient#serverStatus() + +Get language server status. 0 for server idle. 1 for server busy. + +*LanguageClient#serverStatusMessage()* +*LanguageClient_serverStatusMessage()* +Signature: LanguageClient#serverStatusMessage() + +Get a detail message of server status. + +*LanguageClient#statusLine()* +*LanguageClient_statusLine()* +Signature: LanguageClient#statusLine() + +Example status line making use of |LanguageClient_serverStatusMessage|. + +*LanguageClient#cquery_base* +*LanguageClient_cquery_base* +Signature: LanguageClient#cquery_base(...) + +Call $cquery/base. + +*LanguageClient#cquery_callers* +*LanguageClient_cquery_callers* +Signature: LanguageClient#cquery_callers(...) + +Call $cquery/callers. + +*LanguageClient#cquery_vars* +*LanguageClient_cquery_vars* +Signature: LanguageClient#cquery_vars(...) + +Call $cquery/vars. + +*LanguageClient#java_classFileContent* +Signature: LanguageClient#java_classFileContent(...) + +Call java/classFileContent. + +*LanguageClient#explainErrorAtPoint* +Signature: LanguageClient#explainErrorAtPoint(...) + +Show detailed error under cursor. + +*LanguageClient#debugInfo* +Signature: LanguageClient#debugInfo(...) + +Print out debug info. + +============================================================================== +5. Events *LanguageClientEvents* + +LanguageClient provides two events for use with |User| |autocmd|s. + +5.1 LanguageClientStarted +*LanguageClientStarted* + +This event is triggered after LanguageClient has successfully started. + +5.2 LanguageClientStopped +*LanguageClientStopped* + +This event is triggered after LanguageClient has stopped. + +Example: > + augroup LanguageClient_config + autocmd! + autocmd User LanguageClientStarted setlocal signcolumn=yes + autocmd User LanguageClientStopped setlocal signcolumn=auto + augroup END + +5.3 LanguageClientDiagnosticsChanged +*LanguageClientDiagnosticsChanged* + +This event is triggered when diagnostics changed. + +5.4 LanguageClientBufReadPost +*LanguageClientBufReadPost* + +Triggered after BufReadPost is successfully handled by language client. + +============================================================================== +6. License *LanguageClientLicense* + +The MIT License. + +============================================================================== +7. Bugs *LanguageClientBugs* + +Please report all bugs at https://github.com/autozimu/LanguageClient-neovim/issues + +If you believe you've find a bug in this plugin, please first try to help +narrow down where the error happens, which will reduce bug fix time/effort +significantly. + +Experiment with VS Code plugin if the language server has one. + +Try increasing logging level to 'INFO' or 'DEBUG' using the +|LanguageClient_setLoggingLevel| function, and check the log file. + +There is also an utility script in: > + + $RUNTIME/tests/wrapper-server.sh + +which can work like a proxy and logs all stdin and stdout of the language +server into a log file. + +============================================================================== +8. Contributing *LanguageClientContributing* + +https://github.com/autozimu/LanguageClient-neovim + +vim: ft=help diff --git a/vim/bundle/LanguageClient-neovim/install.ps1 b/vim/bundle/LanguageClient-neovim/install.ps1 new file mode 100644 index 0000000..edcfbda --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/install.ps1 @@ -0,0 +1,19 @@ +$version = '0.1.123' +$name = 'languageclient' +$url = "https://github.com/autozimu/LanguageClient-neovim/releases/download/$version/$name-$version-" + +if ($ENV:PROCESSOR_ARCHITECTURE -eq 'AMD64') { + $url += 'x86_64' +} else { + $url += 'i686' +} + +$url += '-pc-windows-gnu.exe' + +$path = "bin\$name.exe" +if (Test-Path $path) { + Remove-Item -Force $path +} +echo "Downloading $url ..." +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +Invoke-WebRequest -Uri $url -OutFile $path diff --git a/vim/bundle/LanguageClient-neovim/install.sh b/vim/bundle/LanguageClient-neovim/install.sh new file mode 100755 index 0000000..fce601e --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/install.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env sh +# Try install by +# - download binary +# - build with cargo + +set -o nounset # error when referencing undefined variable +set -o errexit # exit when command fails + +version=0.1.123 +name=languageclient + +try_curl() { + command -v curl > /dev/null && \ + curl --fail --location "$1" --output bin/$name +} + +try_wget() { + command -v wget > /dev/null && \ + wget --output-document=bin/$name "$1" +} + +download() { + echo "Downloading bin/${name}..." + url=https://github.com/autozimu/LanguageClient-neovim/releases/download/$version/${1} + if (try_curl "$url" || try_wget "$url"); then + chmod a+x bin/$name + return + else + try_build || echo "Prebuilt binary might not be ready yet. Please check minutes later." + fi +} + +try_build() { + if command -v cargo > /dev/null; then + echo "Trying build locally ..." + make release + else + return 1 + fi +} + +rm -f bin/languageclient + +arch=$(uname -sm) +case "${arch}" in + "Linux x86_64") download $name-$version-x86_64-unknown-linux-musl ;; + "Linux i686") download $name-$version-i686-unknown-linux-musl ;; + "Linux aarch64") download $name-$version-aarch64-unknown-linux-gnu ;; + "Darwin x86_64") download $name-$version-x86_64-apple-darwin ;; + "FreeBSD amd64") download $name-$version-x86_64-unknown-freebsd ;; + *) echo "No pre-built binary available for ${arch}."; try_build ;; +esac diff --git a/vim/bundle/LanguageClient-neovim/min-vimrc.vim b/vim/bundle/LanguageClient-neovim/min-vimrc.vim new file mode 100644 index 0000000..62631d2 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/min-vimrc.vim @@ -0,0 +1,24 @@ +call plug#begin('~/.local/share/nvim/plugged') + +Plug 'autozimu/LanguageClient-neovim', { + \ 'branch': 'next', + \ 'do': 'bash install.sh', + \ } + +call plug#end() + +augroup filetype_rust + autocmd! + autocmd BufReadPost *.rs setlocal filetype=rust +augroup END + +" Always draw sign column. Prevent buffer moving when adding/deleting sign. +set signcolumn=yes + +let g:LanguageClient_serverCommands = { + \ 'rust': ['rustup', 'run', 'stable', 'rls'], + \ } +let $RUST_BACKTRACE = 1 +let g:LanguageClient_loggingLevel = 'INFO' +let g:LanguageClient_loggingFile = stdpath('data') . '/LanguageClient.log' +let g:LanguageClient_serverStderr = stdpath('data') . '/LanguageServer.log' diff --git a/vim/bundle/LanguageClient-neovim/plugin/LanguageClient.vim b/vim/bundle/LanguageClient-neovim/plugin/LanguageClient.vim new file mode 100644 index 0000000..7b2fe10 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/plugin/LanguageClient.vim @@ -0,0 +1,128 @@ +function! LanguageClient_textDocument_hover(...) + return call('LanguageClient#textDocument_hover', a:000) +endfunction + +function! LanguageClient_textDocument_definition(...) + return call('LanguageClient#textDocument_definition', a:000) +endfunction + +function! LanguageClient_textDocument_typeDefinition(...) + return call('LanguageClient#textDocument_typeDefinition', a:000) +endfunction + +function! LanguageClient_textDocument_implementation(...) + return call('LanguageClient#textDocument_implementation', a:000) +endfunction + +function! LanguageClient_textDocument_rename(...) + return call('LanguageClient#textDocument_rename', a:000) +endfunction + +function! LanguageClient_textDocument_documentSymbol(...) + return call('LanguageClient#textDocument_documentSymbol', a:000) +endfunction + +function! LanguageClient_textDocument_references(...) + return call('LanguageClient#textDocument_references', a:000) +endfunction + +function! LanguageClient_textDocument_codeAction(...) + return call('LanguageClient#textDocument_codeAction', a:000) +endfunction + +function! LanguageClient_textDocument_completion(...) + return call('LanguageClient#textDocument_completion', a:000) +endfunction + +function! LanguageClient_textDocument_formatting(...) + return call('LanguageClient#textDocument_formatting', a:000) +endfunction + +function! LanguageClient_textDocument_rangeFormatting(...) + return call('LanguageClient#textDocument_rangeFormatting', a:000) +endfunction + +function! LanguageClient_textDocument_documentHighlight(...) + return call('LanguageClient#textDocument_documentHighlight', a:000) +endfunction + +function! LanguageClient_workspace_symbol(...) + return call('LanguageClient#workspace_symbol', a:000) +endfunction + +function! LanguageClient_workspace_applyEdit(...) + return call('LanguageClient#workspace_applyEdit', a:000) +endfunction + +function! LanguageClient_workspace_executeCommand(...) + return call('LanguageClient#workspace_executeCommand', a:000) +endfunction + +function! LanguageClient_setLoggingLevel(...) + return call('LanguageClient#setLoggingLevel', a:000) +endfunction + +function! LanguageClient_registerServerCommands(...) + return call('LanguageClient#registerServerCommands', a:000) +endfunction + +function! LanguageClient_registerHandlers(...) + return call('LanguageClient#registerHandlers', a:000) +endfunction + +function! LanguageClient_omniComplete(...) + return call('LanguageClient#omniComplete', a:000) +endfunction + +function! LanguageClient_complete(...) + return call('LanguageClient#complete', a:000) +endfunction + +function! LanguageClient_serverStatus(...) + return call('LanguageClient#serverStatus', a:000) +endfunction + +function! LanguageClient_serverStatusMessage(...) + return call('LanguageClient#serverStatusMessage', a:000) +endfunction + +function! LanguageClient_statusLine(...) + return call('LanguageClient#statusLine', a:000) +endfunction + +function! LanguageClient_clearDocumentHighlight(...) + return call('LanguageClient#clearDocumentHighlight', a:000) +endfunction + +function! LanguageClient_cquery_base(...) + return call('LanguageClient#cquery_base', a:000) +endfunction + +function! LanguageClient_cquery_vars(...) + return call('LanguageClient#cquery_vars', a:000) +endfunction + +command! -nargs=* LanguageClientStart :call LanguageClient#startServer() +command! LanguageClientStop :call LanguageClient#exit() + +augroup languageClient + autocmd! + autocmd BufNewFile * call LanguageClient#handleBufNewFile() + autocmd BufReadPost * call LanguageClient#handleBufReadPost() + autocmd BufWritePost * call LanguageClient#handleBufWritePost() + autocmd BufDelete * call LanguageClient#handleBufDelete() + autocmd TextChanged * call LanguageClient#handleTextChanged() + autocmd TextChangedI * call LanguageClient#handleTextChanged() + if exists('##TextChangedP') + autocmd TextChangedP * call LanguageClient#handleTextChanged() + endif + autocmd CursorMoved * call LanguageClient#handleCursorMoved() + autocmd VimLeavePre * call LanguageClient#handleVimLeavePre() + + autocmd CompleteDone * call LanguageClient#handleCompleteDone() + + if get(g:, 'LanguageClient_signatureHelpOnCompleteDone', 0) + autocmd CompleteDone * + \ call LanguageClient#textDocument_signatureHelp({}, 's:HandleOutputNothing') + endif +augroup END diff --git a/vim/bundle/LanguageClient-neovim/rplugin/python3/.gitignore b/vim/bundle/LanguageClient-neovim/rplugin/python3/.gitignore new file mode 100644 index 0000000..4373681 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/rplugin/python3/.gitignore @@ -0,0 +1,2 @@ +tests/nvimPID +LanguageClient/.ropeproject diff --git a/vim/bundle/LanguageClient-neovim/rplugin/python3/denite/source/codeAction.py b/vim/bundle/LanguageClient-neovim/rplugin/python3/denite/source/codeAction.py new file mode 100644 index 0000000..5907225 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/rplugin/python3/denite/source/codeAction.py @@ -0,0 +1,24 @@ +from typing import List, Dict +from .base import Base + + +class Source(Base): + def __init__(self, vim): + super().__init__(vim) + + self.name = 'codeAction' + self.kind = 'command' + + def gather_candidates(self, context: Dict) -> List[Dict]: + result = self.vim.funcs.LanguageClient_runSync( + 'LanguageClient_textDocument_codeAction', {}) or [] + return [convert_to_candidate(item) for item in result] + + +def convert_to_candidate(cmd: Dict) -> Dict: + cmd_str = '{}: {}'.format(cmd['command'], cmd['title']) + return { + 'word': cmd_str, + 'action__command': 'call ' + 'LanguageClient_FZFSinkCommand("{}")'.format(cmd_str), + } diff --git a/vim/bundle/LanguageClient-neovim/rplugin/python3/denite/source/contextMenu.py b/vim/bundle/LanguageClient-neovim/rplugin/python3/denite/source/contextMenu.py new file mode 100644 index 0000000..f8d4cda --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/rplugin/python3/denite/source/contextMenu.py @@ -0,0 +1,21 @@ +from typing import List, Dict +from .base import Base + + +class Source(Base): + def __init__(self, vim): + super().__init__(vim) + + self.name = 'contextMenu' + self.kind = 'command' + + def convert_to_candidate(self, item): + return { + "word": item, + 'action__command': 'call ' + 'LanguageClient_handleContextMenuItem("{}")'.format(item), + } + + def gather_candidates(self, context: Dict) -> List[Dict]: + result = self.vim.funcs.LanguageClient_contextMenuItems() or {} + return [self.convert_to_candidate(key) for key in result.keys()] diff --git a/vim/bundle/LanguageClient-neovim/rplugin/python3/denite/source/documentSymbol.py b/vim/bundle/LanguageClient-neovim/rplugin/python3/denite/source/documentSymbol.py new file mode 100644 index 0000000..77a5837 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/rplugin/python3/denite/source/documentSymbol.py @@ -0,0 +1,31 @@ +from typing import List, Dict + +from .base import Base + + +def convert_to_candidate(symbol: Dict, bufname: str) -> Dict: + name = symbol["name"] + start = symbol["location"]["range"]["start"] + line = start["line"] + 1 + character = start["character"] + 1 + return { + "word": "{}:{}:\t{}".format(line, character, name), + "action__path": bufname, + "action__line": line, + "action__col": character, + } + + +class Source(Base): + def __init__(self, vim): + super().__init__(vim) + + self.name = 'documentSymbol' + self.kind = 'file' + + def gather_candidates(self, context: Dict) -> List[Dict]: + bufname = self.vim.current.buffer.name + result = self.vim.funcs.LanguageClient_runSync( + 'LanguageClient_textDocument_documentSymbol', {}) or [] + return [convert_to_candidate(symbol, bufname) for + symbol in result] diff --git a/vim/bundle/LanguageClient-neovim/rplugin/python3/denite/source/references.py b/vim/bundle/LanguageClient-neovim/rplugin/python3/denite/source/references.py new file mode 100644 index 0000000..1dc49a2 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/rplugin/python3/denite/source/references.py @@ -0,0 +1,88 @@ +from urllib import request, parse +from os import path +from typing import List, Dict + +from .base import Base + +GREP_HEADER_SYNTAX = ( + 'syntax match deniteSource_grepHeader ' + r'/\v[^:]*:\d+(:\d+)? / ' + 'contained keepend') + +GREP_FILE_SYNTAX = ( + 'syntax match deniteSource_grepFile ' + r'/[^:]*:/ ' + 'contained containedin=deniteSource_grepHeader ' + 'nextgroup=deniteSource_grepLineNR') +GREP_FILE_HIGHLIGHT = 'highlight default link deniteSource_grepFile Comment' + +GREP_LINE_SYNTAX = ( + 'syntax match deniteSource_grepLineNR ' + r'/\d\+\(:\d\+\)\?/ ' + 'contained containedin=deniteSource_grepHeader') +GREP_LINE_HIGHLIGHT = 'highlight default link deniteSource_grepLineNR LineNR' + +GREP_PATTERNS_HIGHLIGHT = 'highlight default link deniteGrepPatterns Function' + + +def uri_to_path(uri: str) -> str: + return request.url2pathname(parse.urlparse(uri).path) + + +class Source(Base): + def __init__(self, vim): + super().__init__(vim) + self.vim = vim + + self.name = 'references' + self.kind = 'file' + + def define_syntax(self): + self.vim.command( + 'syntax region ' + self.syntax_name + ' start=// end=/$/ ' + 'contains=deniteSource_grepHeader,deniteMatchedRange' + ' contained') + # TODO: make this match the 'range' on each location + # self.vim.command( + # 'syntax match deniteGrepPatterns ' + + # r'/%s/ ' % r'\|'.join(util.regex_convert_str_vim(pattern) + # for pattern in self.context['__patterns']) + + # 'contained containedin=' + self.syntax_name) + + def highlight(self): + self.vim.command(GREP_HEADER_SYNTAX) + self.vim.command(GREP_FILE_SYNTAX) + self.vim.command(GREP_FILE_HIGHLIGHT) + self.vim.command(GREP_LINE_SYNTAX) + self.vim.command(GREP_LINE_HIGHLIGHT) + self.vim.command(GREP_PATTERNS_HIGHLIGHT) + + def convert_to_candidates(self, locations: List[Dict]) -> List[Dict]: + candidates = [] + pwd = self.vim.funcs.getcwd() + for loc in locations: + filepath = uri_to_path(loc["uri"]) + relpath = path.relpath(filepath, pwd) + start = loc["range"]["start"] + line = start["line"] + 1 + character = start["character"] + 1 + text = loc.get("text", "") + output = '{0}:{1}{2} {3}'.format( + relpath, + line, + (':' + str(character) if character != 0 else ''), + text) + candidates.append({ + "word": output, + "abbr": output, + "action__path": filepath, + "action__line": line, + "action__col": character, + }) + + return candidates + + def gather_candidates(self, context): + result = self.vim.funcs.LanguageClient_runSync( + "LanguageClient#textDocument_references", {}) or [] + return self.convert_to_candidates(result) diff --git a/vim/bundle/LanguageClient-neovim/rplugin/python3/denite/source/workspaceSymbol.py b/vim/bundle/LanguageClient-neovim/rplugin/python3/denite/source/workspaceSymbol.py new file mode 100644 index 0000000..e7dcc02 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/rplugin/python3/denite/source/workspaceSymbol.py @@ -0,0 +1,44 @@ +from urllib import request, parse +from os import path +from typing import List, Dict + +from .base import Base + + +def uri_to_path(uri: str) -> str: + return request.url2pathname(parse.urlparse(uri).path) + + +class Source(Base): + def __init__(self, vim): + super().__init__(vim) + self.vim = vim + + self.name = 'workspaceSymbol' + self.kind = 'file' + + def convert_to_candidates(self, symbols: List[Dict]) -> List[Dict]: + candidates = [] + pwd = self.vim.funcs.getcwd() + for sb in symbols: + name = sb["name"] + uri = sb["location"]["uri"] + filepath = uri_to_path(uri) + relpath = path.relpath(filepath, pwd) + start = sb["location"]["range"]["start"] + line = start["line"] + 1 + character = start["character"] + 1 + candidates.append({ + "word": "{}:{}:{}:\t{}".format( + relpath, line, character, name), + "action__path": filepath, + "action__line": line, + "action__col": character, + }) + + return candidates + + def gather_candidates(self, context): + result = self.vim.funcs.LanguageClient_runSync( + 'LanguageClient#workspace_symbol', '', {}) or [] + return self.convert_to_candidates(result) diff --git a/vim/bundle/LanguageClient-neovim/rplugin/python3/deoplete/sources/LanguageClientSource.py b/vim/bundle/LanguageClient-neovim/rplugin/python3/deoplete/sources/LanguageClientSource.py new file mode 100644 index 0000000..d9a984a --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/rplugin/python3/deoplete/sources/LanguageClientSource.py @@ -0,0 +1,49 @@ +from .base import Base + + +CompleteOutputs = "g:LanguageClient_omniCompleteResults" + + +class Source(Base): + def __init__(self, vim): + super().__init__(vim) + + self.name = "LanguageClient" + self.mark = "[LC]" + self.rank = 1000 + self.min_pattern_length = 1 + self.filetypes = vim.eval( + "get(g:, 'LanguageClient_serverCommands', {})").keys() + self.input_pattern += r'(\.|::|->)\w*$' + + def get_complete_position(self, context): + return self.vim.call( + 'LanguageClient#get_complete_start', context['input']) + + def gather_candidates(self, context): + if context["is_async"]: + outputs = self.vim.eval(CompleteOutputs) + if len(outputs) != 0: + context["is_async"] = False + # TODO: error handling. + candidates = outputs[0].get("result", []) + # log(str(candidates)) + return candidates + else: + context["is_async"] = True + self.vim.command("let {} = []".format(CompleteOutputs)) + character = (context["complete_position"] + + len(context["complete_str"])) + self.vim.funcs.LanguageClient_omniComplete({ + "character": character, + "complete_position": context["complete_position"], + }) + return [] + + +# f = open("/tmp/deoplete.log", "w") + + +# def log(message): +# f.writelines([message]) +# f.flush() diff --git a/vim/bundle/LanguageClient-neovim/src/languageclient.rs b/vim/bundle/LanguageClient-neovim/src/languageclient.rs new file mode 100644 index 0000000..d7a71b0 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/src/languageclient.rs @@ -0,0 +1,3023 @@ +use super::*; +use lsp::notification::Notification; +use lsp::request::Request; + +impl State { + /////// Utils /////// + + pub fn gather_args( + &mut self, + exps: &[E], + map: &Value, + ) -> Result { + let mut map = map + .as_object() + .cloned() + .unwrap_or_else(serde_json::Map::new); + let mut keys_request = vec![]; + let mut exps_request = vec![]; + for e in exps { + let k = e.to_key(); + if !map.contains_key(&k) { + keys_request.push(k); + exps_request.push(e.to_exp()); + } + } + let values_request: Vec = if keys_request.is_empty() { + vec![] + } else { + info!( + "Some arguments are not available. Requesting from vim. Keys: {:?}. Exps: {:?}", + keys_request, exps_request, + ); + self.eval::<&[_], _>(exps_request.as_ref())? + }; + for (k, v) in keys_request.into_iter().zip(values_request.into_iter()) { + map.insert(k, v); + } + + let mut result = vec![]; + for e in exps { + let k = e.to_key(); + result.push( + map.remove(&k) + .ok_or_else(|| format_err!("Failed to get value! k: {}", k))?, + ); + } + + info!("gather_args: {:?} = {:?}", exps, result); + Ok(serde_json::from_value(Value::Array(result))?) + } + + fn sync_settings(&mut self) -> Result<()> { + let (loggingFile, loggingLevel, serverStderr): ( + Option, + log::LevelFilter, + Option, + ) = self.eval( + [ + "get(g:, 'LanguageClient_loggingFile', v:null)", + "get(g:, 'LanguageClient_loggingLevel', 'WARN')", + "get(g:, 'LanguageClient_serverStderr', v:null)", + ] + .as_ref(), + )?; + logger::update_settings(&self.logger, &loggingFile, loggingLevel)?; + + #[allow(unknown_lints)] + #[allow(type_complexity)] + let ( + autoStart, + serverCommands, + selectionUI, + trace, + settingsPath, + loadSettings, + rootMarkers, + change_throttle, + wait_output_timeout, + diagnosticsEnable, + diagnosticsList, + diagnosticsDisplay, + windowLogMessageLevel, + hoverPreview, + completionPreferTextEdit, + is_nvim, + ): ( + u64, + HashMap>, + Option, + Option, + String, + u64, + Option, + Option, + Option, + u64, + Option, + Value, + String, + Option, + u64, + u64, + ) = self.eval( + [ + "!!get(g:, 'LanguageClient_autoStart', 1)", + "get(g:, 'LanguageClient_serverCommands', {})", + "get(g:, 'LanguageClient_selectionUI', v:null)", + "get(g:, 'LanguageClient_trace', v:null)", + "expand(get(g:, 'LanguageClient_settingsPath', '.vim/settings.json'))", + "!!get(g:, 'LanguageClient_loadSettings', 1)", + "get(g:, 'LanguageClient_rootMarkers', v:null)", + "get(g:, 'LanguageClient_changeThrottle', v:null)", + "get(g:, 'LanguageClient_waitOutputTimeout', v:null)", + "!!get(g:, 'LanguageClient_diagnosticsEnable', 1)", + "get(g:, 'LanguageClient_diagnosticsList', 'Quickfix')", + "get(g:, 'LanguageClient_diagnosticsDisplay', {})", + "get(g:, 'LanguageClient_windowLogMessageLevel', 'Warning')", + "get(g:, 'LanguageClient_hoverPreview', 'Auto')", + "get(g:, 'LanguageClient_completionPreferTextEdit', 0)", + "has('nvim')", + ] + .as_ref(), + )?; + + let (diagnosticsSignsMax, documentHighlightDisplay): (Option, Value) = self.eval( + [ + "get(g:, 'LanguageClient_diagnosticsSignsMax', v:null)", + "get(g:, 'LanguageClient_documentHighlightDisplay', {})", + ] + .as_ref(), + )?; + + // vimscript use 1 for true, 0 for false. + let autoStart = autoStart == 1; + let loadSettings = loadSettings == 1; + + let trace = if let Some(t) = trace { + match t.to_ascii_uppercase().as_str() { + "OFF" => Some(TraceOption::Off), + "MESSAGES" => Some(TraceOption::Messages), + "VERBOSE" => Some(TraceOption::Verbose), + _ => bail!("Invalid option for LanguageClient_trace: {}", t), + } + } else { + Some(TraceOption::default()) + }; + + let selectionUI = if let Some(s) = selectionUI { + SelectionUI::from_str(&s)? + } else if self.eval::<_, i64>("get(g:, 'loaded_fzf')")? == 1 { + SelectionUI::FZF + } else { + SelectionUI::default() + }; + + let change_throttle = change_throttle.map(|t| Duration::from_millis((t * 1000.0) as u64)); + let wait_output_timeout = + Duration::from_millis((wait_output_timeout.unwrap_or(10.0) * 1000.0) as u64); + + let diagnosticsEnable = diagnosticsEnable == 1; + + let diagnosticsList = if let Some(s) = diagnosticsList { + DiagnosticsList::from_str(&s)? + } else { + DiagnosticsList::Disabled + }; + + let windowLogMessageLevel = match windowLogMessageLevel.to_ascii_uppercase().as_str() { + "ERROR" => MessageType::Error, + "WARNING" => MessageType::Warning, + "INFO" => MessageType::Info, + "LOG" => MessageType::Log, + _ => bail!( + "Invalid option for LanguageClient_windowLogMessageLevel: {}", + windowLogMessageLevel + ), + }; + + let hoverPreview = if let Some(s) = hoverPreview { + HoverPreviewOption::from_str(&s)? + } else { + HoverPreviewOption::Auto + }; + + let completionPreferTextEdit = completionPreferTextEdit == 1; + + let is_nvim = is_nvim == 1; + + self.update(|state| { + state.autoStart = autoStart; + state.serverCommands.extend(serverCommands); + state.selectionUI = selectionUI; + state.trace = trace; + state.diagnosticsEnable = diagnosticsEnable; + state.diagnosticsList = diagnosticsList; + state.diagnosticsDisplay = serde_json::from_value( + serde_json::to_value(&state.diagnosticsDisplay)?.combine(&diagnosticsDisplay), + )?; + state.diagnosticsSignsMax = diagnosticsSignsMax; + state.documentHighlightDisplay = serde_json::from_value( + serde_json::to_value(&state.documentHighlightDisplay)? + .combine(&documentHighlightDisplay), + )?; + state.windowLogMessageLevel = windowLogMessageLevel; + state.settingsPath = settingsPath; + state.loadSettings = loadSettings; + state.rootMarkers = rootMarkers; + state.change_throttle = change_throttle; + state.wait_output_timeout = wait_output_timeout; + state.hoverPreview = hoverPreview; + state.completionPreferTextEdit = completionPreferTextEdit; + state.loggingFile = loggingFile; + state.loggingLevel = loggingLevel; + state.serverStderr = serverStderr; + state.is_nvim = is_nvim; + Ok(()) + })?; + + Ok(()) + } + + fn get_workspace_settings(&self, root: &str) -> Result { + if !self.loadSettings { + return Ok(Value::Null); + } + + let path = Path::new(root).join(self.settingsPath.clone()); + let buffer = read_to_string(&path).with_context(|err| { + format!("Failed to read file ({}): {}", path.to_string_lossy(), err) + })?; + let value = serde_json::from_str(&buffer)?; + let value = expand_json_path(value); + Ok(value) + } + + fn define_signs(&mut self) -> Result<()> { + info!("Defining signs"); + + let mut cmds = vec![]; + for entry in self.diagnosticsDisplay.values() { + cmds.push(format!( + "sign define LanguageClient{} text={} texthl={}", + entry.name, entry.signText, entry.signTexthl, + )); + } + + self.command(cmds)?; + Ok(()) + } + + fn apply_WorkspaceEdit(&mut self, edit: &WorkspaceEdit, params: &Value) -> Result<()> { + debug!( + "Begin apply WorkspaceEdit: {:?}. Params: {:?}", + edit, params + ); + let (filename, line, character): (String, u64, u64) = + self.gather_args(&[VimVar::Filename, VimVar::Line, VimVar::Character], params)?; + + if let Some(ref changes) = edit.document_changes { + match changes { + DocumentChanges::Edits(ref changes) => { + for e in changes { + self.apply_TextEdits(&e.text_document.uri.filepath()?, &e.edits)?; + } + } + DocumentChanges::Operations(ref ops) => { + for op in ops { + if let DocumentChangeOperation::Edit(ref e) = op { + self.apply_TextEdits(&e.text_document.uri.filepath()?, &e.edits)?; + } + // TODO: handle ResourceOp. + } + } + } + } + if let Some(ref changes) = edit.changes { + for (uri, edits) in changes { + self.apply_TextEdits(&uri.filepath()?, edits)?; + } + } + self.edit(&None, &filename)?; + self.cursor(line + 1, character + 1)?; + debug!("End apply WorkspaceEdit"); + Ok(()) + } + + pub fn textDocument_documentHighlight(&mut self, params: &Value) -> Result { + self.textDocument_didChange(params)?; + info!("Begin {}", lsp::request::DocumentHighlightRequest::METHOD); + let (languageId, filename, line, character, handle): ( + String, + String, + u64, + u64, + bool, + ) = self.gather_args( + &[ + VimVar::LanguageId, + VimVar::Filename, + VimVar::Line, + VimVar::Character, + VimVar::Handle, + ], + params, + )?; + + let result = self.call( + Some(&languageId), + lsp::request::DocumentHighlightRequest::METHOD, + TextDocumentPositionParams { + text_document: TextDocumentIdentifier { + uri: filename.to_url()?, + }, + position: Position { line, character }, + }, + )?; + + if !handle { + return Ok(result); + } + + let document_highlight: Option> = + serde_json::from_value(result.clone())?; + if let Some(document_highlight) = document_highlight { + let highlights = document_highlight + .into_iter() + .map(|DocumentHighlight { range, kind }| { + Ok(Highlight { + line: range.start.line, + character_start: range.start.character, + character_end: range.end.character, + group: self + .documentHighlightDisplay + .get( + &kind + .unwrap_or(DocumentHighlightKind::Text) + .to_int() + .unwrap(), + ).ok_or_else(|| err_msg("Failed to get display"))? + .texthl + .clone(), + text: String::new(), + }) + }).collect::>>()?; + + let buffer = self.call(None, "nvim_win_get_buf", json!([0]))?; + + let source = if let Some(hs) = self.document_highlight_source { + if hs.buffer == buffer { + // If we want to highlight in the same buffer as last time, we can reuse + // the previous source. + Some(hs.source) + } else { + // Clear the highlight in the previous buffer. + self.notify( + None, + "nvim_buf_clear_highlight", + json!([hs.buffer, hs.source, 0, -1]), + )?; + + None + } + } else { + None + }; + + let source = match source { + Some(source) => source, + None => { + // Create a new source. + let source = self.call( + None, + "nvim_buf_add_highlight", + json!([buffer, 0, "Error", 1, 1, 1]), + )?; + self.document_highlight_source = Some(HighlightSource { buffer, source }); + source + } + }; + + self.notify( + None, + "nvim_buf_clear_highlight", + json!([buffer, source, 0, -1]), + )?; + self.notify(None, "s:AddHighlights", json!([source, highlights]))?; + } + + info!("End {}", lsp::request::DocumentHighlightRequest::METHOD); + Ok(result) + } + + pub fn languageClient_clearDocumentHighlight(&mut self, _: &Value) -> Result<()> { + info!("Begin {}", NOTIFICATION__ClearDocumentHighlight); + + if let Some(HighlightSource { buffer, source }) = self.document_highlight_source.take() { + self.notify( + None, + "nvim_buf_clear_highlight", + json!([buffer, source, 0, -1]), + )?; + } + + info!("End {}", NOTIFICATION__ClearDocumentHighlight); + Ok(()) + } + + fn apply_TextEdits>(&mut self, path: P, edits: &[TextEdit]) -> Result<()> { + debug!("Begin apply TextEdits: {:?}", edits); + if edits.is_empty() { + return Ok(()); + } + + let mut edits = edits.to_vec(); + + // Edits must be applied from bottom to top, so that earlier edits will not interfere + // with the positioning of later edits. Edits that start with the same position must be + // applied in reverse order, so that multiple inserts will have their text appear in the + // same order the server sent it, and so that a delete/replace (according to the LSP spec, + // there can only be one per start position and it must be after the inserts) will work on + // the original document, not on the just-inserted text. + edits.sort_by_key(|edit| (edit.range.start.line, edit.range.start.character)); + edits.reverse(); + + self.edit(&None, &path)?; + + let mut lines: Vec = self.call(None, "getline", json!([1, '$']))?; + let lines_len_prev = lines.len(); + let fixendofline = self.eval::<_, u8>("&fixendofline")? == 1; + if lines.last().map(String::is_empty) == Some(false) && fixendofline { + lines.push("".to_owned()); + } + + let mut lines = apply_TextEdits(&lines, &edits)?; + + if lines.last().map(String::is_empty) == Some(true) && fixendofline { + lines.pop(); + } + if lines.len() < lines_len_prev { + self.command(format!("{},{}d", lines.len() + 1, lines_len_prev))?; + } + if self.call::<_, i64>(None, "setline", json!([1, lines]))? != 0 { + bail!("Failed to set buffer content!"); + } + debug!("End apply TextEdits"); + Ok(()) + } + + fn update_quickfixlist(&mut self) -> Result<()> { + let qflist: Vec<_> = self + .diagnostics + .iter() + .flat_map(|(filename, diagnostics)| { + diagnostics + .iter() + .map(|dn| QuickfixEntry { + filename: filename.to_owned(), + lnum: dn.range.start.line + 1, + col: Some(dn.range.start.character + 1), + nr: dn.code.clone().map(|ns| ns.to_string()), + text: Some(dn.message.to_owned()), + typ: dn.severity.map(|sev| sev.to_quickfix_entry_type()), + }).collect::>() + }).collect(); + + match self.diagnosticsList { + DiagnosticsList::Quickfix => { + self.setqflist(&qflist)?; + } + DiagnosticsList::Location => { + self.setloclist(&qflist)?; + } + DiagnosticsList::Disabled => {} + } + + Ok(()) + } + + fn process_diagnostics(&mut self, filename: &str, diagnostics: &[Diagnostic]) -> Result<()> { + if !self.text_documents.contains_key(filename) { + return Ok(()); + } + + let lines: Vec<_> = self + .text_documents + .get(filename) + .map(|d| d.text.lines().map(ToOwned::to_owned).collect()) + .unwrap_or_default(); + + // Line diagnostics. + let mut line_diagnostics = HashMap::new(); + for entry in diagnostics { + let line = entry.range.start.line; + let mut msg = String::new(); + if let Some(severity) = entry.severity { + msg += &format!("[{:?}]", severity); + } + if let Some(ref code) = entry.code { + let s = code.to_string(); + if !s.is_empty() { + msg += &format!("[{}]", s); + } + } + msg += &entry.message; + line_diagnostics.insert((filename.to_owned(), line), msg); + } + self.line_diagnostics.retain(|&(ref f, _), _| f != filename); + self.line_diagnostics.extend(line_diagnostics); + + // Signs. + let mut signs: Vec<_> = diagnostics + .iter() + .map(|dn| { + let line = dn.range.start.line; + let text = lines + .get(line as usize) + .map(|l| l.to_string()) + .unwrap_or_default(); + + Sign::new(line + 1, text, dn.severity) + }).collect(); + + // There might be multiple diagnostics for one line. Show only highest severity. + signs.sort_unstable(); + signs.dedup(); + if let Some(diagnosticSignsMax) = self.diagnosticsSignsMax { + signs.truncate(diagnosticSignsMax as usize); + } + + self.signs.insert(filename.to_owned(), signs); + + // Highlight. + let diagnosticsDisplay = self.diagnosticsDisplay.clone(); + + let mut highlights = vec![]; + for dn in diagnostics { + let line = dn.range.start.line; + let character_start = dn.range.start.character; + let character_end = dn.range.end.character; + + let severity = dn.severity.unwrap_or(DiagnosticSeverity::Hint); + let group = diagnosticsDisplay + .get(&severity.to_int()?) + .ok_or_else(|| err_msg("Failed to get display"))? + .texthl + .clone(); + // TODO: handle multi-line range. + let text = lines + .get(line as usize) + .and_then(|l| l.get((character_start as usize)..(character_end as usize))) + .map(ToOwned::to_owned) + .unwrap_or_default(); + + highlights.push(Highlight { + line, + character_start, + character_end, + group, + text, + }); + } + // dedup? + self.highlights.insert(filename.to_owned(), highlights); + + if !self.is_nvim { + // Clear old highlights. + let ids = self.highlight_match_ids.clone(); + self.notify(None, "s:MatchDelete", json!([ids]))?; + + // Group diagnostics by severity so we can highlight them + // in a single call. + let mut match_groups: HashMap<_, Vec<_>> = HashMap::new(); + + for dn in diagnostics { + let severity = dn + .severity + .unwrap_or(DiagnosticSeverity::Information) + .to_int()?; + match_groups + .entry(severity) + .or_insert_with(Vec::new) + .push(dn); + } + + let mut new_match_ids = Vec::new(); + + for (severity, dns) in match_groups { + let hl_group = diagnosticsDisplay + .get(&severity) + .ok_or_else(|| err_msg("Failed to get display"))? + .texthl + .clone(); + let ranges: Vec> = dns + .iter() + .flat_map(|dn| { + if dn.range.start.line == dn.range.end.line { + let length = dn.range.end.character - dn.range.start.character; + // Vim line numbers are 1 off + // `matchaddpos` expects an array of [line, col, length] + // for each match. + vec![vec![ + dn.range.start.line + 1, + dn.range.start.character + 1, + length, + ]] + } else { + let mut middleLines: Vec<_> = (dn.range.start.line + 1 + ..dn.range.end.line) + .map(|l| vec![l + 1]) + .collect(); + let startLine = vec![ + dn.range.start.line + 1, + dn.range.start.character + 1, + 999_999, //Clear to the end of the line + ]; + let endLine = + vec![dn.range.end.line + 1, 1, dn.range.end.character + 1]; + middleLines.push(startLine); + middleLines.push(endLine); + middleLines + } + }).collect(); + + let match_id = self.call(None, "matchaddpos", json!([hl_group, ranges]))?; + new_match_ids.push(match_id); + } + self.highlight_match_ids = new_match_ids; + } + + Ok(()) + } + + fn display_locations(&mut self, locations: &[Location]) -> Result<()> { + let location_to_quickfix_entry = + |state: &mut Self, loc: &Location| -> Result { + let filename = loc.uri.filepath()?.to_string_lossy().into_owned(); + let start = loc.range.start; + let text = state.get_line(&filename, start.line).unwrap_or_default(); + + Ok(QuickfixEntry { + filename, + lnum: start.line + 1, + col: Some(start.character + 1), + text: Some(text), + nr: None, + typ: None, + }) + }; + + match self.get(|state| Ok(state.selectionUI.clone()))? { + SelectionUI::FZF => { + let cwd: String = self.eval("getcwd()")?; + let source: Result> = locations + .iter() + .map(|loc| { + let filename = loc.uri.filepath()?; + let start = loc.range.start; + let text = self.get_line(&filename, start.line).unwrap_or_default(); + let relpath = diff_paths(&filename, Path::new(&cwd)).unwrap_or(filename); + Ok(format!( + "{}:{}:{}:\t{}", + relpath.to_string_lossy(), + start.line + 1, + start.character + 1, + text + )) + }).collect(); + let source = source?; + + self.call::<_, u8>( + None, + "s:FZF", + json!([source, format!("s:{}", NOTIFICATION__FZFSinkLocation)]), + )?; + } + SelectionUI::Quickfix => { + let list: Result> = locations + .iter() + .map(|loc| location_to_quickfix_entry(self, loc)) + .collect(); + let list = list?; + self.setqflist(&list)?; + self.echo("Quickfix list updated.")?; + } + SelectionUI::LocationList => { + let list: Result> = locations + .iter() + .map(|loc| location_to_quickfix_entry(self, loc)) + .collect(); + let list = list?; + self.setloclist(&list)?; + self.echo("Location list updated.")?; + } + } + Ok(()) + } + + fn registerCMSource(&mut self, languageId: &str, result: &Value) -> Result<()> { + info!("Begin register NCM source"); + let exists_CMRegister: u64 = self.eval("exists('g:cm_matcher')")?; + if exists_CMRegister == 0 { + return Ok(()); + } + + let result: InitializeResult = serde_json::from_value(result.clone())?; + if result.capabilities.completion_provider.is_none() { + return Ok(()); + } + + let trigger_patterns = result + .capabilities + .completion_provider + .map(|opt| { + let strings: Vec<_> = opt + .trigger_characters + .unwrap_or_default() + .iter() + .map(|c| regex::escape(c)) + .collect(); + strings + }).unwrap_or_default(); + + self.call::<_, u8>( + None, + "cm#register_source", + json!([{ + "name": format!("LanguageClient_{}", languageId), + "priority": 9, + "scopes": [languageId], + "cm_refresh_patterns": trigger_patterns, + "abbreviation": "LC", + "cm_refresh": REQUEST__NCMRefresh, + }]), + )?; + info!("End register NCM source"); + Ok(()) + } + + fn registerNCM2Source(&mut self, languageId: &str, result: &Value) -> Result<()> { + info!("Begin register NCM2 source"); + let exists_ncm2: u64 = self.eval("exists('g:ncm2_loaded')")?; + if exists_ncm2 == 0 { + return Ok(()); + } + + let result: InitializeResult = serde_json::from_value(result.clone())?; + if result.capabilities.completion_provider.is_none() { + return Ok(()); + } + + let trigger_patterns = result + .capabilities + .completion_provider + .map(|opt| { + let strings: Vec<_> = opt + .trigger_characters + .unwrap_or_default() + .iter() + .map(|c| regex::escape(c)) + .collect(); + strings + }).unwrap_or_default(); + + self.call::<_, u8>( + None, + "ncm2#register_source", + json!([{ + "name": format!("LanguageClient_{}", languageId), + "priority": 9, + "scope": [languageId], + "complete_pattern": trigger_patterns, + "mark": "LC", + "on_complete": REQUEST__NCM2OnComplete, + }]), + )?; + info!("End register NCM2 source"); + Ok(()) + } + + fn get_line>(&mut self, path: P, line: u64) -> Result { + let value = self.call( + None, + "getbufline", + json!([path.as_ref().to_string_lossy(), line + 1]), + )?; + let mut texts: Vec = serde_json::from_value(value)?; + let mut text = texts.pop().unwrap_or_default(); + + if text.is_empty() { + let reader = BufReader::new(File::open(path)?); + text = reader + .lines() + .nth(line.to_usize()?) + .ok_or_else(|| format_err!("Failed to get line! line: {}", line))??; + } + + Ok(text.trim().into()) + } + + fn try_handle_command_by_client(&mut self, cmd: &Command) -> Result { + if !CommandsClient.contains(&cmd.command.as_str()) { + return Ok(false); + } + + if cmd.command == "java.apply.workspaceEdit" { + if let Some(ref edits) = cmd.arguments { + for edit in edits { + let edit: WorkspaceEdit = serde_json::from_value(edit.clone())?; + self.apply_WorkspaceEdit(&edit, &Value::Null)?; + } + } + } else { + bail!("Not implemented: {}", cmd.command); + } + + Ok(true) + } + + fn cleanup(&mut self, languageId: &str) -> Result<()> { + info!("Begin cleanup"); + + let root = self + .roots + .get(languageId) + .cloned() + .ok_or_else(|| format_err!("No project root found! languageId: {}", languageId))?; + + let mut filenames = vec![]; + for f in self.diagnostics.keys() { + if f.starts_with(&root) { + filenames.push(f.clone()); + } + } + for f in filenames { + self.process_diagnostics(&f, &[])?; + } + self.languageClient_handleCursorMoved(&Value::Null)?; + + self.diagnostics.retain(|f, _| !f.starts_with(&root)); + self.update_quickfixlist()?; + + self.writers.remove(languageId); + self.child_ids.remove(languageId); + self.last_cursor_line = 0; + self.text_documents.retain(|f, _| !f.starts_with(&root)); + self.roots.remove(languageId); + + self.command(vec![ + format!("let {}=0", VIM__ServerStatus), + format!("let {}=''", VIM__ServerStatusMessage), + ])?; + self.notify(None, "s:ExecuteAutocmd", "LanguageClientStopped")?; + + info!("End cleanup"); + Ok(()) + } + + fn preview(&mut self, lines: &[S]) -> Result<()> + where + S: AsRef + Serialize, + { + let bufname = "__LanguageClient__"; + + let mut cmd = String::new(); + cmd += "silent! pedit! +setlocal\\ buftype=nofile\\ filetype=markdown\\ nobuflisted\\ noswapfile\\ nonumber "; + cmd += bufname; + self.command(cmd)?; + + if self.get(|state| Ok(state.is_nvim))? { + let bufnr: u64 = serde_json::from_value(self.call(None, "bufnr", bufname)?)?; + self.call::<_, Option>( + None, + "nvim_buf_set_lines", + json!([bufnr, 0, -1, 0, lines]), + )?; + } else if self.call::<_, i64>(None, "setbufline", json!([bufname, 1, lines]))? != 0 { + bail!("Failed to set preview buffer content!"); + // TODO: removing existing bottom lines. + } + + Ok(()) + } + + /////// LSP /////// + + fn initialize(&mut self, params: &Value) -> Result { + info!("Begin {}", lsp::request::Initialize::METHOD); + let (languageId, filename): (String, String) = + self.gather_args(&[VimVar::LanguageId, VimVar::Filename], params)?; + let (rootPath, has_snippet_support): (Option, u64) = self.gather_args( + &[ + ("rootPath", "v:null"), + ("hasSnippetSupport", "s:hasSnippetSupport()"), + ], + params, + )?; + let root = if let Some(r) = rootPath { + r + } else { + let rootMarkers = self.get(|state| Ok(state.rootMarkers.clone()))?; + let root = get_rootPath(Path::new(&filename), &languageId, &rootMarkers)? + .to_string_lossy() + .into_owned(); + self.echomsg_ellipsis(format!("LanguageClient project root: {}", root))?; + root + }; + info!("Project root: {}", root); + let has_snippet_support = has_snippet_support > 0; + self.update(|state| Ok(state.roots.insert(languageId.clone(), root.clone())))?; + + let initialization_options = self + .get_workspace_settings(&root) + .map(|s| s["initializationOptions"].clone()) + .unwrap_or_else(|err| { + warn!("Failed to get initializationOptions: {}", err); + json!(Value::Null) + }); + let initialization_options = + get_default_initializationOptions(&languageId).combine(&initialization_options); + let initialization_options = if initialization_options.is_null() { + None + } else { + Some(initialization_options) + }; + + let trace = self.trace.clone(); + + let result: Value = self.call( + Some(&languageId), + lsp::request::Initialize::METHOD, + InitializeParams { + process_id: Some(u64::from(std::process::id())), + root_path: Some(root.clone()), + root_uri: Some(root.to_url()?), + initialization_options, + capabilities: ClientCapabilities { + text_document: Some(TextDocumentClientCapabilities { + completion: Some(CompletionCapability { + completion_item: Some(CompletionItemCapability { + snippet_support: Some(has_snippet_support), + ..CompletionItemCapability::default() + }), + ..CompletionCapability::default() + }), + ..TextDocumentClientCapabilities::default() + }), + workspace: Some(WorkspaceClientCapabilities { + apply_edit: Some(true), + did_change_watched_files: Some(GenericCapability { + dynamic_registration: Some(true), + }), + ..WorkspaceClientCapabilities::default() + }), + ..ClientCapabilities::default() + }, + trace, + workspace_folders: None, + }, + )?; + + self.update(|state| { + state + .capabilities + .insert(languageId.clone(), result.clone()); + Ok(()) + })?; + + info!("End {}", lsp::request::Initialize::METHOD); + + if let Err(e) = self.registerCMSource(&languageId, &result) { + let message = format!("LanguageClient: failed to register as NCM source: {}", e); + error!("{}\n{:?}", message, e); + self.echoerr(message)?; + } + if let Err(e) = self.registerNCM2Source(&languageId, &result) { + let message = format!("LanguageClient: failed to register as NCM source: {}", e); + error!("{}\n{:?}", message, e); + self.echoerr(message)?; + } + + Ok(result) + } + + fn initialized(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", lsp::notification::Initialized::METHOD); + let (languageId,): (String,) = self.gather_args(&[VimVar::LanguageId], params)?; + self.notify( + Some(&languageId), + lsp::notification::Initialized::METHOD, + InitializedParams {}, + )?; + info!("End {}", lsp::notification::Initialized::METHOD); + Ok(()) + } + + pub fn textDocument_hover(&mut self, params: &Value) -> Result { + self.textDocument_didChange(params)?; + info!("Begin {}", lsp::request::HoverRequest::METHOD); + let (languageId, filename, line, character, handle): ( + String, + String, + u64, + u64, + bool, + ) = self.gather_args( + &[ + VimVar::LanguageId, + VimVar::Filename, + VimVar::Line, + VimVar::Character, + VimVar::Handle, + ], + params, + )?; + + let result = self.call( + Some(&languageId), + lsp::request::HoverRequest::METHOD, + TextDocumentPositionParams { + text_document: TextDocumentIdentifier { + uri: filename.to_url()?, + }, + position: Position { line, character }, + }, + )?; + + if !handle { + return Ok(result); + } + + let hover: Option = serde_json::from_value(result.clone())?; + if let Some(hover) = hover { + let use_preview = match &self.hoverPreview { + HoverPreviewOption::Always => true, + HoverPreviewOption::Never => false, + HoverPreviewOption::Auto => hover.lines_len() > 1, + }; + if use_preview { + self.preview(&hover.to_display())? + } else { + self.echo_ellipsis(hover.to_string())? + } + } + + info!("End {}", lsp::request::HoverRequest::METHOD); + Ok(result) + } + + /// Generic find locations, e.g, definitions, references. + pub fn find_locations(&mut self, params: &Value) -> Result { + self.textDocument_didChange(params)?; + let (method,): (String,) = self.gather_args(&["method"], params)?; + info!("Begin {}", method); + let (languageId, filename, line, character, handle, goto_cmd): ( + String, + String, + u64, + u64, + bool, + Option, + ) = self.gather_args( + &[ + VimVar::LanguageId, + VimVar::Filename, + VimVar::Line, + VimVar::Character, + VimVar::Handle, + VimVar::GotoCmd, + ], + params, + )?; + + let params = serde_json::to_value(TextDocumentPositionParams { + text_document: TextDocumentIdentifier { + uri: filename.to_url()?, + }, + position: Position { line, character }, + })?.combine(params); + + let result = self.call(Some(&languageId), &method, ¶ms)?; + + if !handle { + return Ok(result); + } + + let response: Option = result.clone().to_lsp()?; + + match response { + None => { + self.echowarn("Not found!")?; + return Ok(Value::Null); + } + Some(GotoDefinitionResponse::Scalar(loc)) => { + self.edit(&goto_cmd, loc.uri.filepath()?)?; + self.cursor(loc.range.start.line + 1, loc.range.start.character + 1)?; + } + Some(GotoDefinitionResponse::Array(arr)) => match arr.len() { + 0 => self.echowarn("Not found!")?, + 1 => { + let loc = arr.get(0).ok_or_else(|| err_msg("Not found!"))?; + self.edit(&goto_cmd, loc.uri.filepath()?)?; + self.cursor(loc.range.start.line + 1, loc.range.start.character + 1)?; + let cur_file: String = self.eval("expand('%')")?; + self.echomsg_ellipsis(format!( + "[LC]: {} {}:{}", + cur_file, + loc.range.start.line + 1, + loc.range.start.character + 1 + ))?; + } + _ => self.display_locations(&arr)?, + }, + }; + + info!("End {}", method); + Ok(result) + } + + pub fn textDocument_rename(&mut self, params: &Value) -> Result { + self.textDocument_didChange(params)?; + info!("Begin {}", lsp::request::Rename::METHOD); + let (buftype, languageId, filename, line, character, cword, new_name, handle): ( + String, + String, + String, + u64, + u64, + String, + Option, + bool, + ) = self.gather_args( + &[ + VimVar::Buftype, + VimVar::LanguageId, + VimVar::Filename, + VimVar::Line, + VimVar::Character, + VimVar::Cword, + VimVar::NewName, + VimVar::Handle, + ], + params, + )?; + if !buftype.is_empty() || languageId.is_empty() { + return Ok(Value::Null); + } + + let mut new_name = new_name.unwrap_or_default(); + if new_name.is_empty() { + let value = self.call(None, "s:getInput", ["Rename to: ".to_owned(), cword])?; + new_name = serde_json::from_value(value)?; + } + if new_name.is_empty() { + return Ok(Value::Null); + } + + let result = self.call( + Some(&languageId), + lsp::request::Rename::METHOD, + RenameParams { + text_document: TextDocumentIdentifier { + uri: filename.to_url()?, + }, + position: Position { line, character }, + new_name, + }, + )?; + + if !handle || result == Value::Null { + return Ok(result); + } + + let edit: WorkspaceEdit = serde_json::from_value(result.clone())?; + self.apply_WorkspaceEdit(&edit, params)?; + + info!("End {}", lsp::request::Rename::METHOD); + Ok(result) + } + + pub fn textDocument_documentSymbol(&mut self, params: &Value) -> Result { + self.textDocument_didChange(params)?; + info!("Begin {}", lsp::request::DocumentSymbolRequest::METHOD); + + let (buftype, languageId, filename, handle): (String, String, String, bool) = self + .gather_args( + &[ + VimVar::Buftype, + VimVar::LanguageId, + VimVar::Filename, + VimVar::Handle, + ], + params, + )?; + + if !buftype.is_empty() { + return Ok(Value::Null); + } + + let result = self.call( + Some(&languageId), + lsp::request::DocumentSymbolRequest::METHOD, + DocumentSymbolParams { + text_document: TextDocumentIdentifier { + uri: filename.to_url()?, + }, + }, + )?; + + if !handle { + return Ok(result); + } + + let symbols: Vec = serde_json::from_value(result.clone())?; + + match self.get(|state| Ok(state.selectionUI.clone()))? { + SelectionUI::FZF => { + let source: Vec<_> = symbols + .iter() + .map(|sym| { + let start = sym.location.range.start; + format!( + "{}:{}:\t{}\t\t{:?}", + start.line + 1, + start.character + 1, + sym.name, + sym.kind + ) + }).collect(); + + self.call::<_, u8>( + None, + "s:FZF", + json!([source, format!("s:{}", NOTIFICATION__FZFSinkLocation)]), + )?; + } + SelectionUI::Quickfix => { + let list: Result> = symbols.iter().map(QuickfixEntry::from_lsp).collect(); + let list = list?; + self.setqflist(&list)?; + self.echo("Document symbols populated to quickfix list.")?; + } + SelectionUI::LocationList => { + let list: Result> = symbols.iter().map(QuickfixEntry::from_lsp).collect(); + let list = list?; + self.setloclist(&list)?; + self.echo("Document symbols populated to location list.")?; + } + } + + info!("End {}", lsp::request::DocumentSymbolRequest::METHOD); + Ok(result) + } + + pub fn textDocument_codeAction(&mut self, params: &Value) -> Result { + self.textDocument_didChange(params)?; + info!("Begin {}", lsp::request::CodeActionRequest::METHOD); + let (buftype, languageId, filename, line, character, handle): ( + String, + String, + String, + u64, + u64, + bool, + ) = self.gather_args( + &[ + VimVar::Buftype, + VimVar::LanguageId, + VimVar::Filename, + VimVar::Line, + VimVar::Character, + VimVar::Handle, + ], + params, + )?; + if !buftype.is_empty() || languageId.is_empty() { + return Ok(Value::Null); + } + + // Unify filename. + let filename = filename.canonicalize(); + + let diagnostics: Vec<_> = self + .diagnostics + .get(&filename) + .unwrap_or(&vec![]) + .iter() + .filter(|dn| { + let start = dn.range.start; + let end = dn.range.end; + (line, character) >= (start.line, start.character) + && (line, character) < (end.line, end.character) + }).cloned() + .collect(); + let result: Value = self.call( + Some(&languageId), + lsp::request::CodeActionRequest::METHOD, + CodeActionParams { + text_document: TextDocumentIdentifier { + uri: filename.to_url()?, + }, + range: Range { + start: Position { line, character }, + end: Position { line, character }, + }, + context: CodeActionContext { + diagnostics, + only: None, + }, + }, + )?; + + let commands: Vec = serde_json::from_value(result.clone())?; + + let source: Vec<_> = commands + .iter() + .map(|cmd| format!("{}: {}", cmd.command, cmd.title)) + .collect(); + + self.update(|state| { + state.stashed_codeAction_commands = commands; + Ok(()) + })?; + + if !handle { + return Ok(result); + } + + self.call::<_, u8>(None, "s:FZF", json!([source, NOTIFICATION__FZFSinkCommand]))?; + + info!("End {}", lsp::request::CodeActionRequest::METHOD); + Ok(result) + } + + pub fn textDocument_completion(&mut self, params: &Value) -> Result { + self.textDocument_didChange(params)?; + info!("Begin {}", lsp::request::Completion::METHOD); + + let (buftype, languageId, filename, line, character, handle): ( + String, + String, + String, + u64, + u64, + bool, + ) = self.gather_args( + &[ + VimVar::Buftype, + VimVar::LanguageId, + VimVar::Filename, + VimVar::Line, + VimVar::Character, + VimVar::Handle, + ], + params, + )?; + if !buftype.is_empty() || languageId.is_empty() { + return Ok(Value::Null); + } + + let result = self.call( + Some(&languageId), + lsp::request::Completion::METHOD, + TextDocumentPositionParams { + text_document: TextDocumentIdentifier { + uri: filename.to_url()?, + }, + position: Position { line, character }, + }, + )?; + + if !handle { + return Ok(result); + } + + info!("End {}", lsp::request::Completion::METHOD); + Ok(result) + } + + pub fn textDocument_signatureHelp(&mut self, params: &Value) -> Result { + self.textDocument_didChange(params)?; + info!("Begin {}", lsp::request::SignatureHelpRequest::METHOD); + let (buftype, languageId, filename, line, character, handle): ( + String, + String, + String, + u64, + u64, + bool, + ) = self.gather_args( + &[ + VimVar::Buftype, + VimVar::LanguageId, + VimVar::Filename, + VimVar::Line, + VimVar::Character, + VimVar::Handle, + ], + params, + )?; + if !buftype.is_empty() || languageId.is_empty() { + return Ok(Value::Null); + } + + let result = self.call( + Some(&languageId), + lsp::request::SignatureHelpRequest::METHOD, + TextDocumentPositionParams { + text_document: TextDocumentIdentifier { + uri: filename.to_url()?, + }, + position: Position { line, character }, + }, + )?; + + if !handle || result == Value::Null { + return Ok(result); + } + + let help: SignatureHelp = serde_json::from_value(result)?; + if help.signatures.is_empty() { + return Ok(Value::Null); + } + let active_signature = help + .signatures + .get(help.active_signature.unwrap_or(0).to_usize()?) + .ok_or_else(|| err_msg("Failed to get active signature"))?; + let active_parameter: Option<&ParameterInformation>; + if let Some(ref parameters) = active_signature.parameters { + active_parameter = parameters.get(help.active_parameter.unwrap_or(0).to_usize()?); + } else { + active_parameter = None; + } + + if let Some(active_parameter) = active_parameter { + let mut cmd = "echo".to_owned(); + let chunks: Vec<&str> = active_signature + .label + .split(&active_parameter.label) + .collect(); + if chunks.len() == 2 { + let begin = chunks.get(0).cloned().unwrap_or_default(); + let end = chunks.get(1).cloned().unwrap_or_default(); + cmd += &format!( + " | echon '{}' | echohl WarningMsg | echon '{}' | echohl None | echon '{}'", + begin, active_parameter.label, end + ); + } else { + // Active parameter is not part of signature. + cmd += &format!(" | echo '{}'", active_signature.label); + } + self.command(&cmd)?; + } else { + self.echo(&active_signature.label)?; + } + + info!("End {}", lsp::request::SignatureHelpRequest::METHOD); + Ok(Value::Null) + } + + pub fn textDocument_references(&mut self, params: &Value) -> Result { + info!("Begin {}", lsp::request::References::METHOD); + + let (buftype, include_declaration): (String, bool) = + self.gather_args(&[VimVar::Buftype, VimVar::IncludeDeclaration], params)?; + if !buftype.is_empty() { + return Ok(Value::Null); + } + + let params = json!({ + "method": lsp::request::References::METHOD, + "context": ReferenceContext { + include_declaration, + } + }).combine(params); + + let result = self.find_locations(¶ms)?; + + info!("End {}", lsp::request::References::METHOD); + Ok(result) + } + + pub fn textDocument_formatting(&mut self, params: &Value) -> Result { + self.textDocument_didChange(params)?; + info!("Begin {}", lsp::request::Formatting::METHOD); + let (buftype, languageId, filename, handle): (String, String, String, bool) = self + .gather_args( + &[ + VimVar::Buftype, + VimVar::LanguageId, + VimVar::Filename, + VimVar::Handle, + ], + params, + )?; + if !buftype.is_empty() || languageId.is_empty() { + return Ok(Value::Null); + } + + let (tab_size, insert_spaces): (u64, u64) = + self.eval(["shiftwidth()", "&expandtab"].as_ref())?; + let insert_spaces = insert_spaces == 1; + let result = self.call( + Some(&languageId), + lsp::request::Formatting::METHOD, + DocumentFormattingParams { + text_document: TextDocumentIdentifier { + uri: filename.to_url()?, + }, + options: FormattingOptions { + tab_size, + insert_spaces, + properties: HashMap::new(), + }, + }, + )?; + + if !handle { + return Ok(result); + } + + let text_edits: Option> = serde_json::from_value(result.clone())?; + let text_edits = text_edits.unwrap_or_default(); + let edit = lsp::WorkspaceEdit { + changes: Some(hashmap!{filename.to_url()? => text_edits}), + document_changes: None, + }; + self.apply_WorkspaceEdit(&edit, params)?; + info!("End {}", lsp::request::Formatting::METHOD); + Ok(result) + } + + pub fn textDocument_rangeFormatting(&mut self, params: &Value) -> Result { + self.textDocument_didChange(params)?; + info!("Begin {}", lsp::request::RangeFormatting::METHOD); + let (buftype, languageId, filename, handle, start_line, end_line): ( + String, + String, + String, + bool, + u64, + u64, + ) = self.gather_args( + &[ + VimVar::Buftype.to_key().as_str(), + VimVar::LanguageId.to_key().as_str(), + VimVar::Filename.to_key().as_str(), + VimVar::Handle.to_key().as_str(), + "LSP#range_start_line()", + "LSP#range_end_line()", + ], + params, + )?; + if !buftype.is_empty() || languageId.is_empty() { + return Ok(Value::Null); + } + + let (tab_size, insert_spaces): (u64, u64) = + self.eval(["shiftwidth()", "&expandtab"].as_ref())?; + let insert_spaces = insert_spaces == 1; + let result = self.call( + Some(&languageId), + lsp::request::RangeFormatting::METHOD, + DocumentRangeFormattingParams { + text_document: TextDocumentIdentifier { + uri: filename.to_url()?, + }, + options: FormattingOptions { + tab_size, + insert_spaces, + properties: HashMap::new(), + }, + range: Range { + start: Position { + line: start_line, + character: 0, + }, + end: Position { + line: end_line, + character: 0, + }, + }, + }, + )?; + + if !handle { + return Ok(result); + } + + let text_edits: Option> = serde_json::from_value(result.clone())?; + let text_edits = text_edits.unwrap_or_default(); + let edit = lsp::WorkspaceEdit { + changes: Some(hashmap!{filename.to_url()? => text_edits}), + document_changes: None, + }; + self.apply_WorkspaceEdit(&edit, params)?; + info!("End {}", lsp::request::RangeFormatting::METHOD); + Ok(result) + } + + pub fn completionItem_resolve(&mut self, params: &Value) -> Result { + self.textDocument_didChange(params)?; + info!("Begin {}", lsp::request::ResolveCompletionItem::METHOD); + let (buftype, languageId, handle): (String, String, bool) = self.gather_args( + &[VimVar::Buftype, VimVar::LanguageId, VimVar::Handle], + params, + )?; + if !buftype.is_empty() || languageId.is_empty() { + return Ok(Value::Null); + } + let (completion_item,): (CompletionItem,) = + self.gather_args(&["completionItem"], params)?; + + let result = self.call( + Some(&languageId), + lsp::request::ResolveCompletionItem::METHOD, + completion_item, + )?; + + if !handle { + return Ok(result); + } + + // TODO: proper integration. + let msg = format!("comletionItem/resolve result not handled: {:?}", result); + warn!("{}", msg); + self.echowarn(&msg)?; + + info!("End {}", lsp::request::ResolveCompletionItem::METHOD); + Ok(Value::Null) + } + + pub fn workspace_symbol(&mut self, params: &Value) -> Result { + self.textDocument_didChange(params)?; + info!("Begin {}", lsp::request::WorkspaceSymbol::METHOD); + let (buftype, languageId, handle): (String, String, bool) = self.gather_args( + &[VimVar::Buftype, VimVar::LanguageId, VimVar::Handle], + params, + )?; + if !buftype.is_empty() || languageId.is_empty() { + return Ok(Value::Null); + } + + let (query,): (String,) = self.gather_args(&[("query", "")], params)?; + let result = self.call( + Some(&languageId), + lsp::request::WorkspaceSymbol::METHOD, + WorkspaceSymbolParams { query }, + )?; + + if !handle { + return Ok(result); + } + + let symbols: Vec = serde_json::from_value(result.clone())?; + + match self.get(|state| Ok(state.selectionUI.clone()))? { + SelectionUI::FZF => { + let cwd: String = self.eval("getcwd()")?; + let source: Result> = symbols + .iter() + .map(|sym| { + let filename = sym.location.uri.filepath()?; + let relpath = diff_paths(&filename, Path::new(&cwd)).unwrap_or(filename); + let start = sym.location.range.start; + Ok(format!( + "{}:{}:{}:\t{}\t\t{:?}", + relpath.to_string_lossy(), + start.line + 1, + start.character + 1, + sym.name, + sym.kind + )) + }).collect(); + let source = source?; + + self.call::<_, u8>( + None, + "s:FZF", + json!([source, format!("s:{}", NOTIFICATION__FZFSinkLocation)]), + )?; + } + SelectionUI::Quickfix => { + let list: Result> = symbols.iter().map(QuickfixEntry::from_lsp).collect(); + let list = list?; + self.setqflist(&list)?; + self.echo("Workspace symbols populated to quickfix list.")?; + } + SelectionUI::LocationList => { + let list: Result> = symbols.iter().map(QuickfixEntry::from_lsp).collect(); + let list = list?; + self.setloclist(&list)?; + self.echo("Workspace symbols populated to location list.")?; + } + } + + info!("End {}", lsp::request::WorkspaceSymbol::METHOD); + Ok(result) + } + + pub fn workspace_executeCommand(&mut self, params: &Value) -> Result { + info!("Begin {}", lsp::request::ExecuteCommand::METHOD); + let (languageId,): (String,) = self.gather_args(&[VimVar::LanguageId], params)?; + let (command, arguments): (String, Vec) = + self.gather_args(&["command", "arguments"], params)?; + + let result = self.call( + Some(&languageId), + lsp::request::ExecuteCommand::METHOD, + ExecuteCommandParams { command, arguments }, + )?; + info!("End {}", lsp::request::ExecuteCommand::METHOD); + Ok(result) + } + + pub fn workspace_applyEdit(&mut self, params: &Value) -> Result { + info!("Begin {}", lsp::request::ApplyWorkspaceEdit::METHOD); + + let params: ApplyWorkspaceEditParams = params.clone().to_lsp()?; + self.apply_WorkspaceEdit(¶ms.edit, &Value::Null)?; + + info!("End {}", lsp::request::ApplyWorkspaceEdit::METHOD); + + Ok(serde_json::to_value(ApplyWorkspaceEditResponse { + applied: true, + })?) + } + + pub fn workspace_didChangeConfiguration(&mut self, params: &Value) -> Result<()> { + info!( + "Begin {}", + lsp::notification::DidChangeConfiguration::METHOD + ); + let (languageId,): (String,) = self.gather_args(&[VimVar::LanguageId], params)?; + let (settings,): (Value,) = self.gather_args(&["settings"], params)?; + + self.notify( + Some(languageId.as_str()), + lsp::notification::DidChangeConfiguration::METHOD, + DidChangeConfigurationParams { settings }, + )?; + info!("End {}", lsp::notification::DidChangeConfiguration::METHOD); + Ok(()) + } + + pub fn textDocument_didOpen(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", lsp::notification::DidOpenTextDocument::METHOD); + let (buftype, languageId, filename, text): (String, String, String, Vec) = self + .gather_args( + &[ + VimVar::Buftype, + VimVar::LanguageId, + VimVar::Filename, + VimVar::Text, + ], + params, + )?; + + if !buftype.is_empty() || languageId.is_empty() { + return Ok(()); + } + + let text_document = TextDocumentItem { + uri: filename.to_url()?, + language_id: languageId.clone(), + version: 0, + text: text.join("\n"), + }; + + self.update(|state| { + Ok(state + .text_documents + .insert(filename.clone(), text_document.clone())) + })?; + + self.notify( + Some(&languageId), + lsp::notification::DidOpenTextDocument::METHOD, + DidOpenTextDocumentParams { text_document }, + )?; + + self.command("setlocal omnifunc=LanguageClient#complete")?; + let root = self.roots.get(&languageId).cloned().unwrap_or_default(); + self.notify( + None, + "setbufvar", + json!([filename, "LanguageClient_projectRoot", root]), + )?; + self.notify(None, "s:ExecuteAutocmd", "LanguageClientBufReadPost")?; + + info!("End {}", lsp::notification::DidOpenTextDocument::METHOD); + Ok(()) + } + + pub fn textDocument_didChange(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", lsp::notification::DidChangeTextDocument::METHOD); + let (buftype, languageId, filename): (String, String, String) = self.gather_args( + &[VimVar::Buftype, VimVar::LanguageId, VimVar::Filename], + params, + )?; + if !buftype.is_empty() || languageId.is_empty() { + return Ok(()); + } + if !self.get(|state| Ok(state.text_documents.contains_key(&filename)))? { + info!("Not opened yet. Switching to didOpen."); + return self.textDocument_didOpen(params); + } + + let (text,): (Vec,) = self.gather_args(&[VimVar::Text], params)?; + + let text = text.join("\n"); + let text_state = self + .get(|state| { + state + .text_documents + .get(&filename) + .ok_or_else(|| { + format_err!("TextDocumentItem not found! filename: {}", filename) + }).map(|doc| doc.text.clone()) + }).unwrap_or_default(); + if text == text_state { + info!("Texts equal. Skipping didChange."); + return Ok(()); + } + + let version = self.update(|state| { + let document = state.text_documents.get_mut(&filename).ok_or_else(|| { + format_err!("Failed to get TextDocumentItem! filename: {}", filename) + })?; + + let version = document.version + 1; + document.version = version; + document.text = text.clone(); + + if state.change_throttle.is_some() { + let metadata = state + .text_documents_metadata + .entry(filename.clone()) + .or_insert_with(TextDocumentItemMetadata::default); + metadata.last_change = Instant::now(); + } + Ok(version) + })?; + + self.notify( + Some(&languageId), + lsp::notification::DidChangeTextDocument::METHOD, + DidChangeTextDocumentParams { + text_document: VersionedTextDocumentIdentifier { + uri: filename.to_url()?, + version: Some(version), + }, + content_changes: vec![TextDocumentContentChangeEvent { + range: None, + range_length: None, + text, + }], + }, + )?; + + info!("End {}", lsp::notification::DidChangeTextDocument::METHOD); + Ok(()) + } + + pub fn textDocument_didSave(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", lsp::notification::DidSaveTextDocument::METHOD); + let (buftype, languageId, filename): (String, String, String) = self.gather_args( + &[VimVar::Buftype, VimVar::LanguageId, VimVar::Filename], + params, + )?; + if !buftype.is_empty() || languageId.is_empty() { + return Ok(()); + } + let uri = filename.to_url()?; + + self.notify( + Some(&languageId), + lsp::notification::DidSaveTextDocument::METHOD, + DidSaveTextDocumentParams { + text_document: TextDocumentIdentifier { uri: uri.clone() }, + }, + )?; + + info!("End {}", lsp::notification::DidSaveTextDocument::METHOD); + Ok(()) + } + + pub fn textDocument_didClose(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", lsp::notification::DidCloseTextDocument::METHOD); + let (buftype, languageId, filename): (String, String, String) = self.gather_args( + &[VimVar::Buftype, VimVar::LanguageId, VimVar::Filename], + params, + )?; + if !buftype.is_empty() || languageId.is_empty() { + return Ok(()); + } + + self.notify( + Some(&languageId), + lsp::notification::DidCloseTextDocument::METHOD, + DidCloseTextDocumentParams { + text_document: TextDocumentIdentifier { + uri: filename.to_url()?, + }, + }, + )?; + info!("End {}", lsp::notification::DidCloseTextDocument::METHOD); + Ok(()) + } + + pub fn textDocument_publishDiagnostics(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", lsp::notification::PublishDiagnostics::METHOD); + let params: PublishDiagnosticsParams = params.clone().to_lsp()?; + if !self.get(|state| Ok(state.diagnosticsEnable))? { + return Ok(()); + } + + let mut filename = params.uri.filepath()?.to_string_lossy().into_owned(); + // Workaround bug: remove first '/' in case of '/C:/blabla'. + if filename.chars().nth(0) == Some('/') && filename.chars().nth(2) == Some(':') { + filename.remove(0); + } + // Unify name to avoid mismatch due to case insensitivity. + let filename = filename.canonicalize(); + + let diagnostics = params.diagnostics; + + self.diagnostics + .insert(filename.clone(), diagnostics.clone()); + self.update_quickfixlist()?; + + let current_filename: String = self.eval(VimVar::Filename)?; + if filename != current_filename.canonicalize() { + return Ok(()); + } + self.process_diagnostics(¤t_filename, &diagnostics)?; + self.languageClient_handleCursorMoved(&Value::Null)?; + self.notify(None, "s:ExecuteAutocmd", "LanguageClientDiagnosticsChanged")?; + + info!("End {}", lsp::notification::PublishDiagnostics::METHOD); + Ok(()) + } + + pub fn window_logMessage(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", lsp::notification::LogMessage::METHOD); + let params: LogMessageParams = params.clone().to_lsp()?; + let threshold = self.get(|state| state.windowLogMessageLevel.to_int())?; + if params.typ.to_int()? > threshold { + return Ok(()); + } + + let msg = format!("[{:?}] {}", params.typ, params.message); + self.echomsg(&msg)?; + info!("End {}", lsp::notification::LogMessage::METHOD); + Ok(()) + } + + pub fn window_showMessage(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", lsp::notification::ShowMessage::METHOD); + let params: ShowMessageParams = params.clone().to_lsp()?; + let msg = format!("[{:?}] {}", params.typ, params.message); + self.echomsg(&msg)?; + info!("End {}", lsp::notification::ShowMessage::METHOD); + Ok(()) + } + + pub fn client_registerCapability(&mut self, languageId: &str, params: &Value) -> Result { + info!("Begin {}", lsp::request::RegisterCapability::METHOD); + let params: RegistrationParams = params.clone().to_lsp()?; + for r in ¶ms.registrations { + match r.method.as_str() { + lsp::notification::DidChangeWatchedFiles::METHOD => { + let opt: DidChangeWatchedFilesRegistrationOptions = + serde_json::from_value(r.register_options.clone().unwrap_or_default())?; + if !self.watchers.contains_key(languageId) { + let (watcher_tx, watcher_rx) = channel(); + // TODO: configurable duration. + let watcher = notify::watcher(watcher_tx, Duration::from_secs(2))?; + self.watchers.insert(languageId.to_owned(), watcher); + self.watcher_rxs.insert(languageId.to_owned(), watcher_rx); + } + + if let Some(ref mut watcher) = self.watchers.get_mut(languageId) { + for w in &opt.watchers { + let recursive_mode = if w.glob_pattern.ends_with("**") { + notify::RecursiveMode::Recursive + } else { + notify::RecursiveMode::NonRecursive + }; + watcher + .watch(w.glob_pattern.trim_right_matches("**"), recursive_mode)?; + } + } + } + _ => { + warn!("Unknown registration: {:?}", r); + } + } + } + + self.registrations.extend(params.registrations); + info!("End {}", lsp::request::RegisterCapability::METHOD); + Ok(Value::Null) + } + + pub fn client_unregisterCapability( + &mut self, + languageId: &str, + params: &Value, + ) -> Result { + info!("Begin {}", lsp::request::UnregisterCapability::METHOD); + let params: UnregistrationParams = params.clone().to_lsp()?; + let mut regs_removed = vec![]; + for r in ¶ms.unregisterations { + if let Some(idx) = self + .registrations + .iter() + .position(|i| i.id == r.id && i.method == r.method) + { + regs_removed.push(self.registrations.swap_remove(idx)); + } + } + + for r in ®s_removed { + match r.method.as_str() { + lsp::notification::DidChangeWatchedFiles::METHOD => { + let opt: DidChangeWatchedFilesRegistrationOptions = + serde_json::from_value(r.register_options.clone().unwrap_or_default())?; + if let Some(ref mut watcher) = self.watchers.get_mut(languageId) { + for w in opt.watchers { + watcher.unwatch(w.glob_pattern)?; + } + } + } + _ => { + warn!("Unknown registration: {:?}", r); + } + } + } + + info!("End {}", lsp::request::UnregisterCapability::METHOD); + Ok(Value::Null) + } + + pub fn exit(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", lsp::notification::Exit::METHOD); + let (languageId,): (String,) = self.gather_args(&[VimVar::LanguageId], params)?; + + let result = self.notify( + Some(&languageId), + lsp::notification::Exit::METHOD, + Value::Null, + ); + if let Err(err) = result { + error!("Error: {:?}", err); + } + if let Err(err) = self.cleanup(&languageId) { + error!("Error: {:?}", err); + } + info!("End {}", lsp::notification::Exit::METHOD); + Ok(()) + } + + /////// Extensions by this plugin /////// + + pub fn languageClient_getState(&mut self, _params: &Value) -> Result { + info!("Begin {}", REQUEST__GetState); + let s = self.get(|state| Ok(serde_json::to_string(state)?))?; + info!("End {}", REQUEST__GetState); + Ok(Value::String(s)) + } + + pub fn languageClient_isAlive(&mut self, params: &Value) -> Result { + info!("Begin {}", REQUEST__IsAlive); + let (languageId,): (String,) = self.gather_args(&[VimVar::LanguageId], params)?; + let is_alive = self.get(|state| Ok(state.writers.contains_key(&languageId)))?; + info!("End {}", REQUEST__IsAlive); + Ok(Value::Bool(is_alive)) + } + + pub fn languageClient_registerServerCommands(&mut self, params: &Value) -> Result { + info!("Begin {}", REQUEST__RegisterServerCommands); + let commands: HashMap> = params.clone().to_lsp()?; + self.update(|state| { + state.serverCommands.extend(commands); + Ok(()) + })?; + let exp = format!( + "let g:LanguageClient_serverCommands={}", + serde_json::to_string(&self.get(|state| Ok(state.serverCommands.clone()))?)? + ); + self.command(&exp)?; + info!("End {}", REQUEST__RegisterServerCommands); + Ok(Value::Null) + } + + pub fn languageClient_setLoggingLevel(&mut self, params: &Value) -> Result { + info!("Begin {}", REQUEST__SetLoggingLevel); + let (loggingLevel,): (log::LevelFilter,) = self.gather_args(&["loggingLevel"], params)?; + logger::update_settings(&self.logger, &self.loggingFile, loggingLevel)?; + self.loggingLevel = loggingLevel; + info!("End {}", REQUEST__SetLoggingLevel); + Ok(Value::Null) + } + + pub fn languageClient_setDiagnosticsList(&mut self, params: &Value) -> Result { + info!("Begin {}", REQUEST__SetDiagnosticsList); + let (diagnosticsList,): (DiagnosticsList,) = + self.gather_args(&["diagnosticsList"], params)?; + self.diagnosticsList = diagnosticsList; + info!("End {}", REQUEST__SetDiagnosticsList); + Ok(Value::Null) + } + + pub fn languageClient_registerHandlers(&mut self, params: &Value) -> Result { + info!("Begin {}", REQUEST__RegisterHandlers); + let handlers: HashMap = params.clone().to_lsp()?; + self.update(|state| { + state.user_handlers.extend(handlers); + Ok(()) + })?; + info!("End {}", REQUEST__RegisterHandlers); + Ok(Value::Null) + } + + pub fn languageClient_omniComplete(&mut self, params: &Value) -> Result { + info!("Begin {}", REQUEST__OmniComplete); + let result = self.textDocument_completion(params)?; + let result: Option = serde_json::from_value(result)?; + let result = result.unwrap_or_else(|| CompletionResponse::Array(vec![])); + let mut matches = match result { + CompletionResponse::Array(arr) => arr, + CompletionResponse::List(list) => list.items, + }; + if !matches.iter().any(|m| m.sort_text.is_none()) { + matches.sort_by(|m1, m2| { + m1.sort_text + .as_ref() + .unwrap() + .cmp(m2.sort_text.as_ref().unwrap()) + }); + } + + let (complete_position,): (Option,) = + self.gather_args(&[("complete_position", "v:null")], params)?; + + let matches: Result> = matches + .iter() + .map(|item| VimCompleteItem::from_lsp(item, complete_position)) + .collect(); + let matches = matches?; + info!("End {}", REQUEST__OmniComplete); + Ok(serde_json::to_value(matches)?) + } + + pub fn languageClient_handleBufNewFile(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", NOTIFICATION__HandleBufNewFile); + let (buftype, languageId, filename): (String, String, String) = self.gather_args( + &[VimVar::Buftype, VimVar::LanguageId, VimVar::Filename], + params, + )?; + if !buftype.is_empty() || languageId.is_empty() || filename.is_empty() { + return Ok(()); + } + let autoStart: u8 = self.eval("!!get(g:, 'LanguageClient_autoStart', 1)")?; + if autoStart == 1 { + let ret = self.languageClient_startServer(params); + // This is triggered from autocmd, silent all errors. + if let Err(err) = ret { + warn!("Failed to start language server automatically. {}", err); + } + } + + info!("End {}", NOTIFICATION__HandleBufNewFile); + Ok(()) + } + + pub fn languageClient_handleBufReadPost(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", NOTIFICATION__HandleBufReadPost); + let (buftype, languageId, filename): (String, String, String) = self.gather_args( + &[VimVar::Buftype, VimVar::LanguageId, VimVar::Filename], + params, + )?; + if !buftype.is_empty() || languageId.is_empty() || filename.is_empty() { + return Ok(()); + } + + // File opened before. + if self.get(|state| Ok(state.text_documents.contains_key(&filename)))? { + info!("File is opened before."); + return Ok(()); + } + + let filename = filename.canonicalize(); + + if self.get(|state| Ok(state.writers.contains_key(&languageId)))? { + self.textDocument_didOpen(params)?; + + if let Some(diagnostics) = self.diagnostics.get(&filename).cloned() { + self.process_diagnostics(&filename, &diagnostics)?; + self.languageClient_handleCursorMoved(params)?; + } + } else { + let autoStart: u8 = self.eval("!!get(g:, 'LanguageClient_autoStart', 1)")?; + if autoStart == 1 { + let ret = self.languageClient_startServer(params); + // This is triggered from autocmd, silent all errors. + if let Err(err) = ret { + warn!("Failed to start language server automatically. {}", err); + } + } + } + + info!("End {}", NOTIFICATION__HandleBufReadPost); + Ok(()) + } + + pub fn languageClient_handleTextChanged(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", NOTIFICATION__HandleTextChanged); + let (buftype, filename): (String, String) = + self.gather_args(&[VimVar::Buftype, VimVar::Filename], params)?; + if !buftype.is_empty() { + info!( + "Skip handleTextChanged as buftype is non-empty: {}", + buftype + ); + } + let skip_notification = self.get(|state| { + if let Some(metadata) = state.text_documents_metadata.get(&filename) { + if let Some(throttle) = state.change_throttle { + if metadata.last_change.elapsed() < throttle { + return Ok(true); + } + } + } + Ok(false) + })?; + if skip_notification { + info!("Skip handleTextChanged due to throttling"); + return Ok(()); + } + + self.textDocument_didChange(params)?; + info!("End {}", NOTIFICATION__HandleTextChanged); + Ok(()) + } + + pub fn languageClient_handleBufWritePost(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", NOTIFICATION__HandleBufWritePost); + self.textDocument_didSave(params)?; + info!("End {}", NOTIFICATION__HandleBufWritePost); + Ok(()) + } + + pub fn languageClient_handleBufDelete(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", NOTIFICATION__HandleBufWritePost); + let (filename,): (String,) = self.gather_args(&[VimVar::Filename], params)?; + self.update(|state| { + state.text_documents.retain(|f, _| f != &filename); + state.diagnostics.retain(|f, _| f != &filename); + state.line_diagnostics.retain(|fl, _| fl.0 != filename); + state.signs.retain(|f, _| f != &filename); + Ok(()) + })?; + self.textDocument_didClose(params)?; + info!("End {}", NOTIFICATION__HandleBufWritePost); + Ok(()) + } + + pub fn languageClient_handleCursorMoved(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", NOTIFICATION__HandleCursorMoved); + let (buftype, filename, line): (String, String, u64) = + self.gather_args(&[VimVar::Buftype, VimVar::Filename, VimVar::Line], params)?; + let (visible_line_start, visible_line_end): (u64, u64) = self.gather_args( + &["LSP#visible_line_start()", "LSP#visible_line_end()"], + params, + )?; + if !buftype.is_empty() && !self.diagnostics.contains_key(&filename) { + return Ok(()); + } + + if line != self.last_cursor_line { + self.last_cursor_line = line; + + let message = self + .line_diagnostics + .get(&(filename.clone(), line)) + .cloned() + .unwrap_or_default(); + + if message != self.last_line_diagnostic { + self.echo_ellipsis(&message)?; + self.last_line_diagnostic = message; + } + } + + let signs: Vec<_> = self + .signs + .entry(filename.clone()) + .or_insert_with(|| vec![]) + .iter() + .filter_map(|s| { + if s.line < visible_line_start + 1 || s.line > visible_line_end + 1 { + return None; + } + + Some(s.clone()) + }).collect(); + if Some(&signs) != self.signs_placed.get(&filename) { + let empty = vec![]; + + let (signs, cmds) = get_command_update_signs( + self.signs_placed.get(&filename).unwrap_or(&empty), + &signs, + &filename, + ); + self.signs_placed.insert(filename.clone(), signs); + + info!("Updating signs: {:?}", cmds); + self.command(&cmds)?; + } + + let highlights: Vec<_> = self + .highlights + .entry(filename.clone()) + .or_insert_with(|| vec![]) + .iter() + .filter_map(|h| { + if h.line < visible_line_start || h.line > visible_line_end { + return None; + } + + Some(h.clone()) + }).collect(); + + if Some(&highlights) != self.highlights_placed.get(&filename) && self.is_nvim { + let source = if let Some(source) = self.highlight_source { + source + } else { + let source = self.call( + None, + "nvim_buf_add_highlight", + json!([0, 0, "Error", 1, 1, 1]), + )?; + self.highlight_source = Some(source); + source + }; + + self.highlights_placed + .insert(filename.clone(), highlights.clone()); + + self.notify( + None, + "nvim_buf_clear_highlight", + json!([0, source, visible_line_start, visible_line_end]), + )?; + + self.notify(None, "s:AddHighlights", json!([source, highlights]))?; + } + + info!("End {}", NOTIFICATION__HandleCursorMoved); + Ok(()) + } + + pub fn languageClient_handleCompleteDone(&mut self, params: &Value) -> Result<()> { + let (filename, completed_item, line, character): ( + String, + VimCompleteItem, + u64, + u64, + ) = self.gather_args( + &[ + VimVar::Filename.to_key().as_str(), + "completed_item", + VimVar::Line.to_key().as_str(), + VimVar::Character.to_key().as_str(), + ], + params, + )?; + + let user_data = match completed_item.user_data { + Some(user_data) => user_data, + _ => return Ok(()), + }; + let user_data: VimCompleteItemUserData = serde_json::from_str(&user_data)?; + let lspitem = match user_data.lspitem { + Some(lspitem) => lspitem, + _ => return Ok(()), + }; + + let mut edits = vec![]; + if self.completionPreferTextEdit { + if let Some(edit) = lspitem.text_edit { + self.command("undo")?; + edits.push(edit.clone()); + }; + } + if let Some(aedits) = lspitem.additional_text_edits { + edits.extend(aedits.clone()); + }; + + if edits.is_empty() { + return Ok(()); + } + + self.apply_TextEdits(filename, &edits)?; + self.cursor(line + 1, character + 1) + } + + pub fn languageClient_FZFSinkLocation(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", NOTIFICATION__FZFSinkLocation); + let params = match params { + Value::Array(ref arr) => Value::Array(arr.clone()), + _ => { + bail!("Expecting array params!"); + } + }; + + let lines: Vec = serde_json::from_value(params)?; + if lines.is_empty() { + err_msg("No selection!"); + } + + let location = lines + .get(0) + .ok_or_else(|| format_err!("Failed to get line! lines: {:?}", lines))? + .split('\t') + .next() + .ok_or_else(|| format_err!("Failed to parse: {:?}", lines))?; + let mut tokens: Vec<_> = location.split_terminator(':').collect(); + tokens.reverse(); + let filename: String = if tokens.len() > 2 { + let relpath = tokens + .pop() + .ok_or_else(|| format_err!("Failed to get file path! tokens: {:?}", tokens))? + .to_owned(); + let cwd: String = self.eval("getcwd()")?; + Path::new(&cwd).join(relpath).to_string_lossy().into_owned() + } else { + self.eval(VimVar::Filename)? + }; + let line = tokens + .pop() + .ok_or_else(|| format_err!("Failed to get line! tokens: {:?}", tokens))? + .to_int()? + - 1; + let character = tokens + .pop() + .ok_or_else(|| format_err!("Failed to get character! tokens: {:?}", tokens))? + .to_int()? + - 1; + + self.edit(&None, &filename)?; + self.cursor(line + 1, character + 1)?; + + info!("End {}", NOTIFICATION__FZFSinkLocation); + Ok(()) + } + + pub fn languageClient_FZFSinkCommand(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", NOTIFICATION__FZFSinkCommand); + let (selection,): (String,) = self.gather_args(&["selection"], params)?; + let tokens: Vec<&str> = selection.splitn(2, ": ").collect(); + let command = tokens + .get(0) + .cloned() + .ok_or_else(|| format_err!("Failed to get command! tokens: {:?}", tokens))?; + let title = tokens + .get(1) + .cloned() + .ok_or_else(|| format_err!("Failed to get title! tokens: {:?}", tokens))?; + let entry = self.get(|state| { + let commands = &state.stashed_codeAction_commands; + + commands + .iter() + .find(|e| e.command == command && e.title == title) + .cloned() + .ok_or_else(|| { + format_err!("No stashed command found! stashed commands: {:?}", commands) + }) + })?; + + if self.try_handle_command_by_client(&entry)? { + return Ok(()); + } + + self.workspace_executeCommand(&json!({ + "command": entry.command, + "arguments": entry.arguments, + }))?; + + self.update(|state| { + state.stashed_codeAction_commands = vec![]; + Ok(()) + })?; + + info!("End {}", NOTIFICATION__FZFSinkCommand); + Ok(()) + } + + pub fn NCM_refresh(&mut self, params: &Value) -> Result { + info!("Begin {}", REQUEST__NCMRefresh); + let params: NCMRefreshParams = serde_json::from_value(rpc::to_value(params.clone())?)?; + let NCMRefreshParams { info, ctx } = params; + if ctx.typed.is_empty() { + return Ok(Value::Null); + } + + let filename = ctx.filepath.clone(); + let line = ctx.lnum - 1; + let character = ctx.col - 1; + + let result = self.textDocument_completion(&json!({ + "buftype": "", + "languageId": ctx.filetype, + "filename": filename, + "line": line, + "character": character, + "handle": false, + }))?; + let result: Option = serde_json::from_value(result)?; + let result = result.unwrap_or_else(|| CompletionResponse::Array(vec![])); + let is_incomplete = match result { + CompletionResponse::Array(_) => false, + CompletionResponse::List(ref list) => list.is_incomplete, + }; + let matches: Result> = match result { + CompletionResponse::Array(arr) => arr, + CompletionResponse::List(list) => list.items, + }.iter() + .map(|item| VimCompleteItem::from_lsp(item, None)) + .collect(); + let matches = matches?; + self.call::<_, u8>( + None, + "cm#complete", + json!([info.name, ctx, ctx.startcol, matches, is_incomplete]), + )?; + info!("End {}", REQUEST__NCMRefresh); + Ok(Value::Null) + } + + pub fn NCM2_on_complete(&mut self, params: &Value) -> Result { + info!("Begin {}", REQUEST__NCM2OnComplete); + + let orig_ctx: Value = serde_json::from_value(rpc::to_value(params.clone())?)?; + let orig_ctx = &orig_ctx["ctx"]; + + let ctx: NCM2Context = serde_json::from_value(orig_ctx.clone())?; + if ctx.typed.is_empty() { + return Ok(Value::Null); + } + + let filename = ctx.filepath.clone(); + let line = ctx.lnum - 1; + let character = ctx.ccol - 1; + + let result = self.textDocument_completion(&json!({ + "buftype": "", + "languageId": ctx.filetype, + "filename": filename, + "line": line, + "character": character, + "handle": false})); + let is_incomplete; + let matches; + if let Ok(ref value) = result { + let completion = serde_json::from_value(value.clone())?; + is_incomplete = match completion { + CompletionResponse::List(ref list) => list.is_incomplete, + _ => false, + }; + let matches_result: Result> = match completion { + CompletionResponse::Array(arr) => arr, + CompletionResponse::List(list) => list.items, + }.iter() + .map(|item| VimCompleteItem::from_lsp(item, None)) + .collect(); + matches = matches_result?; + } else { + is_incomplete = true; + matches = vec![]; + } + self.call::<_, u8>( + None, + "ncm2#complete", + json!([orig_ctx, ctx.startccol, matches, is_incomplete]), + )?; + info!("End {}", REQUEST__NCM2OnComplete); + result + } + + pub fn languageClient_explainErrorAtPoint(&mut self, params: &Value) -> Result { + info!("Begin {}", REQUEST__ExplainErrorAtPoint); + let (buftype, filename, line, character): (String, String, u64, u64) = self.gather_args( + &[ + VimVar::Buftype, + VimVar::Filename, + VimVar::Line, + VimVar::Character, + ], + params, + )?; + if !buftype.is_empty() { + return Ok(Value::Null); + } + let diag = self.get(|state| { + state + .diagnostics + .get(&filename) + .ok_or_else(|| format_err!("No diagnostics found: filename: {}", filename,))? + .iter() + .find(|d| { + (line, character) >= (d.range.start.line, d.range.start.character) + && (line, character) < (d.range.end.line, d.range.end.character) + }).cloned() + .ok_or_else(|| { + format_err!( + "No diagnostics found: filename: {}, line: {}, character: {}", + filename, + line, + character + ) + }) + })?; + let message: Vec<_> = diag.message.lines().collect(); + self.preview(&message)?; + + info!("End {}", REQUEST__ExplainErrorAtPoint); + Ok(Value::Null) + } + + // Extensions by language servers. + pub fn language_status(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", NOTIFICATION__LanguageStatus); + let params: LanguageStatusParams = params.clone().to_lsp()?; + let msg = format!("{} {}", params.typee, params.message); + self.echomsg(&msg)?; + info!("End {}", NOTIFICATION__LanguageStatus); + Ok(()) + } + + pub fn rustDocument_implementations(&mut self, params: &Value) -> Result { + info!("Begin {}", REQUEST__RustImplementations); + let (buftype, languageId, filename, line, character, handle): ( + String, + String, + String, + u64, + u64, + bool, + ) = self.gather_args( + &[ + VimVar::Buftype, + VimVar::LanguageId, + VimVar::Filename, + VimVar::Line, + VimVar::Character, + VimVar::Handle, + ], + params, + )?; + if !buftype.is_empty() || languageId.is_empty() { + return Ok(Value::Null); + } + + let result = self.call( + Some(&languageId), + REQUEST__RustImplementations, + TextDocumentPositionParams { + text_document: TextDocumentIdentifier { + uri: filename.to_url()?, + }, + position: Position { line, character }, + }, + )?; + + if !handle { + return Ok(result); + } + + let locations: Vec = serde_json::from_value(result.clone())?; + self.display_locations(&locations)?; + + info!("End {}", REQUEST__RustImplementations); + Ok(result) + } + + pub fn rust_handleBeginBuild(&mut self, _params: &Value) -> Result<()> { + info!("Begin {}", NOTIFICATION__RustBeginBuild); + self.command(vec![ + format!("let {}=1", VIM__ServerStatus), + format!("let {}='Rust: build begin'", VIM__ServerStatusMessage), + ])?; + info!("End {}", NOTIFICATION__RustBeginBuild); + Ok(()) + } + + pub fn rust_handleDiagnosticsBegin(&mut self, _params: &Value) -> Result<()> { + info!("Begin {}", NOTIFICATION__RustDiagnosticsBegin); + self.command(vec![ + format!("let {}=1", VIM__ServerStatus), + format!("let {}='Rust: diagnostics begin'", VIM__ServerStatusMessage), + ])?; + info!("End {}", NOTIFICATION__RustDiagnosticsBegin); + Ok(()) + } + + pub fn rust_handleDiagnosticsEnd(&mut self, _params: &Value) -> Result<()> { + info!("Begin {}", NOTIFICATION__RustDiagnosticsEnd); + self.command(vec![ + format!("let {}=0", VIM__ServerStatus), + format!("let {}='Rust: diagnostics end'", VIM__ServerStatusMessage), + ])?; + info!("End {}", NOTIFICATION__RustDiagnosticsEnd); + Ok(()) + } + + pub fn window_progress(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", NOTIFICATION__WindowProgress); + let params: WindowProgressParams = params.clone().to_lsp()?; + + let done = params.done.unwrap_or(false); + + let mut buf = "LS: ".to_owned(); + + if done { + buf += "Idle"; + } else { + // For RLS this can be "Build" or "Diagnostics" or "Indexing". + buf += params + .title + .as_ref() + .map(|title| title.as_ref()) + .unwrap_or("Busy"); + + // For RLS this is the crate name, present only if the progress isn't known. + if let Some(message) = params.message { + buf += &format!(" ({})", &message); + } + // For RLS this is the progress percentage, present only if the it's known. + if let Some(percentage) = params.percentage { + buf += &format!(" ({:.1}% done)", percentage); + } + } + + self.command(vec![ + format!("let {}={}", VIM__ServerStatus, if done { 0 } else { 1 }), + format!( + "let {}='{}'", + VIM__ServerStatusMessage, + &escape_single_quote(buf) + ), + ])?; + info!("End {}", NOTIFICATION__WindowProgress); + Ok(()) + } + + pub fn languageClient_startServer(&mut self, params: &Value) -> Result { + info!("Begin {}", REQUEST__StartServer); + let (cmdargs,): (Vec,) = self.gather_args(&[("cmdargs", "[]")], params)?; + let cmdparams = vim_cmd_args_to_value(&cmdargs)?; + let params = params.combine(&cmdparams); + let (buftype, languageId, filename): (String, String, String) = self.gather_args( + &[VimVar::Buftype, VimVar::LanguageId, VimVar::Filename], + ¶ms, + )?; + + if !buftype.is_empty() || filename.is_empty() { + return Ok(Value::Null); + } + + if self.get(|state| Ok(state.writers.contains_key(&languageId)))? { + bail!( + "Language client has already started for language {}.", + &languageId + ); + } + + self.sync_settings()?; + + let command = self.get(|state| { + state + .serverCommands + .get(&languageId) + .cloned() + .ok_or_else(|| { + format_err!( + "No language server command found for file type: {}.", + &languageId + ) + }) + })?; + + let (child_id, reader, writer): (_, Box, Box) = + if command.get(0).map(|c| c.starts_with("tcp://")) == Some(true) { + let addr = command + .get(0) + .map(|s| s.replace("tcp://", "")) + .ok_or_else(|| err_msg("Server command can't be empty!"))?; + let stream = TcpStream::connect(addr)?; + let reader = Box::new(BufReader::new(stream.try_clone()?)); + let writer = Box::new(BufWriter::new(stream)); + (None, reader, writer) + } else { + let home = env::home_dir().ok_or_else(|| err_msg("Failed to get home dir"))?; + let command: Vec<_> = command + .into_iter() + .map(|cmd| { + if cmd.starts_with('~') { + cmd.replacen('~', &home.to_string_lossy(), 1) + } else { + cmd + } + }).collect(); + + let stderr = match self.serverStderr { + Some(ref path) => std::fs::OpenOptions::new() + .create(true) + .append(true) + .open(path) + .with_context(|err| format!("Failed to open file ({}): {}", path, err))? + .into(), + None => Stdio::null(), + }; + + let process = std::process::Command::new( + command.get(0).ok_or_else(|| err_msg("Empty command!"))?, + ).args(&command[1..]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(stderr) + .spawn() + .with_context(|err| { + format!("Failed to start language server ({:?}): {}", command, err) + })?; + + let child_id = Some(process.id()); + let reader = Box::new(BufReader::new( + process + .stdout + .ok_or_else(|| err_msg("Failed to get subprocess stdout"))?, + )); + let writer = Box::new(BufWriter::new( + process + .stdin + .ok_or_else(|| err_msg("Failed to get subprocess stdin"))?, + )); + (child_id, reader, writer) + }; + + self.update(|state| { + child_id.map(|id| state.child_ids.insert(languageId.clone(), id)); + state.writers.insert(languageId.clone(), writer); + Ok(()) + })?; + + let thread_name = format!("reader-{}", languageId); + let languageId_clone = languageId.clone(); + let tx = self.tx.clone(); + std::thread::Builder::new() + .name(thread_name.clone()) + .spawn(move || { + if let Err(err) = vim::loop_reader(reader, &Some(languageId_clone.clone()), &tx) { + let _ = tx.send(Message::Notification( + Some(languageId_clone.clone()), + rpc::Notification { + jsonrpc: None, + method: NOTIFICATION__ServerExited.into(), + params: json!({ + "languageId": languageId_clone, + "message": format!("{}", err), + }).to_params() + .unwrap_or_default(), + }, + )); + } + })?; + + info!("End {}", REQUEST__StartServer); + + if self.get(|state| Ok(state.writers.len()))? == 1 { + self.define_signs()?; + } + + self.initialize(¶ms)?; + self.initialized(¶ms)?; + + let root = self.roots.get(&languageId).cloned().unwrap_or_default(); + match self.get_workspace_settings(&root) { + Ok(Value::Null) => (), + Ok(settings) => self.workspace_didChangeConfiguration(&json!({ + VimVar::LanguageId.to_key(): languageId, + "settings": settings, + }))?, + Err(err) => warn!("Failed to get workspace settings: {}", err), + } + + self.textDocument_didOpen(¶ms)?; + self.textDocument_didChange(¶ms)?; + + self.notify(None, "s:ExecuteAutocmd", "LanguageClientStarted")?; + Ok(Value::Null) + } + + pub fn languageClient_serverExited(&mut self, params: &Value) -> Result<()> { + let (languageId, message): (String, String) = self.gather_args( + [VimVar::LanguageId.to_key().as_str(), "message"].as_ref(), + params, + )?; + + if self.writers.contains_key(&languageId) { + if let Err(err) = self.cleanup(&languageId) { + error!("Error in cleanup: {:?}", err); + } + if let Err(err) = self.echoerr(format!( + "Language server {} exited unexpectedly: {}", + languageId, message + )) { + error!("Error in echoerr: {:?}", err); + } + } + + Ok(()) + } + + pub fn handle_fs_events(&mut self) -> Result<()> { + let mut pending_changes = HashMap::new(); + for (languageId, watcher_rx) in &mut self.watcher_rxs { + let mut events = vec![]; + loop { + let result = watcher_rx.try_recv(); + let event = match result { + Ok(event) => event, + Err(TryRecvError::Empty) => { + break; + } + Err(TryRecvError::Disconnected) => { + bail!("File system notification channel disconnected!"); + } + }; + events.push(event); + } + + let mut changes = vec![]; + for e in events { + if let Ok(c) = e.to_lsp() { + changes.extend(c); + } + } + + if changes.is_empty() { + continue; + } + + pending_changes.insert(languageId.to_owned(), changes); + } + + for (languageId, changes) in pending_changes { + self.workspace_didChangeWatchedFiles(&json!({ + "languageId": languageId, + "changes": changes + }))?; + } + + Ok(()) + } + + pub fn workspace_didChangeWatchedFiles(&mut self, params: &Value) -> Result<()> { + info!("Begin {}", lsp::notification::DidChangeWatchedFiles::METHOD); + let (languageId,): (String,) = self.gather_args([VimVar::LanguageId].as_ref(), params)?; + + let params: DidChangeWatchedFilesParams = params.clone().to_lsp()?; + self.notify( + Some(&languageId), + lsp::notification::DidChangeWatchedFiles::METHOD, + params, + )?; + + info!("End {}", lsp::notification::DidChangeWatchedFiles::METHOD); + Ok(()) + } + + pub fn java_classFileContents(&mut self, params: &Value) -> Result { + info!("Begin {}", REQUEST__ClassFileContents); + let (languageId,): (String,) = self.gather_args(&[VimVar::LanguageId], params)?; + + let content: String = self.call( + Some(languageId.as_str()), + REQUEST__ClassFileContents, + params, + )?; + + info!("End {}", REQUEST__ClassFileContents); + Ok(Value::String(content)) + } + + pub fn debug_info(&mut self, params: &Value) -> Result { + info!("Begin {}", REQUEST__DebugInfo); + let (languageId,): (String,) = self.gather_args(&[VimVar::LanguageId], params)?; + let mut msg = String::new(); + msg += &format!( + "Project root: {}\n", + self.roots.get(&languageId).cloned().unwrap_or_default() + ); + msg += &format!( + "Language server process id: {}\n", + self.child_ids.get(&languageId).cloned().unwrap_or_default(), + ); + msg += &format!( + "Language server stderr: {}\n", + self.serverStderr.clone().unwrap_or_default() + ); + msg += &format!("Log level: {}\n", self.loggingLevel); + msg += &format!( + "Log file: {}\n", + self.loggingFile.clone().unwrap_or_default() + ); + self.echo(&msg)?; + info!("End {}", REQUEST__DebugInfo); + Ok(json!(msg)) + } +} diff --git a/vim/bundle/LanguageClient-neovim/src/logger.rs b/vim/bundle/LanguageClient-neovim/src/logger.rs new file mode 100644 index 0000000..5b0106b --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/src/logger.rs @@ -0,0 +1,55 @@ +use super::*; +use log::LevelFilter; +use log4rs::append::file::FileAppender; +use log4rs::config::{Appender, Config, Logger, Root}; +use log4rs::encode::pattern::PatternEncoder; +use log4rs::Handle; + +fn create_config(path: &Option, level: LevelFilter) -> Result { + let encoder = + PatternEncoder::new("{date(%H:%M:%S)} {level} {thread} {file}:{line} {message}{n}"); + + let mut config_builder = + Config::builder().logger(Logger::builder().build("languageclient", level)); + + let mut root_builder = Root::builder(); + if let Some(path) = path { + // Ensure log file writable. + { + let mut f = std::fs::OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(path) + .with_context(|err| format!("Failed to open file ({}): {}", path, err))?; + #[allow(write_literal)] + writeln!( + f, + "#######\nLanguageClient {} {}\n#######", + env!("CARGO_PKG_VERSION"), + env!("GIT_HASH") + )?; + } + + let appender = FileAppender::builder() + .encoder(Box::new(encoder)) + .build(path)?; + config_builder = + config_builder.appender(Appender::builder().build("logfile", Box::new(appender))); + root_builder = root_builder.appender("logfile"); + } + let config = config_builder.build(root_builder.build(level))?; + Ok(config) +} + +pub fn init() -> Result { + let handle = log4rs::init_config(create_config(&None, LevelFilter::Warn)?)?; + + Ok(handle) +} + +pub fn update_settings(handle: &Handle, path: &Option, level: LevelFilter) -> Result<()> { + let config = create_config(path, level)?; + handle.set_config(config); + Ok(()) +} diff --git a/vim/bundle/LanguageClient-neovim/src/main.rs b/vim/bundle/LanguageClient-neovim/src/main.rs new file mode 100644 index 0000000..9af0e01 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/src/main.rs @@ -0,0 +1,97 @@ +#![allow(non_snake_case, non_upper_case_globals, unknown_lints)] + +use std::collections::{HashMap, HashSet, VecDeque}; +use std::convert::Into; +use std::env; +use std::fmt::Debug; +use std::fs::{read_to_string, File}; +use std::io::prelude::*; +use std::io::{BufRead, BufReader, BufWriter}; +use std::net::TcpStream; +use std::ops::Deref; +use std::path::{Path, PathBuf}; +use std::process::{ChildStdin, ChildStdout, Stdio}; +use std::str::FromStr; +use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; +use std::thread; +use std::time::{Duration, Instant}; + +#[macro_use] +extern crate log; +extern crate log4rs; + +#[macro_use] +extern crate failure; +#[allow(unused_imports)] +use failure::{err_msg, Error, ResultExt}; + +#[macro_use] +extern crate maplit; + +extern crate serde; +use serde::de::DeserializeOwned; +use serde::Serialize; +#[macro_use] +extern crate serde_derive; +#[macro_use] +extern crate serde_json; + +extern crate jsonrpc_core as rpc; +use rpc::{Params, Value}; + +extern crate languageserver_types as lsp; +use lsp::request::GotoDefinitionResponse; +use lsp::*; + +extern crate url; +use url::Url; + +extern crate pathdiff; +use pathdiff::diff_paths; + +extern crate diff; + +extern crate glob; +extern crate regex; + +extern crate notify; +#[allow(unused_imports)] +use notify::Watcher; + +#[macro_use] +extern crate structopt; +use structopt::StructOpt; + +mod types; +use types::*; +mod utils; +use utils::*; +mod languageclient; +mod logger; +mod rpchandler; +mod vim; + +#[derive(Debug, StructOpt)] +struct Arguments {} + +fn main() -> Result<()> { + let version = format!("{} {}", env!("CARGO_PKG_VERSION"), env!("GIT_HASH")); + let args = Arguments::clap().version(version.as_str()); + let _ = args.get_matches(); + + let mut state = State::new()?; + + let tx = state.tx.clone(); + let reader_thread_name: String = "reader-main".into(); + thread::Builder::new() + .name(reader_thread_name.clone()) + .spawn(move || { + let stdin = std::io::stdin(); + let stdin = stdin.lock(); + if let Err(err) = vim::loop_reader(stdin, &None, &tx) { + error!("{} exited: {:?}", reader_thread_name, err); + } + })?; + + state.loop_message() +} diff --git a/vim/bundle/LanguageClient-neovim/src/rpchandler.rs b/vim/bundle/LanguageClient-neovim/src/rpchandler.rs new file mode 100644 index 0000000..c0e4639 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/src/rpchandler.rs @@ -0,0 +1,177 @@ +use super::*; +use lsp::notification::Notification; +use lsp::request::Request; + +impl State { + pub fn handle_method_call( + &mut self, + languageId: Option<&str>, + method_call: &rpc::MethodCall, + ) -> Result { + let params = serde_json::to_value(method_call.params.clone())?; + + let user_handler = self.get(|state| { + state + .user_handlers + .get(&method_call.method) + .cloned() + .ok_or_else(|| err_msg("No user handler")) + }); + if let Ok(user_handler) = user_handler { + return self.call(None, &user_handler, params); + } + + match method_call.method.as_str() { + lsp::request::RegisterCapability::METHOD => { + self.client_registerCapability(languageId.unwrap_or_default(), ¶ms) + } + lsp::request::UnregisterCapability::METHOD => { + self.client_unregisterCapability(languageId.unwrap_or_default(), ¶ms) + } + lsp::request::HoverRequest::METHOD => self.textDocument_hover(¶ms), + REQUEST__FindLocations => self.find_locations(¶ms), + lsp::request::Rename::METHOD => self.textDocument_rename(¶ms), + lsp::request::DocumentSymbolRequest::METHOD => { + self.textDocument_documentSymbol(¶ms) + } + lsp::request::WorkspaceSymbol::METHOD => self.workspace_symbol(¶ms), + lsp::request::CodeActionRequest::METHOD => self.textDocument_codeAction(¶ms), + lsp::request::Completion::METHOD => self.textDocument_completion(¶ms), + lsp::request::SignatureHelpRequest::METHOD => self.textDocument_signatureHelp(¶ms), + lsp::request::References::METHOD => self.textDocument_references(¶ms), + lsp::request::Formatting::METHOD => self.textDocument_formatting(¶ms), + lsp::request::RangeFormatting::METHOD => self.textDocument_rangeFormatting(¶ms), + lsp::request::ResolveCompletionItem::METHOD => self.completionItem_resolve(¶ms), + lsp::request::ExecuteCommand::METHOD => self.workspace_executeCommand(¶ms), + lsp::request::ApplyWorkspaceEdit::METHOD => self.workspace_applyEdit(¶ms), + lsp::request::DocumentHighlightRequest::METHOD => { + self.textDocument_documentHighlight(¶ms) + } + REQUEST__RustImplementations => self.rustDocument_implementations(¶ms), + // Extensions. + REQUEST__GetState => self.languageClient_getState(¶ms), + REQUEST__IsAlive => self.languageClient_isAlive(¶ms), + REQUEST__StartServer => self.languageClient_startServer(¶ms), + REQUEST__RegisterServerCommands => self.languageClient_registerServerCommands(¶ms), + REQUEST__SetLoggingLevel => self.languageClient_setLoggingLevel(¶ms), + REQUEST__SetDiagnosticsList => self.languageClient_setDiagnosticsList(¶ms), + REQUEST__RegisterHandlers => self.languageClient_registerHandlers(¶ms), + REQUEST__NCMRefresh => self.NCM_refresh(¶ms), + REQUEST__NCM2OnComplete => self.NCM2_on_complete(¶ms), + REQUEST__ExplainErrorAtPoint => self.languageClient_explainErrorAtPoint(¶ms), + REQUEST__OmniComplete => self.languageClient_omniComplete(¶ms), + REQUEST__ClassFileContents => self.java_classFileContents(¶ms), + REQUEST__DebugInfo => self.debug_info(¶ms), + + _ => { + let languageId_target = if languageId.is_some() { + // Message from language server. No handler found. + let msg = format!("Message not handled: {:?}", method_call); + if method_call.method.starts_with('$') { + warn!("{}", msg); + return Ok(Value::default()); + } else { + return Err(err_msg(msg)); + } + } else { + // Message from vim. Proxy to language server. + let (languageId_target,): (String,) = + self.gather_args(&[VimVar::LanguageId], ¶ms)?; + info!( + "Proxy message directly to language server: {:?}", + method_call + ); + Some(languageId_target) + }; + + self.call(languageId_target.as_deref(), &method_call.method, ¶ms) + } + } + } + + pub fn handle_notification( + &mut self, + languageId: Option<&str>, + notification: &rpc::Notification, + ) -> Result<()> { + let params = serde_json::to_value(notification.params.clone())?; + + let user_handler = self.get(|state| { + state + .user_handlers + .get(¬ification.method) + .cloned() + .ok_or_else(|| err_msg("No user handler")) + }); + if let Ok(user_handler) = user_handler { + self.call::<_, u8>(None, &user_handler, params.clone())?; + return Ok(()); + } + + match notification.method.as_str() { + lsp::notification::DidChangeConfiguration::METHOD => { + self.workspace_didChangeConfiguration(¶ms)? + } + lsp::notification::DidOpenTextDocument::METHOD => self.textDocument_didOpen(¶ms)?, + lsp::notification::DidChangeTextDocument::METHOD => { + self.textDocument_didChange(¶ms)? + } + lsp::notification::DidSaveTextDocument::METHOD => self.textDocument_didSave(¶ms)?, + lsp::notification::DidCloseTextDocument::METHOD => { + self.textDocument_didClose(¶ms)? + } + lsp::notification::PublishDiagnostics::METHOD => { + self.textDocument_publishDiagnostics(¶ms)? + } + lsp::notification::LogMessage::METHOD => self.window_logMessage(¶ms)?, + lsp::notification::ShowMessage::METHOD => self.window_showMessage(¶ms)?, + lsp::notification::Exit::METHOD => self.exit(¶ms)?, + // Extensions. + NOTIFICATION__HandleBufNewFile => self.languageClient_handleBufNewFile(¶ms)?, + NOTIFICATION__HandleBufReadPost => self.languageClient_handleBufReadPost(¶ms)?, + NOTIFICATION__HandleTextChanged => self.languageClient_handleTextChanged(¶ms)?, + NOTIFICATION__HandleBufWritePost => self.languageClient_handleBufWritePost(¶ms)?, + NOTIFICATION__HandleBufDelete => self.languageClient_handleBufDelete(¶ms)?, + NOTIFICATION__HandleCursorMoved => self.languageClient_handleCursorMoved(¶ms)?, + NOTIFICATION__HandleCompleteDone => self.languageClient_handleCompleteDone(¶ms)?, + NOTIFICATION__FZFSinkLocation => self.languageClient_FZFSinkLocation(¶ms)?, + NOTIFICATION__FZFSinkCommand => self.languageClient_FZFSinkCommand(¶ms)?, + NOTIFICATION__ClearDocumentHighlight => { + self.languageClient_clearDocumentHighlight(¶ms)? + } + // Extensions by language servers. + NOTIFICATION__LanguageStatus => self.language_status(¶ms)?, + NOTIFICATION__RustBeginBuild => self.rust_handleBeginBuild(¶ms)?, + NOTIFICATION__RustDiagnosticsBegin => self.rust_handleDiagnosticsBegin(¶ms)?, + NOTIFICATION__RustDiagnosticsEnd => self.rust_handleDiagnosticsEnd(¶ms)?, + NOTIFICATION__WindowProgress => self.window_progress(¶ms)?, + NOTIFICATION__ServerExited => self.languageClient_serverExited(¶ms)?, + + _ => { + let languageId_target = if languageId.is_some() { + // Message from language server. No handler found. + let msg = format!("Message not handled: {:?}", notification); + if notification.method.starts_with('$') { + warn!("{}", msg); + return Ok(()); + } else { + return Err(err_msg(msg)); + } + } else { + // Message from vim. Proxy to language server. + let (languageId_target,): (String,) = + self.gather_args(&[VimVar::LanguageId], ¶ms)?; + info!( + "Proxy message directly to language server: {:?}", + notification + ); + Some(languageId_target) + }; + + self.notify(languageId_target.as_deref(), ¬ification.method, ¶ms)?; + } + }; + + Ok(()) + } +} diff --git a/vim/bundle/LanguageClient-neovim/src/types.rs b/vim/bundle/LanguageClient-neovim/src/types.rs new file mode 100644 index 0000000..bdaced4 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/src/types.rs @@ -0,0 +1,1066 @@ +use super::*; + +pub type Result = std::result::Result; + +#[derive(Debug, Fail)] +pub enum LCError { + #[fail( + display = "Language server is not running for: {}", + languageId + )] + NoLanguageServer { languageId: String }, +} + +// Extensions. +pub const REQUEST__GetState: &str = "languageClient/getState"; +pub const REQUEST__IsAlive: &str = "languageClient/isAlive"; +pub const REQUEST__StartServer: &str = "languageClient/startServer"; +pub const REQUEST__RegisterServerCommands: &str = "languageClient/registerServerCommands"; +pub const REQUEST__OmniComplete: &str = "languageClient/omniComplete"; +pub const REQUEST__SetLoggingLevel: &str = "languageClient/setLoggingLevel"; +pub const REQUEST__SetDiagnosticsList: &str = "languageClient/setDiagnosticsList"; +pub const REQUEST__RegisterHandlers: &str = "languageClient/registerHandlers"; +pub const REQUEST__NCMRefresh: &str = "LanguageClient_NCMRefresh"; +pub const REQUEST__NCM2OnComplete: &str = "LanguageClient_NCM2OnComplete"; +pub const REQUEST__ExplainErrorAtPoint: &str = "languageClient/explainErrorAtPoint"; +pub const REQUEST__FindLocations: &str = "languageClient/findLocations"; +pub const REQUEST__DebugInfo: &str = "languageClient/debugInfo"; +pub const NOTIFICATION__HandleBufNewFile: &str = "languageClient/handleBufNewFile"; +pub const NOTIFICATION__HandleBufReadPost: &str = "languageClient/handleBufReadPost"; +pub const NOTIFICATION__HandleTextChanged: &str = "languageClient/handleTextChanged"; +pub const NOTIFICATION__HandleBufWritePost: &str = "languageClient/handleBufWritePost"; +pub const NOTIFICATION__HandleBufDelete: &str = "languageClient/handleBufDelete"; +pub const NOTIFICATION__HandleCursorMoved: &str = "languageClient/handleCursorMoved"; +pub const NOTIFICATION__HandleCompleteDone: &str = "languageClient/handleCompleteDone"; +pub const NOTIFICATION__FZFSinkLocation: &str = "LanguageClient_FZFSinkLocation"; +pub const NOTIFICATION__FZFSinkCommand: &str = "LanguageClient_FZFSinkCommand"; +pub const NOTIFICATION__ServerExited: &str = "$languageClient/serverExited"; +pub const NOTIFICATION__ClearDocumentHighlight: &str = "languageClient/clearDocumentHighlight"; + +// Extensions by language servers. +pub const REQUEST__RustImplementations: &str = "rustDocument/implementations"; +pub const NOTIFICATION__RustBeginBuild: &str = "rustDocument/beginBuild"; +pub const NOTIFICATION__RustDiagnosticsBegin: &str = "rustDocument/diagnosticsBegin"; +pub const NOTIFICATION__RustDiagnosticsEnd: &str = "rustDocument/diagnosticsEnd"; +// This is an RLS extension but the name is general enough to assume it might be implemented by +// other language servers or planned for inclusion in the base protocol. +pub const NOTIFICATION__WindowProgress: &str = "window/progress"; +pub const NOTIFICATION__LanguageStatus: &str = "language/status"; +pub const REQUEST__ClassFileContents: &str = "java/classFileContents"; + +pub const CommandsClient: &[&str] = &["java.apply.workspaceEdit"]; + +// Vim variable names +pub const VIM__ServerStatus: &str = "g:LanguageClient_serverStatus"; +pub const VIM__ServerStatusMessage: &str = "g:LanguageClient_serverStatusMessage"; + +/// Thread safe read. +pub trait SyncRead: BufRead + Sync + Send + Debug {} +impl SyncRead for BufReader {} +impl SyncRead for BufReader {} + +/// Thread safe write. +pub trait SyncWrite: Write + Sync + Send + Debug {} +impl SyncWrite for BufWriter {} +impl SyncWrite for BufWriter {} + +pub type Id = u64; + +#[derive(Debug, Serialize, Deserialize)] +pub enum Message { + MethodCall(Option, rpc::MethodCall), + Notification(Option, rpc::Notification), + Output(rpc::Output), +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum Call { + MethodCall(Option, rpc::MethodCall), + Notification(Option, rpc::Notification), +} + +#[derive(Clone, Copy, Serialize)] +pub struct HighlightSource { + pub buffer: u64, + pub source: u64, +} + +#[derive(Serialize)] +pub struct State { + // Program state. + pub id: Id, + #[serde(skip_serializing)] + pub tx: Sender, + #[serde(skip_serializing)] + pub rx: Receiver, + pub pending_calls: VecDeque, + pub pending_outputs: HashMap, + + pub child_ids: HashMap, + #[serde(skip_serializing)] + pub writers: HashMap>, + pub capabilities: HashMap, + pub registrations: Vec, + pub roots: HashMap, + pub text_documents: HashMap, + pub text_documents_metadata: HashMap, + // filename => diagnostics. + pub diagnostics: HashMap>, + #[serde(skip_serializing)] + pub line_diagnostics: HashMap<(String, u64), String>, + pub signs: HashMap>, + pub signs_placed: HashMap>, + pub highlight_source: Option, + pub highlights: HashMap>, + pub highlights_placed: HashMap>, + // TODO: make file specific. + pub highlight_match_ids: Vec, + pub document_highlight_source: Option, + pub user_handlers: HashMap, + #[serde(skip_serializing)] + pub watchers: HashMap, + #[serde(skip_serializing)] + pub watcher_rxs: HashMap>, + + pub is_nvim: bool, + pub last_cursor_line: u64, + pub last_line_diagnostic: String, + pub stashed_codeAction_commands: Vec, + + // User settings. + pub serverCommands: HashMap>, + pub autoStart: bool, + pub selectionUI: SelectionUI, + pub trace: Option, + pub diagnosticsEnable: bool, + pub diagnosticsList: DiagnosticsList, + pub diagnosticsDisplay: HashMap, + pub diagnosticsSignsMax: Option, + pub documentHighlightDisplay: HashMap, + pub windowLogMessageLevel: MessageType, + pub settingsPath: String, + pub loadSettings: bool, + pub rootMarkers: Option, + pub change_throttle: Option, + pub wait_output_timeout: Duration, + pub hoverPreview: HoverPreviewOption, + pub completionPreferTextEdit: bool, + + pub loggingFile: Option, + pub loggingLevel: log::LevelFilter, + pub serverStderr: Option, + #[serde(skip_serializing)] + pub logger: log4rs::Handle, +} + +impl State { + pub fn new() -> Result { + let logger = logger::init()?; + + let (tx, rx) = channel(); + + Ok(State { + id: 0, + tx, + rx, + pending_calls: VecDeque::new(), + pending_outputs: HashMap::new(), + + child_ids: HashMap::new(), + writers: HashMap::new(), + capabilities: HashMap::new(), + registrations: vec![], + roots: HashMap::new(), + text_documents: HashMap::new(), + text_documents_metadata: HashMap::new(), + diagnostics: HashMap::new(), + line_diagnostics: HashMap::new(), + signs: HashMap::new(), + signs_placed: HashMap::new(), + highlight_source: None, + highlights: HashMap::new(), + highlights_placed: HashMap::new(), + highlight_match_ids: Vec::new(), + document_highlight_source: None, + user_handlers: HashMap::new(), + watchers: HashMap::new(), + watcher_rxs: HashMap::new(), + + is_nvim: false, + last_cursor_line: 0, + last_line_diagnostic: " ".into(), + stashed_codeAction_commands: vec![], + + serverCommands: HashMap::new(), + autoStart: true, + selectionUI: SelectionUI::LocationList, + trace: None, + diagnosticsEnable: true, + diagnosticsList: DiagnosticsList::Quickfix, + diagnosticsDisplay: DiagnosticsDisplay::default(), + diagnosticsSignsMax: None, + documentHighlightDisplay: DocumentHighlightDisplay::default(), + windowLogMessageLevel: MessageType::Warning, + settingsPath: format!(".vim{}settings.json", std::path::MAIN_SEPARATOR), + loadSettings: false, + rootMarkers: None, + change_throttle: None, + wait_output_timeout: Duration::from_secs(10), + hoverPreview: HoverPreviewOption::default(), + completionPreferTextEdit: false, + loggingFile: None, + loggingLevel: log::LevelFilter::Warn, + serverStderr: None, + + logger, + }) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum SelectionUI { + FZF, + Quickfix, + LocationList, +} + +impl Default for SelectionUI { + fn default() -> Self { + SelectionUI::LocationList + } +} + +impl FromStr for SelectionUI { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s.to_ascii_uppercase().as_str() { + "FZF" => Ok(SelectionUI::FZF), + "QUICKFIX" => Ok(SelectionUI::Quickfix), + "LOCATIONLIST" | "LOCATION-LIST" => Ok(SelectionUI::LocationList), + _ => bail!("Invalid option for LanguageClient_selectionUI: {}", s), + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum HoverPreviewOption { + Always, + Auto, + Never, +} + +impl Default for HoverPreviewOption { + fn default() -> Self { + HoverPreviewOption::Auto + } +} + +impl FromStr for HoverPreviewOption { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s.to_ascii_uppercase().as_str() { + "ALWAYS" => Ok(HoverPreviewOption::Always), + "AUTO" => Ok(HoverPreviewOption::Auto), + "NEVER" => Ok(HoverPreviewOption::Never), + _ => bail!("Invalid option for LanguageClient_hoverPreview: {}", s), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum DiagnosticsList { + Quickfix, + Location, + Disabled, +} + +impl Default for DiagnosticsList { + fn default() -> Self { + DiagnosticsList::Quickfix + } +} + +impl FromStr for DiagnosticsList { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s.to_ascii_uppercase().as_str() { + "QUICKFIX" => Ok(DiagnosticsList::Quickfix), + "LOCATION" => Ok(DiagnosticsList::Location), + "DISABLED" => Ok(DiagnosticsList::Disabled), + _ => bail!("Invalid option for LanguageClient_diagnosticsList: {}", s), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DiagnosticsDisplay { + pub name: String, + pub texthl: String, + pub signText: String, + pub signTexthl: String, +} + +impl DiagnosticsDisplay { + pub fn default() -> HashMap { + let mut map = HashMap::new(); + map.insert( + 1, + DiagnosticsDisplay { + name: "Error".to_owned(), + texthl: "ALEError".to_owned(), + signText: "✖".to_owned(), + signTexthl: "ALEErrorSign".to_owned(), + }, + ); + map.insert( + 2, + DiagnosticsDisplay { + name: "Warning".to_owned(), + texthl: "ALEWarning".to_owned(), + signText: "⚠".to_owned(), + signTexthl: "ALEWarningSign".to_owned(), + }, + ); + map.insert( + 3, + DiagnosticsDisplay { + name: "Information".to_owned(), + texthl: "ALEInfo".to_owned(), + signText: "ℹ".to_owned(), + signTexthl: "ALEInfoSign".to_owned(), + }, + ); + map.insert( + 4, + DiagnosticsDisplay { + name: "Hint".to_owned(), + texthl: "ALEInfo".to_owned(), + signText: "➤".to_owned(), + signTexthl: "ALEInfoSign".to_owned(), + }, + ); + map + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Sign { + pub id: u64, + pub line: u64, + pub text: String, + pub severity: Option, +} + +impl Sign { + pub fn new(line: u64, text: String, severity: Option) -> Sign { + Sign { + id: Self::get_id(line, severity), + line, + text, + severity, + } + } + + fn get_id(line: u64, severity: Option) -> u64 { + let base_id = 75_000; + base_id + (line - 1) * 4 + severity + .unwrap_or(DiagnosticSeverity::Hint) + .to_int() + .unwrap_or(4) + - 1 + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DocumentHighlightDisplay { + pub name: String, + pub texthl: String, +} + +impl DocumentHighlightDisplay { + pub fn default() -> HashMap { + let mut map = HashMap::new(); + map.insert( + 1, + DocumentHighlightDisplay { + name: "Text".to_owned(), + texthl: "SpellCap".to_owned(), + }, + ); + map.insert( + 2, + DocumentHighlightDisplay { + name: "Read".to_owned(), + texthl: "SpellLocal".to_owned(), + }, + ); + map.insert( + 3, + DocumentHighlightDisplay { + name: "Write".to_owned(), + texthl: "SpellRare".to_owned(), + }, + ); + map + } +} + +impl std::cmp::Ord for Sign { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.id.cmp(&other.id) + } +} + +impl std::cmp::PartialOrd for Sign { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl std::cmp::PartialEq for Sign { + fn eq(&self, other: &Self) -> bool { + // Quick check whether sign should be updated. + self.text == other.text && self.severity == other.severity + } +} + +impl std::cmp::Eq for Sign {} + +use std::hash::{Hash, Hasher}; +impl Hash for Sign { + fn hash(&self, state: &mut H) { + self.id.hash(state); + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Highlight { + pub line: u64, + pub character_start: u64, + pub character_end: u64, + pub group: String, + pub text: String, +} + +impl PartialEq for Highlight { + fn eq(&self, other: &Self) -> bool { + // Quick check whether highlight should be updated. + self.text == other.text && self.group == other.group + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct QuickfixEntry { + pub filename: String, + pub lnum: u64, + pub col: Option, + pub nr: Option, + pub text: Option, + #[serde(rename = "type")] + pub typ: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct NCMInfo { + pub name: String, + pub abbreviation: String, + pub enable: u64, + pub scopes: Vec, + pub cm_refresh_patterns: Vec, + pub early_cache: u64, + pub cm_refresh: String, + pub priority: u64, + pub auto_popup: u64, + pub cm_refresh_length: u64, + pub sort: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct NCMContext { + pub bufnr: u64, + pub lnum: u64, + pub col: u64, + pub filetype: String, + pub typed: String, + pub filepath: String, + + pub scope: String, + pub startcol: u64, + pub base: String, + pub force: u64, + pub early_cache: bool, + + pub scope_match: String, + pub changedtick: u64, + pub curpos: Vec, + pub match_end: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct NCMRefreshParams { + pub info: NCMInfo, + pub ctx: NCMContext, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct NCM2Context { + pub bufnr: u64, + pub lnum: u64, + pub ccol: u64, + pub filetype: String, + pub typed: String, + pub filepath: String, + pub scope: String, + pub startccol: u64, + pub base: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct VimCompleteItem { + pub word: String, + pub abbr: String, + pub menu: String, + pub info: String, + pub kind: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub icase: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub dup: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub snippet: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub is_snippet: Option, + // NOTE: `user_data` can only be string in vim. So cannot specify concrete type here. + #[serde(skip_serializing_if = "Option::is_none")] + pub user_data: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct VimCompleteItemUserData { + #[serde(skip_serializing_if = "Option::is_none")] + pub lspitem: Option, +} + +impl VimCompleteItem { + pub fn from_lsp( + lspitem: &CompletionItem, + complete_position: Option, + ) -> Result { + let abbr = lspitem.label.clone(); + let mut word = lspitem.insert_text.clone().unwrap_or_default(); + if word.is_empty() { + match (lspitem.text_edit.clone(), complete_position) { + (Some(text_edit), Some(complete_position)) => { + // TextEdit range start might be different from vim expected completion start. + // From spec, TextEdit can only span one line, i.e., the current line. + if text_edit.range.start.line != complete_position { + word = text_edit + .new_text + .get((complete_position as usize)..) + .and_then(|line| line.split_whitespace().next()) + .map_or_else(String::new, ToOwned::to_owned); + } else { + word = text_edit.new_text.clone(); + } + } + (Some(text_edit), _) => { + word = text_edit.new_text.clone(); + } + (_, _) => { + word = lspitem.label.clone(); + } + } + } + + let is_snippet; + let snippet; + if lspitem.insert_text_format == Some(InsertTextFormat::Snippet) { + is_snippet = Some(true); + snippet = Some(word.clone()); + } else { + is_snippet = None; + snippet = None; + }; + + let mut info = String::new(); + if let Some(ref doc) = lspitem.documentation { + info += &doc.to_string(); + } + + let user_data = VimCompleteItemUserData { + lspitem: Some(lspitem.clone()), + }; + + Ok(VimCompleteItem { + word, + abbr, + icase: Some(1), + dup: Some(1), + menu: lspitem.detail.clone().unwrap_or_default(), + info, + kind: lspitem.kind.map(|k| format!("{:?}", k)).unwrap_or_default(), + snippet, + is_snippet, + user_data: Some(serde_json::to_string(&user_data)?), + }) + } +} + +pub trait ToRpcError { + fn to_rpc_error(&self) -> rpc::Error; +} + +impl ToRpcError for Error { + fn to_rpc_error(&self) -> rpc::Error { + rpc::Error { + code: rpc::ErrorCode::InternalError, + message: self.to_string(), + data: None, + } + } +} + +pub trait ToParams { + fn to_params(self) -> Result>; +} + +impl ToParams for T +where + T: Serialize, +{ + fn to_params(self) -> Result> { + let json_value = serde_json::to_value(self)?; + + let params = match json_value { + Value::Null => None, + Value::Bool(_) | Value::Number(_) | Value::String(_) => { + Some(Params::Array(vec![json_value])) + } + Value::Array(vec) => Some(Params::Array(vec)), + Value::Object(map) => Some(Params::Map(map)), + }; + + Ok(params) + } +} + +pub trait ToInt { + fn to_int(&self) -> Result; +} + +impl<'a> ToInt for &'a str { + fn to_int(&self) -> Result { + Ok(u64::from_str(self)?) + } +} + +impl ToInt for rpc::Id { + fn to_int(&self) -> Result { + match *self { + rpc::Id::Num(id) => Ok(id), + rpc::Id::Str(ref s) => s.as_str().to_int(), + rpc::Id::Null => Err(err_msg("Null id")), + } + } +} + +pub trait ToString { + fn to_string(&self) -> String; +} + +impl ToString for lsp::MarkedString { + fn to_string(&self) -> String { + match *self { + MarkedString::String(ref s) => s.clone(), + MarkedString::LanguageString(ref ls) => ls.value.clone(), + } + } +} + +impl ToString for lsp::MarkupContent { + fn to_string(&self) -> String { + self.value.clone() + } +} + +impl ToString for Hover { + fn to_string(&self) -> String { + match self.contents { + HoverContents::Scalar(ref ms) => ms.to_string(), + HoverContents::Array(ref vec) => vec + .iter() + .map(|i| i.to_string()) + .collect::>() + .join("\n"), + HoverContents::Markup(ref mc) => mc.to_string(), + } + } +} + +impl ToString for lsp::Documentation { + fn to_string(&self) -> String { + match *self { + lsp::Documentation::String(ref s) => s.to_owned(), + lsp::Documentation::MarkupContent(ref mc) => mc.to_string(), + } + } +} + +impl ToString for NumberOrString { + fn to_string(&self) -> String { + match *self { + NumberOrString::Number(n) => format!("{}", n), + NumberOrString::String(ref s) => s.clone(), + } + } +} + +pub trait ToDisplay { + fn to_display(&self) -> Vec; +} + +impl ToDisplay for lsp::MarkedString { + fn to_display(&self) -> Vec { + match *self { + MarkedString::String(ref s) => s.lines().map(|i| i.to_string()).collect(), + MarkedString::LanguageString(ref ls) => { + let mut buf = Vec::new(); + + buf.push(format!("```{}", ls.language)); + buf.extend(ls.value.lines().map(|i| i.to_string())); + buf.push("```".to_string()); + + buf + } + } + } +} + +impl ToDisplay for MarkupContent { + fn to_display(&self) -> Vec { + self.value.lines().map(str::to_string).collect() + } +} + +impl ToDisplay for Hover { + fn to_display(&self) -> Vec { + match self.contents { + HoverContents::Scalar(ref ms) => ms.to_display(), + HoverContents::Array(ref arr) => arr.iter().flat_map(ToDisplay::to_display).collect(), + HoverContents::Markup(ref mc) => mc.to_display(), + } + } +} + +pub trait LinesLen { + fn lines_len(&self) -> usize; +} + +impl LinesLen for lsp::MarkedString { + fn lines_len(&self) -> usize { + match *self { + MarkedString::String(ref s) => s.lines().count(), + MarkedString::LanguageString(ref ls) => ls.value.lines().count(), + } + } +} + +impl LinesLen for MarkupContent { + fn lines_len(&self) -> usize { + self.value.lines().count() + } +} + +impl LinesLen for Hover { + fn lines_len(&self) -> usize { + match self.contents { + HoverContents::Scalar(ref c) => c.lines_len(), + HoverContents::Array(ref arr) => arr.iter().map(|i| i.lines_len()).sum(), + HoverContents::Markup(ref c) => c.lines_len(), + } + } +} + +pub trait DiagnosticSeverityExt { + fn to_quickfix_entry_type(&self) -> char; +} + +impl DiagnosticSeverityExt for DiagnosticSeverity { + fn to_quickfix_entry_type(&self) -> char { + match *self { + DiagnosticSeverity::Error => 'E', + DiagnosticSeverity::Warning => 'W', + DiagnosticSeverity::Information => 'I', + DiagnosticSeverity::Hint => 'H', + } + } +} + +impl ToInt for DiagnosticSeverity { + fn to_int(&self) -> Result { + Ok(*self as u64) + } +} + +impl ToInt for MessageType { + fn to_int(&self) -> Result { + Ok(*self as u64) + } +} + +impl ToInt for DocumentHighlightKind { + fn to_int(&self) -> Result { + Ok(*self as u64) + } +} + +pub trait ToUsize { + fn to_usize(&self) -> Result; +} + +impl ToUsize for u64 { + fn to_usize(&self) -> Result { + Ok(*self as usize) + } +} + +#[derive(Debug, PartialEq)] +pub enum VimVar { + Buftype, + LanguageId, + Filename, + Line, + Character, + Text, + Cword, + NewName, + GotoCmd, + Handle, + IncludeDeclaration, +} + +pub trait VimExp { + fn to_key(&self) -> String; + fn to_exp(&self) -> String; +} + +impl VimExp for VimVar { + fn to_key(&self) -> String { + match *self { + VimVar::Buftype => "buftype", + VimVar::LanguageId => "languageId", + VimVar::Filename => "filename", + VimVar::Line => "line", + VimVar::Character => "character", + VimVar::Text => "text", + VimVar::Cword => "cword", + VimVar::NewName => "newName", + VimVar::GotoCmd => "gotoCmd", + VimVar::Handle => "handle", + VimVar::IncludeDeclaration => "includeDeclaration", + }.to_owned() + } + + fn to_exp(&self) -> String { + match *self { + VimVar::Buftype => "&buftype", + VimVar::LanguageId => "&filetype", + VimVar::Filename => "LSP#filename()", + VimVar::Line => "LSP#line()", + VimVar::Character => "LSP#character()", + VimVar::Text => "LSP#text()", + VimVar::Cword => "expand('')", + VimVar::NewName | VimVar::GotoCmd => "v:null", + VimVar::Handle | VimVar::IncludeDeclaration => "v:true", + }.to_owned() + } +} + +impl<'a> VimExp for &'a str { + fn to_key(&self) -> String { + self.to_string() + } + + fn to_exp(&self) -> String { + self.to_string() + } +} + +impl VimExp for String { + fn to_key(&self) -> String { + self.clone() + } + + fn to_exp(&self) -> String { + self.clone() + } +} + +impl<'a> VimExp for (&'a str, &'a str) { + fn to_key(&self) -> String { + self.0.to_owned() + } + + fn to_exp(&self) -> String { + self.1.to_owned() + } +} + +impl<'a, T> VimExp for &'a [T] +where + T: VimExp, +{ + fn to_key(&self) -> String { + String::new() + } + + fn to_exp(&self) -> String { + let mut exp = "[".to_owned(); + for (i, e) in self.iter().enumerate() { + if i != 0 { + exp += ", "; + } + exp += &e.to_exp(); + } + exp += "]"; + exp + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct LanguageStatusParams { + #[serde(rename = "type")] + pub typee: String, + pub message: String, +} + +#[derive(Debug, Eq, PartialEq, Deserialize, Serialize, Clone)] +#[serde(untagged)] +pub enum RootMarkers { + Array(Vec), + Map(HashMap>), +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct WindowProgressParams { + pub title: Option, + pub message: Option, + pub percentage: Option, + pub done: Option, +} + +pub trait Filepath { + fn filepath(&self) -> Result; +} + +impl Filepath for Url { + fn filepath(&self) -> Result { + self.to_file_path().or_else(|_| Ok(self.as_str().into())) + } +} + +#[derive(Debug, Serialize)] +pub struct TextDocumentItemMetadata { + #[serde(skip_serializing)] + pub last_change: Instant, +} + +impl Default for TextDocumentItemMetadata { + fn default() -> Self { + Self { + last_change: Instant::now(), + } + } +} + +pub trait OptionDeref { + fn as_deref(&self) -> Option<&T::Target>; +} + +impl OptionDeref for Option { + fn as_deref(&self) -> Option<&T::Target> { + self.as_ref().map(Deref::deref) + } +} + +pub trait ToLSP { + fn to_lsp(self) -> Result; +} + +impl ToLSP> for notify::DebouncedEvent { + fn to_lsp(self) -> Result> { + match self { + notify::DebouncedEvent::Create(p) => Ok(vec![FileEvent { + uri: p.to_url()?, + typ: FileChangeType::Created, + }]), + notify::DebouncedEvent::NoticeWrite(p) | notify::DebouncedEvent::Write(p) => { + Ok(vec![FileEvent { + uri: p.to_url()?, + typ: FileChangeType::Changed, + }]) + } + notify::DebouncedEvent::NoticeRemove(p) | notify::DebouncedEvent::Remove(p) => { + Ok(vec![FileEvent { + uri: p.to_url()?, + typ: FileChangeType::Deleted, + }]) + } + notify::DebouncedEvent::Rename(p1, p2) => Ok(vec![ + FileEvent { + uri: p1.to_url()?, + typ: FileChangeType::Deleted, + }, + FileEvent { + uri: p2.to_url()?, + typ: FileChangeType::Created, + }, + ]), + notify::DebouncedEvent::Chmod(_) | notify::DebouncedEvent::Rescan => Ok(vec![]), + e @ notify::DebouncedEvent::Error(_, _) => Err(format_err!("{:?}", e)), + } + } +} + +impl ToLSP for Value +where + T: DeserializeOwned, +{ + fn to_lsp(self) -> Result { + Ok(serde_json::from_value(self)?) + } +} + +impl ToLSP for Option +where + T: DeserializeOwned, +{ + fn to_lsp(self) -> Result { + serde_json::to_value(self)?.to_lsp() + } +} + +pub trait FromLSP +where + Self: Sized, +{ + fn from_lsp(f: &F) -> Result; +} + +impl FromLSP for QuickfixEntry { + fn from_lsp(sym: &SymbolInformation) -> Result { + let start = sym.location.range.start; + + Ok(QuickfixEntry { + filename: sym.location.uri.filepath()?.to_string_lossy().into_owned(), + lnum: start.line + 1, + col: Some(start.character + 1), + text: Some(sym.name.clone()), + nr: None, + typ: None, + }) + } +} diff --git a/vim/bundle/LanguageClient-neovim/src/utils.rs b/vim/bundle/LanguageClient-neovim/src/utils.rs new file mode 100644 index 0000000..b60a80a --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/src/utils.rs @@ -0,0 +1,504 @@ +use super::*; + +pub fn escape_single_quote>(s: S) -> String { + s.as_ref().replace("'", "''") +} + +#[test] +fn test_escape_single_quote() { + assert_eq!(escape_single_quote("my' precious"), "my'' precious"); +} + +pub fn get_rootPath<'a>( + path: &'a Path, + languageId: &str, + rootMarkers: &Option, +) -> Result<&'a Path> { + if let Some(ref rootMarkers) = *rootMarkers { + let empty = vec![]; + let rootMarkers = match *rootMarkers { + RootMarkers::Array(ref arr) => arr, + RootMarkers::Map(ref map) => map.get(languageId).unwrap_or(&empty), + }; + + for marker in rootMarkers { + let ret = traverse_up(path, |dir| { + let p = dir.join(marker); + let p = p.to_str(); + if p.is_none() { + return false; + } + let p = p.unwrap_or_default(); + match glob::glob(p) { + Ok(paths) => paths.count() > 0, + _ => false, + } + }); + + if ret.is_ok() { + return ret; + } + } + } + + match languageId { + "rust" => traverse_up(path, |dir| dir.join("Cargo.toml").exists()), + "php" => traverse_up(path, |dir| dir.join("composer.json").exists()), + "javascript" | "typescript" => traverse_up(path, |dir| dir.join("package.json").exists()), + "python" => traverse_up(path, |dir| { + dir.join("setup.py").exists() + || dir.join("Pipfile").exists() + || dir.join("requirements.txt").exists() + }), + "c" | "cpp" => traverse_up(path, |dir| dir.join("compile_commands.json").exists()), + "cs" => traverse_up(path, is_dotnet_root), + "java" => traverse_up(path, |dir| { + dir.join(".project").exists() + || dir.join("pom.xml").exists() + || dir.join("build.gradle").exists() + }), + "scala" => traverse_up(path, |dir| dir.join("build.sbt").exists()), + "haskell" => traverse_up(path, |dir| dir.join("stack.yaml").exists()) + .or_else(|_| traverse_up(path, |dir| dir.join(".cabal").exists())), + _ => Err(format_err!("Unknown languageId: {}", languageId)), + }.or_else(|_| { + traverse_up(path, |dir| { + dir.join(".git").exists() || dir.join(".hg").exists() || dir.join(".svn").exists() + }) + }).or_else(|_| { + let parent = path + .parent() + .ok_or_else(|| format_err!("Failed to get parent dir! path: {:?}", path)); + warn!( + "Unknown project type. Fallback to use dir as project root: {:?}", + parent + ); + parent + }) +} + +fn traverse_up(path: &Path, predicate: F) -> Result<&Path> +where + F: Fn(&Path) -> bool, +{ + if predicate(path) { + return Ok(path); + } + + let next_path = path.parent().ok_or_else(|| err_msg("Hit root"))?; + + traverse_up(next_path, predicate) +} + +fn is_dotnet_root(dir: &Path) -> bool { + if dir.join("project.json").exists() { + return true; + } + if !dir.is_dir() { + return false; + } + + let entries = match dir.read_dir() { + Ok(entries) => entries, + Err(_) => return false, + }; + for entry in entries { + if let Ok(entry) = entry { + if entry.path().ends_with(".csproj") { + return true; + } + } + } + + false +} + +pub trait ToUrl { + fn to_url(&self) -> Result; +} + +impl + std::fmt::Debug> ToUrl for P { + fn to_url(&self) -> Result { + Url::from_file_path(self) + .or_else(|_| Url::from_str(&self.as_ref().to_string_lossy())) + .or_else(|_| Err(format_err!("Failed to convert ({:?}) to Url", self))) + } +} + +pub fn apply_TextEdits(lines: &[String], edits: &[TextEdit]) -> Result> { + // Edits are ordered from bottom to top, from right to left. + let mut edits_by_index = vec![]; + for edit in edits { + let start_line = edit.range.start.line.to_usize()?; + let start_character: usize = edit.range.start.character.to_usize()?; + let end_line: usize = edit.range.end.line.to_usize()?; + let end_character: usize = edit.range.end.character.to_usize()?; + + let start = lines[..std::cmp::min(start_line, lines.len())] + .iter() + .map(|l| l.len()) + .fold(0, |acc, l| acc + l + 1 /*line ending*/) + + start_character; + let end = lines[..std::cmp::min(end_line, lines.len())] + .iter() + .map(|l| l.len()) + .fold(0, |acc, l| acc + l + 1 /*line ending*/) + + end_character; + edits_by_index.push((start, end, &edit.new_text)); + } + + let mut text = lines.join("\n"); + for (start, end, new_text) in edits_by_index { + let start = std::cmp::min(start, text.len()); + let end = std::cmp::min(end, text.len()); + text = String::new() + &text[..start] + new_text + &text[end..]; + } + + Ok(text.lines().map(|l| l.to_owned()).collect()) +} + +#[test] +fn test_apply_TextEdit() { + let lines: Vec = r#"fn main() { +0; +} +"#.lines() + .map(|l| l.to_owned()) + .collect(); + + let expect: Vec = r#"fn main() { + 0; +} +"#.lines() + .map(|l| l.to_owned()) + .collect(); + + let edit = TextEdit { + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + new_text: r#"fn main() { + 0; +} +"#.to_owned(), + }; + + assert_eq!(apply_TextEdits(&lines, &[edit]).unwrap(), expect); +} + +#[test] +fn test_apply_TextEdit_overlong_end() { + let lines: Vec = r#"abc = 123"#.lines().map(|l| l.to_owned()).collect(); + + let expect: Vec = r#"nb = 123"#.lines().map(|l| l.to_owned()).collect(); + + let edit = TextEdit { + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 99999999, + character: 0, + }, + }, + new_text: r#"nb = 123"#.to_owned(), + }; + + assert_eq!(apply_TextEdits(&lines, &[edit]).unwrap(), expect); +} + +fn get_command_add_sign(sign: &Sign, filename: &str) -> String { + format!( + "sign place {} line={} name=LanguageClient{:?} file={}", + sign.id, + sign.line, + sign.severity.unwrap_or(DiagnosticSeverity::Hint), + filename + ) +} + +#[test] +fn test_get_command_add_sign() { + let sign = Sign::new(1, "".to_owned(), Some(DiagnosticSeverity::Error)); + assert_eq!( + get_command_add_sign(&sign, ""), + "sign place 75000 line=1 name=LanguageClientError file=" + ); + + let sign = Sign::new(7, "".to_owned(), Some(DiagnosticSeverity::Error)); + assert_eq!( + get_command_add_sign(&sign, ""), + "sign place 75024 line=7 name=LanguageClientError file=" + ); + + let sign = Sign::new(7, "".to_owned(), Some(DiagnosticSeverity::Hint)); + assert_eq!( + get_command_add_sign(&sign, ""), + "sign place 75027 line=7 name=LanguageClientHint file=" + ); +} + +fn get_command_delete_sign(sign: &Sign, filename: &str) -> String { + format!("sign unplace {} file={}", sign.id, filename) +} + +#[test] +fn test_get_command_delete_sign() {} + +use diff; + +pub fn get_command_update_signs( + signs_prev: &[Sign], + signs: &[Sign], + filename: &str, +) -> (Vec, Vec) { + // Sign id might become different due to lines shifting. Use sign's existing sign id to + // track same sign. + let mut signs_next = vec![]; + + let mut cmds = vec![]; + for comp in diff::slice(signs_prev, signs) { + match comp { + diff::Result::Left(sign) => { + cmds.push(get_command_delete_sign(sign, filename)); + } + diff::Result::Right(sign) => { + cmds.push(get_command_add_sign(sign, filename)); + signs_next.push(sign.clone()); + } + diff::Result::Both(sign, _) => { + signs_next.push(sign.clone()); + } + } + } + + (signs_next, cmds) +} + +#[test] +fn test_get_command_update_signs() { + let signs_prev = vec![Sign::new( + 1, + "abcde".to_string(), + Some(DiagnosticSeverity::Error), + )]; + let signs = vec![Sign::new( + 3, + "abcde".to_string(), + Some(DiagnosticSeverity::Error), + )]; + let (signs_next, cmds) = get_command_update_signs(&signs_prev, &signs, "f"); + assert_eq!( + serde_json::to_string(&signs_next).unwrap(), + "[{\"id\":75000,\"line\":1,\"text\":\"abcde\",\"severity\":1}]" + ); + assert!(cmds.is_empty()); +} + +pub trait Combine { + /// Recursively combine two objects. + fn combine(&self, other: &Self) -> Self + where + Self: Sized + Clone; +} + +impl Combine for Value { + fn combine(&self, other: &Self) -> Self { + match (self, other) { + (this, Value::Null) => this.clone(), + (Value::Object(this), Value::Object(other)) => { + let mut map = serde_json::Map::new(); + let mut keys: HashSet = HashSet::new(); + for k in this.keys() { + keys.insert(k.clone()); + } + for k in other.keys() { + keys.insert(k.clone()); + } + for k in keys.drain() { + let v1 = this.get(&k).unwrap_or(&Value::Null); + let v2 = other.get(&k).unwrap_or(&Value::Null); + map.insert(k, v1.combine(v2)); + } + Value::Object(map) + } + (_, other) => other.clone(), + } + } +} + +/// Expand condensed json path as in VSCode. +/// +/// e.g., +/// ```json +/// { +/// "rust.rls": true +/// } +/// ``` +/// will be expanded to +/// ```json +/// { +/// "rust": { +/// "rls": true +/// } +/// } +/// ``` +pub fn expand_json_path(value: Value) -> Value { + match value { + Value::Object(map) => { + let mut value_expanded = json!({}); + for (k, v) in map { + let mut v = v; + for token in k.rsplit('.') { + v = json!({ token: v }); + } + value_expanded = value_expanded.combine(&v); + } + value_expanded + } + _ => value, + } +} + +#[test] +fn test_expand_json_path() { + assert_eq!( + expand_json_path(json!({ + "k": "v" + })), + json!({ + "k": "v" + }) + ); + assert_eq!( + expand_json_path(json!({ + "rust.rls": true + })), + json!({ + "rust": { + "rls": true + } + }) + ); +} + +pub fn vim_cmd_args_to_value(args: &[String]) -> Result { + let mut map = serde_json::map::Map::new(); + for arg in args { + let mut tokens: Vec<_> = arg.splitn(2, '=').collect(); + tokens.reverse(); + let key = tokens.pop().ok_or_else(|| { + format_err!("Failed to parse command arguments! tokens: {:?}", tokens) + })?; + let value = tokens.pop().ok_or_else(|| { + format_err!("Failed to parse command arguments! tokens: {:?}", tokens) + })?; + let value = Value::String(value.to_owned()); + map.insert(key.to_owned(), value); + } + + Ok(Value::Object(map)) +} + +#[test] +fn test_vim_cmd_args_to_value() { + let cmdargs = ["rootPath=/tmp".to_owned()]; + assert_eq!( + vim_cmd_args_to_value(&cmdargs).unwrap(), + json!({ + "rootPath": "/tmp" + }) + ); +} + +pub fn diff_value<'a>(v1: &'a Value, v2: &'a Value, path: &str) -> HashMap { + let mut diffs = HashMap::new(); + match (v1, v2) { + (&Value::Null, &Value::Null) + | (&Value::Bool(_), &Value::Bool(_)) + | (&Value::Number(_), &Value::Number(_)) + | (&Value::String(_), &Value::String(_)) + | (&Value::Array(_), &Value::Array(_)) => if v1 != v2 { + diffs.insert(path.to_owned(), (v1.clone(), v2.clone())); + }, + (&Value::Object(ref map1), &Value::Object(ref map2)) => { + let keys1: HashSet<&String> = map1.keys().collect(); + let keys2: HashSet<&String> = map2.keys().collect(); + for k in keys1.union(&keys2) { + let mut next_path = String::from(path); + next_path += "."; + next_path += k; + let next_diffs = diff_value( + map1.get(*k).unwrap_or(&Value::Null), + map2.get(*k).unwrap_or(&Value::Null), + &next_path, + ); + diffs.extend(next_diffs); + } + } + _ => { + diffs.insert(path.to_owned(), (v1.clone(), v2.clone())); + } + } + + diffs +} + +#[test] +fn test_diff_value() { + assert_eq!(diff_value(&json!({}), &json!({}), "state",), hashmap!()); + assert_eq!( + diff_value( + &json!({ + "line": 1, + }), + &json!({ + "line": 3, + }), + "state" + ), + hashmap!{ + "state.line".to_owned() => (json!(1), json!(3)), + } + ); +} + +pub trait Canonicalize { + fn canonicalize(&self) -> String; +} + +impl

Canonicalize for P +where + P: AsRef, +{ + fn canonicalize(&self) -> String { + let path = match std::fs::canonicalize(self) { + Ok(path) => path.to_string_lossy().into_owned(), + _ => self.as_ref().to_string_lossy().into_owned(), + }; + + // Trim UNC prefixes. + // See https://github.com/rust-lang/rust/issues/42869 + path.trim_left_matches("\\\\?\\").into() + } +} + +pub fn get_default_initializationOptions(languageId: &str) -> Value { + match languageId { + "java" => json!({ + "extendedClientCapabilities": { + "classFileContentsSupport": true + } + }), + _ => json!(Value::Null), + } +} diff --git a/vim/bundle/LanguageClient-neovim/src/vim.rs b/vim/bundle/LanguageClient-neovim/src/vim.rs new file mode 100644 index 0000000..f47ee00 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/src/vim.rs @@ -0,0 +1,406 @@ +use super::*; + +impl State { + fn poll_call(&mut self) -> Result { + if let Some(msg) = self.pending_calls.pop_front() { + return Ok(msg); + } + + loop { + let msg = self.rx.recv()?; + match msg { + Message::MethodCall(lang_id, method_call) => { + return Ok(Call::MethodCall(lang_id, method_call)); + } + Message::Notification(lang_id, notification) => { + return Ok(Call::Notification(lang_id, notification)); + } + Message::Output(output) => { + let mid = output.id().to_int()?; + self.pending_outputs.insert(mid, output); + } + } + } + } + + fn poll_output(&mut self, id: Id) -> Result { + if let Some(output) = self.pending_outputs.remove(&id) { + return Ok(output); + } + + loop { + let msg = self.rx.recv_timeout(self.wait_output_timeout)?; + match msg { + Message::MethodCall(lang_id, method_call) => self + .pending_calls + .push_back(Call::MethodCall(lang_id, method_call)), + Message::Notification(lang_id, notification) => self + .pending_calls + .push_back(Call::Notification(lang_id, notification)), + Message::Output(output) => { + let mid = output.id().to_int()?; + if mid == id { + return Ok(output); + } else { + self.pending_outputs.insert(mid, output); + } + } + } + } + } + + pub fn loop_message(&mut self) -> Result<()> { + loop { + match self.poll_call()? { + Call::MethodCall(lang_id, method_call) => { + let result = self.handle_method_call(lang_id.as_deref(), &method_call); + if let Err(ref err) = result { + if err.downcast_ref::().is_none() { + error!( + "Error handling message: {}\n\nMessage: {}\n\nError: {:?}", + err, + serde_json::to_string(&method_call).unwrap_or_default(), + err + ); + } + } + let _ = self.output(lang_id.as_deref(), method_call.id, result); + } + Call::Notification(lang_id, notification) => { + let result = self.handle_notification(lang_id.as_deref(), ¬ification); + if let Err(ref err) = result { + if err.downcast_ref::().is_none() { + error!( + "Error handling message: {}\n\nMessage: {}\n\nError: {:?}", + err, + serde_json::to_string(¬ification).unwrap_or_default(), + err + ); + } + } + } + } + + if let Err(err) = self.handle_fs_events() { + warn!("{:?}", err); + } + } + } + + /// Send message to RPC server. + fn write(&mut self, languageId: Option<&str>, message: &str) -> Result<()> { + info!("=> {:?} {}", languageId, message); + if let Some(languageId) = languageId { + let writer = self + .writers + .get_mut(languageId) + .ok_or(LCError::NoLanguageServer { + languageId: languageId.to_owned(), + })?; + write!( + writer, + "Content-Length: {}\r\n\r\n{}", + message.len(), + message + )?; + writer.flush()?; + } else { + println!("Content-Length: {}\n\n{}", message.len(), message); + } + + Ok(()) + } + + /// RPC method call. + pub fn call(&mut self, languageId: Option<&str>, method: &str, params: P) -> Result + where + P: Serialize, + V: DeserializeOwned, + { + self.id += 1; + let id = self.id; + + let method_call = rpc::MethodCall { + jsonrpc: Some(rpc::Version::V2), + id: rpc::Id::Num(id), + method: method.into(), + params: params.to_params()?, + }; + + let message = serde_json::to_string(&method_call)?; + self.write(languageId, &message)?; + + match self.poll_output(id)? { + rpc::Output::Success(success) => Ok(serde_json::from_value(success.result)?), + rpc::Output::Failure(failure) => Err(format_err!("{}", failure.error.message)), + } + } + + /// RPC notification. + pub fn notify

(&mut self, languageId: Option<&str>, method: &str, params: P) -> Result<()> + where + P: Serialize, + { + let notification = rpc::Notification { + jsonrpc: Some(rpc::Version::V2), + method: method.to_owned(), + params: params.to_params()?, + }; + + let message = serde_json::to_string(¬ification)?; + self.write(languageId, &message)?; + + Ok(()) + } + + /// Write an RPC call output. + fn output( + &mut self, + languageId: Option<&str>, + id: rpc::Id, + result: Result, + ) -> Result<()> { + let response = match result { + Ok(ok) => rpc::Output::Success(rpc::Success { + jsonrpc: Some(rpc::Version::V2), + id, + result: ok, + }), + Err(err) => rpc::Output::Failure(rpc::Failure { + jsonrpc: Some(rpc::Version::V2), + id, + error: err.to_rpc_error(), + }), + }; + + let message = serde_json::to_string(&response)?; + self.write(languageId, &message)?; + Ok(()) + } + + /////// Vim wrappers /////// + + #[allow(needless_pass_by_value)] + pub fn eval(&mut self, exp: E) -> Result + where + E: VimExp, + T: DeserializeOwned, + { + let result = self.call(None, "eval", exp.to_exp())?; + Ok(serde_json::from_value(result)?) + } + + pub fn command(&mut self, cmds: P) -> Result<()> { + if self.call::<_, u8>(None, "execute", &cmds)? != 0 { + bail!("Failed to execute command: {:?}", cmds); + } + Ok(()) + } + + ////// Vim builtin function wrappers /////// + + pub fn echo(&mut self, message: S) -> Result<()> + where + S: AsRef + Serialize, + { + self.notify(None, "s:Echo", message) + } + + pub fn echo_ellipsis>(&mut self, message: S) -> Result<()> { + let message = message.as_ref().lines().collect::>().join(" "); + self.notify(None, "s:EchoEllipsis", message) + } + + pub fn echomsg_ellipsis>(&mut self, message: S) -> Result<()> { + let message = message.as_ref().lines().collect::>().join(" "); + self.notify(None, "s:EchomsgEllipsis", message) + } + + pub fn echomsg(&mut self, message: S) -> Result<()> + where + S: AsRef + Serialize, + { + self.notify(None, "s:Echomsg", message) + } + + pub fn echoerr(&mut self, message: S) -> Result<()> + where + S: AsRef + Serialize, + { + self.notify(None, "s:Echoerr", message) + } + + pub fn echowarn(&mut self, message: S) -> Result<()> + where + S: AsRef + Serialize, + { + self.notify(None, "s:Echowarn", message) + } + + pub fn cursor(&mut self, lnum: u64, col: u64) -> Result<()> { + self.notify(None, "cursor", json!([lnum, col])) + } + + pub fn setline(&mut self, lnum: u64, text: &[String]) -> Result<()> { + if self.call::<_, u8>(None, "setline", json!([lnum, text]))? != 0 { + bail!("Failed to set buffer content!"); + } + Ok(()) + } + + pub fn edit>(&mut self, goto_cmd: &Option, path: P) -> Result<()> { + let path = path.as_ref().to_string_lossy(); + + let goto = goto_cmd.as_deref().unwrap_or("edit"); + if self.call::<_, u8>(None, "s:Edit", json!([goto, path]))? != 0 { + bail!("Failed to edit file: {}", path); + } + + if path.starts_with("jdt://") { + self.command("setlocal buftype=nofile filetype=java noswapfile")?; + + let result = self.java_classFileContents(&json!({ + VimVar::LanguageId.to_key(): "java", + "uri": path, + }))?; + let content = match result { + Value::String(s) => s, + _ => bail!("Unexpected type: {:?}", result), + }; + let lines: Vec = content + .lines() + .map(std::string::ToString::to_string) + .collect(); + self.setline(1, &lines)?; + } + Ok(()) + } + + pub fn setqflist(&mut self, list: &[QuickfixEntry]) -> Result<()> { + if self.call::<_, u8>(None, "setqflist", json!([list, "r"]))? != 0 { + bail!("Failed to set quickfix list!"); + } + Ok(()) + } + + pub fn setloclist(&mut self, list: &[QuickfixEntry]) -> Result<()> { + if self.call::<_, u8>(None, "setloclist", json!([0, list, "r"]))? != 0 { + bail!("Failed to set location list!"); + } + Ok(()) + } + + pub fn get(&self, f: F) -> Result + where + F: FnOnce(&State) -> Result, + { + f(self) + } + + pub fn update(&mut self, f: F) -> Result + where + F: FnOnce(&mut State) -> Result, + { + use log::Level; + + let before = if log_enabled!(Level::Debug) { + let s = serde_json::to_string(self)?; + serde_json::from_str(&s)? + } else { + Value::default() + }; + let result = f(self); + let after = if log_enabled!(Level::Debug) { + let s = serde_json::to_string(self)?; + serde_json::from_str(&s)? + } else { + Value::default() + }; + for (k, (v1, v2)) in diff_value(&before, &after, "state") { + debug!("{}: {} ==> {}", k, v1, v2); + } + result + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +enum RawMessage { + Notification(rpc::Notification), + MethodCall(rpc::MethodCall), + Output(rpc::Output), +} + +pub fn loop_reader( + input: T, + languageId: &Option, + tx: &Sender, +) -> Result<()> { + // Count how many consequent empty lines. + let mut count_empty_lines = 0; + + let mut input = input; + let mut content_length = 0; + loop { + let mut message = String::new(); + let mut line = String::new(); + if languageId.is_some() { + input.read_line(&mut line)?; + let line = line.trim(); + if line.is_empty() { + count_empty_lines += 1; + if count_empty_lines > 5 { + bail!("Unable to read from language server"); + } + + let mut buf = vec![0; content_length]; + input.read_exact(buf.as_mut_slice())?; + message = String::from_utf8(buf)?; + } else { + count_empty_lines = 0; + if !line.starts_with("Content-Length") { + continue; + } + + let tokens: Vec<&str> = line.splitn(2, ':').collect(); + let len = tokens + .get(1) + .ok_or_else(|| format_err!("Failed to get length! tokens: {:?}", tokens))? + .trim(); + content_length = usize::from_str(len)?; + } + } else if input.read_line(&mut message)? == 0 { + break; + } + + let message = message.trim(); + if message.is_empty() { + continue; + } + info!("<= {:?} {}", languageId, message); + // FIXME: Remove extra `meta` property from javascript-typescript-langserver. + let s = message.replace(r#","meta":{}"#, ""); + let message = serde_json::from_str(&s); + if let Err(ref err) = message { + error!( + "Failed to deserialize output: {}\n\n Message: {}\n\nError: {:?}", + err, s, err + ); + continue; + } + // TODO: cleanup. + let message = message.unwrap(); + let message = match message { + RawMessage::MethodCall(method_call) => { + Message::MethodCall(languageId.clone(), method_call) + } + RawMessage::Notification(notification) => { + Message::Notification(languageId.clone(), notification) + } + RawMessage::Output(output) => Message::Output(output), + }; + tx.send(message)?; + } + + Ok(()) +} diff --git a/vim/bundle/LanguageClient-neovim/tests/LanguageClient_test.py b/vim/bundle/LanguageClient-neovim/tests/LanguageClient_test.py new file mode 100644 index 0000000..fb2b58c --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/LanguageClient_test.py @@ -0,0 +1,210 @@ +import os +import time +import threading +import neovim +import pytest + + +threading.current_thread().name = "Test" + + +NVIM_LISTEN_ADDRESS = "/tmp/nvim-LanguageClient-IntegrationTest" + + +project_root = os.path.dirname(os.path.abspath(__file__)) + + +def join_path(path: str) -> str: + """Join path to this project tests root.""" + return os.path.join(project_root, path) + + +PATH_INDEXJS = join_path("data/sample-js/src/index.js") +PATH_LIBSJS = join_path("data/sample-js/src/libs.js") +PATH_CODEACTION = join_path("data/sample-ts/src/codeAction.ts") +print(PATH_INDEXJS) + + +def assertRetry(predicate, retry_max=100): + retry_delay = 0.1 + retry_count = 0 + + while retry_count < retry_max: + if predicate(): + return + else: + retry_count += 1 + time.sleep(retry_delay) + assert predicate() + + +@pytest.fixture(scope="module") +def nvim() -> neovim.Nvim: + nvim = neovim.attach("socket", path=NVIM_LISTEN_ADDRESS) + time.sleep(1) + nvim.command("edit! {}".format(PATH_INDEXJS)) + time.sleep(3) + return nvim + + +def test_fixture(nvim): + pass + + +def test_textDocument_hover(nvim): + nvim.command("edit! {}".format(PATH_INDEXJS)) + time.sleep(1) + nvim.funcs.cursor(13, 19) + nvim.funcs.LanguageClient_textDocument_hover() + time.sleep(1) + b = next(b for b in nvim.buffers if b.name.endswith('__LanguageClient__')) + expect = "function greet(): number" + + assert expect in b + + +def test_textDocument_definition(nvim): + nvim.command("edit! {}".format(PATH_INDEXJS)) + time.sleep(1) + nvim.funcs.cursor(13, 19) + nvim.funcs.LanguageClient_textDocument_definition() + time.sleep(1) + + assert nvim.current.window.cursor == [7, 9] + + +def test_textDocument_rename(nvim): + nvim.command("edit! {}".format(PATH_INDEXJS)) + time.sleep(1) + expect = [line.replace("greet", "hello") for line in nvim.current.buffer] + nvim.funcs.cursor(13, 19) + nvim.funcs.LanguageClient_textDocument_rename({"newName": "hello"}) + time.sleep(1) + + assert nvim.current.buffer[:] == expect + + nvim.command("edit! {}".format(PATH_INDEXJS)) + + +def test_textDocument_rename_multiple_oneline(nvim): + nvim.command("edit! {}".format(PATH_LIBSJS)) + time.sleep(1) + nvim.funcs.cursor(7, 11) + expect = [line.replace("a", "abc") for line in nvim.current.buffer] + nvim.funcs.LanguageClient_textDocument_rename({"newName": "abc"}) + time.sleep(1) + + assert nvim.current.buffer[:] == expect + + nvim.command("bd!") + nvim.command("edit! {}".format(PATH_INDEXJS)) + + +def test_textDocument_rename_multiple_files(nvim): + nvim.command("edit! {}".format(PATH_INDEXJS)) + time.sleep(1) + nvim.funcs.cursor(20, 5) + expect = [line.replace("yo", "hello") for line in nvim.current.buffer] + nvim.funcs.LanguageClient_textDocument_rename({"newName": "hello"}) + time.sleep(1) + + assert nvim.current.buffer[:] == expect + + nvim.command("bd!") + nvim.command("bd!") + nvim.command("edit! {}".format(PATH_INDEXJS)) + + +def test_textDocument_documentSymbol(nvim): + nvim.command("edit! {}".format(PATH_INDEXJS)) + time.sleep(1) + nvim.funcs.cursor(1, 1) + nvim.funcs.LanguageClient_textDocument_documentSymbol() + time.sleep(1) + + assert nvim.funcs.getloclist(0) + + nvim.command("4lnext") + + assert nvim.current.window.cursor == [19, 0] + + +def test_workspace_symbol(nvim): + nvim.command("edit! {}".format(PATH_LIBSJS)) + time.sleep(1) + nvim.funcs.cursor(1, 1) + nvim.funcs.LanguageClient_workspace_symbol() + time.sleep(1) + + assert nvim.funcs.getloclist(0) + + nvim.command("5lnext") + + assert nvim.current.window.cursor == [7, 0] + + +def test_textDocument_references(nvim): + nvim.command("edit! {}".format(PATH_INDEXJS)) + time.sleep(1) + nvim.funcs.cursor(7, 12) + nvim.funcs.LanguageClient_textDocument_references() + time.sleep(1) + expect = ["function greet() {", + """console.log(greet());"""] + + assert [location["text"] for location in + nvim.funcs.getloclist(0)] == expect + + nvim.command("lnext") + + assert nvim.current.window.cursor == [13, 16] + + +def test_textDocument_references_modified_buffer(nvim): + nvim.command("edit! {}".format(PATH_INDEXJS)) + time.sleep(1) + nvim.funcs.cursor(7, 10) + nvim.input("iabc") + time.sleep(1) + nvim.funcs.LanguageClient_textDocument_references() + time.sleep(1) + + assert nvim.current.window.cursor == [7, 9] + + nvim.command("edit! {}".format(PATH_INDEXJS)) + + +def test_languageClient_registerServerCommands(nvim): + nvim.command('let g:responses = []') + nvim.command("call LanguageClient_registerServerCommands(" + "{'bash': ['bash']}, g:responses)") + time.sleep(1) + assert nvim.vars['responses'][0]['result'] is None + + +def test_languageClient_registerHandlers(nvim): + nvim.command('let g:responses = []') + nvim.command("call LanguageClient_registerHandlers(" + "{'window/progress': 'HandleWindowProgress'}, g:responses)") + time.sleep(1) + assert nvim.vars['responses'][0]['result'] is None + + +# def test_languageClient_textDocument_codeAction(nvim): +# nvim.command("edit {}".format(PATH_CODEACTION)) +# nvim.funcs.cursor(4, 14) +# assertRetry(lambda: len(nvim.funcs.getqflist()) == 1) + +# nvim.funcs.LanguageClient_textDocument_codeAction() +# # Wait for fzf window showup. +# assertRetry(lambda: +# next((b for b in nvim.buffers +# if b.name.startswith('term://')), None) is not None) +# time.sleep(0.2) +# nvim.eval('feedkeys("\")') +# # Wait for fzf window dismiss. +# assertRetry(lambda: +# next((b for b in nvim.buffers +# if b.name.startswith('term://')), None) is None) + +# assertRetry(lambda: len(nvim.funcs.getqflist()) == 0) diff --git a/vim/bundle/LanguageClient-neovim/tests/data/.vim/autoload/.gitignore b/vim/bundle/LanguageClient-neovim/tests/data/.vim/autoload/.gitignore new file mode 100644 index 0000000..43c9cab --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/.vim/autoload/.gitignore @@ -0,0 +1 @@ +plug.vim diff --git a/vim/bundle/LanguageClient-neovim/tests/data/.vimrc b/vim/bundle/LanguageClient-neovim/tests/data/.vimrc new file mode 100644 index 0000000..bfd5769 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/.vimrc @@ -0,0 +1 @@ +let g:LanguageClient_loggingLevel = 'INFO' diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-cpp/.gitignore b/vim/bundle/LanguageClient-neovim/tests/data/sample-cpp/.gitignore new file mode 100644 index 0000000..9bf3746 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-cpp/.gitignore @@ -0,0 +1 @@ +compile_commands.json diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-cpp/.vim/settings.json b/vim/bundle/LanguageClient-neovim/tests/data/sample-cpp/.vim/settings.json new file mode 100644 index 0000000..29b1944 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-cpp/.vim/settings.json @@ -0,0 +1,5 @@ +{ + "initializationOptions": { + "cacheDirectory": "/tmp/cquery" + } +} diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-cpp/CMakeLists.txt b/vim/bundle/LanguageClient-neovim/tests/data/sample-cpp/CMakeLists.txt new file mode 100644 index 0000000..c4618ff --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-cpp/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required (VERSION 2.6) +project (sample) +add_executable(sample main.cpp) diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-cpp/glfwtest.cpp b/vim/bundle/LanguageClient-neovim/tests/data/sample-cpp/glfwtest.cpp new file mode 100644 index 0000000..e06115b --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-cpp/glfwtest.cpp @@ -0,0 +1,9 @@ +#include +#include + +int main() +{ + glfwInit(); + std::this_thread::sleep_for(std::chrono::seconds(1)); + glfwTerminate(); +} diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-cpp/include-completion.c b/vim/bundle/LanguageClient-neovim/tests/data/sample-cpp/include-completion.c new file mode 100644 index 0000000..72f5619 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-cpp/include-completion.c @@ -0,0 +1,5 @@ +#include + +class Foo { + public: + int bar; +}; + +int main(int argc, char* argv[]) { + Foo foo; + foo. + return 0; +} diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-cs/Solution.cs b/vim/bundle/LanguageClient-neovim/tests/data/sample-cs/Solution.cs new file mode 100644 index 0000000..38029c2 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-cs/Solution.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Diagnostics; +using Xunit; +using Shouldly; + +public class Solution +{ + [Fact] + public void Test() + { + 1.Sho + } +} diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-cs/Solution.csproj b/vim/bundle/LanguageClient-neovim/tests/data/sample-cs/Solution.csproj new file mode 100644 index 0000000..fded16d --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-cs/Solution.csproj @@ -0,0 +1,14 @@ + + + + netcoreapp1.1 + + + + + + + + + + diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/.formatter.exs b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/.gitignore b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/.gitignore new file mode 100644 index 0000000..77e8881 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/.gitignore @@ -0,0 +1,24 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where 3rd-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +sample-*.tar + diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/README.md b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/README.md new file mode 100644 index 0000000..8477512 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/README.md @@ -0,0 +1,21 @@ +# Sample + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `sample` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:sample, "~> 0.1.0"} + ] +end +``` + +Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) +and published on [HexDocs](https://hexdocs.pm). Once published, the docs can +be found at [https://hexdocs.pm/sample](https://hexdocs.pm/sample). + diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/config/config.exs b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/config/config.exs new file mode 100644 index 0000000..f5450a7 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/config/config.exs @@ -0,0 +1,30 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Mix.Config module. +use Mix.Config + +# This configuration is loaded before any dependency and is restricted +# to this project. If another project depends on this project, this +# file won't be loaded nor affect the parent project. For this reason, +# if you want to provide default values for your application for +# 3rd-party users, it should be done in your "mix.exs" file. + +# You can configure your application as: +# +# config :sample, key: :value +# +# and access this configuration in your application as: +# +# Application.get_env(:sample, :key) +# +# You can also configure a 3rd-party app: +# +# config :logger, level: :info +# + +# It is also possible to import configuration files, relative to this +# directory. For example, you can emulate configuration per environment +# by uncommenting the line below and defining dev.exs, test.exs and such. +# Configuration from the imported file will override the ones defined +# here (which is why it is important to import them last). +# +# import_config "#{Mix.env()}.exs" diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/lib/sample.ex b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/lib/sample.ex new file mode 100644 index 0000000..bf339fa --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/lib/sample.ex @@ -0,0 +1,32 @@ +defmodule Sample do + @moduledoc """ + Documentation for Sample. + """ + + @doc """ + Hello world. + + ## Examples + + iex> Sample.hello() + :world + + """ + def hello do + :world + end +end + +defmodule Testing.Test do + def aaa(<<"coolish", _rest::binary>>) do + nil + end + + def bbb() do + nil + end + + def ccc(%{id: id}) do + + end +end diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/mix.exs b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/mix.exs new file mode 100644 index 0000000..b907e41 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/mix.exs @@ -0,0 +1,28 @@ +defmodule Sample.MixProject do + use Mix.Project + + def project do + [ + app: :sample, + version: "0.1.0", + elixir: "~> 1.7", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # {:dep_from_hexpm, "~> 0.3.0"}, + # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}, + ] + end +end diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/test/sample_test.exs b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/test/sample_test.exs new file mode 100644 index 0000000..5d82ba2 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/test/sample_test.exs @@ -0,0 +1,8 @@ +defmodule SampleTest do + use ExUnit.Case + doctest Sample + + test "greets the world" do + assert Sample.hello() == :world + end +end diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/test/test_helper.exs b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-elixir/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-go/simple.go b/vim/bundle/LanguageClient-neovim/tests/data/sample-go/simple.go new file mode 100644 index 0000000..103022d --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-go/simple.go @@ -0,0 +1 @@ +package main; var a int diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-hs/main.hs b/vim/bundle/LanguageClient-neovim/tests/data/sample-hs/main.hs new file mode 100644 index 0000000..fd5f923 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-hs/main.hs @@ -0,0 +1,3 @@ +main :: IO() + +main = putStrLn "hello" diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-html/index.html b/vim/bundle/LanguageClient-neovim/tests/data/sample-html/index.html new file mode 100644 index 0000000..52687dd --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-html/index.html @@ -0,0 +1,9 @@ + + + + Sample HTML + + + + + diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-java/.gitignore b/vim/bundle/LanguageClient-neovim/tests/data/sample-java/.gitignore new file mode 100644 index 0000000..1ff0378 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-java/.gitignore @@ -0,0 +1 @@ +.metadata diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-java/pom.xml b/vim/bundle/LanguageClient-neovim/tests/data/sample-java/pom.xml new file mode 100644 index 0000000..1798094 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-java/pom.xml @@ -0,0 +1,18 @@ + + 4.0.0 + app + sample-java + jar + 1.0-SNAPSHOT + sample-java + http://maven.apache.org + + + junit + junit + 3.8.1 + test + + + diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-java/src/main/java/app/App.java b/vim/bundle/LanguageClient-neovim/tests/data/sample-java/src/main/java/app/App.java new file mode 100644 index 0000000..7471c3f --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-java/src/main/java/app/App.java @@ -0,0 +1,13 @@ +package app; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + } +} diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-java/src/test/java/app/AppTest.java b/vim/bundle/LanguageClient-neovim/tests/data/sample-java/src/test/java/app/AppTest.java new file mode 100644 index 0000000..aab629b --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-java/src/test/java/app/AppTest.java @@ -0,0 +1,38 @@ +package app; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigorous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-js/package.json b/vim/bundle/LanguageClient-neovim/tests/data/sample-js/package.json new file mode 100644 index 0000000..768b182 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-js/package.json @@ -0,0 +1,6 @@ +{ + "name": "sample-js", + "version": "1.0.0", + "main": "index.js", + "license": "MIT" +} diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-js/src/index.js b/vim/bundle/LanguageClient-neovim/tests/data/sample-js/src/index.js new file mode 100644 index 0000000..24a1722 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-js/src/index.js @@ -0,0 +1,21 @@ +import {yo} from './libs'; + +/** + * Get the number + * @return {number} The number. + */ +function greet() { + 42 +} + +function main() { + const a = 1; + console.log(greet()); + // place holder for didChange test. +} + +// place holder for didChange test. + +function ref_in_main() { + yo() + 1 +} diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-js/src/libs.js b/vim/bundle/LanguageClient-neovim/tests/data/sample-js/src/libs.js new file mode 100644 index 0000000..639922d --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-js/src/libs.js @@ -0,0 +1,10 @@ + +/** + * Get the number. + * @return {number} The number. + */ +export function yo() { + const a = 1; + const b = a + a; + 42 +} diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-php/composer.json b/vim/bundle/LanguageClient-neovim/tests/data/sample-php/composer.json new file mode 100644 index 0000000..8be33ef --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-php/composer.json @@ -0,0 +1,10 @@ +{ + "name": "lijunfen/tests", + "authors": [ + { + "name": "Junfeng Li", + "email": "lijunfen@amazon.com" + } + ], + "require": {} +} diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-php/main.php b/vim/bundle/LanguageClient-neovim/tests/data/sample-php/main.php new file mode 100644 index 0000000..9229fe2 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-php/main.php @@ -0,0 +1,9 @@ + diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-purescript/.gitignore b/vim/bundle/LanguageClient-neovim/tests/data/sample-purescript/.gitignore new file mode 100644 index 0000000..20e090a --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-purescript/.gitignore @@ -0,0 +1,9 @@ +/bower_components/ +/node_modules/ +/.pulp-cache/ +/output/ +/generated-docs/ +/.psc-package/ +/.psc* +/.purs* +/.psa* diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-purescript/bower.json b/vim/bundle/LanguageClient-neovim/tests/data/sample-purescript/bower.json new file mode 100644 index 0000000..b86d5ff --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-purescript/bower.json @@ -0,0 +1,17 @@ +{ + "name": "sample-purescript", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "output" + ], + "dependencies": { + "purescript-prelude": "^4.1.0", + "purescript-console": "^4.1.0", + "purescript-effect": "^2.0.0" + }, + "devDependencies": { + "purescript-psci-support": "^4.0.0" + } +} diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-purescript/src/Main.purs b/vim/bundle/LanguageClient-neovim/tests/data/sample-purescript/src/Main.purs new file mode 100644 index 0000000..ed7a4b9 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-purescript/src/Main.purs @@ -0,0 +1,9 @@ +module Main where + +import Prelude +import Effect (Effect) +import Effect.Console (log) + +main :: Effect Unit +main = do + log "Hello sailor!" diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-purescript/test/Main.purs b/vim/bundle/LanguageClient-neovim/tests/data/sample-purescript/test/Main.purs new file mode 100644 index 0000000..bbe367f --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-purescript/test/Main.purs @@ -0,0 +1,9 @@ +module Test.Main where + +import Prelude +import Effect (Effect) +import Effect.Console (log) + +main :: Effect Unit +main = do + log "You should add some tests." diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-py/sample.py b/vim/bundle/LanguageClient-neovim/tests/data/sample-py/sample.py new file mode 100644 index 0000000..2d9e5b1 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-py/sample.py @@ -0,0 +1,32 @@ +import sys + + +def greet(name: str) -> str: + """ + Greetings! + """ + return "Yo, " + name + + +greet("there") + +sys.stdin + + +# Multiple definitions +class Test1: + def foo(self, x): + print("Test1 %d" % x) + + +class Test2: + def foo(self, x): + print("Test2 %d" % x) + + +def bar(some_test_object): + some_test_object.foo(42) + + +bar(Test1()) +bar(Test2()) diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-py/setup.py b/vim/bundle/LanguageClient-neovim/tests/data/sample-py/setup.py new file mode 100644 index 0000000..e69de29 diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-rs/.gitignore b/vim/bundle/LanguageClient-neovim/tests/data/sample-rs/.gitignore new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-rs/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-rs/.vim/settings.json b/vim/bundle/LanguageClient-neovim/tests/data/sample-rs/.vim/settings.json new file mode 100644 index 0000000..63a6696 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-rs/.vim/settings.json @@ -0,0 +1,3 @@ +{ + "rust.all_features": true +} diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-rs/Cargo.toml b/vim/bundle/LanguageClient-neovim/tests/data/sample-rs/Cargo.toml new file mode 100644 index 0000000..6273c47 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-rs/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "sample" +version = "0.1.0" +authors = ["Junfeng Li "] + +[features] +f42 = [] + +[dependencies] diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-rs/src/libs.rs b/vim/bundle/LanguageClient-neovim/tests/data/sample-rs/src/libs.rs new file mode 100644 index 0000000..52d772f --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-rs/src/libs.rs @@ -0,0 +1,6 @@ + +pub fn yo() -> i32 { + let a = 1; + let b = a + a; + 42 +} diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-rs/src/main.rs b/vim/bundle/LanguageClient-neovim/tests/data/sample-rs/src/main.rs new file mode 100644 index 0000000..9c4f212 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-rs/src/main.rs @@ -0,0 +1,23 @@ +fn main() { + let a = 1; + println!("{}", greet()); + // place holder for didChange test. +} + + +fn greet() -> i32 { + 42 +} + +// place holder for didChange test. + +mod libs; +use libs::yo; +fn ref_in_main() -> i32 { + yo() + 1 +} + +#[cfg(feature = "f42")] +fn conditional_fn() -> i32 { + 42 +} diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-ts/index.ts b/vim/bundle/LanguageClient-neovim/tests/data/sample-ts/index.ts new file mode 100644 index 0000000..9a87927 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-ts/index.ts @@ -0,0 +1 @@ +[1, 2].length diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-ts/package.json b/vim/bundle/LanguageClient-neovim/tests/data/sample-ts/package.json new file mode 100644 index 0000000..c5d23da --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-ts/package.json @@ -0,0 +1,6 @@ +{ + "name": "sample-ts", + "version": "1.0.0", + "main": "index.js", + "license": "MIT" +} diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-ts/src/codeAction.ts b/vim/bundle/LanguageClient-neovim/tests/data/sample-ts/src/codeAction.ts new file mode 100644 index 0000000..438d019 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-ts/src/codeAction.ts @@ -0,0 +1,8 @@ +class A { + missingThis: number; + constructor() { + missingThis = 33; + } +} + +const a = new A(); diff --git a/vim/bundle/LanguageClient-neovim/tests/data/sample-yaml/ansible.yaml b/vim/bundle/LanguageClient-neovim/tests/data/sample-yaml/ansible.yaml new file mode 100644 index 0000000..6d3aa68 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/sample-yaml/ansible.yaml @@ -0,0 +1,3 @@ +--- +- name: Install docker + y diff --git a/vim/bundle/LanguageClient-neovim/tests/data/vimrc b/vim/bundle/LanguageClient-neovim/tests/data/vimrc new file mode 100644 index 0000000..79a1f1c --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/data/vimrc @@ -0,0 +1,35 @@ +set nocompatible + +let curdir = expand(':p:h') +execute 'set runtimepath+=' . curdir . '/.vim' +call plug#begin(curdir . '/.vim/plugged') + +Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } +Plug 'junegunn/fzf.vim' +Plug expand(':p:h:h:h') + +call plug#end() + +set noswapfile +set hidden + +autocmd BufRead *.ts setlocal filetype=typescript +autocmd BufRead *.rs setlocal filetype=rust + +let g:LanguageClient_devel = 1 +let g:LanguageClient_loggingLevel = 'INFO' +let g:LanguageClient_loggingFile = expand('~/.local/share/nvim/LanguageClient.log') +let g:LanguageClient_serverStderr = expand('~/.local/share/nvim/LanguageServer.log') +let g:LanguageClient_serverCommands = { + \ 'javascript': ['javascript-typescript-stdio'], + \ 'typescript': ['javascript-typescript-stdio'], + \ 'rust': ['rustup', 'run', 'stable', 'rls'], + \ } +let g:LanguageClient_selectionUI = 'location-list' +set formatexpr=LanguageClient#textDocument_rangeFormatting_sync() +" Not supported by neovim in debian +" set signcolumn=yes + +function! HandleWindowProgress(params) abort + echomsg json_encode(a:params) +endfunction diff --git a/vim/bundle/LanguageClient-neovim/tests/test.sh b/vim/bundle/LanguageClient-neovim/tests/test.sh new file mode 100755 index 0000000..d790355 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/test.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +set -o xtrace + +dir=$(dirname $(dirname $(realpath $0))) +cd $dir + +LOG=~/.local/share/nvim/LanguageClient.log + +curl -fLo tests/data/.vim/autoload/plug.vim --create-dirs \ + https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim + +nvim -n -u tests/data/vimrc --headless +PlugInstall +qa +rm -f /tmp/nvim-LanguageClient-IntegrationTest +if [[ "$TMUX" ]]; then + tmux split-window 'NVIM_LISTEN_ADDRESS=/tmp/nvim-LanguageClient-IntegrationTest nvim -n -u tests/data/vimrc' +else + NVIM_LISTEN_ADDRESS=/tmp/nvim-LanguageClient-IntegrationTest nvim -n -u tests/data/vimrc --headless & +fi +PID=$! +sleep 1s + +py.test-3 --capture=no --exitfirst -v $@ +ret=$? + +if [[ $ret != 0 ]]; then + cat $LOG +fi + +if [[ -n $PID ]]; then + kill $PID +fi +exit $ret diff --git a/vim/bundle/LanguageClient-neovim/tests/wrapper-server.sh b/vim/bundle/LanguageClient-neovim/tests/wrapper-server.sh new file mode 100755 index 0000000..0ce86e4 --- /dev/null +++ b/vim/bundle/LanguageClient-neovim/tests/wrapper-server.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Tee server stdio into log file. + +if test -z "$TMP"; then + TMP=/tmp +fi + +LOG="$TMP/wrapper-server.log" + +tee -a $LOG | RUST_LOG=rls rls 2>>$LOG | tee -a $LOG + +# tee -a $LOG | php /user/local/bin/php-language-server.php | tee -a $LOG + +# tee -a $LOG | /usr/local/bin/language-server-stdio.js | tee -a $LOG + +# tee -a $LOG | pyls 2>>$LOG | tee -a $LOG diff --git a/vim/bundle/yats.vim/.gitignore b/vim/bundle/yats.vim/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/vim/bundle/yats.vim/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/vim/bundle/yats.vim/.gitmodules b/vim/bundle/yats.vim/.gitmodules new file mode 100644 index 0000000..0ec9165 --- /dev/null +++ b/vim/bundle/yats.vim/.gitmodules @@ -0,0 +1,3 @@ +[submodule "test/vader.vim"] + path = test/vader.vim + url = https://github.com/junegunn/vader.vim.git diff --git a/vim/bundle/yats.vim/.travis.yml b/vim/bundle/yats.vim/.travis.yml new file mode 100644 index 0000000..24da4d1 --- /dev/null +++ b/vim/bundle/yats.vim/.travis.yml @@ -0,0 +1 @@ +script: cd ./test && ./start diff --git a/vim/bundle/yats.vim/CHANGES.markdown b/vim/bundle/yats.vim/CHANGES.markdown new file mode 100644 index 0000000..9b9f1fc --- /dev/null +++ b/vim/bundle/yats.vim/CHANGES.markdown @@ -0,0 +1,62 @@ +CHANGE LOG +========== + +### Version 1.3 +- Add ServiceWorker +- Some code refactor +- Lots of bug fix + - Nesting block + - Multiline template string + - Negative integer + - Syntax not load second time + - Comment in object literal + - Reserved word not in all string type + + +### Version 1.2 + +- Embed JS in html/erb/haml +- fetch +- sendBeacon +- Improve init performance + +### Version 1.1.1 + +- Function brace in new line +- Web Crypto + +### Version 1.1 + +- JSDoc 3 +- ECMA-402: Intl +- async, await +- Event name +- String bugs fix +- Minor bug fix + +### Version 1.0 + +- New name +- Refactor Web API and DOM +- Remove old definitions +- Supports partial ES6 +- Work with js lib syntax + +### Version 0.9 + +- Add HTML interfaces +- Add CustomEvent +- Add custom element api + +### Version 0.8 + +- Use javascript as group name prefix, which makes SyntaxComplete can work for JavaScript +- Better support for JSDoc +- Support [closure compiler JSDoc annotation](https://developers.google.com/closure/compiler/docs/js-for-compiler) +- Support [Typed Array](http://www.khronos.org/registry/typedarray/specs/latest/) and [File API](http://www.w3.org/TR/FileAPI/) + +License +------- + +The same as Vim + diff --git a/vim/bundle/yats.vim/Makefile b/vim/bundle/yats.vim/Makefile new file mode 100644 index 0000000..e8187cb --- /dev/null +++ b/vim/bundle/yats.vim/Makefile @@ -0,0 +1,364 @@ +# vim:filetype=make:foldmethod=marker:fdl=0: +# +# Makefile: install/uninstall/link vim plugin files. +# Author: Cornelius +# Date: 一 3/15 22:49:26 2010 +# Version: 1.0 +# +# PLEASE DO NOT EDIT THIS FILE. THIS FILE IS AUTO-GENERATED FROM Makefile.tpl +# LICENSE {{{ +# Copyright (c) 2010 +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# }}} +# VIM RECORD FORMAT: {{{ +# { +# version => 0.2, # record spec version +# generated_by => 'Vimana-' . $Vimana::VERSION, +# install_type => 'auto', # auto , make , rake ... etc +# package => $self->package_name, +# files => \@e, +# } +# }}} + +# INTERNAL VARIABLES {{{ + +RECORD_FILE=.record +PWD=`pwd` +README_FILES=`ls -1 | grep -i readme` +WGET_OPT=-c -nv +CURL_OPT= +RECORD_SCRIPT=.mkrecord +TAR=tar czvf + +GIT_SOURCES= + +# INTERNAL FUNCTIONS {{{ +record_file = \ + PTYPE=`cat $(1) | perl -nle 'print $$1 if /^"\s*script\s*type:\s*(\S*)$$/i'` ;\ + echo $(VIMRUNTIME)/$$PTYPE/$(1) >> $(2) + +# }}} + +# PUBLIC FUNCTIONS {{{ + +GIT_SOURCES= +DEPEND_DIR=/tmp/vim-deps + +# Usage: +# +# $(call install_git_sources) +# + +install_git_source = \ + PWD=$(PWD) ; \ + mkdir -p $(DEPEND_DIR) ; \ + cd $(DEPEND_DIR) ; \ + for git_uri in $(GIT_SOURCES) ; do \ + OUTDIR=$$(echo $$git_uri | perl -pe 's{^.*/}{}') ;\ + echo $$OUTDIR ; \ + if [[ -e $$OUTDIR ]] ; then \ + cd $$OUTDIR ; \ + git pull origin master && \ + make install && cd .. ; \ + else \ + git clone $$git_uri $$OUTDIR && \ + cd $$OUTDIR && \ + make install && cd .. ; \ + fi; \ + done ; + + + + +# install file by inspecting content +install_file = \ + PTYPE=`cat $(1) | perl -nle 'print $$1 if /^"\s*script\s*type:\s*(\S*)$$/i'` ;\ + cp -v $(1) $(VIMRUNTIME)/$$PTYPE/$(1) + +link_file = \ + PTYPE=`cat $(1) | perl -nle 'print $$1 if /^"\s*script\s*type:\s*(\S*)$$/i'` ;\ + cp -v $(1) $(VIMRUNTIME)/$$PTYPE/$(1) + +unlink_file = \ + PTYPE=`cat $(1) | perl -nle 'print $$1 if /^"\s*script\s*type:\s*(\S*)$$/i'` ;\ + rm -fv $(VIMRUNTIME)/$$PTYPE/$(1) + +# fetch script from an url +fetch_url = \ + @if [[ -e $(2) ]] ; then \ + exit \ + ; fi \ + ; echo " => $(2)" \ + ; if [[ ! -z `which curl` ]] ; then \ + curl $(CURL_OPT) $(1) -o $(2) ; \ + ; elif [[ ! -z `which wget` ]] ; then \ + wget $(WGET_OPT) $(1) -O $(2) \ + ; fi \ + ; echo $(2) >> .bundlefiles + + +install_source = \ + for git_uri in $(GIT_SOURCES) ; do \ + OUTDIR=$$(echo $$git_uri | perl -pe 's{^.*/}{}') ;\ + echo $$OUTDIR ; \ + done + +# fetch script from github +fetch_github = \ + @if [[ -e $(5) ]] ; then \ + exit \ + ; fi \ + ; echo " => $(5)" \ + ; if [[ ! -z `which curl` ]] ; then \ + curl $(CURL_OPT) http://github.com/$(1)/$(2)/raw/$(3)/$(4) -o $(5) \ + ; elif [[ ! -z `which wget` ]] ; then \ + wget $(WGET_OPT) http://github.com/$(1)/$(2)/raw/$(3)/$(4) -O $(5) \ + ; fi \ + ; echo $(5) >> .bundlefiles + +# fetch script from local file +fetch_local = @cp -v $(1) $(2) \ + ; @echo $(2) >> .bundlefiles + +# 1: NAME , 2: URI +dep_from_git = \ + D=/tmp/$(1)-$$RANDOM ; git clone $(2) $$D ; cd $$D ; make install ; + +dep_from_svn = \ + D=/tmp/$(1)-$$RANDOM ; svn checkout $(2) $$D ; cd $$D ; make install ; + +# }}} +# }}} +# ======= DEFAULT CONFIG ======= {{{ + +# Default plugin name +NAME=`basename \`pwd\`` +VERSION=0.1 + +# Files to add to tarball: +DIRS=`ls -1F | grep / | sed -e 's/\///'` + +# Runtime path to install: +VIMRUNTIME=~/.vim + +# Other Files to be added: +FILES=`ls -1 | grep '.vim$$'` +MKFILES=Makefile `ls -1 | grep '.mk$$'` + +# ======== USER CONFIG ======= {{{ +# please write config in config.mk +# this will override default config +# +# Custom Name: +# +# NAME=[plugin name] +# +# Custom dir list: +# +# DIRS=autoload after doc syntax plugin +# +# Files to add to tarball: +# +# FILES= +# +# Bundle dependent scripts: +# +# bundle-deps: +# $(call fetch_github,[account id],[project],[branch],[source path],[target path]) +# $(call fetch_url,[file url],[target path]) +# $(call fetch_local,[from],[to]) + +SHELL=bash + +CONFIG_FILE=config.mk +-include ~/.vimauthor.mk +-include $(CONFIG_FILE) + +# }}} +# }}} +# ======= SECTIONS ======= {{{ +-include ext.mk + +all: install-deps install + +install-deps: + # check required binaries + [[ -n $$(which git) ]] + [[ -n $$(which bash) ]] + [[ -n $$(which vim) ]] + [[ -n $$(which wget) || -n $$(which curl) ]] + $(call install_git_sources) + +check-require: + @if [[ -n `which wget` || -n `which curl` || -n `which fetch` ]]; then echo "wget|curl|fetch: OK" ; else echo "wget|curl|fetch: NOT OK" ; fi + @if [[ -n `which vim` ]] ; then echo "vim: OK" ; else echo "vim: NOT OK" ; fi + +config: + @rm -f $(CONFIG_FILE) + @echo "NAME=" >> $(CONFIG_FILE) + @echo "VERSION=" >> $(CONFIG_FILE) + @echo "#DIRS=" + @echo "#FILES=" + @echo "" >> $(CONFIG_FILE) + @echo "bundle-deps:" >> $(CONFIG_FILE) + @echo " \$$(call fetch_github,ID,REPOSITORY,BRANCH,PATH,TARGET_PATH)" >> $(CONFIG_FILE) + @echo " \$$(call fetch_url,FILE_URL,TARGET_PATH)" >> $(CONFIG_FILE) + + +init-author: + @echo "AUTHOR=" > ~/.vimauthor.mk + +bundle-deps: + +bundle: bundle-deps + +dist: bundle mkfilelist + @$(TAR) $(NAME)-$(VERSION).tar.gz --exclude '*.svn' --exclude '.git' $(DIRS) $(README_FILES) $(FILES) $(MKFILES) + @echo "$(NAME)-$(VERSION).tar.gz is ready." + +init-runtime: + @mkdir -vp $(VIMRUNTIME) + @mkdir -vp $(VIMRUNTIME)/record + @if [[ -n "$(DIRS)" ]] ; then find $(DIRS) -type d | while read dir ; do \ + mkdir -vp $(VIMRUNTIME)/$$dir ; done ; fi + +release: + if [[ -n `which vimup` ]] ; then \ + fi + +pure-install: + @echo "Using Shell:" $(SHELL) + @echo "Installing" + @if [[ -n "$(DIRS)" ]] ; then find $(DIRS) -type f | while read file ; do \ + cp -v $$file $(VIMRUNTIME)/$$file ; done ; fi + @echo "$(FILES)" | while read vimfile ; do \ + if [[ -n $$vimfile ]] ; then \ + $(call install_file,$$vimfile) ; fi ; done + +install: init-runtime bundle pure-install record + + +uninstall-files: + @echo "Uninstalling" + @if [[ -n "$(DIRS)" ]] ; then find $(DIRS) -type f | while read file ; do \ + rm -fv $(VIMRUNTIME)/$$file ; done ; fi + @echo "$(FILES)" | while read vimfile ; do \ + if [[ -n $$vimfile ]] ; then \ + $(call unlink_file,$$vimfile) ; fi ; done + +uninstall: uninstall-files rmrecord + +link: init-runtime + @echo "Linking" + @if [[ -n "$(DIRS)" ]]; then find $(DIRS) -type f | while read file ; do \ + ln -sfv $(PWD)/$$file $(VIMRUNTIME)/$$file ; done ; fi + @echo "$(FILES)" | while read vimfile ; do \ + if [[ -n $$vimfile ]] ; then \ + $(call link_file,$$vimfile) ; fi ; done + +mkfilelist: + @echo $(NAME) > $(RECORD_FILE) + @echo $(VERSION) >> $(RECORD_FILE) + @if [[ -n "$(DIRS)" ]] ; then find $(DIRS) -type f | while read file ; do \ + echo $(VIMRUNTIME)/$$file >> $(RECORD_FILE) ; done ; fi + @echo "$(FILES)" | while read vimfile ; do \ + if [[ -n $$vimfile ]] ; then \ + $(call record_file,$$vimfile,$(RECORD_FILE)) ; fi ; done + +vimball-edit: + find $(DIRS) -type f > .tmp_list + vim .tmp_list + vim .tmp_list -c ":%MkVimball $(NAME)-$(VERSION) ." -c "q" + @rm -vf .tmp_list + @echo "$(NAME)-$(VERSION).vba is ready." + +vimball: + find $(DIRS) -type f > .tmp_list + vim .tmp_list -c ":%MkVimball $(NAME)-$(VERSION) ." -c "q" + @rm -vf .tmp_list + @echo "$(NAME)-$(VERSION).vba is ready." + +mkrecordscript: + @echo "" > $(RECORD_SCRIPT) + @echo "fun! s:mkmd5(file)" >> $(RECORD_SCRIPT) + @echo " if executable('md5')" >> $(RECORD_SCRIPT) + @echo " return system('cat ' . a:file . ' | md5')" >> $(RECORD_SCRIPT) + @echo " else" >> $(RECORD_SCRIPT) + @echo " return \"\"" >> $(RECORD_SCRIPT) + @echo " endif" >> $(RECORD_SCRIPT) + @echo "endf" >> $(RECORD_SCRIPT) + @echo "let files = readfile('.record')" >> $(RECORD_SCRIPT) + @echo "let package_name = remove(files,0)" >> $(RECORD_SCRIPT) + @echo "let script_version = remove(files,0)" >> $(RECORD_SCRIPT) + @echo "let record = { 'version' : 0.3 , 'generated_by': 'Vim-Makefile' , 'script_version': script_version , 'install_type' : 'makefile' , 'package' : package_name , 'files': [ ] }" >> $(RECORD_SCRIPT) + @echo "for file in files " >> $(RECORD_SCRIPT) + @echo " let md5 = s:mkmd5(file)" >> $(RECORD_SCRIPT) + @echo " cal add( record.files , { 'checksum': md5 , 'file': file } )" >> $(RECORD_SCRIPT) + @echo "endfor" >> $(RECORD_SCRIPT) + @echo "redir => output" >> $(RECORD_SCRIPT) + @echo "silent echon record" >> $(RECORD_SCRIPT) + @echo "redir END" >> $(RECORD_SCRIPT) + @echo "let content = join(split(output,\"\\\\n\"),'')" >> $(RECORD_SCRIPT) + @echo "let record_file = expand('~/.vim/record/' . package_name )" >> $(RECORD_SCRIPT) + @echo "cal writefile( [content] , record_file )" >> $(RECORD_SCRIPT) + @echo "cal delete('.record')" >> $(RECORD_SCRIPT) + @echo "echo \"Done\"" >> $(RECORD_SCRIPT) + + +record: mkfilelist mkrecordscript + vim --noplugin -V10install.log -c "so $(RECORD_SCRIPT)" -c "q" + @echo "Vim script record making log: install.log" +# @rm -vf $(RECORD_FILE) + +rmrecord: + @echo "Removing Record" + @rm -vf $(VIMRUNTIME)/record/$(NAME) + +clean: clean-bundle-deps + @rm -vf $(RECORD_FILE) + @rm -vf $(RECORD_SCRIPT) + @rm -vf install.log + @rm -vf *.tar.gz + +clean-bundle-deps: + @echo "Removing Bundled scripts..." + @if [[ -e .bundlefiles ]] ; then \ + rm -fv `echo \`cat .bundlefiles\``; \ + fi + @rm -fv .bundlefiles + +update: + @echo "Updating Makefile..." + @URL=http://github.com/c9s/vim-makefile/raw/master/Makefile ; \ + if [[ -n `which curl` ]]; then \ + curl $$URL -o Makefile ; \ + if [[ -n `which wget` ]]; then \ + wget -c $$URL ; \ + elif [[ -n `which fetch` ]]; then \ + fetch $$URL ; \ + fi + +version: + @echo version - $(MAKEFILE_VERSION) + +# }}} diff --git a/vim/bundle/yats.vim/README.md b/vim/bundle/yats.vim/README.md new file mode 100644 index 0000000..1b7dcd7 --- /dev/null +++ b/vim/bundle/yats.vim/README.md @@ -0,0 +1,31 @@ +YATS: Yet Another TypeScript Syntax +=================================== + +[![Build Status](https://travis-ci.org/HerringtonDarkholme/yats.vim.svg?branch=master)](https://travis-ci.org/HerringtonDarkholme/yats.vim) + +![screenshot](https://raw.githubusercontent.com/HerringtonDarkholme/yats.vim/master/screenshot.png) + +Yet Another TypeScript Syntax file for Vim, adapted from [YAJS](https://github.com/othree/yajs.vim). +Key differences: + +* Exuberant Highlighting. The most elaborate or byzatine syntax highlighting for TypeScript. +* Bundled with snippets for UltiSnips! +* Builtin Support for tagbar! +* Use 'typescript' as group name's prefix, not 'typeScript' nor 'TypeScript'. Works great with [SyntaxComplete](https://github.com/vim-scripts/SyntaxComplete). +* Inherit Web API and DOM keywords from YAJS +* Syntax fold. Fold long code block, semantically! +* Remove old, unused syntax definitions. + +Credits +------- + +- [othree](https://github.com/othree), whose YAJS is the most up-to-date js syntax for Vim +- Jose Elera, [Enhanced Javascript syntax](http://www.vim.org/scripts/script.php?script_id=3425) +- Zhao Yi, Claudio Fleiner, Scott Shattuck (This file is based on their hard work) +- gumnos (From the #vim IRC Channel in Freenode) (Who helped me figured out the crazy Vim Regexes) + +License +------- + +The same as Vim + diff --git a/vim/bundle/yats.vim/UltiSnips/typescript.snippets b/vim/bundle/yats.vim/UltiSnips/typescript.snippets new file mode 100644 index 0000000..9ca17ef --- /dev/null +++ b/vim/bundle/yats.vim/UltiSnips/typescript.snippets @@ -0,0 +1,93 @@ +priority -50 + +snippet /// "import reference" b +/// +$0 +endsnippet + +snippet ctor "constructor" b +constructor(${0}) { + ${1:// code} +} +endsnippet + +snippet cls "class .." b +class ${1:ClassName}${2: extends ${3:ParentClass}} { + ${4: constructor(${5:args}) { + ${6://code...} + }} +} +endsnippet + +snippet itf "interface .." b +interface ${1:InterfaceName}${2: extends ${3:Parent}} { + ${4://code...} +} +endsnippet + +snippet import "imports a module" b +import $1 = require('$2') +endsnippet + +snippet for "for (...) {...} (counting up)" b +for (var ${1:i} = 0, ${2:len} = ${3:Things.length}; $1 < $2; $1++) { + ${VISUAL}$0 +} +endsnippet + +snippet fun "function (fun)" +function ${1:function_name}(${2:argument}) { + ${VISUAL}$0 +} +endsnippet + +snippet fn "function literal (fn)" +function (${1:argument}) { + ${VISUAL}$0 +} +endsnippet + +snippet af "arrow function (af)" +(${1:argument}) => { + ${VISUAL}$0 +} +endsnippet + +snippet ife "if ___ else" +if (${1:condition}) { + ${2://code} +} +else { + ${3://code} +} +endsnippet + +snippet cl "console.log" b +console.log(${1:"${2:value}"}); +endsnippet + +snippet meth "class/object method" b +${2:methodName}(${3:args}): ${4:void} { + ${0://code} +} +endsnippet + +snippet fi "for prop in obj using hasOwnProperty" b +for (${1:prop} in ${2:obj}){ + if ($2.hasOwnProperty($1)) { + ${VISUAL}$0 + } +} +endsnippet + +snippet timeout "setTimeout function" +setTimeout(function() { + ${VISUAL}$0 +}${2:.bind(${3:this})}, ${1:10}); +endsnippet + +snippet dclm "declare module" b +declare module ${1:name} { + ${VISUAL}$0 +} +endsnippet diff --git a/vim/bundle/yats.vim/compiler/typescript.vim b/vim/bundle/yats.vim/compiler/typescript.vim new file mode 100644 index 0000000..0bc9d26 --- /dev/null +++ b/vim/bundle/yats.vim/compiler/typescript.vim @@ -0,0 +1,21 @@ +if exists('current_compiler') + finish +endif + +let current_compiler='typescript' + +if !exists('g:typescript_compiler_binary') + let g:typescript_compiler_binary = 'tsc' +endif + +if !exists('g:typescript_compiler_options') + if exists('g:syntastic_typescript_tsc_args') + let g:typescript_compiler_options = g:syntastic_typescript_tsc_args + else + let g:typescript_compiler_options = '' + endif +endif + +let &l:makeprg = g:typescript_compiler_binary . ' ' . g:typescript_compiler_options . ' $* %' + +CompilerSet errorformat=%+A\ %#%f\ %#(%l\\\,%c):\ %m,%C%m diff --git a/vim/bundle/yats.vim/config.mk b/vim/bundle/yats.vim/config.mk new file mode 100644 index 0000000..c8ca3d1 --- /dev/null +++ b/vim/bundle/yats.vim/config.mk @@ -0,0 +1,6 @@ +NAME=yajs.vim +VERSION=1.4 + +bundle-deps: + $(call fetch_github,ID,REPOSITORY,BRANCH,PATH,TARGET_PATH) + $(call fetch_url,FILE_URL,TARGET_PATH) diff --git a/vim/bundle/yats.vim/ctags/typescript.ctags b/vim/bundle/yats.vim/ctags/typescript.ctags new file mode 100644 index 0000000..e61834a --- /dev/null +++ b/vim/bundle/yats.vim/ctags/typescript.ctags @@ -0,0 +1,13 @@ +--langdef=typescript +--langmap=typescript:.ts +--regex-typescript=/^[ \t]*(export)?[ \t]*class[ \t]+([a-zA-Z0-9_]+)/\2/c,classes/ +--regex-typescript=/^[ \t]*(export)?[ \t]*abstract class[ \t]+([a-zA-Z0-9_]+)/\2/a,abstract classes/ +--regex-typescript=/^[ \t]*(export)?[ \t]*module[ \t]+([a-zA-Z0-9_]+)/\2/n,modules/ +--regex-typescript=/^[ \t]*(export)?[ \t]*type[ \t]+([a-zA-Z0-9_]+)/\2/t,types/ +--regex-typescript=/^[ \t]*(export)?[ \t]*namespace[ \t]+([a-zA-Z0-9_]+)/\2/n,modules/ +--regex-typescript=/^[ \t]*(export)?[ \t]*function[ \t]+([a-zA-Z0-9_]+)/\2/f,functions/ +--regex-typescript=/^[ \t]*export[ \t]+var[ \t]+([a-zA-Z0-9_]+)/\1/v,variables/ +--regex-typescript=/^[ \t]*var[ \t]+([a-zA-Z0-9_]+)[ \t]*=[ \t]*function[ \t]*\(\)/\1/l,varlambdas/ +--regex-typescript=/^[ \t]*(export)?[ \t]*(public|private)[ \t]+(static)?[ \t]*([a-zA-Z0-9_]+)/\4/m,members/ +--regex-typescript=/^[ \t]*(export)?[ \t]*interface[ \t]+([a-zA-Z0-9_]+)/\2/i,interfaces/ +--regex-typescript=/^[ \t]*(export)?[ \t]*enum[ \t]+([a-zA-Z0-9_]+)/\2/e,enums/ diff --git a/vim/bundle/yats.vim/data/css.yml b/vim/bundle/yats.vim/data/css.yml new file mode 100644 index 0000000..565637c --- /dev/null +++ b/vim/bundle/yats.vim/data/css.yml @@ -0,0 +1,438 @@ +typescriptDOMStyle: + # - A + # - :active + # - ::after (:after) + - align-content + - align-items + - align-self + # - all + # - + - animation + - animation-delay + - animation-direction + - animation-duration + - animation-fill-mode + - animation-iteration-count + - animation-name + - animation-play-state + - animation-timing-function + - appearance + # - attr() + # - auto + # - B + - backface-visibility + - background + - background-attachment + - background-blend-mode + - background-clip + - background-color + - background-image + - background-origin + - background-position + - background-repeat + - background-size + # - + # - ::before (:before) + # - + # - blur() + - border + - border-bottom + - border-bottom-color + - border-bottom-left-radius + - border-bottom-right-radius + - border-bottom-style + - border-bottom-width + - border-collapse + - border-color + - border-image + - border-image-outset + - border-image-repeat + - border-image-slice + - border-image-source + - border-image-width + - border-left + - border-left-color + - border-left-style + - border-left-width + - border-radius + - border-right + - border-right-color + - border-right-style + - border-right-width + - border-spacing + - border-style + - border-top + - border-top-color + - border-top-left-radius + - border-top-right-radius + - border-top-style + - border-top-width + - border-width + - bottom + - box-decoration-break + - box-shadow + - box-sizing + - break-after + - break-before + - break-inside + # - brightness() + # - C + # - calc() + - caption-side + - caret-color + - caret-shape + - caret + # - ch + # - @charset + # - :checked + # - ::choices + # - circle() + - clear + - clip + - clip-path + # - cm + - color + # - + - columns + - column-count + - column-fill + - column-gap + - column-rule + - column-rule-color + - column-rule-style + - column-rule-width + - column-span + - column-width + - content + # - contrast() + # - + - counter-increment + - counter-reset + # - @counter-style + # - cross-fade() + # - cubic-bezier() + - cursor + # - + # - cycle() + # - D + # - :default + # - deg + # - :dir() + - direction + # - :disabled + - display + # - @document + # - dpcm + # - dpi + # - dppx + # - drop-shadow() + # - E + # - element() + # - ellipse() + # - em + # - :empty + - empty-cells + # - :enabled + # - ex + # - F + # - filter() + # - :first + # - :first-child + # - ::first-letter (:first-letter) + # - ::first-line (:first-line) + # - :first-of-type + - flex + - flex-basis + - flex-direction + - flex-flow + - flex-grow + - flex-shrink + - flex-wrap + - float + # - :focus + - font + # - @font-face + - font-family + - font-feature-settings + # - @font-feature-values + - font-kerning + - font-language-override + - font-size + - font-size-adjust + - font-stretch + - font-style + - font-synthesis + - font-variant + - font-variant-alternates + - font-variant-caps + - font-variant-east-asian + - font-variant-ligatures + - font-variant-numeric + - font-variant-position + - font-weight + # - + # - :fullscreen + # - G + - grad + # - + # - grayscale() + - grid + - grid-area + - grid-auto-columns + - grid-auto-flow + - grid-auto-position + - grid-auto-rows + - grid-column + - grid-column-start + - grid-column-end + - grid-row + - grid-row-start + - grid-row-end + - grid-template + - grid-template-areas + - grid-template-rows + - grid-template-columns + # - H + - height + # - :hover + # - hsl() + # - hsla() + # - hue-rotate() + - hyphens + # - hz + # - I + # - icon + # - + # - image()() + - image-rendering + - image-resolution + - image-orientation + - ime-mode + # - @import + # - in + # - :indeterminate + - inherit + # - initial + # - :in-range + # - inset() + # - + # - :invalid + # - invert() + # - isolation + # - J + - justify-content + # - K + # - @keyframes + # - khz + # - L + # - :lang() + # - :last-child + # - :last-of-type + - left + # - :left + # - + - letter-spacing + # - linear-gradient() + - line-break + - line-height + # - :link + - list-style + - list-style-image + - list-style-position + - list-style-type + # - M + - margin + - margin-bottom + - margin-left + - margin-right + - margin-top + - marks + - mask + - mask-type + # - matrix() + # - matrix3d() + - max-height + - max-width + # - @media + - min-height + # - minmax() + - min-width + - mix-blend-mode + # - mm + # - ms + # - N + # - @namespace + # - nav-down + # - nav-index + # - nav-left + # - nav-right + # - nav-up + # - none + # - normal + # - :not() + # - :nth-child() + # - :nth-last-child() + # - :nth-last-of-type() + # - :nth-of-type() + # - + # - O + - object-fit + - object-position + # - :only-child + # - :only-of-type + - opacity + # - opacity() + # - :optional + - order + - orphans + - outline + - outline-color + - outline-offset + - outline-style + - outline-width + # - :out-of-range + - overflow + - overflow-wrap + - overflow-x + - overflow-y + - overflow-clip-box + # - P + - padding + - padding-bottom + - padding-left + - padding-right + - padding-top + # - @page + - page-break-after + - page-break-before + - page-break-inside + # - pc + # - + - perspective + # - perspective() + - perspective-origin + - pointer-events + # - polygon() + - position + # - + # - pt + # - px + # - Q + - quotes + # - R + # - rad + # - radial-gradient() + # - + # - :read-only + # - :read-write + # - rect() + # - rem + # - repeat() + # - ::repeat-index + # - ::repeat-item + # - repeating-linear-gradient() + # - repeating-radial-gradient() + # - :required + - resize + # - + # - rgb() + # - rgba() + - right + # - :right + # - :root + # - rotate() + # - rotatex() + # - rotatey() + # - rotatez() + # - rotate3d() + # - S + # - s + # - saturate() + # - scale() + # - scalex() + # - scaley() + # - scalez() + # - scale3d() + # - :scope + # - ::selection + # - sepia() + # - + - shape-image-threshold + - shape-margin + - shape-outside + # - skew() + # - skewx() + # - skewy() + # - steps() + # - + # - @supports + # - T + - table-layout + - tab-size + # - :target + - text-align + - text-align-last + - text-combine-horizontal + - text-decoration + - text-decoration-color + - text-decoration-line + - text-decoration-style + - text-indent + - text-orientation + - text-overflow + - text-rendering + - text-shadow + - text-transform + - text-underline-position + # -

+var b =
+var a = 123 +var a= ; + +var a = + { + this.state.thing + ?

Hello world

+ :

Hey!

+ } + +

wow

+
+ + +var a = +
+
+ +var b = 123 +var c = ( + <> + + +) + +class A { + render() { + var d = ( + + {this.renderStuff()} + + ) + return + } +} + +// paren + function child +var d = ( + }/> +) + diff --git a/vim/bundle/yats.vim/test/tsx.indent.vader b/vim/bundle/yats.vim/test/tsx.indent.vader new file mode 100644 index 0000000..c53bb75 --- /dev/null +++ b/vim/bundle/yats.vim/test/tsx.indent.vader @@ -0,0 +1,89 @@ +Given typescriptreact (html end tags): +
+ name="cat" + value={value} + onclick={() => { + console.log('hello'); + }} + onremove={() => console.log('bye')} +
+ +Do (indent the block): + gg=G + +Expect typescriptreact indented block: +
+ name="cat" + value={value} + onclick={() => { + console.log('hello'); + }} + onremove={() => console.log('bye')} +
+ +Given typescriptreact (badly indented component): + class MyComponent { + public render() { + return ( + this.state.data[row.index]} + rowHeight={30}> + + + + +
+ ); + } + } + +Do (indent the block): + gg=G + +Expect typescriptreact indented blocks: + class MyComponent { + public render() { + return ( + this.state.data[row.index]} + rowHeight={30}> + + + + +
+ ); + } + } diff --git a/vim/bundle/yats.vim/test/tsx.vader b/vim/bundle/yats.vim/test/tsx.vader new file mode 100644 index 0000000..1278ff6 --- /dev/null +++ b/vim/bundle/yats.vim/test/tsx.vader @@ -0,0 +1,52 @@ +Given typescriptreact (basic usage): + var a =
+ var b = 123 + +Execute: + AssertEqual 'tsxTag', SyntaxAt(1, 9) + AssertEqual 'tsxIntrinsicTagName', SyntaxAt(1, 10) + AssertEqual 'typescriptVariable', SyntaxAt(2, 1) + +Given typescriptreact (basic void): + var a = + var b = 123 + +Execute: + AssertEqual 'tsxTag', SyntaxAt(1, 9) + AssertEqual 'tsxIntrinsicTagName', SyntaxAt(1, 10) + AssertEqual 'typescriptVariable', SyntaxAt(2, 1) + +Given typescriptreact (interpolation): + var a = ; + var b = 123 + +Execute: + AssertEqual 'tsxTagName', SyntaxAt(1, 10) + AssertEqual 'tsxTagName', SyntaxAt(1, 30) + AssertEqual 'typescriptVariable', SyntaxAt(2, 1) + +Given typescriptreact (custom component): + var a = ; + var b = 123 + +Execute: + AssertEqual 'tsxTag', SyntaxAt(1, 9) + AssertEqual 'tsxIntrinsicTagName', SyntaxAt(1, 30) + AssertEqual 'typescriptVariable', SyntaxAt(2, 1) + +Given typescriptreact (digit in tagname): + var a =

; + var b = 123 + +Execute: + AssertEqual 'tsxTag', SyntaxAt(1, 9) + AssertEqual 'tsxIntrinsicTagName', SyntaxAt(1, 16) + AssertEqual 'typescriptVariable', SyntaxAt(2, 1) + +Given typescriptreact (paren + render props): + var d = ( + }/> + ) + +Execute: + AssertEqual 'tsxTagName', SyntaxAt(2, 4) diff --git a/vim/bundle/yats.vim/test/vimrc b/vim/bundle/yats.vim/test/vimrc new file mode 100644 index 0000000..ffa6b42 --- /dev/null +++ b/vim/bundle/yats.vim/test/vimrc @@ -0,0 +1,6 @@ +set nocompatible +filetype off +let &rtp = &rtp.','.expand('%:p:h').'/vader.vim' +let &rtp = &rtp.','.expand('%:p:h:h') +filetype plugin indent on +syntax enable diff --git a/vimrc b/vimrc index 6db7ec7..612d31a 100644 --- a/vimrc +++ b/vimrc @@ -246,17 +246,33 @@ let g:goldenview__enable_at_startup=1 let g:goldenview__enable_default_mapping=0 " neomake -let g:neomake_javascript_eslint_maker = { - \ 'args': ['-f', 'compact', '--rule=no-console:0,no-warning-comments:0,mocha/no-exclusive-tests:0'], +let js_eslint_maker = { + \ 'args': ['-f', 'compact', '--rule=no-console:0,no-warning-comments:0,goodeggs/mocha-no-exclusive-tests:0'], \ 'errorformat': '%E%f: line %l\, col %c\, Error - %m,' . \ '%W%f: line %l\, col %c\, Warning - %m,%-G,%-G%*\d problems%#' \ } +let g:neomake_javascript_eslint_maker = js_eslint_maker +let g:neomake_typescript_eslint_maker = js_eslint_maker +let g:neomake_typescriptreact_eslint_maker = js_eslint_maker +let g:neomake_typescriptreact_tsc_maker = neomake#makers#ft#typescript#tsc() let g:neomake_javascript_enabled_makers = ['eslint', 'flow'] +let g:neomake_typescript_enabled_makers = ['eslint', 'tsc'] +let g:neomake_typescriptreact_enabled_makers = ['eslint', 'tsc'] +let g:neomake_eslint_project_maker = { + \ 'exe': 'yarn', + \ 'args': ['lint', '-f', 'compact', '--rule=no-console:0,no-warning-comments:0,goodeggs/mocha-no-exclusive-tests:0'], + \ 'append_file': 0, + \ 'errorformat': '%E%f: line %l\, col %c\, Error - %m,' . + \ '%W%f: line %l\, col %c\, Warning - %m,%-G,%-G%*\d problems%#' +\ } +let g:neomake_enabled_makers = ['eslint_project', 'flow'] let g:neomake_open_list = 2 call neomake#configure#automake('rw') " neoformat -autocmd BufWritePre,InsertLeave *.js* Neoformat +let g:neoformat_typescriptreact_prettier = neoformat#formatters#typescript#prettier() +let g:neoformat_enabled_typescriptreact = ['prettier'] +autocmd! BufWritePre *.js,*.ts,*.jsx,*.tsx Neoformat " Haskell let hs_highlight_boolean = 1 @@ -272,3 +288,17 @@ let g:jsx_ext_required = 0 " OS X specific options set clipboard=unnamed + +let g:LanguageClient_serverCommands = { + \ 'javascript': ['flow-language-server', '--stdio', '--try-flow-bin'], + \ 'javascript.jsx': ['flow-language-server', '--stdio', '--try-flow-bin'], + \ } + +let g:LanguageClient_changeThrottle = 0.50 +let g:LanguageClient_hoverPreview = "never" + +" Don't let languageclient do any linting +let g:LanguageClient_diagnosticsEnable = 0 + +nnoremap K :call LanguageClient_textDocument_hover() +nnoremap gd :call LanguageClient_textDocument_definition()