mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-19 18:41:56 +03:00
Remove headers from prompt library picker (#12889)
Also, as a drive-by, we're fixing up/down not working in inline assistant editor. Release Notes: - N/A
This commit is contained in:
parent
b6ea393d14
commit
53b0720d54
@ -1026,8 +1026,16 @@ impl InlineAssistEditor {
|
|||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
EditorEvent::Edited => {
|
EditorEvent::Edited => {
|
||||||
|
let prompt = self.prompt_editor.read(cx).text(cx);
|
||||||
|
if self
|
||||||
|
.prompt_history_ix
|
||||||
|
.map_or(true, |ix| self.prompt_history[ix] != prompt)
|
||||||
|
{
|
||||||
|
self.prompt_history_ix.take();
|
||||||
|
self.pending_prompt = prompt;
|
||||||
|
}
|
||||||
|
|
||||||
self.edited_since_done = true;
|
self.edited_since_done = true;
|
||||||
self.pending_prompt = self.prompt_editor.read(cx).text(cx);
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
EditorEvent::Blurred => {
|
EditorEvent::Blurred => {
|
||||||
@ -1102,13 +1110,19 @@ impl InlineAssistEditor {
|
|||||||
if let Some(ix) = self.prompt_history_ix {
|
if let Some(ix) = self.prompt_history_ix {
|
||||||
if ix > 0 {
|
if ix > 0 {
|
||||||
self.prompt_history_ix = Some(ix - 1);
|
self.prompt_history_ix = Some(ix - 1);
|
||||||
let prompt = self.prompt_history[ix - 1].clone();
|
let prompt = self.prompt_history[ix - 1].as_str();
|
||||||
self.set_prompt(&prompt, cx);
|
self.prompt_editor.update(cx, |editor, cx| {
|
||||||
|
editor.set_text(prompt, cx);
|
||||||
|
editor.move_to_beginning(&Default::default(), cx);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if !self.prompt_history.is_empty() {
|
} else if !self.prompt_history.is_empty() {
|
||||||
self.prompt_history_ix = Some(self.prompt_history.len() - 1);
|
self.prompt_history_ix = Some(self.prompt_history.len() - 1);
|
||||||
let prompt = self.prompt_history[self.prompt_history.len() - 1].clone();
|
let prompt = self.prompt_history[self.prompt_history.len() - 1].as_str();
|
||||||
self.set_prompt(&prompt, cx);
|
self.prompt_editor.update(cx, |editor, cx| {
|
||||||
|
editor.set_text(prompt, cx);
|
||||||
|
editor.move_to_beginning(&Default::default(), cx);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1116,24 +1130,21 @@ impl InlineAssistEditor {
|
|||||||
if let Some(ix) = self.prompt_history_ix {
|
if let Some(ix) = self.prompt_history_ix {
|
||||||
if ix < self.prompt_history.len() - 1 {
|
if ix < self.prompt_history.len() - 1 {
|
||||||
self.prompt_history_ix = Some(ix + 1);
|
self.prompt_history_ix = Some(ix + 1);
|
||||||
let prompt = self.prompt_history[ix + 1].clone();
|
let prompt = self.prompt_history[ix + 1].as_str();
|
||||||
self.set_prompt(&prompt, cx);
|
self.prompt_editor.update(cx, |editor, cx| {
|
||||||
|
editor.set_text(prompt, cx);
|
||||||
|
editor.move_to_end(&Default::default(), cx)
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
self.prompt_history_ix = None;
|
self.prompt_history_ix = None;
|
||||||
let pending_prompt = self.pending_prompt.clone();
|
let prompt = self.pending_prompt.as_str();
|
||||||
self.set_prompt(&pending_prompt, cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_prompt(&mut self, prompt: &str, cx: &mut ViewContext<Self>) {
|
|
||||||
self.prompt_editor.update(cx, |editor, cx| {
|
self.prompt_editor.update(cx, |editor, cx| {
|
||||||
editor.buffer().update(cx, |buffer, cx| {
|
editor.set_text(prompt, cx);
|
||||||
let len = buffer.len(cx);
|
editor.move_to_end(&Default::default(), cx)
|
||||||
buffer.edit([(0..len, prompt)], None, cx);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_prompt_editor(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render_prompt_editor(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
let settings = ThemeSettings::get_global(cx);
|
let settings = ThemeSettings::get_global(cx);
|
||||||
|
@ -13,10 +13,9 @@ use futures::{
|
|||||||
};
|
};
|
||||||
use fuzzy::StringMatchCandidate;
|
use fuzzy::StringMatchCandidate;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, percentage, point, size, Animation, AnimationExt, AnyElement, AppContext,
|
actions, percentage, point, size, Animation, AnimationExt, AppContext, BackgroundExecutor,
|
||||||
BackgroundExecutor, Bounds, DevicePixels, EventEmitter, Global, PromptLevel, ReadGlobal,
|
Bounds, DevicePixels, EventEmitter, Global, PromptLevel, ReadGlobal, Subscription, Task,
|
||||||
Subscription, Task, TitlebarOptions, Transformation, UpdateGlobal, View, WindowBounds,
|
TitlebarOptions, Transformation, UpdateGlobal, View, WindowBounds, WindowHandle, WindowOptions,
|
||||||
WindowHandle, WindowOptions,
|
|
||||||
};
|
};
|
||||||
use heed::{types::SerdeBincode, Database, RoTxn};
|
use heed::{types::SerdeBincode, Database, RoTxn};
|
||||||
use language::{language_settings::SoftWrap, Buffer, LanguageRegistry};
|
use language::{language_settings::SoftWrap, Buffer, LanguageRegistry};
|
||||||
@ -26,6 +25,7 @@ use rope::Rope;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::{
|
use std::{
|
||||||
|
cmp::Reverse,
|
||||||
future::Future,
|
future::Future,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::{atomic::AtomicBool, Arc},
|
sync::{atomic::AtomicBool, Arc},
|
||||||
@ -33,8 +33,8 @@ use std::{
|
|||||||
};
|
};
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
use ui::{
|
use ui::{
|
||||||
div, prelude::*, IconButtonShape, ListHeader, ListItem, ListItemSpacing, ListSubHeader,
|
div, prelude::*, IconButtonShape, ListItem, ListItemSpacing, ParentElement, Render,
|
||||||
ParentElement, Render, SharedString, Styled, TitleBar, Tooltip, ViewContext, VisualContext,
|
SharedString, Styled, TitleBar, Tooltip, ViewContext, VisualContext,
|
||||||
};
|
};
|
||||||
use util::{paths::PROMPTS_DIR, ResultExt, TryFutureExt};
|
use util::{paths::PROMPTS_DIR, ResultExt, TryFutureExt};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@ -124,41 +124,23 @@ struct PromptEditor {
|
|||||||
struct PromptPickerDelegate {
|
struct PromptPickerDelegate {
|
||||||
store: Arc<PromptStore>,
|
store: Arc<PromptStore>,
|
||||||
selected_index: usize,
|
selected_index: usize,
|
||||||
entries: Vec<PromptPickerEntry>,
|
matches: Vec<PromptMetadata>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PromptPickerEvent {
|
enum PromptPickerEvent {
|
||||||
Selected { prompt_id: Option<PromptId> },
|
Selected { prompt_id: PromptId },
|
||||||
Confirmed { prompt_id: PromptId },
|
Confirmed { prompt_id: PromptId },
|
||||||
Deleted { prompt_id: PromptId },
|
Deleted { prompt_id: PromptId },
|
||||||
ToggledDefault { prompt_id: PromptId },
|
ToggledDefault { prompt_id: PromptId },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum PromptPickerEntry {
|
|
||||||
DefaultPromptsHeader,
|
|
||||||
DefaultPromptsEmpty,
|
|
||||||
AllPromptsHeader,
|
|
||||||
AllPromptsEmpty,
|
|
||||||
Prompt(PromptMetadata),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PromptPickerEntry {
|
|
||||||
fn prompt_id(&self) -> Option<PromptId> {
|
|
||||||
match self {
|
|
||||||
PromptPickerEntry::Prompt(metadata) => Some(metadata.id),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventEmitter<PromptPickerEvent> for Picker<PromptPickerDelegate> {}
|
impl EventEmitter<PromptPickerEvent> for Picker<PromptPickerDelegate> {}
|
||||||
|
|
||||||
impl PickerDelegate for PromptPickerDelegate {
|
impl PickerDelegate for PromptPickerDelegate {
|
||||||
type ListItem = AnyElement;
|
type ListItem = ListItem;
|
||||||
|
|
||||||
fn match_count(&self) -> usize {
|
fn match_count(&self) -> usize {
|
||||||
self.entries.len()
|
self.matches.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn selected_index(&self) -> usize {
|
fn selected_index(&self) -> usize {
|
||||||
@ -167,14 +149,11 @@ impl PickerDelegate for PromptPickerDelegate {
|
|||||||
|
|
||||||
fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
|
fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
|
||||||
self.selected_index = ix;
|
self.selected_index = ix;
|
||||||
let prompt_id = if let Some(PromptPickerEntry::Prompt(prompt)) =
|
if let Some(prompt) = self.matches.get(self.selected_index) {
|
||||||
self.entries.get(self.selected_index)
|
cx.emit(PromptPickerEvent::Selected {
|
||||||
{
|
prompt_id: prompt.id,
|
||||||
Some(prompt.id)
|
});
|
||||||
} else {
|
}
|
||||||
None
|
|
||||||
};
|
|
||||||
cx.emit(PromptPickerEvent::Selected { prompt_id });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
|
fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
|
||||||
@ -183,48 +162,24 @@ impl PickerDelegate for PromptPickerDelegate {
|
|||||||
|
|
||||||
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
|
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
|
||||||
let search = self.store.search(query);
|
let search = self.store.search(query);
|
||||||
let prev_prompt_id = self
|
let prev_prompt_id = self.matches.get(self.selected_index).map(|mat| mat.id);
|
||||||
.entries
|
|
||||||
.get(self.selected_index)
|
|
||||||
.and_then(|mat| mat.prompt_id());
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
let (entries, selected_index) = cx
|
let (matches, selected_index) = cx
|
||||||
.background_executor()
|
.background_executor()
|
||||||
.spawn(async move {
|
.spawn(async move {
|
||||||
let prompts = search.await;
|
let matches = search.await;
|
||||||
let (default_prompts, prompts) = prompts
|
|
||||||
.into_iter()
|
|
||||||
.partition::<Vec<_>, _>(|prompt| prompt.default);
|
|
||||||
|
|
||||||
let mut entries = Vec::new();
|
|
||||||
entries.push(PromptPickerEntry::DefaultPromptsHeader);
|
|
||||||
if default_prompts.is_empty() {
|
|
||||||
entries.push(PromptPickerEntry::DefaultPromptsEmpty);
|
|
||||||
} else {
|
|
||||||
entries.extend(default_prompts.into_iter().map(PromptPickerEntry::Prompt));
|
|
||||||
}
|
|
||||||
|
|
||||||
entries.push(PromptPickerEntry::AllPromptsHeader);
|
|
||||||
if prompts.is_empty() {
|
|
||||||
entries.push(PromptPickerEntry::AllPromptsEmpty);
|
|
||||||
} else {
|
|
||||||
entries.extend(prompts.into_iter().map(PromptPickerEntry::Prompt));
|
|
||||||
}
|
|
||||||
|
|
||||||
let selected_index = prev_prompt_id
|
let selected_index = prev_prompt_id
|
||||||
.and_then(|prev_prompt_id| {
|
.and_then(|prev_prompt_id| {
|
||||||
entries
|
matches.iter().position(|entry| entry.id == prev_prompt_id)
|
||||||
.iter()
|
|
||||||
.position(|entry| entry.prompt_id() == Some(prev_prompt_id))
|
|
||||||
})
|
})
|
||||||
.or_else(|| entries.iter().position(|entry| entry.prompt_id().is_some()))
|
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
(entries, selected_index)
|
(matches, selected_index)
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.delegate.entries = entries;
|
this.delegate.matches = matches;
|
||||||
this.delegate.set_selected_index(selected_index, cx);
|
this.delegate.set_selected_index(selected_index, cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
@ -233,7 +188,7 @@ impl PickerDelegate for PromptPickerDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
|
fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
|
||||||
if let Some(PromptPickerEntry::Prompt(prompt)) = self.entries.get(self.selected_index) {
|
if let Some(prompt) = self.matches.get(self.selected_index) {
|
||||||
cx.emit(PromptPickerEvent::Confirmed {
|
cx.emit(PromptPickerEvent::Confirmed {
|
||||||
prompt_id: prompt.id,
|
prompt_id: prompt.id,
|
||||||
});
|
});
|
||||||
@ -248,46 +203,26 @@ impl PickerDelegate for PromptPickerDelegate {
|
|||||||
selected: bool,
|
selected: bool,
|
||||||
cx: &mut ViewContext<Picker<Self>>,
|
cx: &mut ViewContext<Picker<Self>>,
|
||||||
) -> Option<Self::ListItem> {
|
) -> Option<Self::ListItem> {
|
||||||
let prompt = self.entries.get(ix)?;
|
let prompt = self.matches.get(ix)?;
|
||||||
let element = match prompt {
|
|
||||||
PromptPickerEntry::DefaultPromptsHeader => ListHeader::new("Default Prompts")
|
|
||||||
.inset(true)
|
|
||||||
.start_slot(
|
|
||||||
Icon::new(IconName::Sparkle)
|
|
||||||
.color(Color::Muted)
|
|
||||||
.size(IconSize::XSmall),
|
|
||||||
)
|
|
||||||
.selected(selected)
|
|
||||||
.into_any_element(),
|
|
||||||
PromptPickerEntry::DefaultPromptsEmpty => {
|
|
||||||
ListSubHeader::new("Star a prompt to add it to the default context")
|
|
||||||
.inset(true)
|
|
||||||
.selected(selected)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
PromptPickerEntry::AllPromptsHeader => ListHeader::new("All Prompts")
|
|
||||||
.inset(true)
|
|
||||||
.start_slot(
|
|
||||||
Icon::new(IconName::Library)
|
|
||||||
.color(Color::Muted)
|
|
||||||
.size(IconSize::XSmall),
|
|
||||||
)
|
|
||||||
.selected(selected)
|
|
||||||
.into_any_element(),
|
|
||||||
PromptPickerEntry::AllPromptsEmpty => ListSubHeader::new("No prompts")
|
|
||||||
.inset(true)
|
|
||||||
.selected(selected)
|
|
||||||
.into_any_element(),
|
|
||||||
PromptPickerEntry::Prompt(prompt) => {
|
|
||||||
let default = prompt.default;
|
let default = prompt.default;
|
||||||
let prompt_id = prompt.id;
|
let prompt_id = prompt.id;
|
||||||
ListItem::new(ix)
|
let element = ListItem::new(ix)
|
||||||
.inset(true)
|
.inset(true)
|
||||||
.spacing(ListItemSpacing::Sparse)
|
.spacing(ListItemSpacing::Sparse)
|
||||||
.selected(selected)
|
.selected(selected)
|
||||||
.child(h_flex().h_5().line_height(relative(1.)).child(Label::new(
|
.child(h_flex().h_5().line_height(relative(1.)).child(Label::new(
|
||||||
prompt.title.clone().unwrap_or("Untitled".into()),
|
prompt.title.clone().unwrap_or("Untitled".into()),
|
||||||
)))
|
)))
|
||||||
|
.end_slot::<IconButton>(default.then(|| {
|
||||||
|
IconButton::new("toggle-default-prompt", IconName::SparkleFilled)
|
||||||
|
.selected(true)
|
||||||
|
.icon_color(Color::Accent)
|
||||||
|
.shape(IconButtonShape::Square)
|
||||||
|
.tooltip(move |cx| Tooltip::text("Remove from Default Prompt", cx))
|
||||||
|
.on_click(cx.listener(move |_, _, cx| {
|
||||||
|
cx.emit(PromptPickerEvent::ToggledDefault { prompt_id })
|
||||||
|
}))
|
||||||
|
}))
|
||||||
.end_hover_slot(
|
.end_hover_slot(
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
@ -320,10 +255,7 @@ impl PickerDelegate for PromptPickerDelegate {
|
|||||||
cx.emit(PromptPickerEvent::ToggledDefault { prompt_id })
|
cx.emit(PromptPickerEvent::ToggledDefault { prompt_id })
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Some(element)
|
Some(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,11 +281,13 @@ impl PromptLibrary {
|
|||||||
let delegate = PromptPickerDelegate {
|
let delegate = PromptPickerDelegate {
|
||||||
store: store.clone(),
|
store: store.clone(),
|
||||||
selected_index: 0,
|
selected_index: 0,
|
||||||
entries: Vec::new(),
|
matches: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let picker = cx.new_view(|cx| {
|
let picker = cx.new_view(|cx| {
|
||||||
let picker = Picker::list(delegate, cx).modal(false).max_height(None);
|
let picker = Picker::uniform_list(delegate, cx)
|
||||||
|
.modal(false)
|
||||||
|
.max_height(None);
|
||||||
picker.focus(cx);
|
picker.focus(cx);
|
||||||
picker
|
picker
|
||||||
});
|
});
|
||||||
@ -376,11 +310,7 @@ impl PromptLibrary {
|
|||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
PromptPickerEvent::Selected { prompt_id } => {
|
PromptPickerEvent::Selected { prompt_id } => {
|
||||||
if let Some(prompt_id) = *prompt_id {
|
self.load_prompt(*prompt_id, false, cx);
|
||||||
self.load_prompt(prompt_id, false, cx);
|
|
||||||
} else {
|
|
||||||
self.focus_picker(&Default::default(), cx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PromptPickerEvent::Confirmed { prompt_id } => {
|
PromptPickerEvent::Confirmed { prompt_id } => {
|
||||||
self.load_prompt(*prompt_id, true, cx);
|
self.load_prompt(*prompt_id, true, cx);
|
||||||
@ -567,21 +497,23 @@ impl PromptLibrary {
|
|||||||
if let Some(prompt_id) = prompt_id {
|
if let Some(prompt_id) = prompt_id {
|
||||||
if picker
|
if picker
|
||||||
.delegate
|
.delegate
|
||||||
.entries
|
.matches
|
||||||
.get(picker.delegate.selected_index())
|
.get(picker.delegate.selected_index())
|
||||||
.map_or(true, |old_selected_prompt| {
|
.map_or(true, |old_selected_prompt| {
|
||||||
old_selected_prompt.prompt_id() != Some(prompt_id)
|
old_selected_prompt.id != prompt_id
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
if let Some(ix) = picker
|
if let Some(ix) = picker
|
||||||
.delegate
|
.delegate
|
||||||
.entries
|
.matches
|
||||||
.iter()
|
.iter()
|
||||||
.position(|mat| mat.prompt_id() == Some(prompt_id))
|
.position(|mat| mat.id == prompt_id)
|
||||||
{
|
{
|
||||||
picker.set_selected_index(ix, true, cx);
|
picker.set_selected_index(ix, true, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
picker.focus(cx);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cx.notify();
|
cx.notify();
|
||||||
@ -1105,7 +1037,7 @@ impl PromptStore {
|
|||||||
let cached_metadata = self.metadata_cache.read().metadata.clone();
|
let cached_metadata = self.metadata_cache.read().metadata.clone();
|
||||||
let executor = self.executor.clone();
|
let executor = self.executor.clone();
|
||||||
self.executor.spawn(async move {
|
self.executor.spawn(async move {
|
||||||
if query.is_empty() {
|
let mut matches = if query.is_empty() {
|
||||||
cached_metadata
|
cached_metadata
|
||||||
} else {
|
} else {
|
||||||
let candidates = cached_metadata
|
let candidates = cached_metadata
|
||||||
@ -1131,7 +1063,9 @@ impl PromptStore {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mat| cached_metadata[mat.candidate_id].clone())
|
.map(|mat| cached_metadata[mat.candidate_id].clone())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
};
|
||||||
|
matches.sort_by_key(|metadata| Reverse(metadata.default));
|
||||||
|
matches
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6540,6 +6540,8 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let text_layout_details = &self.text_layout_details(cx);
|
let text_layout_details = &self.text_layout_details(cx);
|
||||||
|
let selection_count = self.selections.count();
|
||||||
|
let first_selection = self.selections.first_anchor();
|
||||||
|
|
||||||
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
let line_mode = s.line_mode;
|
let line_mode = s.line_mode;
|
||||||
@ -6556,7 +6558,12 @@ impl Editor {
|
|||||||
);
|
);
|
||||||
selection.collapse_to(cursor, goal);
|
selection.collapse_to(cursor, goal);
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
|
if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
|
||||||
|
{
|
||||||
|
cx.propagate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
|
pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
|
||||||
@ -6700,6 +6707,9 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let text_layout_details = &self.text_layout_details(cx);
|
let text_layout_details = &self.text_layout_details(cx);
|
||||||
|
let selection_count = self.selections.count();
|
||||||
|
let first_selection = self.selections.first_anchor();
|
||||||
|
|
||||||
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
let line_mode = s.line_mode;
|
let line_mode = s.line_mode;
|
||||||
s.move_with(|map, selection| {
|
s.move_with(|map, selection| {
|
||||||
@ -6716,6 +6726,11 @@ impl Editor {
|
|||||||
selection.collapse_to(cursor, goal);
|
selection.collapse_to(cursor, goal);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
|
||||||
|
{
|
||||||
|
cx.propagate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
|
pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
|
||||||
|
@ -84,7 +84,9 @@ impl<T: Copy + Ord> Selection<T> {
|
|||||||
}
|
}
|
||||||
self.goal = new_goal;
|
self.goal = new_goal;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Copy> Selection<T> {
|
||||||
pub fn range(&self) -> Range<T> {
|
pub fn range(&self) -> Range<T> {
|
||||||
self.start..self.end
|
self.start..self.end
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user