Skip to content

Commit b0ac40b

Browse files
committed
Add filters to Find in Files to ignore Code, Strings, or Comments for find/replace.
1 parent 2582793 commit b0ac40b

File tree

3 files changed

+123
-3
lines changed

3 files changed

+123
-3
lines changed

editor/find_in_files.cpp

+104-3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "scene/gui/box_container.h"
4040
#include "scene/gui/button.h"
4141
#include "scene/gui/check_box.h"
42+
#include "scene/gui/code_edit.h"
4243
#include "scene/gui/file_dialog.h"
4344
#include "scene/gui/grid_container.h"
4445
#include "scene/gui/label.h"
@@ -47,15 +48,17 @@
4748
#include "scene/gui/tree.h"
4849

4950
const char *FindInFiles::SIGNAL_RESULT_FOUND = "result_found";
51+
CodeEdit *_dummy_code_edit = nullptr;
5052

5153
// TODO: Would be nice in Vector and Vectors.
5254
template <typename T>
5355
inline void pop_back(T &container) {
5456
container.resize(container.size() - 1);
5557
}
5658

57-
static bool find_next(const String &line, const String &pattern, int from, bool match_case, bool whole_words, int &out_begin, int &out_end) {
59+
static bool find_next(const String &line, const String &pattern, int from, bool match_case, bool whole_words, bool ignore_code, bool ignore_strings, bool ignore_comments, int line_number, int &out_begin, int &out_end) {
5860
int end = from;
61+
line_number -= 1; // Get the line index, since that's what we use it for.
5962

6063
while (true) {
6164
int begin = match_case ? line.find(pattern, end) : line.findn(pattern, end);
@@ -77,6 +80,20 @@ static bool find_next(const String &line, const String &pattern, int from, bool
7780
}
7881
}
7982

83+
// Graceful failure, so you still get search results? Maybe it's better to assume it's valid, and not do another if check...
84+
if (_dummy_code_edit == nullptr) {
85+
return true;
86+
}
87+
if (ignore_code && _dummy_code_edit->is_in_comment(line_number, begin) == -1 && _dummy_code_edit->is_in_string(line_number, begin) == -1) {
88+
continue;
89+
}
90+
if (ignore_strings && _dummy_code_edit->is_in_string(line_number, begin) != -1) {
91+
continue;
92+
}
93+
if (ignore_comments && _dummy_code_edit->is_in_comment(line_number, begin) != -1) {
94+
continue;
95+
}
96+
8097
return true;
8198
}
8299
}
@@ -87,6 +104,30 @@ void FindInFiles::set_search_text(const String &p_pattern) {
87104
_pattern = p_pattern;
88105
}
89106

107+
void FindInFiles::set_search_file_context(const Ref<FileAccess> &file) {
108+
ERR_FAIL_NULL_MSG(_dummy_code_edit, "_dummy_code_edit is null, Find in Files cannot use 'Code, Strings, Comments' filters for search.");
109+
_dummy_code_edit->set_text(file->get_as_text());
110+
111+
bool gdscript_delimiters = false;
112+
String extension = file->get_path().get_extension();
113+
if (extension == "gd") {
114+
gdscript_delimiters = true;
115+
}
116+
117+
TypedArray<String> delimiters;
118+
if (gdscript_delimiters) {
119+
delimiters.append("#");
120+
} else { // This is for gdshader, csharp, and other c like languages.
121+
// We don't support other languages in editor do we? Otherwise we could expose a way to add more delimiters.
122+
delimiters.append("//");
123+
delimiters.append("/* */");
124+
}
125+
126+
// Only change comment delimiters, default string delimiters should work with any language.
127+
_dummy_code_edit->clear_comment_delimiters();
128+
_dummy_code_edit->set_comment_delimiters(delimiters);
129+
}
130+
90131
void FindInFiles::set_whole_words(bool p_whole_word) {
91132
_whole_words = p_whole_word;
92133
}
@@ -95,6 +136,18 @@ void FindInFiles::set_match_case(bool p_match_case) {
95136
_match_case = p_match_case;
96137
}
97138

