2018-11-29 14:51:20 -07:00
|
|
|
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
|
|
|
|
|
2018-11-30 13:30:05 -07:00
|
|
|
local function end_attr_hl(attr_)
|
2017-11-30 20:15:39 -07:00
|
|
|
for i, hl in ipairs(hls) do
|
2018-11-30 13:30:05 -07:00
|
|
|
if hl.attr == attr_ and hl.final == -1 then
|
2017-12-24 10:16:58 -07:00
|
|
|
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$')
|
2019-06-18 13:35:35 -07:00
|
|
|
-- Ignore escape sequences with : characters, as specified by ITU's T.416
|
|
|
|
-- Open Document Architecture and interchange format.
|
|
|
|
if sgr and not string.find(sgr, ':') then
|
2018-11-30 13:30:05 -07:00
|
|
|
local match
|
2017-11-30 20:15:39 -07:00
|
|
|
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()
|
2018-01-15 12:14:27 -07:00
|
|
|
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 = {}
|
|
|
|
|
2018-01-15 12:14:27 -07:00
|
|
|
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 }
|