fix clippy
This commit is contained in:
@@ -173,3 +173,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<I> Default for History<I>
|
||||||
|
where
|
||||||
|
I: HistoryItem,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -149,9 +149,7 @@ impl Element for EditorScrollbar {
|
|||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Self::PrepaintState {
|
) -> Self::PrepaintState {
|
||||||
let state = self.state.read(cx);
|
let state = self.state.read(cx);
|
||||||
let Some(snapshot) = state.editor_scrollbar_snapshot.get() else {
|
let snapshot = state.editor_scrollbar_snapshot.get()?;
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let scroll_handle = state.scroll_handle.clone();
|
let scroll_handle = state.scroll_handle.clone();
|
||||||
|
|
||||||
if scroll_handle.offset() != snapshot.cursor_scroll_offset {
|
if scroll_handle.offset() != snapshot.cursor_scroll_offset {
|
||||||
@@ -492,7 +490,7 @@ impl TextElement {
|
|||||||
bounds.size.height,
|
bounds.size.height,
|
||||||
);
|
);
|
||||||
|
|
||||||
bounds.origin = bounds.origin + scroll_offset;
|
bounds.origin += scroll_offset;
|
||||||
|
|
||||||
(cursor_bounds, scroll_offset, current_row)
|
(cursor_bounds, scroll_offset, current_row)
|
||||||
}
|
}
|
||||||
@@ -622,17 +620,17 @@ impl TextElement {
|
|||||||
let mut rev_line_corners = line_corners.iter().rev().peekable();
|
let mut rev_line_corners = line_corners.iter().rev().peekable();
|
||||||
while let Some(corners) = rev_line_corners.next() {
|
while let Some(corners) = rev_line_corners.next() {
|
||||||
points.push(corners.top_left);
|
points.push(corners.top_left);
|
||||||
if let Some(next) = rev_line_corners.peek() {
|
if let Some(next) = rev_line_corners.peek()
|
||||||
if next.top_left.x > corners.top_left.x {
|
&& next.top_left.x > corners.top_left.x
|
||||||
points.push(point(next.top_left.x, corners.top_left.y));
|
{
|
||||||
}
|
points.push(point(next.top_left.x, corners.top_left.y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// print_points_as_svg_path(&line_corners, &points);
|
// print_points_as_svg_path(&line_corners, &points);
|
||||||
|
|
||||||
let path_origin = bounds.origin + point(line_number_width, px(0.));
|
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();
|
let mut builder = gpui::PathBuilder::fill();
|
||||||
builder.move_to(path_origin + first_p);
|
builder.move_to(path_origin + first_p);
|
||||||
for p in points.iter().skip(1) {
|
for p in points.iter().skip(1) {
|
||||||
@@ -690,10 +688,10 @@ impl TextElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut selected_range = state.selected_range;
|
let mut selected_range = state.selected_range;
|
||||||
if let Some(ime_marked_range) = &state.ime_marked_range {
|
if let Some(ime_marked_range) = &state.ime_marked_range
|
||||||
if !ime_marked_range.is_empty() {
|
&& !ime_marked_range.is_empty()
|
||||||
selected_range = (ime_marked_range.end..ime_marked_range.end).into();
|
{
|
||||||
}
|
selected_range = (ime_marked_range.end..ime_marked_range.end).into();
|
||||||
}
|
}
|
||||||
if selected_range.is_empty() {
|
if selected_range.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
@@ -713,7 +711,7 @@ impl TextElement {
|
|||||||
let range = start_ix.max(last_layout.visible_range_offset.start)
|
let range = start_ix.max(last_layout.visible_range_offset.start)
|
||||||
..end_ix.min(last_layout.visible_range_offset.end);
|
..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.
|
/// Calculate the visible range of lines in the viewport.
|
||||||
@@ -1065,7 +1063,7 @@ impl TextElement {
|
|||||||
let shaped_line = window.text_system().shape_line(
|
let shaped_line = window.text_system().shape_line(
|
||||||
display_text.to_string().into(),
|
display_text.to_string().into(),
|
||||||
font_size,
|
font_size,
|
||||||
&runs,
|
runs,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1114,7 +1112,7 @@ impl TextElement {
|
|||||||
let mut wrapped_lines = SmallVec::with_capacity(1);
|
let mut wrapped_lines = SmallVec::with_capacity(1);
|
||||||
|
|
||||||
for range in &line_item.wrapped_lines {
|
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() {
|
let line_runs = if bg_segments.is_empty() {
|
||||||
line_runs
|
line_runs
|
||||||
} else {
|
} else {
|
||||||
@@ -1260,10 +1258,7 @@ impl IntoElement for TextElement {
|
|||||||
|
|
||||||
/// A debug function to print points as SVG path.
|
/// A debug function to print points as SVG path.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn print_points_as_svg_path(
|
fn print_points_as_svg_path(line_corners: &Vec<gpui::Corners<Pixels>>, points: Vec<Point<Pixels>>) {
|
||||||
line_corners: &Vec<gpui::Corners<Pixels>>,
|
|
||||||
points: &Vec<Point<Pixels>>,
|
|
||||||
) {
|
|
||||||
for corners in line_corners {
|
for corners in line_corners {
|
||||||
println!(
|
println!(
|
||||||
"tl: ({}, {}), tr: ({}, {}), bl: ({}, {}), br: ({}, {})",
|
"tl: ({}, {}), tr: ({}, {}), bl: ({}, {}), br: ({}, {})",
|
||||||
@@ -1278,7 +1273,7 @@ fn print_points_as_svg_path(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if points.len() > 0 {
|
if !points.is_empty() {
|
||||||
println!(
|
println!(
|
||||||
"M{},{}",
|
"M{},{}",
|
||||||
points[0].x.as_f32() as i32,
|
points[0].x.as_f32() as i32,
|
||||||
@@ -1353,7 +1348,7 @@ impl Element for TextElement {
|
|||||||
let line_height = window.line_height();
|
let line_height = window.line_height();
|
||||||
|
|
||||||
let (visible_range, visible_buffer_lines, visible_top) =
|
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_start_offset = state.text.line_start_offset(visible_range.start);
|
||||||
let visible_end_offset = state
|
let visible_end_offset = state
|
||||||
.text
|
.text
|
||||||
@@ -1387,7 +1382,7 @@ impl Element for TextElement {
|
|||||||
|
|
||||||
// Calculate the width of the line numbers
|
// Calculate the width of the line numbers
|
||||||
let (line_number_width, line_number_len) =
|
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 mut bounds = bounds;
|
||||||
let wrap_width = if multi_line && state.soft_wrap {
|
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)| {
|
runs.extend(highlight_styles.iter().map(|(range, style)| {
|
||||||
let mut run = text_style.clone().highlight(*style).to_run(range.len());
|
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
|
if let Some(ime_marked_range) = &state.ime_marked_range
|
||||||
&& range.end <= ime_marked_range.end
|
&& range.start >= ime_marked_range.start
|
||||||
{
|
&& range.end <= ime_marked_range.end
|
||||||
run.color = marked_run.color;
|
{
|
||||||
run.strikethrough = marked_run.strikethrough;
|
run.color = marked_run.color;
|
||||||
run.underline = marked_run.underline;
|
run.strikethrough = marked_run.strikethrough;
|
||||||
}
|
run.underline = marked_run.underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
run
|
run
|
||||||
@@ -1505,11 +1500,11 @@ impl Element for TextElement {
|
|||||||
|
|
||||||
// Create shaped lines for whitespace indicators before layout
|
// Create shaped lines for whitespace indicators before layout
|
||||||
let whitespace_indicators =
|
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(
|
let lines = Self::layout_lines(
|
||||||
&state,
|
state,
|
||||||
&display_text,
|
display_text,
|
||||||
&last_layout,
|
&last_layout,
|
||||||
text_size,
|
text_size,
|
||||||
&runs,
|
&runs,
|
||||||
@@ -1621,9 +1616,9 @@ impl Element for TextElement {
|
|||||||
self.layout_cursor(&last_layout, &mut bounds, scroll_size, window, cx);
|
self.layout_cursor(&last_layout, &mut bounds, scroll_size, window, cx);
|
||||||
last_layout.cursor_bounds = cursor_bounds;
|
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 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 =
|
let document_color_paths =
|
||||||
self.layout_document_colors(&document_colors, &last_layout, &bounds, cx);
|
self.layout_document_colors(&document_colors, &last_layout, &bounds, cx);
|
||||||
|
|
||||||
@@ -1666,7 +1661,7 @@ impl Element for TextElement {
|
|||||||
sub_lines.push(
|
sub_lines.push(
|
||||||
window
|
window
|
||||||
.text_system()
|
.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) {
|
for _ in 0..line.wrapped_lines.len().saturating_sub(1) {
|
||||||
sub_lines.push(ShapedLine::default());
|
sub_lines.push(ShapedLine::default());
|
||||||
@@ -1847,7 +1842,7 @@ impl Element for TextElement {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Paint the actual line
|
// Paint the actual line
|
||||||
_ = line.paint(
|
line.paint(
|
||||||
p,
|
p,
|
||||||
line_height,
|
line_height,
|
||||||
text_align,
|
text_align,
|
||||||
@@ -1893,10 +1888,11 @@ impl Element for TextElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Paint blinking cursor
|
// Paint blinking cursor
|
||||||
if focused && show_cursor {
|
if focused
|
||||||
if let Some(cursor_bounds) = prepaint.cursor_bounds_with_scroll() {
|
&& show_cursor
|
||||||
window.paint_quad(fill(cursor_bounds, cx.theme().cursor));
|
&& let Some(cursor_bounds) = prepaint.cursor_bounds_with_scroll()
|
||||||
}
|
{
|
||||||
|
window.paint_quad(fill(cursor_bounds, cx.theme().cursor));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Paint line numbers
|
// Paint line numbers
|
||||||
@@ -1956,26 +1952,24 @@ impl Element for TextElement {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Some(hitbox) = prepaint.hover_definition_hitbox.as_ref() {
|
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)
|
// Paint inline completion first line suffix (after cursor on same line)
|
||||||
if focused {
|
if focused
|
||||||
if let Some(first_line) = &prepaint.ghost_first_line {
|
&& let Some(first_line) = &prepaint.ghost_first_line
|
||||||
if let (Some(cursor_bounds), Some(cursor_row_y)) =
|
&& let (Some(cursor_bounds), Some(cursor_row_y)) =
|
||||||
(prepaint.cursor_bounds_with_scroll(), cursor_row_y)
|
(prepaint.cursor_bounds_with_scroll(), cursor_row_y)
|
||||||
{
|
{
|
||||||
let first_line_x = cursor_bounds.origin.x + cursor_bounds.size.width;
|
let first_line_x = cursor_bounds.origin.x + cursor_bounds.size.width;
|
||||||
let p = point(first_line_x, cursor_row_y);
|
let p = point(first_line_x, cursor_row_y);
|
||||||
|
|
||||||
// Paint background to cover any existing text
|
// Paint background to cover any existing text
|
||||||
let bg_bounds = Bounds::new(p, size(first_line.width + px(4.), line_height));
|
let bg_bounds = Bounds::new(p, size(first_line.width + px(4.), line_height));
|
||||||
window.paint_quad(fill(bg_bounds, cx.theme().surface_background));
|
window.paint_quad(fill(bg_bounds, cx.theme().surface_background));
|
||||||
|
|
||||||
// Paint first line completion text
|
// Paint first line completion text
|
||||||
_ = first_line.paint(p, line_height, text_align, None, window, cx);
|
_ = first_line.paint(p, line_height, text_align, None, window, cx);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.paint_mouse_listeners(window, cx);
|
self.paint_mouse_listeners(window, cx);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ impl Default for TabSize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TabSize {
|
impl TabSize {
|
||||||
pub(super) fn to_string(&self) -> SharedString {
|
pub(super) fn to_string(self) -> SharedString {
|
||||||
if self.hard_tabs {
|
if self.hard_tabs {
|
||||||
"\t".into()
|
"\t".into()
|
||||||
} else {
|
} else {
|
||||||
@@ -146,7 +146,7 @@ impl TextElement {
|
|||||||
builder.line_to(point(pos.x, pos.y + line_height));
|
builder.line_to(point(pos.x, pos.y + line_height));
|
||||||
current_indents.push(pos.x);
|
current_indents.push(pos.x);
|
||||||
}
|
}
|
||||||
} else if last_indents.len() > 0 {
|
} else if !last_indents.is_empty() {
|
||||||
for x in &last_indents {
|
for x in &last_indents {
|
||||||
let pos = point(*x, offset_y);
|
let pos = point(*x, offset_y);
|
||||||
builder.move_to(pos);
|
builder.move_to(pos);
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use gpui::prelude::FluentBuilder as _;
|
use gpui::prelude::FluentBuilder as _;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyElement, App, Context, DefiniteLength, Edges, EdgesRefinement, Entity, Hsla,
|
AnyElement, App, DefiniteLength, Edges, EdgesRefinement, Entity, Hsla, InteractiveElement as _,
|
||||||
InteractiveElement as _, IntoElement, MouseButton, ParentElement as _, Rems, RenderOnce,
|
IntoElement, MouseButton, ParentElement as _, Rems, RenderOnce, StyleRefinement, Styled,
|
||||||
StyleRefinement, Styled, TextAlign, Window, div, px, relative,
|
TextAlign, Window, div, px, relative,
|
||||||
};
|
};
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
|
|
||||||
@@ -13,7 +11,6 @@ use super::element::EditorScrollbar;
|
|||||||
use crate::button::{Button, ButtonVariants as _};
|
use crate::button::{Button, ButtonVariants as _};
|
||||||
use crate::indicator::Indicator;
|
use crate::indicator::Indicator;
|
||||||
use crate::input::clear_button;
|
use crate::input::clear_button;
|
||||||
use crate::menu::PopupMenu;
|
|
||||||
use crate::{IconName, Selectable, Sizable, Size, StyleSized, StyledExt, h_flex, v_flex};
|
use crate::{IconName, Selectable, Sizable, Size, StyleSized, StyledExt, h_flex, v_flex};
|
||||||
|
|
||||||
/// Returns `(background, foreground)` colors for input-like components.
|
/// Returns `(background, foreground)` colors for input-like components.
|
||||||
@@ -42,12 +39,6 @@ pub struct Input {
|
|||||||
focus_bordered: bool,
|
focus_bordered: bool,
|
||||||
tab_index: isize,
|
tab_index: isize,
|
||||||
selected: bool,
|
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 {
|
impl Sizable for Input {
|
||||||
@@ -86,7 +77,6 @@ impl Input {
|
|||||||
focus_bordered: true,
|
focus_bordered: true,
|
||||||
tab_index: 0,
|
tab_index: 0,
|
||||||
selected: false,
|
selected: false,
|
||||||
context_menu_builder: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,15 +144,6 @@ impl Input {
|
|||||||
self
|
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 {
|
fn render_toggle_mask_button(state: &Entity<InputState>, cx: &App) -> impl IntoElement {
|
||||||
let _masked = state.read(cx).masked;
|
let _masked = state.read(cx).masked;
|
||||||
Button::new("toggle-mask")
|
Button::new("toggle-mask")
|
||||||
@@ -234,10 +215,8 @@ impl RenderOnce for Input {
|
|||||||
let text_align = self.style.text.text_align.unwrap_or(TextAlign::Left);
|
let text_align = self.style.text.text_align.unwrap_or(TextAlign::Left);
|
||||||
|
|
||||||
self.state.update(cx, |state, _| {
|
self.state.update(cx, |state, _| {
|
||||||
state.context_menu_builder = self.context_menu_builder.clone();
|
|
||||||
state.disabled = self.disabled;
|
state.disabled = self.disabled;
|
||||||
state.size = self.size;
|
state.size = self.size;
|
||||||
|
|
||||||
// Only for single line mode
|
// Only for single line mode
|
||||||
if state.mode.is_single_line() {
|
if state.mode.is_single_line() {
|
||||||
state.text_align = text_align;
|
state.text_align = text_align;
|
||||||
@@ -342,7 +321,6 @@ impl RenderOnce for Input {
|
|||||||
MouseButton::Right,
|
MouseButton::Right,
|
||||||
window.listener_for(&self.state, InputState::on_mouse_up),
|
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))
|
.on_scroll_wheel(window.listener_for(&self.state, InputState::on_scroll_wheel))
|
||||||
.size_full()
|
.size_full()
|
||||||
.line_height(LINE_HEIGHT)
|
.line_height(LINE_HEIGHT)
|
||||||
@@ -372,7 +350,7 @@ impl RenderOnce for Input {
|
|||||||
.children(prefix)
|
.children(prefix)
|
||||||
.when(state.mode.is_multi_line(), |mut this| {
|
.when(state.mode.is_multi_line(), |mut this| {
|
||||||
let paddings = this.style().padding.clone();
|
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| {
|
.when(!state.mode.is_multi_line(), |this| {
|
||||||
this.child(self.state.clone())
|
this.child(self.state.clone())
|
||||||
|
|||||||
@@ -225,13 +225,12 @@ impl MaskPattern {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if the fraction part is valid
|
// check if the fraction part is valid
|
||||||
if let Some(frac) = frac_part {
|
if let Some(frac) = frac_part
|
||||||
if !frac
|
&& !frac
|
||||||
.chars()
|
.chars()
|
||||||
.all(|ch| ch.is_ascii_digit() || Some(ch) == *separator)
|
.all(|ch| ch.is_ascii_digit() || Some(ch) == *separator)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
@@ -255,10 +254,10 @@ impl MaskPattern {
|
|||||||
|
|
||||||
if token.is_sep() {
|
if token.is_sep() {
|
||||||
// If next token is match, it's valid
|
// If next token is match, it's valid
|
||||||
if let Some(next_token) = tokens.get(pos + 1) {
|
if let Some(next_token) = tokens.get(pos + 1)
|
||||||
if next_token.is_match(ch) {
|
&& next_token.is_match(ch)
|
||||||
return true;
|
{
|
||||||
}
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -305,11 +304,7 @@ impl MaskPattern {
|
|||||||
let mut chars: Vec<char> = int_part.chars().rev().collect();
|
let mut chars: Vec<char> = int_part.chars().rev().collect();
|
||||||
|
|
||||||
// Removing the sign from formatting to avoid cases such as: -,123
|
// Removing the sign from formatting to avoid cases such as: -,123
|
||||||
let maybe_signed = if let Some(pos) = chars.iter().position(is_sign) {
|
let maybe_signed = chars.iter().position(is_sign).map(|pos| chars.remove(pos));
|
||||||
Some(chars.remove(pos))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
for (i, ch) in chars.iter().enumerate() {
|
for (i, ch) in chars.iter().enumerate() {
|
||||||
@@ -386,7 +381,7 @@ impl MaskPattern {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mask_text.to_owned();
|
mask_text.to_owned()
|
||||||
}
|
}
|
||||||
Self::Pattern { tokens, .. } => {
|
Self::Pattern { tokens, .. } => {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
@@ -412,226 +407,3 @@ impl MaskPattern {
|
|||||||
fn is_sign(ch: &char) -> bool {
|
fn is_sign(ch: &char) -> bool {
|
||||||
matches!(ch, '+' | '-')
|
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ mod cursor;
|
|||||||
mod display_map;
|
mod display_map;
|
||||||
mod element;
|
mod element;
|
||||||
mod indent;
|
mod indent;
|
||||||
|
#[allow(clippy::module_inception)]
|
||||||
mod input;
|
mod input;
|
||||||
mod mask_pattern;
|
mod mask_pattern;
|
||||||
mod mode;
|
mod mode;
|
||||||
|
|||||||
@@ -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() {
|
if self.mode.is_single_line() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -168,7 +168,7 @@ impl InputState {
|
|||||||
self.move_vertical(-1, window, cx);
|
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() {
|
if self.mode.is_single_line() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ impl RopeExt for Rope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn iter_lines(&self) -> RopeLines<'_> {
|
fn iter_lines(&self) -> RopeLines<'_> {
|
||||||
RopeLines::new(&self)
|
RopeLines::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line_len(&self, row: usize) -> usize {
|
fn line_len(&self, row: usize) -> usize {
|
||||||
|
|||||||
@@ -54,13 +54,11 @@ impl TextSelector {
|
|||||||
/// Returns the start and end offsets of the selected word.
|
/// Returns the start and end offsets of the selected word.
|
||||||
pub fn word_range(text: &Rope, offset: usize) -> Option<Range<usize>> {
|
pub fn word_range(text: &Rope, offset: usize) -> Option<Range<usize>> {
|
||||||
let offset = text.clip_offset(offset, Bias::Left);
|
let offset = text.clip_offset(offset, Bias::Left);
|
||||||
let Some(char) = text.char_at(offset) else {
|
let char = text.char_at(offset)?;
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
let end = offset + char.len_utf8();
|
let end = offset + char.len_utf8();
|
||||||
let prev_chars = text.chars_at(offset).reversed().take(128);
|
let prev_chars = text.chars_at(offset).reversed().take(128);
|
||||||
let next_chars = text.chars_at(end).take(128);
|
let next_chars = text.chars_at(end).take(128);
|
||||||
|
|
||||||
Some(word_range_from_chars(offset, char, prev_chars, next_chars))
|
Some(word_range_from_chars(offset, char, prev_chars, next_chars))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ use std::cell::Cell;
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use gpui::prelude::FluentBuilder as _;
|
use gpui::prelude::FluentBuilder as _;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, App, AppContext, Bounds, ClipboardItem, Context, Edges, Entity, EntityInputHandler,
|
Action, App, AppContext, Bounds, ClipboardItem, Context, Edges, Entity, EntityInputHandler,
|
||||||
EventEmitter, FocusHandle, Focusable, Half, InteractiveElement as _, IntoElement, KeyBinding,
|
EventEmitter, FocusHandle, Focusable, Half, InteractiveElement as _, IntoElement, KeyBinding,
|
||||||
KeyDownEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement as _,
|
KeyDownEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement as _,
|
||||||
Pixels, Point, Render, ScrollHandle, ScrollWheelEvent, ShapedLine, SharedString, Styled 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 ropey::{Rope, RopeSlice};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@@ -34,7 +33,6 @@ use crate::input::element::RIGHT_MARGIN;
|
|||||||
use crate::input::movement::MoveDirection;
|
use crate::input::movement::MoveDirection;
|
||||||
use crate::input::rope_ext::Position;
|
use crate::input::rope_ext::Position;
|
||||||
use crate::input::{RopeExt as _, Selection};
|
use crate::input::{RopeExt as _, Selection};
|
||||||
use crate::menu::PopupMenu;
|
|
||||||
use crate::{Root, Size};
|
use crate::{Root, Size};
|
||||||
|
|
||||||
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
|
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
|
||||||
@@ -323,6 +321,7 @@ impl LastLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// InputState to keep editing state of the [`super::Input`].
|
/// InputState to keep editing state of the [`super::Input`].
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
pub struct InputState {
|
pub struct InputState {
|
||||||
pub(super) focus_handle: FocusHandle,
|
pub(super) focus_handle: FocusHandle,
|
||||||
pub(super) mode: InputMode,
|
pub(super) mode: InputMode,
|
||||||
@@ -374,24 +373,6 @@ pub struct InputState {
|
|||||||
pub(crate) mask_pattern: MaskPattern,
|
pub(crate) mask_pattern: MaskPattern,
|
||||||
pub(super) placeholder: SharedString,
|
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.
|
/// A flag to indicate if we should ignore the next completion event.
|
||||||
pub(super) silent_replace_text: bool,
|
pub(super) silent_replace_text: bool,
|
||||||
/// A flag to indicate if we should emit InputEvents.
|
/// 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.
|
/// The second element is the column (usize), fallback to use this.
|
||||||
pub(super) preferred_column: Option<(Pixels, usize)>,
|
pub(super) preferred_column: Option<(Pixels, usize)>,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
|
|
||||||
pub(super) _context_menu_task: Task<Result<()>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEmitter<InputEvent> for InputState {}
|
impl EventEmitter<InputEvent> for InputState {}
|
||||||
@@ -479,15 +458,10 @@ impl InputState {
|
|||||||
placeholder: SharedString::default(),
|
placeholder: SharedString::default(),
|
||||||
mask_pattern: MaskPattern::default(),
|
mask_pattern: MaskPattern::default(),
|
||||||
text_align: TextAlign::Left,
|
text_align: TextAlign::Left,
|
||||||
context_menu_builder: None,
|
|
||||||
enable_context_menu: true,
|
|
||||||
completion_inserting: false,
|
|
||||||
silent_replace_text: false,
|
silent_replace_text: false,
|
||||||
emit_events: true,
|
emit_events: true,
|
||||||
size: Size::default(),
|
size: Size::default(),
|
||||||
_subscriptions,
|
_subscriptions,
|
||||||
_context_menu_task: Task::ready(Ok(())),
|
|
||||||
_pending_update: false,
|
|
||||||
cursor_line_end_affinity: false,
|
cursor_line_end_affinity: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -533,15 +507,6 @@ impl InputState {
|
|||||||
self
|
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.
|
/// Set whether search UI allows replacement, default is true.
|
||||||
pub fn replaceable(mut self, allow: bool) -> Self {
|
pub fn replaceable(mut self, allow: bool) -> Self {
|
||||||
self.replaceable = allow;
|
self.replaceable = allow;
|
||||||
@@ -625,26 +590,21 @@ impl InputState {
|
|||||||
new_language: impl Into<SharedString>,
|
new_language: impl Into<SharedString>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
match &mut self.mode {
|
if let InputMode::CodeEditor {
|
||||||
InputMode::CodeEditor {
|
language,
|
||||||
language,
|
parse_task,
|
||||||
parse_task,
|
..
|
||||||
..
|
} = &mut self.mode
|
||||||
} => {
|
{
|
||||||
*language = new_language.into();
|
*language = new_language.into();
|
||||||
parse_task.borrow_mut().take();
|
parse_task.borrow_mut().take();
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset_highlighter(&mut self, cx: &mut Context<Self>) {
|
fn reset_highlighter(&mut self, cx: &mut Context<Self>) {
|
||||||
match &mut self.mode {
|
if let InputMode::CodeEditor { parse_task, .. } = &mut self.mode {
|
||||||
InputMode::CodeEditor { parse_task, .. } => {
|
parse_task.borrow_mut().take();
|
||||||
parse_task.borrow_mut().take();
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
@@ -713,10 +673,6 @@ impl InputState {
|
|||||||
self.selected_range.clear();
|
self.selected_range.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.mode.is_code_editor() {
|
|
||||||
self._pending_update = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move scroll to top
|
// Move scroll to top
|
||||||
self.scroll_handle.set_offset(point(px(0.), px(0.)));
|
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 {
|
pub fn default_value(mut self, value: impl Into<SharedString>) -> Self {
|
||||||
let text: SharedString = value.into();
|
let text: SharedString = value.into();
|
||||||
self.text = Rope::from(text.as_str());
|
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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1146,13 +1099,11 @@ impl InputState {
|
|||||||
offset += 1;
|
offset += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let line = self
|
self.text_for_range(self.range_to_utf16(&(0..offset + 1)), &mut None, window, cx)
|
||||||
.text_for_range(self.range_to_utf16(&(0..offset + 1)), &mut None, window, cx)
|
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.rfind('\n')
|
.rfind('\n')
|
||||||
.map(|i| i + 1)
|
.map(|i| i + 1)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0)
|
||||||
line
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get indent string of next line.
|
/// Get indent string of next line.
|
||||||
@@ -1188,9 +1139,9 @@ impl InputState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if next_indent.len() > current_indent.len() {
|
if next_indent.len() > current_indent.len() {
|
||||||
return next_indent;
|
next_indent
|
||||||
} else {
|
} 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)
|
// If there have IME marked range and is empty (Means pressed Esc to abort IME typing)
|
||||||
// Clear the marked range.
|
// Clear the marked range.
|
||||||
if let Some(ime_marked_range) = &self.ime_marked_range {
|
if let Some(ime_marked_range) = &self.ime_marked_range
|
||||||
if ime_marked_range.len() == 0 {
|
&& ime_marked_range.is_empty()
|
||||||
self.ime_marked_range = None;
|
{
|
||||||
}
|
self.ime_marked_range = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.selecting = true;
|
self.selecting = true;
|
||||||
@@ -1402,25 +1353,6 @@ impl InputState {
|
|||||||
self.selected_word_range = None;
|
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(
|
pub(super) fn on_scroll_wheel(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: &ScrollWheelEvent,
|
event: &ScrollWheelEvent,
|
||||||
@@ -2041,22 +1973,6 @@ impl InputState {
|
|||||||
self.replace_text_in_range(range_utf16, new_text, window, cx);
|
self.replace_text_in_range(range_utf16, new_text, window, cx);
|
||||||
self.silent_replace_text = false;
|
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 {
|
impl EntityInputHandler for InputState {
|
||||||
@@ -2105,7 +2021,7 @@ impl EntityInputHandler for InputState {
|
|||||||
&mut self,
|
&mut self,
|
||||||
range_utf16: Option<Range<usize>>,
|
range_utf16: Option<Range<usize>>,
|
||||||
new_text: &str,
|
new_text: &str,
|
||||||
window: &mut Window,
|
_window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
if self.disabled {
|
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();
|
self.history.end_grouping();
|
||||||
|
|
||||||
// Adjust folds before updating wrap map: remove overlapping folds and shift others
|
// Adjust folds before updating wrap map: remove overlapping folds and shift others
|
||||||
@@ -2156,7 +2072,6 @@ impl EntityInputHandler for InputState {
|
|||||||
self.display_map
|
self.display_map
|
||||||
.on_text_changed(&self.text, &range, &Rope::from(new_text), cx);
|
.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.selected_range = (new_offset..new_offset).into();
|
||||||
self.ime_marked_range.take();
|
self.ime_marked_range.take();
|
||||||
self.update_preferred_column();
|
self.update_preferred_column();
|
||||||
@@ -2206,8 +2121,6 @@ impl EntityInputHandler for InputState {
|
|||||||
self.display_map
|
self.display_map
|
||||||
.on_text_changed(&self.text, &range, &Rope::from(new_text), cx);
|
.on_text_changed(&self.text, &range, &Rope::from(new_text), cx);
|
||||||
|
|
||||||
self.update_fold_candidates_incremental(&range, new_text);
|
|
||||||
|
|
||||||
if new_text.is_empty() {
|
if new_text.is_empty() {
|
||||||
// Cancel selection, when cancel IME input.
|
// Cancel selection, when cancel IME input.
|
||||||
self.selected_range = (range.start..range.start).into();
|
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];
|
let index_offset = last_layout.visible_line_byte_offsets[vi];
|
||||||
|
|
||||||
if start_origin.is_none() {
|
if start_origin.is_none()
|
||||||
if let Some(p) = line.position_for_index(
|
&& let Some(p) = line.position_for_index(
|
||||||
range.start.saturating_sub(index_offset),
|
range.start.saturating_sub(index_offset),
|
||||||
last_layout,
|
last_layout,
|
||||||
false,
|
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 end_origin.is_none()
|
||||||
if let Some(p) = line.position_for_index(
|
&& let Some(p) = line.position_for_index(
|
||||||
range.end.saturating_sub(index_offset),
|
range.end.saturating_sub(index_offset),
|
||||||
last_layout,
|
last_layout,
|
||||||
false,
|
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;
|
y_offset += line.size(line_height).height;
|
||||||
@@ -2316,11 +2229,6 @@ impl Focusable for InputState {
|
|||||||
|
|
||||||
impl Render for InputState {
|
impl Render for InputState {
|
||||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
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()
|
div()
|
||||||
.id("input-state")
|
.id("input-state")
|
||||||
.flex_1()
|
.flex_1()
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ where
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
InputEvent::PressEnter { secondary, shift } => self.on_action_confirm(
|
InputEvent::PressEnter { secondary, .. } => self.on_action_confirm(
|
||||||
&Confirm {
|
&Confirm {
|
||||||
secondary: *secondary,
|
secondary: *secondary,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user