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
|
-- mod-version:3
local core = require "core"
local common = require "core.common"
local config = require "core.config"
local command = require "core.command"
config.plugins.indent_convert = common.merge({
-- set to false to avoid updating the document indent type
update_indent_type = true,
-- The config specification used by the settings gui
config_spec = {
name = "Indent Convert",
{
label = "Update Indent Type",
description = "Disable to avoid updating the document indent type.",
path = "update_indent_type",
type = "toggle",
default = true
}
}
}, config.plugins.indent_convert)
local zero_pattern = _VERSION == "Lua 5.1" and "%z" or "\0"
-- TODO: only set document indent type if there are no selections
-- TODO: correctly restore selections accounting for the offset caused by the conversion
-- To replace N spaces with tabs, we match the last N spaces before the start of
-- the actual code and replace them with a tab.
-- We repeat this until we can't find any more spaces before the code.
-- The problem we encounter with this method is that if we have less than N
-- remaining spaces, those will end up at the start of the line.
-- Eg:
-- int main() {
-- __printf("Hello world\n");
-- ___return 0;
-- }
--
-- Becomes
-- int main() {
-- #printf("Hello world\n");
-- _#return 0;
-- }
--
-- Instead of
-- int main() {
-- #printf("Hello world\n");
-- #_return 0;
-- }
-- With regex we could do something like
-- `regex.gsub("(^(?: {2})*)(?: {2})", "\\1\t")`
-- but the implementation of `regex.gsub` is very slow.
--
-- The workaround is to find the longest possible repetition of N*X spaces and
-- use that information to replace the longest repetition of spaces starting
-- from the beginning of the line, then the second longest...
local function spaces_replacer(text, indent_size)
local spaces = string.rep(" ", indent_size)
local total = 0
local n
local reps = 0
-- find the longest repetition of indent_size*spaces
repeat
reps = reps + 1
local s, _ = string.find(text, "%f[^"..zero_pattern.."\n]"..string.rep(spaces, reps))
until not s
reps = reps - 1
while reps > 0 do
text, n = string.gsub(text,
"(%f[^"..zero_pattern.."\n])("..string.rep(spaces, reps)..")",
"%1"..string.rep("\t", reps))
total = total + n
reps = reps - 1
end
return text, total
end
local function tabs_replacer(text, indent_size)
local spaces = string.rep(" ", indent_size)
local total = 0
local n
-- replace the last tab before the text until there aren't anymore
repeat
text, n = string.gsub(text, "(%f[^"..zero_pattern.."\n]\t*)(\t)", "%1"..spaces)
total = total + n
until n == 0
return text, total
end
local function replacer(doc, fn, indent_size)
return function(text)
return fn(text, indent_size)
end
end
local function get_indent_size(doc)
local indent_size = config.indent_size
if type(doc.get_indent_info) == "function" then
-- use the new `Doc:get_indent_info` function
indent_size = select(2, doc:get_indent_info())
end
return indent_size
end
local function tabs_to_spaces()
local doc = core.active_view.doc
local indent_size = get_indent_size(doc)
local selections = doc.selections
doc:replace(replacer(doc, tabs_replacer, indent_size))
doc.selections = selections
doc:sanitize_selection()
if config.plugins.indent_convert.update_indent_type then
doc.indent_info = {
type = "soft",
size = indent_size,
confirmed = true
}
end
end
local function spaces_to_tabs()
local doc = core.active_view.doc
local indent_size = get_indent_size(doc)
local selections = doc.selections
doc:replace(replacer(doc, spaces_replacer, indent_size))
doc.selections = selections
doc:sanitize_selection()
if config.plugins.indent_convert.update_indent_type then
doc.indent_info = {
type = "hard",
size = indent_size,
confirmed = true
}
end
end
command.add("core.docview", {
["indent-convert:tabs-to-spaces"] = tabs_to_spaces,
["indent-convert:spaces-to-tabs"] = spaces_to_tabs
}
)
|