-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathvi_tags.lua
176 lines (157 loc) · 4.86 KB
/
vi_tags.lua
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
165
166
167
168
169
170
171
172
173
174
175
176
local M = {}
local state = {
tags = nil, -- current set of tags
tagstack = {},-- current tag stack: list of { i=num, tags={tag list} }
tagidx = 0, -- index into tagstack of current level
lasttag = nil,-- last tag list
timestamp = nil, -- tags file modification time.
}
M.state = state
-- Load a tags file
local function load_tags()
vi_mode.err("Loading tags file...")
local tagf = io.open("tags") -- TODO: configurable tags file location
local results = {}
local pat = "^([^\t]*)\t([^\t]*)\t(.*)$"
if tagf then
state.timestamp = lfs.attributes("tags", "modification")
for line in tagf:lines() do
local tname, fname, excmd = line:match(pat)
local flags
if tname then
-- Initialise to an empty list if necessary
if not results[tname] then results[tname] = {} end
-- And append.
local l = results[tname] -- now guaranteed to be a table
do
-- Try to separate the ex command from extension fields
local e,f = excmd:match('^(.-);"\t(.*)$')
if e then
excmd = e
flags = f
end
end
l[#l+1] = { sym=tname, filename=fname, excmd=excmd, flags=flags }
end
end
tagf:close()
end
state.tags = results
end
-- Return or load the tags
local function get_tags()
-- TODO: check if tags file needs reloading
if state.tags == nil or lfs.attributes("tags", "modification") ~= state.timestamp then
load_tags()
end
return state.tags
end
local ts = require('textadept-vi.vi_util').tostring
function M.save_location(filename, pos, taglist)
local newidx = state.tagidx + 1
state.tagstack[newidx] = {
i=1, -- which tag within this list
tags=taglist, -- this level's tags
fromname=filename, -- where we came from
frompos=pos, -- where we came from
}
state.lasttag = taglist
state.tagidx = newidx
end
function M.find_tag_exact(name)
local tags = get_tags()
local result = tags[name]
if result then
local newidx = state.tagidx + 1
-- Clear the stack above where we are.
while newidx <= #state.tagstack do
state.tagstack[#state.tagstack] = nil
end
M.save_location(buffer.filename, buffer.current_pos, result)
return result[1]
else
return nil
end
end
-- Return a list of tags matching Lua pat
function M.match_tag(pat)
local tags = get_tags()
local result = {}
for name,_ in pairs(tags) do
if name:match(pat) then
result[#result+1] = name
end
end
if #result > 0 then return result end
-- return nil if nothing found
end
function M.pop_tag()
if state.tagidx >= 1 then
local tos = state.tagstack[state.tagidx]
io.open_file(tos.fromname)
buffer:goto_pos(tos.frompos)
state.tagidx = state.tagidx - 1
else
vi_mode.err("Top of stack")
end
end
-- Return all the tags in the current level
function M.get_all()
if state.tagidx > 0 then
return state.tagstack[state.tagidx].tags
end
end
-- Go to a particular tag
function M.goto_tag(tag)
io.open_file(tag.filename)
local excmd = tag.excmd
local _, pat = excmd:match("^([?/])(.*)%1$")
if pat then
-- TODO: properly handle regexes and line number tags.
-- For now, assume it's a fixed string possibly with ^ and $ around it.
-- Tag file regexes are treated as in vim's nomagic; ie most special
-- characters are only magic if backquoted (except .).
-- This is an approximation for now.
pat = pat:gsub("(%\\?)([%[%]*+()])", function(pre, magic)
if pre ~= "" then
return pre .. magic
else
return "\\" .. magic
end
end)
buffer.current_pos = 0
buffer.search_anchor()
local pos = buffer:search_next(buffer.FIND_REGEXP + buffer.FIND_MATCHCASE, pat)
if pos >= 0 then
buffer.goto_pos(pos)
else
vi_mode.err("Not found: " .. pat)
end
return
end
-- Try a numeric line number
local pat = excmd:match("^(%d+)$")
if pat then
buffer.goto_line(tonumber(pat)-1)
return
end
-- unknown tag pattern
vi_mode.err('Tag pat: '..excmd)
end
-- Return the next tag at this level, or nil.
function M.tag_next()
local taglist = state.tagstack[state.tagidx]
if taglist.i < #taglist.tags then
taglist.i = taglist.i + 1
return taglist.tags[taglist.i]
end
end
-- Return the next tag at this level, or nil.
function M.tag_prev()
local taglist = state.tagstack[state.tagidx]
if taglist.i > 1 then
taglist.i = taglist.i - 1
return taglist.tags[taglist.i]
end
end
return M