From 2fb60bd197071eb6c1da78e65e00aaaa374bdce1 Mon Sep 17 00:00:00 2001 From: mitchell <70453897+667e-11@users.noreply.github.com> Date: Mon, 25 Mar 2013 01:48:47 -0400 Subject: Added lfs.dir_foreach() for allowing Find in Files to have a filter. Also moved snapopen module into core as io.snapopen(). --- core/file_io.lua | 59 +++++++++++++++- core/init.lua | 1 + core/lfs_ext.lua | 97 +++++++++++++++++++++++++++ core/locale.conf | 3 +- core/locales/locale.de.conf | 1 + core/locales/locale.es.conf | 3 +- core/locales/locale.fr.conf | 3 +- core/locales/locale.ru.conf | 1 + doc/06_AdeptEditing.md | 15 +++-- modules/textadept/find.lua | 80 +++++++++++----------- modules/textadept/init.lua | 1 - modules/textadept/keys.lua | 8 +-- modules/textadept/menu.lua | 4 +- modules/textadept/snapopen.lua | 148 ----------------------------------------- 14 files changed, 217 insertions(+), 207 deletions(-) create mode 100644 core/lfs_ext.lua delete mode 100644 modules/textadept/snapopen.lua diff --git a/core/file_io.lua b/core/file_io.lua index fd45ac4c..878188ed 100644 --- a/core/file_io.lua +++ b/core/file_io.lua @@ -54,6 +54,9 @@ -- * _`filename`_: The UTF-8-encoded filename. -- -- [`buffer:save_as()`]: buffer.html#save_as +-- @field SNAPOPEN_MAX (number) +-- The maximum number of files to list in the snapopen dialog. +-- The default value is `1000`. module('io')]] -- Events. @@ -63,6 +66,8 @@ events.FILE_BEFORE_SAVE = 'file_before_save' events.FILE_AFTER_SAVE = 'file_after_save' events.FILE_SAVED_AS = 'file_saved_as' +io.SNAPOPEN_MAX = 1000 + --- -- List of recently opened files, the most recent being towards the top. -- @class table @@ -141,8 +146,8 @@ io.try_encodings = {'UTF-8', 'ASCII', 'ISO-8859-1', 'MacRoman'} -- Opens *utf8_filenames*, a "\n" delimited string of UTF-8-encoded filenames, -- or user-selected files. -- Emits a `FILE_OPENED` event. --- @param utf8_filenames Optional list of UTF-8-encoded filenames to open. If --- `nil`, the user is prompted with a fileselect dialog. +-- @param utf8_filenames Optional string list of UTF-8-encoded filenames to +-- open. If `nil`, the user is prompted with a fileselect dialog. -- @see _G.events -- @name open_file function io.open_file(utf8_filenames) @@ -394,3 +399,53 @@ function io.open_recent_file() NCURSES and {'--width', gui.size[1] - 2} or '') if i then io.open_file(io.recent_files[i + 1]) end end + +--- +-- Quickly open files from *utf8_paths*, a "\n" delimited string of +-- UTF-8-encoded directory paths, using a filtered list dialog. +-- Files shown in the dialog do not match any pattern in string or table +-- *filter*, and, unless *exclude_FILTER* is `true`, `lfs.FILTER` as well. A +-- filter table contains Lua patterns that match filenames to exclude, with +-- patterns matching folders to exclude listed in a `folders` sub-table. +-- Patterns starting with '!' exclude files and folders that do not match the +-- pattern that follows. Use a table of raw file extensions assigned to an +-- `extensions` key for fast filtering by extension. All strings must be encoded +-- in `_G._CHARSET`, not UTF-8. The number of files in the list is capped at +-- `SNAPOPEN_MAX`. +-- @param utf8_paths String list of UTF-8-encoded directory paths to search. +-- @param filter Optional filter for files and folders to exclude. +-- @param exclude_FILTER Optional flag indicating whether or not to exclude the +-- default filter `lfs.FILTER` in the search. If `false`, adds `lfs.FILTER` to +-- *filter*. +-- The default value is `false` to include the default filter. +-- @param ... Optional additional parameters to pass to `gui.dialog()`. +-- @usage io.snapopen(buffer.filename:match('^.+/')) -- list all files in the +-- current file's directory, subject to the default filter +-- @usage io.snapopen('/project', '!%.lua$') -- list all Lua files in a project +-- directory +-- @usage io.snapopen('/project', {folders = {'build'}}) -- list all source +-- files in a project directory +-- @see lfs.FILTER +-- @see SNAPOPEN_MAX +-- @name snapopen +function io.snapopen(utf8_paths, filter, exclude_FILTER, ...) + local list = {} + for utf8_path in utf8_paths:gmatch('[^\n]+') do + lfs.dir_foreach(utf8_path, function(file) + if #list >= io.SNAPOPEN_MAX then return false end + list[#list + 1] = file:gsub('^%.[/\\]', '') + end, filter, exclude_FILTER) + end + if #list >= io.SNAPOPEN_MAX then + gui.dialog('ok-msgbox', + '--title', _L['File Limit Exceeded'], + '--text', + string.format('%d %s %d', io.SNAPOPEN_MAX, + _L['files or more were found. Showing the first'], + io.SNAPOPEN_MAX), + '--button1', _L['_OK']) + end + local width = NCURSES and {'--width', gui.size[1] - 2} or '' + io.open_file(gui.filteredlist(_L['Open'], _L['File'], list, false, + '--select-multiple', width, ...) or '') +end diff --git a/core/init.lua b/core/init.lua index 5aa38d28..9a61379b 100644 --- a/core/init.lua +++ b/core/init.lua @@ -10,6 +10,7 @@ args = require 'args' _L = require 'locale' events = require 'events' require 'file_io' +require 'lfs_ext' require 'gui' keys = require 'keys' diff --git a/core/lfs_ext.lua b/core/lfs_ext.lua new file mode 100644 index 00000000..d79800a4 --- /dev/null +++ b/core/lfs_ext.lua @@ -0,0 +1,97 @@ +-- Copyright 2007-2013 Mitchell mitchell.att.foicica.com. See LICENSE. + +--[[ This comment is for LuaDoc. +--- +-- Extends the `lfs` library to find files in directories. +module('lfs')]] + +--- +-- Filter table containing common binary file extensions and version control +-- folders to exclude when iterating over files and directories using +-- `dir_foreach` when its `exclude_FILTER` argument is `false`. +-- @see dir_foreach +-- @class table +-- @name FILTER +lfs.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$'} +} + +-- 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 ext = filter.extensions + if ext and ext[file:match('[^%.]+$')] then return true end + for i = 1, #filter do + local patt = filter[i] + if patt:sub(1, 1) ~= '!' then + if file:find(patt) then return true end + else + if not file:find(patt:sub(2)) then return true end + end + end + return false +end + +--- +-- Iterates over all files and sub-directories in the UTF-8-encoded directory +-- *utf8_dir*, calling function *f* on each file found. +-- Files *f* is called on do not match any pattern in string or table *filter*, +-- and, unless *exclude_FILTER* is `true`, `FILTER` as well. A filter table +-- contains Lua patterns that match filenames to exclude, with patterns matching +-- folders to exclude listed in a `folders` sub-table. Patterns starting with +-- '!' exclude files and folders that do not match the pattern that follows. Use +-- a table of raw file extensions assigned to an `extensions` key for fast +-- filtering by extension. All strings must be encoded in `_G._CHARSET`, not +-- UTF-8. +-- @param utf8_dir A UTF-8-encoded directory path to iterate over. +-- @param f Function to call with each full file path found. File paths are +-- **not** encoded in UTF-8, but in `_G._CHARSET`. If *f* returns `false` +-- explicitly, iteration ceases. +-- @param filter Optional filter for files and folders to exclude. +-- @param exclude_FILTER Optional flag indicating whether or not to exclude the +-- default filter `FILTER` in the search. If `false`, adds `FILTER` to +-- *filter*. +-- The default value is `false` to include the default filter. +-- @param recursing Utility flag indicating whether or not this function has +-- been recursively called. This flag is used and set internally, and should +-- not be set otherwise. +-- @see FILTER +-- @name dir_foreach +function lfs.dir_foreach(utf8_dir, f, filter, exclude_FILTER, recursing) + if not recursing then + -- 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 FILTER to filter unless specified otherwise. + if not exclude_FILTER then + for k, v in pairs(lfs.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 + -- 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 + end + local dir = utf8_dir:iconv(_CHARSET, 'UTF-8') + local lfs_attributes = lfs.attributes + for file in lfs.dir(dir) do + if not file:find('^%.%.?$') then -- ignore . and .. + file = dir..(not WIN32 and '/' or '\\')..file + local type = lfs_attributes(file, 'mode') + if type == 'directory' and not exclude(file, filter.folders) then + lfs.dir_foreach(file, f, filter, nil, true) + elseif type == 'file' and not exclude(file, filter) then + if f(file) == false then return end + end + end + end +end diff --git a/core/locale.conf b/core/locale.conf index 297d7bfc..f694e806 100644 --- a/core/locale.conf +++ b/core/locale.conf @@ -81,6 +81,7 @@ _Whole word = _Whole word _Lua pattern = _Lua pattern _In files = _In files Find in Files = Find in Files +Find: = Find: No results found = No results found [Files Found Buffer] = [Files Found Buffer] Search wrapped = Search wrapped @@ -90,7 +91,7 @@ An error occured: = An error occured: %_Cancel = _Cancel replacement(s) made = replacement(s) made % For ncurses: -Find: = Find: +%Find: = Find: Replace: = Replace: [Next] = [Next] [Prev] = [Prev] diff --git a/core/locales/locale.de.conf b/core/locales/locale.de.conf index fcbc4ac3..071c16d2 100644 --- a/core/locales/locale.de.conf +++ b/core/locales/locale.de.conf @@ -82,6 +82,7 @@ _Whole word = Vollständiges Wort _Lua pattern = _Lua-Pattern _In files = _In Dateien Find in Files = In Dateien suchen +Find: = Suchen: No results found = Keine Treffer gefunden [Files Found Buffer] = [Gefundene Dateien-Buffer] Search wrapped = Suche beginnt von oben diff --git a/core/locales/locale.es.conf b/core/locales/locale.es.conf index eb7bb7ea..4ebf1152 100644 --- a/core/locales/locale.es.conf +++ b/core/locales/locale.es.conf @@ -82,6 +82,7 @@ _Whole word = Palabra _completa _Lua pattern = Patrón de _Lua _In files = En ficher_os Find in Files = Buscar en ficheros +Find: = Buscar: No results found = No se han encontrado coincidencias [Files Found Buffer] = [Buffer de búsqueda en ficheros] Search wrapped = La búsqueda ha sobrepasado el final/inicio del documento @@ -91,7 +92,7 @@ An error occured: = Ha ocurrido un error: %_Cancel = _Cancelar replacement(s) made = cambios(s) hecho(s) % For ncurses: -Find: = Buscar: +%Find: = Buscar: Replace: = Reemplazar: [Next] = [Siguiente] [Prev] = [Anterior] diff --git a/core/locales/locale.fr.conf b/core/locales/locale.fr.conf index 8ee12f62..907cf735 100644 --- a/core/locales/locale.fr.conf +++ b/core/locales/locale.fr.conf @@ -82,6 +82,7 @@ _Whole word = _Mot entier _Lua pattern = Pattern _Lua _In files = _Dans les fichiers Find in Files = Rechercher dans les fichiers +Find: = Rechercher: No results found = Aucun résultat trouvé [Files Found Buffer] = [Buffer des fichiers trouvés] Search wrapped = La recherche à bouclée @@ -91,7 +92,7 @@ An error occured: = Une erreur est survenue: %_Cancel = _Annuler replacement(s) made = remplacement(s) effectués % For ncurses: -Find: = Rechercher: +%Find: = Rechercher: Replace: = Remplacer: [Next] = [Suivant] [Prev] = [Précédent] diff --git a/core/locales/locale.ru.conf b/core/locales/locale.ru.conf index d0609613..c8ff6228 100644 --- a/core/locales/locale.ru.conf +++ b/core/locales/locale.ru.conf @@ -82,6 +82,7 @@ _Whole word = _Слово целиком _Lua pattern = _Шаблон lua _In files = _В файлах Find in Files = Найти в файлах +Find: = Найти: No results found = Ничего не найдено [Files Found Buffer] = [Буфер поиска в файлах] Search wrapped = Искать по кругу diff --git a/doc/06_AdeptEditing.md b/doc/06_AdeptEditing.md index 3704e208..1ea88ad4 100644 --- a/doc/06_AdeptEditing.md +++ b/doc/06_AdeptEditing.md @@ -187,15 +187,18 @@ in ncurses) key bindings. Replace in Files is not supported. You will have to "Find in Files" first, and then "Replace All" for each file a result is found in. The "Match Case", "Whole Word", and "Lua pattern" flags still apply. -_Warning_: currently, there is no way to specify a file-type filter, so Find in -Files will scan **all** files, even binary ones, in **all** sub-directories. -Searches also block Textadept from receiving additional input, making the -interface temporarily unresponsive. Searching large directories or projects can -be very time consuming and frustrating, so using a specialized, external tool -such as [ack][] is recommended. +_Warning_: currently, the only way to specify a file-type filter is through the +[find API][] and even though the default filter excludes common binary files +and version control folders from searches, Find in Files could still scan +unrecognized binary files or large, unwanted sub-directories. Searches also +block Textadept from receiving additional input, making the interface +temporarily unresponsive. Searching large directories or projects can be very +time consuming and frustrating, so using a specialized, external tool such as +[ack][] is recommended. ![Find in Files](images/findinfiles.png) +[find API]: api/gui.find.html#FILTER [ack]: http://betterthangrep.com/ ### Incremental Find diff --git a/modules/textadept/find.lua b/modules/textadept/find.lua index 0d316105..411b59c5 100644 --- a/modules/textadept/find.lua +++ b/modules/textadept/find.lua @@ -80,6 +80,22 @@ local MARK_FIND = _SCINTILLA.next_marker_number() local MARK_FIND_COLOR = 0x4D9999 local preferred_view +--- +-- Table of Lua patterns matching files and folders to exclude when finding in +-- files. +-- Each filter string is a pattern that matches filenames to exclude, with +-- patterns matching folders to exclude listed in a `folders` sub-table. +-- Patterns starting with '!' exclude files and folders that do not match the +-- pattern that follows. Use a table of raw file extensions assigned to an +-- `extensions` key for fast filtering by extension. All strings must be encoded +-- in `_G._CHARSET`, not UTF-8. +-- The default value is `lfs.FILTER`, a filter for common binary file extensions +-- and version control folders. +-- @see find_in_files +-- @class table +-- @name FILTER +find.FILTER = lfs.FILTER + -- Text escape sequences with their associated characters. -- @class table -- @name escapes @@ -92,9 +108,11 @@ local escapes = { -- Searches the *utf8_dir* or user-specified directory for files that match -- search text and options and prints the results to a buffer. -- Use the `find_text`, `match_case`, `whole_word`, and `lua` fields to set the --- search text and option flags, respectively. --- @param utf8_dir Optional UTF-8-encoded directory name to search. If `nil`, +-- search text and option flags, respectively. Use `FILTER` to set the search +-- filter. +-- @param utf8_dir Optional UTF-8-encoded directory path to search. If `nil`, -- the user is prompted for one. +-- @see FILTER -- @name find_in_files function find.find_in_files(utf8_dir) if not utf8_dir then @@ -105,46 +123,28 @@ function find.find_in_files(utf8_dir) (buffer.filename or ''):match('^.+[/\\]') or '', '--no-newline') end - if #utf8_dir > 0 then - local text = find.find_entry_text - if not find.lua then text = text:gsub('([().*+?^$%%[%]-])', '%%%1') end - if not find.match_case then text = text:lower() end - if find.whole_word then text = '%f[%w_]'..text..'%f[^%w_]' end - local match_case, whole_word = find.match_case, find.whole_word - local matches = {'Find: '..text} - function search_file(file) - local line_num = 1 - for line in io.lines(file) do - local optimized_line = line - if not match_case then optimized_line = line:lower() end - if optimized_line:find(text) then - file = file:iconv('UTF-8', _CHARSET) - matches[#matches + 1] = ('%s:%s:%s'):format(file, line_num, line) - end - line_num = line_num + 1 + if utf8_dir == '' then return end + + local text = find.find_entry_text + if not find.lua then text = text:gsub('([().*+?^$%%[%]-])', '%%%1') end + if not find.match_case then text = text:lower() end + if find.whole_word then text = '%f[%w_]'..text..'%f[^%w_]' end + local matches = {_L['Find:']..' '..text} + lfs.dir_foreach(utf8_dir, function(file) + local match_case = find.match_case + local line_num = 1 + for line in io.lines(file) do + if (match_case and line or line:lower()):find(text) then + file = file:iconv('UTF-8', _CHARSET) + matches[#matches + 1] = ('%s:%s:%s'):format(file, line_num, line) end + line_num = line_num + 1 end - local lfs_dir, lfs_attributes = lfs.dir, lfs.attributes - function search_dir(directory) - for file in lfs_dir(directory) do - if not file:find('^%.%.?$') then -- ignore . and .. - local path = directory..(not WIN32 and '/' or '\\')..file - local type = lfs_attributes(path, 'mode') - if type == 'directory' then - search_dir(path) - elseif type == 'file' then - search_file(path) - end - end - end - end - local dir = utf8_dir:iconv(_CHARSET, 'UTF-8') - search_dir(dir) - if #matches == 1 then matches[2] = _L['No results found'] end - matches[#matches + 1] = '' - if buffer._type ~= _L['[Files Found Buffer]'] then preferred_view = view end - gui._print(_L['[Files Found Buffer]'], table.concat(matches, '\n')) - end + end, find.FILTER, true) + if #matches == 1 then matches[2] = _L['No results found'] end + matches[#matches + 1] = '' + if buffer._type ~= _L['[Files Found Buffer]'] then preferred_view = view end + gui._print(_L['[Files Found Buffer]'], table.concat(matches, '\n')) end local c = _SCINTILLA.constants diff --git a/modules/textadept/init.lua b/modules/textadept/init.lua index 838c0c42..951c27b4 100644 --- a/modules/textadept/init.lua +++ b/modules/textadept/init.lua @@ -18,7 +18,6 @@ M.filter_through = require 'textadept.filter_through' M.mime_types = require 'textadept.mime_types' M.run = require 'textadept.run' M.session = require 'textadept.session' -M.snapopen = require 'textadept.snapopen' M.snippets = require 'textadept.snippets' -- These need to be loaded last. diff --git a/modules/textadept/keys.lua b/modules/textadept/keys.lua index 2951c2b3..a11f09b4 100644 --- a/modules/textadept/keys.lua +++ b/modules/textadept/keys.lua @@ -224,9 +224,7 @@ M.utils = { end, select_command = function() _M.textadept.menu.select_command() end, snapopen_filedir = function() - if buffer.filename then - _M.textadept.snapopen.open(buffer.filename:match('^(.+)[/\\]')) - end + if buffer.filename then io.snapopen(buffer.filename:match('^(.+)[/\\]')) end end, show_style = function() local buffer = buffer @@ -494,8 +492,8 @@ keys.f2 = m_bookmarks.goto_next keys[not NCURSES and 'sf2' or 'f3'] = m_bookmarks.goto_prev keys[not NCURSES and 'af2' or 'f4'] = m_bookmarks.goto_bookmark -- Snapopen. -keys[not OSX and 'cu' or 'mu'] = {m_textadept.snapopen.open, _USERHOME} --- TODO: {m_textadept.snapopen.open, _HOME} +keys[not OSX and 'cu' or 'mu'] = {io.snapopen, _USERHOME} +-- TODO: {io.snapopen, _HOME} keys[not OSX and (not NCURSES and 'caO' or 'mO') or 'cmO'] = utils.snapopen_filedir if not NCURSES then keys[not OSX and 'ci' or 'mi'] = utils.show_style end diff --git a/modules/textadept/menu.lua b/modules/textadept/menu.lua index 6cc6d447..b1ffd0c2 100644 --- a/modules/textadept/menu.lua +++ b/modules/textadept/menu.lua @@ -138,8 +138,8 @@ M.menubar = { {_L['_Goto Bookmark...'], m_bookmarks.goto_bookmark}, }, { title = _L['Snap_open'], - {_L['Snapopen _User Home'], {m_textadept.snapopen.open, _USERHOME}}, - {_L['Snapopen _Textadept Home'], {m_textadept.snapopen.open, _HOME}}, + {_L['Snapopen _User Home'], {io.snapopen, _USERHOME}}, + {_L['Snapopen _Textadept Home'], {io.snapopen, _HOME}}, {_L['Snapopen _Current Directory'], utils.snapopen_filedir}, }, { title = _L['_Snippets'], diff --git a/modules/textadept/snapopen.lua b/modules/textadept/snapopen.lua deleted file mode 100644 index 04e60002..00000000 --- a/modules/textadept/snapopen.lua +++ /dev/null @@ -1,148 +0,0 @@ --- Copyright 2007-2013 Mitchell mitchell.att.foicica.com. See LICENSE. - -local M = {} - ---[[ This comment is for LuaDoc. ---- --- Quickly open files in a set of directories using a filtered list dialog. --- @field DEFAULT_DEPTH (number) --- The maximum directory depth to search. --- The default value is `99`. --- @field MAX (number) --- The maximum number of files to list. --- The default value is `1000`. -module('_M.textadept.snapopen')]] - ---- --- The default filter table containing common binary file extensions and version --- control folders to exclude from snapopen file lists. --- @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 = 99 -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 files from the set of directories *utf8_paths* using a filtered --- list dialog. --- Files shown in the dialog do not match any pattern in string or table --- *filter*, and, unless *exclude_FILTER* is `true`, `FILTER` as well. A filter --- table contains Lua patterns that match filenames to exclude. Patterns --- starting with '!' exclude files that do not match the pattern that follows. --- The filter may also contain an `extensions` key whose value is a table of --- file extensions to exclude. Additionally, it may contain a `folders` key --- whose value is a table of folder names to exclude. Extensions and folder --- names must be encoded in UTF-8. The number of files in the list is capped at --- `MAX`. --- @param utf8_paths A UTF-8 string directory path or table of UTF-8 directory --- paths to search. --- @param filter Optional filter for files and folders to exclude. --- @param exclude_FILTER Optional flag indicating whether or not to exclude the --- default filter `FILTER` in the search. If `false`, adds `FILTER` to --- *filter*. --- The default value is `false` to include the default filter. --- @param depth Number of directories to recurse into for finding files. --- The default value is `DEFAULT_DEPTH`. --- @usage _M.textadept.snapopen.open(buffer.filename:match('^.+/')) -- list all --- files in the current file's directory, subject to the default filter --- @usage _M.textadept.snapopen.open('/project', '!%.lua$') -- list all Lua --- files in a project directory --- @usage _M.textadept.snapopen.open('/project', {folders = {'build'}}) -- list --- all source files in a project directory --- @see FILTER --- @see DEFAULT_DEPTH --- @see MAX --- @name open -function M.open(utf8_paths, filter, 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 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'], - '--text', - string.format('%d %s %d', M.MAX, - _L['files or more were found. Showing the first'], - M.MAX), - '--button1', _L['_OK']) - end - local width = NCURSES and {'--width', gui.size[1] - 2} or '' - local utf8_filenames = gui.filteredlist(_L['Open'], _L['File'], list, false, - '--select-multiple', width) or '' - for filename in utf8_filenames:gmatch('[^\n]+') do io.open_file(filename) end -end - -return M -- cgit v1.2.3