Skip to content

Commit c9b9908

Browse files
committed
refactor(frecency): make the ux the same as history, expose history-size to cli
1 parent a4a1c2c commit c9b9908

File tree

10 files changed

+95
-58
lines changed

10 files changed

+95
-58
lines changed

.config/config.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ history_size = 200
2828
# When false: history navigation is scoped to the current channel
2929
global_history = false
3030

31+
# Frecency settings
32+
# ----------------
33+
# Maximum number of entries to keep in the frecency list (default: 0)
34+
# Enable frecency (frequency + recency) scoring to boost previously selected entries
35+
# Set to 0 to disable frecency functionality entirely
36+
frecency_size = 0
37+
# Whether to use global frecency across all channels (default: false)
38+
# When true: frecency scoring considers selections from all channels
39+
# When false: frecency scoring is scoped to the current channel
40+
global_frecency = false
41+
3142
[ui]
3243
# How much space to allocate for the UI (in percentage of the screen)
3344
# ┌─────────────────────────┐

docs/01-Users/09-cli.md

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,16 @@ minimum height is ensured (set by default at 15 lines)
465465

466466
### 📚 History Options
467467

468+
#### `--history-size <SIZE>`
469+
470+
**Purpose**: Set the maximum number of entries to track in search history
471+
472+
- **Both Modes**: Same behavior
473+
- **Default**: 100 (from configuration)
474+
- **Range**: 0 to disable, positive integers to enable
475+
- **Use Case**: When set to a positive value, enables search history that tracks recent queries. When set to 0, search history is disabled
476+
- **Example**: `tv files --history-size 50`
477+
468478
#### `--global-history`
469479

470480
**Purpose**: Enables global history for the current session
@@ -476,23 +486,24 @@ minimum height is ensured (set by default at 15 lines)
476486

477487
### 🎯 Frecency Options
478488

479-
#### `--frecency`
489+
#### `--frecency-size <SIZE>`
480490

481-
**Purpose**: Enable frecency scoring to boost previously selected entries
491+
**Purpose**: Set the maximum number of entries to track in frecency scoring
482492

483493
- **Both Modes**: Same behavior
484-
- **Default**: Disabled
485-
- **Use Case**: When enabled, entries that were previously selected will be ranked higher in the results list based on frequency of use and recency of access
486-
- **Example**: `tv files --frecency`
494+
- **Default**: 0 (frecency disabled)
495+
- **Range**: 0 to disable, positive integers to enable
496+
- **Use Case**: When set to a positive value, enables frecency scoring that boosts frequently used entries. When set to 0, frecency is disabled
497+
- **Example**: `tv files --frecency-size 100`
487498

488499
#### `--global-frecency`
489500

490501
**Purpose**: Use global frecency across all channels instead of channel-specific frecency
491502

492-
- **Both Modes**: This flag only works when `--frecency` is enabled
503+
- **Both Modes**: Same behavior
493504
- **Default**: Channel-specific frecency (scoped to current channel)
494505
- **Use Case**: When enabled, frecency scoring will consider selections from all channels. When disabled (default), frecency is scoped to the current channel
495-
- **Example**: `tv files --frecency --global-frecency`
506+
- **Example**: `tv files --global-frecency`
496507

497508
### 🔧 Special Mode Options
498509

