├── .github └── workflows │ └── ci.yml ├── .stylua.toml ├── LICENSE ├── README.md ├── demo.gif └── lua └── docs-view.lua /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | format_check: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: lua format check 16 | uses: JohnnyMorganz/stylua-action@v1 17 | with: 18 | token: ${{ secrets.GITHUB_TOKEN }} 19 | args: --color always --check lua/ 20 | 21 | -------------------------------------------------------------------------------- /.stylua.toml: -------------------------------------------------------------------------------- 1 | column_width = 100 2 | line_endings = "Unix" 3 | indent_type = "Spaces" 4 | indent_width = 2 5 | quote_style = "AutoPreferDouble" 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![StandWithPalestine](https://raw.githubusercontent.com/Safouene1/support-palestine-banner/master/StandWithPalestine.svg)](https://techforpalestine.org/learn-more) 2 | 3 | # nvim-docs-view 4 | 5 | A neovim plugin to display lsp hover documentation in a side panel. 6 | 7 | > Inspired by the VSCode extension [Docs View](https://marketplace.visualstudio.com/items?itemName=bierner.docs-view). 8 | 9 | doc-view-example 10 | 11 | ## Installation 12 | 13 | Using [packer.nvim](https://github.com/wbthomason/packer.nvim) 14 | 15 | ```lua 16 | use { 17 | "amrbashir/nvim-docs-view", 18 | opt = true, 19 | cmd = { "DocsViewToggle" }, 20 | config = function() 21 | require("docs-view").setup { 22 | position = "right", 23 | width = 60, 24 | } 25 | end 26 | } 27 | ``` 28 | 29 | Using [vim-plug](https://github.com/junegunn/vim-plug) 30 | 31 | ```viml 32 | Plug 'amrbashir/nvim-docs-view', { 'on': 'DocsViewToggle'} 33 | 34 | lua << EOF 35 | require("docs-view").setup { 36 | position = "right", 37 | width = 60, 38 | } 39 | EOF 40 | ``` 41 | 42 | Using [lazy.nvim](https://github.com/folke/lazy.nvim) 43 | ```lua 44 | { 45 | "amrbashir/nvim-docs-view", 46 | lazy = true, 47 | cmd = "DocsViewToggle", 48 | opts = { 49 | position = "right", 50 | width = 60 51 | } 52 | } 53 | ``` 54 | 55 | ## Options 56 | 57 | - `position`: 58 | - description: Determines where to open the docs view panel. 59 | - type: `string` 60 | - default: `right` 61 | - possible: `right` | `left` | `top` | `bottom` 62 | - `height`: 63 | - description: Height of the docs view panel when position is set to `top` or `bottom` 64 | - type: `number` 65 | - default: `10` 66 | - `width`: 67 | - description: Width of the docs view panel when position is set to `right` or `left` 68 | - type: `number` 69 | - default: `60` 70 | - `update_mode`: 71 | - description: Determines the mechanism used to update the docs view panel content. If `auto`, the content will update upon cursor move. If `manual`, the content will only update once `:DocsViewUpdate` is called. 72 | - type: `string` 73 | - default: `auto` 74 | - possible: `auto` | `manual` 75 | 76 | ## Commands 77 | 78 | - `:DocsViewToggle` to open/close the docs view panel. 79 | - `:DocsViewUpdate` to manually update the docs view panel (will open the docs view panel if necessary). 80 | 81 | ## LICENSE 82 | 83 | [MIT](./LICENSE) © Amr Bashir 84 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amrbashir/nvim-docs-view/f674ba57349849bce894efdd54096483c88e810b/demo.gif -------------------------------------------------------------------------------- /lua/docs-view.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | local cfg = {} 3 | local buf, win, prev_win, autocmd 4 | local get_clients 5 | 6 | M.update = function() 7 | if not win or not vim.api.nvim_win_is_valid(win) then 8 | M.toggle() 9 | end 10 | 11 | local clients = get_clients() 12 | local gotHover = false 13 | for i = 1, #clients do 14 | if clients[i].supports_method("textDocument/hover") then 15 | gotHover = true 16 | break 17 | end 18 | end 19 | if not gotHover then 20 | return 21 | end 22 | 23 | local l, c = unpack(vim.api.nvim_win_get_cursor(0)) 24 | vim.lsp.buf_request(0, "textDocument/hover", { 25 | textDocument = { uri = "file://" .. vim.api.nvim_buf_get_name(0) }, 26 | position = { line = l - 1, character = c }, 27 | }, function(err, result, ctx, config) 28 | if win and vim.api.nvim_win_is_valid(win) and result and result.contents then 29 | local md_lines = vim.lsp.util.convert_input_to_markdown_lines(result.contents) 30 | md_lines = vim.lsp.util.trim_empty_lines(md_lines) 31 | if vim.tbl_isempty(md_lines) then 32 | return 33 | end 34 | 35 | vim.api.nvim_buf_set_option(buf, "modifiable", true) 36 | vim.api.nvim_buf_set_lines(buf, 0, -1, true, {}) 37 | vim.lsp.util.stylize_markdown(buf, md_lines) 38 | vim.api.nvim_buf_set_option(buf, "modifiable", false) 39 | end 40 | end) 41 | end 42 | 43 | M.toggle = function() 44 | if win and vim.api.nvim_win_is_valid(win) then 45 | vim.api.nvim_win_close(win, false) 46 | if autocmd then 47 | vim.api.nvim_del_autocmd(autocmd) 48 | end 49 | buf, win, prev_win, autocmd = nil, nil, nil, nil 50 | else 51 | local height = cfg["height"] 52 | local width = cfg["width"] 53 | local update_mode = cfg["update_mode"] 54 | if update_mode ~= "manual" then 55 | update_mode = "auto" 56 | end 57 | 58 | prev_win = vim.api.nvim_get_current_win() 59 | 60 | if cfg.position == "bottom" then 61 | vim.api.nvim_command("bel new") 62 | width = vim.api.nvim_win_get_width(prev_win) 63 | elseif cfg.position == "top" then 64 | vim.api.nvim_command("top new") 65 | width = vim.api.nvim_win_get_height(prev_win) 66 | elseif cfg.position == "left" then 67 | vim.api.nvim_command("topleft vnew") 68 | else 69 | vim.api.nvim_command("botright vnew") 70 | end 71 | 72 | win = vim.api.nvim_get_current_win() 73 | buf = vim.api.nvim_get_current_buf() 74 | 75 | if cfg.position == "bottom" or cfg.position == "top" then 76 | vim.api.nvim_win_set_height(win, math.ceil(height)) 77 | end 78 | vim.api.nvim_win_set_width(win, math.ceil(width)) 79 | 80 | vim.api.nvim_buf_set_name(buf, "Docs View") 81 | vim.api.nvim_buf_set_option(buf, "buftype", "nofile") 82 | vim.api.nvim_buf_set_option(buf, "swapfile", false) 83 | vim.api.nvim_buf_set_option(buf, "bufhidden", "wipe") 84 | vim.api.nvim_buf_set_option(buf, "filetype", "nvim-docs-view") 85 | vim.api.nvim_buf_set_option(buf, "buflisted", false) 86 | 87 | vim.api.nvim_set_current_win(prev_win) 88 | 89 | if update_mode == "auto" then 90 | autocmd = vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, { 91 | pattern = "*", 92 | callback = function() 93 | if win and vim.api.nvim_win_is_valid(win) then 94 | M.update() 95 | else 96 | vim.api.nvim_del_autocmd(autocmd) 97 | buf, win, prev_win, autocmd = nil, nil, nil, nil 98 | end 99 | end, 100 | }) 101 | end 102 | end 103 | end 104 | 105 | M.setup = function(user_cfg) 106 | local default_cfg = { 107 | position = "right", 108 | height = 10, 109 | width = 60, 110 | update_mode = "auto", 111 | } 112 | 113 | cfg = vim.tbl_extend("force", default_cfg, user_cfg) 114 | 115 | if vim.fn.has("nvim-0.8.0") then 116 | get_clients = function() 117 | return vim.lsp.get_active_clients() 118 | end 119 | else 120 | get_clients = function() 121 | return vim.lsp.buf_get_clients(0) 122 | end 123 | end 124 | 125 | vim.api.nvim_create_user_command("DocsViewToggle", M.toggle, { nargs = 0 }) 126 | vim.api.nvim_create_user_command("DocsViewUpdate", M.update, { nargs = 0 }) 127 | end 128 | 129 | return M 130 | --------------------------------------------------------------------------------