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
142
143
144
145
146
147
148
149
150
151
152
153
|
-- mod-version:3
local core = require "core"
local command = require "core.command"
local common = require "core.common"
local config = require "core.config"
local keymap = require "core.keymap"
config.plugins.lfautoinsert = common.merge({ map = {
["{%s*\n"] = "}",
["%(%s*\n"] = ")",
["%f[[]%[%s*\n"] = "]",
["=%s*\n"] = false,
[":%s*\n"] = false,
["->%s*\n"] = false,
["^%s*<([^/!][^%s>]*)[^>]*>%s*\n"] = "</$TEXT>",
["^%s*{{#([^/][^%s}]*)[^}]*}}%s*\n"] = "{{/$TEXT}}",
["/%*%s*\n"] = "*/",
["c/c++"] = {
file_patterns = {
"%.c$", "%.h$", "%.inl$", "%.cpp$", "%.hpp$",
"%.cc$", "%.C$", "%.cxx$", "%.c++$", "%.hh$",
"%.H$", "%.hxx$", "%.h++$"
},
map = {
["^#if.*\n"] = "#endif",
["^#else.*\n"] = "#endif",
}
},
["lua"] = {
file_patterns = { "%.lua$", "%.nelua$" },
map = {
["%f[%w]do%s*\n"] = "end",
["%f[%w]then%s*\n"] = "end",
["%f[%w]else%s*\n"] = "end",
["%f[%w]repeat%s*\n"] = "until",
["%f[%w]function.*%)%s*\n"] = "end",
["%[%[%s*\n"] = "]]"
}
},
} }, config.plugins.lfautoinsert)
local function get_autoinsert_map(filename)
local map = {}
if not filename then return map end
for pattern, closing in pairs(config.plugins.lfautoinsert.map) do
if type(closing) == "table" then
if common.match_pattern(filename, closing.file_patterns) then
for p, e in pairs(closing.map) do
map[p] = e
end
end
else
map[pattern] = closing
end
end
return map
end
local function indent_size(doc, line)
local text = doc.lines[line] or ""
local s, e = text:find("^[\t ]*")
return e - s
end
command.add("core.docview!", {
["autoinsert:newline"] = function(dv)
local not_applied = { }
local fallback = true
local doc = dv.doc
local indent_type, soft_size = doc:get_indent_info()
local indent_string = indent_type == "hard" and "\t" or string.rep(" ", soft_size)
for idx, line, col, _, _ in doc:get_selections(true, true) do
-- We need to add `\n` to keep compatibility with the patterns
-- that expected a newline to be placed where the caret is.
local text = doc.lines[line]:sub(1, col - 1) .. '\n'
local remainder = doc.lines[line]:sub(col, -1)
local line_indent_size = indent_size(doc, line)
-- Add more lines to remainder to detect `close`
for i=line+1,#doc.lines do
-- Stop adding when we find a line with a different indent level
if #doc.lines[i] > 1 and indent_size(doc, i) ~= line_indent_size then break end
remainder = remainder .. doc.lines[i]
-- Continue adding until the first non-empty line
if string.find(doc.lines[i], "%S") then
break
end
end
local current_indent = text:match("^[\t ]*")
local pre, post
for ptn, close in pairs(get_autoinsert_map(doc.filename)) do
local s, _, str = text:find(ptn)
if s then
pre = string.format("\n%s%s", current_indent, indent_string)
if not close then break end
close = str and close:gsub("$TEXT", str) or close
if (col == #doc.lines[line] and indent_size(doc, line + 1) <= line_indent_size) or
(col < #doc.lines[line])
then
local clean_remainder = remainder:match("%s*(.*)")
-- Avoid inserting `close` if it's already present
local already_closed = clean_remainder:find(close, 1, true) == 1
if not already_closed and col == #doc.lines[line] then
-- Add the `close` only if we're at the end of the line
post = string.format("\n%s%s", current_indent, close)
elseif already_closed and col < #doc.lines[line] then
-- Indent the already present `close`
-- TODO: cleanup the spaces between the caret and the `close`
post = string.format("\n%s", current_indent)
end
end
break
end
end
if pre or post then
fallback = false
doc:text_input(pre or "", idx)
local l, c, l2, c2 = doc:get_selection_idx(idx)
doc:text_input(post or "", idx)
doc:set_selections(idx, l, c, l2, c2)
else
table.insert(not_applied, {idx, current_indent})
end
end
-- Only call the fallback if no autoinsert was applied
if fallback then
command.perform("doc:newline")
else
for _,v in ipairs(not_applied) do
local idx, indent = table.unpack(v)
doc:text_input("\n"..indent, idx)
end
end
end
})
keymap.add {
["return"] = { "autoinsert:newline" }
}
return {
add = function(file_patterns, map)
table.insert(
config.plugins.lfautoinsert.map,
{ file_patterns = file_patterns, map=map }
)
end
}
|