aboutsummaryrefslogtreecommitdiff
path: root/plugins/bracketmatch.lua
blob: 71fb26ab30f2a271d728246f8267b9b11ec57ad7 (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
local core = require "core"
local style = require "core.style"
local config = require "core.config"
local command = require "core.command"
local keymap = require "core.keymap"
local DocView = require "core.docview"

local bracket_map = { ["["] = "]", ["("] = ")", ["{"] = "}" }


local state = {}

local function update_state(line_limit)
  line_limit = line_limit or math.huge

  -- reset if we don't have a document (eg. DocView isn't focused)
  local doc = core.active_view.doc
  if not doc then
    state = {}
    return
  end

  -- early exit if nothing has changed since the last call
  local line, col = doc:get_selection()
  if state.doc == doc and state.line == line and state.col == col
  and state.limit == line_limit then
    return
  end

  -- find matching rbracket if we have an lbracket
  local line2, col2
  local chr = doc:get_text(line, col - 1, line, col)
  local rbracket = bracket_map[chr]

  if rbracket then
    local ptn = "[%" .. chr .. "%" .. rbracket .. "]"
    local offset = col - 1
    local depth = 1

    for i = line, math.min(#doc.lines, line + line_limit) do
      while offset do
        local n = doc.lines[i]:find(ptn, offset + 1)
        if n then
          local match = doc.lines[i]:sub(n, n)
          if match == chr then
            depth = depth + 1
          elseif match == rbracket then
            depth = depth - 1
            if depth == 0 then line2, col2 = i, n end
          end
        end
        offset = n
      end
      if line2 then break end
      offset = 0
    end
  end

  -- update
  state = {
    doc = doc,
    line = line,
    col = col,
    line2 = line2,
    col2 = col2,
    limit = line_limit,
  }
end


local update = DocView.update

function DocView:update(...)
  update(self, ...)
  update_state(100)
end


local draw_line_text = DocView.draw_line_text

function DocView:draw_line_text(idx, x, y)
  draw_line_text(self, idx, x, y)

  if self.doc == state.doc and idx == state.line2 then
    local color = style.bracketmatch_color or style.syntax["function"]
    local x1 = x + self:get_col_x_offset(idx, state.col2)
    local x2 = x + self:get_col_x_offset(idx, state.col2 + 1)
    local h = math.ceil(1 * SCALE)
    renderer.draw_rect(x1, y + self:get_line_height() - h, x2 - x1, h, color)
  end
end


command.add("core.docview", {
  ["bracket-match:move-to-matching"] = function()
    update_state()
    if state.line2 then
      core.active_view.doc:set_selection(state.line2, state.col2)
    end
  end,
})

keymap.add { ["ctrl+m"] = "bracket-match:move-to-matching" }