Merge branch 'main' into panels
This commit is contained in:
commit
146809eef0
183 changed files with 10202 additions and 5720 deletions
|
@ -1,8 +1,8 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use crate::EditorSettings;
|
||||
use gpui::{Entity, ModelContext};
|
||||
use settings::Settings;
|
||||
use settings::SettingsStore;
|
||||
use smol::Timer;
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct BlinkManager {
|
||||
blink_interval: Duration,
|
||||
|
@ -15,8 +15,8 @@ pub struct BlinkManager {
|
|||
|
||||
impl BlinkManager {
|
||||
pub fn new(blink_interval: Duration, cx: &mut ModelContext<Self>) -> Self {
|
||||
cx.observe_global::<Settings, _>(move |this, cx| {
|
||||
// Make sure we blink the cursors if the setting is re-enabled
|
||||
// Make sure we blink the cursors if the setting is re-enabled
|
||||
cx.observe_global::<SettingsStore, _>(move |this, cx| {
|
||||
this.blink_cursors(this.blink_epoch, cx)
|
||||
})
|
||||
.detach();
|
||||
|
@ -64,7 +64,7 @@ impl BlinkManager {
|
|||
}
|
||||
|
||||
fn blink_cursors(&mut self, epoch: usize, cx: &mut ModelContext<Self>) {
|
||||
if cx.global::<Settings>().cursor_blink {
|
||||
if settings::get::<EditorSettings>(cx).cursor_blink {
|
||||
if epoch == self.blink_epoch && self.enabled && !self.blinking_paused {
|
||||
self.visible = !self.visible;
|
||||
cx.notify();
|
||||
|
|
|
@ -13,8 +13,9 @@ use gpui::{
|
|||
fonts::{FontId, HighlightStyle},
|
||||
Entity, ModelContext, ModelHandle,
|
||||
};
|
||||
use language::{OffsetUtf16, Point, Subscription as BufferSubscription};
|
||||
use settings::Settings;
|
||||
use language::{
|
||||
language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
|
||||
};
|
||||
use std::{any::TypeId, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
|
||||
pub use suggestion_map::Suggestion;
|
||||
use suggestion_map::SuggestionMap;
|
||||
|
@ -276,8 +277,7 @@ impl DisplayMap {
|
|||
.as_singleton()
|
||||
.and_then(|buffer| buffer.read(cx).language())
|
||||
.map(|language| language.name());
|
||||
|
||||
cx.global::<Settings>().tab_size(language_name.as_deref())
|
||||
language_settings(language_name.as_deref(), cx).tab_size
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -844,8 +844,12 @@ pub mod tests {
|
|||
use super::*;
|
||||
use crate::{movement, test::marked_display_snapshot};
|
||||
use gpui::{color::Color, elements::*, test::observe, AppContext};
|
||||
use language::{Buffer, Language, LanguageConfig, SelectionGoal};
|
||||
use language::{
|
||||
language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
|
||||
Buffer, Language, LanguageConfig, SelectionGoal,
|
||||
};
|
||||
use rand::{prelude::*, Rng};
|
||||
use settings::SettingsStore;
|
||||
use smol::stream::StreamExt;
|
||||
use std::{env, sync::Arc};
|
||||
use theme::SyntaxTheme;
|
||||
|
@ -882,9 +886,7 @@ pub mod tests {
|
|||
log::info!("wrap width: {:?}", wrap_width);
|
||||
|
||||
cx.update(|cx| {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.editor_overrides.tab_size = NonZeroU32::new(tab_size);
|
||||
cx.set_global(settings)
|
||||
init_test(cx, |s| s.defaults.tab_size = NonZeroU32::new(tab_size));
|
||||
});
|
||||
|
||||
let buffer = cx.update(|cx| {
|
||||
|
@ -939,9 +941,11 @@ pub mod tests {
|
|||
tab_size = *tab_sizes.choose(&mut rng).unwrap();
|
||||
log::info!("setting tab size to {:?}", tab_size);
|
||||
cx.update(|cx| {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.editor_overrides.tab_size = NonZeroU32::new(tab_size);
|
||||
cx.set_global(settings)
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |s| {
|
||||
s.defaults.tab_size = NonZeroU32::new(tab_size);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
30..=44 => {
|
||||
|
@ -1119,7 +1123,7 @@ pub mod tests {
|
|||
#[gpui::test(retries = 5)]
|
||||
fn test_soft_wraps(cx: &mut AppContext) {
|
||||
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let font_cache = cx.font_cache();
|
||||
|
||||
|
@ -1131,7 +1135,6 @@ pub mod tests {
|
|||
.unwrap();
|
||||
let font_size = 12.0;
|
||||
let wrap_width = Some(64.);
|
||||
cx.set_global(Settings::test(cx));
|
||||
|
||||
let text = "one two three four five\nsix seven eight";
|
||||
let buffer = MultiBuffer::build_simple(text, cx);
|
||||
|
@ -1211,7 +1214,8 @@ pub mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_text_chunks(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let text = sample_text(6, 6, 'a');
|
||||
let buffer = MultiBuffer::build_simple(&text, cx);
|
||||
let family_id = cx
|
||||
|
@ -1225,6 +1229,7 @@ pub mod tests {
|
|||
let font_size = 14.0;
|
||||
let map =
|
||||
cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
|
||||
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(
|
||||
vec![
|
||||
|
@ -1289,11 +1294,8 @@ pub mod tests {
|
|||
.unwrap(),
|
||||
);
|
||||
language.set_theme(&theme);
|
||||
cx.update(|cx| {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.editor_defaults.tab_size = Some(2.try_into().unwrap());
|
||||
cx.set_global(settings);
|
||||
});
|
||||
|
||||
cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap())));
|
||||
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
|
||||
buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
|
||||
|
@ -1382,7 +1384,7 @@ pub mod tests {
|
|||
);
|
||||
language.set_theme(&theme);
|
||||
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
cx.update(|cx| init_test(cx, |_| {}));
|
||||
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
|
||||
buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
|
||||
|
@ -1429,9 +1431,8 @@ pub mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||
cx.update(|cx| init_test(cx, |_| {}));
|
||||
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
let theme = SyntaxTheme::new(vec![
|
||||
("operator".to_string(), Color::red().into()),
|
||||
("string".to_string(), Color::green().into()),
|
||||
|
@ -1510,7 +1511,8 @@ pub mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_clip_point(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::AppContext) {
|
||||
let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx);
|
||||
|
||||
|
@ -1559,7 +1561,7 @@ pub mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_clip_at_line_ends(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
fn assert(text: &str, cx: &mut gpui::AppContext) {
|
||||
let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx);
|
||||
|
@ -1578,7 +1580,8 @@ pub mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_tabs_with_multibyte_chars(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let text = "✅\t\tα\nβ\t\n🏀β\t\tγ";
|
||||
let buffer = MultiBuffer::build_simple(text, cx);
|
||||
let font_cache = cx.font_cache();
|
||||
|
@ -1639,7 +1642,8 @@ pub mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_max_point(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
|
||||
let font_cache = cx.font_cache();
|
||||
let family_id = font_cache
|
||||
|
@ -1718,4 +1722,13 @@ pub mod tests {
|
|||
}
|
||||
chunks
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut AppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
|
||||
cx.foreground().forbid_parking();
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
language::init(cx);
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, f);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -993,7 +993,7 @@ mod tests {
|
|||
use crate::multi_buffer::MultiBuffer;
|
||||
use gpui::{elements::Empty, Element};
|
||||
use rand::prelude::*;
|
||||
use settings::Settings;
|
||||
use settings::SettingsStore;
|
||||
use std::env;
|
||||
use util::RandomCharIter;
|
||||
|
||||
|
@ -1013,7 +1013,7 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_basic_blocks(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
let family_id = cx
|
||||
.font_cache()
|
||||
|
@ -1189,7 +1189,7 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_blocks_on_wrapped_lines(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
let family_id = cx
|
||||
.font_cache()
|
||||
|
@ -1239,7 +1239,7 @@ mod tests {
|
|||
|
||||
#[gpui::test(iterations = 100)]
|
||||
fn test_random_blocks(cx: &mut gpui::AppContext, mut rng: StdRng) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
let operations = env::var("OPERATIONS")
|
||||
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
|
||||
|
@ -1647,6 +1647,11 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
theme::init((), cx);
|
||||
}
|
||||
|
||||
impl TransformBlock {
|
||||
fn as_custom(&self) -> Option<&Block> {
|
||||
match self {
|
||||
|
|
|
@ -1204,7 +1204,7 @@ mod tests {
|
|||
use crate::{MultiBuffer, ToPoint};
|
||||
use collections::HashSet;
|
||||
use rand::prelude::*;
|
||||
use settings::Settings;
|
||||
use settings::SettingsStore;
|
||||
use std::{cmp::Reverse, env, mem, sync::Arc};
|
||||
use sum_tree::TreeMap;
|
||||
use util::test::sample_text;
|
||||
|
@ -1213,7 +1213,7 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_basic_folds(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
|
||||
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
|
||||
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
||||
|
@ -1286,7 +1286,7 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_adjacent_folds(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
let buffer = MultiBuffer::build_simple("abcdefghijkl", cx);
|
||||
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
|
||||
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
||||
|
@ -1349,7 +1349,7 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_merging_folds_via_edit(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
|
||||
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
|
||||
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
||||
|
@ -1400,7 +1400,7 @@ mod tests {
|
|||
|
||||
#[gpui::test(iterations = 100)]
|
||||
fn test_random_folds(cx: &mut gpui::AppContext, mut rng: StdRng) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
let operations = env::var("OPERATIONS")
|
||||
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
|
||||
.unwrap_or(10);
|
||||
|
@ -1676,6 +1676,10 @@ mod tests {
|
|||
assert_eq!(snapshot.buffer_rows(3).collect::<Vec<_>>(), [Some(6)]);
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
}
|
||||
|
||||
impl FoldMap {
|
||||
fn merged_fold_ranges(&self) -> Vec<Range<usize>> {
|
||||
let buffer = self.buffer.lock().clone();
|
||||
|
|
|
@ -578,7 +578,7 @@ mod tests {
|
|||
use crate::{display_map::fold_map::FoldMap, MultiBuffer};
|
||||
use gpui::AppContext;
|
||||
use rand::{prelude::StdRng, Rng};
|
||||
use settings::Settings;
|
||||
use settings::SettingsStore;
|
||||
use std::{
|
||||
env,
|
||||
ops::{Bound, RangeBounds},
|
||||
|
@ -631,7 +631,8 @@ mod tests {
|
|||
|
||||
#[gpui::test(iterations = 100)]
|
||||
fn test_random_suggestions(cx: &mut AppContext, mut rng: StdRng) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
let operations = env::var("OPERATIONS")
|
||||
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
|
||||
.unwrap_or(10);
|
||||
|
@ -834,6 +835,11 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut AppContext) {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
theme::init((), cx);
|
||||
}
|
||||
|
||||
impl SuggestionMap {
|
||||
pub fn randomly_mutate(
|
||||
&self,
|
||||
|
|
|
@ -1043,16 +1043,16 @@ mod tests {
|
|||
};
|
||||
use gpui::test::observe;
|
||||
use rand::prelude::*;
|
||||
use settings::Settings;
|
||||
use settings::SettingsStore;
|
||||
use smol::stream::StreamExt;
|
||||
use std::{cmp, env, num::NonZeroU32};
|
||||
use text::Rope;
|
||||
|
||||
#[gpui::test(iterations = 100)]
|
||||
async fn test_random_wraps(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx);
|
||||
|
||||
cx.foreground().set_block_on_ticks(0..=50);
|
||||
cx.foreground().forbid_parking();
|
||||
let operations = env::var("OPERATIONS")
|
||||
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
|
||||
.unwrap_or(10);
|
||||
|
@ -1287,6 +1287,14 @@ mod tests {
|
|||
wrap_map.read_with(cx, |map, _| assert!(map.pending_edits.is_empty()));
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
cx.update(|cx| {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
theme::init((), cx);
|
||||
});
|
||||
}
|
||||
|
||||
fn wrap_text(
|
||||
unwrapped_text: &str,
|
||||
wrap_width: Option<f32>,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
mod blink_manager;
|
||||
pub mod display_map;
|
||||
mod editor_settings;
|
||||
mod element;
|
||||
|
||||
mod git;
|
||||
|
@ -19,15 +20,17 @@ mod editor_tests;
|
|||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub mod test;
|
||||
|
||||
use ::git::diff::DiffHunk;
|
||||
use aho_corasick::AhoCorasick;
|
||||
use anyhow::{anyhow, Result};
|
||||
use blink_manager::BlinkManager;
|
||||
use client::ClickhouseEvent;
|
||||
use client::{ClickhouseEvent, TelemetrySettings};
|
||||
use clock::ReplicaId;
|
||||
use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
|
||||
use copilot::Copilot;
|
||||
pub use display_map::DisplayPoint;
|
||||
use display_map::*;
|
||||
pub use editor_settings::EditorSettings;
|
||||
pub use element::*;
|
||||
use futures::FutureExt;
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
|
@ -51,6 +54,7 @@ pub use items::MAX_TAB_TITLE_LEN;
|
|||
use itertools::Itertools;
|
||||
pub use language::{char_kind, CharKind};
|
||||
use language::{
|
||||
language_settings::{self, all_language_settings},
|
||||
AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, CursorShape,
|
||||
Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language, OffsetRangeExt,
|
||||
OffsetUtf16, Point, Selection, SelectionGoal, TransactionId,
|
||||
|
@ -70,7 +74,7 @@ use scroll::{
|
|||
};
|
||||
use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use settings::SettingsStore;
|
||||
use smallvec::SmallVec;
|
||||
use snippet::Snippet;
|
||||
use std::{
|
||||
|
@ -85,7 +89,7 @@ use std::{
|
|||
time::{Duration, Instant},
|
||||
};
|
||||
pub use sum_tree::Bias;
|
||||
use theme::{DiagnosticStyle, Theme};
|
||||
use theme::{DiagnosticStyle, Theme, ThemeSettings};
|
||||
use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
|
||||
use workspace::{ItemNavHistory, ViewId, Workspace};
|
||||
|
||||
|
@ -286,7 +290,12 @@ pub enum Direction {
|
|||
Next,
|
||||
}
|
||||
|
||||
pub fn init_settings(cx: &mut AppContext) {
|
||||
settings::register::<EditorSettings>(cx);
|
||||
}
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
init_settings(cx);
|
||||
cx.add_action(Editor::new_file);
|
||||
cx.add_action(Editor::cancel);
|
||||
cx.add_action(Editor::newline);
|
||||
|
@ -436,7 +445,7 @@ pub enum EditorMode {
|
|||
Full,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SoftWrap {
|
||||
None,
|
||||
EditorWidth,
|
||||
|
@ -471,7 +480,7 @@ pub struct Editor {
|
|||
select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
|
||||
ime_transaction: Option<TransactionId>,
|
||||
active_diagnostics: Option<ActiveDiagnosticGroup>,
|
||||
soft_wrap_mode_override: Option<settings::SoftWrap>,
|
||||
soft_wrap_mode_override: Option<language_settings::SoftWrap>,
|
||||
get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
|
||||
override_text_style: Option<Box<OverrideTextStyle>>,
|
||||
project: Option<ModelHandle<Project>>,
|
||||
|
@ -516,6 +525,15 @@ pub struct EditorSnapshot {
|
|||
ongoing_scroll: OngoingScroll,
|
||||
}
|
||||
|
||||
impl EditorSnapshot {
|
||||
fn has_scrollbar_info(&self) -> bool {
|
||||
self.buffer_snapshot
|
||||
.git_diff_hunks_in_range(0..self.max_point().row())
|
||||
.next()
|
||||
.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct SelectionHistoryEntry {
|
||||
selections: Arc<[Selection<Anchor>]>,
|
||||
|
@ -1229,8 +1247,8 @@ impl Editor {
|
|||
) -> Self {
|
||||
let editor_view_id = cx.view_id();
|
||||
let display_map = cx.add_model(|cx| {
|
||||
let settings = cx.global::<Settings>();
|
||||
let style = build_style(&*settings, get_field_editor_theme.as_deref(), None, cx);
|
||||
let settings = settings::get::<ThemeSettings>(cx);
|
||||
let style = build_style(settings, get_field_editor_theme.as_deref(), None, cx);
|
||||
DisplayMap::new(
|
||||
buffer.clone(),
|
||||
style.text.font_id,
|
||||
|
@ -1247,7 +1265,17 @@ impl Editor {
|
|||
let blink_manager = cx.add_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
|
||||
|
||||
let soft_wrap_mode_override =
|
||||
(mode == EditorMode::SingleLine).then(|| settings::SoftWrap::None);
|
||||
(mode == EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
|
||||
|
||||
let mut project_subscription = None;
|
||||
if mode == EditorMode::Full && buffer.read(cx).is_singleton() {
|
||||
if let Some(project) = project.as_ref() {
|
||||
project_subscription = Some(cx.observe(project, |_, _, cx| {
|
||||
cx.emit(Event::TitleChanged);
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
let mut this = Self {
|
||||
handle: cx.weak_handle(),
|
||||
buffer: buffer.clone(),
|
||||
|
@ -1301,9 +1329,14 @@ impl Editor {
|
|||
cx.subscribe(&buffer, Self::on_buffer_event),
|
||||
cx.observe(&display_map, Self::on_display_map_changed),
|
||||
cx.observe(&blink_manager, |_, _, cx| cx.notify()),
|
||||
cx.observe_global::<Settings, _>(Self::settings_changed),
|
||||
cx.observe_global::<SettingsStore, _>(Self::settings_changed),
|
||||
],
|
||||
};
|
||||
|
||||
if let Some(project_subscription) = project_subscription {
|
||||
this._subscriptions.push(project_subscription);
|
||||
}
|
||||
|
||||
this.end_selection(cx);
|
||||
this.scroll_manager.show_scrollbar(cx);
|
||||
|
||||
|
@ -1315,7 +1348,7 @@ impl Editor {
|
|||
cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
|
||||
}
|
||||
|
||||
this.report_editor_event("open", cx);
|
||||
this.report_editor_event("open", None, cx);
|
||||
this
|
||||
}
|
||||
|
||||
|
@ -1395,7 +1428,7 @@ impl Editor {
|
|||
|
||||
fn style(&self, cx: &AppContext) -> EditorStyle {
|
||||
build_style(
|
||||
cx.global::<Settings>(),
|
||||
settings::get::<ThemeSettings>(cx),
|
||||
self.get_field_editor_theme.as_deref(),
|
||||
self.override_text_style.as_deref(),
|
||||
cx,
|
||||
|
@ -2353,7 +2386,7 @@ impl Editor {
|
|||
}
|
||||
|
||||
fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
|
||||
if !cx.global::<Settings>().show_completions_on_input {
|
||||
if !settings::get::<EditorSettings>(cx).show_completions_on_input {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3082,6 +3115,8 @@ impl Editor {
|
|||
copilot
|
||||
.update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
|
||||
.detach_and_log_err(cx);
|
||||
|
||||
self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
|
||||
}
|
||||
self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
|
||||
cx.notify();
|
||||
|
@ -3099,6 +3134,8 @@ impl Editor {
|
|||
copilot.discard_completions(&self.copilot_state.completions, cx)
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
|
||||
self.report_copilot_event(None, false, cx)
|
||||
}
|
||||
|
||||
self.display_map
|
||||
|
@ -3116,17 +3153,12 @@ impl Editor {
|
|||
snapshot: &MultiBufferSnapshot,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> bool {
|
||||
let settings = cx.global::<Settings>();
|
||||
|
||||
let path = snapshot.file_at(location).map(|file| file.path());
|
||||
let path = snapshot.file_at(location).map(|file| file.path().as_ref());
|
||||
let language_name = snapshot
|
||||
.language_at(location)
|
||||
.map(|language| language.name());
|
||||
if !settings.show_copilot_suggestions(language_name.as_deref(), path.map(|p| p.as_ref())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
let settings = all_language_settings(cx);
|
||||
settings.copilot_enabled(language_name.as_deref(), path)
|
||||
}
|
||||
|
||||
fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
|
||||
|
@ -3427,12 +3459,9 @@ impl Editor {
|
|||
{
|
||||
let indent_size =
|
||||
buffer.indent_size_for_line(line_buffer_range.start.row);
|
||||
let language_name = buffer
|
||||
.language_at(line_buffer_range.start)
|
||||
.map(|language| language.name());
|
||||
let indent_len = match indent_size.kind {
|
||||
IndentKind::Space => {
|
||||
cx.global::<Settings>().tab_size(language_name.as_deref())
|
||||
buffer.settings_at(line_buffer_range.start, cx).tab_size
|
||||
}
|
||||
IndentKind::Tab => NonZeroU32::new(1).unwrap(),
|
||||
};
|
||||
|
@ -3544,12 +3573,11 @@ impl Editor {
|
|||
}
|
||||
|
||||
// Otherwise, insert a hard or soft tab.
|
||||
let settings = cx.global::<Settings>();
|
||||
let language_name = buffer.language_at(cursor, cx).map(|l| l.name());
|
||||
let tab_size = if settings.hard_tabs(language_name.as_deref()) {
|
||||
let settings = buffer.settings_at(cursor, cx);
|
||||
let tab_size = if settings.hard_tabs {
|
||||
IndentSize::tab()
|
||||
} else {
|
||||
let tab_size = settings.tab_size(language_name.as_deref()).get();
|
||||
let tab_size = settings.tab_size.get();
|
||||
let char_column = snapshot
|
||||
.text_for_range(Point::new(cursor.row, 0)..cursor)
|
||||
.flat_map(str::chars)
|
||||
|
@ -3602,10 +3630,9 @@ impl Editor {
|
|||
delta_for_start_row: u32,
|
||||
cx: &AppContext,
|
||||
) -> u32 {
|
||||
let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
|
||||
let settings = cx.global::<Settings>();
|
||||
let tab_size = settings.tab_size(language_name.as_deref()).get();
|
||||
let indent_kind = if settings.hard_tabs(language_name.as_deref()) {
|
||||
let settings = buffer.settings_at(selection.start, cx);
|
||||
let tab_size = settings.tab_size.get();
|
||||
let indent_kind = if settings.hard_tabs {
|
||||
IndentKind::Tab
|
||||
} else {
|
||||
IndentKind::Space
|
||||
|
@ -3674,11 +3701,8 @@ impl Editor {
|
|||
let buffer = self.buffer.read(cx);
|
||||
let snapshot = buffer.snapshot(cx);
|
||||
for selection in &selections {
|
||||
let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
|
||||
let tab_size = cx
|
||||
.global::<Settings>()
|
||||
.tab_size(language_name.as_deref())
|
||||
.get();
|
||||
let settings = buffer.settings_at(selection.start, cx);
|
||||
let tab_size = settings.tab_size.get();
|
||||
let mut rows = selection.spanned_rows(false, &display_map);
|
||||
|
||||
// Avoid re-outdenting a row that has already been outdented by a
|
||||
|
@ -5546,68 +5570,91 @@ impl Editor {
|
|||
}
|
||||
|
||||
fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
|
||||
self.go_to_hunk_impl(Direction::Next, cx)
|
||||
}
|
||||
|
||||
fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
|
||||
self.go_to_hunk_impl(Direction::Prev, cx)
|
||||
}
|
||||
|
||||
pub fn go_to_hunk_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
|
||||
let snapshot = self
|
||||
.display_map
|
||||
.update(cx, |display_map, cx| display_map.snapshot(cx));
|
||||
let selection = self.selections.newest::<Point>(cx);
|
||||
|
||||
fn seek_in_direction(
|
||||
this: &mut Editor,
|
||||
snapshot: &DisplaySnapshot,
|
||||
initial_point: Point,
|
||||
is_wrapped: bool,
|
||||
direction: Direction,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> bool {
|
||||
let hunks = if direction == Direction::Next {
|
||||
if !self.seek_in_direction(
|
||||
&snapshot,
|
||||
selection.head(),
|
||||
false,
|
||||
snapshot
|
||||
.buffer_snapshot
|
||||
.git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
|
||||
cx,
|
||||
) {
|
||||
let wrapped_point = Point::zero();
|
||||
self.seek_in_direction(
|
||||
&snapshot,
|
||||
wrapped_point,
|
||||
true,
|
||||
snapshot
|
||||
.buffer_snapshot
|
||||
.git_diff_hunks_in_range(initial_point.row..u32::MAX, false)
|
||||
} else {
|
||||
snapshot
|
||||
.buffer_snapshot
|
||||
.git_diff_hunks_in_range(0..initial_point.row, true)
|
||||
};
|
||||
|
||||
let display_point = initial_point.to_display_point(snapshot);
|
||||
let mut hunks = hunks
|
||||
.map(|hunk| diff_hunk_to_display(hunk, &snapshot))
|
||||
.skip_while(|hunk| {
|
||||
if is_wrapped {
|
||||
false
|
||||
} else {
|
||||
hunk.contains_display_row(display_point.row())
|
||||
}
|
||||
})
|
||||
.dedup();
|
||||
|
||||
if let Some(hunk) = hunks.next() {
|
||||
this.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
let row = hunk.start_display_row();
|
||||
let point = DisplayPoint::new(row, 0);
|
||||
s.select_display_ranges([point..point]);
|
||||
});
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
.git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if !seek_in_direction(self, &snapshot, selection.head(), false, direction, cx) {
|
||||
let wrapped_point = match direction {
|
||||
Direction::Next => Point::zero(),
|
||||
Direction::Prev => snapshot.buffer_snapshot.max_point(),
|
||||
};
|
||||
seek_in_direction(self, &snapshot, wrapped_point, true, direction, cx);
|
||||
fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
|
||||
let snapshot = self
|
||||
.display_map
|
||||
.update(cx, |display_map, cx| display_map.snapshot(cx));
|
||||
let selection = self.selections.newest::<Point>(cx);
|
||||
|
||||
if !self.seek_in_direction(
|
||||
&snapshot,
|
||||
selection.head(),
|
||||
false,
|
||||
snapshot
|
||||
.buffer_snapshot
|
||||
.git_diff_hunks_in_range_rev(0..selection.head().row),
|
||||
cx,
|
||||
) {
|
||||
let wrapped_point = snapshot.buffer_snapshot.max_point();
|
||||
self.seek_in_direction(
|
||||
&snapshot,
|
||||
wrapped_point,
|
||||
true,
|
||||
snapshot
|
||||
.buffer_snapshot
|
||||
.git_diff_hunks_in_range_rev(0..wrapped_point.row),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn seek_in_direction(
|
||||
&mut self,
|
||||
snapshot: &DisplaySnapshot,
|
||||
initial_point: Point,
|
||||
is_wrapped: bool,
|
||||
hunks: impl Iterator<Item = DiffHunk<u32>>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> bool {
|
||||
let display_point = initial_point.to_display_point(snapshot);
|
||||
let mut hunks = hunks
|
||||
.map(|hunk| diff_hunk_to_display(hunk, &snapshot))
|
||||
.skip_while(|hunk| {
|
||||
if is_wrapped {
|
||||
false
|
||||
} else {
|
||||
hunk.contains_display_row(display_point.row())
|
||||
}
|
||||
})
|
||||
.dedup();
|
||||
|
||||
if let Some(hunk) = hunks.next() {
|
||||
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
let row = hunk.start_display_row();
|
||||
let point = DisplayPoint::new(row, 0);
|
||||
s.select_display_ranges([point..point]);
|
||||
});
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6439,27 +6486,24 @@ impl Editor {
|
|||
}
|
||||
|
||||
pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
|
||||
let language_name = self
|
||||
.buffer
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.and_then(|singleton_buffer| singleton_buffer.read(cx).language())
|
||||
.map(|l| l.name());
|
||||
|
||||
let settings = cx.global::<Settings>();
|
||||
let settings = self.buffer.read(cx).settings_at(0, cx);
|
||||
let mode = self
|
||||
.soft_wrap_mode_override
|
||||
.unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
|
||||
.unwrap_or_else(|| settings.soft_wrap);
|
||||
match mode {
|
||||
settings::SoftWrap::None => SoftWrap::None,
|
||||
settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
|
||||
settings::SoftWrap::PreferredLineLength => {
|
||||
SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
|
||||
language_settings::SoftWrap::None => SoftWrap::None,
|
||||
language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
|
||||
language_settings::SoftWrap::PreferredLineLength => {
|
||||
SoftWrap::Column(settings.preferred_line_length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
|
||||
pub fn set_soft_wrap_mode(
|
||||
&mut self,
|
||||
mode: language_settings::SoftWrap,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.soft_wrap_mode_override = Some(mode);
|
||||
cx.notify();
|
||||
}
|
||||
|
@ -6474,8 +6518,8 @@ impl Editor {
|
|||
self.soft_wrap_mode_override.take();
|
||||
} else {
|
||||
let soft_wrap = match self.soft_wrap_mode(cx) {
|
||||
SoftWrap::None => settings::SoftWrap::EditorWidth,
|
||||
SoftWrap::EditorWidth | SoftWrap::Column(_) => settings::SoftWrap::None,
|
||||
SoftWrap::None => language_settings::SoftWrap::EditorWidth,
|
||||
SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
|
||||
};
|
||||
self.soft_wrap_mode_override = Some(soft_wrap);
|
||||
}
|
||||
|
@ -6550,8 +6594,8 @@ impl Editor {
|
|||
let buffer = &snapshot.buffer_snapshot;
|
||||
let start = buffer.anchor_before(0);
|
||||
let end = buffer.anchor_after(buffer.len());
|
||||
let theme = cx.global::<Settings>().theme.as_ref();
|
||||
self.background_highlights_in_range(start..end, &snapshot, theme)
|
||||
let theme = theme::current(cx);
|
||||
self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
|
||||
}
|
||||
|
||||
fn document_highlights_for_position<'a>(
|
||||
|
@ -6861,44 +6905,88 @@ impl Editor {
|
|||
.collect()
|
||||
}
|
||||
|
||||
fn report_editor_event(&self, name: &'static str, cx: &AppContext) {
|
||||
if let Some((project, file)) = self.project.as_ref().zip(
|
||||
self.buffer
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.and_then(|b| b.read(cx).file()),
|
||||
) {
|
||||
let settings = cx.global::<Settings>();
|
||||
fn report_copilot_event(
|
||||
&self,
|
||||
suggestion_id: Option<String>,
|
||||
suggestion_accepted: bool,
|
||||
cx: &AppContext,
|
||||
) {
|
||||
let Some(project) = &self.project else {
|
||||
return
|
||||
};
|
||||
|
||||
let extension = Path::new(file.file_name(cx))
|
||||
.extension()
|
||||
.and_then(|e| e.to_str());
|
||||
let telemetry = project.read(cx).client().telemetry().clone();
|
||||
telemetry.report_mixpanel_event(
|
||||
match name {
|
||||
"open" => "open editor",
|
||||
"save" => "save editor",
|
||||
_ => name,
|
||||
},
|
||||
json!({ "File Extension": extension, "Vim Mode": settings.vim_mode, "In Clickhouse": true }),
|
||||
settings.telemetry(),
|
||||
);
|
||||
let event = ClickhouseEvent::Editor {
|
||||
file_extension: extension.map(ToString::to_string),
|
||||
vim_mode: settings.vim_mode,
|
||||
operation: name,
|
||||
copilot_enabled: settings.features.copilot,
|
||||
copilot_enabled_for_language: settings.show_copilot_suggestions(
|
||||
self.language_at(0, cx)
|
||||
.map(|language| language.name())
|
||||
.as_deref(),
|
||||
self.file_at(0, cx)
|
||||
.map(|file| file.path().clone())
|
||||
.as_deref(),
|
||||
),
|
||||
};
|
||||
telemetry.report_clickhouse_event(event, settings.telemetry())
|
||||
}
|
||||
// If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
|
||||
let file_extension = self
|
||||
.buffer
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.and_then(|b| b.read(cx).file())
|
||||
.and_then(|file| Path::new(file.file_name(cx)).extension())
|
||||
.and_then(|e| e.to_str())
|
||||
.map(|a| a.to_string());
|
||||
|
||||
let telemetry = project.read(cx).client().telemetry().clone();
|
||||
let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
|
||||
|
||||
let event = ClickhouseEvent::Copilot {
|
||||
suggestion_id,
|
||||
suggestion_accepted,
|
||||
file_extension,
|
||||
};
|
||||
telemetry.report_clickhouse_event(event, telemetry_settings);
|
||||
}
|
||||
|
||||
fn report_editor_event(
|
||||
&self,
|
||||
name: &'static str,
|
||||
file_extension: Option<String>,
|
||||
cx: &AppContext,
|
||||
) {
|
||||
let Some(project) = &self.project else {
|
||||
return
|
||||
};
|
||||
|
||||
// If None, we are in a file without an extension
|
||||
let file_extension = file_extension.or(self
|
||||
.buffer
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.and_then(|b| b.read(cx).file())
|
||||
.and_then(|file| Path::new(file.file_name(cx)).extension())
|
||||
.and_then(|e| e.to_str())
|
||||
.map(|a| a.to_string()));
|
||||
|
||||
let vim_mode = cx
|
||||
.global::<SettingsStore>()
|
||||
.untyped_user_settings()
|
||||
.get("vim_mode")
|
||||
== Some(&serde_json::Value::Bool(true));
|
||||
let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
|
||||
let copilot_enabled = all_language_settings(cx).copilot_enabled(None, None);
|
||||
let copilot_enabled_for_language = self
|
||||
.buffer
|
||||
.read(cx)
|
||||
.settings_at(0, cx)
|
||||
.show_copilot_suggestions;
|
||||
|
||||
let telemetry = project.read(cx).client().telemetry().clone();
|
||||
telemetry.report_mixpanel_event(
|
||||
match name {
|
||||
"open" => "open editor",
|
||||
"save" => "save editor",
|
||||
_ => name,
|
||||
},
|
||||
json!({ "File Extension": file_extension, "Vim Mode": vim_mode, "In Clickhouse": true }),
|
||||
telemetry_settings,
|
||||
);
|
||||
let event = ClickhouseEvent::Editor {
|
||||
file_extension,
|
||||
vim_mode,
|
||||
operation: name,
|
||||
copilot_enabled,
|
||||
copilot_enabled_for_language,
|
||||
};
|
||||
telemetry.report_clickhouse_event(event, telemetry_settings)
|
||||
}
|
||||
|
||||
/// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
|
||||
|
@ -6930,7 +7018,7 @@ impl Editor {
|
|||
let mut lines = Vec::new();
|
||||
let mut line: VecDeque<Chunk> = VecDeque::new();
|
||||
|
||||
let theme = &cx.global::<Settings>().theme.editor.syntax;
|
||||
let theme = &theme::current(cx).editor.syntax;
|
||||
|
||||
for chunk in chunks {
|
||||
let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
|
||||
|
@ -7352,7 +7440,7 @@ impl View for Editor {
|
|||
}
|
||||
|
||||
fn build_style(
|
||||
settings: &Settings,
|
||||
settings: &ThemeSettings,
|
||||
get_field_editor_theme: Option<&GetFieldEditorTheme>,
|
||||
override_text_style: Option<&OverrideTextStyle>,
|
||||
cx: &AppContext,
|
||||
|
@ -7382,7 +7470,7 @@ fn build_style(
|
|||
let font_id = font_cache
|
||||
.select_font(font_family_id, &font_properties)
|
||||
.unwrap();
|
||||
let font_size = settings.buffer_font_size;
|
||||
let font_size = settings.buffer_font_size(cx);
|
||||
EditorStyle {
|
||||
text: TextStyle {
|
||||
color: settings.theme.editor.text_color,
|
||||
|
@ -7552,10 +7640,10 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend
|
|||
}
|
||||
|
||||
Arc::new(move |cx: &mut BlockContext| {
|
||||
let settings = cx.global::<Settings>();
|
||||
let settings = settings::get::<ThemeSettings>(cx);
|
||||
let theme = &settings.theme.editor;
|
||||
let style = diagnostic_style(diagnostic.severity, is_valid, theme);
|
||||
let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
|
||||
let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
|
||||
Flex::column()
|
||||
.with_children(highlighted_lines.iter().map(|(line, highlights)| {
|
||||
Label::new(
|
||||
|
|
43
crates/editor/src/editor_settings.rs
Normal file
43
crates/editor/src/editor_settings.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Setting;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EditorSettings {
|
||||
pub cursor_blink: bool,
|
||||
pub hover_popover_enabled: bool,
|
||||
pub show_completions_on_input: bool,
|
||||
pub show_scrollbars: ShowScrollbars,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ShowScrollbars {
|
||||
#[default]
|
||||
Auto,
|
||||
System,
|
||||
Always,
|
||||
Never,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct EditorSettingsContent {
|
||||
pub cursor_blink: Option<bool>,
|
||||
pub hover_popover_enabled: Option<bool>,
|
||||
pub show_completions_on_input: Option<bool>,
|
||||
pub show_scrollbars: Option<ShowScrollbars>,
|
||||
}
|
||||
|
||||
impl Setting for EditorSettings {
|
||||
const KEY: Option<&'static str> = None;
|
||||
|
||||
type FileContent = EditorSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &gpui::AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
}
|
||||
}
|
|
@ -12,10 +12,12 @@ use gpui::{
|
|||
serde_json, TestAppContext,
|
||||
};
|
||||
use indoc::indoc;
|
||||
use language::{BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegistry, Point};
|
||||
use language::{
|
||||
language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
|
||||
BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegistry, Point,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use project::FakeFs;
|
||||
use settings::EditorSettings;
|
||||
use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
|
||||
use unindent::Unindent;
|
||||
use util::{
|
||||
|
@ -29,7 +31,8 @@ use workspace::{
|
|||
|
||||
#[gpui::test]
|
||||
fn test_edit_events(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let buffer = cx.add_model(|cx| {
|
||||
let mut buffer = language::Buffer::new(0, "123456", cx);
|
||||
buffer.set_group_interval(Duration::from_secs(1));
|
||||
|
@ -156,7 +159,8 @@ fn test_edit_events(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut now = Instant::now();
|
||||
let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
|
||||
let group_interval = buffer.read_with(cx, |buffer, _| buffer.transaction_group_interval());
|
||||
|
@ -226,7 +230,8 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_ime_composition(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let buffer = cx.add_model(|cx| {
|
||||
let mut buffer = language::Buffer::new(0, "abcde", cx);
|
||||
// Ensure automatic grouping doesn't occur.
|
||||
|
@ -328,7 +333,7 @@ fn test_ime_composition(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_selection_with_mouse(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
|
||||
|
@ -395,7 +400,8 @@ fn test_selection_with_mouse(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_canceling_pending_selection(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
|
||||
build_editor(buffer, cx)
|
||||
|
@ -429,6 +435,8 @@ fn test_canceling_pending_selection(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_clone(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (text, selection_ranges) = marked_text_ranges(
|
||||
indoc! {"
|
||||
one
|
||||
|
@ -439,7 +447,6 @@ fn test_clone(cx: &mut TestAppContext) {
|
|||
"},
|
||||
true,
|
||||
);
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&text, cx);
|
||||
|
@ -487,7 +494,8 @@ fn test_clone(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_navigation_history(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
cx.set_global(DragAndDrop::<Workspace>::default());
|
||||
use workspace::item::Item;
|
||||
|
||||
|
@ -600,7 +608,8 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_cancel(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
|
||||
build_editor(buffer, cx)
|
||||
|
@ -642,7 +651,8 @@ fn test_cancel(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_fold_action(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(
|
||||
&"
|
||||
|
@ -731,7 +741,8 @@ fn test_fold_action(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_move_cursor(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
|
||||
let (_, view) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
|
||||
|
||||
|
@ -806,7 +817,8 @@ fn test_move_cursor(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
|
||||
build_editor(buffer.clone(), cx)
|
||||
|
@ -910,7 +922,8 @@ fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
|
||||
build_editor(buffer.clone(), cx)
|
||||
|
@ -959,7 +972,8 @@ fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_beginning_end_of_line(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("abc\n def", cx);
|
||||
build_editor(buffer, cx)
|
||||
|
@ -1121,7 +1135,8 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
|
||||
build_editor(buffer, cx)
|
||||
|
@ -1172,7 +1187,8 @@ fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
|
||||
build_editor(buffer, cx)
|
||||
|
@ -1229,6 +1245,7 @@ fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
|
||||
|
@ -1343,6 +1360,7 @@ async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.set_state("one «two threeˇ» four");
|
||||
cx.update_editor(|editor, cx| {
|
||||
|
@ -1353,7 +1371,8 @@ async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("one two three four", cx);
|
||||
build_editor(buffer.clone(), cx)
|
||||
|
@ -1388,7 +1407,8 @@ fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_newline(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
|
||||
build_editor(buffer.clone(), cx)
|
||||
|
@ -1410,7 +1430,8 @@ fn test_newline(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_newline_with_old_selections(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(
|
||||
"
|
||||
|
@ -1491,11 +1512,8 @@ fn test_newline_with_old_selections(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_newline_above(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||
settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap());
|
||||
});
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.tab_size = NonZeroU32::new(4)
|
||||
});
|
||||
|
||||
let language = Arc::new(
|
||||
|
@ -1506,8 +1524,9 @@ async fn test_newline_above(cx: &mut gpui::TestAppContext) {
|
|||
.with_indents_query(r#"(_ "(" ")" @end) @indent"#)
|
||||
.unwrap(),
|
||||
);
|
||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
||||
cx.set_state(indoc! {"
|
||||
const a: ˇA = (
|
||||
(ˇ
|
||||
|
@ -1516,6 +1535,7 @@ async fn test_newline_above(cx: &mut gpui::TestAppContext) {
|
|||
)ˇ
|
||||
ˇ);ˇ
|
||||
"});
|
||||
|
||||
cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
|
||||
cx.assert_editor_state(indoc! {"
|
||||
ˇ
|
||||
|
@ -1540,11 +1560,8 @@ async fn test_newline_above(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_newline_below(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||
settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap());
|
||||
});
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.tab_size = NonZeroU32::new(4)
|
||||
});
|
||||
|
||||
let language = Arc::new(
|
||||
|
@ -1555,8 +1572,9 @@ async fn test_newline_below(cx: &mut gpui::TestAppContext) {
|
|||
.with_indents_query(r#"(_ "(" ")" @end) @indent"#)
|
||||
.unwrap(),
|
||||
);
|
||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
||||
cx.set_state(indoc! {"
|
||||
const a: ˇA = (
|
||||
(ˇ
|
||||
|
@ -1565,6 +1583,7 @@ async fn test_newline_below(cx: &mut gpui::TestAppContext) {
|
|||
)ˇ
|
||||
ˇ);ˇ
|
||||
"});
|
||||
|
||||
cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
|
||||
cx.assert_editor_state(indoc! {"
|
||||
const a: A = (
|
||||
|
@ -1589,7 +1608,8 @@ async fn test_newline_below(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_insert_with_old_selections(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
|
||||
let mut editor = build_editor(buffer.clone(), cx);
|
||||
|
@ -1615,12 +1635,11 @@ fn test_insert_with_old_selections(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_tab(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||
settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap());
|
||||
});
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.tab_size = NonZeroU32::new(3)
|
||||
});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.set_state(indoc! {"
|
||||
ˇabˇc
|
||||
ˇ🏀ˇ🏀ˇefg
|
||||
|
@ -1646,6 +1665,8 @@ async fn test_tab(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
let language = Arc::new(
|
||||
Language::new(
|
||||
|
@ -1704,7 +1725,10 @@ async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAp
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.tab_size = NonZeroU32::new(4)
|
||||
});
|
||||
|
||||
let language = Arc::new(
|
||||
Language::new(
|
||||
LanguageConfig::default(),
|
||||
|
@ -1713,14 +1737,9 @@ async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
|
|||
.with_indents_query(r#"(_ "{" "}" @end) @indent"#)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||
settings.editor_overrides.tab_size = Some(4.try_into().unwrap());
|
||||
});
|
||||
});
|
||||
|
||||
cx.set_state(indoc! {"
|
||||
fn a() {
|
||||
if b {
|
||||
|
@ -1741,6 +1760,10 @@ async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.tab_size = NonZeroU32::new(4);
|
||||
});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
cx.set_state(indoc! {"
|
||||
|
@ -1810,13 +1833,12 @@ async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||
settings.editor_overrides.hard_tabs = Some(true);
|
||||
});
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.hard_tabs = Some(true);
|
||||
});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
// select two ranges on one line
|
||||
cx.set_state(indoc! {"
|
||||
«oneˇ» «twoˇ»
|
||||
|
@ -1907,25 +1929,25 @@ async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
cx.set_global(
|
||||
Settings::test(cx)
|
||||
.with_language_defaults(
|
||||
"TOML",
|
||||
EditorSettings {
|
||||
tab_size: Some(2.try_into().unwrap()),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.with_language_defaults(
|
||||
"Rust",
|
||||
EditorSettings {
|
||||
tab_size: Some(4.try_into().unwrap()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
);
|
||||
init_test(cx, |settings| {
|
||||
settings.languages.extend([
|
||||
(
|
||||
"TOML".into(),
|
||||
LanguageSettingsContent {
|
||||
tab_size: NonZeroU32::new(2),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"Rust".into(),
|
||||
LanguageSettingsContent {
|
||||
tab_size: NonZeroU32::new(4),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
let toml_language = Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
name: "TOML".into(),
|
||||
|
@ -2020,6 +2042,8 @@ fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_backspace(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
// Basic backspace
|
||||
|
@ -2067,8 +2091,9 @@ async fn test_backspace(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_delete(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.set_state(indoc! {"
|
||||
onˇe two three
|
||||
fou«rˇ» five six
|
||||
|
@ -2095,7 +2120,8 @@ async fn test_delete(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_delete_line(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
|
||||
build_editor(buffer, cx)
|
||||
|
@ -2119,7 +2145,6 @@ fn test_delete_line(cx: &mut TestAppContext) {
|
|||
);
|
||||
});
|
||||
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
|
||||
build_editor(buffer, cx)
|
||||
|
@ -2139,7 +2164,8 @@ fn test_delete_line(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_duplicate_line(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
|
||||
build_editor(buffer, cx)
|
||||
|
@ -2191,7 +2217,8 @@ fn test_duplicate_line(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_move_line_up_down(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
|
||||
build_editor(buffer, cx)
|
||||
|
@ -2289,7 +2316,8 @@ fn test_move_line_up_down(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
|
||||
build_editor(buffer, cx)
|
||||
|
@ -2315,7 +2343,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_transpose(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
_ = cx
|
||||
.add_window(|cx| {
|
||||
|
@ -2417,6 +2445,8 @@ fn test_transpose(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_clipboard(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
|
||||
|
@ -2497,6 +2527,8 @@ async fn test_clipboard(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
let language = Arc::new(Language::new(
|
||||
LanguageConfig::default(),
|
||||
|
@ -2609,7 +2641,8 @@ async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_select_all(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
|
||||
build_editor(buffer, cx)
|
||||
|
@ -2625,7 +2658,8 @@ fn test_select_all(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_select_line(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
|
||||
build_editor(buffer, cx)
|
||||
|
@ -2671,7 +2705,8 @@ fn test_select_line(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_split_selection_into_lines(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
|
||||
build_editor(buffer, cx)
|
||||
|
@ -2741,7 +2776,8 @@ fn test_split_selection_into_lines(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_add_selection_above_below(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
|
||||
build_editor(buffer, cx)
|
||||
|
@ -2935,6 +2971,8 @@ fn test_add_selection_above_below(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_select_next(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.set_state("abc\nˇabc abc\ndefabc\nabc");
|
||||
|
||||
|
@ -2959,7 +2997,8 @@ async fn test_select_next(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let language = Arc::new(Language::new(
|
||||
LanguageConfig::default(),
|
||||
Some(tree_sitter_rust::language()),
|
||||
|
@ -3100,7 +3139,8 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let language = Arc::new(
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
|
@ -3160,6 +3200,8 @@ async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
let language = Arc::new(Language::new(
|
||||
|
@ -3329,6 +3371,8 @@ async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
let html_language = Arc::new(
|
||||
|
@ -3563,6 +3607,8 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
let rust_language = Arc::new(
|
||||
|
@ -3660,7 +3706,8 @@ async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let language = Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
brackets: BracketPairConfig {
|
||||
|
@ -3814,7 +3861,8 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let language = Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
brackets: BracketPairConfig {
|
||||
|
@ -3919,7 +3967,7 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_snippets(cx: &mut gpui::TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (text, insertion_ranges) = marked_text_ranges(
|
||||
indoc! {"
|
||||
|
@ -4027,7 +4075,7 @@ async fn test_snippets(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
|
@ -4111,16 +4159,14 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
|
|||
assert!(!cx.read(|cx| editor.is_dirty(cx)));
|
||||
|
||||
// Set rust language override and assert overriden tabsize is sent to language server
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||
settings.language_overrides.insert(
|
||||
"Rust".into(),
|
||||
EditorSettings {
|
||||
tab_size: Some(8.try_into().unwrap()),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
})
|
||||
update_test_settings(cx, |settings| {
|
||||
settings.languages.insert(
|
||||
"Rust".into(),
|
||||
LanguageSettingsContent {
|
||||
tab_size: NonZeroU32::new(8),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
|
||||
|
@ -4141,7 +4187,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
|
@ -4227,16 +4273,14 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
|
|||
assert!(!cx.read(|cx| editor.is_dirty(cx)));
|
||||
|
||||
// Set rust language override and assert overriden tabsize is sent to language server
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||
settings.language_overrides.insert(
|
||||
"Rust".into(),
|
||||
EditorSettings {
|
||||
tab_size: Some(8.try_into().unwrap()),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
})
|
||||
update_test_settings(cx, |settings| {
|
||||
settings.languages.insert(
|
||||
"Rust".into(),
|
||||
LanguageSettingsContent {
|
||||
tab_size: NonZeroU32::new(8),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
|
||||
|
@ -4257,7 +4301,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
|
@ -4342,7 +4386,7 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
|
@ -4399,7 +4443,7 @@ async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
|
@ -4514,6 +4558,8 @@ async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext)
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_completion(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
|
@ -4651,8 +4697,10 @@ async fn test_completion(cx: &mut gpui::TestAppContext) {
|
|||
apply_additional_edits.await.unwrap();
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||
settings.show_completions_on_input = false;
|
||||
cx.update_global::<SettingsStore, _, _>(|settings, cx| {
|
||||
settings.update_user_settings::<EditorSettings>(cx, |settings| {
|
||||
settings.show_completions_on_input = Some(false);
|
||||
});
|
||||
})
|
||||
});
|
||||
cx.set_state("editorˇ");
|
||||
|
@ -4681,7 +4729,8 @@ async fn test_completion(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let language = Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
line_comment: Some("// ".into()),
|
||||
|
@ -4764,8 +4813,7 @@ async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let language = Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
|
@ -4778,6 +4826,7 @@ async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext)
|
|||
let registry = Arc::new(LanguageRegistry::test());
|
||||
registry.add(language.clone());
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update_buffer(|buffer, cx| {
|
||||
buffer.set_language_registry(registry);
|
||||
buffer.set_language(Some(language), cx);
|
||||
|
@ -4897,6 +4946,8 @@ async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext)
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
let html_language = Arc::new(
|
||||
|
@ -5021,7 +5072,8 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
|
||||
let multibuffer = cx.add_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(0);
|
||||
|
@ -5067,7 +5119,8 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let markers = vec![('[', ']').into(), ('(', ')').into()];
|
||||
let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
|
||||
indoc! {"
|
||||
|
@ -5140,7 +5193,8 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_refresh_selections(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
|
||||
let mut excerpt1_id = None;
|
||||
let multibuffer = cx.add_model(|cx| {
|
||||
|
@ -5224,7 +5278,8 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
|
||||
let mut excerpt1_id = None;
|
||||
let multibuffer = cx.add_model(|cx| {
|
||||
|
@ -5282,7 +5337,8 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let language = Arc::new(
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
|
@ -5355,7 +5411,8 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_highlighted_ranges(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
|
||||
build_editor(buffer.clone(), cx)
|
||||
|
@ -5395,7 +5452,7 @@ fn test_highlighted_ranges(cx: &mut TestAppContext) {
|
|||
let mut highlighted_ranges = editor.background_highlights_in_range(
|
||||
anchor_range(Point::new(3, 4)..Point::new(7, 4)),
|
||||
&snapshot,
|
||||
cx.global::<Settings>().theme.as_ref(),
|
||||
theme::current(cx).as_ref(),
|
||||
);
|
||||
// Enforce a consistent ordering based on color without relying on the ordering of the
|
||||
// highlight's `TypeId` which is non-deterministic.
|
||||
|
@ -5425,7 +5482,7 @@ fn test_highlighted_ranges(cx: &mut TestAppContext) {
|
|||
editor.background_highlights_in_range(
|
||||
anchor_range(Point::new(5, 6)..Point::new(6, 4)),
|
||||
&snapshot,
|
||||
cx.global::<Settings>().theme.as_ref(),
|
||||
theme::current(cx).as_ref(),
|
||||
),
|
||||
&[(
|
||||
DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
|
||||
|
@ -5437,7 +5494,8 @@ fn test_highlighted_ranges(cx: &mut TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_following(cx: &mut gpui::TestAppContext) {
|
||||
Settings::test_async(cx);
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
|
||||
|
||||
|
@ -5459,10 +5517,12 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|
|||
});
|
||||
|
||||
let is_still_following = Rc::new(RefCell::new(true));
|
||||
let follower_edit_event_count = Rc::new(RefCell::new(0));
|
||||
let pending_update = Rc::new(RefCell::new(None));
|
||||
follower.update(cx, {
|
||||
let update = pending_update.clone();
|
||||
let is_still_following = is_still_following.clone();
|
||||
let follower_edit_event_count = follower_edit_event_count.clone();
|
||||
|_, cx| {
|
||||
cx.subscribe(&leader, move |_, leader, event, cx| {
|
||||
leader
|
||||
|
@ -5475,6 +5535,9 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|
|||
if Editor::should_unfollow_on_event(event, cx) {
|
||||
*is_still_following.borrow_mut() = false;
|
||||
}
|
||||
if let Event::BufferEdited = event {
|
||||
*follower_edit_event_count.borrow_mut() += 1;
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
@ -5494,6 +5557,7 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|
|||
assert_eq!(follower.selections.ranges(cx), vec![1..1]);
|
||||
});
|
||||
assert_eq!(*is_still_following.borrow(), true);
|
||||
assert_eq!(*follower_edit_event_count.borrow(), 0);
|
||||
|
||||
// Update the scroll position only
|
||||
leader.update(cx, |leader, cx| {
|
||||
|
@ -5510,6 +5574,7 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|
|||
vec2f(1.5, 3.5)
|
||||
);
|
||||
assert_eq!(*is_still_following.borrow(), true);
|
||||
assert_eq!(*follower_edit_event_count.borrow(), 0);
|
||||
|
||||
// Update the selections and scroll position. The follower's scroll position is updated
|
||||
// via autoscroll, not via the leader's exact scroll position.
|
||||
|
@ -5576,7 +5641,8 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
|
||||
Settings::test_async(cx);
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
|
@ -5805,6 +5871,8 @@ fn test_combine_syntax_and_fuzzy_match_highlights() {
|
|||
|
||||
#[gpui::test]
|
||||
async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
let diff_base = r#"
|
||||
|
@ -5924,6 +5992,8 @@ fn test_split_words() {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
|
||||
let mut assert = |before, after| {
|
||||
let _state_context = cx.set_state(before);
|
||||
|
@ -5972,6 +6042,8 @@ async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (copilot, copilot_lsp) = Copilot::fake(cx);
|
||||
cx.update(|cx| cx.set_global(copilot));
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
|
@ -6223,6 +6295,8 @@ async fn test_copilot_completion_invalidation(
|
|||
deterministic: Arc<Deterministic>,
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (copilot, copilot_lsp) = Copilot::fake(cx);
|
||||
cx.update(|cx| cx.set_global(copilot));
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
|
@ -6288,11 +6362,10 @@ async fn test_copilot_multibuffer(
|
|||
deterministic: Arc<Deterministic>,
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (copilot, copilot_lsp) = Copilot::fake(cx);
|
||||
cx.update(|cx| {
|
||||
cx.set_global(Settings::test(cx));
|
||||
cx.set_global(copilot)
|
||||
});
|
||||
cx.update(|cx| cx.set_global(copilot));
|
||||
|
||||
let buffer_1 = cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx));
|
||||
let buffer_2 = cx.add_model(|cx| Buffer::new(0, "c = 3\nd = 4\n", cx));
|
||||
|
@ -6392,14 +6465,16 @@ async fn test_copilot_disabled_globs(
|
|||
deterministic: Arc<Deterministic>,
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) {
|
||||
let (copilot, copilot_lsp) = Copilot::fake(cx);
|
||||
cx.update(|cx| {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.copilot.disabled_globs = vec![glob::Pattern::new(".env*").unwrap()];
|
||||
cx.set_global(settings);
|
||||
cx.set_global(copilot)
|
||||
init_test(cx, |settings| {
|
||||
settings
|
||||
.copilot
|
||||
.get_or_insert(Default::default())
|
||||
.disabled_globs = Some(vec![".env*".to_string()]);
|
||||
});
|
||||
|
||||
let (copilot, copilot_lsp) = Copilot::fake(cx);
|
||||
cx.update(|cx| cx.set_global(copilot));
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
"/test",
|
||||
|
@ -6596,3 +6671,30 @@ fn handle_copilot_completion_request(
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn update_test_settings(
|
||||
cx: &mut TestAppContext,
|
||||
f: impl Fn(&mut AllLanguageSettingsContent),
|
||||
) {
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, f);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
|
||||
cx.foreground().forbid_parking();
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
theme::init((), cx);
|
||||
client::init_settings(cx);
|
||||
language::init(cx);
|
||||
Project::init_settings(cx);
|
||||
workspace::init_settings(cx);
|
||||
crate::init(cx);
|
||||
});
|
||||
|
||||
update_test_settings(cx, f);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ use super::{
|
|||
};
|
||||
use crate::{
|
||||
display_map::{BlockStyle, DisplaySnapshot, FoldStatus, TransformBlock},
|
||||
editor_settings::ShowScrollbars,
|
||||
git::{diff_hunk_to_display, DisplayDiffHunk},
|
||||
hover_popover::{
|
||||
hide_hover, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH,
|
||||
|
@ -13,7 +14,7 @@ use crate::{
|
|||
link_go_to_definition::{
|
||||
go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link,
|
||||
},
|
||||
mouse_context_menu, EditorStyle, GutterHover, UnfoldAt,
|
||||
mouse_context_menu, EditorSettings, EditorStyle, GutterHover, UnfoldAt,
|
||||
};
|
||||
use clock::ReplicaId;
|
||||
use collections::{BTreeMap, HashMap};
|
||||
|
@ -35,9 +36,11 @@ use gpui::{
|
|||
};
|
||||
use itertools::Itertools;
|
||||
use json::json;
|
||||
use language::{Bias, CursorShape, DiagnosticSeverity, OffsetUtf16, Selection};
|
||||
use language::{
|
||||
language_settings::ShowWhitespaceSetting, Bias, CursorShape, DiagnosticSeverity, OffsetUtf16,
|
||||
Selection,
|
||||
};
|
||||
use project::ProjectPath;
|
||||
use settings::{GitGutter, Settings, ShowWhitespaces};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
|
@ -47,7 +50,8 @@ use std::{
|
|||
ops::Range,
|
||||
sync::Arc,
|
||||
};
|
||||
use workspace::item::Item;
|
||||
use text::Point;
|
||||
use workspace::{item::Item, GitGutterSetting, WorkspaceSettings};
|
||||
|
||||
enum FoldMarkers {}
|
||||
|
||||
|
@ -547,11 +551,11 @@ impl EditorElement {
|
|||
let scroll_top = scroll_position.y() * line_height;
|
||||
|
||||
let show_gutter = matches!(
|
||||
&cx.global::<Settings>()
|
||||
.git_overrides
|
||||
settings::get::<WorkspaceSettings>(cx)
|
||||
.git
|
||||
.git_gutter
|
||||
.unwrap_or_default(),
|
||||
GitGutter::TrackedFiles
|
||||
GitGutterSetting::TrackedFiles
|
||||
);
|
||||
|
||||
if show_gutter {
|
||||
|
@ -608,7 +612,7 @@ impl EditorElement {
|
|||
layout: &mut LayoutState,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) {
|
||||
let diff_style = &cx.global::<Settings>().theme.editor.diff.clone();
|
||||
let diff_style = &theme::current(cx).editor.diff.clone();
|
||||
let line_height = layout.position_map.line_height;
|
||||
|
||||
let scroll_position = layout.position_map.snapshot.scroll_position();
|
||||
|
@ -648,7 +652,7 @@ impl EditorElement {
|
|||
|
||||
//TODO: This rendering is entirely a horrible hack
|
||||
DiffHunkStatus::Removed => {
|
||||
let row = *display_row_range.start();
|
||||
let row = display_row_range.start;
|
||||
|
||||
let offset = line_height / 2.;
|
||||
let start_y = row as f32 * line_height - offset - scroll_top;
|
||||
|
@ -670,11 +674,11 @@ impl EditorElement {
|
|||
}
|
||||
};
|
||||
|
||||
let start_row = *display_row_range.start();
|
||||
let end_row = *display_row_range.end();
|
||||
let start_row = display_row_range.start;
|
||||
let end_row = display_row_range.end;
|
||||
|
||||
let start_y = start_row as f32 * line_height - scroll_top;
|
||||
let end_y = end_row as f32 * line_height - scroll_top + line_height;
|
||||
let end_y = end_row as f32 * line_height - scroll_top;
|
||||
|
||||
let width = diff_style.width_em * line_height;
|
||||
let highlight_origin = bounds.origin() + vec2f(-width, start_y);
|
||||
|
@ -708,6 +712,7 @@ impl EditorElement {
|
|||
let scroll_left = scroll_position.x() * max_glyph_width;
|
||||
let content_origin = bounds.origin() + vec2f(layout.gutter_margin, 0.);
|
||||
let line_end_overshoot = 0.15 * layout.position_map.line_height;
|
||||
let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces;
|
||||
|
||||
scene.push_layer(Some(bounds));
|
||||
|
||||
|
@ -882,9 +887,10 @@ impl EditorElement {
|
|||
content_origin,
|
||||
scroll_left,
|
||||
visible_text_bounds,
|
||||
cx,
|
||||
whitespace_setting,
|
||||
&invisible_display_ranges,
|
||||
visible_bounds,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1022,15 +1028,16 @@ impl EditorElement {
|
|||
let mut first_row_y_offset = 0.0;
|
||||
|
||||
// Impose a minimum height on the scrollbar thumb
|
||||
let row_height = height / max_row;
|
||||
let min_thumb_height =
|
||||
style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size);
|
||||
let thumb_height = (row_range.end - row_range.start) * height / max_row;
|
||||
let thumb_height = (row_range.end - row_range.start) * row_height;
|
||||
if thumb_height < min_thumb_height {
|
||||
first_row_y_offset = (min_thumb_height - thumb_height) / 2.0;
|
||||
height -= min_thumb_height - thumb_height;
|
||||
}
|
||||
|
||||
let y_for_row = |row: f32| -> f32 { top + first_row_y_offset + row * height / max_row };
|
||||
let y_for_row = |row: f32| -> f32 { top + first_row_y_offset + row * row_height };
|
||||
|
||||
let thumb_top = y_for_row(row_range.start) - first_row_y_offset;
|
||||
let thumb_bottom = y_for_row(row_range.end) + first_row_y_offset;
|
||||
|
@ -1044,6 +1051,54 @@ impl EditorElement {
|
|||
background: style.track.background_color,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let diff_style = theme::current(cx).editor.diff.clone();
|
||||
for hunk in layout
|
||||
.position_map
|
||||
.snapshot
|
||||
.buffer_snapshot
|
||||
.git_diff_hunks_in_range(0..(max_row.floor() as u32))
|
||||
{
|
||||
let start_display = Point::new(hunk.buffer_range.start, 0)
|
||||
.to_display_point(&layout.position_map.snapshot.display_snapshot);
|
||||
let end_display = Point::new(hunk.buffer_range.end, 0)
|
||||
.to_display_point(&layout.position_map.snapshot.display_snapshot);
|
||||
let start_y = y_for_row(start_display.row() as f32);
|
||||
let mut end_y = if hunk.buffer_range.start == hunk.buffer_range.end {
|
||||
y_for_row((end_display.row() + 1) as f32)
|
||||
} else {
|
||||
y_for_row((end_display.row()) as f32)
|
||||
};
|
||||
|
||||
if end_y - start_y < 1. {
|
||||
end_y = start_y + 1.;
|
||||
}
|
||||
let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y));
|
||||
|
||||
let color = match hunk.status() {
|
||||
DiffHunkStatus::Added => diff_style.inserted,
|
||||
DiffHunkStatus::Modified => diff_style.modified,
|
||||
DiffHunkStatus::Removed => diff_style.deleted,
|
||||
};
|
||||
|
||||
let border = Border {
|
||||
width: 1.,
|
||||
color: style.thumb.border.color,
|
||||
overlay: false,
|
||||
top: false,
|
||||
right: true,
|
||||
bottom: false,
|
||||
left: true,
|
||||
};
|
||||
|
||||
scene.push_quad(Quad {
|
||||
bounds,
|
||||
background: Some(color),
|
||||
border,
|
||||
corner_radius: style.thumb.corner_radius,
|
||||
})
|
||||
}
|
||||
|
||||
scene.push_quad(Quad {
|
||||
bounds: thumb_bounds,
|
||||
border: style.thumb.border,
|
||||
|
@ -1219,7 +1274,7 @@ impl EditorElement {
|
|||
.row;
|
||||
|
||||
buffer_snapshot
|
||||
.git_diff_hunks_in_range(buffer_start_row..buffer_end_row, false)
|
||||
.git_diff_hunks_in_range(buffer_start_row..buffer_end_row)
|
||||
.map(|hunk| diff_hunk_to_display(hunk, snapshot))
|
||||
.dedup()
|
||||
.collect()
|
||||
|
@ -1412,7 +1467,7 @@ impl EditorElement {
|
|||
editor: &mut Editor,
|
||||
cx: &mut LayoutContext<Editor>,
|
||||
) -> (f32, Vec<BlockLayout>) {
|
||||
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
||||
let tooltip_style = theme::current(cx).tooltip.clone();
|
||||
let scroll_x = snapshot.scroll_anchor.offset.x();
|
||||
let (fixed_blocks, non_fixed_blocks) = snapshot
|
||||
.blocks_in_range(rows.clone())
|
||||
|
@ -1738,9 +1793,10 @@ impl LineWithInvisibles {
|
|||
content_origin: Vector2F,
|
||||
scroll_left: f32,
|
||||
visible_text_bounds: RectF,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
whitespace_setting: ShowWhitespaceSetting,
|
||||
selection_ranges: &[Range<DisplayPoint>],
|
||||
visible_bounds: RectF,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) {
|
||||
let line_height = layout.position_map.line_height;
|
||||
let line_y = row as f32 * line_height - scroll_top;
|
||||
|
@ -1754,7 +1810,6 @@ impl LineWithInvisibles {
|
|||
);
|
||||
|
||||
self.draw_invisibles(
|
||||
cx,
|
||||
&selection_ranges,
|
||||
layout,
|
||||
content_origin,
|
||||
|
@ -1764,12 +1819,13 @@ impl LineWithInvisibles {
|
|||
scene,
|
||||
visible_bounds,
|
||||
line_height,
|
||||
whitespace_setting,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
|
||||
fn draw_invisibles(
|
||||
&self,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
selection_ranges: &[Range<DisplayPoint>],
|
||||
layout: &LayoutState,
|
||||
content_origin: Vector2F,
|
||||
|
@ -1779,17 +1835,13 @@ impl LineWithInvisibles {
|
|||
scene: &mut SceneBuilder,
|
||||
visible_bounds: RectF,
|
||||
line_height: f32,
|
||||
whitespace_setting: ShowWhitespaceSetting,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) {
|
||||
let settings = cx.global::<Settings>();
|
||||
let allowed_invisibles_regions = match settings
|
||||
.editor_overrides
|
||||
.show_whitespaces
|
||||
.or(settings.editor_defaults.show_whitespaces)
|
||||
.unwrap_or_default()
|
||||
{
|
||||
ShowWhitespaces::None => return,
|
||||
ShowWhitespaces::Selection => Some(selection_ranges),
|
||||
ShowWhitespaces::All => None,
|
||||
let allowed_invisibles_regions = match whitespace_setting {
|
||||
ShowWhitespaceSetting::None => return,
|
||||
ShowWhitespaceSetting::Selection => Some(selection_ranges),
|
||||
ShowWhitespaceSetting::All => None,
|
||||
};
|
||||
|
||||
for invisible in &self.invisibles {
|
||||
|
@ -1934,11 +1986,11 @@ impl Element<Editor> for EditorElement {
|
|||
let is_singleton = editor.is_singleton(cx);
|
||||
|
||||
let highlighted_rows = editor.highlighted_rows();
|
||||
let theme = cx.global::<Settings>().theme.as_ref();
|
||||
let theme = theme::current(cx);
|
||||
let highlighted_ranges = editor.background_highlights_in_range(
|
||||
start_anchor..end_anchor,
|
||||
&snapshot.display_snapshot,
|
||||
theme,
|
||||
theme.as_ref(),
|
||||
);
|
||||
|
||||
fold_ranges.extend(
|
||||
|
@ -2013,7 +2065,15 @@ impl Element<Editor> for EditorElement {
|
|||
));
|
||||
}
|
||||
|
||||
let show_scrollbars = editor.scroll_manager.scrollbars_visible();
|
||||
let show_scrollbars = match settings::get::<EditorSettings>(cx).show_scrollbars {
|
||||
ShowScrollbars::Auto => {
|
||||
snapshot.has_scrollbar_info() || editor.scroll_manager.scrollbars_visible()
|
||||
}
|
||||
ShowScrollbars::System => editor.scroll_manager.scrollbars_visible(),
|
||||
ShowScrollbars::Always => true,
|
||||
ShowScrollbars::Never => false,
|
||||
};
|
||||
|
||||
let include_root = editor
|
||||
.project
|
||||
.as_ref()
|
||||
|
@ -2773,17 +2833,19 @@ mod tests {
|
|||
use super::*;
|
||||
use crate::{
|
||||
display_map::{BlockDisposition, BlockProperties},
|
||||
editor_tests::{init_test, update_test_settings},
|
||||
Editor, MultiBuffer,
|
||||
};
|
||||
use gpui::TestAppContext;
|
||||
use language::language_settings;
|
||||
use log::info;
|
||||
use settings::Settings;
|
||||
use std::{num::NonZeroU32, sync::Arc};
|
||||
use util::test::sample_text;
|
||||
|
||||
#[gpui::test]
|
||||
fn test_layout_line_numbers(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
|
||||
Editor::new(EditorMode::Full, buffer, None, None, cx)
|
||||
|
@ -2801,7 +2863,8 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("", cx);
|
||||
Editor::new(EditorMode::Full, buffer, None, None, cx)
|
||||
|
@ -2861,26 +2924,27 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_all_invisibles_drawing(cx: &mut TestAppContext) {
|
||||
let tab_size = 4;
|
||||
const TAB_SIZE: u32 = 4;
|
||||
|
||||
let input_text = "\t \t|\t| a b";
|
||||
let expected_invisibles = vec![
|
||||
Invisible::Tab {
|
||||
line_start_offset: 0,
|
||||
},
|
||||
Invisible::Whitespace {
|
||||
line_offset: tab_size as usize,
|
||||
line_offset: TAB_SIZE as usize,
|
||||
},
|
||||
Invisible::Tab {
|
||||
line_start_offset: tab_size as usize + 1,
|
||||
line_start_offset: TAB_SIZE as usize + 1,
|
||||
},
|
||||
Invisible::Tab {
|
||||
line_start_offset: tab_size as usize * 2 + 1,
|
||||
line_start_offset: TAB_SIZE as usize * 2 + 1,
|
||||
},
|
||||
Invisible::Whitespace {
|
||||
line_offset: tab_size as usize * 3 + 1,
|
||||
line_offset: TAB_SIZE as usize * 3 + 1,
|
||||
},
|
||||
Invisible::Whitespace {
|
||||
line_offset: tab_size as usize * 3 + 3,
|
||||
line_offset: TAB_SIZE as usize * 3 + 3,
|
||||
},
|
||||
];
|
||||
assert_eq!(
|
||||
|
@ -2892,12 +2956,11 @@ mod tests {
|
|||
"Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
|
||||
);
|
||||
|
||||
cx.update(|cx| {
|
||||
let mut test_settings = Settings::test(cx);
|
||||
test_settings.editor_defaults.show_whitespaces = Some(ShowWhitespaces::All);
|
||||
test_settings.editor_defaults.tab_size = Some(NonZeroU32::new(tab_size).unwrap());
|
||||
cx.set_global(test_settings);
|
||||
init_test(cx, |s| {
|
||||
s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
|
||||
s.defaults.tab_size = NonZeroU32::new(TAB_SIZE);
|
||||
});
|
||||
|
||||
let actual_invisibles =
|
||||
collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, 500.0);
|
||||
|
||||
|
@ -2906,11 +2969,9 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_invisibles_dont_appear_in_certain_editors(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
let mut test_settings = Settings::test(cx);
|
||||
test_settings.editor_defaults.show_whitespaces = Some(ShowWhitespaces::All);
|
||||
test_settings.editor_defaults.tab_size = Some(NonZeroU32::new(4).unwrap());
|
||||
cx.set_global(test_settings);
|
||||
init_test(cx, |s| {
|
||||
s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
|
||||
s.defaults.tab_size = NonZeroU32::new(4);
|
||||
});
|
||||
|
||||
for editor_mode_without_invisibles in [
|
||||
|
@ -2961,19 +3022,18 @@ mod tests {
|
|||
);
|
||||
info!("Expected invisibles: {expected_invisibles:?}");
|
||||
|
||||
init_test(cx, |_| {});
|
||||
|
||||
// Put the same string with repeating whitespace pattern into editors of various size,
|
||||
// take deliberately small steps during resizing, to put all whitespace kinds near the wrap point.
|
||||
let resize_step = 10.0;
|
||||
let mut editor_width = 200.0;
|
||||
while editor_width <= 1000.0 {
|
||||
cx.update(|cx| {
|
||||
let mut test_settings = Settings::test(cx);
|
||||
test_settings.editor_defaults.tab_size = Some(NonZeroU32::new(tab_size).unwrap());
|
||||
test_settings.editor_defaults.show_whitespaces = Some(ShowWhitespaces::All);
|
||||
test_settings.editor_defaults.preferred_line_length = Some(editor_width as u32);
|
||||
test_settings.editor_defaults.soft_wrap =
|
||||
Some(settings::SoftWrap::PreferredLineLength);
|
||||
cx.set_global(test_settings);
|
||||
update_test_settings(cx, |s| {
|
||||
s.defaults.tab_size = NonZeroU32::new(tab_size);
|
||||
s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
|
||||
s.defaults.preferred_line_length = Some(editor_width as u32);
|
||||
s.defaults.soft_wrap = Some(language_settings::SoftWrap::PreferredLineLength);
|
||||
});
|
||||
|
||||
let actual_invisibles =
|
||||
|
@ -3021,7 +3081,7 @@ mod tests {
|
|||
|
||||
let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
|
||||
let (_, layout_state) = editor.update(cx, |editor, cx| {
|
||||
editor.set_soft_wrap_mode(settings::SoftWrap::EditorWidth, cx);
|
||||
editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
|
||||
editor.set_wrap_width(Some(editor_width), cx);
|
||||
|
||||
let mut new_parents = Default::default();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::ops::RangeInclusive;
|
||||
use std::ops::Range;
|
||||
|
||||
use git::diff::{DiffHunk, DiffHunkStatus};
|
||||
use language::Point;
|
||||
|
@ -15,7 +15,7 @@ pub enum DisplayDiffHunk {
|
|||
},
|
||||
|
||||
Unfolded {
|
||||
display_row_range: RangeInclusive<u32>,
|
||||
display_row_range: Range<u32>,
|
||||
status: DiffHunkStatus,
|
||||
},
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ impl DisplayDiffHunk {
|
|||
&DisplayDiffHunk::Folded { display_row } => display_row,
|
||||
DisplayDiffHunk::Unfolded {
|
||||
display_row_range, ..
|
||||
} => *display_row_range.start(),
|
||||
} => display_row_range.start,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ impl DisplayDiffHunk {
|
|||
|
||||
DisplayDiffHunk::Unfolded {
|
||||
display_row_range, ..
|
||||
} => display_row_range.clone(),
|
||||
} => display_row_range.start..=display_row_range.end - 1,
|
||||
};
|
||||
|
||||
range.contains(&display_row)
|
||||
|
@ -77,16 +77,12 @@ pub fn diff_hunk_to_display(hunk: DiffHunk<u32>, snapshot: &DisplaySnapshot) ->
|
|||
} else {
|
||||
let start = hunk_start_point.to_display_point(snapshot).row();
|
||||
|
||||
let hunk_end_row_inclusive = hunk
|
||||
.buffer_range
|
||||
.end
|
||||
.saturating_sub(1)
|
||||
.max(hunk.buffer_range.start);
|
||||
let hunk_end_row_inclusive = hunk.buffer_range.end.max(hunk.buffer_range.start);
|
||||
let hunk_end_point = Point::new(hunk_end_row_inclusive, 0);
|
||||
let end = hunk_end_point.to_display_point(snapshot).row();
|
||||
|
||||
DisplayDiffHunk::Unfolded {
|
||||
display_row_range: start..=end,
|
||||
display_row_range: start..end,
|
||||
status: hunk.status(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,12 +33,14 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test::editor_lsp_test_context::EditorLspTestContext;
|
||||
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
|
||||
use indoc::indoc;
|
||||
use language::{BracketPair, BracketPairConfig, Language, LanguageConfig};
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new(
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
display_map::ToDisplayPoint, Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSnapshot,
|
||||
EditorStyle, RangeToAnchorExt,
|
||||
display_map::ToDisplayPoint, Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings,
|
||||
EditorSnapshot, EditorStyle, RangeToAnchorExt,
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use gpui::{
|
||||
|
@ -12,7 +12,6 @@ use gpui::{
|
|||
};
|
||||
use language::{Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry};
|
||||
use project::{HoverBlock, HoverBlockKind, Project};
|
||||
use settings::Settings;
|
||||
use std::{ops::Range, sync::Arc, time::Duration};
|
||||
use util::TryFutureExt;
|
||||
|
||||
|
@ -38,7 +37,7 @@ pub fn hover(editor: &mut Editor, _: &Hover, cx: &mut ViewContext<Editor>) {
|
|||
/// The internal hover action dispatches between `show_hover` or `hide_hover`
|
||||
/// depending on whether a point to hover over is provided.
|
||||
pub fn hover_at(editor: &mut Editor, point: Option<DisplayPoint>, cx: &mut ViewContext<Editor>) {
|
||||
if cx.global::<Settings>().hover_popover_enabled {
|
||||
if settings::get::<EditorSettings>(cx).hover_popover_enabled {
|
||||
if let Some(point) = point {
|
||||
show_hover(editor, point, false, cx);
|
||||
} else {
|
||||
|
@ -654,7 +653,7 @@ impl DiagnosticPopover {
|
|||
_ => style.hover_popover.container,
|
||||
};
|
||||
|
||||
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
||||
let tooltip_style = theme::current(cx).tooltip.clone();
|
||||
|
||||
MouseEventHandler::<DiagnosticPopover, _>::new(0, cx, |_, _| {
|
||||
text.with_soft_wrap(true)
|
||||
|
@ -694,7 +693,7 @@ impl DiagnosticPopover {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test::editor_lsp_test_context::EditorLspTestContext;
|
||||
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
|
||||
use gpui::fonts::Weight;
|
||||
use indoc::indoc;
|
||||
use language::{Diagnostic, DiagnosticSet};
|
||||
|
@ -706,6 +705,8 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_mouse_hover_info_popover(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
|
||||
|
@ -773,6 +774,8 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_keyboard_hover_info_popover(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
|
||||
|
@ -816,6 +819,8 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_hover_diagnostic_and_info_popovers(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
|
||||
|
@ -882,7 +887,8 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_render_blocks(cx: &mut gpui::TestAppContext) {
|
||||
Settings::test_async(cx);
|
||||
init_test(cx, |_| {});
|
||||
|
||||
cx.add_window(|cx| {
|
||||
let editor = Editor::single_line(None, cx);
|
||||
let style = editor.style(cx);
|
||||
|
@ -1006,8 +1012,7 @@ mod tests {
|
|||
.zip(expected_styles.iter().cloned())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
rendered.text,
|
||||
dbg!(expected_text),
|
||||
rendered.text, expected_text,
|
||||
"wrong text for input {blocks:?}"
|
||||
);
|
||||
assert_eq!(
|
||||
|
|
|
@ -16,7 +16,6 @@ use language::{
|
|||
};
|
||||
use project::{FormatTrigger, Item as _, Project, ProjectPath};
|
||||
use rpc::proto::{self, update_view};
|
||||
use settings::Settings;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
|
@ -27,7 +26,7 @@ use std::{
|
|||
path::{Path, PathBuf},
|
||||
};
|
||||
use text::Selection;
|
||||
use util::{ResultExt, TryFutureExt};
|
||||
use util::{paths::FILE_ROW_COLUMN_DELIMITER, ResultExt, TryFutureExt};
|
||||
use workspace::item::{BreadcrumbText, FollowableItemHandle};
|
||||
use workspace::{
|
||||
item::{FollowableItem, Item, ItemEvent, ItemHandle, ProjectItem},
|
||||
|
@ -566,7 +565,7 @@ impl Item for Editor {
|
|||
cx: &AppContext,
|
||||
) -> AnyElement<T> {
|
||||
Flex::row()
|
||||
.with_child(Label::new(self.title(cx).to_string(), style.label.clone()).aligned())
|
||||
.with_child(Label::new(self.title(cx).to_string(), style.label.clone()).into_any())
|
||||
.with_children(detail.and_then(|detail| {
|
||||
let path = path_for_buffer(&self.buffer, detail, false, cx)?;
|
||||
let description = path.to_string_lossy();
|
||||
|
@ -580,6 +579,7 @@ impl Item for Editor {
|
|||
.aligned(),
|
||||
)
|
||||
}))
|
||||
.align_children_center()
|
||||
.into_any()
|
||||
}
|
||||
|
||||
|
@ -636,7 +636,7 @@ impl Item for Editor {
|
|||
project: ModelHandle<Project>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
self.report_editor_event("save", cx);
|
||||
self.report_editor_event("save", None, cx);
|
||||
let format = self.perform_format(project.clone(), FormatTrigger::Save, cx);
|
||||
let buffers = self.buffer().clone().read(cx).all_buffers();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
|
@ -685,6 +685,11 @@ impl Item for Editor {
|
|||
.as_singleton()
|
||||
.expect("cannot call save_as on an excerpt list");
|
||||
|
||||
let file_extension = abs_path
|
||||
.extension()
|
||||
.map(|a| a.to_string_lossy().to_string());
|
||||
self.report_editor_event("save", file_extension, cx);
|
||||
|
||||
project.update(cx, |project, cx| {
|
||||
project.save_buffer_as(buffer, abs_path, cx)
|
||||
})
|
||||
|
@ -1110,8 +1115,12 @@ impl View for CursorPosition {
|
|||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
if let Some(position) = self.position {
|
||||
let theme = &cx.global::<Settings>().theme.workspace.status_bar;
|
||||
let mut text = format!("{},{}", position.row + 1, position.column + 1);
|
||||
let theme = &theme::current(cx).workspace.status_bar;
|
||||
let mut text = format!(
|
||||
"{}{FILE_ROW_COLUMN_DELIMITER}{}",
|
||||
position.row + 1,
|
||||
position.column + 1
|
||||
);
|
||||
if self.selected_count > 0 {
|
||||
write!(text, " ({} selected)", self.selected_count).unwrap();
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use crate::{Anchor, DisplayPoint, Editor, EditorSnapshot, SelectPhase};
|
||||
use gpui::{Task, ViewContext};
|
||||
use language::{Bias, ToOffset};
|
||||
use project::LocationLink;
|
||||
use settings::Settings;
|
||||
use std::ops::Range;
|
||||
use util::TryFutureExt;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -211,7 +209,7 @@ pub fn show_link_definition(
|
|||
});
|
||||
|
||||
// Highlight symbol using theme link definition highlight style
|
||||
let style = cx.global::<Settings>().theme.editor.link_definition;
|
||||
let style = theme::current(cx).editor.link_definition;
|
||||
this.highlight_text::<LinkGoToDefinitionState>(
|
||||
vec![highlight_range],
|
||||
style,
|
||||
|
@ -297,6 +295,8 @@ fn go_to_fetched_definition_of_kind(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
|
||||
use futures::StreamExt;
|
||||
use gpui::{
|
||||
platform::{self, Modifiers, ModifiersChangedEvent},
|
||||
|
@ -305,12 +305,10 @@ mod tests {
|
|||
use indoc::indoc;
|
||||
use lsp::request::{GotoDefinition, GotoTypeDefinition};
|
||||
|
||||
use crate::test::editor_lsp_test_context::EditorLspTestContext;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_link_go_to_type_definition(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
|
||||
|
@ -417,6 +415,8 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_link_go_to_definition(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
|
||||
|
|
|
@ -57,13 +57,14 @@ pub fn deploy_context_menu(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::test::editor_lsp_test_context::EditorLspTestContext;
|
||||
|
||||
use super::*;
|
||||
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
|
||||
use indoc::indoc;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
|
||||
|
|
|
@ -369,11 +369,12 @@ pub fn split_display_range_by_lines(
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::{test::marked_display_snapshot, Buffer, DisplayMap, ExcerptRange, MultiBuffer};
|
||||
use settings::Settings;
|
||||
use settings::SettingsStore;
|
||||
|
||||
#[gpui::test]
|
||||
fn test_previous_word_start(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||
assert_eq!(
|
||||
|
@ -400,7 +401,8 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_previous_subword_start(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||
assert_eq!(
|
||||
|
@ -434,7 +436,8 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_find_preceding_boundary(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
fn assert(
|
||||
marked_text: &str,
|
||||
cx: &mut gpui::AppContext,
|
||||
|
@ -466,7 +469,8 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_next_word_end(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||
assert_eq!(
|
||||
|
@ -490,7 +494,8 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_next_subword_end(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||
assert_eq!(
|
||||
|
@ -523,7 +528,8 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_find_boundary(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
fn assert(
|
||||
marked_text: &str,
|
||||
cx: &mut gpui::AppContext,
|
||||
|
@ -555,7 +561,8 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_surrounding_word(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||
assert_eq!(
|
||||
|
@ -576,7 +583,8 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_move_up_and_down_with_excerpts(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
let family_id = cx
|
||||
.font_cache()
|
||||
.load_family(&["Helvetica"], &Default::default())
|
||||
|
@ -691,4 +699,11 @@ mod tests {
|
|||
(DisplayPoint::new(7, 2), SelectionGoal::Column(2)),
|
||||
);
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
theme::init((), cx);
|
||||
language::init(cx);
|
||||
crate::init(cx);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,9 @@ use git::diff::DiffHunk;
|
|||
use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
|
||||
pub use language::Completion;
|
||||
use language::{
|
||||
char_kind, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape,
|
||||
char_kind,
|
||||
language_settings::{language_settings, LanguageSettings},
|
||||
AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape,
|
||||
DiagnosticEntry, File, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16,
|
||||
Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _,
|
||||
ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
|
||||
|
@ -1165,6 +1167,9 @@ impl MultiBuffer {
|
|||
) {
|
||||
self.sync(cx);
|
||||
let ids = excerpt_ids.into_iter().collect::<Vec<_>>();
|
||||
if ids.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut buffers = self.buffers.borrow_mut();
|
||||
let mut snapshot = self.snapshot.borrow_mut();
|
||||
|
@ -1372,6 +1377,15 @@ impl MultiBuffer {
|
|||
.and_then(|(buffer, offset)| buffer.read(cx).language_at(offset))
|
||||
}
|
||||
|
||||
pub fn settings_at<'a, T: ToOffset>(
|
||||
&self,
|
||||
point: T,
|
||||
cx: &'a AppContext,
|
||||
) -> &'a LanguageSettings {
|
||||
let language = self.language_at(point, cx);
|
||||
language_settings(language.map(|l| l.name()).as_deref(), cx)
|
||||
}
|
||||
|
||||
pub fn for_each_buffer(&self, mut f: impl FnMut(&ModelHandle<Buffer>)) {
|
||||
self.buffers
|
||||
.borrow()
|
||||
|
@ -2764,6 +2778,16 @@ impl MultiBufferSnapshot {
|
|||
.and_then(|(buffer, offset)| buffer.language_at(offset))
|
||||
}
|
||||
|
||||
pub fn settings_at<'a, T: ToOffset>(
|
||||
&'a self,
|
||||
point: T,
|
||||
cx: &'a AppContext,
|
||||
) -> &'a LanguageSettings {
|
||||
self.point_to_buffer_offset(point)
|
||||
.map(|(buffer, offset)| buffer.settings_at(offset, cx))
|
||||
.unwrap_or_else(|| language_settings(None, cx))
|
||||
}
|
||||
|
||||
pub fn language_scope_at<'a, T: ToOffset>(&'a self, point: T) -> Option<LanguageScope> {
|
||||
self.point_to_buffer_offset(point)
|
||||
.and_then(|(buffer, offset)| buffer.language_scope_at(offset))
|
||||
|
@ -2817,20 +2841,15 @@ impl MultiBufferSnapshot {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn git_diff_hunks_in_range<'a>(
|
||||
pub fn git_diff_hunks_in_range_rev<'a>(
|
||||
&'a self,
|
||||
row_range: Range<u32>,
|
||||
reversed: bool,
|
||||
) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
|
||||
let mut cursor = self.excerpts.cursor::<Point>();
|
||||
|
||||
if reversed {
|
||||
cursor.seek(&Point::new(row_range.end, 0), Bias::Left, &());
|
||||
if cursor.item().is_none() {
|
||||
cursor.prev(&());
|
||||
}
|
||||
} else {
|
||||
cursor.seek(&Point::new(row_range.start, 0), Bias::Right, &());
|
||||
cursor.seek(&Point::new(row_range.end, 0), Bias::Left, &());
|
||||
if cursor.item().is_none() {
|
||||
cursor.prev(&());
|
||||
}
|
||||
|
||||
std::iter::from_fn(move || {
|
||||
|
@ -2860,7 +2879,7 @@ impl MultiBufferSnapshot {
|
|||
|
||||
let buffer_hunks = excerpt
|
||||
.buffer
|
||||
.git_diff_hunks_intersecting_range(buffer_start..buffer_end, reversed)
|
||||
.git_diff_hunks_intersecting_range_rev(buffer_start..buffer_end)
|
||||
.filter_map(move |hunk| {
|
||||
let start = multibuffer_start.row
|
||||
+ hunk
|
||||
|
@ -2880,12 +2899,70 @@ impl MultiBufferSnapshot {
|
|||
})
|
||||
});
|
||||
|
||||
if reversed {
|
||||
cursor.prev(&());
|
||||
} else {
|
||||
cursor.next(&());
|
||||
cursor.prev(&());
|
||||
|
||||
Some(buffer_hunks)
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub fn git_diff_hunks_in_range<'a>(
|
||||
&'a self,
|
||||
row_range: Range<u32>,
|
||||
) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
|
||||
let mut cursor = self.excerpts.cursor::<Point>();
|
||||
|
||||
cursor.seek(&Point::new(row_range.start, 0), Bias::Right, &());
|
||||
|
||||
std::iter::from_fn(move || {
|
||||
let excerpt = cursor.item()?;
|
||||
let multibuffer_start = *cursor.start();
|
||||
let multibuffer_end = multibuffer_start + excerpt.text_summary.lines;
|
||||
if multibuffer_start.row >= row_range.end {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut buffer_start = excerpt.range.context.start;
|
||||
let mut buffer_end = excerpt.range.context.end;
|
||||
let excerpt_start_point = buffer_start.to_point(&excerpt.buffer);
|
||||
let excerpt_end_point = excerpt_start_point + excerpt.text_summary.lines;
|
||||
|
||||
if row_range.start > multibuffer_start.row {
|
||||
let buffer_start_point =
|
||||
excerpt_start_point + Point::new(row_range.start - multibuffer_start.row, 0);
|
||||
buffer_start = excerpt.buffer.anchor_before(buffer_start_point);
|
||||
}
|
||||
|
||||
if row_range.end < multibuffer_end.row {
|
||||
let buffer_end_point =
|
||||
excerpt_start_point + Point::new(row_range.end - multibuffer_start.row, 0);
|
||||
buffer_end = excerpt.buffer.anchor_before(buffer_end_point);
|
||||
}
|
||||
|
||||
let buffer_hunks = excerpt
|
||||
.buffer
|
||||
.git_diff_hunks_intersecting_range(buffer_start..buffer_end)
|
||||
.filter_map(move |hunk| {
|
||||
let start = multibuffer_start.row
|
||||
+ hunk
|
||||
.buffer_range
|
||||
.start
|
||||
.saturating_sub(excerpt_start_point.row);
|
||||
let end = multibuffer_start.row
|
||||
+ hunk
|
||||
.buffer_range
|
||||
.end
|
||||
.min(excerpt_end_point.row + 1)
|
||||
.saturating_sub(excerpt_start_point.row);
|
||||
|
||||
Some(DiffHunk {
|
||||
buffer_range: start..end,
|
||||
diff_base_byte_range: hunk.diff_base_byte_range.clone(),
|
||||
})
|
||||
});
|
||||
|
||||
cursor.next(&());
|
||||
|
||||
Some(buffer_hunks)
|
||||
})
|
||||
.flatten()
|
||||
|
@ -3785,10 +3862,9 @@ mod tests {
|
|||
use gpui::{AppContext, TestAppContext};
|
||||
use language::{Buffer, Rope};
|
||||
use rand::prelude::*;
|
||||
use settings::Settings;
|
||||
use settings::SettingsStore;
|
||||
use std::{env, rc::Rc};
|
||||
use unindent::Unindent;
|
||||
|
||||
use util::test::sample_text;
|
||||
|
||||
#[gpui::test]
|
||||
|
@ -4080,19 +4156,25 @@ mod tests {
|
|||
|
||||
let leader_multibuffer = cx.add_model(|_| MultiBuffer::new(0));
|
||||
let follower_multibuffer = cx.add_model(|_| MultiBuffer::new(0));
|
||||
let follower_edit_event_count = Rc::new(RefCell::new(0));
|
||||
|
||||
follower_multibuffer.update(cx, |_, cx| {
|
||||
cx.subscribe(&leader_multibuffer, |follower, _, event, cx| {
|
||||
match event.clone() {
|
||||
let follower_edit_event_count = follower_edit_event_count.clone();
|
||||
cx.subscribe(
|
||||
&leader_multibuffer,
|
||||
move |follower, _, event, cx| match event.clone() {
|
||||
Event::ExcerptsAdded {
|
||||
buffer,
|
||||
predecessor,
|
||||
excerpts,
|
||||
} => follower.insert_excerpts_with_ids_after(predecessor, buffer, excerpts, cx),
|
||||
Event::ExcerptsRemoved { ids } => follower.remove_excerpts(ids, cx),
|
||||
Event::Edited => {
|
||||
*follower_edit_event_count.borrow_mut() += 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
.detach();
|
||||
});
|
||||
|
||||
|
@ -4131,6 +4213,7 @@ mod tests {
|
|||
leader_multibuffer.read(cx).snapshot(cx).text(),
|
||||
follower_multibuffer.read(cx).snapshot(cx).text(),
|
||||
);
|
||||
assert_eq!(*follower_edit_event_count.borrow(), 2);
|
||||
|
||||
leader_multibuffer.update(cx, |leader, cx| {
|
||||
let excerpt_ids = leader.excerpt_ids();
|
||||
|
@ -4140,6 +4223,27 @@ mod tests {
|
|||
leader_multibuffer.read(cx).snapshot(cx).text(),
|
||||
follower_multibuffer.read(cx).snapshot(cx).text(),
|
||||
);
|
||||
assert_eq!(*follower_edit_event_count.borrow(), 3);
|
||||
|
||||
// Removing an empty set of excerpts is a noop.
|
||||
leader_multibuffer.update(cx, |leader, cx| {
|
||||
leader.remove_excerpts([], cx);
|
||||
});
|
||||
assert_eq!(
|
||||
leader_multibuffer.read(cx).snapshot(cx).text(),
|
||||
follower_multibuffer.read(cx).snapshot(cx).text(),
|
||||
);
|
||||
assert_eq!(*follower_edit_event_count.borrow(), 3);
|
||||
|
||||
// Adding an empty set of excerpts is a noop.
|
||||
leader_multibuffer.update(cx, |leader, cx| {
|
||||
leader.push_excerpts::<usize>(buffer_2.clone(), [], cx);
|
||||
});
|
||||
assert_eq!(
|
||||
leader_multibuffer.read(cx).snapshot(cx).text(),
|
||||
follower_multibuffer.read(cx).snapshot(cx).text(),
|
||||
);
|
||||
assert_eq!(*follower_edit_event_count.borrow(), 3);
|
||||
|
||||
leader_multibuffer.update(cx, |leader, cx| {
|
||||
leader.clear(cx);
|
||||
|
@ -4148,6 +4252,7 @@ mod tests {
|
|||
leader_multibuffer.read(cx).snapshot(cx).text(),
|
||||
follower_multibuffer.read(cx).snapshot(cx).text(),
|
||||
);
|
||||
assert_eq!(*follower_edit_event_count.borrow(), 4);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
@ -4595,7 +4700,7 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
snapshot
|
||||
.git_diff_hunks_in_range(0..12, false)
|
||||
.git_diff_hunks_in_range(0..12)
|
||||
.map(|hunk| (hunk.status(), hunk.buffer_range))
|
||||
.collect::<Vec<_>>(),
|
||||
&expected,
|
||||
|
@ -4603,7 +4708,7 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
snapshot
|
||||
.git_diff_hunks_in_range(0..12, true)
|
||||
.git_diff_hunks_in_range_rev(0..12)
|
||||
.map(|hunk| (hunk.status(), hunk.buffer_range))
|
||||
.collect::<Vec<_>>(),
|
||||
expected
|
||||
|
@ -5034,7 +5139,8 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
fn test_history(cx: &mut AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
|
||||
let buffer_1 = cx.add_model(|cx| Buffer::new(0, "1234", cx));
|
||||
let buffer_2 = cx.add_model(|cx| Buffer::new(0, "5678", cx));
|
||||
let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
|
||||
|
|
|
@ -34,13 +34,17 @@ impl<'a> EditorLspTestContext<'a> {
|
|||
) -> EditorLspTestContext<'a> {
|
||||
use json::json;
|
||||
|
||||
let app_state = cx.update(AppState::test);
|
||||
|
||||
cx.update(|cx| {
|
||||
theme::init((), cx);
|
||||
language::init(cx);
|
||||
crate::init(cx);
|
||||
pane::init(cx);
|
||||
Project::init_settings(cx);
|
||||
workspace::init_settings(cx);
|
||||
});
|
||||
|
||||
let app_state = cx.update(AppState::test);
|
||||
|
||||
let file_name = format!(
|
||||
"file.{}",
|
||||
language
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
use crate::{
|
||||
display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer,
|
||||
};
|
||||
use futures::Future;
|
||||
use gpui::{
|
||||
keymap_matcher::Keystroke, AppContext, ContextHandle, ModelContext, ViewContext, ViewHandle,
|
||||
};
|
||||
use indoc::indoc;
|
||||
use language::{Buffer, BufferSnapshot};
|
||||
use std::{
|
||||
any::TypeId,
|
||||
ops::{Deref, DerefMut, Range},
|
||||
};
|
||||
|
||||
use futures::Future;
|
||||
use indoc::indoc;
|
||||
|
||||
use crate::{
|
||||
display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer,
|
||||
};
|
||||
use gpui::{
|
||||
keymap_matcher::Keystroke, AppContext, ContextHandle, ModelContext, ViewContext, ViewHandle,
|
||||
};
|
||||
use language::{Buffer, BufferSnapshot};
|
||||
use settings::Settings;
|
||||
use util::{
|
||||
assert_set_eq,
|
||||
test::{generate_marked_text, marked_text_ranges},
|
||||
|
@ -30,15 +27,10 @@ pub struct EditorTestContext<'a> {
|
|||
impl<'a> EditorTestContext<'a> {
|
||||
pub fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> {
|
||||
let (window_id, editor) = cx.update(|cx| {
|
||||
cx.set_global(Settings::test(cx));
|
||||
crate::init(cx);
|
||||
|
||||
let (window_id, editor) = cx.add_window(Default::default(), |cx| {
|
||||
cx.add_window(Default::default(), |cx| {
|
||||
cx.focus_self();
|
||||
build_editor(MultiBuffer::build_simple("", cx), cx)
|
||||
});
|
||||
|
||||
(window_id, editor)
|
||||
})
|
||||
});
|
||||
|
||||
Self {
|
||||
|
@ -212,6 +204,7 @@ impl<'a> EditorTestContext<'a> {
|
|||
self.assert_selections(expected_selections, marked_text.to_string())
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) {
|
||||
let expected_ranges = self.ranges(marked_text);
|
||||
let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, cx| {
|
||||
|
@ -228,6 +221,7 @@ impl<'a> EditorTestContext<'a> {
|
|||
assert_set_eq!(actual_ranges, expected_ranges);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn assert_editor_text_highlights<Tag: ?Sized + 'static>(&mut self, marked_text: &str) {
|
||||
let expected_ranges = self.ranges(marked_text);
|
||||
let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
|
||||
|
@ -241,12 +235,14 @@ impl<'a> EditorTestContext<'a> {
|
|||
assert_set_eq!(actual_ranges, expected_ranges);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn assert_editor_selections(&mut self, expected_selections: Vec<Range<usize>>) {
|
||||
let expected_marked_text =
|
||||
generate_marked_text(&self.buffer_text(), &expected_selections, true);
|
||||
self.assert_selections(expected_selections, expected_marked_text)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_selections(
|
||||
&mut self,
|
||||
expected_selections: Vec<Range<usize>>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue