Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Navigation of daily notes #62

Open
2 of 4 tasks
Kxnr opened this issue Apr 25, 2024 · 10 comments
Open
2 of 4 tasks

Navigation of daily notes #62

Kxnr opened this issue Apr 25, 2024 · 10 comments

Comments

@Kxnr
Copy link
Contributor

Kxnr commented Apr 25, 2024

One of my most used operations (as a longtime vimwiki user moving over to Obsidian) is to open a daily note. I really like the ability to use today, tomorrow, etc. to link to the daily note for the corresponding day and would like a corresponding way to open daily notes.

Would it be possible to also add today, tomorrow, etc. as workspace symbols, or otherwise add a command to open these notes?

@Feel-ix-343
Copy link
Owner

Thank you so much for commenting! I've with you on this being important.

The workspace symbols idea would be perfect. However, I don't believe it would be possible to open daily notes that have not been created yet; you want tomorrow but 2024-04-26 doesn't exist for example.

What do you think about a CLI for this? I have been using this little nu-shell script for opening daily notes .. it could be ported

#!/usr/bin/env nu


def main [change: int = 0, --gui] {
    let day = (date now) + ($"($change)day" | into duration)
    let day = ($day | date to-record)
    cd ~/Notes

    let month = if $day.month <  10 {
      "0" + ($day.month | into string)
    } else {
      $day.month | into string
    }

    let day_num = if $day.day < 10 {
      "0" + ($day.day | into string)
    } else {
      $day.day | into string
    }

    if $gui {
      neovide $"($day.year)-($month)-($day_num).md"
    } else {
      nvim $"($day.year)-($month)-($day_num).md"
    }

}

this allows you to prompt today for today, and today +1 for tomorrow ... ; We could also make completions for today, tomorrow, yesterday, ... and the editor it would use would be the one set in $EDITOR

What do you think about this?

There is also the option of making an LSP command and implementing it for every text editor

@Kxnr
Copy link
Contributor Author

Kxnr commented Apr 26, 2024

Thank you so much for commenting!

Of course! This is a cool idea for a project and right up my alley. Not an expert in LSP at all, but learning more about how useful it can be.

However, I don't believe it would be possible to open daily notes that have not been created yet

This might be editor dependent; a proof of concept seems like the non-existent file may be created for helix and nvim, at least, though I don't see anything in the spec that says it has to be valid.

proof of concept diff:

