diff options
-rw-r--r-- | core/.find.lua | 13 | ||||
-rw-r--r-- | core/ext/find.lua | 91 | ||||
-rw-r--r-- | core/locale.lua | 4 | ||||
-rw-r--r-- | src/lua_interface.c | 4 | ||||
-rw-r--r-- | src/textadept.c | 10 | ||||
-rw-r--r-- | src/textadept.h | 3 |
6 files changed, 106 insertions, 19 deletions
diff --git a/core/.find.lua b/core/.find.lua index ab6a9c85..71fa915b 100644 --- a/core/.find.lua +++ b/core/.find.lua @@ -12,6 +12,12 @@ module('textadept.find') -- find with Lua patterns and replace with Lua captures and even Lua code! Lua -- captures (%n) are available for a Lua pattern search and embedded Lua code -- enclosed in %() is always available. +-- +-- Find in Files will prompt for a directory to recursively search and display +-- the results in a new buffer. Double-clicking a search result will jump to it +-- in the file. 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. --- -- Textadept's find table. @@ -35,14 +41,15 @@ local escapes = {} -- This is used by the find dialog. It is recommended to use the buffer:find() -- function for scripting. -- @param text The text to find. --- @param flags Search flags. This is a number mask of 3 flags: match case (2), --- whole word (4), and Lua pattern (8) joined with binary AND. -- @param next Flag indicating whether or not the search direction is forward. +-- @param flags Search flags. This is a number mask of 4 flags: match case (2), +-- whole word (4), Lua pattern (8), and in files (16) joined with binary OR. +-- If nil, this is determined based on the checkboxes in the find box. -- @param nowrap Flag indicating whether or not the search won't wrap. -- @param wrapped Utility flag indicating whether or not the search has wrapped -- for displaying useful statusbar information. This flag is used and set -- internally, and should not be set otherwise. -function find.find(text, flags, next, nowrap, wrapped) end +function find.find(text, next, flags, nowrap, wrapped) end --- -- Replaces found text. diff --git a/core/ext/find.lua b/core/ext/find.lua index e036934f..11a6f448 100644 --- a/core/ext/find.lua +++ b/core/ext/find.lua @@ -18,30 +18,37 @@ local escapes = { -- function for scripting. -- @param text The text to find. -- @param next Flag indicating whether or not the search direction is forward. --- @param flags Search flags. This is a number mask of 3 flags: match case (2), --- whole word (4), and Lua pattern (8) joined with binary OR. If nil, this is --- determined based on the checkboxes in the find box. +-- @param flags Search flags. This is a number mask of 4 flags: match case (2), +-- whole word (4), Lua pattern (8), and in files (16) joined with binary OR. +-- If nil, this is determined based on the checkboxes in the find box. -- @param nowrap Flag indicating whether or not the search won't wrap. -- @param wrapped Utility flag indicating whether or not the search has wrapped -- for displaying useful statusbar information. This flag is used and set -- internally, and should not be set otherwise. function find.find(text, next, flags, nowrap, wrapped) local buffer = buffer - local increment, result - text = text:gsub('\\[abfnrtv\\]', escapes) - find.captures = nil + local locale = textadept.locale + + local increment if buffer.current_pos == buffer.anchor then increment = 0 elseif not wrapped then increment = next and 1 or -1 end + if not flags then local find, c = find, textadept.constants flags = 0 if find.match_case then flags = flags + c.SCFIND_MATCHCASE end if find.whole_word then flags = flags + c.SCFIND_WHOLEWORD end if find.lua then flags = flags + 8 end + if find.in_files then flags = flags + 16 end end + + local result + find.captures = nil + text = text:gsub('\\[abfnrtv\\]', escapes) + if flags < 8 then buffer:goto_pos(buffer[next and 'current_pos' or 'anchor'] + increment) buffer:search_anchor() @@ -51,7 +58,8 @@ function find.find(text, next, flags, nowrap, wrapped) result = buffer:search_prev(flags, text) end if result then buffer:scroll_caret() end - else -- lua pattern search (forward search only) + + elseif flags < 16 then -- lua pattern search (forward search only) local buffer_text = buffer:get_text(buffer.length) local results = { buffer_text:find(text, buffer.anchor + increment) } if #results > 0 then @@ -61,7 +69,56 @@ function find.find(text, next, flags, nowrap, wrapped) else result = -1 end + + else -- find in files + local dir = + cocoa_dialog('fileselect', { + title = locale.FIND_IN_FILES_TITLE, + text = locale.FIND_IN_FILES_TEXT, + ['select-only-directories'] = true, + ['with-directory'] = (buffer.filename or ''):match('^.+[/\\]'), + ['no-newline'] = true + }) + if #dir > 0 then + if not find.lua then text = text:gsub('([().*+?^$%%[%]-])', '%%%1') end + if find.whole_word then text = '[^%W_]'..text..'[^%W_]' end + local lfs = require 'lfs' + local match_case = find.match_case + local whole_word = find.whole_word + local format = string.format + local matches = {} + 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 whole_word then optimized_line = ' '..line..' ' end + if string.find(optimized_line, text) then + matches[#matches + 1] = format('%s:%s:%s', file, line_num, line) + end + line_num = line_num + 1 + end + end + function search_dir(directory) + for file in lfs.dir(directory) do + if not file:match('^%.') then + local path = directory..'/'..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 + search_dir(dir) + if #matches == 0 then matches[1] = locale.FIND_NO_RESULTS end + textadept._print('shows_files_found', table.concat(matches, '\n')) + end + return end + if result == -1 and not nowrap and not wrapped then -- wrap the search local anchor, pos = buffer.anchor, buffer.current_pos if next or flags >= 8 then @@ -69,16 +126,17 @@ function find.find(text, next, flags, nowrap, wrapped) else buffer:goto_pos(buffer.length) end - textadept.statusbar_text = textadept.locale.FIND_SEARCH_WRAPPED + textadept.statusbar_text = locale.FIND_SEARCH_WRAPPED result = find.find(text, next, flags, true, true) if not result then - textadept.statusbar_text = textadept.locale.FIND_NO_RESULTS + textadept.statusbar_text = locale.FIND_NO_RESULTS buffer:goto_pos(anchor) end return result elseif result ~= -1 and not wrapped then textadept.statusbar_text = '' end + return result ~= -1 end @@ -145,3 +203,18 @@ function find.replace_all(ftext, rtext, flags) textadept.statusbar_text = string.format(textadept.locale.FIND_REPLACEMENTS_MADE, tostring(count)) end + +--- +-- When the user double-clicks a found file, go to the line in the file the text +-- was found at. +-- @param pos The position of the caret. +-- @param line_num The line double-clicked. +function goto_file(pos, line_num) + if buffer.shows_files_found then + line = buffer:get_line(line_num) + local file, line_num = line:match('^(.+):(%d+):.+$') + textadept.io.open(file) + _m.textadept.editing.goto_line(line_num) + end +end +textadept.events.add_handler('double_click', goto_file) diff --git a/core/locale.lua b/core/locale.lua index c01c6604..4eaa2177 100644 --- a/core/locale.lua +++ b/core/locale.lua @@ -86,6 +86,10 @@ FIND_ERROR_DIALOG_TITLE = 'Error' FIND_ERROR_DIALOG_TEXT = 'An error occured' -- "%d replacement(s) made" FIND_REPLACEMENTS_MADE = '%d replacement(s) made' +-- Find in Files +FIND_IN_FILES_TITLE = 'Find in Files' +-- Select Directory to Search +FIND_IN_FILES_TEXT = 'Select Directory to Search' -- core/ext/keys.lua diff --git a/src/lua_interface.c b/src/lua_interface.c index cc9103f3..2e9c3f5f 100644 --- a/src/lua_interface.c +++ b/src/lua_interface.c @@ -1296,6 +1296,8 @@ static int l_find_mt_index(lua_State *lua) { lua_pushboolean(lua, toggled(whole_word_opt)); else if (streq(key, "lua")) lua_pushboolean(lua, toggled(lua_opt)); + else if (streq(key, "in_files")) + lua_pushboolean(lua, toggled(in_files_opt)); else lua_rawget(lua, 1); return 1; @@ -1314,6 +1316,8 @@ static int l_find_mt_newindex(lua_State *lua) { toggle(whole_word_opt, lua_toboolean(lua, -1) ? TRUE : FALSE); else if (streq(key, "lua")) toggle(lua_opt, lua_toboolean(lua, -1) ? TRUE : FALSE); + else if (streq(key, "in_files")) + toggle(in_files_opt, lua_toboolean(lua, -1) ? TRUE : FALSE); else lua_rawset(lua, 1); return 0; diff --git a/src/textadept.c b/src/textadept.c index a76717e8..4fc36e83 100644 --- a/src/textadept.c +++ b/src/textadept.c @@ -60,8 +60,8 @@ static void pm_menu_activate(GtkWidget *menu_item, gpointer menu_id); // Find/Replace GtkWidget *findbox, *find_entry, *replace_entry, *fnext_button, *fprev_button, - *r_button, *ra_button, *match_case_opt, *whole_word_opt, - /**incremental_opt,*/ *lua_opt; + *r_button, *ra_button, *match_case_opt, *whole_word_opt, *lua_opt, + *in_files_opt; GtkAttachOptions ao_normal = static_cast<GtkAttachOptions>(GTK_SHRINK | GTK_FILL), ao_expand = static_cast<GtkAttachOptions>(GTK_EXPAND | GTK_FILL); @@ -920,8 +920,8 @@ GtkWidget *find_create_ui() { ra_button = gtk_button_new_with_mnemonic("Replace _All"); match_case_opt = gtk_check_button_new_with_mnemonic("_Match case"); whole_word_opt = gtk_check_button_new_with_mnemonic("_Whole word"); - //incremental_opt = gtk_check_button_new_with_mnemonic("_Incremental"); lua_opt = gtk_check_button_new_with_mnemonic("_Lua pattern"); + in_files_opt = gtk_check_button_new_with_mnemonic("_In Files"); gtk_label_set_mnemonic_widget(GTK_LABEL(flabel), find_entry); gtk_label_set_mnemonic_widget(GTK_LABEL(rlabel), replace_entry); @@ -937,8 +937,8 @@ GtkWidget *find_create_ui() { attach(ra_button, 3, 4, 1, 2, ao_normal, ao_normal, 0, 0); attach(match_case_opt, 4, 5, 0, 1, ao_normal, ao_normal, 5, 0); attach(whole_word_opt, 4, 5, 1, 2, ao_normal, ao_normal, 5, 0); - //attach(incremental_opt, 5, 6, 0, 1, ao_normal, ao_normal, 5, 0); attach(lua_opt, 5, 6, 0, 1, ao_normal, ao_normal, 5, 0); + attach(in_files_opt, 5, 6, 1, 2, ao_normal, ao_normal, 5, 0); signal(fnext_button, "clicked", button_clicked); signal(fprev_button, "clicked", button_clicked); @@ -952,8 +952,8 @@ GtkWidget *find_create_ui() { GTK_WIDGET_UNSET_FLAGS(ra_button, GTK_CAN_FOCUS); GTK_WIDGET_UNSET_FLAGS(match_case_opt, GTK_CAN_FOCUS); GTK_WIDGET_UNSET_FLAGS(whole_word_opt, GTK_CAN_FOCUS); - //GTK_WIDGET_UNSET_FLAGS(incremental_opt, GTK_CAN_FOCUS); GTK_WIDGET_UNSET_FLAGS(lua_opt, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS(in_files_opt, GTK_CAN_FOCUS); return findbox; } diff --git a/src/textadept.h b/src/textadept.h index 5ccea139..c0677ae2 100644 --- a/src/textadept.h +++ b/src/textadept.h @@ -33,8 +33,7 @@ using namespace Scintilla; extern GtkWidget *window, *focused_editor, *command_entry, *pm_container, *pm_entry, *pm_view, *findbox, *find_entry, *replace_entry, *fnext_button, *fprev_button, *r_button, *ra_button, - *match_case_opt, *whole_word_opt, /**incremental_opt,*/ - *lua_opt; + *match_case_opt, *whole_word_opt, *lua_opt, *in_files_opt; extern GtkEntryCompletion *command_entry_completion; extern GtkTreeStore *cec_store, *pm_store; extern lua_State *lua; |