1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
local function highlight_formatted(line, linenr)
local chars = {}
local prev_char = ''
local overstrike, escape = false, false
local hls = {} -- Store highlight groups as { attr, start, end }
local NONE, BOLD, UNDERLINE, ITALIC = 0, 1, 2, 3
local hl_groups = {[BOLD]="manBold", [UNDERLINE]="manUnderline", [ITALIC]="manItalic"}
local attr = NONE
local byte = 0 -- byte offset
local function end_attr_hl(attr)
for i, hl in ipairs(hls) do
if hl[1] == attr and hl[3] == -1 then
hl[3] = byte
hls[i] = hl
end
end
end
local function add_attr_hl(code)
local on = true
if code == 0 then
attr = NONE
on = false
elseif code == 1 then
attr = BOLD
elseif code == 21 or code == 22 then
attr = BOLD
on = false
elseif code == 3 then
attr = ITALIC
elseif code == 23 then
attr = ITALIC
on = false
elseif code == 4 then
attr = UNDERLINE
elseif code == 24 then
attr = UNDERLINE
on = false
else
attr = NONE
return
end
if on then
hls[#hls + 1] = {attr, byte, -1}
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
-- Break input into UTF8 characters
for char in line:gmatch("[^\128-\191][\128-\191]*") do
if overstrike then
local last_hl = hls[#hls]
if char == prev_char then
if char == '_' and attr == UNDERLINE and last_hl and last_hl[3] == byte then
-- 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
char = [[·]]
elseif prev_char == [[·]] and char == 'o' then
-- bullet (additional handling for '+^H+^Ho^Ho')
attr = BOLD
char = [[·]]
else
-- use plain char
attr = NONE
end
-- Grow the previous highlight group if possible
if last_hl and last_hl[1] == attr and last_hl[3] == byte then
last_hl[3] = byte + #char
else
hls[#hls + 1] = {attr, byte, byte + #char}
end
overstrike = false
prev_char = ''
byte = byte + #char
chars[#chars + 1] = char
elseif escape then
-- Use prev_char to store the escape sequence
prev_char = prev_char .. char
local sgr = prev_char:match("^%[([\020-\063]*)m$")
if sgr then
local match = ''
while sgr and #sgr > 0 do
match, sgr = sgr:match("^(%d*);?(.*)")
add_attr_hl(match + 0) -- coerce to number
end
escape = false
elseif not prev_char:match("^%[[\020-\063]*$") then
-- Stop looking if this isn't a partial CSI sequence
escape = false
end
elseif char == "\027" then
escape = true
prev_char = ''
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
for i, hl in ipairs(hls) do
if hl[1] ~= NONE then
vim.api.nvim_buf_add_highlight(
0,
-1,
hl_groups[hl[1]],
linenr - 1,
hl[2],
hl[3]
)
end
end
return table.concat(chars, '')
end
return { highlight_formatted = highlight_formatted }
|