man/tv.1

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
.SH NAME
55
television \- Cross\-platform, fast and extensible general purpose fuzzy finder TUI.
66
.SH SYNOPSIS
7-
\fBtelevision\fR [\fB\-\-preview\-offset\fR] [\fB\-\-no\-preview\fR] [\fB\-\-hide\-preview\fR] [\fB\-\-show\-preview\fR] [\fB\-\-no\-status\-bar\fR] [\fB\-\-hide\-status\-bar\fR] [\fB\-\-show\-status\-bar\fR] [\fB\-t\fR|\fB\-\-tick\-rate\fR] [\fB\-\-watch\fR] [\fB\-k\fR|\fB\-\-keybindings\fR] [\fB\-\-expect\fR] [\fB\-i\fR|\fB\-\-input\fR] [\fB\-\-input\-header\fR] [\fB\-\-input\-prompt\fR] [\fB\-\-input\-border\fR] [\fB\-\-input\-padding\fR] [\fB\-\-preview\-header\fR] [\fB\-\-preview\-footer\fR] [\fB\-\-preview\-border\fR] [\fB\-\-preview\-padding\fR] [\fB\-\-hide\-preview\-scrollbar\fR] [\fB\-s\fR|\fB\-\-source\-command\fR] [\fB\-\-ansi\fR] [\fB\-\-source\-display\fR] [\fB\-\-source\-output\fR] [\fB\-\-results\-border\fR] [\fB\-\-results\-padding\fR] [\fB\-\-source\-entry\-delimiter\fR] [\fB\-p\fR|\fB\-\-preview\-command\fR] [\fB\-\-cache\-preview\fR] [\fB\-\-layout\fR] [\fB\-\-autocomplete\-prompt\fR] [\fB\-\-exact\fR] [\fB\-\-select\-1\fR] [\fB\-\-take\-1\fR] [\fB\-\-take\-1\-fast\fR] [\fB\-\-no\-remote\fR] [\fB\-\-hide\-remote\fR] [\fB\-\-show\-remote\fR] [\fB\-\-no\-help\-panel\fR] [\fB\-\-hide\-help\-panel\fR] [\fB\-\-show\-help\-panel\fR] [\fB\-\-ui\-scale\fR] [\fB\-\-preview\-size\fR] [\fB\-\-config\-file\fR] [\fB\-\-cable\-dir\fR] [\fB\-\-global\-history\fR] [\fB\-\-frecency\fR] [\fB\-\-global\-frecency\fR] [\fB\-\-height\fR] [\fB\-\-width\fR] [\fB\-\-inline\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fICHANNEL\fR] [\fIPATH\fR] [\fIsubcommands\fR]
7+
\fBtelevision\fR [\fB\-\-preview\-offset\fR] [\fB\-\-no\-preview\fR] [\fB\-\-hide\-preview\fR] [\fB\-\-show\-preview\fR] [\fB\-\-no\-status\-bar\fR] [\fB\-\-hide\-status\-bar\fR] [\fB\-\-show\-status\-bar\fR] [\fB\-t\fR|\fB\-\-tick\-rate\fR] [\fB\-\-watch\fR] [\fB\-k\fR|\fB\-\-keybindings\fR] [\fB\-\-expect\fR] [\fB\-i\fR|\fB\-\-input\fR] [\fB\-\-input\-header\fR] [\fB\-\-input\-prompt\fR] [\fB\-\-input\-border\fR] [\fB\-\-input\-padding\fR] [\fB\-\-preview\-header\fR] [\fB\-\-preview\-footer\fR] [\fB\-\-preview\-border\fR] [\fB\-\-preview\-padding\fR] [\fB\-\-hide\-preview\-scrollbar\fR] [\fB\-s\fR|\fB\-\-source\-command\fR] [\fB\-\-ansi\fR] [\fB\-\-source\-display\fR] [\fB\-\-source\-output\fR] [\fB\-\-results\-border\fR] [\fB\-\-results\-padding\fR] [\fB\-\-source\-entry\-delimiter\fR] [\fB\-p\fR|\fB\-\-preview\-command\fR] [\fB\-\-cache\-preview\fR] [\fB\-\-layout\fR] [\fB\-\-autocomplete\-prompt\fR] [\fB\-\-exact\fR] [\fB\-\-select\-1\fR] [\fB\-\-take\-1\fR] [\fB\-\-take\-1\-fast\fR] [\fB\-\-no\-remote\fR] [\fB\-\-hide\-remote\fR] [\fB\-\-show\-remote\fR] [\fB\-\-no\-help\-panel\fR] [\fB\-\-hide\-help\-panel\fR] [\fB\-\-show\-help\-panel\fR] [\fB\-\-ui\-scale\fR] [\fB\-\-preview\-size\fR] [\fB\-\-config\-file\fR] [\fB\-\-cable\-dir\fR] [\fB\-\-history\-size\fR] [\fB\-\-global\-history\fR] [\fB\-\-global\-frecency\fR] [\fB\-\-frecency\-size\fR] [\fB\-\-height\fR] [\fB\-\-width\fR] [\fB\-\-inline\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fICHANNEL\fR] [\fIPATH\fR] [\fIsubcommands\fR]
88
.SH DESCRIPTION
99
Cross\-platform, fast and extensible general purpose fuzzy finder TUI.
1010
.SH OPTIONS
@@ -381,6 +381,13 @@ Provide a custom cable directory to use.
381381

