about summary refs log tree commit diff stats
path: root/pkgs/by-name/ts/tskm/src
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/by-name/ts/tskm/src')
-rw-r--r--pkgs/by-name/ts/tskm/src/browser/mod.rs56
-rw-r--r--pkgs/by-name/ts/tskm/src/cli.rs10
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/input/handle.rs9
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/open/handle.rs22
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/open/mod.rs38
5 files changed, 101 insertions, 34 deletions
diff --git a/pkgs/by-name/ts/tskm/src/browser/mod.rs b/pkgs/by-name/ts/tskm/src/browser/mod.rs
index 8dd52663..2129982f 100644
--- a/pkgs/by-name/ts/tskm/src/browser/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/browser/mod.rs
@@ -1,5 +1,5 @@
 use std::{
-    env,
+    env, fs,
     io::Write,
     os::unix::net::UnixStream,
     path::PathBuf,
@@ -14,11 +14,14 @@ use url::Url;
 use crate::{state::State, task};
 
 #[allow(clippy::too_many_lines)]
-pub fn open_in_browser(
+pub fn open_in_browser<U>(
     selected_project: &task::Project,
     state: &mut State,
-    url: Option<Url>,
-) -> Result<()> {
+    urls: Option<Vec<U>>,
+) -> Result<()>
+where
+    U: Into<Url>,
+{
     let old_project: Option<task::Project> =
         task::Project::get_current().context("Failed to get currently active project")?;
     let old_task: Option<task::Task> =
@@ -101,20 +104,44 @@ pub fn open_in_browser(
             )
         }));
 
