change history session_id to a systematically generated number (#481)

* change history session_id to a systematically generated number

* added env var, removed comments

* tweak test

* add public function to get the history session id

* switch from reedline commit date to unix_epoch
This commit is contained in:
Darren Schroeder 2022-09-18 17:04:42 -05:00 committed by GitHub
parent 1bf9ad6bc7
commit 3fa87ef1e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 29 additions and 38 deletions

View File

@ -3,8 +3,6 @@ use crate::{
history::SearchFilter, history::SearchFilter,
menu_functions::{parse_selection_char, ParseAction}, menu_functions::{parse_selection_char, ParseAction},
}; };
use crate::result::{ReedlineError, ReedlineErrorVariants};
#[cfg(feature = "external_printer")] #[cfg(feature = "external_printer")]
use { use {
crate::external_printer::ExternalPrinter, crate::external_printer::ExternalPrinter,
@ -25,6 +23,7 @@ use {
}, },
painting::{Painter, PromptLines}, painting::{Painter, PromptLines},
prompt::{PromptEditMode, PromptHistorySearchStatus}, prompt::{PromptEditMode, PromptHistorySearchStatus},
result::{ReedlineError, ReedlineErrorVariants},
utils::text_manipulation, utils::text_manipulation,
EditCommand, ExampleHighlighter, Highlighter, LineBuffer, Menu, MenuEvent, Prompt, EditCommand, ExampleHighlighter, Highlighter, LineBuffer, Menu, MenuEvent, Prompt,
PromptHistorySearch, ReedlineMenu, Signal, UndoBehavior, ValidationResult, Validator, PromptHistorySearch, ReedlineMenu, Signal, UndoBehavior, ValidationResult, Validator,
@ -34,7 +33,9 @@ use {
event::{Event, KeyCode, KeyEvent, KeyModifiers}, event::{Event, KeyCode, KeyEvent, KeyModifiers},
terminal, Result, terminal, Result,
}, },
std::{borrow::Borrow, fs::File, io, io::Write, process::Command, time::Duration}, std::{
borrow::Borrow, fs::File, io, io::Write, process::Command, time::Duration, time::SystemTime,
},
}; };
// The POLL_WAIT is used to specify for how long the POLL should wait for // The POLL_WAIT is used to specify for how long the POLL should wait for
@ -155,6 +156,7 @@ impl Reedline {
let hinter = None; let hinter = None;
let validator = None; let validator = None;
let edit_mode = Box::new(Emacs::default()); let edit_mode = Box::new(Emacs::default());
let hist_session_id = Self::create_history_session_id();
Reedline { Reedline {
editor: Editor::default(), editor: Editor::default(),
@ -162,7 +164,7 @@ impl Reedline {
history_cursor: HistoryCursor::new(HistoryNavigationQuery::Normal( history_cursor: HistoryCursor::new(HistoryNavigationQuery::Normal(
LineBuffer::default(), LineBuffer::default(),
)), )),
history_session_id: None, history_session_id: hist_session_id,
history_last_run_id: None, history_last_run_id: None,
input_mode: InputMode::Regular, input_mode: InputMode::Regular,
painter, painter,
@ -182,6 +184,21 @@ impl Reedline {
} }
} }
/// Get a new history session id based on the current time and the first commit datetime of reedline
fn create_history_session_id() -> Option<HistorySessionId> {
let nanos = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
Ok(n) => n.as_nanos() as i64,
Err(_) => 0,
};
Some(HistorySessionId::new(nanos))
}
/// Return the previously generated history session id
pub fn get_history_session_id(&self) -> Option<HistorySessionId> {
self.history_session_id
}
/// A builder to include a [`Hinter`] in your instance of the Reedline engine /// A builder to include a [`Hinter`] in your instance of the Reedline engine
/// # Example /// # Example
/// ```rust /// ```rust
@ -856,13 +873,7 @@ impl Reedline {
let buf = self.editor.get_buffer(); let buf = self.editor.get_buffer();
if !buf.is_empty() { if !buf.is_empty() {
let mut entry = HistoryItem::from_command_line(buf); let mut entry = HistoryItem::from_command_line(buf);
// todo: in theory there's a race condition here because another shell might get the next session id at the same time entry.session_id = self.history_session_id;
entry.session_id =
Some(*self.history_session_id.get_or_insert_with(|| {
self.history
.next_session_id()
.expect("todo: error handling")
}));
let entry = self.history.save(entry).expect("todo: error handling"); let entry = self.history.save(entry).expect("todo: error handling");
self.history_last_run_id = entry.id; self.history_last_run_id = entry.id;
} }

View File

@ -2,7 +2,7 @@ use chrono::Utc;
use crate::{core_editor::LineBuffer, HistoryItem, Result}; use crate::{core_editor::LineBuffer, HistoryItem, Result};
use super::{HistoryItemId, HistorySessionId}; use super::HistoryItemId;
/// Browsing modes for a [`History`] /// Browsing modes for a [`History`]
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -149,7 +149,6 @@ pub trait History: Send {
fn load(&self, id: HistoryItemId) -> Result<HistoryItem>; fn load(&self, id: HistoryItemId) -> Result<HistoryItem>;
/// retrieves the next unused session id /// retrieves the next unused session id
fn next_session_id(&mut self) -> Result<HistorySessionId>;
/// count the results of a query /// count the results of a query
fn count(&self, query: SearchQuery) -> Result<i64>; fn count(&self, query: SearchQuery) -> Result<i64>;
@ -179,6 +178,8 @@ mod test {
#[cfg(not(feature = "sqlite"))] #[cfg(not(feature = "sqlite"))]
const IS_FILE_BASED: bool = true; const IS_FILE_BASED: bool = true;
use crate::HistorySessionId;
fn create_item(session: i64, cwd: &str, cmd: &str, exit_status: i64) -> HistoryItem { fn create_item(session: i64, cwd: &str, cmd: &str, exit_status: i64) -> HistoryItem {
HistoryItem { HistoryItem {
id: None, id: None,

View File

@ -1,6 +1,5 @@
use super::{ use super::{
base::CommandLineSearch, History, HistoryItem, HistoryItemId, HistorySessionId, base::CommandLineSearch, History, HistoryItem, HistoryItemId, SearchDirection, SearchQuery,
SearchDirection, SearchQuery,
}; };
use crate::{ use crate::{
result::{ReedlineError, ReedlineErrorVariants}, result::{ReedlineError, ReedlineErrorVariants},
@ -256,11 +255,6 @@ impl History for FileBackedHistory {
} }
Ok(()) Ok(())
} }
fn next_session_id(&mut self) -> Result<HistorySessionId> {
// doesn't support separating out different sessions
Ok(HistorySessionId::new(0))
}
} }
impl FileBackedHistory { impl FileBackedHistory {

View File

@ -1,6 +1,3 @@
use chrono::{TimeZone, Utc};
use rusqlite::{named_params, params, Connection, ToSql};
use super::{ use super::{
base::{CommandLineSearch, SearchDirection, SearchQuery}, base::{CommandLineSearch, SearchDirection, SearchQuery},
History, HistoryItem, HistoryItemId, HistorySessionId, History, HistoryItem, HistoryItemId, HistorySessionId,
@ -9,10 +6,10 @@ use crate::{
result::{ReedlineError, ReedlineErrorVariants}, result::{ReedlineError, ReedlineErrorVariants},
Result, Result,
}; };
use chrono::{TimeZone, Utc};
const SQLITE_APPLICATION_ID: i32 = 1151497937; use rusqlite::{named_params, params, Connection, ToSql};
use std::{path::PathBuf, time::Duration}; use std::{path::PathBuf, time::Duration};
const SQLITE_APPLICATION_ID: i32 = 1151497937;
/// A history that stores the values to an SQLite database. /// A history that stores the values to an SQLite database.
/// In addition to storing the command, the history can store an additional arbitrary HistoryEntryContext, /// In addition to storing the command, the history can store an additional arbitrary HistoryEntryContext,
@ -156,18 +153,6 @@ impl History for SqliteBackedHistory {
// no-op (todo?) // no-op (todo?)
Ok(()) Ok(())
} }
fn next_session_id(&mut self) -> Result<HistorySessionId> {
Ok(HistorySessionId::new(
self.db
.query_row(
"select coalesce(max(session_id), 0) + 1 from history",
params![],
|r| r.get(0),
)
.map_err(map_sqlite_err)?,
))
}
} }
fn map_sqlite_err(err: rusqlite::Error) -> ReedlineError { fn map_sqlite_err(err: rusqlite::Error) -> ReedlineError {
// TODO: better error mapping // TODO: better error mapping