139+
void FindInFiles::set_ignore_code(bool p_ignore_code) {
140+
_ignore_code = p_ignore_code;
141+
}
142+
143+
void FindInFiles::set_ignore_strings(bool p_ignore_strings) {
144+
_ignore_strings = p_ignore_strings;
145+
}
146+
147+
void FindInFiles::set_ignore_comments(bool p_ignore_comments) {
148+
_ignore_comments = p_ignore_comments;
149+
}
150+
98151
void FindInFiles::set_folder(const String &folder) {
99152
_root_dir = folder;
100153
}
@@ -268,6 +321,10 @@ void FindInFiles::_scan_file(const String &fpath) {
268321

269322
int line_number = 0;
270323

324+
if (_ignore_code || _ignore_strings || _ignore_comments) {
325+
set_search_file_context(f);
326+
}
327+
271328
while (!f->eof_reached()) {
272329
// Line number starts at 1.
273330
++line_number;
@@ -277,7 +334,7 @@ void FindInFiles::_scan_file(const String &fpath) {
277334

278335
String line = f->get_line();
279336

280-
while (find_next(line, _pattern, end, _match_case, _whole_words, begin, end)) {
337+
while (find_next(line, _pattern, end, _match_case, _whole_words, _ignore_code, _ignore_strings, _ignore_comments, line_number, begin, end)) {
281338
emit_signal(SNAME(SIGNAL_RESULT_FOUND), fpath, line_number, begin, end, line);
282339
}
283340
}
@@ -302,6 +359,9 @@ FindInFilesDialog::FindInFilesDialog() {
302359
set_min_size(Size2(500 * EDSCALE, 0));
303360
set_title(TTR("Find in Files"));
304361

362+
// Intentionally not added as child.
363+
_dummy_code_edit = memnew(CodeEdit);
364+
305365
VBoxContainer *vbc = memnew(VBoxContainer);
306366
vbc->set_anchor_and_offset(SIDE_LEFT, Control::ANCHOR_BEGIN, 8 * EDSCALE);
307367
vbc->set_anchor_and_offset(SIDE_TOP, Control::ANCHOR_BEGIN, 8 * EDSCALE);
@@ -350,6 +410,31 @@ FindInFilesDialog::FindInFilesDialog() {
350410
gc->add_child(hbc);
351411
}
352412

413+
Label *search_in_label = memnew(Label);
414+
search_in_label->set_text(TTR("Inside:"));
415+
gc->add_child(search_in_label);
416+
417+
{
418+
HBoxContainer *hbc = memnew(HBoxContainer);
419+
420+
_include_code_checkbox = memnew(CheckBox);
421+
_include_code_checkbox->set_text(TTR("Code"));
422+
_include_code_checkbox->set_pressed(true);
423+
hbc->add_child(_include_code_checkbox);
424+
425+
_include_strings_checkbox = memnew(CheckBox);
426+
_include_strings_checkbox->set_text(TTR("Strings"));
427+
_include_strings_checkbox->set_pressed(true);
428+
hbc->add_child(_include_strings_checkbox);
429+
430+
_include_comments_checkbox = memnew(CheckBox);
431+
_include_comments_checkbox->set_text(TTR("Comments"));
432+
_include_comments_checkbox->set_pressed(true);
433+
hbc->add_child(_include_comments_checkbox);
434+
435+
gc->add_child(hbc);
436+
}
437+
353438
Label *folder_label = memnew(Label);
354439
folder_label->set_text(TTR("Folder:"));
355440
gc->add_child(folder_label);
@@ -460,6 +545,18 @@ bool FindInFilesDialog::is_whole_words() const {
460545
return _whole_words_checkbox->is_pressed();
461546
}
462547

548+
bool FindInFilesDialog::is_ignore_code() const {
549+
return !_include_code_checkbox->is_pressed();
550+
}
551+
552+
bool FindInFilesDialog::is_ignore_strings() const {
553+
return !_include_strings_checkbox->is_pressed();
554+
}
555+
556+
bool FindInFilesDialog::is_ignore_comments() const {
557+
return !_include_comments_checkbox->is_pressed();
558+
}
559+
463560
String FindInFilesDialog::get_folder() const {
464561
String text = _folder_line_edit->get_text();
465562
return text.strip_edges();
@@ -947,6 +1044,10 @@ void FindInFilesPanel::apply_replaces_in_file(const String &fpath, const Vector<
9471044
Ref<FileAccess> f = FileAccess::open(fpath, FileAccess::READ);
9481045
ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file from path '" + fpath + "'.");
9491046

1047+
if (_finder->is_ignore_code() || _finder->is_ignore_strings() || _finder->is_ignore_comments()) {
1048+
_finder->set_search_file_context(f);
1049+
}
1050+
9501051
String buffer;
9511052
int current_line = 1;
9521053

@@ -971,7 +1072,7 @@ void FindInFilesPanel::apply_replaces_in_file(const String &fpath, const Vector<
9711072
int repl_end = locations[i].end + offset;
9721073

9731074
int _;
974-
if (!find_next(line, search_text, repl_begin, _finder->is_match_case(), _finder->is_whole_words(), _, _)) {
1075+
if (!find_next(line, search_text, repl_begin, _finder->is_match_case(), _finder->is_whole_words(), _finder->is_ignore_code(), _finder->is_ignore_strings(), _finder->is_ignore_comments(), repl_line_number, _, _)) {
9751076
// Make sure the replace is still valid in case the file was tampered with.
9761077
print_verbose(String("Occurrence no longer matches, replace will be ignored in {0}: line {1}, col {2}").format(varray(fpath, repl_line_number, repl_begin)));
9771078
continue;

editor/find_in_files.h

+16
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,22 @@ class FindInFiles : public Node {
4343
static const char *SIGNAL_FINISHED;
4444

4545
void set_search_text(const String &p_pattern);
46+
void set_search_file_context(const Ref<FileAccess> &file);
4647
void set_whole_words(bool p_whole_word);
4748
void set_match_case(bool p_match_case);
49+
void set_ignore_code(bool p_ignore_code);
50+
void set_ignore_strings(bool p_ignore_strings);
51+
void set_ignore_comments(bool p_ignore_comments);
4852
void set_folder(const String &folder);
4953
void set_filter(const HashSet<String> &exts);
5054

5155
String get_search_text() const { return _pattern; }
5256

5357
bool is_whole_words() const { return _whole_words; }
5458
bool is_match_case() const { return _match_case; }
59+
bool is_ignore_code() const { return _ignore_code; }
60+
bool is_ignore_strings() const { return _ignore_strings; }
61+
bool is_ignore_comments() const { return _ignore_comments; }
5562

5663
void start();
5764
void stop();
@@ -76,6 +83,9 @@ class FindInFiles : public Node {
7683
String _root_dir;
7784
bool _whole_words = true;
7885
bool _match_case = true;
86+
bool _ignore_code = false;
87+
bool _ignore_strings = false;
88+
bool _ignore_comments = false;
7989

8090
// State
8191
bool _searching = false;
@@ -114,6 +124,9 @@ class FindInFilesDialog : public AcceptDialog {
114124
String get_replace_text() const;
115125
bool is_match_case() const;
116126
bool is_whole_words() const;
127+
bool is_ignore_code() const;
128+
bool is_ignore_strings() const;
129+
bool is_ignore_comments() const;
117130
String get_folder() const;
118131
HashSet<String> get_filter() const;
119132

@@ -140,6 +153,9 @@ class FindInFilesDialog : public AcceptDialog {
140153
LineEdit *_folder_line_edit = nullptr;
141154
CheckBox *_match_case_checkbox = nullptr;
142155
CheckBox *_whole_words_checkbox = nullptr;
156+
CheckBox *_include_code_checkbox = nullptr;
157+
CheckBox *_include_strings_checkbox = nullptr;
158+
CheckBox *_include_comments_checkbox = nullptr;
143159
Button *_find_button = nullptr;
144160
Button *_replace_button = nullptr;
145161
FileDialog *_folder_dialog = nullptr;

editor/plugins/script_editor_plugin.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -4030,6 +4030,9 @@ void ScriptEditor::_start_find_in_files(bool with_replace) {
40304030
f->set_search_text(find_in_files_dialog->get_search_text());
40314031
f->set_match_case(find_in_files_dialog->is_match_case());
40324032
f->set_whole_words(find_in_files_dialog->is_whole_words());
4033+
f->set_ignore_code(find_in_files_dialog->is_ignore_code());
4034+
f->set_ignore_strings(find_in_files_dialog->is_ignore_strings());
4035+
f->set_ignore_comments(find_in_files_dialog->is_ignore_comments());
40334036
f->set_folder(find_in_files_dialog->get_folder());
40344037
f->set_filter(find_in_files_dialog->get_filter());
40354038

0 commit comments

Comments
 (0)