aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar mitchell <70453897+667e-11@users.noreply.github.com>2020-07-25 23:59:12 -0400
committerGravatar mitchell <70453897+667e-11@users.noreply.github.com>2020-07-25 23:59:12 -0400
commit27f86d967cef13620101cff60a82c65302ee1877 (patch)
treece17eee9322ece597eebe6ab0f2c267ebe5448ea
parentdfcb98978d6fba1d9a79279d01b05cc2fd184556 (diff)
Find & Replace Pane now allows file filters to be specified for Find in Files.
Also updated `ui.find.focus()` to accept an optional table of options (e.g. in_files, incremental, etc.) for convenience.
-rw-r--r--core/locale.conf1
-rw-r--r--core/locales/locale.ar.conf1
-rw-r--r--core/locales/locale.de.conf1
-rw-r--r--core/locales/locale.es.conf1
-rw-r--r--core/locales/locale.fr.conf1
-rw-r--r--core/locales/locale.it.conf1
-rw-r--r--core/locales/locale.pl.conf1
-rw-r--r--core/locales/locale.ru.conf1
-rw-r--r--core/locales/locale.sv.conf1
-rw-r--r--core/locales/locale.zh.conf1
-rw-r--r--modules/textadept/find.lua72
-rw-r--r--modules/textadept/menu.lua9
-rw-r--r--src/textadept.c4
-rw-r--r--test/test.lua67
14 files changed, 120 insertions, 42 deletions
diff --git a/core/locale.conf b/core/locale.conf
index b5792cb4..67ce1ff4 100644
--- a/core/locale.conf
+++ b/core/locale.conf
@@ -89,6 +89,7 @@ returned non-zero status = returned non-zero status
# The text displayed in the find & replace pane.
Find: = _Find:
Replace: = R_eplace:
+Filter: = Filt_er:
# The button text displayed in the GUI find & replace pane.
Find Next = Find _Next
Find Prev = Find _Prev
diff --git a/core/locales/locale.ar.conf b/core/locales/locale.ar.conf
index 08dde3c0..d2168834 100644
--- a/core/locales/locale.ar.conf
+++ b/core/locales/locale.ar.conf
@@ -89,6 +89,7 @@ returned non-zero status = returned non-zero status
# The text displayed in the find & replace pane.
Find: = _ابحث:
Replace: = ا_ستبدال:
+Filter: = Filt_er:
# The button text displayed in the GUI find & replace pane.
Find Next = ابحث عن ال_تالي
Find Prev = ابحث عن ال_سابق
diff --git a/core/locales/locale.de.conf b/core/locales/locale.de.conf
index 9ddee348..a5d66120 100644
--- a/core/locales/locale.de.conf
+++ b/core/locales/locale.de.conf
@@ -89,6 +89,7 @@ returned non-zero status = returned non-zero status
# The text displayed in the find & replace pane.
Find: = Suchen
Replace: = Ersetzen
+Filter: = Filt_er:
# The button text displayed in the GUI find & replace pane.
Find Next = Vorwärts suchen
Find Prev = Rückwärts suchen
diff --git a/core/locales/locale.es.conf b/core/locales/locale.es.conf
index 3a6f8dd6..b9067999 100644
--- a/core/locales/locale.es.conf
+++ b/core/locales/locale.es.conf
@@ -89,6 +89,7 @@ returned non-zero status = returned non-zero status
# The text displayed in the find & replace pane.
Find: = _Buscar:
Replace: = Reempla_zar:
+Filter: = Filt_er:
# The button text displayed in the GUI find & replace pane.
Find Next = Buscar _siguiente
Find Prev = Buscar a_nterior
diff --git a/core/locales/locale.fr.conf b/core/locales/locale.fr.conf
index c2b45231..0580dfd7 100644
--- a/core/locales/locale.fr.conf
+++ b/core/locales/locale.fr.conf
@@ -90,6 +90,7 @@ returned non-zero status = returned non-zero status
# The text displayed in the find & replace pane.
Find: = _Rechercher:
Replace: = Remplacer p_ar:
+Filter: = Filt_er:
# The button text displayed in the GUI find & replace pane.
Find Next = Rechercher le _suivant
Find Prev = Rechercher le _précédent
diff --git a/core/locales/locale.it.conf b/core/locales/locale.it.conf
index 03c394a4..ac3bf39b 100644
--- a/core/locales/locale.it.conf
+++ b/core/locales/locale.it.conf
@@ -89,6 +89,7 @@ returned non-zero status = returned non-zero status
# The text displayed in the find & replace pane.
Find: = _Cerca:
Replace: = Sost_ituisci con:
+Filter: = Filt_er:
# The button text displayed in the GUI find & replace pane.
Find Next = Cerca il _seguente
Find Prev = Cerca il _precedente
diff --git a/core/locales/locale.pl.conf b/core/locales/locale.pl.conf
index 506aea78..51f5b86e 100644
--- a/core/locales/locale.pl.conf
+++ b/core/locales/locale.pl.conf
@@ -90,6 +90,7 @@ returned non-zero status = returned non-zero status
# The text displayed in the find & replace pane.
Find: = _Znajdź:
Replace: = Za_mień:
+Filter: = Filt_er:
# The button text displayed in the GUI find & replace pane.
Find Next = Znajdź _następne
Find Prev = Znajdź _poprzednie
diff --git a/core/locales/locale.ru.conf b/core/locales/locale.ru.conf
index ec8e8999..f0bbb5e2 100644
--- a/core/locales/locale.ru.conf
+++ b/core/locales/locale.ru.conf
@@ -89,6 +89,7 @@ returned non-zero status = returned non-zero status
# The text displayed in the find & replace pane.
Find: = Н_айти:
Replace: = _Заменить:
+Filter: = Filt_er:
# The button text displayed in the GUI find & replace pane.
Find Next = Найти _следующий
Find Prev = Найти _предыдущий
diff --git a/core/locales/locale.sv.conf b/core/locales/locale.sv.conf
index 2b74dbb6..a7bf7dfb 100644
--- a/core/locales/locale.sv.conf
+++ b/core/locales/locale.sv.conf
@@ -89,6 +89,7 @@ returned non-zero status = returned non-zero status
# The text displayed in the find & replace pane.
Find: = _Sök:
Replace: = _Ersätt:
+Filter: = Filt_er:
# The button text displayed in the GUI find & replace pane.
Find Next = Sök _nästa
Find Prev = Sök _förra
diff --git a/core/locales/locale.zh.conf b/core/locales/locale.zh.conf
index 2745bf92..48469c26 100644
--- a/core/locales/locale.zh.conf
+++ b/core/locales/locale.zh.conf
@@ -89,6 +89,7 @@ returned non-zero status = returned non-zero status
# The text displayed in the find & replace pane.
Find: = 查找(_F):
Replace: = 替换(_E):
+Filter: = Filt_er:
# The button text displayed in the GUI find & replace pane.
Find Next = 查找下一个(_N)
Find Prev = 查找上一个(_P)
diff --git a/modules/textadept/find.lua b/modules/textadept/find.lua
index 59286512..92489578 100644
--- a/modules/textadept/find.lua
+++ b/modules/textadept/find.lua
@@ -9,6 +9,8 @@ local M = ui.find
-- The text in the "Find" entry.
-- @field replace_entry_text (string)
-- The text in the "Replace" entry.
+-- When searching for text in a directory of files, this is the current file
+-- and directory filter.
-- @field match_case (bool)
-- Match search text case sensitively.
-- The default value is `false`.
@@ -20,7 +22,7 @@ local M = ui.find
-- Interpret search text as a Regular Expression.
-- The default value is `false`.
-- @field in_files (bool)
--- Find search text in a list of files.
+-- Find search text in a directory of files.
-- The default value is `false`.
-- @field incremental (bool)
-- Find search text incrementally as it is typed.
@@ -92,7 +94,7 @@ events.FIND_WRAPPED = 'find_wrapped'
local preferred_view
---
--- Map of file paths to filters used in `ui.find.find_in_files()`.
+-- Map of directory paths to filters used in `ui.find.find_in_files()`.
-- @class table
-- @name find_in_files_filters
-- @see find_in_files
@@ -102,8 +104,36 @@ M.find_in_files_filters = {}
-- expected during a find session ("replace all" with selected text normally
-- does "replace in selection"). Also track find text for incremental find (if
-- text has changed, the user is still typing; if text is the same, the user
--- clicked "Find Next" or "Find Prev").
-local find_text, found_text
+-- clicked "Find Next" or "Find Prev"). Keep track of repl_text for
+-- non-"In files" in order to restore it from filter text as necessary.
+local find_text, found_text, repl_text = nil, nil, ''
+
+-- Returns a reasonable initial directory for use with Find in Files.
+local function ff_dir()
+ return io.get_project_root() or (buffer.filename or ''):match('^.+[/\\]') or
+ lfs.currentdir()
+end
+
+local orig_focus = M.focus
+---
+-- Displays and focuses the Find & Replace Pane.
+-- @param options Optional table of options to initially set.
+-- @name focus
+function M.focus(options)
+ if assert_type(options, 'table/nil', 1) then
+ for k, v in pairs(options) do M[k] = v end
+ end
+ M.replace_label_text = not M.in_files and _L['Replace:'] or _L['Filter:']
+ if M.in_files then
+ repl_text = M.replace_entry_text -- save
+ local filter = M.find_in_files_filters[ff_dir()] or lfs.default_filter
+ M.replace_entry_text = type(filter) == 'string' and filter or
+ table.concat(filter, ';')
+ elseif repl_text and M.replace_entry_text ~= repl_text then
+ M.replace_entry_text = repl_text -- restore
+ end
+ orig_focus()
+end
-- Returns a bit-mask of search flags to use in Scintilla search functions based
-- on the checkboxes in the find box.
@@ -223,12 +253,12 @@ events.connect(
-- set the search text and option flags, respectively.
-- A filter determines which files to search in, with the default filter being
-- `ui.find.find_in_files_filters[dir]` (if it exists) or `lfs.default_filter`.
--- A filter consists of Lua patterns that match filenames to include or exclude.
--- Patterns are inclusive by default. Exclusive patterns begin with a '!'. If no
--- inclusive patterns are given, any filename is initially considered. As a
--- convenience, file extensions can be specified literally instead of as a Lua
--- pattern (e.g. '.lua' vs. '%.lua$'), and '/' also matches the Windows
--- directory separator ('[/\\]' is not needed).
+-- A filter consists of Lua patterns that match file and directory paths to
+-- include or exclude. Patterns are inclusive by default. Exclusive patterns
+-- begin with a '!'. If no inclusive patterns are given, any filename is
+-- initially considered. As a convenience, file extensions can be specified
+-- literally instead of as a Lua pattern (e.g. '.lua' vs. '%.lua$'), and '/'
+-- also matches the Windows directory separator ('[/\\]' is not needed).
-- If *filter* is `nil`, the filter from the `ui.find.find_in_files_filters`
-- table for *dir* is used. If that filter does not exist, `lfs.default_filter`
-- is used.
@@ -243,20 +273,26 @@ function M.find_in_files(dir, filter)
if not assert_type(dir, 'string/nil', 1) then
dir = ui.dialogs.fileselect{
title = _L['Select Directory'], select_only_directories = true,
- with_directory = io.get_project_root() or
- (buffer.filename or ''):match('^.+[/\\]') or lfs.currentdir()
+ with_directory = ff_dir()
}
if not dir then return end
end
if not assert_type(filter, 'string/table/nil', 2) then
+ if M.replace_entry_text ~= repl_text then
+ -- Update stored filter.
+ local t = {}
+ for patt in M.replace_entry_text:gmatch('[^;]+') do t[#t + 1] = patt end
+ M.find_in_files_filters[dir] = t
+ end
filter = M.find_in_files_filters[dir] or lfs.default_filter
end
if buffer._type ~= _L['[Files Found Buffer]'] then preferred_view = view end
ui.silent_print = false
- ui._print(
- _L['[Files Found Buffer]'],
- string.format('%s %s', _L['Find:']:gsub('_', ''), M.find_entry_text))
+ ui._print(_L['[Files Found Buffer]'], string.format(
+ '%s %s\n%s %s', _L['Find:']:gsub('_', ''), M.find_entry_text,
+ _L['Filter:']:gsub('_', ''),
+ type(filter) == 'string' and filter or table.concat(filter, ';')))
buffer.indicator_current = M.INDIC_FIND
-- Determine which files to search.
@@ -445,12 +481,6 @@ end)
--[[ The functions below are Lua C functions.
---
--- Displays and focuses the Find & Replace Pane.
--- @class function
--- @name focus
-local focus
-
----
-- Mimics pressing the "Find Next" button.
-- @class function
-- @name find_next
diff --git a/modules/textadept/menu.lua b/modules/textadept/menu.lua
index 14efd40f..d6120771 100644
--- a/modules/textadept/menu.lua
+++ b/modules/textadept/menu.lua
@@ -139,21 +139,18 @@ local default_menubar = {
{
title = _L['Search'],
{_L['Find'], function()
- ui.find.in_files, ui.find.incremental = false, false
- ui.find.focus()
+ ui.find.focus{in_files = false, incremental = false}
end},
{_L['Find Next'], ui.find.find_next},
{_L['Find Previous'], ui.find.find_prev},
{_L['Replace'], ui.find.replace},
{_L['Replace All'], ui.find.replace_all},
{_L['Find Incremental'], function()
- ui.find.in_files, ui.find.incremental = false, true
- ui.find.focus()
+ ui.find.focus{in_files = false, incremental = true}
end},
SEPARATOR,
{_L['Find in Files'], function()
- ui.find.in_files, ui.find.incremental = true, false
- ui.find.focus()
+ ui.find.focus{in_files = true, incremental = false}
end},
{_L['Goto Next File Found'], function() ui.find.goto_file_found(true) end},
{_L['Goto Previous File Found'], function()
diff --git a/src/textadept.c b/src/textadept.c
index c30899e2..b24753d9 100644
--- a/src/textadept.c
+++ b/src/textadept.c
@@ -422,10 +422,10 @@ static int focus_find(lua_State *L) {
int e_width = COLS - o_width - b_width - l_width - 1;
find_entry = newCDKEntry(
findbox, l_width - strlen(find_label), TOP, NULL, find_label, A_NORMAL, '_',
- vMIXED, e_width, 0, 64, false, false);
+ vMIXED, e_width, 0, 1024, false, false);
repl_entry = newCDKEntry(
findbox, l_width - strlen(repl_label), BOTTOM, NULL, repl_label, A_NORMAL,
- '_', vMIXED, e_width, 0, 64, false, false);
+ '_', vMIXED, e_width, 0, 1024, false, false);
CDKBUTTONBOX *buttonbox, *optionbox;
buttonbox = newCDKButtonbox(
findbox, COLS - o_width - b_width, TOP, 2, b_width, NULL, 2, 2,
diff --git a/test/test.lua b/test/test.lua
index 00ed70be..3831dc33 100644
--- a/test/test.lua
+++ b/test/test.lua
@@ -1850,7 +1850,7 @@ function test_editing_highlight_word()
update()
pos = buffer:indicator_end(textadept.editing.INDIC_HIGHLIGHT, 2)
assert_equal(pos, 1) -- no highlights
- textadept.editing.highlight_words = textadept.editing.HIGHLIGHT_SELECTED -- reset
+ textadept.editing.highlight_words = textadept.editing.HIGHLIGHT_SELECTED
-- Verify partial word selections do not highlight words.
buffer:set_sel(1, 3)
pos = buffer:indicator_end(textadept.editing.INDIC_HIGHLIGHT, 2)
@@ -2098,8 +2098,8 @@ function test_ui_find_find_text()
ui.find.find_entry_text = 'quux'
ui.find.find_next()
assert_equal(buffer.selection_start, buffer.selection_end) -- no match
- ui.find.find_entry_text = '' -- reset
- ui.find.match_case, ui.find.regex = false, false -- reset
+ ui.find.match_case, ui.find.regex = false, false
+ ui.find.find_entry_text = ''
buffer:close(true)
end
@@ -2112,6 +2112,8 @@ function test_ui_find_highlight_results()
end
end
+ local highlight_all_matches = ui.find.highlight_all_matches
+ ui.find.highlight_all_matches = true
buffer.new()
buffer:append_text(table.concat({
'foo',
@@ -2140,7 +2142,7 @@ function test_ui_find_highlight_results()
buffer:position_from_line(LINE(4)),
buffer:position_from_line(LINE(4)) + 8,
}
- ui.find.regex = false -- reset
+ ui.find.regex = false
-- Do not highlight short searches (potential performance issue).
ui.find.find_entry_text = 'f'
ui.find.find_next()
@@ -2152,8 +2154,8 @@ function test_ui_find_highlight_results()
ui.find.find_next()
pos = buffer:indicator_end(ui.find.INDIC_FIND, 2)
assert_equal(pos, 1)
- ui.find.highlight_all_matches = true -- reset
- ui.find.find_entry_text = '' -- reset
+ ui.find.find_entry_text = ''
+ ui.find.highlight_all_matches = highlight_all_matches -- reset
buffer:close(true)
end
@@ -2167,7 +2169,6 @@ function test_ui_find_incremental()
}, '\n'))
assert_equal(buffer.current_pos, POS(1))
ui.find.incremental = true
- if not CURSES then ui.find.focus() end
ui.find.find_entry_text = 'f' -- simulate 'f' keypress
if CURSES then events.emit(events.FIND_TEXT_CHANGED) end -- simulate
assert_equal(buffer.selection_start, POS(1) + 1)
@@ -2204,8 +2205,8 @@ function test_ui_find_incremental()
if CURSES then events.emit(events.FIND_TEXT_CHANGED) end -- simulate
assert_equal(buffer:get_sel_text(), 'foobar')
ui.find.whole_word = false
- ui.find.find_entry_text = '' -- reset
- ui.find.incremental = false -- reset
+ ui.find.find_entry_text = ''
+ ui.find.incremental = false
buffer:close(true)
end
@@ -2235,8 +2236,8 @@ function test_ui_find_incremental_highlight()
local mask = buffer:indicator_all_on_for(pos)
assert(mask & bit > 0, 'no indicator on line %d', buffer:line_from_position(pos))
end
- ui.find.find_entry_text = '' -- reset
- ui.find.incremental = false -- reset
+ ui.find.find_entry_text = ''
+ ui.find.incremental = false
buffer:close(true)
end
@@ -2290,6 +2291,46 @@ function test_ui_find_find_in_files()
assert_raises(function() ui.find.find_in_files('', 1) end, 'string/table/nil expected, got number')
end
+function test_ui_find_find_in_files_interactive()
+ local filter = ui.find.find_in_files_filters[_HOME]
+ ui.find.find_in_files_filters[_HOME] = nil -- ensure not set
+ ui.find.find_entry_text = 'foo'
+ ui.find.in_files = true
+ ui.find.replace_entry_text = '/test'
+ events.emit(events.FIND, ui.find.find_entry_text, true)
+ local results = buffer:get_text()
+ assert(results:find('Filter: /test\n'), 'no filter defined')
+ assert(results:find('src/foo.c'), 'foo.c not found')
+ assert(results:find('include/foo.h'), 'foo.h not found')
+ assert_equal(table.concat(ui.find.find_in_files_filters[_HOME], ';'), '/test')
+ buffer:clear_all()
+ ui.find.replace_entry_text = '/test;.c'
+ events.emit(events.FIND, ui.find.find_entry_text, true)
+ results = buffer:get_text()
+ assert(results:find('Filter: /test;.c\n'), 'no filter defined')
+ assert(results:find('src/foo.c'), 'foo.c not found')
+ assert(not results:find('include/foo.h'), 'foo.h found')
+ assert_equal(table.concat(ui.find.find_in_files_filters[_HOME], ';'), '/test;.c')
+ if not CURSES then
+ -- Verify save and restore of replacement text and directory filters.
+ ui.find.focus{in_files = false}
+ assert_equal(ui.find.in_files, false)
+ ui.find.replace_entry_text = 'bar'
+ ui.find.focus{in_files = true}
+ assert_equal(ui.find.in_files, true)
+ assert_equal(ui.find.replace_entry_text, '/test;.c')
+ ui.find.focus{in_files = false}
+ assert_equal(ui.find.replace_entry_text, 'bar')
+ end
+ ui.find.find_entry_text = ''
+ ui.find.in_files = false
+ buffer:close()
+ ui.goto_view(1)
+ view:unsplit()
+ buffer:close()
+ ui.find.find_in_files_filters[_HOME] = filter
+end
+
function test_ui_find_replace()
buffer.new()
buffer:set_text('foofoo')
@@ -2313,7 +2354,7 @@ function test_ui_find_replace()
ui.find.replace_entry_text = ''
ui.find.replace()
assert_equal(buffer:get_text(), 'barbooሴ')
- ui.find.find_entry_text, ui.find.replace_entry_text = '', '' -- reset
+ ui.find.find_entry_text, ui.find.replace_entry_text = '', ''
buffer:close(true)
end
@@ -2344,7 +2385,7 @@ function test_ui_find_replace_all()
ui.find.find_entry_text, ui.find.replace_entry_text = 'quux', ''
ui.find.replace_all()
assert_equal(buffer:get_text(), '\nbar\nbaz\n')
- ui.find.find_entry_text, ui.find.replace_entry_text = '', '' -- reset
+ ui.find.find_entry_text, ui.find.replace_entry_text = '', ''
buffer:close(true)
end