LCOV - code coverage report
Current view: top level - dashql-core-shell/src - unicode.rs Coverage Total Hit
Test: DashQL 6e74948 Lines: 100.0 % 27 27
Test Date: 2026-11-06 17:46:50

            Line data    Source code
       1              : //! Text measurement of the shell: extended grapheme clusters (`unicode-segmentation`) and terminal column widths
       2              : //! (`unicode-width`).
       3              : //!
       4              : //! Cursors move by grapheme clusters, and a cluster occupies the columns of `unicode-width`. A cluster that starts
       5              : //! with a control character (like `"\r\n"`) does not advance the cursor and has width 0. Texts that are not valid
       6              : //! UTF-8 are measured byte by byte: every byte is a cluster of width 1.
       7              : 
       8              : use unicode_segmentation::UnicodeSegmentation;
       9              : use unicode_width::UnicodeWidthStr;
      10              : 
      11              : /// Is the text valid UTF-8?
      12              : #[inline]
      13           86 : pub fn is_valid(text: &[u8]) -> bool {
      14           86 :     std::str::from_utf8(text).is_ok()
      15           86 : }
      16              : 
      17              : /// The terminal columns of one grapheme cluster
      18       111644 : fn cluster_width(cluster: &str) -> usize {
      19       111644 :     if cluster.starts_with(char::is_control) {
      20            1 :         0
      21              :     } else {
      22       111643 :         cluster.width()
      23              :     }
      24       111644 : }
      25              : 
      26              : /// The terminal columns of a text: the sum of its cluster widths, or its byte length if it is not valid UTF-8
      27        61601 : pub fn display_width(text: &[u8]) -> usize {
      28        61601 :     match std::str::from_utf8(text) {
      29        61600 :         Ok(text) => text.graphemes(true).map(cluster_width).sum(),
      30            1 :         Err(_) => text.len(),
      31              :     }
      32        61601 : }
      33              : 
      34              : /// The grapheme clusters of a text as (begin, end) byte ranges, one byte per cluster if it is not valid UTF-8
      35          202 : pub fn graphemes(text: &[u8]) -> Vec<(usize, usize)> {
      36          202 :     match std::str::from_utf8(text) {
      37         1503 :         Ok(text) => text.grapheme_indices(true).map(|(begin, cluster)| (begin, begin + cluster.len())).collect(),
      38            2 :         Err(_) => (0..text.len()).map(|begin| (begin, begin + 1)).collect(),
      39              :     }
      40          202 : }
      41              : 
      42              : /// The end of the grapheme cluster that starts at the byte offset `offset`, the text length at the end
      43        50012 : pub fn next_grapheme(text: &str, offset: usize) -> usize {
      44        50012 :     offset + text[offset..].graphemes(true).next().map_or(0, str::len)
      45        50012 : }
      46              : 
      47              : /// The begin of the grapheme cluster that ends at the byte offset `offset`, 0 at the begin
      48           64 : pub fn previous_grapheme(text: &str, offset: usize) -> usize {
      49           64 :     offset - text[..offset].graphemes(true).next_back().map_or(0, str::len)
      50           64 : }
      51              : 
      52              : /// The begin offsets of the grapheme clusters of a text, followed by the text length
      53         2301 : pub fn grapheme_boundaries(text: &str) -> Vec<usize> {
      54         2301 :     text.grapheme_indices(true).map(|(begin, _)| begin).chain(std::iter::once(text.len())).collect()
      55         2301 : }
        

Generated by: LCOV version 2.0-1