aboutsummaryrefslogtreecommitdiff
path: root/modules/textadept/snapopen.lua
blob: 3d295730e8c8afb872a743f3c828cfd6bc7ee5f2 (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
-- Copyright 2007-2011 Mitchell mitchell<att>caladbolg.net. See LICENSE.

local L = locale.localize

local M = {}

--[[ This comment is for LuaDoc.
---
-- Snapopen for the textadept module.
module('_m.textadept.snapopen', package.seeall)]]

-- Markdown:
-- ## Settings
--
-- * `PATHS` [table]: Table of default UTF-8 paths to search.
-- * `DEFAULT_DEPTH` [number]: Maximum directory depth to search. The default
--   value is `4`.
-- * `MAX` [number]: Maximum number of files to list. The default value is
--   `1000`.
--
-- ## 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 .hg folder in the local Mercurial repository.
--     local project_dir = '/path/to/project'
--     snapopen(project_dir, { folders = { '%.hg' } }, true)

-- settings
M.PATHS = {}
M.DEFAULT_DEPTH = 4
M.MAX = 1000
-- end settings

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)
  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. 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 exclusive Flag indicating whether or not to exclude `PATHS` in the
--   search. Defaults to `false`.
-- @param depth Number of directories to recurse into for finding files.
--   Defaults to `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, exclusive, depth)
  if not utf8_paths then utf8_paths = {} end
  if type(utf8_paths) == 'string' then utf8_paths = { utf8_paths } end
  if not filter then filter = {} end
  if type(filter) == 'string' then filter = { filter } end
  if not exclusive then
    for _, path in ipairs(M.PATHS) do utf8_paths[#utf8_paths + 1] = path end
  end
  DEPTH = depth or M.DEFAULT_DEPTH
  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