diff options
author | 2020-07-25 23:59:12 -0400 | |
---|---|---|
committer | 2020-07-25 23:59:12 -0400 | |
commit | 27f86d967cef13620101cff60a82c65302ee1877 (patch) | |
tree | ce17eee9322ece597eebe6ab0f2c267ebe5448ea | |
parent | dfcb98978d6fba1d9a79279d01b05cc2fd184556 (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.conf | 1 | ||||
-rw-r--r-- | core/locales/locale.ar.conf | 1 | ||||
-rw-r--r-- | core/locales/locale.de.conf | 1 | ||||
-rw-r--r-- | core/locales/locale.es.conf | 1 | ||||
-rw-r--r-- | core/locales/locale.fr.conf | 1 | ||||
-rw-r--r-- | core/locales/locale.it.conf | 1 | ||||
-rw-r--r-- | core/locales/locale.pl.conf | 1 | ||||
-rw-r--r-- | core/locales/locale.ru.conf | 1 | ||||
-rw-r--r-- | core/locales/locale.sv.conf | 1 | ||||
-rw-r--r-- | core/locales/locale.zh.conf | 1 | ||||
-rw-r--r-- | modules/textadept/find.lua | 72 | ||||
-rw-r--r-- | modules/textadept/menu.lua | 9 | ||||
-rw-r--r-- | src/textadept.c | 4 | ||||
-rw-r--r-- | test/test.lua | 67 |
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 |