11use std:: borrow:: Cow ;
22use std:: cell:: RefCell ;
3+ use std:: path:: PathBuf ;
34
45use eyre:: Result ;
56use rustyline:: completion:: {
@@ -13,7 +14,10 @@ use rustyline::highlight::{
1314 Highlighter ,
1415} ;
1516use rustyline:: hint:: Hinter as RustylineHinter ;
16- use rustyline:: history:: DefaultHistory ;
17+ use rustyline:: history:: {
18+ FileHistory ,
19+ SearchDirection ,
20+ } ;
1721use rustyline:: validate:: {
1822 ValidationContext ,
1923 ValidationResult ,
@@ -44,6 +48,7 @@ use super::tool_manager::{
4448} ;
4549use crate :: database:: settings:: Setting ;
4650use crate :: os:: Os ;
51+ use crate :: util:: directories:: chat_cli_bash_history_path;
4752
4853pub const COMMANDS : & [ & str ] = & [
4954 "/clear" ,
@@ -262,31 +267,26 @@ impl Completer for ChatCompleter {
262267
263268/// Custom hinter that provides shadowtext suggestions
264269pub struct ChatHinter {
265- /// Command history for providing suggestions based on past commands
266- history : Vec < String > ,
267270 /// Whether history-based hints are enabled
268271 history_hints_enabled : bool ,
272+ history_path : PathBuf ,
269273}
270274
271275impl ChatHinter {
272276 /// Creates a new ChatHinter instance
273- pub fn new ( history_hints_enabled : bool ) -> Self {
277+ pub fn new ( history_hints_enabled : bool , history_path : PathBuf ) -> Self {
274278 Self {
275- history : Vec :: new ( ) ,
276279 history_hints_enabled,
280+ history_path,
277281 }
278282 }
279283
280- /// Updates the history with a new command
281- pub fn update_history ( & mut self , command : & str ) {
282- let command = command. trim ( ) ;
283- if !command. is_empty ( ) && !command. contains ( '\n' ) && !command. contains ( '\r' ) {
284- self . history . push ( command. to_string ( ) ) ;
285- }
284+ pub fn get_history_path ( & self ) -> PathBuf {
285+ self . history_path . clone ( )
286286 }
287287
288- /// Finds the best hint for the current input
289- fn find_hint ( & self , line : & str ) -> Option < String > {
288+ /// Finds the best hint for the current input using rustyline's history
289+ fn find_hint ( & self , line : & str , ctx : & Context < ' _ > ) -> Option < String > {
290290 // If line is empty, no hint
291291 if line. is_empty ( ) {
292292 return None ;
@@ -300,13 +300,20 @@ impl ChatHinter {
300300 . map ( |cmd| cmd[ line. len ( ) ..] . to_string ( ) ) ;
301301 }
302302
303- // Try to find a hint from history if history hints are enabled
303+ // Try to find a hint from rustyline's history if history hints are enabled
304304 if self . history_hints_enabled {
305- return self . history
306- . iter ( )
307- . rev ( ) // Start from most recent
308- . find ( |cmd| cmd. starts_with ( line) && cmd. len ( ) > line. len ( ) )
309- . map ( |cmd| cmd[ line. len ( ) ..] . to_string ( ) ) ;
305+ let history = ctx. history ( ) ;
306+ let history_len = history. len ( ) ;
307+ if history_len == 0 {
308+ return None ;
309+ }
310+
311+ if let Ok ( Some ( search_result) ) = history. starts_with ( line, history_len - 1 , SearchDirection :: Reverse ) {
312+ let entry = search_result. entry . to_string ( ) ;
313+ if entry. len ( ) > line. len ( ) {
314+ return Some ( entry[ line. len ( ) ..] . to_string ( ) ) ;
315+ }
316+ }
310317 }
311318
312319 None
@@ -316,13 +323,13 @@ impl ChatHinter {
316323impl RustylineHinter for ChatHinter {
317324 type Hint = String ;
318325
319- fn hint ( & self , line : & str , pos : usize , _ctx : & Context < ' _ > ) -> Option < Self :: Hint > {
326+ fn hint ( & self , line : & str , pos : usize , ctx : & Context < ' _ > ) -> Option < Self :: Hint > {
320327 // Only provide hints when cursor is at the end of the line
321328 if pos < line. len ( ) {
322329 return None ;
323330 }
324331
325- self . find_hint ( line)
332+ self . find_hint ( line, ctx )
326333 }
327334}
328335
@@ -363,9 +370,8 @@ pub struct ChatHelper {
363370}
364371
365372impl ChatHelper {
366- /// Updates the history of the ChatHinter with a new command
367- pub fn update_hinter_history ( & mut self , command : & str ) {
368- self . hinter . update_history ( command) ;
373+ pub fn get_history_path ( & self ) -> PathBuf {
374+ self . hinter . get_history_path ( )
369375 }
370376}
371377
@@ -426,7 +432,7 @@ pub fn rl(
426432 os : & Os ,
427433 sender : PromptQuerySender ,
428434 receiver : PromptQueryResponseReceiver ,
429- ) -> Result < Editor < ChatHelper , DefaultHistory > > {
435+ ) -> Result < Editor < ChatHelper , FileHistory > > {
430436 let edit_mode = match os. database . settings . get_string ( Setting :: ChatEditMode ) . as_deref ( ) {
431437 Some ( "vi" | "vim" ) => EditMode :: Vi ,
432438 _ => EditMode :: Emacs ,
@@ -437,21 +443,30 @@ pub fn rl(
437443 . edit_mode ( edit_mode)
438444 . build ( ) ;
439445
440- // Default to disabled if setting doesn't exist
441446 let history_hints_enabled = os
442447 . database
443448 . settings
444449 . get_bool ( Setting :: ChatEnableHistoryHints )
445450 . unwrap_or ( false ) ;
451+
452+ let history_path = chat_cli_bash_history_path ( os) ?;
453+
446454 let h = ChatHelper {
447455 completer : ChatCompleter :: new ( sender, receiver) ,
448- hinter : ChatHinter :: new ( history_hints_enabled) ,
456+ hinter : ChatHinter :: new ( history_hints_enabled, history_path ) ,
449457 validator : MultiLineValidator ,
450458 } ;
451459
452460 let mut rl = Editor :: with_config ( config) ?;
453461 rl. set_helper ( Some ( h) ) ;
454462
463+ // Load history from ~/.aws/amazonq/cli_history
464+ if let Err ( e) = rl. load_history ( & rl. helper ( ) . unwrap ( ) . get_history_path ( ) ) {
465+ if !matches ! ( e, ReadlineError :: Io ( ref io_err) if io_err. kind( ) == std:: io:: ErrorKind :: NotFound ) {
466+ eprintln ! ( "Warning: Failed to load history: {}" , e) ;
467+ }
468+ }
469+
455470 // Add custom keybinding for Alt+Enter to insert a newline
456471 rl. bind_sequence (
457472 KeyEvent ( KeyCode :: Enter , Modifiers :: ALT ) ,
@@ -487,6 +502,7 @@ pub fn rl(
487502mod tests {
488503 use crossterm:: style:: Stylize ;
489504 use rustyline:: highlight:: Highlighter ;
505+ use rustyline:: history:: DefaultHistory ;
490506
491507 use super :: * ;
492508
@@ -537,7 +553,7 @@ mod tests {
537553 let ( _, prompt_response_receiver) = tokio:: sync:: broadcast:: channel :: < PromptQueryResult > ( 5 ) ;
538554 let helper = ChatHelper {
539555 completer : ChatCompleter :: new ( prompt_request_sender, prompt_response_receiver) ,
540- hinter : ChatHinter :: new ( true ) ,
556+ hinter : ChatHinter :: new ( true , PathBuf :: new ( ) ) ,
541557 validator : MultiLineValidator ,
542558 } ;
543559
@@ -553,7 +569,7 @@ mod tests {
553569 let ( _, prompt_response_receiver) = tokio:: sync:: broadcast:: channel :: < PromptQueryResult > ( 5 ) ;
554570 let helper = ChatHelper {
555571 completer : ChatCompleter :: new ( prompt_request_sender, prompt_response_receiver) ,
556- hinter : ChatHinter :: new ( true ) ,
572+ hinter : ChatHinter :: new ( true , PathBuf :: new ( ) ) ,
557573 validator : MultiLineValidator ,
558574 } ;
559575
@@ -569,7 +585,7 @@ mod tests {
569585 let ( _, prompt_response_receiver) = tokio:: sync:: broadcast:: channel :: < PromptQueryResult > ( 5 ) ;
570586 let helper = ChatHelper {
571587 completer : ChatCompleter :: new ( prompt_request_sender, prompt_response_receiver) ,
572- hinter : ChatHinter :: new ( true ) ,
588+ hinter : ChatHinter :: new ( true , PathBuf :: new ( ) ) ,
573589 validator : MultiLineValidator ,
574590 } ;
575591
@@ -585,7 +601,7 @@ mod tests {
585601 let ( _, prompt_response_receiver) = tokio:: sync:: broadcast:: channel :: < PromptQueryResult > ( 5 ) ;
586602 let helper = ChatHelper {
587603 completer : ChatCompleter :: new ( prompt_request_sender, prompt_response_receiver) ,
588- hinter : ChatHinter :: new ( true ) ,
604+ hinter : ChatHinter :: new ( true , PathBuf :: new ( ) ) ,
589605 validator : MultiLineValidator ,
590606 } ;
591607
@@ -604,7 +620,7 @@ mod tests {
604620 let ( _, prompt_response_receiver) = tokio:: sync:: broadcast:: channel :: < PromptQueryResult > ( 5 ) ;
605621 let helper = ChatHelper {
606622 completer : ChatCompleter :: new ( prompt_request_sender, prompt_response_receiver) ,
607- hinter : ChatHinter :: new ( true ) ,
623+ hinter : ChatHinter :: new ( true , PathBuf :: new ( ) ) ,
608624 validator : MultiLineValidator ,
609625 } ;
610626
@@ -620,7 +636,7 @@ mod tests {
620636 let ( _, prompt_response_receiver) = tokio:: sync:: broadcast:: channel :: < PromptQueryResult > ( 1 ) ;
621637 let helper = ChatHelper {
622638 completer : ChatCompleter :: new ( prompt_request_sender, prompt_response_receiver) ,
623- hinter : ChatHinter :: new ( true ) ,
639+ hinter : ChatHinter :: new ( true , PathBuf :: new ( ) ) ,
624640 validator : MultiLineValidator ,
625641 } ;
626642
@@ -635,7 +651,7 @@ mod tests {
635651 let ( _, prompt_response_receiver) = tokio:: sync:: broadcast:: channel :: < PromptQueryResult > ( 1 ) ;
636652 let helper = ChatHelper {
637653 completer : ChatCompleter :: new ( prompt_request_sender, prompt_response_receiver) ,
638- hinter : ChatHinter :: new ( true ) ,
654+ hinter : ChatHinter :: new ( true , PathBuf :: new ( ) ) ,
639655 validator : MultiLineValidator ,
640656 } ;
641657
@@ -650,7 +666,7 @@ mod tests {
650666 let ( _, prompt_response_receiver) = tokio:: sync:: broadcast:: channel :: < PromptQueryResult > ( 1 ) ;
651667 let helper = ChatHelper {
652668 completer : ChatCompleter :: new ( prompt_request_sender, prompt_response_receiver) ,
653- hinter : ChatHinter :: new ( true ) ,
669+ hinter : ChatHinter :: new ( true , PathBuf :: new ( ) ) ,
654670 validator : MultiLineValidator ,
655671 } ;
656672
@@ -664,7 +680,7 @@ mod tests {
664680
665681 #[ test]
666682 fn test_chat_hinter_command_hint ( ) {
667- let hinter = ChatHinter :: new ( true ) ;
683+ let hinter = ChatHinter :: new ( true , PathBuf :: new ( ) ) ;
668684
669685 // Test hint for a command
670686 let line = "/he" ;
@@ -694,11 +710,7 @@ mod tests {
694710
695711 #[ test]
696712 fn test_chat_hinter_history_hint_disabled ( ) {
697- let mut hinter = ChatHinter :: new ( false ) ;
698-
699- // Add some history
700- hinter. update_history ( "Hello, world!" ) ;
701- hinter. update_history ( "How are you?" ) ;
713+ let hinter = ChatHinter :: new ( false , PathBuf :: new ( ) ) ;
702714
703715 // Test hint from history - should be None since history hints are disabled
704716 let line = "How" ;
0 commit comments