Repository: Fildo7525/pretty_hover
Branch: master
Commit: a4212464431e
Files: 17
Total size: 54.4 KB
Directory structure:
gitextract_zbixqniw/
├── .github/
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── examples/
│ └── parsing.lua
└── lua/
└── pretty_hover/
├── config.lua
├── core/
│ ├── compatibility.lua
│ └── util.lua
├── highlight.lua
├── init.lua
├── local_request.lua
├── number.lua
└── parser/
├── init.lua
├── parser.lua
└── references.lua
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**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.
================================================
FILE: .gitignore
================================================
lua/.luarc.json
test
================================================
FILE: CONTRIBUTING.md
================================================
Anybody is welcome to contribute to this project. If you have any idea, how to improve any
features or you have an idea for a new feature do not hesitate to fork this project
and create a PR. Any improvement is welocme.
In case you want to contribute to this project, here is the structure of the project:
**/core**
- *compatibility.lua*
- All the functions that change during the nvim versions.
- *util.lua*
- Utility project functions.
**/parser**
- *init.lua*
- Parser module public implementation. This should be used to parse the
buffer. It is used internally by the plugin, too.
- *parser.lua*
- Internal implementation of the parser. The parser improvement or functionality has to be extended here.
- *references.lua*
- Parses the lines and detects the references in the buffer.
**/**
- *config.lua*
- Default configuration of the plugin. This is used to set the default configuration of the plugin.
- *highlight.lua*
- Module applying the highlighting detected by the parser.
- *init.lua*
- The main module of the plugin. This supplys the public API of the plugin.
- *number.lua*
- Module that creates a popup window with the number interpratation in multiple bases.
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2023 Filip Lobpreis
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.
================================================
FILE: README.md
================================================
<h1 align="center">
pretty_hover
</h1>
<p align="center">
<a href="https://github.com/Fildo7525/pretty_hover/stargazers">
<img
alt="Stargazers"
src="https://img.shields.io/github/stars/Fildo7525/pretty_hover?style=for-the-badge&logo=starship&color=fae3b0&logoColor=d9e0ee&labelColor=282a36"
/>
</a>
<a href="https://github.com/Fildo7525/pretty_hover/issues">
<img
alt="Issues"
src="https://img.shields.io/github/issues/Fildo7525/pretty_hover?style=for-the-badge&logo=gitbook&color=ddb6f2&logoColor=d9e0ee&labelColor=282a36"
/>
</a>
<a href="https://github.com/Fildo7525/pretty_hover/contributors">
<img
alt="Contributors"
src="https://img.shields.io/github/contributors/Fildo7525/pretty_hover?style=for-the-badge&logo=opensourceinitiative&color=abe9b3&logoColor=d9e0ee&labelColor=282a36"
/>
</a>
</p>
## Table of contents
- [How it looks](#how-it-looks)
- [Installation and setup](#installation-and-setup)
- [Configuration](#configuration)
- [Integration](#integration)
- [Blink.cmp](#blink.cmp)
- [Default config](#default-configuration)
- [Limitations](#limitations)
- [Contributing](#contributing)
- [Inspiration](#inspiration)
Pretty_hover is a lightweight plugin that parses the hover message before opening the popup window.
The output can be easily manipulated with. This will result in a more readable hover message.
An additional feature is `number conversion`. If you are tired of constantly converting some numbers to hex, octal
or binary you can use this plugin to do it for you.
### How it looks
> _**NOTE**_: The colors of the text depend on the color of your chosen colorscheme.
These pictures are taken with colorscheme `catppuccin-mocha`
Using native vim.lsp.buf.hover()
<img src="https://github.com/user-attachments/assets/5f4bd780-8a24-44c8-8a8e-4803ae9f7ace">
Using pretty_hover
<img src="https://github.com/user-attachments/assets/e547359e-0a82-4fac-ba75-388cdd291804">
## Installation and setup
### via Lazy
```lua
{
"Fildo7525/pretty_hover",
event = "LspAttach",
opts = {}
},
```
### via Packer
```lua
use {
"Fildo7525/pretty_hover",
config = function()
require("pretty_hover").setup({})
end
}
```
### Using Pretty Hover
To open a hover window, run the following lua snippet (or bind it to a key)
```lua
require("pretty_hover").hover()
```
To close a hover window either move the cursor as with nvim's hover popup or
run the following lua snippet (e.g. from a keymap)
```lua
require("pretty_hover").close()
```
**NOTE: When focused on a hover window, you can also press `q` to close the hover window**
### Configuration
| Parameter | Description |
|----------------- | -------------- |
| line | If one of the supplied strings is located as the first word in the line the whole line is surrounded by `line.styler`. |
| listing | These words will be substituted with `listing.styler`. |
| group | Table containing group name and its detectors. If this word is detected at the beginning of a line the next word is surrounded by `group.styler`. The whole group is separated by an line and the first line containing es the group name. |
| header | List of strings. If this word is detected at the beginning of a line the word is substituted by `header.styler` |
| return statement | This words are substituted with **Return** (in bold) |
| references | If any word from this list is detected, the next word is surrounded by `references.styler[1]`. If this word is located in `line` section the next word is surrounded by `references.styler[2]` (see [Limitations](#limitations)) |
| hl | This is a table of highlighting groups. You can define new groups by specifying at least two parameters. `color` and `detect`. Flag `line` is not mandatory, however by setting this flag you can ensure that the whole line is highlighted. When a detector from the table `detect` is found the detector is made uppercase, omits the beginning tag and gets highlighted. |
| border | Sets the border of the hover window. (none \| single \| double \| rounded \| solid \| shadow). |
| wrap | Flag whether to wrap the text if the window is smaller. Otherwise the floating window is scrollable horizontally |
| max_width | Sets the maximum width of the window. If you don't want any limitation set to nil. |
| max_height | Sets the maximum height of the window. If you don't want any limitation set to nil. |
| toggle | Flag detecting whether you want to have the hover just as a toggle window or make the popup focusable. |
| multi_server | Flag detecting whether you want to use the new multi lsp support or not. |
> _**NOTE**_: To really use this plugin you have to create a keymap that calls `require('pretty_hover').hover()` function.
The plugin supports code blocks. By specifying `@code{cpp}` the text in the popup window is highlighted with its filetype highlighter
until the `@endcode` is hit. When the filetype is not specified in the flag `@code` the filetype from the currently opened file is used.
#### Default configuration
```lua
{
-- Tables grouping the detected strings and using the markdown highlighters.
header = {
detect = { "[\\@]class" },
styler = '###',
},
line = {
detect = { "[\\@]brief" },
styler = '**',
},
listing = {
detect = { "[\\@]li" },
styler = " - ",
},
references = {
detect = { "[\\@]ref", "[\\@]c", "[\\@]name" },
styler = { "**", "`" },
},
group = {
detect = {
-- ["Group name"] = {"detectors"}
["Parameters"] = { "[\\@]param", "[\\@]*param*" },
["Types"] = { "[\\@]tparam" },
["See"] = { "[\\@]see" },
["Return Value"] = { "[\\@]retval" },
},
styler = "`",
},
-- Tables used for cleaner identification of hover segments.
code = {
start = { "[\\@]code" },
ending = { "[\\@]endcode" },
},
return_statement = {
"[\\@]return",
"[\\@]*return*",
},
-- Highlight groups used in the hover method. Feel free to define your own highlight group.
hl = {
error = {
color = "#DC2626",
detect = { "[\\@]error", "[\\@]bug" },
line = false, -- Flag detecting if the whole line should be highlighted
},
warning = {
color = "#FBBF24",
detect = { "[\\@]warning", "[\\@]thread_safety", "[\\@]throw" },
line = false,
},
info = {
color = "#2563EB",
detect = { "[\\@]remark", "[\\@]note", "[\\@]notes" },
},
-- Here you can set up your highlight groups.
},
-- If you use nvim 0.11.0 or higher you can choose, whether you want to use the new
-- multi lsp support or not. Otherwise this option is ignored.
multi_server = true,
border = "rounded",
wrap = true,
max_width = nil,
max_height = nil,
toggle = false,
}
```
### Integration
The plugin supports an easy integration:
```lua
local parsed = require("pretty_hover.parser").parse(text)
```
the parsed variable contains two fields `text` and `highlight`. The `text` field contains the converted text to markdown
and the `highlight` field contains the highlight groups for the text.
You can use the `parsed` variable to display the hover message in your own way.
```lua
vim.lsp.util.open_floating_preview(parsed.text, "markdown", {
focus = true,
focusable = true,
wrap = true,
wrap_at = 100,
max_width = 100,
border = "rounded",
focus_id = "pretty-hover-example",
})
```
To see an example of the implementation see the `pretty_hover/examples/parsing.lua` file.
#### Blink.cmp
This functionality is supported for blink.cmp from version v0.13.0 and higher.
To use this plugin with `blink.cmp` documentation you can add the following code snippet to you configuration:
```lua
{
completion = {
documentation = {
draw = function(opts)
if opts.item and opts.item.documentation and opts.item.documentation.value then
local out = require("pretty_hover.parser").parse(opts.item.documentation.value)
opts.item.documentation.value = out:string()
end
opts.default_implementation(opts)
end,
}
},
}
```
### Limitations
Currently, Neovim supports these markdown stylers: \`, \*, \`\`\`[language]. Unfortunately, you cannot do any
of their combination. If the support is extended there will be more options to style the pop-up window.
Newly this plugin started supporting highlighting see the [Configuration](#configuration) for more information.
### Contributing
If you have any idea how to improve this plugin do not hesitate to create a PR. Otherwise, if you know how
to improve the plugin mention it in a new issue. Enjoy the plugin.
### Inspiration
https://github.com/lewis6991/hover.nvim
================================================
FILE: examples/parsing.lua
================================================
local text = [[### function `main`
---
→ `int`
Parameters:
- `int argc`
- `char ** argv`
@brief Neque porro quisquam est qui dolorem @c ipsum quia dolor sit amet, consectetur, adipisci velit..."
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
Lorem Ipsum has been the @c industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.
It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
@note This is a note.
@param argc Number of arguments from the command line.
@param argv The arguments in format of the strings.
@return int The return of the program. Usually 0 for successful run.
---
```cpp
int main(int argc, char *argv[])
```
]]
local text_table = {
" ### function `main`",
"---",
" → `int`",
"Parameters:",
" - `int argc`",
" - `char ** argv`",
"",
"@brief Neque porro quisquam est qui dolorem @c ipsum quia dolor sit amet, consectetur, adipisci velit...",
"",
"Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
"Lorem Ipsum has been the @c industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.",
"It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.",
"It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
"",
"@note This is a note.",
"",
"@param argc Number of arguments from the command line.",
"@param argv The arguments in format of the strings.",
"@return int The return of the program. Usually 0 for successful run.",
"---",
"```cpp",
"int main(int argc, char *argv[])",
"```",
}
local parser = require("pretty_hover.parser")
local out = parser.parse(text)
--[[ local out = parser.parse(text_table) ]]
local bufnr, winnr = vim.lsp.util.open_floating_preview(out.text, "markdown", {
focus = true,
focusable = true,
wrap = true,
wrap_at = 100,
max_width = 100,
border = "rounded",
focus_id = "pretty-hover-example",
})
--[[ require('pretty_hover.highlight').apply_highlight(out.highlighting, bufnr, require("pretty_hover").get_config()) ]]
require('pretty_hover.highlight').apply_highlight(out.highlighting, bufnr)
================================================
FILE: lua/pretty_hover/config.lua
================================================
---@class PrettyHoverConfig
local M = {
_config = {},
header = {
detect = {"[\\@]class"},
styler = '###',
},
line = {
detect = { "[\\@]brief" },
styler = '**',
},
listing = {
detect = { "[\\@]li" },
styler = " - ",
},
references = {
detect = {
"[\\@]ref",
"[\\@]c",
"[\\@]name",
"[\\@]a",
},
styler = { "**", "`" },
},
group = {
detect = {
["Parameters"] = { "[\\@]param", "[\\@]*param*" },
["Types"] = { "[\\@]tparam" },
["See"] = { "[\\@]see" },
["Return Value"] = { "[\\@]retval" },
},
styler = "`",
},
code = {
start = {"[\\@]code"},
ending = {"[\\@]endcode"},
},
return_statement = {
"[\\@]return",
"[\\@]*return*",
},
hl = {
error = {
color = "#DC2626",
detect = {"[\\@]error", "[\\@]bug"},
line = false,
},
warning = {
color = "#FBBF24",
detect = {"[\\@]warning", "[\\@]thread_safety", "[\\@]throw"},
line = false,
},
info = {
color = "#4FC1FF",
detect = {"[\\@]remark", "[\\@]note", "[\\@]notes"},
}
},
multi_server = true,
border = "rounded",
wrap = true,
max_width = nil,
max_height = nil,
toggle = false,
}
---@class PrettyHoverConfig
---@brief This class is used to configure the pretty_hover plugin.
---@param config table Table of options to be used for the pretty_hover configuration. If none or empty is provided, the
---previous configuration will be used. If the previous configuration is also empty, the default configuration will be used.
---@return table config The configuration table that will be used for the pretty_hover plugin.
function M:instance(config)
config = config or {}
if vim.tbl_isempty(config) and not vim.tbl_isempty(self._config) then
return self._config
end
self._config = vim.tbl_deep_extend('force', {}, self, config)
require("pretty_hover.highlight").setup_colors(self._config)
return self._config
end
-- return M
return M
================================================
FILE: lua/pretty_hover/core/compatibility.lua
================================================
local M = {}
--- Function that encapsulates the changes in nvim api for getting the active clients.
---
--- @return table List of active clients.
function M.get_clients()
if vim.version().minor >= 11 then
return vim.lsp.get_clients()
else
return vim.lsp.get_active_clients()
end
end
function M.nvim_hl(name, fg)
if vim.version().minor >= 11 then
local ns = vim.api.nvim_get_namespaces()["pretty_hover_ns"]
return vim.api.nvim_get_hl(ns, {name = name})
end
return vim.api.nvim_get_hl_by_name(name, fg)
end
return M
================================================
FILE: lua/pretty_hover/core/util.lua
================================================
local api = vim.api
local hl = require("pretty_hover.highlight")
local hover_ns = api.nvim_create_namespace('pretty_hover_range')
local compatibility = require("pretty_hover.core.compatibility")
local M = {}
local winnr = 0
local bufnr = 0
function string:split(delimiter)
local result = { }
local from = 1
local delim_from, delim_to = string.find( self, delimiter, from )
while delim_from do
table.insert( result, string.sub( self, from , delim_from-1 ) )
from = delim_to + 1
delim_from, delim_to = string.find( self, delimiter, from )
end
table.insert( result, string.sub( self, from ) )
return result
end
--- Check if a table contains desired element. vim.tbl_contains does not work for all cases.
---@param tbl table Table to be checked.
---@param el string Element to be checked.
---@return boolean True if the table contains the element, false otherwise.
function M.tbl_contains(tbl, el)
if not el then
return false
end
if not tbl then
return false
end
for _, v in pairs(tbl) do
if el:find(v) then
return true
end
end
return false
end
--- Checks the table for the desired element. If the element is found, it is returned, otherwise nil is returned.
---@param tbl table Table to be checked.
---@param el string Element to be checked for.
---@return string The element if it is found, nil otherwise.
function M.find(tbl, el)
if not el or not tbl then
return ""
end
for _, v in pairs(tbl) do
if el:find(v) then
return el
end
end
return ""
end
--- Count the printable strings in the table.
---@param tbl table Table of string from hover.
---@return number Number of printable lines.
function M.printable_table_size(tbl)
local count = 0
for _, el in pairs(tbl) do
if el then
count = count + 1
end
end
return count
end
--- Splits a string into a table of strings.
---@param toSplit string String to be split.
---@param separator string|nil The separator. If not defined, the separator is set to "%S+".
---@return table Table of strings split by the separator.
function M.split(toSplit, separator)
local indentation = nil
if separator == nil then
indentation = string.match(toSplit, "^%s+")
separator = "%S+"
end
if toSplit == nil then
return {}
end
local chunks = {}
if indentation ~= nil and indentation:len() > 0 then
table.insert(chunks, indentation)
end
for substring in toSplit:gmatch(separator) do
-- These both cases are here because of python server. Some servers have '.... ' in front of every line and some
-- servers surround the whole message with '```text' and '```'. This is a workaround for that.
if substring:sub(1, 2) == ". " then
substring = substring:sub(5)
end
table.insert(chunks, substring)
end
return chunks
end
--- Join the elements of a table into a string with a delimiter.
---@param tbl table Table to be joined.
---@param delim string Delimiter to be used.
---@return string Joined string.
function M.join_table(tbl, delim)
local result = ""
for idx, chunk in pairs(tbl) do
result = result .. chunk
if idx ~= #tbl then
result = result .. delim
end
end
return result
end
--- This function checks all the active clients for current buffer and returns the active client that supports the current file type.
---@return table|nil Active client for the current buffer or nil if there is no active client.
function M.get_current_active_client()
for _, client in ipairs(compatibility.get_clients()) do
if M.tbl_contains(client.config.filetypes, vim.bo.filetype) then
return client
end
end
return nil
end
--- Close the opened floating window.
function M.close_float()
-- Safeguard around accidentally calling close when there is no pretty_hover window open
if winnr == 0 and bufnr == 0 then
return
end
api.nvim_buf_clear_namespace(vim.fn.bufnr(), hover_ns, 0, -1)
-- Before closing the window, check if it is still valid.
if not api.nvim_win_is_valid(winnr) then
winnr = 0
bufnr = 0
return
end
api.nvim_win_close(winnr, true)
winnr = 0
bufnr = 0
end
--- The file is a link in markdown style and is represented as [\w+](<uri>#L<row>,<col>).
--- This function opens the file in a new buffer and jumps to the given line and column.
function M.open_file_under_cursor()
local line = api.nvim_get_current_line()
local target = line:match("%[(.-)%]%((.-)#L(%d+),?(%d*)%)")
if not target then
vim.notify("1. No valid file link under cursor", vim.log.levels.WARN)
return
end
local _, uri, row, col = line:match("%[(.-)%]%((.-)#L(%d+),?(%d*)%)")
if not uri or not row then
vim.notify("2. No valid file link under cursor", vim.log.levels.WARN)
return
end
row = tonumber(row)
col = tonumber(col) or 0
M.close_float()
-- Open the file in a new buffer
vim.cmd("edit " .. uri)
-- Jump to the specified line and column
api.nvim_win_set_cursor(0, {row, col})
end
--- Opens a floating window with the documentation transformed from doxygen to markdown.
---@param hover_text string[] Text to be converted.
---@param format string Filetype to be used for the conversion.
---@param config table Table of options to be used for the conversion to the markdown language.
function M.open_float(hover_text, format, config)
if not hover_text or #hover_text == 0 then
-- There is nothing to display, quit out early
local tabled_numbers = require("pretty_hover.number").get_number_representations()
if not tabled_numbers then
vim.notify("No information available", vim.log.levels.INFO)
return
end
M.open_float(tabled_numbers:split("\n"), format, config)
return
end
-- Convert Doxygen comments to Markdown format
local out = require("pretty_hover.parser").parse(hover_text)
if #out.text == 0 then
vim.notify("No information available", vim.log.levels.INFO)
return
end
if config.toggle and winnr ~= 0 then
M.close_float()
return
end
local language = format
if config.one_liner then
language = vim.bo.filetype
end
bufnr, winnr = vim.lsp.util.open_floating_preview(out.text, language, {
border = config.border,
focusable = true,
focus = true,
focus_id = "pretty-hover",
wrap = config.wrap,
wrap_at = config.max_width and config.max_width - 2 or nil,
max_width = config.max_width,
max_height = config.max_height,
})
vim.wo[winnr].foldenable = false
vim.bo[bufnr].modifiable = false
vim.bo[bufnr].bufhidden = 'wipe'
hl.apply_highlight(out.highlighting, bufnr, config)
vim.keymap.set('n', 'gf', M.open_file_under_cursor, {
buffer = bufnr,
silent = true,
nowait = true,
})
vim.keymap.set('n', 'q', M.close_float, {
buffer = bufnr,
silent = true,
nowait = true,
})
return bufnr, winnr
end
return M
================================================
FILE: lua/pretty_hover/highlight.lua
================================================
local M = {}
local compatibility = require "pretty_hover.core.compatibility"
local api = vim.api
--- Convert HEX color representation to RGB
---@param hex string HEX color representation
---@return number|nil, number|nil, number|nil # RGB color representation
function M.hex2rgb(hex)
hex = hex:gsub("#", "")
return tonumber("0x" .. hex:sub(1, 2)), tonumber("0x" .. hex:sub(3, 4)), tonumber("0x" .. hex:sub(5, 6))
end
--- Check if HEX color is dark
---@param hex string HEX color representation
---@return boolean True if color is dark, false otherwise
function M.is_dark(hex)
local r, g, b = M.hex2rgb(hex)
local lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255
return lum <= 0.5
end
--- Get the highlight group
---@param name string Highlight group name
---@return table|nil Highlight group
function M.get_hl(name)
local hl = compatibility.nvim_hl(name, true)
for _, key in pairs({ "foreground", "background", "special" }) do
if hl[key] then
hl[key] = string.format("#%06x", hl[key])
end
end
return hl
end
--- Setup color groups for pretty_hover plugin.
---@param config table Options from the config.
function M.setup_colors(config)
M.hl_ns = api.nvim_create_namespace("pretty_hover_ns")
local normal = M.get_hl("Normal")
if not normal then
vim.notify("No normal highlight group found", vim.log.levels.WARN)
return
end
for kw, hl_groups in pairs(config.hl) do
local kw_color = hl_groups.color or "default"
local hex
if kw_color:sub(1, 1) == "#" then
hex = kw_color
else
local colors = M.options.colors[kw_color]
colors = type(colors) == "string" and { colors } or colors
for _, color in pairs(colors) do
if color:sub(1, 1) == "#" then
hex = color
break
end
local c = M.get_hl(color)
if c and c.foreground then
hex = c.foreground
break
end
end
end
if not hex then
error("Todo: no color for " .. kw)
end
vim.cmd("hi def PH" .. kw .. " guibg=NONE guifg=" .. hex .. " gui=NONE")
end
end
--- Applies the highlight to the lines of the opened floating window.
--- The used groups are ErrorMsg and WarningMsg. For the proper highlighting, the
--- highlight groups must be defined.
---@param hl_data table Table of control variables that were set during the conversion to markdown.
---@param bufnr number Buffer number of the pop-up window.
---@param config table|nil Table of configurations.
---@overload fun(hl_data: table, bufnr: number)
---@overload fun(hl_data: table, bufnr: number, config: table)
function M.apply_highlight(hl_data, bufnr, config)
if not config then
config = require("pretty_hover").get_config()
end
if M.hl_ns then
api.nvim_buf_clear_namespace(bufnr, M.hl_ns, 0, -1)
end
M.hl_ns = api.nvim_create_namespace("pretty_hover_ns")
for name, _ in pairs(config.hl) do
if hl_data.lines[tostring(name)] then
for _, line in pairs(hl_data.lines[tostring(name)]) do
if type(line) == "table" then
api.nvim_buf_add_highlight(bufnr, M.hl_ns, "PH"..tostring(name), line.line_nr, 0, line.to);
end
end
end
end
end
return M
================================================
FILE: lua/pretty_hover/init.lua
================================================
local api = vim.api
local cfg = require("pretty_hover.config")
local h_util = require("pretty_hover.core.util")
local local_hover_request = require("pretty_hover.local_request").local_hover_request
local M = {}
--- Parses the response from the server and displays the hover information converted to markdown.
function M.hover(config)
local params = vim.lsp.util.make_position_params(0, 'utf-16')
-- Check if the server for this file type exists and supports hover.
local client = h_util.get_current_active_client()
local hover_support_present = client and client.capabilities.textDocument.hover
if not client or not hover_support_present then
vim.notify("There is no client for this filetype or the client does not support the hover capability.", vim.log.levels.WARN)
return
end
config = config or {}
cfg:instance().hover_cnf = config
vim.lsp.buf_request_all(0, "textDocument/hover", params, local_hover_request)
end
--- Setup the plugin to use the given options.
---@param config table Options to be set for the plugin.
function M.setup(config)
config = cfg:instance(config)
if config.toggle then
local id = api.nvim_create_augroup("pretty_hover_augroup", {
clear = true,
})
api.nvim_create_autocmd({ "CursorMoved" }, {
callback = function()
require("pretty_hover.core.util").close_float()
end,
group = id,
})
end
end
--- Close the opened floating window.
function M.close()
h_util.close_float()
end
function M.get_config()
return cfg:instance()
end
return M
================================================
FILE: lua/pretty_hover/local_request.lua
================================================
local api = vim.api
local lsp = vim.lsp
local util = vim.lsp.util
local hover_ns = api.nvim_create_namespace('pretty_hover_range')
local cfg = require("pretty_hover.config")
local h_util = require("pretty_hover.core.util")
local number = require("pretty_hover.number")
local M = {}
local function parse_response_contents(contents)
local hover_text = contents.value;
-- vtsls workaround, this lsp does not contain value in the contents. It's just pure text.
if type(contents) == "string" then
hover_text = contents
end
if hover_text ~= nil then
return hover_text
end
-- typescript-tools.nvim workaround
-- Add a test in case there are no contents.
if not pcall(function() hover_text = contents[1].value end) then
return
end
hover_text = hover_text or ""
for i = 2, #contents do
if type(contents[i]) ~= "string" then
vim.notify("Unexpected item type found in hover request's response.\n" ..
"Please report an issue on github: https://github.com/Fildo7525/pretty_hover",
vim.log.levels.ERROR)
break
end
hover_text = hover_text .. contents[i]
end
return hover_text
end
local function request_below11(results)
local called = false
for _, response in pairs(results) do
if response.result and response.result.contents and called == false then
called = true
local contents = response.result.contents
-- We have to do this because of java. Sometimes is the value parameter split
-- into two chunks. Leaving the rest of the hover message as the second argument
-- in the received table.
if contents.language == "java" then
for _, content in pairs(contents) do
local hover_text = content.value or content
if not hover_text then
vim.notify("There is no text to be displayed", vim.log.levels.INFO)
return
end
h_util.open_float(hover_text, "markdown", cfg:instance())
end
else
local hover_text = parse_response_contents(response.result.contents)
if not hover_text then
vim.notify("There is no text to be displayed", vim.log.levels.INFO)
return
end
h_util.open_float(hover_text, "markdown", cfg:instance())
end
end
end
if not called then
local hover_text = number.get_number_representations()
if not hover_text then
return
end
h_util.open_float(hover_text, "markdown", cfg:instance())
return
end
end
local function request_above11(results, ctx)
local bufnr = assert(ctx.bufnr)
if api.nvim_get_current_buf() ~= bufnr then
-- Ignore result since buffer changed. This happens for slow language servers.
return
end
-- Filter errors from results
local results1 = {} --- @type table<integer,lsp.Hover>
for client_id, resp in pairs(results) do
local err, result = resp.err, resp.result
if err then
lsp.log.error(err.code, err.message)
elseif result then
results1[client_id] = result
end
end
if vim.tbl_isempty(results1) then
if cfg:instance().hover_cnf.silent ~= true then
local hover_text = number.get_number_representations()
if not hover_text then
vim.notify('No information available')
return
end
h_util.open_float(hover_text, "markdown", cfg:instance())
return
end
return
end
local contents = {} --- @type string[]
local nresults = #vim.tbl_keys(results1)
local format = 'markdown'
for client_id, result in pairs(results1) do
local client = assert(lsp.get_client_by_id(client_id))
if nresults > 1 then
-- Show client name if there are multiple clients
contents[#contents + 1] = string.format('# %s', client.name)
end
if type(result.contents) == 'table' and result.contents.kind == 'plaintext' then
if #results1 == 1 then
format = 'plaintext'
contents = vim.split(result.contents.value or '', '\n', { trimempty = true })
else
-- Surround plaintext with ``` to get correct formatting
contents[#contents + 1] = '```'
vim.list_extend(
contents,
vim.split(result.contents.value or '', '\n', { trimempty = true })
)
contents[#contents + 1] = '```'
end
else
vim.list_extend(contents, util.convert_input_to_markdown_lines(result.contents))
end
local range = result.range
if range then
local start = range.start
local end_ = range['end']
local start_idx = util._get_line_byte_from_position(bufnr, start, client.offset_encoding)
local end_idx = util._get_line_byte_from_position(bufnr, end_, client.offset_encoding)
vim.hl.range(
bufnr,
hover_ns,
'LspReferenceTarget',
{ start.line, start_idx },
{ end_.line, end_idx },
{ priority = vim.hl.priorities.user }
)
end
contents[#contents + 1] = '---'
end
-- Remove last linebreak ('---')
contents[#contents] = nil
if vim.tbl_isempty(contents) then
if cfg:instance().hover_cnf.silent ~= true then
vim.notify('No information available')
end
return
end
local _, winnr = h_util.open_float(contents, format, cfg:instance())
-- Remove selection highlighting after window is closed
api.nvim_create_autocmd('WinClosed', {
pattern = tostring(winnr),
once = true,
callback = function()
api.nvim_buf_clear_namespace(bufnr, hover_ns, 0, -1)
return true
end,
})
end
--- Function that will be used in hover request invoked by lsp.
---@param results table Table of responses from the server.
---@param ctx table Context of the request.
function M.local_hover_request(results, ctx)
-- Multi-server support is only available in nvim-0.11 and above.
-- The user can still decide to use the multi-server or not.
if vim.fn.has('nvim-0.11') == 1 and cfg:instance().multi_server then
request_above11(results, ctx)
return
end
request_below11(results)
end
return M
================================================
FILE: lua/pretty_hover/number.lua
================================================
local M = {}
--- Convert the input number to the specified base.
--- @param num number Number to be converted.
--- @param base number Base to convert the number to.
--- @return string The number converted into specified base in a string format.
function M.toBase(num, base)
local baseChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
local baseStr = ""
local idx = 0
while num > 0 do
local rem = num % base
baseStr = baseChars:sub(rem + 1, rem + 1) .. baseStr
num = math.floor(num / base)
idx = idx + 1
if idx % 4 == 0 and num > 0 then
baseStr = " " .. baseStr
end
end
return baseStr == "" and "0" or baseStr
end
--- Function to convert a number to its binary representation
--- @see toBase
--- @param num number Number to be converted.
--- @return string The number converted into binary in a string format.
function M.toBinary(num)
return M.toBase(num, 2)
end
--- Function to convert a number to its octal representation
--- @see toBase
--- @param num number Number to be converted.
--- @return string The number converted into octal in a string format.
function M.toOctal(num)
return M.toBase(num, 8)
end
--- Function to convert a number to its hexadecimal representation
--- @see toBase
--- @param num number Number to be converted.
--- @return string The number converted into hexadecimal in a string format.
function M.toHex(num)
return M.toBase(num, 16)
end
--- Pretty prints the decimal number by adding spaces after every 3 digits.
--- @param num string Number to be pretty printed.
--- @return string The pretty printed number.
function M.prettyDecimal(num)
local len = #num
local pretty = ""
local idx = 0
for i = len, 1, -1 do
pretty = num:sub(i, i) .. pretty
idx = idx + 1
if idx % 3 == 0 and i ~= 1 then
pretty = " " .. pretty
end
end
return pretty
end
--- Get the type of the number.
--- @param num string Number to get the type of.
--- @return number|nil The type of the number.
function M.get_number_type(num)
local original
if type(num) == 'string' and tonumber(num) then
original = num
else
return nil
end
if original[1] ~= 0 or #original < 2 then
return 10
elseif original[2] == 'x' then
return 16
elseif original[2] == 'b' then
return 2
elseif original[2] == 'o' then
return 8
else
return 10
end
end
--- Function to get all representations of a number
--- @param num string Number to get the representations of.
--- @param type number Type of the number.
--- @param return_type string The type of the return value, either "string" or "table".
--- @return string[]|nil A table containing the representations of the number in different bases.
function M.get_numerical_representations(num, type, return_type)
local tmp = tonumber(num, type)
if not tmp then
return nil
end
local decimal = M.prettyDecimal(tostring(tmp))
local binary = M.toBinary(tmp)
local octal = M.toOctal(tmp)
local hexadecimal = M.toHex(tmp)
local s = string.format("### Number types:\n---\nBinary: 0b%s\nOctal: 0o%s\nDecimal: %s\nHexadecimal: 0x%s\n", binary, octal, decimal, hexadecimal)
if return_type == "string" then
return s
end
return s:split("\n")
end
--- Function to get the number representations of the current word under the cursor.
--- @see get_number_type
--- @param return_type? string The number to get the representations of.
--- @return string|string[]|nil The number representations of the current word under the cursor.
function M.get_number_representations(return_type)
local num = vim.fn.expand("<cword>");
if return_type == nil then
return_type = "table"
end
if num:sub(1,1) == '-' then
num = num:sub(2)
end
local number_type = M.get_number_type(num)
if not number_type then
return
end
if number_type ~= 10 then
num = num:sub(2)
end
return M.get_numerical_representations(num, number_type, return_type)
end
return M
================================================
FILE: lua/pretty_hover/parser/init.lua
================================================
local M = {}
local parser = require("pretty_hover.parser.parser")
---@class ParserOutput
---@field public text table
---@field public highlighting table
---@field public new fun(self, text: table, highlighting: table): ParserOutput
---@field public empty fun(self): ParserOutput
---@field public string fun(self): string
local ParserOutput = {
text = {},
highlighting = {},
string = function(self)
return table.concat(self.text, "\n")
end,
}
--- Comparator function to check if two ParserOutput structs are equal.
---@param lhs ParserOutput
---@param rhs ParserOutput
---@return boolean Result whether the lhs and rhs sides of the comparison are the same
local function ParserOutputEQ(lhs, rhs)
local str_lhs = vim.inspect(lhs)
local str_rhs = vim.inspect(rhs)
return vim.fn.sha256(str_lhs) == vim.fn.sha256(str_rhs)
end
--- Creates new ParserOutput object.
---
---@param text table of parsed input. This goes directly to functions like vim.lsp.util.open_floating_preview
---@param highlighting table of highlighting data
---
---The highlighting data can be applied fx. like this
---
---```lua
--- if M.hl_ns then
--- api.nvim_buf_clear_namespace(bufnr, M.hl_ns, 0, -1)
--- end
---
--- M.hl_ns = api.nvim_create_namespace("pretty_hover_ns")
---
--- for name, _ in pairs(config.hl) do
--- if hl_data.lines[tostring(name)] then
--- for _, line in pairs(hl_data.lines[tostring(name)]) do
--- if type(line) == "table" then
--- api.nvim_buf_add_highlight(bufnr, M.hl_ns, "PH"..tostring(name), line.line_nr, 0, line.to);
--- end
--- end
--- end
--- end
---```
---
---@return ParserOutput out New object of the referenced type
function ParserOutput:new(text, highlighting)
local out = {}
setmetatable(out, { __index = self, __eq = ParserOutputEQ })
out.text = text
out.highlighting = highlighting
return out
end
function ParserOutput:empty()
return ParserOutput:new({}, {})
end
--- @brief This method parses the input string or table and converts the contents from doxygen into markdown format.
---
--- NOTE: The string must have new lines inside. If the string is not separated by them the parsing will not be done.
--- Additionally, if nil, empty string or empty table are passed in the returned object will have empty text and highlighting
--- fields.
---
--- The output highlighting data can be applied fx. this
---
--- ```lua
--- function M.apply_highlight(config, hl_data, bufnr)
---ll if M.hl_ns then
--- api.nvim_buf_clear_namespace(bufnr, M.hl_ns, 0, -1)
--- end
---
--- M.hl_ns = api.nvim_create_namespace("pretty_hover_ns")
---
--- for name, _ in pairs(config.hl) do
--- if hl_data.lines[tostring(name)] then
--- for _, line in pairs(hl_data.lines[tostring(name)]) do
--- if type(line) == "table" then
--- api.nvim_buf_add_highlight(bufnr, M.hl_ns, "PH"..tostring(name), line.line_nr, 0, line.to);
--- end
--- end
--- end
--- end
--- end
--- ```
--- @param text string|table Text as a string
--- @return ParserOutput Converted doxygen text into markdown.
---
--- @see pretty_hover.core.util.open_float function for the implementation in this plugin
---
--- @overload fun(text: string): ParserOutput
--- @overload fun(text: table): ParserOutput
function M.parse(text)
if not text
or (type(text) == "string" and text == "")
or (type(text) == "table" and vim.tbl_isempty(text))
then
return ParserOutput:empty()
end
local config = require("pretty_hover.config"):instance()
local hl_data = {
replacement = "",
lines = {},
}
local tbl = parser.convert_to_markdown(text, config, hl_data)
return ParserOutput:new(tbl, hl_data)
end
return M
================================================
FILE: lua/pretty_hover/parser/parser.lua
================================================
local util = require("pretty_hover.core.util")
local M = {
brief = {
detected = false,
option = "",
},
text_start_detected = false,
}
--- Transforms the line from doxygen type into markdown
---@param line string Line to be transformed.
---@param config table Table of options to be used for the conversion to the markdown language.
---@param hl_data table Table of control variables to be used for the pop-up window highlighting.
---@param control table Table of control variables to be used for the conversion to the markdown language.
---@return table Table of strings from doxygen to markdown.
function M.transform_line(line, config, control, hl_data)
local result = {}
-- Some servers add whitespaces infornt of some rows.
if line:find("^%s+[\\@]") then
line = line:gsub("^%s+", "")
end
if line:find(" ") then
line = line:gsub(" ", " ")
end
if line:find("^(%s*)\\%-%s*") then
line = line:gsub("^(%s*)\\%-%s*", "%1- ")
-- vim.print(line)
end
if line:find("^```text$") then
M.text_start_detected = true
line = ""
end
if line:find("^```$") and M.text_start_detected then
M.text_start_detected = false
line = ""
end
local tbl = util.split(line)
local el = tbl[1]
local insertEmptyLine = false
for name, group in pairs(config.hl) do
if util.tbl_contains(group.detect, el) then
tbl[1] = string.upper(util.find(group.detect, el))
if tbl[1]:sub(1, 1) == '@' then
tbl[1] = tbl[1]:sub(2)
else
tbl[1] = tbl[1]:sub(3)
end
hl_data.lines[tostring(name)].detected = true
hl_data.replacement = tbl[1]
end
end
-- Either end the brief line or extend it to the next line.
if M.brief.detected and el and not el:sub(1,2):gmatch("[\\@]")() then
if M.brief.option == "continue" then
table.insert(result, "")
M.brief.detected = false
M.brief.option = ""
elseif M.brief.option == "start" then
tbl[1] = config.line.styler .. tbl[1]
tbl[#tbl] = tbl[#tbl] .. config.line.styler
M.brief.detected = false
M.brief.option = ""
end
end
if util.tbl_contains(config.header.detect, el) then
tbl[1] = config.header.styler
insertEmptyLine = true;
elseif util.tbl_contains(config.line.detect, el) then
table.remove(tbl, 1)
M.brief.detected = true
if #tbl == 0 then
M.brief.option = "start"
else
tbl[1] = config.line.styler .. (tbl[1] or "")
tbl[#tbl] = tbl[#tbl] .. config.line.styler
M.brief.option = "continue"
end
elseif util.tbl_contains(config.listing.detect, el) then
tbl[1] = config.listing.styler
elseif util.tbl_contains(config.return_statement, el) then
table.insert(result, "")
tbl[1] = "**Return**"
line = util.join_table(tbl, " ")
elseif util.tbl_contains(config.code.start, el) then
local language = el:gmatch("{(%w+)}")() or vim.o.filetype
table.insert(result, "```" .. language)
table.remove(tbl, 1)
elseif util.tbl_contains(config.code.ending, el) then
table.insert(result, "```")
table.remove(tbl, 1)
end
for name, group in pairs(config.group.detect) do
if group and util.tbl_contains(group, el) and string.match(tbl[1], el) and tbl[2] ~= nil then
tbl[2] = config.group.styler .. tbl[2] .. config.group.styler
if el == tbl[1] then
table.remove(tbl, 1)
end
if control[name] then
control[tostring(name)] = false
table.insert(result, "---")
table.insert(result, "**" .. name .. "**")
end
end
end
local ref = require("pretty_hover.parser.references")
tbl = ref.check_line_for_references(tbl, config)
line = util.join_table(tbl, " ")
table.insert(result, line)
if insertEmptyLine then
table.insert(result, "")
end
return result
end
--- Converts a string returned by response.result.contents.value from vim.lsp[textDocument/hover] to markdown.
---@param toConvert string|table Documentation of the string to be converted.
---@param config table Table of options to be used for the conversion to the markdown language.
---@param hl_data table Table of control variables to be used for the pop-up window highlighting.
---@return table Converted table of strings from doxygen to markdown.
---@overload fun(toConvert: string, config: table, hl_data: table): table
---@overload fun(toConvert: table, config: table, hl_data: table): table
function M.convert_to_markdown(toConvert, config, hl_data)
config.one_liner = false
local result = {}
local control = {}
for name, group in pairs(config.group.detect) do
control[tostring(name)] = true
end
local lines = toConvert
if type(toConvert) == "string" then
lines = util.split(toConvert, "([^\n]*)\n?")
end
if #lines == 0 then
return result
end
-- Remove footer padding. The last line is always empty.
if lines[#lines] == "" then
table.remove(lines, #lines)
end
for name, _ in pairs(config.hl) do
hl_data.lines[tostring(name)] = {}
end
for _, line in pairs(lines) do
local toAdd = M.transform_line(line, config, control, hl_data)
vim.list_extend(result, toAdd)
for name, group in pairs(hl_data.lines) do
if group.detected then
group.detected = false
table.insert(hl_data.lines[tostring(name)], {
line_nr = util.printable_table_size(result) - 2,
to = (config.hl[tostring(name)].line and -1 or string.len(hl_data.replacement))
})
end
end
end
-- If the message is only one-liner, remove the code block.
-- See issue #24
if #result == 3 and result[#result] == "```" then
result = { result[2] }
config.one_liner = true
end
return result
end
return M
================================================
FILE: lua/pretty_hover/parser/references.lua
================================================
local M = {}
local util = require("pretty_hover.core.util")
--- Detect if the check line is already in bold.
---@param table_line table Table of words to be checked.
---@return boolean True if the line style is bold, false otherwise.
function M.is_bold(table_line)
local last_word = table_line[#table_line]
return table_line[1]:find("*") == 1 and last_word:find("*") == #last_word-1
end
--- Based on the tabled_line markdown representation, this function returns the surrounding string.
---@param tabled_line table Table of words to be checked.
---@param config table Table of options to be used for the conversion to the markdown language.
---@return table The first element of the table is boolean which indicates if the string is already converted. Second element is the surrounding string.
function M.get_surround_string(tabled_line, config)
if tabled_line and #tabled_line > 0 and M.is_bold(tabled_line) then
return { is_brief = true, marker = config.references.styler[2]}
else
return { is_brief = false, marker = config.references.styler[1]}
end
end
--- Checks the current line on the index if it is an opening reference.
---@param tabled_line table Table of strings representing current line.
---@param index integer Index of the line to be checked.
---@return boolean True if the reference is opening, false otherwise.
function M.is_opening_reference(tabled_line, index)
if not tabled_line or not tabled_line[index + 1] or not tabled_line[index] then
return false;
end
return (tabled_line[index]:find("[(]") or tabled_line[index+1]:find("[(]")) and not tabled_line[index+1]:find("[)]")
end
--- Surrounds the reference from the front. If the reference is opened, it is not closed.
---@param tabled_line table Table of strings representing current line.
---@param index integer Index of the word to be checked.
---@param config table Table of options to be used for the conversion to the markdown language.
---@param surround table Table of the surrounding strings.
function M.surround_references(tabled_line, index, config, surround)
-- Surround the word in brief line.
if surround.is_brief then
-- End the brief line formatting if possible.
if tabled_line[index-1] then
tabled_line[index-1] = tabled_line[index-1] .. config.line.styler
end
-- Start the reference formatting.
tabled_line[index] = surround.marker .. tabled_line[index+1]
-- End the reference formatting and start the brief line formatting if possible.
if tabled_line[index+2] and not surround.openedReference then
tabled_line[index] = tabled_line[index] .. surround.marker
tabled_line[index+2] = config.line.styler .. tabled_line[index+2]
elseif tabled_line[index+2] then
-- The reference is opened so we don't add ending reference.
else
tabled_line[index] = string.sub(tabled_line[index], 1, #tabled_line[index]-2) .. surround.marker
if tabled_line[index]:find('[)]') then
surround.openedReference = false
end
end
-- Surround the word in non-brief line.
else
if not tabled_line or not tabled_line[index + 1] or not tabled_line[index] then
return;
end
tabled_line[index] = surround.marker .. tabled_line[index+1]
-- End the reference formatting and start the brief line formatting if possible.
if not surround.openedReference then
tabled_line[index] = tabled_line[index] .. surround.marker
end
end
end
--- Close the opened reference if it is opened.
---@param tabled_line table Table of strings representing current line.
---@param index integer Index of the word to be checked.
---@param config table Table of options to be used for the conversion to the markdown language.
---@param surround table Table of the surrounding strings.
function M.close_opened_references(tabled_line, index, config, surround)
if surround.openedReference and tabled_line[index]:find("[)]") then
if surround.is_brief then
if tabled_line[index+1] then
tabled_line[index] = tabled_line[index] .. surround.marker
tabled_line[index+1] = config.line.styler .. tabled_line[index+1]
else
tabled_line[index] = string.sub(tabled_line[index], 1, #tabled_line[index]-2) .. surround.marker
end
else
tabled_line[index] = tabled_line[index] .. surround.marker
end
surround.openedReference = false
end
end
--- Detects the HTML style hyperlinks in the line and converts them to markdown.
---@param tabled_line table Line from the hover message split into words.
---@param word string Word to be checked.
---@param index integer Index of the word from the @c tabled_line to be checked.
function M.detect_hyper_links(tabled_line, word, index)
if word ~= "\\<a" then
return
end
table.remove(tabled_line, index)
word = tabled_line[index]
local whole_link = vim.split(word, "\"", {trimempty = true})
local link = whole_link[2]
local styler = require("pretty_hover").get_config().line.styler
-- The link is not closed in the same part of the line separated by the space.
if word:sub(1,4) == "href" and word:match("\\</a>") then
-- Handle the case of the link being the last word in the line.
styler = word:match("\\</a>" .. styler) ~= nil and styler or ""
local link_text = whole_link[3]:match("([%w_:.]+)\\</a>") or link
tabled_line[index] = "[" .. link_text .. "](" .. link .. ")" .. styler
-- The link is closed in the next part of the line.
elseif word:sub(1,4) == "href" then
local link_text = whole_link[3]:sub(2)
table.remove(tabled_line, index)
-- Accumulate all the words until the closing tag.
while not tabled_line[index]:match("\\</a>") do
link_text = link_text .. " " .. tabled_line[index]
table.remove(tabled_line, index)
end
-- The last word may be a space or just the closing tag.
local final_word = tabled_line[index]:match("(%w+)\\</a>") or ""
link_text = link_text .. " " .. final_word
-- Handle the case of the link being the last word in the line.
styler = tabled_line[index]:match("\\</a>" .. styler) ~= nil and styler or ""
tabled_line[index] = "[" .. link_text .. "](" .. link .. ")" ..styler
end
end
--- Remove escaping sequence before every character in the escapees string.
--- The used escapees are '*' by default. Be sure that adding character to the string will not break
--- the highlighting. e.g. adding _ will not do harm in words like __foo but in workds like __foo__.
--- The second one will be highlighted as bold. The first one will be shortened to _foo.
---
--- @param tabled_line table Line from the hover message split into words.
--- @param escapees table? Characters to be removed from the escape sequence.
local function remove_excape_characters(tabled_line, escapees)
escapees = vim.tbl_deep_extend("force", { "*" }, escapees or {})
local to_escape = table.concat(escapees)
for index, word in ipairs(tabled_line) do
tabled_line[index] = word:gsub("\\([" .. to_escape .. "])", "%1")
end
end
--- Converts all the references to markdown text.
---@param tabled_line table Words to be checked.
---@param config table Table of options to be used for the conversion to the markdown language.
---@return table Converted line to markdown.
function M.check_line_for_references(tabled_line, config)
local surround = M.get_surround_string(tabled_line, config)
surround.openedReference = false
remove_excape_characters(tabled_line)
for index, word in ipairs(tabled_line) do
if util.tbl_contains(config.references.detect, word) then
-- Handle the parenthesis surrounding the reference.
if tabled_line[index]:sub(1,1) == "(" then
tabled_line[index+1] = "(" .. tabled_line[index+1]
end
if M.is_opening_reference(tabled_line, index) then
surround.openedReference = true
end
M.surround_references(tabled_line, index, config, surround)
table.remove(tabled_line, index + 1)
end
M.close_opened_references(tabled_line, index, config, surround)
M.detect_hyper_links(tabled_line, word, index)
-- We cannot use `word` because it will change also the hyperlinks.
tabled_line[index] = tabled_line[index]:gsub("\\(<%w+)", "%1")
end
return tabled_line
end
return M
gitextract_zbixqniw/
├── .github/
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── examples/
│ └── parsing.lua
└── lua/
└── pretty_hover/
├── config.lua
├── core/
│ ├── compatibility.lua
│ └── util.lua
├── highlight.lua
├── init.lua
├── local_request.lua
├── number.lua
└── parser/
├── init.lua
├── parser.lua
└── references.lua
Condensed preview — 17 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (61K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 834,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 595,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
},
{
"path": ".gitignore",
"chars": 21,
"preview": "lua/.luarc.json\ntest\n"
},
{
"path": "CONTRIBUTING.md",
"chars": 1232,
"preview": "Anybody is welcome to contribute to this project. If you have any idea, how to improve any\nfeatures or you have an idea "
},
{
"path": "LICENSE",
"chars": 1071,
"preview": "MIT License\n\nCopyright (c) 2023 Filip Lobpreis\n\nPermission is hereby granted, free of charge, to any person obtaining a "
},
{
"path": "README.md",
"chars": 8507,
"preview": "<h1 align=\"center\">\npretty_hover\n</h1>\n\n<p align=\"center\">\n<a href=\"https://github.com/Fildo7525/pretty_hover/stargazers"
},
{
"path": "examples/parsing.lua",
"chars": 2745,
"preview": "local text = [[### function `main`\r\n---\r\n\t→ `int`\r\nParameters:\r\n\t- `int argc`\r\n\t- `char ** argv`\r\n\r\n@brief Neque porro q"
},
{
"path": "lua/pretty_hover/config.lua",
"chars": 1897,
"preview": "---@class PrettyHoverConfig\nlocal M = {\n\t_config = {},\n\n\theader = {\n\t\tdetect = {\"[\\\\@]class\"},\n\t\tstyler = '###',\n\t},\n\tli"
},
{
"path": "lua/pretty_hover/core/compatibility.lua",
"chars": 532,
"preview": "local M = {}\n\n--- Function that encapsulates the changes in nvim api for getting the active clients.\n---\n--- @return tab"
},
{
"path": "lua/pretty_hover/core/util.lua",
"chars": 6654,
"preview": "local api = vim.api\nlocal hl = require(\"pretty_hover.highlight\")\nlocal hover_ns = api.nvim_create_namespace('pretty_hove"
},
{
"path": "lua/pretty_hover/highlight.lua",
"chars": 3076,
"preview": "local M = {}\n\nlocal compatibility = require \"pretty_hover.core.compatibility\"\n\nlocal api = vim.api\n\n--- Convert HEX colo"
},
{
"path": "lua/pretty_hover/init.lua",
"chars": 1513,
"preview": "local api = vim.api\nlocal cfg = require(\"pretty_hover.config\")\nlocal h_util = require(\"pretty_hover.core.util\")\nlocal lo"
},
{
"path": "lua/pretty_hover/local_request.lua",
"chars": 5659,
"preview": "local api = vim.api\nlocal lsp = vim.lsp\nlocal util = vim.lsp.util\nlocal hover_ns = api.nvim_create_namespace('pretty_hov"
},
{
"path": "lua/pretty_hover/number.lua",
"chars": 3842,
"preview": "local M = {}\n\n--- Convert the input number to the specified base.\n--- @param num number Number to be converted.\n--- @par"
},
{
"path": "lua/pretty_hover/parser/init.lua",
"chars": 3756,
"preview": "local M = {}\r\n\r\nlocal parser = require(\"pretty_hover.parser.parser\")\r\n\r\n---@class ParserOutput\r\n---@field public text ta"
},
{
"path": "lua/pretty_hover/parser/parser.lua",
"chars": 5675,
"preview": "local util = require(\"pretty_hover.core.util\")\r\n\r\nlocal M = {\r\n\tbrief = {\r\n\t\tdetected = false,\r\n\t\toption = \"\",\r\n\t},\r\n\tte"
},
{
"path": "lua/pretty_hover/parser/references.lua",
"chars": 8067,
"preview": "local M = {}\n\nlocal util = require(\"pretty_hover.core.util\")\n\n--- Detect if the check line is already in bold.\n---@param"
}
]
About this extraction
This page contains the full source code of the Fildo7525/pretty_hover GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 17 files (54.4 KB), approximately 15.0k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.