aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md1
-rw-r--r--plugins/indent_convert.lua128
2 files changed, 129 insertions, 0 deletions
diff --git a/README.md b/README.md
index 7d0176a..dd7d7fd 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,7 @@ Plugin | Description
[`hidelinenumbers`](plugins/hidelinenumbers.lua?raw=1) | Hides the line numbers on the left of documents *([screenshot](https://user-images.githubusercontent.com/3920290/81692043-b8b19c00-9455-11ea-8d74-ad99be4b9c5f.png))*
[`hidestatus`](plugins/hidestatus.lua?raw=1) | Hides the status bar at the bottom of the window
~~[`inanimate`](plugins/inanimate.lua?raw=1)~~ | Integrated in lite-xl using `config.transitions = false` ~~Disables all transition animations~~
+*[`indent_convert`](plugins/indent_convert.lua?raw=1)* | Convert between tabs and spaces indentation
*[`indentguide`](plugins/indentguide.lua?raw=1)* | Adds indent guides *([screenshot](https://user-images.githubusercontent.com/3920290/79640716-f9860000-818a-11ea-9c3b-26d10dd0e0c0.png))*
*[`Kinc Projects`](https://github.com/Kode-Community/kinc_plugin)** | Adds [Kinc](https://github.com/Kode/Kinc) Project generation with basic build commands(depends on [`console`](https://github.com/franko/console))
[`language_angelscript`](plugins/language_angelscript.lua?raw=1) | Syntax for the [Angelscript](https://www.angelcode.com/angelscript/) programming language
diff --git a/plugins/indent_convert.lua b/plugins/indent_convert.lua
new file mode 100644
index 0000000..c86686d
--- /dev/null
+++ b/plugins/indent_convert.lua
@@ -0,0 +1,128 @@
+-- mod-version:2 -- lite-xl 2.0
+local core = require "core"
+local config = require "core.config"
+local command = require "core.command"
+
+config.plugins.indent_convert = {
+ update_indent_type = true -- set to false to avoid updating the document indent type
+}
+
+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
+ }
+)