Skip to content

Commit 55af00e

Browse files
committed
delete from file on file-backed history
1 parent baeecc2 commit 55af00e

File tree

1 file changed

+90
-71
lines changed

1 file changed

+90
-71
lines changed

src/history/file_backed.rs

+90-71
Original file line numberDiff line numberDiff line change
@@ -198,26 +198,93 @@ impl History for FileBackedHistory {
198198
"Invalid ID (not usize)",
199199
))
200200
})?;
201-
if self.len_on_disk <= id {
202-
self.entries.remove(id);
203-
Ok(())
204-
} else {
205-
// Since no ID is written to disk, it's not possible to delete them.
206-
// E.g. consider another instance having deleted entries, after this instance
207-
// loaded the file.
208-
Err(ReedlineError(
209-
ReedlineErrorVariants::HistoryFeatureUnsupported {
210-
history: "FileBackedHistory",
211-
feature: "removing entries",
212-
},
213-
))
201+
202+
let delete = self.entries[id].clone();
203+
let mut i = 0;
204+
while i < self.entries.len() {
205+
if self.entries[i] == delete {
206+
self.entries.remove(i);
207+
if i < self.len_on_disk {
208+
self.len_on_disk -= 1;
209+
}
210+
} else {
211+
i += 1;
212+
}
214213
}
214+
215+
self.sync_and_delete_item(Some(&delete))
216+
.map_err(|e| ReedlineError(ReedlineErrorVariants::IOError(e)))
215217
}
216218

217219
/// Writes unwritten history contents to disk.
218220
///
219221
/// If file would exceed `capacity` truncates the oldest entries.
220222
fn sync(&mut self) -> std::io::Result<()> {
223+
self.sync_and_delete_item(None)
224+
}
225+
226+
fn session(&self) -> Option<HistorySessionId> {
227+
self.session
228+
}
229+
}
230+
231+
impl FileBackedHistory {
232+
/// Creates a new in-memory history that remembers `n <= capacity` elements
233+
///
234+
/// # Panics
235+
///
236+
/// If `capacity == usize::MAX`
237+
pub fn new(capacity: usize) -> Self {
238+
if capacity == usize::MAX {
239+
panic!("History capacity too large to be addressed safely");
240+
}
241+
FileBackedHistory {
242+
capacity,
243+
entries: VecDeque::new(),
244+
file: None,
245+
len_on_disk: 0,
246+
session: None,
247+
}
248+
}
249+
250+
/// Creates a new history with an associated history file.
251+
///
252+
/// History file format: commands separated by new lines.
253+
/// If file exists file will be read otherwise empty file will be created.
254+
///
255+
///
256+
/// **Side effects:** creates all nested directories to the file
257+
///
258+
pub fn with_file(capacity: usize, file: PathBuf) -> std::io::Result<Self> {
259+
let mut hist = Self::new(capacity);
260+
if let Some(base_dir) = file.parent() {
261+
std::fs::create_dir_all(base_dir)?;
262+
}
263+
hist.file = Some(file);
264+
hist.sync()?;
265+
Ok(hist)
266+
}
267+
268+
// this history doesn't store any info except command line
269+
fn construct_entry(id: Option<HistoryItemId>, command_line: String) -> HistoryItem {
270+
HistoryItem {
271+
id,
272+
start_timestamp: None,
273+
command_line,
274+
session_id: None,
275+
hostname: None,
276+
cwd: None,
277+
duration: None,
278+
exit_status: None,
279+
more_info: None,
280+
}
281+
}
282+
283+
/// Writes unwritten history contents to disk, and optionally deletes
284+
/// all occurences of the provided item.
285+
///
286+
/// If file would exceed `capacity` truncates the oldest entries.
287+
fn sync_and_delete_item(&mut self, delete: Option<&str>) -> std::io::Result<()> {
221288
if let Some(fname) = &self.file {
222289
// The unwritten entries
223290
let own_entries = self.entries.range(self.len_on_disk..);
@@ -236,17 +303,26 @@ impl History for FileBackedHistory {
236303
let mut writer_guard = f_lock.write()?;
237304
let (mut foreign_entries, truncate) = {
238305
let reader = BufReader::new(writer_guard.deref());
306+
let mut deletions = false;
239307
let mut from_file = reader
240308
.lines()
241309
.map(|o| o.map(|i| decode_entry(&i)))
310+
.filter(|x| {
311+
if x.as_deref().ok() == delete {
312+
deletions = true;
313+
false
314+
} else {
315+
true
316+
}
317+
})
242318
.collect::<std::io::Result<VecDeque<_>>>()?;
243319
if from_file.len() + own_entries.len() > self.capacity {
244320
(
245321
from_file.split_off(from_file.len() - (self.capacity - own_entries.len())),
246322
true,
247323
)
248324
} else {
249-
(from_file, false)
325+
(from_file, deletions)
250326
}
251327
};
252328

@@ -282,63 +358,6 @@ impl History for FileBackedHistory {
282358
}
283359
Ok(())
284360
}
285-
286-
fn session(&self) -> Option<HistorySessionId> {
287-
self.session
288-
}
289-
}
290-
291-
impl FileBackedHistory {
292-
/// Creates a new in-memory history that remembers `n <= capacity` elements
293-
///
294-
/// # Panics
295-
///
296-
/// If `capacity == usize::MAX`
297-
pub fn new(capacity: usize) -> Self {
298-
if capacity == usize::MAX {
299-
panic!("History capacity too large to be addressed safely");
300-
}
301-
FileBackedHistory {
302-
capacity,
303-
entries: VecDeque::new(),
304-
file: None,
305-
len_on_disk: 0,
306-
session: None,
307-
}
308-
}
309-
310-
/// Creates a new history with an associated history file.
311-
///
312-
/// History file format: commands separated by new lines.
313-
/// If file exists file will be read otherwise empty file will be created.
314-
///
315-
///
316-
/// **Side effects:** creates all nested directories to the file
317-
///
318-
pub fn with_file(capacity: usize, file: PathBuf) -> std::io::Result<Self> {
319-
let mut hist = Self::new(capacity);
320-
if let Some(base_dir) = file.parent() {
321-
std::fs::create_dir_all(base_dir)?;
322-
}
323-
hist.file = Some(file);
324-
hist.sync()?;
325-
Ok(hist)
326-
}
327-
328-
// this history doesn't store any info except command line
329-
fn construct_entry(id: Option<HistoryItemId>, command_line: String) -> HistoryItem {
330-
HistoryItem {
331-
id,
332-
start_timestamp: None,
333-
command_line,
334-
session_id: None,
335-
hostname: None,
336-
cwd: None,
337-
duration: None,
338-
exit_status: None,
339-
more_info: None,
340-
}
341-
}
342361
}
343362

344363
impl Drop for FileBackedHistory {

0 commit comments

Comments
 (0)