summaryrefslogtreecommitdiff
path: root/config/essentials/vis/format.lua
blob: 15488dd8abd46680754a97c0da03cf28764a063b (plain)
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
local global_options = {check_same = true}

local function stdio_formatter(cmd, options)
    local function apply(win, range, pos)
        local size = win.file.size
        local all = {start = 0, finish = size}
        if range == nil then range = all end
        local command = type(cmd) == 'function' and cmd(win, range, pos) or cmd
        local check_same = (options and options.check_same ~= nil) and
                               options.check_same or global_options.check_same
        local check = check_same == true or
                          (type(check_same) == 'number' and check_same >= size)
        local status, out, err = vis:pipe(win.file, all, command)
        if status ~= 0 then
            vis:message(err)
        elseif out == nil or out == '' then
            vis:info('No output from formatter')
        elseif not check or win.file:content(all) ~= out then
            local start, finish = range.start, range.finish
            win.file:delete(range)
            win.file:insert(start,
                            out:sub(start + 1, finish + (out:len() - size)))
        end
        return pos
    end
    return {
        apply = apply,
        options = options or {ranged = type(cmd) == 'function'}
    }
end

local function with_filename(win, option)
    if win.file.path then
        return option .. "'" .. win.file.path:gsub("'", "\\'") .. "'"
    else
        return ''
    end
end

local formatters = {}
formatters = {
    bash = stdio_formatter(function(win)
        return 'shfmt ' .. with_filename(win, '--filename ') .. ' -'
    end),
    csharp = stdio_formatter('dotnet csharpier'),
    go = stdio_formatter('gofmt'),
    lua = {
        pick = function(win)
            local _, out = vis:pipe(win.file,
                                    {start = 0, finish = win.file.size},
                                    'test -e .lua-format && echo luaformatter || echo stylua')
            return formatters[out:gsub('\n$', '')]
        end
    },
    luaformatter = stdio_formatter('lua-format'),
    markdown = stdio_formatter(function(win)
        if win.options and win.options.colorcolumn ~= 0 then
            return 'prettier --parser markdown --prose-wrap always ' ..
                       ('--print-width ' .. (win.options.colorcolumn - 1) .. ' ') ..
                       with_filename(win, '--stdin-filepath ')
        else
            return 'prettier --parser markdown ' ..
                       with_filename(win, '--stdin-filepath ')
        end
    end, {ranged = false}),
    powershell = stdio_formatter([[
    "$( (command -v powershell.exe || command -v pwsh) 2>/dev/null )" -c '
        Invoke-Formatter  -ScriptDefinition `
          ([IO.StreamReader]::new([Console]::OpenStandardInput()).ReadToEnd())
      ' | sed -e :a -e '/^[\r\n]*$/{$d;N;};/\n$/ba'
  ]]),
    rust = stdio_formatter('rustfmt'),
    stylua = stdio_formatter(function(win, range)
        if range and (range.start ~= 0 or range.finish ~= win.file.size) then
            return
                'stylua -s --range-start ' .. range.start .. ' --range-end ' ..
                    range.finish .. with_filename(win, ' --stdin-filepath ') ..
                    ' -'
        else
            return 'stylua -s ' .. with_filename(win, '--stdin-filepath ') ..
                       ' -'
        end
    end),
    text = stdio_formatter(function(win)
        if win.options and win.options.colorcolumn ~= 0 then
            return 'fmt -w ' .. (win.options.colorcolumn - 1)
        else
            return 'fmt'
        end
    end, {ranged = false})
}

local function getwinforfile(file)
    for win in vis:windows() do
        if win and win.file and win.file.path == file.path then
            return win
        end
    end
end

local function apply(file_or_keys, range, pos)
    local win =
        type(file_or_keys) ~= 'string' and getwinforfile(file_or_keys) or
            vis.win
    local ret = type(file_or_keys) ~= 'string' and function() return pos end or
                    function() return 0 end
    pos = pos or win.selection.pos
    local formatter = formatters[win.syntax]
    if formatter and formatter.pick then formatter = formatter.pick(win) end
    if formatter == nil then
        vis:info('No formatter for ' .. win.syntax)
        return ret()
    end
    if range ~= nil and not formatter.options.ranged and range.start ~= 0 and
        range.finish ~= win.file.size then
        vis:info('Formatter for ' .. win.syntax .. ' does not support ranges')
        return ret()
    end
    pos = formatter.apply(win, range, pos) or pos
    vis:insert('') -- redraw and friends don't work
    win.selection.pos = pos
    return ret()
end

return {
    formatters = formatters,
    options = global_options,
    apply = apply,
    stdio_formatter = stdio_formatter,
    with_filename = with_filename
}