mirror of
https://github.com/neovim/neovim.git
synced 2024-12-24 21:25:04 -07:00
fix(tohtml): support ranges again
This commit is contained in:
parent
a5d4e3467d
commit
118ae7e5ed
@ -4412,7 +4412,7 @@ vim.text.hexencode({str}) *vim.text.hexencode()*
|
|||||||
Lua module: tohtml *vim.tohtml*
|
Lua module: tohtml *vim.tohtml*
|
||||||
|
|
||||||
|
|
||||||
:TOhtml {file} *:TOhtml*
|
:[range]TOhtml {file} *:TOhtml*
|
||||||
Converts the buffer shown in the current window to HTML, opens the generated
|
Converts the buffer shown in the current window to HTML, opens the generated
|
||||||
HTML in a new split window, and saves its contents to {file}. If {file} is not
|
HTML in a new split window, and saves its contents to {file}. If {file} is not
|
||||||
given, a temporary file (created by |tempname()|) is used.
|
given, a temporary file (created by |tempname()|) is used.
|
||||||
@ -4434,6 +4434,8 @@ tohtml.tohtml({winid}, {opt}) *tohtml.tohtml.tohtml()*
|
|||||||
• {width}? (`integer`, default: 'textwidth' if non-zero or
|
• {width}? (`integer`, default: 'textwidth' if non-zero or
|
||||||
window width otherwise) Width used for items which are
|
window width otherwise) Width used for items which are
|
||||||
either right aligned or repeat a character infinitely.
|
either right aligned or repeat a character infinitely.
|
||||||
|
• {range}? (`integer[]`, default: entire buffer) Range of
|
||||||
|
rows to use.
|
||||||
|
|
||||||
Return: ~
|
Return: ~
|
||||||
(`string[]`)
|
(`string[]`)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
--- @brief
|
--- @brief
|
||||||
---<pre>help
|
---<pre>help
|
||||||
---:TOhtml {file} *:TOhtml*
|
---:[range]TOhtml {file} *:TOhtml*
|
||||||
---Converts the buffer shown in the current window to HTML, opens the generated
|
---Converts the buffer shown in the current window to HTML, opens the generated
|
||||||
---HTML in a new split window, and saves its contents to {file}. If {file} is not
|
---HTML in a new split window, and saves its contents to {file}. If {file} is not
|
||||||
---given, a temporary file (created by |tempname()|) is used.
|
---given, a temporary file (created by |tempname()|) is used.
|
||||||
@ -40,7 +40,8 @@
|
|||||||
--- @field winid integer
|
--- @field winid integer
|
||||||
--- @field bufnr integer
|
--- @field bufnr integer
|
||||||
--- @field width integer
|
--- @field width integer
|
||||||
--- @field buflen integer
|
--- @field start integer
|
||||||
|
--- @field end_ integer
|
||||||
|
|
||||||
--- @class (private) vim.tohtml.styletable
|
--- @class (private) vim.tohtml.styletable
|
||||||
--- @field [integer] vim.tohtml.line (integer: (1-index, exclusive))
|
--- @field [integer] vim.tohtml.line (integer: (1-index, exclusive))
|
||||||
@ -393,7 +394,7 @@ end
|
|||||||
|
|
||||||
--- @param state vim.tohtml.state
|
--- @param state vim.tohtml.state
|
||||||
local function styletable_syntax(state)
|
local function styletable_syntax(state)
|
||||||
for row = 1, state.buflen do
|
for row = state.start, state.end_ do
|
||||||
local prev_id = 0
|
local prev_id = 0
|
||||||
local prev_col = nil
|
local prev_col = nil
|
||||||
for col = 1, #vim.fn.getline(row) + 1 do
|
for col = 1, #vim.fn.getline(row) + 1 do
|
||||||
@ -413,7 +414,7 @@ end
|
|||||||
--- @param state vim.tohtml.state
|
--- @param state vim.tohtml.state
|
||||||
local function styletable_diff(state)
|
local function styletable_diff(state)
|
||||||
local styletable = state.style
|
local styletable = state.style
|
||||||
for row = 1, state.buflen do
|
for row = state.start, state.end_ do
|
||||||
local style_line = styletable[row]
|
local style_line = styletable[row]
|
||||||
local filler = vim.fn.diff_filler(row)
|
local filler = vim.fn.diff_filler(row)
|
||||||
if filler ~= 0 then
|
if filler ~= 0 then
|
||||||
@ -423,7 +424,7 @@ local function styletable_diff(state)
|
|||||||
{ { fill:rep(state.width), register_hl(state, 'DiffDelete') } }
|
{ { fill:rep(state.width), register_hl(state, 'DiffDelete') } }
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
if row == state.buflen + 1 then
|
if row == state.end_ + 1 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
local prev_id = 0
|
local prev_id = 0
|
||||||
@ -465,7 +466,9 @@ local function styletable_treesitter(state)
|
|||||||
if not query then
|
if not query then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
for capture, node, metadata in query:iter_captures(root, buf_highlighter.bufnr, 0, state.buflen) do
|
for capture, node, metadata in
|
||||||
|
query:iter_captures(root, buf_highlighter.bufnr, state.start - 1, state.end_)
|
||||||
|
do
|
||||||
local srow, scol, erow, ecol = node:range()
|
local srow, scol, erow, ecol = node:range()
|
||||||
--- @diagnostic disable-next-line: invisible
|
--- @diagnostic disable-next-line: invisible
|
||||||
local c = q._query.captures[capture]
|
local c = q._query.captures[capture]
|
||||||
@ -519,7 +522,7 @@ local function _styletable_extmarks_virt_text(state, extmark, namespaces)
|
|||||||
--- @type integer,integer
|
--- @type integer,integer
|
||||||
local row, col = extmark[2], extmark[3]
|
local row, col = extmark[2], extmark[3]
|
||||||
if
|
if
|
||||||
row < state.buflen
|
row < vim.api.nvim_buf_line_count(state.bufnr)
|
||||||
and (
|
and (
|
||||||
extmark[4].virt_text_pos == 'inline'
|
extmark[4].virt_text_pos == 'inline'
|
||||||
or extmark[4].virt_text_pos == 'eol'
|
or extmark[4].virt_text_pos == 'eol'
|
||||||
@ -628,7 +631,7 @@ end
|
|||||||
local function styletable_folds(state)
|
local function styletable_folds(state)
|
||||||
local styletable = state.style
|
local styletable = state.style
|
||||||
local has_folded = false
|
local has_folded = false
|
||||||
for row = 1, state.buflen do
|
for row = state.start, state.end_ do
|
||||||
if vim.fn.foldclosed(row) > 0 then
|
if vim.fn.foldclosed(row) > 0 then
|
||||||
has_folded = true
|
has_folded = true
|
||||||
styletable[row].hide = true
|
styletable[row].hide = true
|
||||||
@ -650,7 +653,7 @@ end
|
|||||||
local function styletable_conceal(state)
|
local function styletable_conceal(state)
|
||||||
local bufnr = state.bufnr
|
local bufnr = state.bufnr
|
||||||
vim._with({ buf = bufnr }, function()
|
vim._with({ buf = bufnr }, function()
|
||||||
for row = 1, state.buflen do
|
for row = state.start, state.end_ do
|
||||||
--- @type table<integer,[integer,integer,string]>
|
--- @type table<integer,[integer,integer,string]>
|
||||||
local conceals = {}
|
local conceals = {}
|
||||||
local line_len_exclusive = #vim.fn.getline(row) + 1
|
local line_len_exclusive = #vim.fn.getline(row) + 1
|
||||||
@ -768,7 +771,7 @@ local function styletable_statuscolumn(state)
|
|||||||
local max = tonumber(foldcolumn:match('^%w-:(%d)')) or 1
|
local max = tonumber(foldcolumn:match('^%w-:(%d)')) or 1
|
||||||
local maxfold = 0
|
local maxfold = 0
|
||||||
vim._with({ buf = state.bufnr }, function()
|
vim._with({ buf = state.bufnr }, function()
|
||||||
for row = 1, vim.api.nvim_buf_line_count(state.bufnr) do
|
for row = state.start, state.end_ do
|
||||||
local foldlevel = vim.fn.foldlevel(row)
|
local foldlevel = vim.fn.foldlevel(row)
|
||||||
if foldlevel > maxfold then
|
if foldlevel > maxfold then
|
||||||
maxfold = foldlevel
|
maxfold = foldlevel
|
||||||
@ -783,7 +786,7 @@ local function styletable_statuscolumn(state)
|
|||||||
|
|
||||||
--- @type table<integer,any>
|
--- @type table<integer,any>
|
||||||
local statuses = {}
|
local statuses = {}
|
||||||
for row = 1, state.buflen do
|
for row = state.start, state.end_ do
|
||||||
local status = vim.api.nvim_eval_statusline(
|
local status = vim.api.nvim_eval_statusline(
|
||||||
statuscolumn,
|
statuscolumn,
|
||||||
{ winid = state.winid, use_statuscol_lnum = row, highlights = true }
|
{ winid = state.winid, use_statuscol_lnum = row, highlights = true }
|
||||||
@ -833,7 +836,7 @@ local function styletable_listchars(state)
|
|||||||
})
|
})
|
||||||
|
|
||||||
if listchars.eol then
|
if listchars.eol then
|
||||||
for row = 1, state.buflen do
|
for row = state.start, state.end_ do
|
||||||
local style_line = state.style[row]
|
local style_line = state.style[row]
|
||||||
style_line_insert_overlay_char(
|
style_line_insert_overlay_char(
|
||||||
style_line,
|
style_line,
|
||||||
@ -1127,16 +1130,22 @@ end
|
|||||||
local function extend_pre(out, state)
|
local function extend_pre(out, state)
|
||||||
local styletable = state.style
|
local styletable = state.style
|
||||||
table.insert(out, '<pre>')
|
table.insert(out, '<pre>')
|
||||||
|
local out_start = #out
|
||||||
local hide_count = 0
|
local hide_count = 0
|
||||||
--- @type integer[]
|
--- @type integer[]
|
||||||
local stack = {}
|
local stack = {}
|
||||||
|
|
||||||
|
local before = ''
|
||||||
|
local after = ''
|
||||||
local function loop(row)
|
local function loop(row)
|
||||||
|
local inside = row <= state.end_ and row >= state.start
|
||||||
local style_line = styletable[row]
|
local style_line = styletable[row]
|
||||||
if style_line.hide and (styletable[row - 1] or {}).hide then
|
if style_line.hide and (styletable[row - 1] or {}).hide then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
if inside then
|
||||||
_extend_virt_lines(out, state, row)
|
_extend_virt_lines(out, state, row)
|
||||||
|
end
|
||||||
--Possible improvement (altermo):
|
--Possible improvement (altermo):
|
||||||
--Instead of looping over all the buffer characters per line,
|
--Instead of looping over all the buffer characters per line,
|
||||||
--why not loop over all the style_line cells,
|
--why not loop over all the style_line cells,
|
||||||
@ -1146,7 +1155,9 @@ local function extend_pre(out, state)
|
|||||||
end
|
end
|
||||||
local line = vim.api.nvim_buf_get_lines(state.bufnr, row - 1, row, false)[1] or ''
|
local line = vim.api.nvim_buf_get_lines(state.bufnr, row - 1, row, false)[1] or ''
|
||||||
local s = ''
|
local s = ''
|
||||||
|
if inside then
|
||||||
s = s .. _pre_text_to_html(state, row)
|
s = s .. _pre_text_to_html(state, row)
|
||||||
|
end
|
||||||
local true_line_len = #line + 1
|
local true_line_len = #line + 1
|
||||||
for k in pairs(style_line) do
|
for k in pairs(style_line) do
|
||||||
if type(k) == 'number' and k > true_line_len then
|
if type(k) == 'number' and k > true_line_len then
|
||||||
@ -1193,7 +1204,7 @@ local function extend_pre(out, state)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if cell[3] then
|
if cell[3] and inside then
|
||||||
s = s .. _virt_text_to_html(state, cell)
|
s = s .. _virt_text_to_html(state, cell)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1204,7 +1215,7 @@ local function extend_pre(out, state)
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
if hide_count == 0 then
|
if hide_count == 0 and inside then
|
||||||
s = s
|
s = s
|
||||||
.. _char_to_html(
|
.. _char_to_html(
|
||||||
state,
|
state,
|
||||||
@ -1213,12 +1224,20 @@ local function extend_pre(out, state)
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if row > state.end_ + 1 then
|
||||||
|
after = after .. s
|
||||||
|
elseif row < state.start then
|
||||||
|
before = s .. before
|
||||||
|
else
|
||||||
table.insert(out, s)
|
table.insert(out, s)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
for row = 1, state.buflen + 1 do
|
for row = 1, vim.api.nvim_buf_line_count(state.bufnr) + 1 do
|
||||||
loop(row)
|
loop(row)
|
||||||
end
|
end
|
||||||
|
out[out_start] = out[out_start] .. before
|
||||||
|
out[#out] = out[#out] .. after
|
||||||
assert(#stack == 0, 'an open HTML tag was never closed')
|
assert(#stack == 0, 'an open HTML tag was never closed')
|
||||||
table.insert(out, '</pre>')
|
table.insert(out, '</pre>')
|
||||||
end
|
end
|
||||||
@ -1250,6 +1269,7 @@ local function global_state_to_state(winid, global_state)
|
|||||||
if not width or width < 1 then
|
if not width or width < 1 then
|
||||||
width = vim.api.nvim_win_get_width(winid)
|
width = vim.api.nvim_win_get_width(winid)
|
||||||
end
|
end
|
||||||
|
local range = opt.range or { 1, vim.api.nvim_buf_line_count(bufnr) }
|
||||||
local state = setmetatable({
|
local state = setmetatable({
|
||||||
winid = winid == 0 and vim.api.nvim_get_current_win() or winid,
|
winid = winid == 0 and vim.api.nvim_get_current_win() or winid,
|
||||||
opt = vim.wo[winid],
|
opt = vim.wo[winid],
|
||||||
@ -1257,7 +1277,8 @@ local function global_state_to_state(winid, global_state)
|
|||||||
bufnr = bufnr,
|
bufnr = bufnr,
|
||||||
tabstop = (' '):rep(vim.bo[bufnr].tabstop),
|
tabstop = (' '):rep(vim.bo[bufnr].tabstop),
|
||||||
width = width,
|
width = width,
|
||||||
buflen = vim.api.nvim_buf_line_count(bufnr),
|
start = range[1],
|
||||||
|
end_ = range[2],
|
||||||
}, { __index = global_state })
|
}, { __index = global_state })
|
||||||
return state --[[@as vim.tohtml.state]]
|
return state --[[@as vim.tohtml.state]]
|
||||||
end
|
end
|
||||||
@ -1316,35 +1337,22 @@ local function state_generate_style(state)
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param winid integer[]|integer
|
--- @param winid integer
|
||||||
--- @param opt? vim.tohtml.opt
|
--- @param opt? vim.tohtml.opt
|
||||||
--- @return string[]
|
--- @return string[]
|
||||||
local function win_to_html(winid, opt)
|
local function win_to_html(winid, opt)
|
||||||
if type(winid) == 'number' then
|
|
||||||
winid = { winid }
|
|
||||||
end
|
|
||||||
--- @cast winid integer[]
|
|
||||||
assert(#winid > 0, 'no window specified')
|
|
||||||
opt = opt or {}
|
opt = opt or {}
|
||||||
local title = table.concat(
|
local title = vim.api.nvim_buf_get_name(vim.api.nvim_win_get_buf(winid))
|
||||||
vim.tbl_map(vim.api.nvim_buf_get_name, vim.tbl_map(vim.api.nvim_win_get_buf, winid)),
|
|
||||||
','
|
|
||||||
)
|
|
||||||
local global_state = opt_to_global_state(opt, title)
|
local global_state = opt_to_global_state(opt, title)
|
||||||
--- @type vim.tohtml.state[]
|
local state = global_state_to_state(winid, global_state)
|
||||||
local states = {}
|
|
||||||
for _, i in ipairs(winid) do
|
|
||||||
local state = global_state_to_state(i, global_state)
|
|
||||||
state_generate_style(state)
|
state_generate_style(state)
|
||||||
table.insert(states, state)
|
|
||||||
end
|
|
||||||
local html = {}
|
local html = {}
|
||||||
extend_html(html, function()
|
extend_html(html, function()
|
||||||
extend_head(html, global_state)
|
extend_head(html, global_state)
|
||||||
extend_body(html, function()
|
extend_body(html, function()
|
||||||
for _, state in ipairs(states) do
|
|
||||||
extend_pre(html, state)
|
extend_pre(html, state)
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
return html
|
return html
|
||||||
@ -1371,6 +1379,10 @@ local M = {}
|
|||||||
--- infinitely.
|
--- infinitely.
|
||||||
--- (default: 'textwidth' if non-zero or window width otherwise)
|
--- (default: 'textwidth' if non-zero or window width otherwise)
|
||||||
--- @field width? integer
|
--- @field width? integer
|
||||||
|
---
|
||||||
|
--- Range of rows to use.
|
||||||
|
--- (default: entire buffer)
|
||||||
|
--- @field range? integer[]
|
||||||
|
|
||||||
--- Converts the buffer shown in the window {winid} to HTML and returns the output as a list of string.
|
--- Converts the buffer shown in the window {winid} to HTML and returns the output as a list of string.
|
||||||
--- @param winid? integer Window to convert (defaults to current window)
|
--- @param winid? integer Window to convert (defaults to current window)
|
||||||
|
@ -5,8 +5,8 @@ vim.g.loaded_2html_plugin = true
|
|||||||
|
|
||||||
vim.api.nvim_create_user_command('TOhtml', function(args)
|
vim.api.nvim_create_user_command('TOhtml', function(args)
|
||||||
local outfile = args.args ~= '' and args.args or vim.fn.tempname() .. '.html'
|
local outfile = args.args ~= '' and args.args or vim.fn.tempname() .. '.html'
|
||||||
local html = require('tohtml').tohtml()
|
local html = require('tohtml').tohtml(0, { range = { args.line1, args.line2 } })
|
||||||
vim.fn.writefile(html, outfile)
|
vim.fn.writefile(html, outfile)
|
||||||
vim.cmd.split(outfile)
|
vim.cmd.split(outfile)
|
||||||
vim.bo.filetype = 'html'
|
vim.bo.filetype = 'html'
|
||||||
end, { bar = true, nargs = '?' })
|
end, { bar = true, nargs = '?', range = '%' })
|
||||||
|
@ -176,6 +176,44 @@ describe(':TOhtml', function()
|
|||||||
}, fn.readfile(out_file))
|
}, fn.readfile(out_file))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('expected internal html generated from range', function()
|
||||||
|
insert([[
|
||||||
|
line1
|
||||||
|
line2
|
||||||
|
line3
|
||||||
|
]])
|
||||||
|
local ns = api.nvim_create_namespace ''
|
||||||
|
api.nvim_buf_set_extmark(0, ns, 0, 0, { end_col = 1, end_row = 1, hl_group = 'Visual' })
|
||||||
|
exec('set termguicolors')
|
||||||
|
local bg = fn.synIDattr(fn.hlID('Normal'), 'bg#', 'gui')
|
||||||
|
local fg = fn.synIDattr(fn.hlID('Normal'), 'fg#', 'gui')
|
||||||
|
exec_lua [[
|
||||||
|
local html = vim.cmd'2,2TOhtml'
|
||||||
|
]]
|
||||||
|
local out_file = api.nvim_buf_get_name(api.nvim_get_current_buf())
|
||||||
|
eq({
|
||||||
|
'<!DOCTYPE html>',
|
||||||
|
'<html>',
|
||||||
|
'<head>',
|
||||||
|
'<meta charset="UTF-8">',
|
||||||
|
'<title></title>',
|
||||||
|
('<meta name="colorscheme" content="%s"></meta>'):format(api.nvim_get_var('colors_name')),
|
||||||
|
'<style>',
|
||||||
|
'* {font-family: monospace}',
|
||||||
|
('body {background-color: %s; color: %s}'):format(bg, fg),
|
||||||
|
'.Visual {background-color: #9b9ea4}',
|
||||||
|
'</style>',
|
||||||
|
'</head>',
|
||||||
|
'<body style="display: flex">',
|
||||||
|
'<pre><span class="Visual">',
|
||||||
|
'l</span>ine2',
|
||||||
|
'',
|
||||||
|
'</pre>',
|
||||||
|
'</body>',
|
||||||
|
'</html>',
|
||||||
|
}, fn.readfile(out_file))
|
||||||
|
end)
|
||||||
|
|
||||||
it('highlight attributes generated', function()
|
it('highlight attributes generated', function()
|
||||||
--Make sure to uncomment the attribute in `html_syntax_match()`
|
--Make sure to uncomment the attribute in `html_syntax_match()`
|
||||||
exec('hi LINE gui=' .. table.concat({
|
exec('hi LINE gui=' .. table.concat({
|
||||||
|
Loading…
Reference in New Issue
Block a user