diff --git a/src/main.rs b/src/main.rs
index d73567c..8b3fa6c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -291,9 +291,10 @@ impl Backend {
 #[tower_lsp::async_trait]
 impl LanguageServer for Backend {
     async fn initialize(&self, i: InitializeParams) -> Result<InitializeResult> {
-
         let root_dir = match i.root_uri {
-            Some(uri) => uri.to_file_path().or(Err(Error::new(ErrorCode::InvalidParams)))?,
+            Some(uri) => uri
+                .to_file_path()
+                .or(Err(Error::new(ErrorCode::InvalidParams)))?,
             None => std::env::current_dir().or(Err(Error::new(ErrorCode::InvalidParams)))?,
         };
 
@@ -612,7 +613,8 @@ impl LanguageServer for Backend {
         &self,
         params: WorkspaceSymbolParams,
     ) -> Result<Option<Vec<SymbolInformation>>> {
-        self.bind_vault(|vault| Ok(workspace_symbol(vault, &params)))
+        let settings = self.bind_settings(|settings| Ok(settings.clone())).await?;
+        self.bind_vault(|vault| Ok(workspace_symbol(&settings, vault, &params)))
             .await
     }
 
diff --git a/src/symbol.rs b/src/symbol.rs
index ce3fbd5..09c8b2a 100644
--- a/src/symbol.rs
+++ b/src/symbol.rs
@@ -1,3 +1,4 @@
+use chrono::{Duration, NaiveDate};
 use std::{iter, path::Path};
 
 use itertools::Itertools;
@@ -6,14 +7,18 @@ use tower_lsp::lsp_types::{
     SymbolKind, Url, WorkspaceSymbolParams,
 };
 
-use crate::vault::{MDHeading, Referenceable, Vault};
+use crate::{
+    config::Settings,
+    vault::{MDHeading, Referenceable, Vault},
+};

 pub fn workspace_symbol(
+    settings: &Settings,
     vault: &Vault,
     _params: &WorkspaceSymbolParams,
 ) -> Option<Vec<SymbolInformation>> {
     let referenceables = vault.select_referenceable_nodes(None);
-    let symbol_informations = referenceables
+    let mut symbol_informations = referenceables
         .into_iter()
         .flat_map(|referenceable| {
             let range = match referenceable {
@@ -48,6 +53,62 @@ pub fn workspace_symbol(
         })
         .collect_vec();
 
+    fn date_to_filename(settings: &Settings, date: NaiveDate) -> String {
+        date.format(settings.dailynote.as_str()).to_string()
+    }
+
+    fn relative_date_string(date: NaiveDate) -> Option<String> {
+        let today = chrono::Local::now().date_naive();
+
+        if today == date {
+            Some("today".to_string())
+        } else {
+            match (date - today).num_days() {
+                1 => Some("tomorrow".to_string()),
+                2..=7 => Some(format!("next {}", date.format("%A"))),
+                -1 => Some("yesterday".to_string()),
+                -7..=-1 => Some(format!("last {}", date.format("%A"))),
+                _ => None,
+            }
+        }
+    }
+
+    fn date_to_match_string(settings: &Settings, date: NaiveDate) -> Option<String> {
+        let refname = date_to_filename(settings, date);
+        format!("{}: {}", relative_date_string(date)?, refname).into()
+    }
+
+    let today = chrono::Local::now().date_naive();
+    let days = (-7..=7)
+        .flat_map(|i| Some(today + Duration::try_days(i)?))
+        // .flat_map(|date| relative_date_string(date))
+        // TODO: this filters out duplicates, which may not actually be desirable here?
+        // .filter(|date| !refnames.contains(&date.ref_name))
+        // TODO: collect Symbol information here
+        .filter_map(|date| {
+            Some(SymbolInformation {
+                name: date_to_match_string(settings, date)?,
+                kind: SymbolKind::FILE,
+                location: Location {
+                    uri: Url::from_file_path(date_to_filename(settings, date)).ok()?,
+                    range: tower_lsp::lsp_types::Range {
+                        start: tower_lsp::lsp_types::Position {
+                            line: 0,
+                            character: 0,
+                        },
+                        end: tower_lsp::lsp_types::Position {
+                            line: 0,
+                            character: 1,
+                        },
+                    },
+                },
+                container_name: None,
+                tags: None,
+                deprecated: None,
+            })
+        });
+
+    symbol_informations.extend(days);
     Some(symbol_informations)
 }

What do you think about a CLI for this?

It's not something I'd expect to be in scope for this project, and so not something I'd ask to be included here. The existing CLI tools and editor plugins for managing wikis of various flavors are pretty reasonable. I'd likely stick with one of those if there isn't a good way to accomplish some of the wiki management through an LSP.

Sadly, it seems like workspace/executeCommand might not be super broadly supported by editors--I may give this a go on a fork and see if I can get a reasonable set of commands working.

@Feel-ix-343
Copy link
Owner

Hey

That snippet looks good. I just dont know how the symbols will work for locations of files that to not yet exist.

It would be perfect if the file is created, but I expect the editor would throw an error

@Kxnr
Copy link
Contributor Author

Kxnr commented May 3, 2024

At least helix and neovim open non-existent files as empty buffers, though they may be swallowing an error internally.

I've started to prototype out daily note functionality as lsp commands which I'd be happy to contribute back once I have something decent. The commands should be able to be a lot better anyways (to have next note/prev note, for example, not just specific days).

@Feel-ix-343
Copy link
Owner

Cool looks good.

Could you open a PR? Its okay even if you don't have much code yet

@Kxnr
Copy link
Contributor Author

Kxnr commented May 10, 2024

Happy to, I'll get one up this weekend

@Feel-ix-343
Copy link
Owner

awesome!

@Feel-ix-343
Copy link
Owner

Feel-ix-343 commented May 19, 2024

  • Open lspconfig PR to support commands

@Feel-ix-343 Feel-ix-343 changed the title Workspace symbols for daily notes Navigation of daily notes May 20, 2024
@Kxnr
Copy link
Contributor Author

Kxnr commented May 20, 2024

re: #85 (comment)

A plugin isn't super valuable for me, I've just wrapped lua vim.lsp.buf.execute_command({command="jump", arguments={...}}) into a lua function that uses today as a default argument which is perfect for me, though I'm sure there are folks who would prefer a plugin. I'll take a look at the issue for the plugin and see if there's somewhere I can help out

@Feel-ix-343
Copy link
Owner

Yea that makes total sense. I am on the same page as you.

I think it would be valuable for beginners to have a default setup for lsp capabilities (file watcher), code lens, and commands - things that nvim-lspconfig cannot do. It would also enable some advanced features.

The issue is here #95; It would be awesome to collaborate more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants