neovim/runtime/lua/man.lua

171 lines
4.7 KiB
Lua
Raw Normal View History

require('vim.compat')
2017-12-24 10:16:58 -07:00
local buf_hls = {}
local function highlight_line(line, linenr)
2017-11-19 22:19:08 -07:00
local chars = {}
local prev_char = ''
2017-11-30 20:15:39 -07:00
local overstrike, escape = false, false
2017-12-24 10:16:58 -07:00
local hls = {} -- Store highlight groups as { attr, start, final }
2017-11-30 20:15:39 -07:00
local NONE, BOLD, UNDERLINE, ITALIC = 0, 1, 2, 3
local hl_groups = {[BOLD]="manBold", [UNDERLINE]="manUnderline", [ITALIC]="manItalic"}
2017-11-19 22:19:08 -07:00
local attr = NONE
local byte = 0 -- byte offset
2017-11-30 20:15:39 -07:00
local function end_attr_hl(attr)
for i, hl in ipairs(hls) do
2017-12-24 10:16:58 -07:00
if hl.attr == attr and hl.final == -1 then
hl.final = byte
2017-11-30 20:15:39 -07:00
hls[i] = hl
end
end
end
local function add_attr_hl(code)
2017-12-24 10:16:58 -07:00
local continue_hl = true
2017-11-30 20:15:39 -07:00
if code == 0 then
attr = NONE
2017-12-24 10:16:58 -07:00
continue_hl = false
2017-11-30 20:15:39 -07:00
elseif code == 1 then
attr = BOLD
2017-12-24 10:16:58 -07:00
elseif code == 22 then
2017-11-30 20:15:39 -07:00
attr = BOLD
2017-12-24 10:16:58 -07:00
continue_hl = false
2017-11-30 20:15:39 -07:00
elseif code == 3 then
attr = ITALIC
elseif code == 23 then
attr = ITALIC
2017-12-24 10:16:58 -07:00
continue_hl = false
2017-11-30 20:15:39 -07:00
elseif code == 4 then
attr = UNDERLINE
elseif code == 24 then
attr = UNDERLINE
2017-12-24 10:16:58 -07:00
continue_hl = false
2017-11-30 20:15:39 -07:00
else
attr = NONE
return
end
2017-12-24 10:16:58 -07:00
if continue_hl then
hls[#hls + 1] = {attr=attr, start=byte, final=-1}
2017-11-30 20:15:39 -07:00
else
if attr == NONE then
for a, _ in pairs(hl_groups) do
end_attr_hl(a)
end
else
end_attr_hl(attr)
end
end
end
2017-12-24 10:16:58 -07:00
-- Break input into UTF8 code points. ASCII code points (from 0x00 to 0x7f)
-- can be represented in one byte. Any code point above that is represented by
-- a leading byte (0xc0 and above) and continuation bytes (0x80 to 0xbf, or
-- decimal 128 to 191).
2017-11-19 22:19:08 -07:00
for char in line:gmatch("[^\128-\191][\128-\191]*") do
if overstrike then
local last_hl = hls[#hls]
if char == prev_char then
2017-12-24 10:16:58 -07:00
if char == '_' and attr == UNDERLINE and last_hl and last_hl.final == byte then
2017-11-19 22:19:08 -07:00
-- This underscore is in the middle of an underlined word
attr = UNDERLINE
else
attr = BOLD
end
elseif prev_char == '_' then
-- char is underlined
attr = UNDERLINE
elseif prev_char == '+' and char == 'o' then
-- bullet (overstrike text '+^Ho')
attr = BOLD
2017-12-24 10:16:58 -07:00
char = '·'
elseif prev_char == '·' and char == 'o' then
2017-11-19 22:19:08 -07:00
-- bullet (additional handling for '+^H+^Ho^Ho')
attr = BOLD
2017-12-24 10:16:58 -07:00
char = '·'
2017-11-19 22:19:08 -07:00
else
-- use plain char
attr = NONE
end
-- Grow the previous highlight group if possible
2017-12-24 10:16:58 -07:00
if last_hl and last_hl.attr == attr and last_hl.final == byte then
last_hl.final = byte + #char
2017-11-19 22:19:08 -07:00
else
2017-12-24 10:16:58 -07:00
hls[#hls + 1] = {attr=attr, start=byte, final=byte + #char}
2017-11-19 22:19:08 -07:00
end
overstrike = false
prev_char = ''
byte = byte + #char
chars[#chars + 1] = char
2017-11-30 20:15:39 -07:00
elseif escape then
-- Use prev_char to store the escape sequence
prev_char = prev_char .. char
2017-12-24 10:16:58 -07:00
-- We only want to match against SGR sequences, which consist of ESC
-- followed by '[', then a series of parameter and intermediate bytes in
-- the range 0x20 - 0x3f, then 'm'. (See ECMA-48, sections 5.4 & 8.3.117)
local sgr = prev_char:match("^%[([\032-\063]*)m$")
2017-11-30 20:15:39 -07:00
if sgr then
local match = ''
while sgr and #sgr > 0 do
2017-12-24 10:16:58 -07:00
-- Match against SGR parameters, which may be separated by ';'
2017-11-30 20:15:39 -07:00
match, sgr = sgr:match("^(%d*);?(.*)")
add_attr_hl(match + 0) -- coerce to number
end
escape = false
2017-12-24 10:16:58 -07:00
elseif not prev_char:match("^%[[\032-\063]*$") then
2017-11-30 20:15:39 -07:00
-- Stop looking if this isn't a partial CSI sequence
escape = false
end
elseif char == "\027" then
escape = true
prev_char = ''
2017-11-19 22:19:08 -07:00
elseif char == "\b" then
overstrike = true
prev_char = chars[#chars]
byte = byte - #prev_char
chars[#chars] = nil
else
byte = byte + #char
chars[#chars + 1] = char
end
end
2017-12-24 10:16:58 -07:00
for _, hl in ipairs(hls) do
if hl.attr ~= NONE then
buf_hls[#buf_hls + 1] = {
2017-11-19 22:19:08 -07:00
0,
-1,
2017-12-24 10:16:58 -07:00
hl_groups[hl.attr],
2017-11-19 22:19:08 -07:00
linenr - 1,
2017-12-24 10:16:58 -07:00
hl.start,
hl.final
}
2017-11-19 22:19:08 -07:00
end
end
return table.concat(chars, '')
end
2017-12-24 10:16:58 -07:00
local function highlight_man_page()
local mod = vim.api.nvim_buf_get_option(0, "modifiable")
vim.api.nvim_buf_set_option(0, "modifiable", true)
2017-12-24 10:16:58 -07:00
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
for i, line in ipairs(lines) do
lines[i] = highlight_line(line, i)
end
vim.api.nvim_buf_set_lines(0, 0, -1, false, lines)
for _, args in ipairs(buf_hls) do
vim.api.nvim_buf_add_highlight(unpack(args))
end
buf_hls = {}
vim.api.nvim_buf_set_option(0, "modifiable", mod)
2017-12-24 10:16:58 -07:00
end
return { highlight_man_page = highlight_man_page }