summaryrefslogtreecommitdiff
path: root/vis/plugins/vis-commentary/init.lua
blob: 6ebecded7e0b1b2fdc97788362494893e8eb2702 (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
--
-- vis-commentary
--
-- comment strings and matching patterns are taken from:
-- https://github.com/rgieseke/textadept/blob/9906c1fcec1c33c6a83c33dc7874669b5c6113f8/modules/textadept/editing.lua
--

local comment_string = {
    actionscript='//', ada='--', ansi_c='/*|*/', antlr='//', apdl='!', apl='#',
    applescript='--', asp='\'', autoit=';', awk='#', b_lang='//', bash='#',
    batch=':', bibtex='%', boo='#', chuck='//', cmake='#', coffeescript='#',
    context='%', cpp='//', crystal='#', csharp='//', css='/*|*/', cuda='//',
    dart='//', desktop='#', django='{#|#}', dmd='//', dockerfile='#', dot='//',
    eiffel='--', elixir='#', erlang='%', faust='//', fish='#', forth='|\\',
    fortran='!', fsharp='//', gap='#', gettext='#', gherkin='#', glsl='//',
    gnuplot='#', go='//', groovy='//', gtkrc='#', haskell='--', html='<!--|-->',
    icon='#', idl='//', inform='!', ini='#', Io='#', java='//', javascript='//',
    json='/*|*/', jsp='//', latex='%', ledger='#', less='//', lilypond='%',
    lisp=';', logtalk='%', lua='--', makefile='#', markdown='<!--|-->', matlab='#', 
    moonscript='--', myrddin='//', nemerle='//', nsis='#', objective_c='//', 
    pascal='//', perl='#', php='//', pico8='//', pike='//', pkgbuild='#', prolog='%',
    props='#', protobuf='//', ps='%', pure='//', python='#', rails='#', rc='#',
    rebol=';', rest='.. ', rexx='--', rhtml='<!--|-->', rstats='#', ruby='#',
    rust='//', sass='//', scala='//', scheme=';', smalltalk='"|"', sml='(*)',
    snobol4='#', sql='#', tcl='#', tex='%', text='', toml='#', vala='//',
    vb='\'', vbscript='\'', verilog='//', vhdl='--', wsf='<!--|-->',
    xml='<!--|-->', yaml='#'
}

-- escape all magic characters with a '%'
local function esc(str)
    if not str then return "" end
    return (str:gsub('%%', '%%%%')
        :gsub('^%^', '%%^')
        :gsub('%$$', '%%$')
        :gsub('%(', '%%(')
        :gsub('%)', '%%)')
        :gsub('%.', '%%.')
        :gsub('%[', '%%[')
        :gsub('%]', '%%]')
        :gsub('%*', '%%*')
        :gsub('%+', '%%+')
        :gsub('%-', '%%-')
        :gsub('%?', '%%?'))
end

-- escape '%'
local function pesc(str)
    if not str then return "" end
    return str:gsub('%%', '%%%%')
end

function rtrim(s)
    local n = #s
    while n > 0 and s:find("^%s", n) do n = n - 1 end
    return s:sub(1, n)
 end

local function comment_line(lines, lnum, prefix, suffix)
    if suffix ~= "" then suffix = " " .. suffix end
    lines[lnum] = string.gsub(lines[lnum],
                              "(%s*)(.*)",
                              "%1" .. pesc(prefix) .. " %2" .. pesc(suffix))
end

local function uncomment_line(lines, lnum, prefix, suffix)
    local match_str = "^(%s*)" .. esc(prefix) .. "%s?(.*)" .. esc(suffix)
    m = table.pack(lines[lnum]:match(match_str))
    lines[lnum] = m[1] .. rtrim(m[2])
end

local function is_comment(line, prefix)
    return (line:match("^%s*(.+)"):sub(0, #prefix) == prefix)
end

local function toggle_line_comment(lines, lnum, prefix, suffix)
    if not lines or not lines[lnum] then return end
    if not lines[lnum]:match("^%s*(.+)") then return end -- ignore empty lines
    if is_comment(lines[lnum], prefix) then
        uncomment_line(lines, lnum, prefix, suffix)
    else
        comment_line(lines, lnum, prefix, suffix)
    end
end

-- if one line inside the block is not a comment, comment the block.
-- only uncomment, if every single line is comment.
local function block_comment(lines, a, b, prefix, suffix)
    local uncomment = true
    for i=a,b do
        if lines[i]:match("^%s*(.+)") and not is_comment(lines[i], prefix) then
            uncomment = false
        end
    end

    if uncomment then
        for i=a,b do
            if lines[i]:match("^%s*(.+)") then
                uncomment_line(lines, i, prefix, suffix)
            end
        end
    else
        for i=a,b do
            if lines[i]:match("^%s*(.+)") then
                comment_line(lines, i, prefix, suffix)
            end
        end
    end
end

vis:map(vis.modes.NORMAL, "gcc", function()
    local win = vis.win
    local lines = win.file.lines
    local comment = comment_string[win.syntax]
    if not comment then return end
    local prefix, suffix = comment:match('^([^|]+)|?([^|]*)$')
    if not prefix then return end

    for sel in win:selections_iterator() do
        local lnum = sel.line
        local col = sel.col

        toggle_line_comment(lines, lnum, prefix, suffix)
        sel:to(lnum, col)  -- restore cursor position
    end

    win:draw()
end, "Toggle comment on a the current line")

local function visual_f(i)
    return function()
        local win = vis.win
        local lines = win.file.lines

        local comment = comment_string[win.syntax]
        if not comment then return end

        local prefix, suffix = comment:match('^([^|]+)|?([^|]*)$')
        if not prefix then return end

        for sel in win:selections_iterator() do
            local r = sel.range
            local lnum = sel.line     -- line number of cursor
            local col = sel.col       -- column of cursor

            if sel.anchored and r then
                sel.pos = r.start
                local a = sel.line
                sel.pos = r.finish
                local b = sel.line - i

                block_comment(lines, a, b, prefix, suffix)

                sel:to(lnum, col)     -- restore cursor position
            end
        end

        win:draw()
        vis.mode = vis.modes.NORMAL     -- go to normal mode
    end
end

vis:map(vis.modes.VISUAL_LINE, "gc", visual_f(1), "Toggle comment on the selected lines")
vis:map(vis.modes.VISUAL, "gc", visual_f(0), "Toggle comment on the selected lines")