382382
This flag works identically in both channel mode and ad\-hoc mode.
383383
.TP
384+
\fB\-\-history\-size\fR=\fIHISTORY_SIZE\fR
385+
Maximum number of entries to track in search history.
386+
387+
When set to 0, search history is disabled.
388+
When set to a positive value, search history is enabled and will track
389+
up to this many recent search queries.
390+
.TP
384391
\fB\-\-global\-history\fR
385392
Use global history instead of channel\-specific history.
386393

@@ -389,22 +396,21 @@ This flag only works in channel mode.
389396
When enabled, history navigation will show entries from all channels.
390397
When disabled (default), history navigation is scoped to the current channel.
391398
.TP
392-
\fB\-\-frecency\fR
393-
Enable frecency scoring to boost previously selected entries.
394-
395-
This flag works in both channel mode and ad\-hoc mode.
396-
397-
When enabled, entries that were previously selected will be ranked higher
398-
in the results list based on frequency of use and recency of access.
399-
.TP
400399
\fB\-\-global\-frecency\fR
401400
Use global frecency across all channels instead of channel\-specific frecency.
402401

403-
This flag only works when \-\-frecency is enabled.
402+
This flag works identically in both channel mode and ad\-hoc mode (if global is used).
404403

405404
When enabled, frecency scoring will consider selections from all channels.
406405
When disabled (default), frecency is scoped to the current channel.
407406
.TP
407+
\fB\-\-frecency\-size\fR=\fIFRECENCY_SIZE\fR
408+
Maximum number of entries to track in frecency scoring.
409+
410+
When set to 0 (default), frecency scoring is disabled.
411+
When set to a positive value, frecency scoring is enabled and will track
412+
up to this many frequently used entries.
413+
.TP
408414
\fB\-\-height\fR=\fIINTEGER\fR
409415
Height in lines for non\-fullscreen mode.
410416

