Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/fuzzy-search/fuzzy-search-popover.vala
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down
87 changes: 87 additions & 0 deletions plugins/fuzzy-search/fuzzy-search-project.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2023 elementary, Inc. <https://elementary.io>
*
* Authored by: Marvin Ahlgrimm
*/

public class Scratch.Services.SearchProject {
public string root_path { get; private set; }
public Gee.ArrayList<string> 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<string> ();
}

public async void parse_async (string path) {
new Thread<void> (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;
}
}
}
4 changes: 2 additions & 2 deletions plugins/fuzzy-search/fuzzy-search.plugin
Original file line number Diff line number Diff line change
Expand Up @@ -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 <colinkiama@gmail.com>
Copyright=Copyright © 2021 Marvin Ahlgrimm
169 changes: 55 additions & 114 deletions plugins/fuzzy-search/fuzzy-search.vala
Original file line number Diff line number Diff line change
Expand Up @@ -5,94 +5,18 @@
* Authored by: Marvin Ahlgrimm
*/

public class Scratch.Services.SearchProject {
public string root_path { get; private set; }
public Gee.ArrayList<string> 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<string> ();
}

public async void parse_async (string path) {
new Thread<void> (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<string, Services.SearchProject> 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 () {

Expand All @@ -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) {
/* <Alt>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<string, Services.SearchProject> ();
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<string, Services.SearchProject> ();

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);
}
}

Expand Down
1 change: 1 addition & 0 deletions plugins/fuzzy-search/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module_files = [
'file-item.vala',
'fuzzy-search-popover.vala',
'search-result.vala',
'fuzzy-search-project.vala'
]

module_deps = [
Expand Down
3 changes: 2 additions & 1 deletion src/Widgets/Sidebar.vala
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 ();
Expand Down