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
|
-- Copyright 2007-2012 Mitchell mitchell.att.foicica.com. See LICENSE.
local M = {}
--[[ This comment is for LuaDoc.
---
-- Snapopen for the textadept module.
--
-- ## Examples
--
-- local snapopen = _M.textadept.snapopen.open
--
-- -- Show all files in PATHS.
-- snapopen()
--
-- -- Show all files in the current file's directory.
-- snapopen(buffer.filename:match('^(.+)[/\\]'), nil, true)
--
-- -- Show all Lua files in PATHS.
-- snapopen(nil, '!%.lua$')
--
-- -- Ignore the project's 'images' folder and HTML pages.
-- snapopen('/path/to/project', {
-- folders = { 'images' },
-- extensions = { 'html' }
-- }, true)
-- @field DEFAULT_DEPTH (number)
-- Maximum directory depth to search. The default value is `4`.
-- @field MAX (number)
-- Maximum number of files to list. The default value is `1000`.
module('_M.textadept.snapopen')]]
---
-- Table of default UTF-8 paths to search.
-- @class table
-- @name PATHS
M.PATHS = {}
---
-- Default file and directory filters.
-- @class table
-- @name FILTER
M.FILTER = {
extensions = {
'a', 'bmp', 'bz2', 'class', 'dll', 'exe', 'gif', 'gz', 'jar', 'jpeg', 'jpg',
'o', 'png', 'so', 'tar', 'tgz', 'tif', 'tiff', 'zip'
},
folders = { '%.bzr$', '%.git$', '%.hg$', '%.svn$', 'CVS$' }
}
M.DEFAULT_DEPTH = 4
M.MAX = 1000
local lfs_dir, lfs_attributes = lfs.dir, lfs.attributes
local DEPTH = M.DEFAULT_DEPTH
-- Determines whether or not the given file matches the given filter.
-- @param file The filename.
-- @param filter The filter table.
-- @return boolean `true` or `false`.
local function exclude(file, filter)
if not filter then return false end
local string_match, string_sub = string.match, string.sub
local utf8_file = file:iconv('UTF-8', _CHARSET)
local ext = filter.extensions
if ext and ext[utf8_file:match('[^%.]+$')] then return true end
for i = 1, #filter do
local patt = filter[i]
if string_sub(patt, 1, 1) ~= '!' then
if string_match(utf8_file, patt) then return true end
else
if not string_match(utf8_file, string_sub(patt, 2)) then return true end
end
end
return false
end
-- Adds a directory's contents to a list of files.
-- @param utf8_dir The UTF-8 directory to open.
-- @param list The list of files to add dir's contents to.
-- @param depth The current depth of nested folders.
-- @param filter The filter table.
local function add_directory(utf8_dir, list, depth, filter)
local string_match, string_gsub, MAX = string.match, string.gsub, M.MAX
local dir = utf8_dir:iconv(_CHARSET, 'UTF-8')
for file in lfs_dir(dir) do
if not string_match(file, '^%.%.?$') then
file = dir..(not WIN32 and '/' or '\\')..file
if lfs_attributes(file, 'mode') == 'directory' then
if not exclude(file, filter.folders) and depth < DEPTH then
add_directory(file, list, depth + 1, filter)
end
elseif not exclude(file, filter) then
if #list >= MAX then return end
list[#list + 1] = string_gsub(file, '^%.[/\\]', '')
end
end
end
end
---
-- Quickly open a file in set of directories.
-- @param utf8_paths A UTF-8 string directory path or table of UTF-8 directory
-- paths to search.
-- @param filter A filter for files and folders to exclude. The filter may be
-- a string or table. Each filter is a Lua pattern. Any files matching a
-- filter are excluded. Prefix a pattern with `!` to exclude any files that
-- do not match the filter. File extensions can be more efficiently excluded
-- by adding the extension text to a table assigned to an `extensions` key in
-- the filter table instead of using individual filters. Directories can be
-- excluded by adding filters to a table assigned to a `folders` key in the
-- filter table. All strings should be UTF-8 encoded.
-- @param exclude_PATHS Flag indicating whether or not to exclude `PATHS` in the
-- search. The default value is `false`.
-- @param exclude_FILTER Flag indicating whether or not to exclude `FILTER` from
-- `filter` in the search. If false, adds `FILTER` to the given `filter`.
-- The default value is `false`.
-- @param depth Number of directories to recurse into for finding files.
-- The default value is `DEFAULT_DEPTH`.
-- @usage _M.textadept.snapopen.open()
-- @usage _M.textadept.snapopen.open(buffer.filename:match('^.+/'), nil, true)
-- @usage _M.textadept.snapopen.open(nil, '!%.lua$')
-- @usage _M.textadept.snapopen.open(nil, { folders = { '%.hg' } })
-- @name open
function M.open(utf8_paths, filter, exclude_PATHS, exclude_FILTER, depth)
-- Convert utf8_paths to a table from nil or string arguments.
if not utf8_paths then utf8_paths = {} end
if type(utf8_paths) == 'string' then utf8_paths = { utf8_paths } end
-- Convert filter to a table from nil or string arguments.
if not filter then filter = {} end
if type(filter) == 'string' then filter = { filter } end
-- Add PATHS to utf8_paths unless specified otherwise.
if not exclude_PATHS then
for i = 1, #M.PATHS do utf8_paths[#utf8_paths + 1] = M.PATHS[i] end
end
-- Add FILTER to filter unless specified otherwise.
if not exclude_FILTER then
for k, v in pairs(M.FILTER) do
if not filter[k] then filter[k] = {} end
local filter_k = filter[k]
for i = 1, #v do filter_k[#filter_k + 1] = v[i] end
end
end
DEPTH = depth or M.DEFAULT_DEPTH
-- Create file extension filter hash table for quick lookups.
local ext = filter.extensions
if ext then for i = 1, #ext do ext[ext[i]] = true end end
-- Create the file list, prompt the user to choose a file, then open the file.
local list = {}
for _, path in ipairs(utf8_paths) do add_directory(path, list, 1, filter) end
if #list >= M.MAX then
gui.dialog('ok-msgbox',
'--title', _L['File Limit Exceeded'],
'--informative-text',
string.format('%d %s %d', M.MAX,
_L['files or more were found. Showing the first'],
M.MAX))
end
local utf8_filenames = gui.filteredlist(_L['Open'], _L['File'], list, false,
'--select-multiple') or ''
for filename in utf8_filenames:gmatch('[^\n]+') do io.open_file(filename) end
end
return M
|