television/app.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
channels::{entry::Entry, prototypes::ActionSpec},
55
config::layers::LayeredConfig,
66
event::{Event, EventLoop, InputEvent, Key, MouseInputEvent},
7-
frecency::{DEFAULT_FRECENCY_SIZE, Frecency},
7+
frecency::Frecency,
88
history::History,
99
render::{RenderingTask, UiState, render},
1010
television::{Mode, Television},
@@ -121,25 +121,20 @@ impl App {
121121
let television =
122122
Television::new(action_tx.clone(), layered_config, cable_channels);
123123

124+
// Initialize history
124125
let mut history = History::new(
125126
television.merged_config.history_size,
126127
&television.merged_config.channel_name,
127128
television.merged_config.global_history,
128-
&television.merged_config.data_dir.clone(),
129+
&television.merged_config.data_dir,
129130
);
130131
if let Err(e) = history.init() {
131132
error!("Failed to initialize history: {}", e);
132133
}
133134

134135
// Initialize frecency
135-
let frecency_size = if television.merged_config.frecency {
136-
DEFAULT_FRECENCY_SIZE
137-
} else {
138-
0
139-
};
140-
141136
let mut frecency = Frecency::new(
142-
frecency_size,
137+
television.merged_config.frecency_size,
143138
&television.merged_config.channel_name,
144139
television.merged_config.global_frecency,
145140
&television.merged_config.data_dir,

television/cli/args.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,14 @@ pub struct Cli {
505505
#[arg(long, value_name = "PATH", verbatim_doc_comment, value_parser = validate_directory_path)]
506506
pub cable_dir: Option<String>,
507507

508+
/// Maximum number of entries to track in search history.
509+
///
510+
/// When set to 0, search history is disabled.
511+
/// When set to a positive value, search history is enabled and will track
512+
/// up to this many recent search queries.
513+
#[arg(long, verbatim_doc_comment)]
514+
pub history_size: Option<usize>,
515+
508516
/// Use global history instead of channel-specific history.
509517
///
510518
/// This flag only works in channel mode.
@@ -514,24 +522,24 @@ pub struct Cli {
514522
#[arg(long, verbatim_doc_comment)]
515523
pub global_history: bool,
516524

517-
/// Enable frecency scoring to boost previously selected entries.
518-
///
519-
/// This flag works in both channel mode and ad-hoc mode.
520-
///
521-
/// When enabled, entries that were previously selected will be ranked higher
522-
/// in the results list based on frequency of use and recency of access.
523-
#[arg(long, verbatim_doc_comment)]
524-
pub frecency: bool,
525-
526525
/// Use global frecency across all channels instead of channel-specific frecency.
527526
///
528-
/// This flag only works when --frecency is enabled.
527+
/// This flag works identically in both channel mode and ad-hoc mode (if global is used).
528+
// Otherwise if just works for channel mode.
529529
///
530530
/// When enabled, frecency scoring will consider selections from all channels.
531531
/// When disabled (default), frecency is scoped to the current channel.
532532
#[arg(long, verbatim_doc_comment)]
533533
pub global_frecency: bool,
534534

535+
/// Maximum number of entries to track in frecency scoring.
536+
///
537+
/// When set to 0 (default), frecency scoring is disabled.
538+
/// When set to a positive value, frecency scoring is enabled and will track
539+
/// up to this many frequently used entries.
540+
#[arg(long, verbatim_doc_comment)]
541+
pub frecency_size: Option<usize>,
542+
535543
/// Height in lines for non-fullscreen mode.
536544
///
537545
/// This flag works identically in both channel mode and ad-hoc mode.

television/cli/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,9 @@ pub struct ChannelCli {
127127
#[derive(Debug, Clone, Default)]
128128
pub struct GlobalCli {
129129
pub workdir: Option<PathBuf>,
130+
pub history_size: Option<usize>,
130131
pub global_history: bool,
131-
pub frecency: bool,
132+
pub frecency_size: Option<usize>,
132133
pub global_frecency: bool,
133134
pub config_file: Option<PathBuf>,
134135
pub cable_dir: Option<PathBuf>,
@@ -372,8 +373,9 @@ pub fn post_process(cli: Cli, readable_stdin: bool) -> PostProcessedCli {
372373
global: GlobalCli {
373374
// Workdir, global history and frecency
374375
workdir: working_directory,
376+
history_size: cli.history_size,
375377
global_history: cli.global_history,
376-
frecency: cli.frecency,
378+
frecency_size: cli.frecency_size,
377379
global_frecency: cli.global_frecency,
378380

379381
// Configuration sources

television/config/layers.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,14 @@ impl LayeredConfig {
6767
let data_dir = self.base_config.application.data_dir.clone();
6868
let default_channel =
6969
self.base_config.application.default_channel.clone();
70-
let history_size = self.base_config.application.history_size;
70+
let history_size = self
71+
.global_cli
72+
.history_size
73+
.unwrap_or(self.base_config.application.history_size);
74+
let frecency_size = self
75+
.global_cli
76+
.frecency_size
77+
.unwrap_or(self.base_config.application.frecency_size);
7178
let theme = self.base_config.ui.theme.clone();
7279
let shell_integration_commands =
7380
self.base_config.shell_integration.commands.clone();
@@ -398,9 +405,6 @@ impl LayeredConfig {
398405
let global_history = self.global_cli.global_history
399406
|| self.channel.history.global_mode.unwrap_or_default()
400407
|| self.base_config.application.global_history;
401-
let frecency = self.global_cli.frecency
402-
|| self.channel.frecency.enabled.unwrap_or_default()
403-
|| self.base_config.application.frecency;
404408
let global_frecency = self.global_cli.global_frecency
405409
|| self.channel.frecency.global_mode.unwrap_or_default()
406410
|| self.base_config.application.global_frecency;
@@ -428,7 +432,7 @@ impl LayeredConfig {
428432
default_channel,
429433
history_size,
430434
global_history,
431-
frecency,
435+
frecency_size,
432436
global_frecency,
433437
working_directory,
434438
autocomplete_prompt,
@@ -521,7 +525,7 @@ pub struct MergedConfig {
521525
pub default_channel: String,
522526
pub history_size: usize,
523527
pub global_history: bool,
524-
pub frecency: bool,
528+
pub frecency_size: usize,
525529
pub global_frecency: bool,
526530
pub working_directory: Option<PathBuf>,
527531
pub autocomplete_prompt: Option<String>,

television/config/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ pub struct AppConfig {
4545
/// Whether to use global history (all channels) or channel-specific history (default)
4646
#[serde(default = "default_global_history")]
4747
pub global_history: bool,
48-
/// Whether to use frecency
49-
#[serde(default = "default_frecency")]
50-
pub frecency: bool,
48+
/// Maximum number of entries to keep in the frecency list
49+
#[serde(default = "default_frecency_size")]
50+
pub frecency_size: usize,
5151
/// Whether to use global frecency (all channels) or channel-specific frecency (default)
5252
#[serde(default = "default_global_frecency")]
5353
pub global_frecency: bool,
@@ -62,7 +62,7 @@ impl Default for AppConfig {
6262
default_channel: default_channel(),
6363
history_size: default_history_size(),
6464
global_history: default_global_history(),
65-
frecency: default_frecency(),
65+
frecency_size: default_frecency_size(),
6666
global_frecency: default_global_frecency(),
6767
}
6868
}
@@ -80,8 +80,8 @@ fn default_global_history() -> bool {
8080
false
8181
}
8282

83-
fn default_frecency() -> bool {
84-
false
83+
fn default_frecency_size() -> usize {
84+
0
8585
}
8686

8787
fn default_global_frecency() -> bool {

television/frecency.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use tracing::debug;
99

1010
const FRECENCY_FILE_NAME: &str = "frecency.json";
1111
const SECONDS_PER_DAY: f64 = 86400.0; // 24 * 60 * 60
12-
pub const DEFAULT_FRECENCY_SIZE: usize = 1000;
1312
/// Maximum number of frecent items to prioritize in search results
1413
/// This ensures frequently-used items get higher priority in nucleo matching
1514
pub const FRECENT_ITEMS_PRIORITY_COUNT: usize = 200;

tests/cli/frecency.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Tests for CLI frecency behavior: --frecency and --global-frecency flags.
1+
//! Tests for CLI frecency behavior: --global-frecency flag.
22
//!
33
//! These tests verify Television's frecency scoring system that boosts previously
44
//! selected entries in future searches based on frequency and recency of access.
@@ -15,7 +15,8 @@ fn tv_frecency_with_data_dir(
1515
) -> portable_pty::CommandBuilder {
1616
let mut cmd = tv_local_config_and_cable_with_args(args);
1717
cmd.env("TELEVISION_DATA", data_dir);
18-
cmd.arg("--frecency");
18+
cmd.arg("--frecency-size");
19+
cmd.arg("100");
1920
cmd
2021
}
2122

@@ -488,7 +489,7 @@ fn test_frecency_corrupted_file_handling() {
488489
}
489490
}
490491

491-
/// Tests frecency without the --frecency flag to ensure it's properly disabled.
492+
/// Tests frecency without the --frecency-size flag to ensure it's properly disabled.
492493
#[test]
493494
fn test_frecency_disabled_behavior() {
494495
let temp_data = TempDataDir::init_with_empty_frecency();
@@ -497,15 +498,15 @@ fn test_frecency_disabled_behavior() {
497498
// Create test data
498499
let test_entries = ["first.txt", "second.txt", "third.txt"];
499500

500-
// First session: select third.txt WITHOUT --frecency flag
501+
// First session: select third.txt WITHOUT --frecency-size flag
501502
{
502503
let mut tester = PtyTester::new();
503504
let mut cmd = tv_local_config_and_cable_with_args(&[
504505
"--source-command",
505506
&format!("printf '{}'", test_entries.join("\n")),
506507
]);
507508
cmd.env("TELEVISION_DATA", data_dir);
508-
// Note: NO --frecency flag
509+
// Note: NO --frecency-size flag
509510

510511
let mut child = tester.spawn_command_tui(cmd);
511512

@@ -530,7 +531,7 @@ fn test_frecency_disabled_behavior() {
530531
.unwrap_or_else(|_| "[]".to_string());
531532
assert!(
532533
!frecency_content.contains("third.txt"),
533-
"Frecency file should not contain selected entry when --frecency flag is not used. Content:\n{}",
534+
"Frecency file should not contain selected entry when --frecency-size flag is not used. Content:\n{}",
534535
frecency_content
535536
);
536537
}
@@ -543,7 +544,7 @@ fn test_frecency_disabled_behavior() {
543544
&format!("printf '{}'", test_entries.join("\n")),
544545
]);
545546
cmd.env("TELEVISION_DATA", data_dir);
546-
// Note: Still NO --frecency flag
547+
// Note: Still NO --frecency-size flag
547548

548549
let mut child = tester.spawn_command_tui(cmd);
549550

0 commit comments

Comments
 (0)