about summary refs log tree commit diff stats
path: root/pkgs/by-name/ts/tskm/src/interface
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/by-name/ts/tskm/src/interface')
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs51
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/neorg/mod.rs9
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/open/handle.rs115
3 files changed, 117 insertions, 58 deletions
diff --git a/pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs b/pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs
index a9a46ee7..d904b12e 100644
--- a/pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs
@@ -1,33 +1,46 @@
 use std::{
     env,
-    fs::{self, read_to_string, OpenOptions},
+    fs::{self, read_to_string, File, OpenOptions},
     io::Write,
     process::Command,
 };
 
-use anyhow::{bail, Result};
+use anyhow::{bail, Context, Result};
 
-use crate::cli::NeorgCommand;
+use crate::{cli::NeorgCommand, state::State};
 
-pub fn handle(command: NeorgCommand) -> Result<()> {
+pub fn handle(command: NeorgCommand, state: &mut State) -> Result<()> {
     match command {
         NeorgCommand::Task { id } => {
-            let project = id.project()?;
-            let path = dirs::data_local_dir()
+            let project = id.project(state)?;
+            let base = dirs::data_local_dir()
                 .expect("This should exists")
-                .join("notes")
-                .join(project.get_neorg_path()?);
+                .join("tskm/notes");
+            let path = base.join(project.get_neorg_path()?);
 
             fs::create_dir_all(path.parent().expect("This should exist"))?;
 
             {
-                let contents = read_to_string(&path)?;
-                if contents.contains(format!("% {}", id.to_uuid()?).as_str()) {
+                let contents = if path.exists() {
+                    read_to_string(&path)
+                        .with_context(|| format!("Failed to read file: '{}'", path.display()))?
+                } else {
+                    File::create(&path)
+                        .with_context(|| format!("Failed to create file: '{}'", path.display()))?;
+                    String::new()
+                };
+
+                if !contents.contains(format!("% {}", id.uuid()).as_str()) {
                     let mut options = OpenOptions::new();
-                    options.append(true).create(true);
+                    options.append(true).create(false);
 
                     let mut file = options.open(&path)?;
-                    file.write_all(format!("* TITLE (% {})", id.to_uuid()?).as_bytes())?;
+                    file.write_all(format!("* TITLE (% {})", id.uuid()).as_bytes())
+                        .with_context(|| {
+                            format!("Failed to write task uuid to file: '{}'", path.display())
+                        })?;
+                    file.flush()
+                        .with_context(|| format!("Failed to flush file: '{}'", path.display()))?;
                 }
             }
 
@@ -36,7 +49,7 @@ pub fn handle(command: NeorgCommand) -> Result<()> {
                 .args([
                     path.to_str().expect("Should be a utf-8 str"),
                     "-c",
-                    format!("/% {}", id.to_uuid()?).as_str(),
+                    format!("/% {}", id.uuid()).as_str(),
                 ])
                 .status()?;
             if !status.success() {
@@ -46,7 +59,7 @@ pub fn handle(command: NeorgCommand) -> Result<()> {
             {
                 let status = Command::new("git")
                     .args(["add", "."])
-                    .current_dir(&path)
+                    .current_dir(path.parent().expect("Will exist"))
                     .status()?;
                 if !status.success() {
                     bail!("Git add . failed!");
@@ -56,14 +69,10 @@ pub fn handle(command: NeorgCommand) -> Result<()> {
                     .args([
                         "commit",
                         "--message",
-                        format!(
-                            "chore({}): Update",
-                            path.parent().expect("Should have a parent").display()
-                        )
-                        .as_str(),
+                        format!("chore({}): Update", project.get_neorg_path()?.display()).as_str(),
                         "--no-gpg-sign",
                     ])
-                    .current_dir(&path)
+                    .current_dir(path.parent().expect("Will exist"))
                     .status()?;
                 if !status.success() {
                     bail!("Git commit failed!");
@@ -71,7 +80,7 @@ pub fn handle(command: NeorgCommand) -> Result<()> {
             }
 
             {
-                id.annotate("[neorg data]")?;
+                id.mark_neorg_data(state)?;
             }
         }
     }
diff --git a/pkgs/by-name/ts/tskm/src/interface/neorg/mod.rs b/pkgs/by-name/ts/tskm/src/interface/neorg/mod.rs
index dc5cdf19..51d58ab3 100644
--- a/pkgs/by-name/ts/tskm/src/interface/neorg/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/neorg/mod.rs
@@ -8,11 +8,18 @@ pub mod handle;
 pub use handle::handle;
 
 impl Project {
+    /// Return the stored neorg path of this project.
+    /// The returned path will never start with a slash (/).
     pub(super) fn get_neorg_path(&self) -> Result<PathBuf> {
         let project_path = run_task(&[
             "_get",
             format!("rc.context.{}.rc.neorg_path", self.to_context_display()).as_str(),
         ])?;
-        Ok(PathBuf::from(project_path.as_str()))
+
+        let final_path = project_path
+            .strip_prefix('/')
+            .unwrap_or(project_path.as_str());
+
+        Ok(PathBuf::from(final_path))
     }
 }
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 dc0d165d..4d7341b2 100644
--- a/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
@@ -1,17 +1,23 @@
-use std::process;
+use std::{
+    fs,
+    net::{IpAddr, Ipv4Addr},
+    path::PathBuf,
+    process,
+};
 
 use anyhow::{bail, Context, Result};
-use log::{error, info};
+use log::{error, info, warn};
+use url::Url;
 
-use crate::{cli::OpenCommand, rofi, task};
+use crate::{cli::OpenCommand, rofi, state::State, task};
 
-pub fn handle(command: OpenCommand) -> Result<()> {
+pub fn handle(command: OpenCommand, state: &mut State) -> Result<()> {
     match command {
         OpenCommand::Review => {
             for project in task::Project::all().context("Failed to get all project files")? {
                 if project.is_touched() {
                     info!("Reviewing project: '{}'", project.to_project_display());
-                    open_in_browser(project).with_context(|| {
+                    open_in_browser(project, state, None).with_context(|| {
                         format!(
                             "Failed to open project ('{}') in Firefox",
                             project.to_project_display()
@@ -26,23 +32,13 @@ pub fn handle(command: OpenCommand) -> Result<()> {
                 }
             }
         }
-        OpenCommand::Project { project } => {
-            let project = if let Some(p) = project {
-                p
-            } else if let Some(p) =
-                task::Project::get_current().context("Failed to get currently focused project")?
-            {
-                p
-            } else {
-                bail!("You need to either supply a project or have a project active!");
-            };
-
+        OpenCommand::Project { project, url } => {
             project.touch().context("Failed to touch project")?;
-            open_in_browser(&project).with_context(|| {
+            open_in_browser(&project, state, url).with_context(|| {
                 format!("Failed to open project: {}", project.to_project_display())
             })?;
         }
-        OpenCommand::Select => {
+        OpenCommand::Select { url } => {
             let selected_project: task::Project = task::Project::from_project_string(
                 &rofi::select(
                     task::Project::all()
@@ -60,7 +56,7 @@ pub fn handle(command: OpenCommand) -> Result<()> {
                 .touch()
                 .context("Failed to touch project")?;
 
-            open_in_browser(&selected_project).context("Failed to open project")?;
+            open_in_browser(&selected_project, state, url).context("Failed to open project")?;
         }
         OpenCommand::ListTabs { project } => {
             let project = if let Some(p) = project {
@@ -109,12 +105,15 @@ pub fn handle(command: OpenCommand) -> Result<()> {
     Ok(())
 }
 
-fn open_in_browser(selected_project: &task::Project) -> Result<()> {
+fn open_in_browser(
+    selected_project: &task::Project,
+    state: &mut State,
+    url: Option<Url>,
+) -> Result<()> {
     let old_project: Option<task::Project> =
         task::Project::get_current().context("Failed to get currently active project")?;
-    // We have ensured that only one task may be active
-    let old_task: Option<task::Id> =
-        task::Id::get_current().context("Failed to get currently active task")?;
+    let old_task: Option<task::Task> =
+        task::Task::get_current(state).context("Failed to get currently active task")?;
 
     selected_project.activate().with_context(|| {
         format!(
@@ -124,7 +123,7 @@ fn open_in_browser(selected_project: &task::Project) -> Result<()> {
     })?;
 
     let tracking_task = {
-        let all_tasks = selected_project.get_tasks().with_context(|| {
+        let all_tasks = selected_project.get_tasks(state).with_context(|| {
             format!(
                 "Failed to get assoctiated tasks for project: '{}'",
                 selected_project.to_project_display()
@@ -132,7 +131,7 @@ fn open_in_browser(selected_project: &task::Project) -> Result<()> {
         })?;
 
         let tracking_task = all_tasks.into_iter().find(|t| {
-            let maybe_desc = t.description();
+            let maybe_desc = t.description(state);
             if let Ok(desc) = maybe_desc {
                 desc == "tracking"
             } else {
@@ -149,31 +148,75 @@ fn open_in_browser(selected_project: &task::Project) -> Result<()> {
                 "Starting task {} -> tracking",
                 selected_project.to_project_display()
             );
-            task.start()
+            task.start(state)
                 .with_context(|| format!("Failed to start task {task}"))?;
         }
         tracking_task
     };
 
-    let status = process::Command::new("firefox")
-        .args([
-            "-P",
-            selected_project.to_project_display().as_str(),
-            "about:newtab",
-        ])
-        .status()
-        .context("Failed to start firefox")?;
+    let status = {
+        let mut args = vec!["-P".to_owned(), selected_project.to_project_display()];
+        if let Some(url) = url {
+            args.push(url.to_string());
+        } else {
+            let lock_file = dirs::home_dir()
+                .expect("Exists")
+                .join(".mozilla/firefox")
+                .join(selected_project.to_project_display())
+                .join("lock");
+
+            if lock_file.exists() {
+                let (ip, pid): (IpAddr, u32) = {
+                    let link = fs::read_link(&lock_file).with_context(|| {
+                        format!("Failed to readlink lock at '{}'", lock_file.display())
+                    })?;
+
+                    let (ip, pid) = link
+                        .to_str()
+                        .expect("Should work")
+                        .split_once(':')
+                        .expect("The split works");
+
+                    (
+                        ip.parse().expect("Should be a valid ip address"),
+                        pid.parse().expect("Should be a valid pid"),
+                    )
+                };
+
+                if ip != Ipv4Addr::new(127, 0, 0, 2) {
+                    warn!("Your ip is weird..");
+                }
+
+                if PathBuf::from("/proc").join(pid.to_string()).exists() {
+                    // Another Firefox instance has already been started for this project
+                    // Add a buffer URL to force Firefox to open it in the already open instance
+                    args.push("about:newtab".to_owned());
+                } else {
+                    // This project does not yet have another Firefox instance
+                    // We do not need to add anything to the arguments, Firefox will open a new
+                    // instance.
+                }
+            } else {
+                // There is no lock file and thus no instance already open.
+            }
+        };
+
+        process::Command::new("firefox")
+            .args(args)
+            .status()
+            .context("Failed to start firefox")?
+    };
 
     if !status.success() {
         error!("Firefox run exited with error.");
     }
 
     if let Some(task) = tracking_task {
-        task.stop()
+        task.stop(state)
             .with_context(|| format!("Failed to stop task {task}"))?;
     }
     if let Some(task) = old_task {
-        task.start()
+        task.start(state)
             .with_context(|| format!("Failed to start task {task}"))?;
     }