summaryrefslogtreecommitdiff
path: root/mut/vis
diff options
context:
space:
mode:
authorMike Vink <59492084+ivi-vink@users.noreply.github.com>2025-01-29 14:34:17 +0000
committerMike Vink <59492084+ivi-vink@users.noreply.github.com>2025-01-29 14:34:17 +0000
commitd2d2ba307694a24ed2587c3bed71d761fc80b650 (patch)
tree7014ea54a4461fb2022f38e69a3a66e6b4b9d1a1 /mut/vis
parent3f6e4c0707f411cb8ea3f4c541ec0001153aa6e1 (diff)
parent9f4b260f6d71fdaa8690fa98426a6e5b1cb4939b (diff)
Merge commit '9f4b260f6d71fdaa8690fa98426a6e5b1cb4939b' as 'mut/vis/vis-editorconfig'
Diffstat (limited to 'mut/vis')
-rw-r--r--mut/vis/vis-editorconfig/.editorconfig5
-rw-r--r--mut/vis/vis-editorconfig/README.md89
-rw-r--r--mut/vis/vis-editorconfig/edconf.lua208
-rw-r--r--mut/vis/vis-editorconfig/init.lua4
4 files changed, 306 insertions, 0 deletions
diff --git a/mut/vis/vis-editorconfig/.editorconfig b/mut/vis/vis-editorconfig/.editorconfig
new file mode 100644
index 0000000..d60c879
--- /dev/null
+++ b/mut/vis/vis-editorconfig/.editorconfig
@@ -0,0 +1,5 @@
+root = true
+
+[*.lua]
+indent_style = space
+indent_size = 2
diff --git a/mut/vis/vis-editorconfig/README.md b/mut/vis/vis-editorconfig/README.md
new file mode 100644
index 0000000..9b9b72c
--- /dev/null
+++ b/mut/vis/vis-editorconfig/README.md
@@ -0,0 +1,89 @@
+# vis-editorconfig
+
+A [vis][vis] plugin for [editorconfig][ec].
+
+[vis]: https://github.com/martanne/vis
+[ec]: http://editorconfig.org/
+
+## Installation
+
+You'll need the Lua wrapper for editorconfig-core installed. This can
+be done through luarocks: `luarocks install editorconfig-core`
+
+```shell
+git clone https://github.com/seifferth/vis-editorconfig "$HOME/.config/vis/edconf"
+```
+
+Then add `require('edconf')` to your `visrc.lua`.
+
+You may use a different name for the local repository, if you like.
+You could, for instance, use `$HOME/.config/vis/vis-editorconfig` and
+`require('vis-editorconfig')`. Note, however, that the repository **must
+not** be called `editorconfig`. Since the editorconfig-core lua library
+is also called `editorconfig`, naming the repository `editorconfig`
+will cause name conflicts that result in infinite recursion.
+
+## Functionality
+
+Not all editorconfig functionality is supported by vis and hence by this
+plugin. At this moment, there is full support for the following settings:
+
+- indent_style
+- indent_size
+- tab_width
+- insert_final_newline
+
+The following settings are implemented partially and / or support is
+turned off by default:
+
+- spell_language: This is not yet part of the editorconfig specification
+ (cf. <https://github.com/editorconfig/editorconfig/issues/315>), but
+ it is implemented anyway. Since vis does not support spellchecking
+ natively, this plugin will only set `vis.win.file.spell_language` to
+ the specified value. It is then up to the spellchecking plugin to
+ respect that variable.
+- trim_trailing_whitespace: Turned off by default, can be enabled
+ via `:set edconfhooks on`.
+- end_of_line: Turned off by default, partial support can be enabled
+ via `:set edconfhooks on`. Only `crlf` and `lf` are supported, plain
+ `cr` is not. The implementation is also very basic. If end_of_line
+ is set to `crlf`, a return character will be inserted at the end of
+ each line that does not yet end with `crlf`. If end_of_line is set
+ to `lf`, return characters at the end of a line will be stripped.
+ While I would encourage every vis user to stick to `lf` terminated
+ files, this might be convenient if, for some reason, they do need
+ to compose `crlf` terminated files.
+- max_line_length: Turned off by default, partial support can be
+ enabled via `:set edconfhooks on`. There is no straightforward way
+ to automatically wrap content that might be written in arbitrary
+ programming (or non-programming) languages. For that reason,
+ vis-editorconfig doesn't even try. If max_line_length is enabled,
+ vis-editorconfig issues a warning, however, indicating which lines
+ are longer than the specified length. Note that you will miss this
+ warning if you write your file with `:wq`, so if you care about it,
+ write it with `:w<RETURN>:q`.
+
+The reason those last three settings are optional and turned off by
+default is their scalability. Each of those operations is implemented
+as a pre-save-hook with a complexity of O(n), where n is the filesize.
+Since vis is incredibly good at editing huge files efficiently, there
+seems to be a very real danger that those hooks could cause the editor
+to freeze just before a user's valuable changes are written to disk.
+
+You can turn support for those pre-save-hooks on or off at any time
+by running
+
+ :set edconfhooks on
+
+or
+
+ :set edconfhooks off
+
+If `edconfhooks` are enabled, they will be executed as configured in
+`.editorconfig`. If you want to take a less cautious approach and enable
+these hooks by default, simply add an additional line below the module
+import in `visrc.lua`:
+
+ require('editorconfig/edconf')
+ vis:command('set edconfhooks on') -- supposing you did previously
+ -- require('vis')
diff --git a/mut/vis/vis-editorconfig/edconf.lua b/mut/vis/vis-editorconfig/edconf.lua
new file mode 100644
index 0000000..7c92f4d
--- /dev/null
+++ b/mut/vis/vis-editorconfig/edconf.lua
@@ -0,0 +1,208 @@
+require "vis"
+local ec = require "editorconfig"
+local M = {}
+
+-- Simple wrapper
+local function vis_set(option, value)
+ if type(value) == "boolean" then
+ if value then
+ value = "yes"
+ else
+ value = "no"
+ end
+ end
+
+ vis:command("set " .. option .. " " .. value)
+end
+
+local function set_pre_save(f, value)
+ if value == "true" then
+ vis.events.subscribe(vis.events.FILE_SAVE_PRE, f)
+ else
+ vis.events.unsubscribe(vis.events.FILE_SAVE_PRE, f)
+ end
+end
+
+local function set_file_open(f, value)
+ if value == "true" then
+ vis.events.subscribe(vis.events.FILE_OPEN, f)
+ else
+ vis.events.unsubscribe(vis.events.FILE_OPEN, f)
+ end
+end
+
+-- Custom functionality
+M.hooks_enabled = false
+vis:option_register("edconfhooks", "bool", function(value)
+ M.hooks_enabled = value
+end, "Enable optional pre-save-hooks for certain editorconfig settings")
+
+local function insert_final_newline(file)
+ -- Technically speaking, this is a pre-save-hook as well and could
+ -- therefore respect edconf_hooks_enabled. Since this function runs
+ -- blazingly fast and scales with a complexity of O(1), however,
+ -- there is no need to disable it.
+ if file.size > 0 and file:content(file.size-1, 1) ~= '\n' then
+ file:insert(file.size, '\n')
+ end
+end
+
+local function strip_final_newline(file)
+ -- In theory, this would have a complexity of O(n) as well and could
+ -- thus be made optional via edconf_hooks_enabled. On the other hand,
+ -- this is probably a very rare edge case, so stripping all trailing
+ -- newline characters is probably safe enough.
+ while file:content(file.size-1, 1) == '\n' do
+ file:delete(file.size-1, 1)
+ end
+end
+
+local function trim_trailing_whitespace(file)
+ if not M.hooks_enabled then return end
+ for i=1, #file.lines do
+ if string.match(file.lines[i], '[ \t]$') then
+ file.lines[i] = string.gsub(file.lines[i], '[ \t]*$', '')
+ end
+ end
+end
+
+local function enforce_crlf_eol(file)
+ if not M.hooks_enabled then return end
+ for i=1, #file.lines do
+ if not string.match(file.lines[i], '\r$') then
+ file.lines[i] = string.gsub(file.lines[i], '$', '\r')
+ end
+ end
+end
+
+local function enforce_lf_eol(file)
+ if not M.hooks_enabled then return end
+ for i=1, #file.lines do
+ if string.match(file.lines[i], '\r$') then
+ file.lines[i] = string.gsub(file.lines[i], '\r$', '')
+ end
+ end
+end
+
+M.max_line_length = 80 -- This is ugly, but we do want to use
+ -- single function that we can register
+ -- or unregister as needed
+local function max_line_length(file)
+ if not M.hooks_enabled then return end
+ local overlong_lines = {}
+ for i=1, #file.lines do
+ if string.len(file.lines[i]) > M.max_line_length then
+ table.insert(overlong_lines, i)
+ end
+ end
+ if #overlong_lines > 0 then
+ local lines_are = (function(x)
+ if x>1 then return "lines are" else return "line is" end
+ end)(#overlong_lines)
+ vis:info(string.format(
+ "%d %s longer than %d characters: %s",
+ #overlong_lines, lines_are, M.max_line_length,
+ table.concat(overlong_lines, ",")
+ ))
+ end
+end
+
+local OPTIONS = {
+ indent_style = function (value)
+ vis_set("expandtab", (value == "space"))
+ end,
+
+ indent_size = function (value)
+ if value ~= "tab" then -- tab_width is a synonym anyway
+ vis_set("tabwidth", value)
+ end
+ end,
+
+ tab_width = function (value)
+ vis_set("tabwidth", value)
+ end,
+
+ spelling_language = function (value, file)
+ file.spelling_language = value
+ end,
+
+ insert_final_newline = function (value)
+ -- According to the editorconfig specification, insert_final_newline
+ -- false is supposed to mean stripping the final newline, if present.
+ -- See https://editorconfig-specification.readthedocs.io/#supported-pairs
+ --
+ -- Quote: insert_final_newline Set to true ensure file ends with a
+ -- newline when saving and false to ensure it doesn’t.
+ --
+ set_pre_save(insert_final_newline, tostring(value == "true"))
+ set_pre_save(strip_final_newline, tostring(value == "false"))
+ end,
+
+ trim_trailing_whitespace = function (value)
+ set_pre_save(trim_trailing_whitespace, value)
+ end,
+
+ -- End of line is only partially implemented. While vis does not
+ -- support customized newlines, it does work well enough with crlf
+ -- newlines. Therefore, setting end_of_line=crlf will just ensure
+ -- that there is a cr at the end of each line. Setting end_of_line=lf
+ -- will strip any cr characters at the end of lines. This hopefully
+ -- eases the pain of working with crlf files a little.
+ end_of_line = function (value)
+ set_pre_save(enforce_crlf_eol, tostring(value == "crlf"))
+ set_pre_save(enforce_lf_eol, tostring(value == "lf"))
+ end,
+
+ -- There is probably no straightforward way to enforce a maximum line
+ -- length across different programming languages. If a maximum line
+ -- length is set, we can at least issue a warning, however.
+ max_line_length = function(value)
+ if value ~= "off" then
+ M.max_line_length = tonumber(value)
+ end
+ set_pre_save(max_line_length, tostring(value ~= "off"))
+ end,
+
+ -- Not supported by vis
+ -- charset
+ -- Partial support
+ -- end_of_line
+ -- max_line_length
+}
+
+-- Compatible with editorconfig-core-lua v0.3.0
+local function ec_iter(p)
+ local i = 0
+ local props, keys = ec.parse(p)
+ local n = #keys
+ return function ()
+ i = i + 1
+ if i <= n then
+ return keys[i], props[keys[i]]
+ end
+ end
+end
+
+local function ec_set_values(win)
+ if not win or not win.file or not win.file.path then return end
+ for name, value in ec_iter(win.file.path) do
+ if OPTIONS[name] then
+ OPTIONS[name](value, win.file)
+ end
+ end
+end
+
+
+vis:command_register("econfig_parse", function()
+ ec_set_values(vis.win)
+end, "(Re)parse an editorconfig file")
+
+vis.events.subscribe(vis.events.WIN_OPEN, function (win)
+ ec_set_values(win)
+end)
+
+vis.events.subscribe(vis.events.FILE_SAVE_POST, function()
+ ec_set_values(vis.win)
+end)
+
+return M
diff --git a/mut/vis/vis-editorconfig/init.lua b/mut/vis/vis-editorconfig/init.lua
new file mode 100644
index 0000000..39291d4
--- /dev/null
+++ b/mut/vis/vis-editorconfig/init.lua
@@ -0,0 +1,4 @@
+local source_str = debug.getinfo(1, 'S').source:sub(2)
+local script_path = source_str:match('(.*/)')
+
+return dofile(script_path .. 'edconf.lua')