diff --git a/plugins/fuzzy-search/fuzzy-search-popover.vala b/plugins/fuzzy-search/fuzzy-search-popover.vala index 742e1fcec9..98273e17f0 100644 --- a/plugins/fuzzy-search/fuzzy-search-popover.vala +++ b/plugins/fuzzy-search/fuzzy-search-popover.vala @@ -87,7 +87,7 @@ public class Scratch.FuzzySearchPopover : Gtk.Popover { construct { this.get_style_context ().add_class ("fuzzy-popover"); - title_label = new Gtk.Label (_("Search for project files")); + title_label = new Gtk.Label (_("Find project files")); title_label.halign = Gtk.Align.START; title_label.get_style_context ().add_class ("h4"); diff --git a/plugins/fuzzy-search/fuzzy-search-project.vala b/plugins/fuzzy-search/fuzzy-search-project.vala new file mode 100644 index 0000000000..24559449bc --- /dev/null +++ b/plugins/fuzzy-search/fuzzy-search-project.vala @@ -0,0 +1,87 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2023 elementary, Inc. + * + * Authored by: Marvin Ahlgrimm + */ + +public class Scratch.Services.SearchProject { + public string root_path { get; private set; } + public Gee.ArrayList relative_file_paths { get; private set; } + private MonitoredRepository? monitored_repo; + + public SearchProject (string root, MonitoredRepository? repo) { + root_path = root; + monitored_repo = repo; + relative_file_paths = new Gee.ArrayList (); + } + + public async void parse_async (string path) { + new Thread (null, () => { + parse_async_internal.begin (path, (obj, res) => { + parse_async_internal.end (res); + }); + + Idle.add (parse_async.callback); + }); + + yield; + } + + private async void parse_async_internal (string path) { + try { + // Ignore dot-prefixed directories + string path_basename = Path.get_basename (path); + if (FileUtils.test (path, GLib.FileTest.IS_DIR) && path_basename.has_prefix (".")) { + return; + } + + try { + // Don't use paths which are ignored from .gitignore + if (monitored_repo != null && monitored_repo.path_is_ignored (path)) { + return; + } + } catch (Error e) { + warning ("An error occurred while checking if item '%s' is git-ignored: %s", path, e.message); + } + + var dir = Dir.open (path); + var name = dir.read_name (); + + while (name != null) { + var new_search_path = ""; + if (path.has_suffix (GLib.Path.DIR_SEPARATOR_S)) { + new_search_path = path.substring (0, path.length - 1); + } else { + new_search_path = path; + } + + parse_async_internal.begin (new_search_path + GLib.Path.DIR_SEPARATOR_S + name, (obj, res) => { + parse_async_internal.end (res); + }); + + name = dir.read_name (); + } + } catch (FileError e) { + // This adds branch is reached when a non-directory was reached, i.e. is a file + // If a file was reached, add it's relative path (starting after the project root path) + // to the list. + + // Relative paths are used because the longer the path is the less accurate are the results + if (check_if_valid_path_to_add (path)) { + var subpath = path.replace (root_path, ""); + relative_file_paths.add (subpath.substring (1, subpath.length - 1)); + } + } + } + + private bool check_if_valid_path_to_add (string path) { + try { + File file = File.new_for_path (path); + var file_info = file.query_info ("standard::*", 0); + return Utils.check_if_valid_text_file (path, file_info); + } catch (Error e) { + return false; + } + } +} diff --git a/plugins/fuzzy-search/fuzzy-search.plugin b/plugins/fuzzy-search/fuzzy-search.plugin index 3c6ec9c228..57b3e4f42e 100644 --- a/plugins/fuzzy-search/fuzzy-search.plugin +++ b/plugins/fuzzy-search/fuzzy-search.plugin @@ -2,8 +2,8 @@ Module=fuzzy-search Loader=C IAge=1 -Name=Fuzzy Search -Description=Fuzzy search all project files (Show using ALT + F) +Name=Find Project Files +Description=Fuzzy search for files in all open projects (Show using ALT + F) Icon=system-search Authors=Marvin Ahlgrimm;Colin Kiama Copyright=Copyright © 2021 Marvin Ahlgrimm diff --git a/plugins/fuzzy-search/fuzzy-search.vala b/plugins/fuzzy-search/fuzzy-search.vala index 5ded14329f..caea6e9b40 100644 --- a/plugins/fuzzy-search/fuzzy-search.vala +++ b/plugins/fuzzy-search/fuzzy-search.vala @@ -5,94 +5,18 @@ * Authored by: Marvin Ahlgrimm */ -public class Scratch.Services.SearchProject { - public string root_path { get; private set; } - public Gee.ArrayList relative_file_paths { get; private set; } - private MonitoredRepository? monitored_repo; - - public SearchProject (string root, MonitoredRepository? repo) { - root_path = root; - monitored_repo = repo; - relative_file_paths = new Gee.ArrayList (); - } - - public async void parse_async (string path) { - new Thread (null, () => { - parse_async_internal.begin (path, (obj, res) => { - parse_async_internal.end (res); - }); - - Idle.add (parse_async.callback); - }); - - yield; - } - - private async void parse_async_internal (string path) { - try { - // Ignore dot-prefixed directories - string path_basename = Path.get_basename (path); - if (FileUtils.test (path, GLib.FileTest.IS_DIR) && path_basename.has_prefix (".")) { - return; - } - - try { - // Don't use paths which are ignored from .gitignore - if (monitored_repo != null && monitored_repo.path_is_ignored (path)) { - return; - } - } catch (Error e) { - warning ("An error occurred while checking if item '%s' is git-ignored: %s", path, e.message); - } - - var dir = Dir.open (path); - var name = dir.read_name (); - - while (name != null) { - var new_search_path = ""; - if (path.has_suffix (GLib.Path.DIR_SEPARATOR_S)) { - new_search_path = path.substring (0, path.length - 1); - } else { - new_search_path = path; - } - - parse_async_internal.begin (new_search_path + GLib.Path.DIR_SEPARATOR_S + name, (obj, res) => { - parse_async_internal.end (res); - }); - - name = dir.read_name (); - } - } catch (FileError e) { - // This adds branch is reached when a non-directory was reached, i.e. is a file - // If a file was reached, add it's relative path (starting after the project root path) - // to the list. - - // Relative paths are used because the longer the path is the less accurate are the results - if (check_if_valid_path_to_add (path)) { - var subpath = path.replace (root_path, ""); - relative_file_paths.add (subpath.substring (1, subpath.length - 1)); - } - } - } - - private bool check_if_valid_path_to_add (string path) { - try { - File file = File.new_for_path (path); - var file_info = file.query_info ("standard::*", 0); - return Utils.check_if_valid_text_file (path, file_info); - } catch (Error e) { - return false; - } - } -} public class Scratch.Plugins.FuzzySearch: Peas.ExtensionBase, Peas.Activatable { public Object object { owned get; construct; } + private const uint ACCEL_KEY = Gdk.Key.F; + private const Gdk.ModifierType ACCEL_MODTYPE = Gdk.ModifierType.MOD1_MASK; + private Gee.HashMap project_paths; private MainWindow window = null; private Scratch.Services.Interface plugins; private Gtk.EventControllerKey key_controller; + private Gtk.MenuItem fuzzy_menuitem; public void update_state () { @@ -102,64 +26,81 @@ public class Scratch.Plugins.FuzzySearch: Peas.ExtensionBase, Peas.Activatable { plugins = (Scratch.Services.Interface) object; plugins.hook_window.connect ((w) => { - if (window != null) + if (window != null) { return; + } window = w; - key_controller = new Gtk.EventControllerKey (window); + key_controller = new Gtk.EventControllerKey (window) { + propagation_phase = BUBBLE + }; key_controller.key_pressed.connect (on_window_key_press_event); + + fuzzy_menuitem = new Gtk.MenuItem.with_label (_("Find Project Files")); + var child = ((Gtk.Bin)fuzzy_menuitem).get_child (); + if (child is Gtk.AccelLabel) { + ((Gtk.AccelLabel)child).set_accel (ACCEL_KEY, ACCEL_MODTYPE); + } + + fuzzy_menuitem.activate.connect (fuzzy_find); + fuzzy_menuitem.show (); + window.sidebar.project_menu.append (fuzzy_menuitem); }); } bool on_window_key_press_event (uint keyval, uint keycode, Gdk.ModifierType state) { /* f shows fuzzy search dialog */ switch (Gdk.keyval_to_upper (keyval)) { - case Gdk.Key.F: - if (state == Gdk.ModifierType.MOD1_MASK) { - var settings = new GLib.Settings ("io.elementary.code.folder-manager"); + case ACCEL_KEY: + if (state == ACCEL_MODTYPE) { + fuzzy_find (); + return true; + } - string[] opened_folders = settings.get_strv ("opened-folders"); - if (opened_folders == null || opened_folders.length < 1) { - return false; - } + break; + default: + return false; + } - project_paths = new Gee.HashMap (); + return false; + } - foreach (unowned string path in settings.get_strv ("opened-folders")) { - var monitor = Services.GitManager.get_monitored_repository (path); - var project_path = new Services.SearchProject (path, monitor); - project_path.parse_async.begin (path, (obj, res) => { - project_path.parse_async.end (res); - }); + private void fuzzy_find () { + var settings = new GLib.Settings ("io.elementary.code.folder-manager"); - project_paths[path] = project_path; - } + string[] opened_folders = settings.get_strv ("opened-folders"); + if (opened_folders == null || opened_folders.length < 1) { + return; + } - var popover = new Scratch.FuzzySearchPopover (project_paths, window); - popover.open_file.connect ((filepath) => { - var file = new Scratch.FolderManager.File (filepath); - var doc = new Scratch.Services.Document (window.actions, file.file); + project_paths = new Gee.HashMap (); - window.open_document (doc); - popover.popdown (); - }); + foreach (unowned string path in settings.get_strv ("opened-folders")) { + var monitor = Services.GitManager.get_monitored_repository (path); + var project_path = new Services.SearchProject (path, monitor); + project_path.parse_async.begin (path, (obj, res) => { + project_path.parse_async.end (res); + }); - popover.close_search.connect (() => popover.popdown ()); - popover.popup (); + project_paths[path] = project_path; + } - return true; - } + var popover = new Scratch.FuzzySearchPopover (project_paths, window); + popover.open_file.connect ((filepath) => { + var file = new Scratch.FolderManager.File (filepath); + var doc = new Scratch.Services.Document (window.actions, file.file); - break; - default: - return false; - } + window.open_document (doc); + popover.popdown (); + }); - return false; + popover.close_search.connect (() => popover.popdown ()); + popover.popup (); } public void deactivate () { key_controller.key_pressed.disconnect (on_window_key_press_event); + window.sidebar.project_menu.remove (fuzzy_menuitem); } } diff --git a/plugins/fuzzy-search/meson.build b/plugins/fuzzy-search/meson.build index 155ce60c7f..a4168ca408 100644 --- a/plugins/fuzzy-search/meson.build +++ b/plugins/fuzzy-search/meson.build @@ -6,6 +6,7 @@ module_files = [ 'file-item.vala', 'fuzzy-search-popover.vala', 'search-result.vala', + 'fuzzy-search-project.vala' ] module_deps = [ diff --git a/src/Widgets/Sidebar.vala b/src/Widgets/Sidebar.vala index 578acef4fb..4b4102a035 100644 --- a/src/Widgets/Sidebar.vala +++ b/src/Widgets/Sidebar.vala @@ -25,6 +25,7 @@ public class Code.Sidebar : Gtk.Grid { public Gtk.Stack stack { get; private set; } public Code.ChooseProjectButton choose_project_button { get; private set; } public Hdy.HeaderBar headerbar { get; private set; } + public Gtk.Menu project_menu { get; construct; } private Gtk.StackSwitcher stack_switcher; @@ -70,7 +71,7 @@ public class Code.Sidebar : Gtk.Grid { order_projects_menu_item.action_name = Scratch.MainWindow.ACTION_PREFIX + Scratch.MainWindow.ACTION_ORDER_FOLDERS; - var project_menu = new Gtk.Menu (); + project_menu = new Gtk.Menu (); project_menu.append (collapse_all_menu_item); project_menu.append (order_projects_menu_item); project_menu.show_all ();