Skip to content
This repository has been archived by the owner on Mar 31, 2024. It is now read-only.

Commit

Permalink
Revert "Always refresh recent projects when searching"
Browse files Browse the repository at this point in the history
Refreshing in the initial search took too long, making GNOME ignore our
results, which had weird effects on the user interface.  Notably, enter-
ing a full term would often show no results, and removing a single
letter then gave the desired result.

This reverts commit ea9c17f and the
changes done in GH-55
  • Loading branch information
swsnr committed Mar 21, 2024
1 parent 55209ee commit 7a9de52
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 58 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ and this project doesn't really care for versioning.

## [Unreleased]

### Added
- Reload interface and `systemctl reload` support (reverts [GH-55]).

### Changed
- Explicitly refresh recent projects (reverts [GH-55]).

## [1.17.0] – 2024-02-12

### Changed
Expand Down
6 changes: 5 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::Registry;

use providers::*;
use reload::*;
use searchprovider::*;

mod config;
mod launch;
mod providers;
mod reload;
mod searchprovider;
mod systemd;

Expand Down Expand Up @@ -104,10 +106,11 @@ fn main() -> Result<()> {
.filter_map(|provider| {
gio::DesktopAppInfo::new(provider.desktop_id).map(|gio_app| {
event!(Level::INFO, "Found app {}", provider.desktop_id);
let search_provider = JetbrainsProductSearchProvider::new(
let mut search_provider = JetbrainsProductSearchProvider::new(
App::from(gio_app),
&provider.config,
);
let _ = search_provider.reload_recent_projects();
(provider.objpath(), search_provider)
})
})
Expand All @@ -126,6 +129,7 @@ fn main() -> Result<()> {
builder.serve_at(path, provider)
},
)?
.serve_at("/", ReloadAll)?
.serve_log_control(LogControl1::new(control))?
.name(BUSNAME)?
.build()
Expand Down
65 changes: 65 additions & 0 deletions src/reload.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright Sebastian Wiesner <[email protected]>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Reload all recent projects across all exposed provider interfaces.

use crate::providers::PROVIDERS;
use crate::searchprovider::JetbrainsProductSearchProvider;
use tracing::{event, instrument, Level};
use zbus::{interface, ObjectServer};

#[derive(Debug)]
pub struct ReloadAll;

#[interface(name = "de.swsnr.searchprovider.ReloadAll")]
impl ReloadAll {
/// Reload all recent projects in all registered search providers..
#[instrument(skip(self, server))]
pub async fn reload_all(
&self,
#[zbus(object_server)] server: &ObjectServer,
) -> zbus::fdo::Result<()> {
event!(
Level::DEBUG,
"Reloading recent projects of all registered search providers"
);
let mut is_failed = false;
for provider in PROVIDERS {
match server
.interface::<_, JetbrainsProductSearchProvider>(provider.objpath())
.await
{
Err(error) => {
event!(
Level::DEBUG,
"Skipping {} ({}): {error}",
provider.label,
provider.desktop_id
);
}
Ok(search_provider_interface) => {
if let Err(error) = search_provider_interface
.get_mut()
.await
.reload_recent_projects()
{
is_failed = true;
let iface = search_provider_interface.get().await;
let app_id = iface.app().id();
event!(Level::ERROR, %app_id, "Failed to reload recent projects of {}: {}", app_id, error);
}
}
}
}
if is_failed {
Err(zbus::fdo::Error::Failed(
"Failed to reload recent projects of some providers".to_string(),
))
} else {
Ok(())
}
}
}
101 changes: 44 additions & 57 deletions src/searchprovider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,33 +268,6 @@ async fn launch_app_in_new_scope(
})
}

/// Calculate how well `recent_projects` matches all of the given `terms`.
///
/// If all terms match the name of the `recent_projects`, the project receives a base score of 10.
/// If all terms match the directory of the `recent_projects`, the project gets scored for each
/// term according to how far right the term appears in the directory, under the assumption that
/// the right most part of a directory path is the most specific.
///
/// All matches are done on the lowercase text, i.e. case insensitve.
fn score_recent_project(recent_project: &JetbrainsRecentProject, terms: &[&str]) -> f64 {
let name = recent_project.name.to_lowercase();
let directory = recent_project.directory.to_lowercase();
terms
.iter()
.try_fold(0.0, |score, term| {
directory
.rfind(&term.to_lowercase())
// We add 1 to avoid returning zero if the term matches right at the beginning.
.map(|index| score + ((index + 1) as f64 / recent_project.directory.len() as f64))
})
.unwrap_or(0.0)
+ if terms.iter().all(|term| name.contains(&term.to_lowercase())) {
10.0
} else {
0.0
}
}

