aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/tshighlighter.lua
blob: 1440acf0d02df54fb9a88d1efdfe48e84abb893f (plain) (blame)
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
local a = vim.api

-- support reload for quick experimentation
local TSHighlighter = rawget(vim.treesitter, 'TSHighlighter') or {}
TSHighlighter.__index = TSHighlighter

-- These are conventions defined by tree-sitter, though it
-- needs to be user extensible also.
-- TODO(bfredl): this is very much incomplete, we will need to
-- go through a few tree-sitter provided queries and decide
-- on translations that makes the most sense.
TSHighlighter.hl_map = {
    keyword="Keyword",
    string="String",
    type="Type",
    comment="Comment",
    constant="Constant",
    operator="Operator",
    number="Number",
    label="Label",
    ["function"]="Function",
    ["function.special"]="Function",
}

function TSHighlighter.new(query, bufnr, ft)
  local self = setmetatable({}, TSHighlighter)
  self.parser = vim.treesitter.get_parser(bufnr, ft, function(...) self:on_change(...) end)
  self.buf = self.parser.bufnr
  -- TODO(bfredl): perhaps on_start should be called uncondionally, instead for only on mod?
  local tree = self.parser:parse()
  self.root = tree:root()
  self:set_query(query)
  self.edit_count = 0
  self.redraw_count = 0
  self.line_count = {}
  a.nvim_buf_set_option(self.buf, "syntax", "")
  a.nvim__buf_set_luahl(self.buf, {
    on_start=function(...) return self:on_start(...) end,
    on_window=function(...) return self:on_window(...) end,
    on_line=function(...) return self:on_line(...) end,
  })

  -- Tricky: if syntax hasn't been enabled, we need to reload color scheme
  -- but use synload.vim rather than syntax.vim to not enable
  -- syntax FileType autocmds. Later on we should integrate with the
  -- `:syntax` and `set syntax=...` machinery properly.
  if vim.g.syntax_on ~= 1 then
    vim.api.nvim_command("runtime! syntax/synload.vim")
  end
  return self
end

function TSHighlighter:set_query(query)
  if type(query) == "string" then
    query = vim.treesitter.parse_query(self.parser.lang, query)
  end
  self.query = query

  self.id_map = {}
  for i, capture in ipairs(self.query.captures) do
    local hl = 0
    local firstc = string.sub(capture, 1, 1)
    local hl_group = self.hl_map[capture]
    if firstc ~= string.lower(firstc) then
      hl_group = vim.split(capture, '.', true)[1]
    end
    if hl_group then
      hl = a.nvim_get_hl_id_by_name(hl_group)
    end
    self.id_map[i] = hl
  end

    a.nvim__buf_redraw_range(self.buf, 0, a.nvim_buf_line_count(self.buf))
end

function TSHighlighter:on_change(changes)
  for _, ch in ipairs(changes or {}) do
    a.nvim__buf_redraw_range(self.buf, ch[1], ch[3]+1)
  end
  self.edit_count = self.edit_count + 1
end

function TSHighlighter:on_start(_, _buf, _tick)
  local tree = self.parser:parse()
  self.root = tree:root()
end

function TSHighlighter:on_window(_, _win, _buf, _topline, botline)
  self.iter = nil
  self.nextrow = 0
  self.botline = botline
  self.redraw_count = self.redraw_count + 1
end

function TSHighlighter:on_line(_, _win, buf, line)
  if self.iter == nil then
    self.iter = self.query:iter_captures(self.root,buf,line,self.botline)
  end
  while line >= self.nextrow do
    local capture, node, match = self.iter()
    local active = true
    if capture == nil then
      break
    end
    if match ~= nil then
      active = self:run_pred(match)
      match.active = active
    end
    local start_row, start_col, end_row, end_col = node:range()
    local hl = self.id_map[capture]
    if hl > 0 and active and end_row >= line then
      a.nvim__put_attr(hl, start_row, start_col, end_row, end_col)
    end
    if start_row > line then
      self.nextrow = start_row
    end
  end
  self.line_count[line] = (self.line_count[line] or 0) + 1
  --return tostring(self.line_count[line])
end

return TSHighlighter