11mod api;
22mod parse;
33mod prompt;
4- mod terminal ;
4+ mod stdio ;
55
66use std:: io:: {
77 IsTerminal ,
@@ -16,13 +16,13 @@ use color_eyre::owo_colors::OwoColorize;
1616use crossterm:: style:: {
1717 Attribute ,
1818 Color ,
19- Print ,
2019} ;
2120use crossterm:: {
2221 cursor,
2322 execute,
2423 queue,
2524 style,
25+ terminal,
2626} ;
2727use eyre:: {
2828 Result ,
@@ -40,7 +40,7 @@ use spinners::{
4040 Spinner ,
4141 Spinners ,
4242} ;
43- use terminal :: StdioOutput ;
43+ use stdio :: StdioOutput ;
4444use winnow:: Partial ;
4545use winnow:: stream:: Offset ;
4646
@@ -79,7 +79,8 @@ pub async fn chat(mut input: String) -> Result<ExitCode> {
7979 }
8080
8181 let mut output = StdioOutput :: new ( is_interactive) ;
82- let result = try_chat ( & mut output, input, is_interactive) . await ;
82+ let client = StreamingClient :: new ( ) . await ?;
83+ let result = try_chat ( & mut output, input, is_interactive, & client) . await ;
8384
8485 if is_interactive {
8586 queue ! ( output, style:: SetAttribute ( Attribute :: Reset ) , style:: ResetColor ) . ok ( ) ;
@@ -89,9 +90,13 @@ pub async fn chat(mut input: String) -> Result<ExitCode> {
8990 result. map ( |_| ExitCode :: SUCCESS )
9091}
9192
92- async fn try_chat < W : Write > ( output : & mut W , mut input : String , interactive : bool ) -> Result < ( ) > {
93+ async fn try_chat < W : Write > (
94+ output : & mut W ,
95+ mut input : String ,
96+ interactive : bool ,
97+ client : & StreamingClient ,
98+ ) -> Result < ( ) > {
9399 let mut rl = if interactive { Some ( rl ( ) ?) } else { None } ;
94- let client = StreamingClient :: new ( ) . await ?;
95100 let mut rx = None ;
96101 let mut conversation_id: Option < String > = None ;
97102 let mut message_id = None ;
@@ -158,8 +163,8 @@ You can include additional context by adding the following to your prompt:
158163 let mut offset = 0 ;
159164 let mut ended = false ;
160165
161- let columns = crossterm :: terminal:: window_size ( ) ? . columns . into ( ) ;
162- let mut state = ParseState :: new ( columns ) ;
166+ let terminal_width = terminal:: window_size ( ) . map ( |s| s . columns . into ( ) ) . ok ( ) ;
167+ let mut state = ParseState :: new ( terminal_width ) ;
163168
164169 loop {
165170 if let Some ( response) = rx. recv ( ) . await {
@@ -229,11 +234,11 @@ You can include additional context by adding the following to your prompt:
229234 buf. push ( '\n' ) ;
230235 }
231236
232- if !buf. is_empty ( ) && interactive {
237+ if !buf. is_empty ( ) && interactive && spinner . is_some ( ) {
233238 drop ( spinner. take ( ) ) ;
234239 queue ! (
235240 output,
236- crossterm :: terminal:: Clear ( crossterm :: terminal:: ClearType :: CurrentLine ) ,
241+ terminal:: Clear ( terminal:: ClearType :: CurrentLine ) ,
237242 cursor:: MoveToColumn ( 0 ) ,
238243 cursor:: Show
239244 ) ?;
@@ -259,32 +264,26 @@ You can include additional context by adding the following to your prompt:
259264 }
260265
261266 if ended {
267+ if let ( Some ( conversation_id) , Some ( message_id) ) = ( & conversation_id, & message_id) {
268+ fig_telemetry:: send_chat_added_message ( conversation_id. to_owned ( ) , message_id. to_owned ( ) ) . await ;
269+ }
270+
262271 if interactive {
263- queue ! (
264- output,
265- style:: ResetColor ,
266- style:: SetAttribute ( Attribute :: Reset ) ,
267- Print ( "\n " )
268- ) ?;
272+ queue ! ( output, style:: ResetColor , style:: SetAttribute ( Attribute :: Reset ) ) ?;
269273
270274 for ( i, citation) in & state. citations {
271275 queue ! (
272276 output,
277+ style:: Print ( "\n " ) ,
273278 style:: SetForegroundColor ( Color :: Blue ) ,
274- style:: Print ( format!( "{i} " ) ) ,
279+ style:: Print ( format!( "[^ {i}]: " ) ) ,
275280 style:: SetForegroundColor ( Color :: DarkGrey ) ,
276281 style:: Print ( format!( "{citation}\n " ) ) ,
277282 style:: SetForegroundColor ( Color :: Reset )
278283 ) ?;
279284 }
280285
281- if !state. citations . is_empty ( ) {
282- execute ! ( output, Print ( "\n " ) ) ?;
283- }
284- }
285-
286- if let ( Some ( conversation_id) , Some ( message_id) ) = ( & conversation_id, & message_id) {
287- fig_telemetry:: send_chat_added_message ( conversation_id. to_owned ( ) , message_id. to_owned ( ) ) . await ;
286+ execute ! ( output, style:: Print ( "\n " ) ) ?;
288287 }
289288
290289 break ;
@@ -313,6 +312,64 @@ You can include additional context by adding the following to your prompt:
313312 } ,
314313 }
315314 }
315+ } else {
316+ break Ok ( ( ) ) ;
316317 }
317318 }
318319}
320+
321+ #[ cfg( test) ]
322+ mod test {
323+ use fig_api_client:: model:: ChatResponseStream ;
324+
325+ use super :: * ;
326+
327+ fn mock_client ( s : impl IntoIterator < Item = & ' static str > ) -> StreamingClient {
328+ StreamingClient :: mock (
329+ s. into_iter ( )
330+ . map ( |s| ChatResponseStream :: AssistantResponseEvent { content : s. into ( ) } )
331+ . collect ( ) ,
332+ )
333+ }
334+
335+ #[ tokio:: test]
336+ async fn try_chat_non_interactive ( ) {
337+ let client = mock_client ( [ "Hello," , " World" , "!" ] ) ;
338+ let mut output = Vec :: new ( ) ;
339+ try_chat ( & mut output, "test" . into ( ) , false , & client) . await . unwrap ( ) ;
340+
341+ let mut expected = Vec :: new ( ) ;
342+ execute ! (
343+ expected,
344+ style:: Print ( "Hello, World!" ) ,
345+ style:: ResetColor ,
346+ style:: SetAttribute ( Attribute :: Reset ) ,
347+ style:: Print ( "\n " )
348+ )
349+ . unwrap ( ) ;
350+
351+ assert_eq ! ( expected, output) ;
352+ }
353+
354+ #[ tokio:: test]
355+ async fn try_chat_non_interactive_citation ( ) {
356+ let client = mock_client ( [ "Citation [[1]](https://aws.com)" ] ) ;
357+ let mut output = Vec :: new ( ) ;
358+ try_chat ( & mut output, "test" . into ( ) , false , & client) . await . unwrap ( ) ;
359+
360+ let mut expected = Vec :: new ( ) ;
361+ execute ! (
362+ expected,
363+ style:: Print ( "Citation " ) ,
364+ style:: SetForegroundColor ( Color :: Blue ) ,
365+ style:: Print ( "[^1]" ) ,
366+ style:: ResetColor ,
367+ style:: ResetColor ,
368+ style:: SetAttribute ( Attribute :: Reset ) ,
369+ style:: Print ( "\n " )
370+ )
371+ . unwrap ( ) ;
372+
373+ assert_eq ! ( expected, output) ;
374+ }
375+ }
0 commit comments