/// A search provider for recent Jetbrains products.
#[derive(Debug)]
pub struct JetbrainsProductSearchProvider {
Expand Down Expand Up @@ -322,31 +295,11 @@ impl JetbrainsProductSearchProvider {
}

/// Reload all recent projects provided by this search provider.
fn reload_recent_projects(&mut self) -> Result<()> {
pub fn reload_recent_projects(&mut self) -> Result<()> {
self.recent_projects = read_recent_projects(self.config, self.app.id())?;
Ok(())
}

/// Find all projects matching the given `terms`.
///
/// Return a list of IDs of matching projects.
fn find_project_ids_by_terms(&self, terms: &[&str]) -> Vec<&str> {
let mut scored_ids = self
.recent_projects
.iter()
.filter_map(|(id, item)| {
let score = score_recent_project(item, terms);
if 0.0 < score {
Some((id.as_ref(), score))
} else {
None
}
})
.collect::<Vec<_>>();
scored_ids.sort_by_key(|(_, score)| -((score * 1000.0) as i64));
scored_ids.into_iter().map(|(id, _)| id).collect()
}

#[instrument(skip(self, connection), fields(app_id = %self.app.id()))]
async fn launch_app_on_default_main_context(
&self,
Expand All @@ -371,6 +324,33 @@ impl JetbrainsProductSearchProvider {
}
}

/// Calculate how well `recent_projects` matches all of the given `terms`.
///
/// If all terms match the name of the `recent_projects`, the project receives a base score of 10.
/// If all terms match the directory of the `recent_projects`, the project gets scored for each
/// term according to how far right the term appears in the directory, under the assumption that
/// the right most part of a directory path is the most specific.
///
/// All matches are done on the lowercase text, i.e. case insensitve.
fn score_recent_project(recent_project: &JetbrainsRecentProject, terms: &[&str]) -> f64 {
let name = recent_project.name.to_lowercase();
let directory = recent_project.directory.to_lowercase();
terms
.iter()
.try_fold(0.0, |score, term| {
directory
.rfind(&term.to_lowercase())
// We add 1 to avoid returning zero if the term matches right at the beginning.
.map(|index| score + ((index + 1) as f64 / recent_project.directory.len() as f64))
})
.unwrap_or(0.0)
+ if terms.iter().all(|term| name.contains(&term.to_lowercase())) {
10.0
} else {
0.0
}
}

/// The DBus interface of the search provider.
///
/// See <https://developer.gnome.org/SearchProvider/> for information.
Expand All @@ -382,15 +362,22 @@ impl JetbrainsProductSearchProvider {
/// and should return an array of result IDs. gnome-shell will call GetResultMetas for (some) of these result
/// IDs to get details about the result that can be be displayed in the result list.
#[instrument(skip(self), fields(app_id = %self.app.id()))]
fn get_initial_result_set(&mut self, terms: Vec<&str>) -> Vec<&str> {
event!(Level::DEBUG, "Reloading recent projects");
if let Err(error) = self.reload_recent_projects() {
// Ignore errors while reloading recent projects, and just resume
// search with what we've got.
event!(Level::ERROR, "Failed to reload recent projects: {}", error);
}
fn get_initial_result_set(&self, terms: Vec<&str>) -> Vec<&str> {
event!(Level::DEBUG, "Searching for {:?}", terms);
let ids = self.find_project_ids_by_terms(&terms);
let mut scored_ids = self
.recent_projects
.iter()
.filter_map(|(id, item)| {
let score = score_recent_project(item, &terms);
if 0.0 < score {
Some((id.as_ref(), score))
} else {
None
}
})
.collect::<Vec<_>>();
scored_ids.sort_by_key(|(_, score)| -((score * 1000.0) as i64));
let ids = scored_ids.into_iter().map(|(id, _)| id).collect();
event!(Level::DEBUG, "Found ids {:?}", ids);
ids
}
Expand All @@ -410,7 +397,7 @@ impl JetbrainsProductSearchProvider {
);
// For simplicity just run the overall search again, and filter out everything not already matched.
let ids = self
.find_project_ids_by_terms(&terms)
.get_initial_result_set(terms)
.into_iter()
.filter(|id| previous_results.contains(id))
.collect();
Expand Down
1 change: 1 addition & 0 deletions systemd/gnome-search-providers-jetbrains.service
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ Description=Jetbrains projects search provider for Gnome shell
Type=dbus
BusName=de.swsnr.searchprovider.Jetbrains
ExecStart=gnome-search-providers-jetbrains
ExecReload=busctl --user call de.swsnr.searchprovider.Jetbrains / de.swsnr.searchprovider.ReloadAll ReloadAll

0 comments on commit 7a9de52

Please sign in to comment.