-        if ipc_socket_path.exists() {
-            let mut stream = UnixStream::connect(ipc_socket_path)?;
+        let socket = if ipc_socket_path.exists() {
+            match UnixStream::connect(&ipc_socket_path) {
+                Ok(ok) => Some(ok),
+                Err(err) => match err.kind() {
+                    std::io::ErrorKind::ConnectionRefused => {
+                        // There is no qutebrowser listening to our connection.
+                        fs::remove_file(&ipc_socket_path).with_context(|| {
+                            format!(
+                                "Failed to remove orphaned qutebrowser socket: {}",
+                                ipc_socket_path.display()
+                            )
+                        })?;
+                        None
+                    }
+                    _ => Err(err).with_context(|| {
+                        format!(
+                            "Failed to connect to qutebrowser's ipc socket at: {}",
+                            ipc_socket_path.display()
+                        )
+                    })?,
+                },
+            }
+        } else {
+            None
+        };
 
-            let real_url = if let Some(url) = url {
-                url.to_string()
+        if let Some(mut stream) = socket {
+            let real_url = if let Some(urls) = urls {
+                urls.into_iter().map(|url| url.into().to_string()).collect()
             } else {
                 // Always add a new tab, so that qutebrowser is marked as “urgent”.
-                "qute://start".to_owned()
+                vec!["qute://start".to_owned()]
             };
 
             stream.write_all(
                 json! {
                     {
-                        "args": [real_url],
+                        "args": real_url,
                         "target_arg": null,
                         "version": "1.0.4",
                         "protocol_version": 1,
@@ -128,10 +155,13 @@ pub fn open_in_browser(
 
             ExitStatus::default()
         } else {
-            let args = if let Some(url) = url {
-                &[url.to_string()][..]
+            let args = if let Some(urls) = urls {
+                urls.into_iter()
+                    .map(Into::<Url>::into)
+                    .map(|u| u.to_string())
+                    .collect()
             } else {
-                &[][..]
+                vec![]
             };
 
             process::Command::new(format!(
diff --git a/pkgs/by-name/ts/tskm/src/cli.rs b/pkgs/by-name/ts/tskm/src/cli.rs
index 90d6023b..359c1050 100644
--- a/pkgs/by-name/ts/tskm/src/cli.rs
+++ b/pkgs/by-name/ts/tskm/src/cli.rs
@@ -13,11 +13,11 @@ use std::{ffi::OsStr, path::PathBuf};
 use anyhow::{bail, Result};
 use clap::{builder::StyledStr, ArgAction, Parser, Subcommand, ValueEnum};
 use clap_complete::{ArgValueCompleter, CompletionCandidate};
-use url::Url;
 
 use crate::{
     interface::{
         input::{Input, Tag},
+        open::UrlLike,
         project::ProjectName,
     },
     state, task,
@@ -126,8 +126,8 @@ pub enum OpenCommand {
         #[arg(value_parser = task::Project::from_project_string, add = ArgValueCompleter::new(complete_project))]
         project: task::Project,
 
-        /// The URL to open.
-        url: Option<Url>,
+        /// The URLs to open.
+        urls: Option<Vec<UrlLike>>,
     },
 
     /// Open a selected project in it's Qutebrowser profile.
@@ -135,8 +135,8 @@ pub enum OpenCommand {
     /// This will use rofi's dmenu mode to select one project from the list of all registered
     /// projects.
     Select {
-        /// The URL to open.
-        url: Option<Url>,
+        /// The URLs to open.
+        urls: Option<Vec<UrlLike>>,
     },
 
     /// List all open tabs in the project.
diff --git a/pkgs/by-name/ts/tskm/src/interface/input/handle.rs b/pkgs/by-name/ts/tskm/src/interface/input/handle.rs
index 11304633..76eea6dc 100644
--- a/pkgs/by-name/ts/tskm/src/interface/input/handle.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/input/handle.rs
@@ -69,10 +69,11 @@ pub fn handle(command: InputCommand, state: &mut State) -> Result<()> {
             'outer: for all in Input::all()?.chunks(100) {
                 info!("Starting review for the first hundred URLs.");
 
-                for input in all {
-                    info!("-> '{input}'");
-                    open_in_browser(&project, state, Some(input.url.clone()))?;
-                }
+                open_in_browser(
+                    &project,
+                    state,
+                    Some(all.iter().map(|f| f.url.clone()).collect()),
+                )?;
 
                 {
                     use std::io::{stdin, stdout, Write};
diff --git a/pkgs/by-name/ts/tskm/src/interface/open/handle.rs b/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
index ca54b422..3897a63b 100644
--- a/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
@@ -8,6 +8,8 @@
 // You should have received a copy of the License along with this program.
 // If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 
+use std::str::FromStr;
+
 use anyhow::{bail, Context, Result};
 use log::{error, info};
 use url::Url;
@@ -17,7 +19,15 @@ use crate::{browser::open_in_browser, cli::OpenCommand, rofi, state::State, task
 fn is_empty(project: &task::Project) -> Result<bool> {
     let tabs = get_tabs(project)?;
 
-    Ok(tabs.is_empty())
+    if tabs.is_empty() {
+        Ok(true)
+    } else if tabs.len() > 1 {
+        Ok(false)
+    } else {
+        let url = &tabs[0].1;
+
+        Ok(url == &Url::from_str("qute://start/").expect("Hardcoded"))
+    }
 }
 
 #[allow(clippy::too_many_lines)]
@@ -33,7 +43,7 @@ pub fn handle(command: OpenCommand, state: &mut State) -> Result<()> {
                         project.to_project_display(),
                         if is_empty { "is empty" } else { "is not empty" }
                     );
-                    open_in_browser(project, state, None).with_context(|| {
+                    open_in_browser(project, state, None::<Vec<Url>>).with_context(|| {
                         format!(
                             "Failed to open project ('{}') in qutebrowser",
                             project.to_project_display()
@@ -51,13 +61,13 @@ pub fn handle(command: OpenCommand, state: &mut State) -> Result<()> {
                 }
             }
         }
-        OpenCommand::Project { project, url } => {
+        OpenCommand::Project { project, urls } => {
             project.touch().context("Failed to touch project")?;
-            open_in_browser(&project, state, url).with_context(|| {
+            open_in_browser(&project, state, urls).with_context(|| {
                 format!("Failed to open project: {}", project.to_project_display())
             })?;
         }
-        OpenCommand::Select { url } => {
+        OpenCommand::Select { urls } => {
             let selected_project: task::Project = task::Project::from_project_string(
                 &rofi::select(
                     task::Project::all()
@@ -75,7 +85,7 @@ pub fn handle(command: OpenCommand, state: &mut State) -> Result<()> {
                 .touch()
                 .context("Failed to touch project")?;
 
-            open_in_browser(&selected_project, state, url).context("Failed to open project")?;
+            open_in_browser(&selected_project, state, urls).context("Failed to open project")?;
         }
         OpenCommand::ListTabs { projects, mode } => {
             let projects = {
diff --git a/pkgs/by-name/ts/tskm/src/interface/open/mod.rs b/pkgs/by-name/ts/tskm/src/interface/open/mod.rs
index 40e057c1..e403b4a8 100644
--- a/pkgs/by-name/ts/tskm/src/interface/open/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/open/mod.rs
@@ -8,7 +8,11 @@
 // You should have received a copy of the License along with this program.
 // If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 
-use std::{fs::File, io::Read, str::FromStr};
+use std::{
+    fs::{self, File},
+    io::Read,
+    str::FromStr,
+};
 
 use anyhow::{anyhow, Context, Result};
 use taskchampion::chrono::NaiveDateTime;
@@ -20,15 +24,37 @@ use crate::task::Project;
 pub mod handle;
 pub use handle::handle;
 
+/// An Url that also accepts file paths
+#[derive(Debug, Clone)]
+pub struct UrlLike(Url);
+
+impl FromStr for UrlLike {
+    type Err = url::ParseError;
+
+    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+        if let Ok(u) = fs::canonicalize(s) {
+            Ok(Self(Url::from_file_path(u).expect(
+                "The path could be canonicalized, as such it is valid for this",
+            )))
+        } else {
+            Url::from_str(s).map(Self)
+        }
+    }
+}
+
+impl From<UrlLike> for Url {
+    fn from(value: UrlLike) -> Self {
+        value.0
+    }
+}
+
 impl Project {
     pub(super) fn get_sessionstore(&self) -> Result<SessionStore> {
         let path = dirs::data_local_dir()
             .context("Failed to get data dir")?
             .join("qutebrowser")
             .join(self.to_project_display())
-            // NOTE(@bpeetz): We could use another real session name, but this file should
-            // always exist. <2025-06-03>
-            .join("data/sessions/_autosave.yml");
+            .join("data/sessions/default.yml");
 
         let mut file = File::open(&path)
             .with_context(|| format!("Failed to open path '{}'", path.display()))?;
@@ -92,7 +118,7 @@ fn qute_store_from_yaml(yaml: &[Yaml]) -> Result<SessionStore> {
                                         Ok::<_, anyhow::Error>(TabHistory {
                                             active: hash
                                                 .get(&Yaml::String("active".to_owned()))
-                                                .ok_or(anyhow!("Missing tab history active"))?
+                                                .unwrap_or(&Yaml::Boolean(false))
                                                 .as_bool()
                                                 .ok_or(anyhow!("tab history active not bool"))?,
                                             last_visited: NaiveDateTime::from_str(
@@ -126,7 +152,7 @@ fn qute_store_from_yaml(yaml: &[Yaml]) -> Result<SessionStore> {
                                             .context("Failed to parse url")?,
                                             zoom: hash
                                                 .get(&Yaml::String("zoom".to_owned()))
-                                                .ok_or(anyhow!("Missing tab history zoom"))?
+                                                .unwrap_or(&Yaml::Real("1.0".to_owned()))
                                                 .as_f64()
                                                 .ok_or(anyhow!("tab history zoom not 64"))?,
                                         })