From 0fa1ae215ac1434591968818f8f75ad5b35d52ec Mon Sep 17 00:00:00 2001 From: sigoden Date: Sat, 4 Mar 2023 09:02:57 +0800 Subject: feat: support highlight reply markdown (#3) * feat: support highlight reply markdown * migrate markdown highlighter from termimad to mdcat * optimize render No need to clear screen when there is no newline in reply token * handle ctrl-c when rendering stream * update readme * fix ctrlc don't abort acquire_stream when establish connection * ensure render_stream's dtect_ctrlc is exit before next readline This will make reedline don't throw err 'The cursor position could not be read within a normal duration' --- src/client.rs | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) (limited to 'src/client.rs') diff --git a/src/client.rs b/src/client.rs index 3f42eb4..1f42346 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,4 +1,5 @@ use crate::config::Config; +use crate::repl::ReplyReceiver; use anyhow::{anyhow, Result}; use eventsource_stream::Eventsource; @@ -8,6 +9,7 @@ use serde_json::{json, Value}; use std::sync::atomic::{AtomicBool, Ordering}; use std::{sync::Arc, time::Duration}; use tokio::runtime::Runtime; +use tokio::time::sleep; const CONNECT_TIMEOUT: Duration = Duration::from_secs(10); const API_URL: &str = "https://api.openai.com/v1/chat/completions"; @@ -45,22 +47,31 @@ impl ChatGptClient { .block_on(async { self.acquire_inner(input, prompt).await }) } - pub fn acquire_stream( + pub fn acquire_stream( &self, input: &str, prompt: Option, - output: &mut String, - handler: T, + receiver: &mut ReplyReceiver, ctrlc: Arc, - ) -> Result<()> - where - T: FnOnce(&mut String, &str) + Copy, - { + ) -> Result<()> { + async fn watch_ctrlc(ctrlc: Arc) { + loop { + if ctrlc.load(Ordering::SeqCst) { + break; + } + sleep(Duration::from_millis(100)).await; + } + } self.runtime.block_on(async { tokio::select! { - ret = self.acquire_stream_inner(input, prompt, handler, output) => { + ret = self.acquire_stream_inner(input, prompt, receiver) => { + receiver.done(); ret } + _ = watch_ctrlc(ctrlc.clone()) => { + receiver.done(); + Ok(()) + }, _ = tokio::signal::ctrl_c() => { ctrlc.store(true, Ordering::SeqCst); Ok(()) @@ -85,19 +96,15 @@ impl ChatGptClient { Ok(output.to_string()) } - async fn acquire_stream_inner( + async fn acquire_stream_inner( &self, content: &str, prompt: Option, - handler: T, - output: &mut String, - ) -> Result<()> - where - T: FnOnce(&mut String, &str) + Copy, - { + receiver: &mut ReplyReceiver, + ) -> Result<()> { let content = combine(content, prompt); if self.config.dry_run { - handler(output, &content); + receiver.text(&content); return Ok(()); } let builder = self.request_builder(&content, true); @@ -121,7 +128,7 @@ impl ChatGptClient { continue; } } - handler(output, text); + receiver.text(text); } } -- cgit v1.2.3