Repository: microsoft/edit
File: src/buffer/mod.rs (Rust)

Code before:
                                );
                                let tab_size = self.tab_size - (cursor_tab.column % self.tab_size);
                                line.push_str(&TAB_WHITESPACE[..tab_size as usize]);

                                // Since we know that we just aligned ourselves to the next tab stop,
                                // we can trivially process any successive tabs.
                                while chunk_off < chunk.len() && chunk[chunk_off] == b'\t' {
                                    line.push_str(&TAB_WHITESPACE[..self.tab_size as usize]);
                                    chunk_off += 1;
                                }
                                continue;
                            }

                            visualizer_buf[2] = if ch == 0x7F {
                                0xA1 // U+2421
                            } else {
                                0x80 | ch // 0x00..=0x1F => U+2400..=U+241F
                            };
                            // Our manually constructed UTF8 is never going to be invalid. Trust.
                            line.push_str(unsafe { str::from_utf8_unchecked(&visualizer_buf) });

                            let visualizer_rect = {
                                let cursor_visualizer = self.cursor_move_to_offset_internal(
                                    cursor_beg,
                                    global_off + chunk_off - 1,
                                );
                                let left = destination.left
                                    + self.margin_width
                                    + cursor_visualizer.visual_pos.x
                                    - origin.x;
                                let top =
                                    destination.top + cursor_visualizer.visual_pos.y - origin.y;
                                Rect { left, top, right: left + 1, bottom: top + 1 }
                            };

                            let bg = fb.indexed(IndexedColor::Yellow);
                            let fg = fb.contrasted(bg);
                            fb.blend_bg(visualizer_rect, bg);
                            fb.blend_fg(visualizer_rect, fg);
                        }
                    }

                    global_off += chunk.len();
                }

                visual_pos_x_max = visual_pos_x_max.max(cursor_end.visual_pos.x);
            }

            fb.replace_text(destination.top + y, destination.left, destination.right, &line);

            // Draw the selection on this line, if any.

Review comment (suggestion):
This is accidentally quadratic, because it always starts from `cursor_beg` again. I'd do either of these two things:
* We split the while loop into two, where one _only_ handles tabs and the other handles all other control chars. Then we only do 2 (?) `cursor_move_to_offset_internal` calls to get the start and end of the control char range and highlight that entire block.
* We copy `cursor_beg` into a `mut cursor_visualizer` variable above and then use it to iterate over these chars via `cursor_visualizer = self.cursor_move_to_offset_internal(cursor_visualizer, ...)`.

I think the first option would be more ideal, but I think we can just go with the 2nd option for now.

Code after:
                                    global_off + chunk_off - 1,
                                );
                                let tab_size = self.tab_size - (cursor_tab.column % self.tab_size);
                                line.push_str(&TAB_WHITESPACE[..tab_size as usize]);

                                // Since we know that we just aligned ourselves to the next tab stop,
                                // we can trivially process any successive tabs.
                                while chunk_off < chunk.len() && chunk[chunk_off] == b'\t' {
                                    line.push_str(&TAB_WHITESPACE[..self.tab_size as usize]);
                                    chunk_off += 1;
                                }
                                continue;
                            }

                            visualizer_buf[2] = if ch == 0x7F {
                                0xA1 // U+2421
                            } else {
                                0x80 | ch // 0x00..=0x1F => U+2400..=U+241F
                            };
                            // Our manually constructed UTF8 is never going to be invalid. Trust.
                            line.push_str(unsafe { str::from_utf8_unchecked(&visualizer_buf) });

                            cursor_visualizer = self.cursor_move_to_offset_internal(
                                cursor_visualizer,
                                global_off + chunk_off - 1,
                            );
                            let visualizer_rect = {
                                let left = destination.left
                                    + self.margin_width
                                    + cursor_visualizer.visual_pos.x
                                    - origin.x;
                                let top =
                                    destination.top + cursor_visualizer.visual_pos.y - origin.y;
                                Rect { left, top, right: left + 1, bottom: top + 1 }
                            };

                            let bg = fb.indexed(IndexedColor::Yellow);
                            let fg = fb.contrasted(bg);
                            fb.blend_bg(visualizer_rect, bg);
                            fb.blend_fg(visualizer_rect, fg);
                        }
                    }

                    global_off += chunk.len();
                }

                visual_pos_x_max = visual_pos_x_max.max(cursor_end.visual_pos.x);
            }

            fb.replace_text(destination.top + y, destination.left, destination.right, &line);