markdown-oxide
markdown-oxide copied to clipboard
Navigation of daily notes
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?
- [x] #96
- [x] #99
- [ ] #100
- [ ] #101
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
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, ¶ms)))
+ let settings = self.bind_settings(|settings| Ok(settings.clone())).await?;
+ self.bind_vault(|vault| Ok(workspace_symbol(&settings, vault, ¶ms)))
.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.
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
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).
Cool looks good.
Could you open a PR? Its okay even if you don't have much code yet
Happy to, I'll get one up this weekend
awesome!
- [x] Open lspconfig PR to support commands
re: https://github.com/Feel-ix-343/markdown-oxide/pull/85#issuecomment-2119381574
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
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.