Compare commits
No commits in common. "main" and "v0.2.0" have entirely different histories.
@ -6,11 +6,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
## [0.3.0] - 2024-06-30
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- add markdown editing mode
|
|
||||||
|
|
||||||
## [0.2.0] - 2024-05-25
|
## [0.2.0] - 2024-05-25
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
920
Cargo.lock
generated
920
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -19,11 +19,8 @@ serde = { version = "1.0.202", features = ["derive"] }
|
|||||||
serde_json = "1.0.117"
|
serde_json = "1.0.117"
|
||||||
itertools = "0.13.0"
|
itertools = "0.13.0"
|
||||||
uuid = { version = "1.8.0", features = ["v4"] }
|
uuid = { version = "1.8.0", features = ["v4"] }
|
||||||
tonic = { version = "0.12.0", features = ["tls", "tls-roots"] }
|
tonic = { version = "0.11.0", features = ["tls", "tls-roots"] }
|
||||||
futures = { version = "0.3.30" }
|
futures = { version = "0.3.30" }
|
||||||
sha2 = { version = "0.10.8" }
|
|
||||||
hex = { version = "0.4.3" }
|
|
||||||
toml = { version = "0.8.14" }
|
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.3.0"
|
version = "0.2.0"
|
||||||
|
4
buf.yaml
4
buf.yaml
@ -1,4 +0,0 @@
|
|||||||
version: v2
|
|
||||||
modules:
|
|
||||||
- path: crates/hyperlog-protos/proto
|
|
||||||
name: buf.build/noschemaplz/hyperlog
|
|
@ -6,7 +6,7 @@ version.workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
tonic.workspace = true
|
tonic.workspace = true
|
||||||
|
|
||||||
prost = "0.13.0"
|
prost = "0.12.6"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build = "0.12.0"
|
tonic-build = "0.11.0"
|
||||||
|
@ -16,8 +16,8 @@ serde_json.workspace = true
|
|||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
tonic.workspace = true
|
tonic.workspace = true
|
||||||
|
|
||||||
tower-http = { version = "0.6.0", features = ["cors", "trace"] }
|
tower-http = { version = "0.5.2", features = ["cors", "trace"] }
|
||||||
sqlx = { version = "0.8.0", features = [
|
sqlx = { version = "0.7.4", features = [
|
||||||
"runtime-tokio",
|
"runtime-tokio",
|
||||||
"tls-rustls",
|
"tls-rustls",
|
||||||
"postgres",
|
"postgres",
|
||||||
|
@ -17,13 +17,9 @@ serde_json.workspace = true
|
|||||||
itertools.workspace = true
|
itertools.workspace = true
|
||||||
tonic.workspace = true
|
tonic.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
sha2.workspace = true
|
|
||||||
uuid.workspace = true
|
|
||||||
hex.workspace = true
|
|
||||||
toml.workspace = true
|
|
||||||
|
|
||||||
ratatui = "0.29.0"
|
ratatui = "0.26.2"
|
||||||
crossterm = { version = "0.28.0", features = ["event-stream"] }
|
crossterm = { version = "0.27.0", features = ["event-stream"] }
|
||||||
directories = "5.0.1"
|
directories = "5.0.1"
|
||||||
human-panic = "2.0.0"
|
human-panic = "2.0.0"
|
||||||
ropey = "1.6.1"
|
ropey = "1.6.1"
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use hyperlog_core::log::GraphItem;
|
use hyperlog_core::log::GraphItem;
|
||||||
use itertools::Itertools;
|
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widgets::{Block, Borders, Padding, Paragraph},
|
widgets::{Block, Borders, Padding, Paragraph},
|
||||||
@ -7,9 +6,8 @@ use ratatui::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
command_parser::CommandParser,
|
command_parser::CommandParser,
|
||||||
commands::{batch::BatchCommand, update_item::UpdateItemCommandExt, Command, IntoCommand},
|
commands::{batch::BatchCommand, IntoCommand},
|
||||||
components::graph_explorer::GraphExplorer,
|
components::graph_explorer::GraphExplorer,
|
||||||
editor,
|
|
||||||
models::IOEvent,
|
models::IOEvent,
|
||||||
state::SharedState,
|
state::SharedState,
|
||||||
Msg,
|
Msg,
|
||||||
@ -104,11 +102,6 @@ impl<'a> App<'a> {
|
|||||||
Msg::MoveUp => self.graph_explorer.move_up()?,
|
Msg::MoveUp => self.graph_explorer.move_up()?,
|
||||||
Msg::OpenCreateItemDialog => self.open_dialog(),
|
Msg::OpenCreateItemDialog => self.open_dialog(),
|
||||||
Msg::OpenCreateItemDialogBelow => self.open_dialog_below(),
|
Msg::OpenCreateItemDialogBelow => self.open_dialog_below(),
|
||||||
Msg::OpenEditor { item } => {
|
|
||||||
if let Some(cmd) = self.open_editor(item) {
|
|
||||||
batch.with(cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Msg::OpenEditItemDialog { item } => self.open_edit_item_dialog(item),
|
Msg::OpenEditItemDialog { item } => self.open_edit_item_dialog(item),
|
||||||
Msg::EnterInsertMode => self.mode = Mode::Insert,
|
Msg::EnterInsertMode => self.mode = Mode::Insert,
|
||||||
Msg::EnterViewMode => self.mode = Mode::View,
|
Msg::EnterViewMode => self.mode = Mode::View,
|
||||||
@ -219,42 +212,6 @@ impl<'a> App<'a> {
|
|||||||
self.mode = Mode::Insert;
|
self.mode = Mode::Insert;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_editor(&self, item: &GraphItem) -> Option<Command> {
|
|
||||||
match editor::EditorSession::new(item).execute() {
|
|
||||||
Ok(None) => {
|
|
||||||
tracing::info!("editor returned without changes, skipping");
|
|
||||||
}
|
|
||||||
Ok(Some(item)) => {
|
|
||||||
if let GraphItem::Item {
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
state,
|
|
||||||
} = item
|
|
||||||
{
|
|
||||||
return Some(
|
|
||||||
self.state.update_item_command().command(
|
|
||||||
&self.root,
|
|
||||||
&self
|
|
||||||
.graph_explorer
|
|
||||||
.get_current_path()
|
|
||||||
.iter()
|
|
||||||
.map(|s| s.as_str())
|
|
||||||
.collect_vec(),
|
|
||||||
&title,
|
|
||||||
&description,
|
|
||||||
state,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("failed to run editor with: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Widget for &mut App<'a> {
|
impl<'a> Widget for &mut App<'a> {
|
||||||
|
@ -9,7 +9,6 @@ pub enum Commands {
|
|||||||
CreateItem { name: String },
|
CreateItem { name: String },
|
||||||
CreateBelow { name: String },
|
CreateBelow { name: String },
|
||||||
Edit,
|
Edit,
|
||||||
Open,
|
|
||||||
|
|
||||||
ShowAll,
|
ShowAll,
|
||||||
HideDone,
|
HideDone,
|
||||||
@ -53,7 +52,6 @@ impl CommandParser {
|
|||||||
"show-all" => Some(Commands::ShowAll),
|
"show-all" => Some(Commands::ShowAll),
|
||||||
"hide-done" => Some(Commands::HideDone),
|
"hide-done" => Some(Commands::HideDone),
|
||||||
"test" => Some(Commands::Test),
|
"test" => Some(Commands::Test),
|
||||||
"o" | "open" => Some(Commands::Open),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
None => None,
|
None => None,
|
||||||
|
@ -5,7 +5,6 @@ pub mod batch;
|
|||||||
pub mod archive;
|
pub mod archive;
|
||||||
pub mod create_item;
|
pub mod create_item;
|
||||||
pub mod create_section;
|
pub mod create_section;
|
||||||
pub mod open_item;
|
|
||||||
pub mod open_update_item_dialog;
|
pub mod open_update_item_dialog;
|
||||||
pub mod toggle_item;
|
pub mod toggle_item;
|
||||||
pub mod update_graph;
|
pub mod update_graph;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use hyperlog_core::log::ItemState;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
models::{IOEvent, Msg},
|
|
||||||
querier::Querier,
|
|
||||||
state::SharedState,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct OpenItemCommand {
|
|
||||||
querier: Querier,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OpenItemCommand {
|
|
||||||
pub fn new(querier: Querier) -> Self {
|
|
||||||
Self { querier }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn command(self, root: &str, path: Vec<String>) -> super::Command {
|
|
||||||
let root = root.to_string();
|
|
||||||
|
|
||||||
super::Command::new(|dispatch| {
|
|
||||||
tokio::spawn(async move {
|
|
||||||
dispatch.send(Msg::OpenItem(IOEvent::Initialized));
|
|
||||||
|
|
||||||
let item = match self.querier.get_async(&root, path).await {
|
|
||||||
Ok(item) => match item {
|
|
||||||
Some(item) => {
|
|
||||||
dispatch.send(Msg::OpenItem(IOEvent::Success(())));
|
|
||||||
item
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
dispatch.send(Msg::OpenItem(IOEvent::Failure(
|
|
||||||
"failed to find a valid item for path".into(),
|
|
||||||
)));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
dispatch.send(Msg::OpenItem(IOEvent::Failure(e.to_string())));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
dispatch.send(Msg::OpenEditor { item });
|
|
||||||
});
|
|
||||||
None
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait OpenItemCommandExt {
|
|
||||||
fn open_item_command(&self) -> OpenItemCommand;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OpenItemCommandExt for SharedState {
|
|
||||||
fn open_item_command(&self) -> OpenItemCommand {
|
|
||||||
OpenItemCommand::new(self.querier.clone())
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
command_parser::Commands,
|
command_parser::Commands,
|
||||||
commands::{
|
commands::{
|
||||||
archive::ArchiveCommandExt, batch::BatchCommand, create_item::CreateItemCommandExt,
|
archive::ArchiveCommandExt, batch::BatchCommand, create_item::CreateItemCommandExt,
|
||||||
create_section::CreateSectionCommandExt, open_item::OpenItemCommandExt,
|
create_section::CreateSectionCommandExt,
|
||||||
open_update_item_dialog::OpenUpdateItemDialogCommandExt, toggle_item::ToggleItemCommandExt,
|
open_update_item_dialog::OpenUpdateItemDialogCommandExt, toggle_item::ToggleItemCommandExt,
|
||||||
update_graph::UpdateGraphCommandExt, Command, IntoCommand,
|
update_graph::UpdateGraphCommandExt, Command, IntoCommand,
|
||||||
},
|
},
|
||||||
@ -144,7 +144,7 @@ impl<'a> GraphExplorer<'a> {
|
|||||||
/// Choses: 0.1.0.0 else nothing
|
/// Choses: 0.1.0.0 else nothing
|
||||||
pub(crate) fn move_right(&mut self) -> Result<()> {
|
pub(crate) fn move_right(&mut self) -> Result<()> {
|
||||||
if let Some(graph) = self.linearize_graph() {
|
if let Some(graph) = self.linearize_graph() {
|
||||||
tracing::trace!("graph: {:?}", graph);
|
tracing::debug!("graph: {:?}", graph);
|
||||||
let position_items = &self.inner.current_position;
|
let position_items = &self.inner.current_position;
|
||||||
|
|
||||||
if let Some(next_item) = graph.next_right(position_items) {
|
if let Some(next_item) = graph.next_right(position_items) {
|
||||||
@ -338,15 +338,6 @@ impl<'a> GraphExplorer<'a> {
|
|||||||
None
|
None
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
Commands::Open => {
|
|
||||||
if self.get_current_item().is_some() {
|
|
||||||
batch.with(
|
|
||||||
self.state
|
|
||||||
.open_item_command()
|
|
||||||
.command(&self.inner.root, self.get_current_path()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -392,6 +383,7 @@ impl<'a> StatefulWidget for GraphExplorer<'a> {
|
|||||||
|
|
||||||
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||||
let Rect { height, .. } = area;
|
let Rect { height, .. } = area;
|
||||||
|
let _height = height as usize;
|
||||||
|
|
||||||
if let Some(graph) = &state.graph {
|
if let Some(graph) = &state.graph {
|
||||||
let movement_graph: MovementGraph =
|
let movement_graph: MovementGraph =
|
||||||
|
@ -1,260 +0,0 @@
|
|||||||
use std::{
|
|
||||||
io::{Read, Write},
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
time::SystemTime,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Context};
|
|
||||||
use hyperlog_core::log::{GraphItem, ItemState};
|
|
||||||
use itertools::Itertools;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use sha2::Digest;
|
|
||||||
|
|
||||||
use crate::project_dirs::get_project_dir;
|
|
||||||
|
|
||||||
pub struct EditorSession<'a> {
|
|
||||||
item: &'a GraphItem,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct EditorFile {
|
|
||||||
title: String,
|
|
||||||
metadata: Metadata,
|
|
||||||
body: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
struct Metadata {
|
|
||||||
state: ItemState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EditorFile {
|
|
||||||
pub fn serialize(&self) -> anyhow::Result<String> {
|
|
||||||
let metadata =
|
|
||||||
toml::to_string_pretty(&self.metadata).context("failed to serialize metadata")?;
|
|
||||||
|
|
||||||
let frontmatter = format!("+++\n{}+++\n", metadata);
|
|
||||||
|
|
||||||
Ok(format!(
|
|
||||||
"{}\n# {}\n\n{}",
|
|
||||||
frontmatter, self.title, self.body
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&GraphItem> for EditorFile {
|
|
||||||
type Error = anyhow::Error;
|
|
||||||
|
|
||||||
fn try_from(value: &GraphItem) -> Result<Self, Self::Error> {
|
|
||||||
if let GraphItem::Item {
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
state,
|
|
||||||
} = value.clone()
|
|
||||||
{
|
|
||||||
Ok(Self {
|
|
||||||
title,
|
|
||||||
metadata: Metadata { state },
|
|
||||||
body: description,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
anyhow::bail!("can only generate a file based on items")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&str> for EditorFile {
|
|
||||||
type Error = anyhow::Error;
|
|
||||||
|
|
||||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
|
||||||
let value = value.to_string();
|
|
||||||
|
|
||||||
let frontmatter_parts = value.split("+++").filter(|p| !p.is_empty()).collect_vec();
|
|
||||||
let frontmatter_content = frontmatter_parts
|
|
||||||
.first()
|
|
||||||
.ok_or(anyhow::anyhow!("no front matter parts were found"))?;
|
|
||||||
|
|
||||||
tracing::trace!("parsing frontmatter content: {}", frontmatter_content);
|
|
||||||
let metadata: Metadata = toml::from_str(frontmatter_content)?;
|
|
||||||
|
|
||||||
let line_parts = value.split("\n");
|
|
||||||
|
|
||||||
let title = line_parts
|
|
||||||
.clone()
|
|
||||||
.find(|p| p.starts_with("# "))
|
|
||||||
.map(|t| t.trim_start_matches("# "))
|
|
||||||
.ok_or(anyhow!("an editor file requires a title with heading 1"))?;
|
|
||||||
let body = line_parts
|
|
||||||
.skip_while(|p| !p.starts_with("# "))
|
|
||||||
.skip(1)
|
|
||||||
.skip_while(|p| p.is_empty())
|
|
||||||
.collect_vec()
|
|
||||||
.join("\n");
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
title: title.to_string(),
|
|
||||||
metadata,
|
|
||||||
body,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<EditorFile> for GraphItem {
|
|
||||||
fn from(value: EditorFile) -> Self {
|
|
||||||
Self::Item {
|
|
||||||
title: value.title,
|
|
||||||
description: value.body,
|
|
||||||
state: value.metadata.state,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SessionFile {
|
|
||||||
path: PathBuf,
|
|
||||||
loaded: SystemTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SessionFile {
|
|
||||||
pub fn get_path(&self) -> &Path {
|
|
||||||
self.path.as_path()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_changed(&self) -> anyhow::Result<bool> {
|
|
||||||
let modified = self.path.metadata()?.modified()?;
|
|
||||||
|
|
||||||
Ok(self.loaded < modified)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for SessionFile {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if self.path.exists() {
|
|
||||||
tracing::debug!("cleaning up file: {}", self.path.display());
|
|
||||||
|
|
||||||
if let Err(e) = std::fs::remove_file(&self.path) {
|
|
||||||
tracing::error!(
|
|
||||||
"failed to cleanup file: {}, error: {}",
|
|
||||||
self.path.display(),
|
|
||||||
e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> EditorSession<'a> {
|
|
||||||
pub fn new(item: &'a GraphItem) -> Self {
|
|
||||||
Self { item }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_file_path(&mut self) -> anyhow::Result<PathBuf> {
|
|
||||||
let name = self
|
|
||||||
.item
|
|
||||||
.get_digest()
|
|
||||||
.ok_or(anyhow::anyhow!("item doesn't have a title"))?;
|
|
||||||
|
|
||||||
let file_path = get_project_dir()
|
|
||||||
.data_dir()
|
|
||||||
.join("edit")
|
|
||||||
.join(format!("{name}.md"));
|
|
||||||
|
|
||||||
Ok(file_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prepare_file(&mut self) -> anyhow::Result<SessionFile> {
|
|
||||||
let file_path = self.get_file_path()?;
|
|
||||||
|
|
||||||
if let Some(parent) = file_path.parent() {
|
|
||||||
tracing::debug!("creating parent dir: {}", parent.display());
|
|
||||||
std::fs::create_dir_all(parent).context("failed to create dir for edit file")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut file =
|
|
||||||
std::fs::File::create(&file_path).context("failed to create file for edit file")?;
|
|
||||||
|
|
||||||
tracing::debug!("writing contents to file: {}", file_path.display());
|
|
||||||
let editor_file = EditorFile::try_from(self.item)?;
|
|
||||||
file.write_all(
|
|
||||||
editor_file
|
|
||||||
.serialize()
|
|
||||||
.context("failed to serialize item to file")?
|
|
||||||
.as_bytes(),
|
|
||||||
)
|
|
||||||
.context("failed to write to file")?;
|
|
||||||
|
|
||||||
let modified_time = file.metadata()?.modified()?;
|
|
||||||
|
|
||||||
Ok(SessionFile {
|
|
||||||
path: file_path,
|
|
||||||
loaded: modified_time,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_item_from_file(&self, session_file: SessionFile) -> anyhow::Result<GraphItem> {
|
|
||||||
let mut file = std::fs::File::open(&session_file.path)?;
|
|
||||||
|
|
||||||
let mut content = String::new();
|
|
||||||
file.read_to_string(&mut content)?;
|
|
||||||
|
|
||||||
let editor_file = EditorFile::try_from(content.as_str())?;
|
|
||||||
|
|
||||||
Ok(editor_file.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute(&mut self) -> anyhow::Result<Option<GraphItem>> {
|
|
||||||
let editor = std::env::var("EDITOR").context("no editor was found for EDITOR env var")?;
|
|
||||||
let session_file = self.prepare_file()?;
|
|
||||||
|
|
||||||
tracing::debug!(
|
|
||||||
"opening editor: {} at path: {}",
|
|
||||||
editor,
|
|
||||||
session_file.get_path().display()
|
|
||||||
);
|
|
||||||
if let Err(e) = std::process::Command::new(editor)
|
|
||||||
.arg(session_file.get_path())
|
|
||||||
.status()
|
|
||||||
{
|
|
||||||
tracing::error!("failed command with: {}", e);
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
tracing::debug!(
|
|
||||||
"returning from editor, checking file: {}",
|
|
||||||
session_file.get_path().display()
|
|
||||||
);
|
|
||||||
if session_file.is_changed()? {
|
|
||||||
tracing::debug!(
|
|
||||||
"file: {} changed, updating item",
|
|
||||||
session_file.get_path().display()
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(Some(self.get_item_from_file(session_file)?))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait ItemExt {
|
|
||||||
fn get_digest(&self) -> Option<String>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ItemExt for &'a GraphItem {
|
|
||||||
fn get_digest(&self) -> Option<String> {
|
|
||||||
if let GraphItem::Item { title, .. } = self {
|
|
||||||
let digest = sha2::Sha256::digest(title.as_bytes());
|
|
||||||
let digest_hex = hex::encode(digest);
|
|
||||||
|
|
||||||
Some(format!(
|
|
||||||
"{}_{}",
|
|
||||||
title
|
|
||||||
.chars()
|
|
||||||
.filter(|c| c.is_ascii_alphanumeric())
|
|
||||||
.take(10)
|
|
||||||
.collect::<String>(),
|
|
||||||
digest_hex.chars().take(10).collect::<String>()
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -33,9 +33,7 @@ mod events;
|
|||||||
mod querier;
|
mod querier;
|
||||||
mod storage;
|
mod storage;
|
||||||
|
|
||||||
mod editor;
|
|
||||||
mod logging;
|
mod logging;
|
||||||
mod project_dirs;
|
|
||||||
mod terminal;
|
mod terminal;
|
||||||
|
|
||||||
pub async fn execute(state: State) -> Result<()> {
|
pub async fn execute(state: State) -> Result<()> {
|
||||||
|
@ -12,7 +12,6 @@ pub enum Msg {
|
|||||||
OpenCreateItemDialog,
|
OpenCreateItemDialog,
|
||||||
OpenCreateItemDialogBelow,
|
OpenCreateItemDialogBelow,
|
||||||
OpenEditItemDialog { item: GraphItem },
|
OpenEditItemDialog { item: GraphItem },
|
||||||
OpenEditor { item: GraphItem },
|
|
||||||
Interact,
|
Interact,
|
||||||
|
|
||||||
EnterInsertMode,
|
EnterInsertMode,
|
||||||
@ -31,8 +30,6 @@ pub enum Msg {
|
|||||||
Archive(IOEvent<()>),
|
Archive(IOEvent<()>),
|
||||||
|
|
||||||
OpenUpdateItemDialog(IOEvent<()>),
|
OpenUpdateItemDialog(IOEvent<()>),
|
||||||
|
|
||||||
OpenItem(IOEvent<()>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
use directories::ProjectDirs;
|
|
||||||
|
|
||||||
pub fn get_project_dir() -> ProjectDirs {
|
|
||||||
ProjectDirs::from("io", "kjuulh", "hyperlog").expect("to be able to get project dirs")
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user