fix clippy

This commit is contained in:
2026-06-03 20:16:30 +07:00
parent b057cee65b
commit 87e0003543
11 changed files with 115 additions and 455 deletions

View File

@@ -173,3 +173,12 @@ where
}
}
}
impl<I> Default for History<I>
where
I: HistoryItem,
{
fn default() -> Self {
Self::new()
}
}

View File

@@ -149,9 +149,7 @@ impl Element for EditorScrollbar {
cx: &mut App,
) -> Self::PrepaintState {
let state = self.state.read(cx);
let Some(snapshot) = state.editor_scrollbar_snapshot.get() else {
return None;
};
let snapshot = state.editor_scrollbar_snapshot.get()?;
let scroll_handle = state.scroll_handle.clone();
if scroll_handle.offset() != snapshot.cursor_scroll_offset {
@@ -492,7 +490,7 @@ impl TextElement {
bounds.size.height,
);
bounds.origin = bounds.origin + scroll_offset;
bounds.origin += scroll_offset;
(cursor_bounds, scroll_offset, current_row)
}
@@ -622,17 +620,17 @@ impl TextElement {
let mut rev_line_corners = line_corners.iter().rev().peekable();
while let Some(corners) = rev_line_corners.next() {
points.push(corners.top_left);
if let Some(next) = rev_line_corners.peek() {
if next.top_left.x > corners.top_left.x {
points.push(point(next.top_left.x, corners.top_left.y));
}
if let Some(next) = rev_line_corners.peek()
&& next.top_left.x > corners.top_left.x
{
points.push(point(next.top_left.x, corners.top_left.y));
}
}
// print_points_as_svg_path(&line_corners, &points);
let path_origin = bounds.origin + point(line_number_width, px(0.));
let first_p = *points.get(0).unwrap();
let first_p = *points.first().unwrap();
let mut builder = gpui::PathBuilder::fill();
builder.move_to(path_origin + first_p);
for p in points.iter().skip(1) {
@@ -690,10 +688,10 @@ impl TextElement {
}
let mut selected_range = state.selected_range;
if let Some(ime_marked_range) = &state.ime_marked_range {
if !ime_marked_range.is_empty() {
selected_range = (ime_marked_range.end..ime_marked_range.end).into();
}
if let Some(ime_marked_range) = &state.ime_marked_range
&& !ime_marked_range.is_empty()
{
selected_range = (ime_marked_range.end..ime_marked_range.end).into();
}
if selected_range.is_empty() {
return None;
@@ -713,7 +711,7 @@ impl TextElement {
let range = start_ix.max(last_layout.visible_range_offset.start)
..end_ix.min(last_layout.visible_range_offset.end);
Self::layout_match_range(range, &last_layout, bounds)
Self::layout_match_range(range, last_layout, bounds)
}
/// Calculate the visible range of lines in the viewport.
@@ -1065,7 +1063,7 @@ impl TextElement {
let shaped_line = window.text_system().shape_line(
display_text.to_string().into(),
font_size,
&runs,
runs,
None,
);
@@ -1114,7 +1112,7 @@ impl TextElement {
let mut wrapped_lines = SmallVec::with_capacity(1);
for range in &line_item.wrapped_lines {
let line_runs = runs_for_range(runs, run_offset, &range);
let line_runs = runs_for_range(runs, run_offset, range);
let line_runs = if bg_segments.is_empty() {
line_runs
} else {
@@ -1260,10 +1258,7 @@ impl IntoElement for TextElement {
/// A debug function to print points as SVG path.
#[allow(unused)]
fn print_points_as_svg_path(
line_corners: &Vec<gpui::Corners<Pixels>>,
points: &Vec<Point<Pixels>>,
) {
fn print_points_as_svg_path(line_corners: &Vec<gpui::Corners<Pixels>>, points: Vec<Point<Pixels>>) {
for corners in line_corners {
println!(
"tl: ({}, {}), tr: ({}, {}), bl: ({}, {}), br: ({}, {})",
@@ -1278,7 +1273,7 @@ fn print_points_as_svg_path(
);
}
if points.len() > 0 {
if !points.is_empty() {
println!(
"M{},{}",
points[0].x.as_f32() as i32,
@@ -1353,7 +1348,7 @@ impl Element for TextElement {
let line_height = window.line_height();
let (visible_range, visible_buffer_lines, visible_top) =
self.calculate_visible_range(&state, line_height, bounds.size.height);
self.calculate_visible_range(state, line_height, bounds.size.height);
let visible_start_offset = state.text.line_start_offset(visible_range.start);
let visible_end_offset = state
.text
@@ -1387,7 +1382,7 @@ impl Element for TextElement {
// Calculate the width of the line numbers
let (line_number_width, line_number_len) =
Self::layout_line_numbers(&state, &text, text_size, &text_style, window);
Self::layout_line_numbers(state, &text, text_size, &text_style, window);
let mut bounds = bounds;
let wrap_width = if multi_line && state.soft_wrap {
@@ -1460,14 +1455,14 @@ impl Element for TextElement {
runs.extend(highlight_styles.iter().map(|(range, style)| {
let mut run = text_style.clone().highlight(*style).to_run(range.len());
if let Some(ime_marked_range) = &state.ime_marked_range {
if range.start >= ime_marked_range.start
&& range.end <= ime_marked_range.end
{
run.color = marked_run.color;
run.strikethrough = marked_run.strikethrough;
run.underline = marked_run.underline;
}
if let Some(ime_marked_range) = &state.ime_marked_range
&& range.start >= ime_marked_range.start
&& range.end <= ime_marked_range.end
{
run.color = marked_run.color;
run.strikethrough = marked_run.strikethrough;
run.underline = marked_run.underline;
}
run
@@ -1505,11 +1500,11 @@ impl Element for TextElement {
// Create shaped lines for whitespace indicators before layout
let whitespace_indicators =
Self::layout_whitespace_indicators(&state, text_size, &text_style, window, cx);
Self::layout_whitespace_indicators(state, text_size, &text_style, window, cx);
let lines = Self::layout_lines(
&state,
&display_text,
state,
display_text,
&last_layout,
text_size,
&runs,
@@ -1621,9 +1616,9 @@ impl Element for TextElement {
self.layout_cursor(&last_layout, &mut bounds, scroll_size, window, cx);
last_layout.cursor_bounds = cursor_bounds;
let search_match_paths = self.layout_search_matches(&last_layout, &mut bounds, cx);
let search_match_paths = self.layout_search_matches(&last_layout, &bounds, cx);
let selection_path = self.layout_selections(&last_layout, &mut bounds, window, cx);
let hover_highlight_path = self.layout_hover_highlight(&last_layout, &mut bounds, cx);
let hover_highlight_path = self.layout_hover_highlight(&last_layout, &bounds, cx);
let document_color_paths =
self.layout_document_colors(&document_colors, &last_layout, &bounds, cx);
@@ -1666,7 +1661,7 @@ impl Element for TextElement {
sub_lines.push(
window
.text_system()
.shape_line(line_no, text_size, &runs, None),
.shape_line(line_no, text_size, runs, None),
);
for _ in 0..line.wrapped_lines.len().saturating_sub(1) {
sub_lines.push(ShapedLine::default());
@@ -1847,7 +1842,7 @@ impl Element for TextElement {
);
// Paint the actual line
_ = line.paint(
line.paint(
p,
line_height,
text_align,
@@ -1893,10 +1888,11 @@ impl Element for TextElement {
}
// Paint blinking cursor
if focused && show_cursor {
if let Some(cursor_bounds) = prepaint.cursor_bounds_with_scroll() {
window.paint_quad(fill(cursor_bounds, cx.theme().cursor));
}
if focused
&& show_cursor
&& let Some(cursor_bounds) = prepaint.cursor_bounds_with_scroll()
{
window.paint_quad(fill(cursor_bounds, cx.theme().cursor));
}
// Paint line numbers
@@ -1956,26 +1952,24 @@ impl Element for TextElement {
});
if let Some(hitbox) = prepaint.hover_definition_hitbox.as_ref() {
window.set_cursor_style(gpui::CursorStyle::PointingHand, &hitbox);
window.set_cursor_style(gpui::CursorStyle::PointingHand, hitbox);
}
// Paint inline completion first line suffix (after cursor on same line)
if focused {
if let Some(first_line) = &prepaint.ghost_first_line {
if let (Some(cursor_bounds), Some(cursor_row_y)) =
(prepaint.cursor_bounds_with_scroll(), cursor_row_y)
{
let first_line_x = cursor_bounds.origin.x + cursor_bounds.size.width;
let p = point(first_line_x, cursor_row_y);
if focused
&& let Some(first_line) = &prepaint.ghost_first_line
&& let (Some(cursor_bounds), Some(cursor_row_y)) =
(prepaint.cursor_bounds_with_scroll(), cursor_row_y)
{
let first_line_x = cursor_bounds.origin.x + cursor_bounds.size.width;
let p = point(first_line_x, cursor_row_y);
// Paint background to cover any existing text
let bg_bounds = Bounds::new(p, size(first_line.width + px(4.), line_height));
window.paint_quad(fill(bg_bounds, cx.theme().surface_background));
// Paint background to cover any existing text
let bg_bounds = Bounds::new(p, size(first_line.width + px(4.), line_height));
window.paint_quad(fill(bg_bounds, cx.theme().surface_background));
// Paint first line completion text
_ = first_line.paint(p, line_height, text_align, None, window, cx);
}
}
// Paint first line completion text
_ = first_line.paint(p, line_height, text_align, None, window, cx);
}
self.paint_mouse_listeners(window, cx);

View File

@@ -26,7 +26,7 @@ impl Default for TabSize {
}
impl TabSize {
pub(super) fn to_string(&self) -> SharedString {
pub(super) fn to_string(self) -> SharedString {
if self.hard_tabs {
"\t".into()
} else {
@@ -146,7 +146,7 @@ impl TextElement {
builder.line_to(point(pos.x, pos.y + line_height));
current_indents.push(pos.x);
}
} else if last_indents.len() > 0 {
} else if !last_indents.is_empty() {
for x in &last_indents {
let pos = point(*x, offset_y);
builder.move_to(pos);

View File

@@ -1,10 +1,8 @@
use std::rc::Rc;
use gpui::prelude::FluentBuilder as _;
use gpui::{
AnyElement, App, Context, DefiniteLength, Edges, EdgesRefinement, Entity, Hsla,
InteractiveElement as _, IntoElement, MouseButton, ParentElement as _, Rems, RenderOnce,
StyleRefinement, Styled, TextAlign, Window, div, px, relative,
AnyElement, App, DefiniteLength, Edges, EdgesRefinement, Entity, Hsla, InteractiveElement as _,
IntoElement, MouseButton, ParentElement as _, Rems, RenderOnce, StyleRefinement, Styled,
TextAlign, Window, div, px, relative,
};
use theme::ActiveTheme;
@@ -13,7 +11,6 @@ use super::element::EditorScrollbar;
use crate::button::{Button, ButtonVariants as _};
use crate::indicator::Indicator;
use crate::input::clear_button;
use crate::menu::PopupMenu;
use crate::{IconName, Selectable, Sizable, Size, StyleSized, StyledExt, h_flex, v_flex};
/// Returns `(background, foreground)` colors for input-like components.
@@ -42,12 +39,6 @@ pub struct Input {
focus_bordered: bool,
tab_index: isize,
selected: bool,
/// An optional context menu builder to allow a custom context menu on the input.
///
/// If set, this will override the built-in context menu.
context_menu_builder:
Option<Rc<dyn Fn(PopupMenu, &mut Window, &mut Context<PopupMenu>) -> PopupMenu>>,
}
impl Sizable for Input {
@@ -86,7 +77,6 @@ impl Input {
focus_bordered: true,
tab_index: 0,
selected: false,
context_menu_builder: None,
}
}
@@ -154,15 +144,6 @@ impl Input {
self
}
/// Sets the context menu for the input.
pub fn context_menu(
mut self,
f: impl Fn(PopupMenu, &mut Window, &mut Context<PopupMenu>) -> PopupMenu + 'static,
) -> Self {
self.context_menu_builder = Some(Rc::new(f));
self
}
fn render_toggle_mask_button(state: &Entity<InputState>, cx: &App) -> impl IntoElement {
let _masked = state.read(cx).masked;
Button::new("toggle-mask")
@@ -234,10 +215,8 @@ impl RenderOnce for Input {
let text_align = self.style.text.text_align.unwrap_or(TextAlign::Left);
self.state.update(cx, |state, _| {
state.context_menu_builder = self.context_menu_builder.clone();
state.disabled = self.disabled;
state.size = self.size;
// Only for single line mode
if state.mode.is_single_line() {
state.text_align = text_align;
@@ -342,7 +321,6 @@ impl RenderOnce for Input {
MouseButton::Right,
window.listener_for(&self.state, InputState::on_mouse_up),
)
.on_mouse_move(window.listener_for(&self.state, InputState::on_mouse_move))
.on_scroll_wheel(window.listener_for(&self.state, InputState::on_scroll_wheel))
.size_full()
.line_height(LINE_HEIGHT)
@@ -372,7 +350,7 @@ impl RenderOnce for Input {
.children(prefix)
.when(state.mode.is_multi_line(), |mut this| {
let paddings = this.style().padding.clone();
this.child(Self::render_editor(paddings, &self.state, &state, window))
this.child(Self::render_editor(paddings, &self.state, state, window))
})
.when(!state.mode.is_multi_line(), |this| {
this.child(self.state.clone())

View File

@@ -225,13 +225,12 @@ impl MaskPattern {
}
// check if the fraction part is valid
if let Some(frac) = frac_part {
if !frac
if let Some(frac) = frac_part
&& !frac
.chars()
.all(|ch| ch.is_ascii_digit() || Some(ch) == *separator)
{
return false;
}
{
return false;
}
true
@@ -255,10 +254,10 @@ impl MaskPattern {
if token.is_sep() {
// If next token is match, it's valid
if let Some(next_token) = tokens.get(pos + 1) {
if next_token.is_match(ch) {
return true;
}
if let Some(next_token) = tokens.get(pos + 1)
&& next_token.is_match(ch)
{
return true;
}
}
}
@@ -305,11 +304,7 @@ impl MaskPattern {
let mut chars: Vec<char> = int_part.chars().rev().collect();
// Removing the sign from formatting to avoid cases such as: -,123
let maybe_signed = if let Some(pos) = chars.iter().position(is_sign) {
Some(chars.remove(pos))
} else {
None
};
let maybe_signed = chars.iter().position(is_sign).map(|pos| chars.remove(pos));
let mut result = String::new();
for (i, ch) in chars.iter().enumerate() {
@@ -386,7 +381,7 @@ impl MaskPattern {
return result;
}
return mask_text.to_owned();
mask_text.to_owned()
}
Self::Pattern { tokens, .. } => {
let mut result = String::new();
@@ -412,226 +407,3 @@ impl MaskPattern {
fn is_sign(ch: &char) -> bool {
matches!(ch, '+' | '-')
}
#[cfg(test)]
mod tests {
use crate::input::mask_pattern::{MaskPattern, MaskToken};
#[test]
fn test_is_match() {
assert_eq!(MaskToken::Sep('(').is_match('('), true);
assert_eq!(MaskToken::Sep('-').is_match('('), false);
assert_eq!(MaskToken::Sep('-').is_match('3'), false);
assert_eq!(MaskToken::Digit.is_match('0'), true);
assert_eq!(MaskToken::Digit.is_match('9'), true);
assert_eq!(MaskToken::Digit.is_match('a'), false);
assert_eq!(MaskToken::Digit.is_match('C'), false);
assert_eq!(MaskToken::Letter.is_match('a'), true);
assert_eq!(MaskToken::Letter.is_match('Z'), true);
assert_eq!(MaskToken::Letter.is_match('3'), false);
assert_eq!(MaskToken::Letter.is_match('-'), false);
assert_eq!(MaskToken::LetterOrDigit.is_match('0'), true);
assert_eq!(MaskToken::LetterOrDigit.is_match('9'), true);
assert_eq!(MaskToken::LetterOrDigit.is_match('a'), true);
assert_eq!(MaskToken::LetterOrDigit.is_match('Z'), true);
assert_eq!(MaskToken::LetterOrDigit.is_match('3'), true);
assert_eq!(MaskToken::Any.is_match('a'), true);
assert_eq!(MaskToken::Any.is_match('3'), true);
assert_eq!(MaskToken::Any.is_match('-'), true);
assert_eq!(MaskToken::Any.is_match(' '), true);
}
#[test]
fn test_mask_none() {
let mask = MaskPattern::None;
assert_eq!(mask.is_none(), true);
assert_eq!(mask.is_valid("1124124ASLDJKljk"), true);
assert_eq!(mask.mask("hello-world"), "hello-world");
assert_eq!(mask.unmask("hello-world"), "hello-world");
}
#[test]
fn test_mask_pattern1() {
let mask = MaskPattern::new("(AA)999-999");
assert_eq!(
mask.tokens(),
Some(&vec![
MaskToken::Sep('('),
MaskToken::Letter,
MaskToken::Letter,
MaskToken::Sep(')'),
MaskToken::Digit,
MaskToken::Digit,
MaskToken::Digit,
MaskToken::Sep('-'),
MaskToken::Digit,
MaskToken::Digit,
MaskToken::Digit,
])
);
assert_eq!(mask.is_valid_at('(', 0), true);
assert_eq!(mask.is_valid_at('H', 0), true);
assert_eq!(mask.is_valid_at('3', 0), false);
assert_eq!(mask.is_valid_at('-', 0), false);
assert_eq!(mask.is_valid_at(')', 1), false);
assert_eq!(mask.is_valid_at('H', 1), true);
assert_eq!(mask.is_valid_at('1', 1), false);
assert_eq!(mask.is_valid_at('e', 2), true);
assert_eq!(mask.is_valid_at(')', 3), true);
assert_eq!(mask.is_valid_at('1', 3), true);
assert_eq!(mask.is_valid_at('2', 4), true);
assert_eq!(mask.is_valid("(AB)123-456"), true);
assert_eq!(mask.mask("AB123456"), "(AB)123-456");
assert_eq!(mask.mask("(AB)123-456"), "(AB)123-456");
assert_eq!(mask.mask("(AB123456"), "(AB)123-456");
assert_eq!(mask.mask("AB123-456"), "(AB)123-456");
assert_eq!(mask.mask("AB123-"), "(AB)123-");
assert_eq!(mask.mask("AB123--"), "(AB)123-");
assert_eq!(mask.mask("AB123-4"), "(AB)123-4");
let unmasked_text = mask.unmask("(AB)123-456");
assert_eq!(unmasked_text, "AB123456");
assert_eq!(mask.is_valid("12AB345"), false);
assert_eq!(mask.is_valid("(11)123-456"), false);
assert_eq!(mask.is_valid("##"), false);
assert_eq!(mask.is_valid("(AB)123456"), true);
}
#[test]
fn test_mask_pattern2() {
let mask = MaskPattern::new("999-999-******");
assert_eq!(
mask.tokens(),
Some(&vec![
MaskToken::Digit,
MaskToken::Digit,
MaskToken::Digit,
MaskToken::Sep('-'),
MaskToken::Digit,
MaskToken::Digit,
MaskToken::Digit,
MaskToken::Sep('-'),
MaskToken::Any,
MaskToken::Any,
MaskToken::Any,
MaskToken::Any,
MaskToken::Any,
MaskToken::Any,
])
);
let text = "123456A(111)";
let masked_text = mask.mask(text);
assert_eq!(masked_text, "123-456-A(111)");
let unmasked_text = mask.unmask(&masked_text);
assert_eq!(unmasked_text, "123456A(111)");
assert_eq!(mask.is_valid(&masked_text), true);
}
#[test]
fn test_number_with_group_separator() {
// Use comma as group separator
let mask = MaskPattern::number(Some(','));
assert_eq!(mask.mask("1234567"), "1,234,567");
assert_eq!(mask.mask("1,234,567"), "1,234,567");
assert_eq!(mask.unmask("1,234,567"), "1234567");
let mask = MaskPattern::number(Some(','));
assert_eq!(mask.mask("1234567.89"), "1,234,567.89");
assert_eq!(mask.unmask("1,234,567.89"), "1234567.89");
// Use space as group separator
let mask = MaskPattern::number(Some(' '));
assert_eq!(mask.mask("1234567"), "1 234 567");
assert_eq!(mask.unmask("1 234 567"), "1234567");
let mask = MaskPattern::number(Some(' '));
assert_eq!(mask.mask("1234567.89"), "1 234 567.89");
assert_eq!(mask.unmask("1 234 567.89"), "1234567.89");
// No group separator
let mask = MaskPattern::number(None);
assert_eq!(mask.mask("1234567"), "1234567");
assert_eq!(mask.unmask("1234567"), "1234567");
let mask = MaskPattern::number(None);
assert_eq!(mask.mask("1234567.89"), "1234567.89");
assert_eq!(mask.unmask("1234567.89"), "1234567.89");
}
#[test]
fn test_number_with_fraction_digits() {
let mask = MaskPattern::Number {
separator: Some(','),
fraction: Some(4),
};
assert_eq!(mask.mask("1234567"), "1,234,567");
assert_eq!(mask.unmask("1,234,567"), "1234567");
assert_eq!(mask.mask("1234567."), "1,234,567.");
assert_eq!(mask.mask("1234567.89"), "1,234,567.89");
assert_eq!(mask.unmask("1,234,567.890"), "1234567.89");
assert_eq!(mask.mask("1234567.891"), "1,234,567.891");
assert_eq!(mask.mask("1234567.891234"), "1,234,567.8912");
let mask = MaskPattern::Number {
separator: Some(','),
fraction: None,
};
assert_eq!(mask.mask("1234567.1234567"), "1,234,567.1234567");
let mask = MaskPattern::Number {
separator: Some(','),
fraction: Some(0),
};
assert_eq!(mask.mask("1234567.1234567"), "1,234,567");
}
#[test]
fn test_signed_number_numbers() {
let mask = MaskPattern::Number {
separator: Some(','),
fraction: Some(2),
};
assert_eq!(mask.is_valid("-"), true);
assert_eq!(mask.is_valid("-1234567"), true);
assert_eq!(mask.is_valid("-1,234,567"), true);
assert_eq!(mask.is_valid("-1234567."), true);
assert_eq!(mask.is_valid("-1234567.89"), true);
assert_eq!(mask.is_valid("+"), true);
assert_eq!(mask.is_valid("+1234567"), true);
assert_eq!(mask.is_valid("+1,234,567"), true);
assert_eq!(mask.is_valid("+1234567."), true);
assert_eq!(mask.is_valid("+1234567.89"), true);
// Only one sign is valid
assert_eq!(mask.is_valid("+-"), false);
assert_eq!(mask.is_valid("-+"), false);
assert_eq!(mask.is_valid("+-1234567"), false);
// No sign is valid in the middle of the number
assert_eq!(mask.is_valid("1,-234,567"), false);
assert_eq!(mask.is_valid("12-34567.89"), false);
// Signs in fractions are invalid
assert_eq!(mask.is_valid("+1234567.-"), false);
// The separator does not show up before the sign i.e. -,123
assert_eq!(mask.mask("-123"), "-123");
assert_eq!(mask.mask("-1234567"), "-1,234,567");
assert_eq!(mask.mask("+1234567"), "+1,234,567");
assert_eq!(mask.unmask("-1,234,567"), "-1234567");
assert_eq!(mask.mask("-1234567."), "-1,234,567.");
assert_eq!(mask.mask("-1234567.89"), "-1,234,567.89");
}
}

View File

@@ -7,6 +7,7 @@ mod cursor;
mod display_map;
mod element;
mod indent;
#[allow(clippy::module_inception)]
mod input;
mod mask_pattern;
mod mode;

View File

@@ -152,7 +152,7 @@ impl InputState {
}
}
pub(super) fn up(&mut self, action: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
pub(super) fn up(&mut self, _action: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
if self.mode.is_single_line() {
return;
}
@@ -168,7 +168,7 @@ impl InputState {
self.move_vertical(-1, window, cx);
}
pub(super) fn down(&mut self, action: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
pub(super) fn down(&mut self, _action: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
if self.mode.is_single_line() {
return;
}

View File

@@ -304,7 +304,7 @@ impl RopeExt for Rope {
}
fn iter_lines(&self) -> RopeLines<'_> {
RopeLines::new(&self)
RopeLines::new(self)
}
fn line_len(&self, row: usize) -> usize {

View File

@@ -54,13 +54,11 @@ impl TextSelector {
/// Returns the start and end offsets of the selected word.
pub fn word_range(text: &Rope, offset: usize) -> Option<Range<usize>> {
let offset = text.clip_offset(offset, Bias::Left);
let Some(char) = text.char_at(offset) else {
return None;
};
let char = text.char_at(offset)?;
let end = offset + char.len_utf8();
let prev_chars = text.chars_at(offset).reversed().take(128);
let next_chars = text.chars_at(end).take(128);
Some(word_range_from_chars(offset, char, prev_chars, next_chars))
}
}

View File

@@ -6,14 +6,13 @@ use std::cell::Cell;
use std::ops::Range;
use std::rc::Rc;
use anyhow::Result;
use gpui::prelude::FluentBuilder as _;
use gpui::{
Action, App, AppContext, Bounds, ClipboardItem, Context, Edges, Entity, EntityInputHandler,
EventEmitter, FocusHandle, Focusable, Half, InteractiveElement as _, IntoElement, KeyBinding,
KeyDownEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement as _,
Pixels, Point, Render, ScrollHandle, ScrollWheelEvent, ShapedLine, SharedString, Styled as _,
Subscription, Task, TextAlign, UTF16Selection, Window, actions, div, point, px,
Subscription, TextAlign, UTF16Selection, Window, actions, div, point, px,
};
use ropey::{Rope, RopeSlice};
use serde::Deserialize;
@@ -34,7 +33,6 @@ use crate::input::element::RIGHT_MARGIN;
use crate::input::movement::MoveDirection;
use crate::input::rope_ext::Position;
use crate::input::{RopeExt as _, Selection};
use crate::menu::PopupMenu;
use crate::{Root, Size};
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
@@ -323,6 +321,7 @@ impl LastLayout {
}
/// InputState to keep editing state of the [`super::Input`].
#[allow(clippy::type_complexity)]
pub struct InputState {
pub(super) focus_handle: FocusHandle,
pub(super) mode: InputMode,
@@ -374,24 +373,6 @@ pub struct InputState {
pub(crate) mask_pattern: MaskPattern,
pub(super) placeholder: SharedString,
/// An optional context menu builder to allow a custom context menu on the input.
///
/// If set, this will override the built-in context menu and ignore the value set in [`Self::enable_context_menu`].
pub(super) context_menu_builder:
Option<Rc<dyn Fn(PopupMenu, &mut Window, &mut Context<PopupMenu>) -> PopupMenu>>,
/// Whether the context menu that shows on right-click is enabled.
///
/// This value will be ignored if a context menu builder is defined in [`Self::context_menu_builder`].
pub(super) enable_context_menu: bool,
/// A flag to indicate if we are currently inserting a completion item.
pub(super) completion_inserting: bool,
/// A flag to indicate if we have a pending update to the text.
///
/// If true, will call some update (for example LSP, Syntax Highlight) before render.
_pending_update: bool,
/// A flag to indicate if we should ignore the next completion event.
pub(super) silent_replace_text: bool,
/// A flag to indicate if we should emit InputEvents.
@@ -403,8 +384,6 @@ pub struct InputState {
/// The second element is the column (usize), fallback to use this.
pub(super) preferred_column: Option<(Pixels, usize)>,
_subscriptions: Vec<Subscription>,
pub(super) _context_menu_task: Task<Result<()>>,
}
impl EventEmitter<InputEvent> for InputState {}
@@ -479,15 +458,10 @@ impl InputState {
placeholder: SharedString::default(),
mask_pattern: MaskPattern::default(),
text_align: TextAlign::Left,
context_menu_builder: None,
enable_context_menu: true,
completion_inserting: false,
silent_replace_text: false,
emit_events: true,
size: Size::default(),
_subscriptions,
_context_menu_task: Task::ready(Ok(())),
_pending_update: false,
cursor_line_end_affinity: false,
}
}
@@ -533,15 +507,6 @@ impl InputState {
self
}
/// Sets whether the context menu that shows on right-click is enabled.
///
/// The context menu is enabled by default.
/// This value will be ignored if a custom context menu is defined on the input.
pub fn context_menu(mut self, enable: bool) -> Self {
self.enable_context_menu = enable;
self
}
/// Set whether search UI allows replacement, default is true.
pub fn replaceable(mut self, allow: bool) -> Self {
self.replaceable = allow;
@@ -625,26 +590,21 @@ impl InputState {
new_language: impl Into<SharedString>,
cx: &mut Context<Self>,
) {
match &mut self.mode {
InputMode::CodeEditor {
language,
parse_task,
..
} => {
*language = new_language.into();
parse_task.borrow_mut().take();
}
_ => {}
if let InputMode::CodeEditor {
language,
parse_task,
..
} = &mut self.mode
{
*language = new_language.into();
parse_task.borrow_mut().take();
}
cx.notify();
}
fn reset_highlighter(&mut self, cx: &mut Context<Self>) {
match &mut self.mode {
InputMode::CodeEditor { parse_task, .. } => {
parse_task.borrow_mut().take();
}
_ => {}
if let InputMode::CodeEditor { parse_task, .. } = &mut self.mode {
parse_task.borrow_mut().take();
}
cx.notify();
}
@@ -713,10 +673,6 @@ impl InputState {
self.selected_range.clear();
}
if self.mode.is_code_editor() {
self._pending_update = true;
}
// Move scroll to top
self.scroll_handle.set_offset(point(px(0.), px(0.)));
@@ -902,9 +858,6 @@ impl InputState {
pub fn default_value(mut self, value: impl Into<SharedString>) -> Self {
let text: SharedString = value.into();
self.text = Rope::from(text.as_str());
// Note: We can't call display_map.set_text here because it needs cx.
// The text will be set during prepare_if_need in element.rs
self._pending_update = true;
self
}
@@ -1146,13 +1099,11 @@ impl InputState {
offset += 1;
}
let line = self
.text_for_range(self.range_to_utf16(&(0..offset + 1)), &mut None, window, cx)
self.text_for_range(self.range_to_utf16(&(0..offset + 1)), &mut None, window, cx)
.unwrap_or_default()
.rfind('\n')
.map(|i| i + 1)
.unwrap_or(0);
line
.unwrap_or(0)
}
/// Get indent string of next line.
@@ -1188,9 +1139,9 @@ impl InputState {
}
if next_indent.len() > current_indent.len() {
return next_indent;
next_indent
} else {
return current_indent;
current_indent
}
}
@@ -1361,10 +1312,10 @@ impl InputState {
) {
// If there have IME marked range and is empty (Means pressed Esc to abort IME typing)
// Clear the marked range.
if let Some(ime_marked_range) = &self.ime_marked_range {
if ime_marked_range.len() == 0 {
self.ime_marked_range = None;
}
if let Some(ime_marked_range) = &self.ime_marked_range
&& ime_marked_range.is_empty()
{
self.ime_marked_range = None;
}
self.selecting = true;
@@ -1402,25 +1353,6 @@ impl InputState {
self.selected_word_range = None;
}
pub(super) fn on_mouse_move(
&mut self,
event: &MouseMoveEvent,
_window: &mut Window,
_cx: &mut Context<Self>,
) {
// Check if mouse is within bounds
let within_bounds = self
.last_bounds
.as_ref()
.map(|bounds| bounds.contains(&event.position))
.unwrap_or(false);
if !within_bounds {
// Clear hover when mouse leaves the input
return;
}
}
pub(super) fn on_scroll_wheel(
&mut self,
event: &ScrollWheelEvent,
@@ -2041,22 +1973,6 @@ impl InputState {
self.replace_text_in_range(range_utf16, new_text, window, cx);
self.silent_replace_text = false;
}
/// Update fold candidates from tree-sitter syntax tree (full extraction).
/// Used only on initial load or language changes.
fn update_fold_candidates(&mut self) {
if !self.mode.is_folding() {
return;
}
}
/// Incrementally update fold candidates after a text edit.
/// Only traverses the edited region of the syntax tree instead of the full tree.
fn update_fold_candidates_incremental(&mut self, _edit_range: &Range<usize>, _new_text: &str) {
if !self.mode.is_folding() {
return;
}
}
}
impl EntityInputHandler for InputState {
@@ -2105,7 +2021,7 @@ impl EntityInputHandler for InputState {
&mut self,
range_utf16: Option<Range<usize>>,
new_text: &str,
window: &mut Window,
_window: &mut Window,
cx: &mut Context<Self>,
) {
if self.disabled {
@@ -2147,7 +2063,7 @@ impl EntityInputHandler for InputState {
}
}
self.push_history(&old_text, &range, &new_text);
self.push_history(&old_text, &range, new_text);
self.history.end_grouping();
// Adjust folds before updating wrap map: remove overlapping folds and shift others
@@ -2156,7 +2072,6 @@ impl EntityInputHandler for InputState {
self.display_map
.on_text_changed(&self.text, &range, &Rope::from(new_text), cx);
self.update_fold_candidates_incremental(&range, new_text);
self.selected_range = (new_offset..new_offset).into();
self.ime_marked_range.take();
self.update_preferred_column();
@@ -2206,8 +2121,6 @@ impl EntityInputHandler for InputState {
self.display_map
.on_text_changed(&self.text, &range, &Rope::from(new_text), cx);
self.update_fold_candidates_incremental(&range, new_text);
if new_text.is_empty() {
// Cancel selection, when cancel IME input.
self.selected_range = (range.start..range.start).into();
@@ -2253,24 +2166,24 @@ impl EntityInputHandler for InputState {
let index_offset = last_layout.visible_line_byte_offsets[vi];
if start_origin.is_none() {
if let Some(p) = line.position_for_index(
if start_origin.is_none()
&& let Some(p) = line.position_for_index(
range.start.saturating_sub(index_offset),
last_layout,
false,
) {
start_origin = Some(p + point(px(0.), y_offset));
}
)
{
start_origin = Some(p + point(px(0.), y_offset));
}
if end_origin.is_none() {
if let Some(p) = line.position_for_index(
if end_origin.is_none()
&& let Some(p) = line.position_for_index(
range.end.saturating_sub(index_offset),
last_layout,
false,
) {
end_origin = Some(p + point(px(0.), y_offset));
}
)
{
end_origin = Some(p + point(px(0.), y_offset));
}
y_offset += line.size(line_height).height;
@@ -2316,11 +2229,6 @@ impl Focusable for InputState {
impl Render for InputState {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
if self._pending_update {
self.update_fold_candidates();
self._pending_update = false;
}
div()
.id("input-state")
.flex_1()

View File

@@ -288,7 +288,7 @@ where
});
});
}
InputEvent::PressEnter { secondary, shift } => self.on_action_confirm(
InputEvent::PressEnter { secondary, .. } => self.on_action_confirm(
&Confirm {
secondary: *secondary,
},