Repository: helix-editor/helix
File: helix-term/src/commands/lsp.rs (Rust)

Code before:
                    {
                        Some(pos) => pos,
                        // Skip inlay hints that have no "real" position
                        None => continue,
                    };

                let mut label = match hint.label {
                    lsp::InlayHintLabel::String(s) => s,
                    lsp::InlayHintLabel::LabelParts(parts) => parts
                        .into_iter()
                        .map(|p| p.value)
                        .collect::<Vec<_>>()
                        .join(""),
                };
                // Truncate the hint if too long
                if let Some(limit) = inlay_hints_length_limit {
                    // Limit on displayed width
                    use helix_core::unicode::width::{UnicodeWidthChar, UnicodeWidthStr};

                    let width = label.width();
                    let limit = limit.get().into();
                    if width > limit {
                        // It's impossible for LSP to send a string of control chars, so 0 is fine
                        let mut floor_boundary = 0;
                        let mut acc = 0;
                        for (i, c) in label.char_indices() {
                            // Control chars are 0-width
                            acc += c.width().unwrap_or(0);

                            if acc > limit {
                                floor_boundary = i;
                                break;
                            }
                        }

                        label.truncate(floor_boundary);
                        label.push('…');
                    }
                }

                let inlay_hints_vec = match hint.kind {
                    Some(lsp::InlayHintKind::TYPE) => &mut type_inlay_hints,
                    Some(lsp::InlayHintKind::PARAMETER) => &mut parameter_inlay_hints,
                    // We can't warn on unknown kind here since LSPs are free to set it or not, for
                    // example Rust Analyzer does not: every kind will be `None`.
                    _ => &mut other_inlay_hints,
                };

                if let Some(true) = hint.padding_left {
                    padding_before_inlay_hints.push(InlineAnnotation::new(char_idx, " "));
                }

Review comment (suggestion):
Instead of character indices we should look at grapheme indices using `helix_core::unicode::segmentation::UnicodeSegmentation`: if we truncate to a character boundary we could cut into a grapheme cluster, turning 🏴‍☠️ into 🏴 for example. (🏴‍☠️ is 🏴 with a zero-width joiner character and ☠️.)

Code after:
                    {
                        Some(pos) => pos,
                        // Skip inlay hints that have no "real" position
                        None => continue,
                    };

                let mut label = match hint.label {
                    lsp::InlayHintLabel::String(s) => s,
                    lsp::InlayHintLabel::LabelParts(parts) => parts
                        .into_iter()
                        .map(|p| p.value)
                        .collect::<Vec<_>>()
                        .join(""),
                };
                // Truncate the hint if too long
                if let Some(limit) = inlay_hints_length_limit {
                    // Limit on displayed width
                    use helix_core::unicode::{
                        segmentation::UnicodeSegmentation, width::UnicodeWidthStr,
                    };

                    let width = label.width();
                    let limit = limit.get().into();
                    if width > limit {
                        let mut floor_boundary = 0;
                        let mut acc = 0;
                        for (i, grapheme_cluster) in label.grapheme_indices(true) {
                            acc += grapheme_cluster.width();

                            if acc > limit {
                                floor_boundary = i;
                                break;
                            }
                        }

                        label.truncate(floor_boundary);
                        label.push('…');
                    }
                }

                let inlay_hints_vec = match hint.kind {
                    Some(lsp::InlayHintKind::TYPE) => &mut type_inlay_hints,
                    Some(lsp::InlayHintKind::PARAMETER) => &mut parameter_inlay_hints,
                    // We can't warn on unknown kind here since LSPs are free to set it or not, for
                    // example Rust Analyzer does not: every kind will be `None`.
                    _ => &mut other_inlay_hints,
                };

                if let Some(true) = hint.padding_left {
                    padding_before_inlay_hints.push(InlineAnnotation::new(char_idx, " "));
                }