summaryrefslogtreecommitdiffstats
path: root/src/render
diff options
context:
space:
mode:
authorsigoden <sigoden@gmail.com>2023-10-31 18:34:38 +0800
committerGitHub <noreply@github.com>2023-10-31 18:34:38 +0800
commit64202758ec8693dd18951c554728bb5c6b7e5eeb (patch)
tree9adc7291e704b44bd39a8b0c42892806bd68cfa1 /src/render
parentbd3fc8e657777e58976aa06756ccc0a151bd0ea3 (diff)
downloadaichat-64202758ec8693dd18951c554728bb5c6b7e5eeb.tar.gz
chore: move split_line_* from repl/cmd.rs to utils/ (#190)
Diffstat (limited to 'src/render')
-rw-r--r--src/render/cmd.rs218
-rw-r--r--src/render/repl.rs3
2 files changed, 3 insertions, 218 deletions
diff --git a/src/render/cmd.rs b/src/render/cmd.rs
index 57b6256..115170b 100644
--- a/src/render/cmd.rs
+++ b/src/render/cmd.rs
@@ -2,6 +2,7 @@ use super::MarkdownRender;
use crate::print_now;
use crate::repl::{ReplyStreamEvent, SharedAbortSignal};
+use crate::utils::{spaces, split_line_sematic, split_line_tail};
use anyhow::Result;
use crossbeam::channel::Receiver;
@@ -65,220 +66,3 @@ pub fn cmd_render_stream(
}
Ok(())
}
-
-fn split_line_sematic(text: &str) -> Option<(String, String)> {
- let mut balance: Vec<Kind> = Vec::new();
- let chars: Vec<char> = text.chars().collect();
- let mut index = 0;
- let len = chars.len();
- while index < len - 1 {
- let ch = chars[index];
- if balance.is_empty()
- && ((matches!(ch, ',' | '.' | ';') && chars[index + 1].is_whitespace())
- || matches!(ch, ',' | '。' | ';'))
- {
- let (output, remain) = chars.split_at(index + 1);
- return Some((output.iter().collect(), remain.iter().collect()));
- }
- if index + 2 < len && do_balance(&mut balance, &chars[index..=index + 2]) {
- index += 3;
- continue;
- }
- if do_balance(&mut balance, &chars[index..=index + 1]) {
- index += 2;
- continue;
- }
- do_balance(&mut balance, &chars[index..=index]);
- index += 1;
- }
-
- None
-}
-
-pub(crate) fn split_line_tail(text: &str) -> (&str, &str) {
- if let Some((head, tail)) = text.rsplit_once('\n') {
- (head, tail)
- } else {
- ("", text)
- }
-}
-
-fn spaces(n: usize) -> String {
- " ".repeat(n)
-}
-
-#[derive(Debug, Clone, Copy, Eq, PartialEq)]
-enum Kind {
- ParentheseStart,
- ParentheseEnd,
- BracketStart,
- BracketEnd,
- Asterisk,
- Asterisk2,
- SingleQuota,
- DoubleQuota,
- Tilde,
- Tilde2,
- Backtick,
- Backtick3,
-}
-
-impl Kind {
- fn from_chars(chars: &[char]) -> Option<Self> {
- let kind = match chars.len() {
- 1 => match chars[0] {
- '(' => Self::ParentheseStart,
- ')' => Self::ParentheseEnd,
- '[' => Self::BracketStart,
- ']' => Self::BracketEnd,
- '*' => Self::Asterisk,
- '\'' => Self::SingleQuota,
- '"' => Self::DoubleQuota,
- '~' => Self::Tilde,
- '`' => Self::Backtick,
- _ => return None,
- },
- 2 if chars[0] == chars[1] => match chars[0] {
- '*' => Self::Asterisk2,
- '~' => Self::Tilde2,
- _ => return None,
- },
- 3 => {
- if chars == ['`', '`', '`'] {
- Self::Backtick3
- } else {
- return None;
- }
- }
- _ => return None,
- };
- Some(kind)
- }
-}
-
-fn do_balance(balance: &mut Vec<Kind>, chars: &[char]) -> bool {
- Kind::from_chars(chars).map_or(false, |kind| {
- let last = balance.last();
- match (kind, last) {
- (Kind::ParentheseEnd, Some(&Kind::ParentheseStart))
- | (Kind::BracketEnd, Some(&Kind::BracketStart))
- | (Kind::Asterisk, Some(&Kind::Asterisk))
- | (Kind::Asterisk2, Some(&Kind::Asterisk2))
- | (Kind::SingleQuota, Some(&Kind::SingleQuota))
- | (Kind::DoubleQuota, Some(&Kind::DoubleQuota))
- | (Kind::Tilde, Some(&Kind::Tilde))
- | (Kind::Tilde2, Some(&Kind::Tilde2))
- | (Kind::Backtick, Some(&Kind::Backtick))
- | (Kind::Backtick3, Some(&Kind::Backtick3)) => {
- balance.pop();
- true
- }
- (
- Kind::ParentheseStart
- | Kind::BracketStart
- | Kind::Asterisk
- | Kind::Asterisk2
- | Kind::SingleQuota
- | Kind::DoubleQuota
- | Kind::Tilde
- | Kind::Tilde2
- | Kind::Backtick
- | Kind::Backtick3,
- _,
- ) => {
- balance.push(kind);
- true
- }
- _ => false,
- }
- })
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- macro_rules! assert_split_line {
- ($a:literal, $b:literal, true) => {
- assert_eq!(
- split_line_sematic(&format!("{}{}", $a, $b)),
- Some(($a.into(), $b.into()))
- );
- };
- ($a:literal, $b:literal, false) => {
- assert_eq!(split_line_sematic(&format!("{}{}", $a, $b)), None);
- };
- }
-
- #[test]
- fn test_split_line() {
- assert_split_line!(
- "Lorem ipsum dolor sit amet,",
- " consectetur adipiscing elit.",
- true
- );
- assert_split_line!(
- "Lorem ipsum dolor sit amet.",
- " consectetur adipiscing elit.",
- true
- );
- assert_split_line!("黃更室幼許刀知,", "波食小午足田世根候法。", true);
- assert_split_line!("黃更室幼許刀知。", "波食小午足田世根候法。", true);
- assert_split_line!("黃更室幼許刀知;", "波食小午足田世根候法。", true);
- assert_split_line!(
- "Lorem ipsum (dolor sit amet).",
- " consectetur adipiscing elit.",
- true
- );
- assert_split_line!(
- "Lorem ipsum dolor sit `amet,",
- " consectetur` adipiscing elit.",
- false
- );
- assert_split_line!(
- "Lorem ipsum dolor sit ```amet,",
- " consectetur``` adipiscing elit.",
- false
- );
- assert_split_line!(
- "Lorem ipsum dolor sit *amet,",
- " consectetur* adipiscing elit.",
- false
- );
- assert_split_line!(
- "Lorem ipsum dolor sit **amet,",
- " consectetur** adipiscing elit.",
- false
- );
- assert_split_line!(
- "Lorem ipsum dolor sit ~amet,",
- " consectetur~ adipiscing elit.",
- false
- );
- assert_split_line!(
- "Lorem ipsum dolor sit ~~amet,",
- " consectetur~~ adipiscing elit.",
- false
- );
- assert_split_line!(
- "Lorem ipsum dolor sit ``amet,",
- " consectetur`` adipiscing elit.",
- true
- );
- assert_split_line!(
- "Lorem ipsum dolor sit \"amet,",
- " consectetur\" adipiscing elit.",
- false
- );
- assert_split_line!(
- "Lorem ipsum dolor sit 'amet,",
- " consectetur' adipiscing elit.",
- false
- );
- assert_split_line!(
- "Lorem ipsum dolor sit amet.",
- "consectetur adipiscing elit.",
- false
- );
- }
-}
diff --git a/src/render/repl.rs b/src/render/repl.rs
index d55c1c1..845a5e3 100644
--- a/src/render/repl.rs
+++ b/src/render/repl.rs
@@ -1,6 +1,7 @@
-use super::{cmd::split_line_tail, MarkdownRender};
+use super::MarkdownRender;
use crate::repl::{ReplyStreamEvent, SharedAbortSignal};
+use crate::utils::split_line_tail;
use anyhow::Result;
use crossbeam::channel::Receiver;