Compare commits
9 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a0de7d52c8 | ||
![]() |
86eafb6b6c | ||
![]() |
2d3a34c864 | ||
![]() |
67068faa1d | ||
![]() |
1d175b4848 | ||
![]() |
c51d54d86d | ||
![]() |
dc55644e7d | ||
![]() |
af9af3ff74 | ||
![]() |
8539d97f43 |
25 changed files with 492 additions and 208 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -11078,7 +11078,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed"
|
name = "zed"
|
||||||
version = "0.112.0"
|
version = "0.112.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activity_indicator",
|
"activity_indicator",
|
||||||
"ai",
|
"ai",
|
||||||
|
|
|
@ -102,6 +102,16 @@
|
||||||
"selections": true
|
"selections": true
|
||||||
},
|
},
|
||||||
"relative_line_numbers": false,
|
"relative_line_numbers": false,
|
||||||
|
// When to populate a new search's query based on the text under the cursor.
|
||||||
|
// This setting can take the following three values:
|
||||||
|
//
|
||||||
|
// 1. Always populate the search query with the word under the cursor (default).
|
||||||
|
// "always"
|
||||||
|
// 2. Only populate the search query when there is text selected
|
||||||
|
// "selection"
|
||||||
|
// 3. Never populate the search query
|
||||||
|
// "never"
|
||||||
|
"seed_search_query_from_cursor": "always",
|
||||||
// Inlay hint related settings
|
// Inlay hint related settings
|
||||||
"inlay_hints": {
|
"inlay_hints": {
|
||||||
// Global switch to toggle hints on and off, switched off by default.
|
// Global switch to toggle hints on and off, switched off by default.
|
||||||
|
|
|
@ -2,7 +2,7 @@ use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::Setting;
|
use settings::Setting;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Clone, Deserialize)]
|
||||||
pub struct EditorSettings {
|
pub struct EditorSettings {
|
||||||
pub cursor_blink: bool,
|
pub cursor_blink: bool,
|
||||||
pub hover_popover_enabled: bool,
|
pub hover_popover_enabled: bool,
|
||||||
|
@ -11,6 +11,15 @@ pub struct EditorSettings {
|
||||||
pub use_on_type_format: bool,
|
pub use_on_type_format: bool,
|
||||||
pub scrollbar: Scrollbar,
|
pub scrollbar: Scrollbar,
|
||||||
pub relative_line_numbers: bool,
|
pub relative_line_numbers: bool,
|
||||||
|
pub seed_search_query_from_cursor: SeedQuerySetting,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum SeedQuerySetting {
|
||||||
|
Always,
|
||||||
|
Selection,
|
||||||
|
Never,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||||
|
@ -38,6 +47,7 @@ pub struct EditorSettingsContent {
|
||||||
pub use_on_type_format: Option<bool>,
|
pub use_on_type_format: Option<bool>,
|
||||||
pub scrollbar: Option<ScrollbarContent>,
|
pub scrollbar: Option<ScrollbarContent>,
|
||||||
pub relative_line_numbers: Option<bool>,
|
pub relative_line_numbers: Option<bool>,
|
||||||
|
pub seed_search_query_from_cursor: Option<SeedQuerySetting>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
|
editor_settings::SeedQuerySetting, link_go_to_definition::hide_link_definition,
|
||||||
movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor,
|
persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor, EditorSettings, Event,
|
||||||
Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
|
ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
|
||||||
};
|
};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
|
@ -13,8 +13,8 @@ use gpui::{
|
||||||
ViewHandle, WeakViewHandle,
|
ViewHandle, WeakViewHandle,
|
||||||
};
|
};
|
||||||
use language::{
|
use language::{
|
||||||
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point,
|
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, OffsetRangeExt,
|
||||||
SelectionGoal,
|
Point, SelectionGoal,
|
||||||
};
|
};
|
||||||
use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath};
|
use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath};
|
||||||
use rpc::proto::{self, update_view, PeerId};
|
use rpc::proto::{self, update_view, PeerId};
|
||||||
|
@ -937,24 +937,28 @@ impl SearchableItem for Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
|
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
|
||||||
let display_map = self.snapshot(cx).display_snapshot;
|
let setting = settings::get::<EditorSettings>(cx).seed_search_query_from_cursor;
|
||||||
|
let snapshot = &self.snapshot(cx).buffer_snapshot;
|
||||||
let selection = self.selections.newest::<usize>(cx);
|
let selection = self.selections.newest::<usize>(cx);
|
||||||
if selection.start == selection.end {
|
|
||||||
let point = selection.start.to_display_point(&display_map);
|
match setting {
|
||||||
let range = surrounding_word(&display_map, point);
|
SeedQuerySetting::Never => String::new(),
|
||||||
let range = range.start.to_offset(&display_map, Bias::Left)
|
SeedQuerySetting::Selection | SeedQuerySetting::Always if !selection.is_empty() => {
|
||||||
..range.end.to_offset(&display_map, Bias::Right);
|
snapshot
|
||||||
let text: String = display_map.buffer_snapshot.text_for_range(range).collect();
|
.text_for_range(selection.start..selection.end)
|
||||||
if text.trim().is_empty() {
|
.collect()
|
||||||
String::new()
|
}
|
||||||
} else {
|
SeedQuerySetting::Selection => String::new(),
|
||||||
text
|
SeedQuerySetting::Always => {
|
||||||
|
let (range, kind) = snapshot.surrounding_word(selection.start);
|
||||||
|
if kind == Some(CharKind::Word) {
|
||||||
|
let text: String = snapshot.text_for_range(range).collect();
|
||||||
|
if !text.trim().is_empty() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String::new()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
display_map
|
|
||||||
.buffer_snapshot
|
|
||||||
.text_for_range(selection.start..selection.end)
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,15 @@ pub struct EditorSettings {
|
||||||
pub use_on_type_format: bool,
|
pub use_on_type_format: bool,
|
||||||
pub scrollbar: Scrollbar,
|
pub scrollbar: Scrollbar,
|
||||||
pub relative_line_numbers: bool,
|
pub relative_line_numbers: bool,
|
||||||
|
pub seed_search_query_from_cursor: SeedQuerySetting,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum SeedQuerySetting {
|
||||||
|
Always,
|
||||||
|
Selection,
|
||||||
|
Never,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||||
|
@ -38,6 +47,7 @@ pub struct EditorSettingsContent {
|
||||||
pub use_on_type_format: Option<bool>,
|
pub use_on_type_format: Option<bool>,
|
||||||
pub scrollbar: Option<ScrollbarContent>,
|
pub scrollbar: Option<ScrollbarContent>,
|
||||||
pub relative_line_numbers: Option<bool>,
|
pub relative_line_numbers: Option<bool>,
|
||||||
|
pub seed_search_query_from_selection: Option<SeedQuerySetting>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
|
editor_settings::SeedQuerySetting, link_go_to_definition::hide_link_definition,
|
||||||
movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor,
|
movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor,
|
||||||
Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
|
EditorSettings, Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot,
|
||||||
|
NavigationData, ToPoint as _,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
|
@ -12,11 +13,12 @@ use gpui::{
|
||||||
VisualContext, WeakView,
|
VisualContext, WeakView,
|
||||||
};
|
};
|
||||||
use language::{
|
use language::{
|
||||||
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point,
|
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, OffsetRangeExt,
|
||||||
SelectionGoal,
|
Point, SelectionGoal,
|
||||||
};
|
};
|
||||||
use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath};
|
use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath};
|
||||||
use rpc::proto::{self, update_view, PeerId};
|
use rpc::proto::{self, update_view, PeerId};
|
||||||
|
use settings::Settings;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
|
@ -950,24 +952,28 @@ impl SearchableItem for Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
|
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
|
||||||
let display_map = self.snapshot(cx).display_snapshot;
|
let setting = EditorSettings::get_global(cx).seed_search_query_from_cursor;
|
||||||
|
let snapshot = &self.snapshot(cx).buffer_snapshot;
|
||||||
let selection = self.selections.newest::<usize>(cx);
|
let selection = self.selections.newest::<usize>(cx);
|
||||||
if selection.start == selection.end {
|
|
||||||
let point = selection.start.to_display_point(&display_map);
|
match setting {
|
||||||
let range = surrounding_word(&display_map, point);
|
SeedQuerySetting::Never => String::new(),
|
||||||
let range = range.start.to_offset(&display_map, Bias::Left)
|
SeedQuerySetting::Selection | SeedQuerySetting::Always if !selection.is_empty() => {
|
||||||
..range.end.to_offset(&display_map, Bias::Right);
|
snapshot
|
||||||
let text: String = display_map.buffer_snapshot.text_for_range(range).collect();
|
.text_for_range(selection.start..selection.end)
|
||||||
if text.trim().is_empty() {
|
.collect()
|
||||||
String::new()
|
}
|
||||||
} else {
|
SeedQuerySetting::Selection => String::new(),
|
||||||
text
|
SeedQuerySetting::Always => {
|
||||||
|
let (range, kind) = snapshot.surrounding_word(selection.start);
|
||||||
|
if kind == Some(CharKind::Word) {
|
||||||
|
let text: String = snapshot.text_for_range(range).collect();
|
||||||
|
if !text.trim().is_empty() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String::new()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
display_map
|
|
||||||
.buffer_snapshot
|
|
||||||
.text_for_range(selection.start..selection.end)
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,7 @@ impl ToJson for RectF {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Refineable, Debug)]
|
#[derive(Refineable, Debug)]
|
||||||
#[refineable(debug)]
|
#[refineable(Debug)]
|
||||||
pub struct Point<T: Clone + Default + Debug> {
|
pub struct Point<T: Clone + Default + Debug> {
|
||||||
pub x: T,
|
pub x: T,
|
||||||
pub y: T,
|
pub y: T,
|
||||||
|
@ -161,7 +161,7 @@ impl<T: Clone + Default + Debug> Into<taffy::geometry::Point<T>> for Point<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Refineable, Clone, Debug)]
|
#[derive(Refineable, Clone, Debug)]
|
||||||
#[refineable(debug)]
|
#[refineable(Debug)]
|
||||||
pub struct Size<T: Clone + Default + Debug> {
|
pub struct Size<T: Clone + Default + Debug> {
|
||||||
pub width: T,
|
pub width: T,
|
||||||
pub height: T,
|
pub height: T,
|
||||||
|
@ -227,7 +227,7 @@ impl Size<Length> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, Refineable, Debug)]
|
#[derive(Clone, Default, Refineable, Debug)]
|
||||||
#[refineable(debug)]
|
#[refineable(Debug)]
|
||||||
pub struct Edges<T: Clone + Default + Debug> {
|
pub struct Edges<T: Clone + Default + Debug> {
|
||||||
pub top: T,
|
pub top: T,
|
||||||
pub right: T,
|
pub right: T,
|
||||||
|
|
|
@ -9,7 +9,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Refineable, Default, Add, AddAssign, Sub, SubAssign, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Refineable, Default, Add, AddAssign, Sub, SubAssign, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
#[refineable(debug)]
|
#[refineable(Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Point<T: Default + Clone + Debug> {
|
pub struct Point<T: Default + Clone + Debug> {
|
||||||
pub x: T,
|
pub x: T,
|
||||||
|
@ -140,7 +140,7 @@ impl<T: Clone + Default + Debug> Clone for Point<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Refineable, Default, Clone, Copy, PartialEq, Div, Hash, Serialize, Deserialize)]
|
#[derive(Refineable, Default, Clone, Copy, PartialEq, Div, Hash, Serialize, Deserialize)]
|
||||||
#[refineable(debug)]
|
#[refineable(Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Size<T: Clone + Default + Debug> {
|
pub struct Size<T: Clone + Default + Debug> {
|
||||||
pub width: T,
|
pub width: T,
|
||||||
|
@ -295,7 +295,7 @@ impl Size<Length> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
|
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
|
||||||
#[refineable(debug)]
|
#[refineable(Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Bounds<T: Clone + Default + Debug> {
|
pub struct Bounds<T: Clone + Default + Debug> {
|
||||||
pub origin: Point<T>,
|
pub origin: Point<T>,
|
||||||
|
@ -459,7 +459,7 @@ impl Bounds<Pixels> {
|
||||||
impl<T: Clone + Debug + Copy + Default> Copy for Bounds<T> {}
|
impl<T: Clone + Debug + Copy + Default> Copy for Bounds<T> {}
|
||||||
|
|
||||||
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
|
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
|
||||||
#[refineable(debug)]
|
#[refineable(Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Edges<T: Clone + Default + Debug> {
|
pub struct Edges<T: Clone + Default + Debug> {
|
||||||
pub top: T,
|
pub top: T,
|
||||||
|
@ -592,7 +592,7 @@ impl Edges<Pixels> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
|
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
|
||||||
#[refineable(debug)]
|
#[refineable(Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Corners<T: Clone + Default + Debug> {
|
pub struct Corners<T: Clone + Default + Debug> {
|
||||||
pub top_left: T,
|
pub top_left: T,
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub use taffy::style::{
|
||||||
pub type StyleCascade = Cascade<Style>;
|
pub type StyleCascade = Cascade<Style>;
|
||||||
|
|
||||||
#[derive(Clone, Refineable, Debug)]
|
#[derive(Clone, Refineable, Debug)]
|
||||||
#[refineable(debug)]
|
#[refineable(Debug)]
|
||||||
pub struct Style {
|
pub struct Style {
|
||||||
/// What layout strategy should be used?
|
/// What layout strategy should be used?
|
||||||
pub display: Display,
|
pub display: Display,
|
||||||
|
@ -129,7 +129,7 @@ pub struct BoxShadow {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Refineable, Clone, Debug)]
|
#[derive(Refineable, Clone, Debug)]
|
||||||
#[refineable(debug)]
|
#[refineable(Debug)]
|
||||||
pub struct TextStyle {
|
pub struct TextStyle {
|
||||||
pub color: Hsla,
|
pub color: Hsla,
|
||||||
pub font_family: SharedString,
|
pub font_family: SharedString,
|
||||||
|
@ -353,7 +353,7 @@ impl Default for Style {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Refineable, Copy, Clone, Default, Debug, PartialEq, Eq)]
|
#[derive(Refineable, Copy, Clone, Default, Debug, PartialEq, Eq)]
|
||||||
#[refineable(debug)]
|
#[refineable(Debug)]
|
||||||
pub struct UnderlineStyle {
|
pub struct UnderlineStyle {
|
||||||
pub thickness: Pixels,
|
pub thickness: Pixels,
|
||||||
pub color: Option<Hsla>,
|
pub color: Option<Hsla>,
|
||||||
|
|
|
@ -1692,14 +1692,25 @@ fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
|
||||||
r#"
|
r#"
|
||||||
(jsx_element) @element
|
(jsx_element) @element
|
||||||
(string) @string
|
(string) @string
|
||||||
|
[
|
||||||
|
(jsx_opening_element)
|
||||||
|
(jsx_closing_element)
|
||||||
|
(jsx_expression)
|
||||||
|
] @default
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let text = r#"a["b"] = <C d="e"></C>;"#;
|
let text = r#"
|
||||||
|
a["b"] = <C d="e">
|
||||||
|
<F></F>
|
||||||
|
{ g() }
|
||||||
|
</C>;
|
||||||
|
"#
|
||||||
|
.unindent();
|
||||||
|
|
||||||
let buffer =
|
let buffer =
|
||||||
Buffer::new(0, cx.model_id() as u64, text).with_language(Arc::new(language), cx);
|
Buffer::new(0, cx.model_id() as u64, &text).with_language(Arc::new(language), cx);
|
||||||
let snapshot = buffer.snapshot();
|
let snapshot = buffer.snapshot();
|
||||||
|
|
||||||
let config = snapshot.language_scope_at(0).unwrap();
|
let config = snapshot.language_scope_at(0).unwrap();
|
||||||
|
@ -1710,7 +1721,9 @@ fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
|
||||||
&[true, true]
|
&[true, true]
|
||||||
);
|
);
|
||||||
|
|
||||||
let string_config = snapshot.language_scope_at(3).unwrap();
|
let string_config = snapshot
|
||||||
|
.language_scope_at(text.find("b\"").unwrap())
|
||||||
|
.unwrap();
|
||||||
assert_eq!(string_config.line_comment_prefix().unwrap().as_ref(), "// ");
|
assert_eq!(string_config.line_comment_prefix().unwrap().as_ref(), "// ");
|
||||||
// Second bracket pair is disabled
|
// Second bracket pair is disabled
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1718,18 +1731,49 @@ fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
|
||||||
&[true, false]
|
&[true, false]
|
||||||
);
|
);
|
||||||
|
|
||||||
let element_config = snapshot.language_scope_at(10).unwrap();
|
// In between JSX tags: use the `element` override.
|
||||||
|
let element_config = snapshot
|
||||||
|
.language_scope_at(text.find("<F>").unwrap())
|
||||||
|
.unwrap();
|
||||||
assert_eq!(element_config.line_comment_prefix(), None);
|
assert_eq!(element_config.line_comment_prefix(), None);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
element_config.block_comment_delimiters(),
|
element_config.block_comment_delimiters(),
|
||||||
Some((&"{/*".into(), &"*/}".into()))
|
Some((&"{/*".into(), &"*/}".into()))
|
||||||
);
|
);
|
||||||
// Both bracket pairs are enabled
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
|
element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
|
||||||
&[true, true]
|
&[true, true]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Within a JSX tag: use the default config.
|
||||||
|
let tag_config = snapshot
|
||||||
|
.language_scope_at(text.find(" d=").unwrap() + 1)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(tag_config.line_comment_prefix().unwrap().as_ref(), "// ");
|
||||||
|
assert_eq!(
|
||||||
|
tag_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
|
||||||
|
&[true, true]
|
||||||
|
);
|
||||||
|
|
||||||
|
// In a JSX expression: use the default config.
|
||||||
|
let expression_in_element_config = snapshot
|
||||||
|
.language_scope_at(text.find("{").unwrap() + 1)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
expression_in_element_config
|
||||||
|
.line_comment_prefix()
|
||||||
|
.unwrap()
|
||||||
|
.as_ref(),
|
||||||
|
"// "
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
expression_in_element_config
|
||||||
|
.brackets()
|
||||||
|
.map(|e| e.1)
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
&[true, true]
|
||||||
|
);
|
||||||
|
|
||||||
buffer
|
buffer
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1696,14 +1696,25 @@ fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
|
||||||
r#"
|
r#"
|
||||||
(jsx_element) @element
|
(jsx_element) @element
|
||||||
(string) @string
|
(string) @string
|
||||||
|
[
|
||||||
|
(jsx_opening_element)
|
||||||
|
(jsx_closing_element)
|
||||||
|
(jsx_expression)
|
||||||
|
] @default
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let text = r#"a["b"] = <C d="e"></C>;"#;
|
let text = r#"
|
||||||
|
a["b"] = <C d="e">
|
||||||
|
<F></F>
|
||||||
|
{ g() }
|
||||||
|
</C>;
|
||||||
|
"#
|
||||||
|
.unindent();
|
||||||
|
|
||||||
let buffer =
|
let buffer =
|
||||||
Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(language), cx);
|
Buffer::new(0, cx.entity_id().as_u64(), &text).with_language(Arc::new(language), cx);
|
||||||
let snapshot = buffer.snapshot();
|
let snapshot = buffer.snapshot();
|
||||||
|
|
||||||
let config = snapshot.language_scope_at(0).unwrap();
|
let config = snapshot.language_scope_at(0).unwrap();
|
||||||
|
@ -1714,7 +1725,9 @@ fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
|
||||||
&[true, true]
|
&[true, true]
|
||||||
);
|
);
|
||||||
|
|
||||||
let string_config = snapshot.language_scope_at(3).unwrap();
|
let string_config = snapshot
|
||||||
|
.language_scope_at(text.find("b\"").unwrap())
|
||||||
|
.unwrap();
|
||||||
assert_eq!(string_config.line_comment_prefix().unwrap().as_ref(), "// ");
|
assert_eq!(string_config.line_comment_prefix().unwrap().as_ref(), "// ");
|
||||||
// Second bracket pair is disabled
|
// Second bracket pair is disabled
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1722,18 +1735,49 @@ fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
|
||||||
&[true, false]
|
&[true, false]
|
||||||
);
|
);
|
||||||
|
|
||||||
let element_config = snapshot.language_scope_at(10).unwrap();
|
// In between JSX tags: use the `element` override.
|
||||||
|
let element_config = snapshot
|
||||||
|
.language_scope_at(text.find("<F>").unwrap())
|
||||||
|
.unwrap();
|
||||||
assert_eq!(element_config.line_comment_prefix(), None);
|
assert_eq!(element_config.line_comment_prefix(), None);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
element_config.block_comment_delimiters(),
|
element_config.block_comment_delimiters(),
|
||||||
Some((&"{/*".into(), &"*/}".into()))
|
Some((&"{/*".into(), &"*/}".into()))
|
||||||
);
|
);
|
||||||
// Both bracket pairs are enabled
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
|
element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
|
||||||
&[true, true]
|
&[true, true]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Within a JSX tag: use the default config.
|
||||||
|
let tag_config = snapshot
|
||||||
|
.language_scope_at(text.find(" d=").unwrap() + 1)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(tag_config.line_comment_prefix().unwrap().as_ref(), "// ");
|
||||||
|
assert_eq!(
|
||||||
|
tag_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
|
||||||
|
&[true, true]
|
||||||
|
);
|
||||||
|
|
||||||
|
// In a JSX expression: use the default config.
|
||||||
|
let expression_in_element_config = snapshot
|
||||||
|
.language_scope_at(text.find("{").unwrap() + 1)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
expression_in_element_config
|
||||||
|
.line_comment_prefix()
|
||||||
|
.unwrap()
|
||||||
|
.as_ref(),
|
||||||
|
"// "
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
expression_in_element_config
|
||||||
|
.brackets()
|
||||||
|
.map(|e| e.1)
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
&[true, true]
|
||||||
|
);
|
||||||
|
|
||||||
buffer
|
buffer
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::ops::ControlFlow;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -58,11 +59,17 @@ impl Prettier {
|
||||||
fs: &dyn Fs,
|
fs: &dyn Fs,
|
||||||
installed_prettiers: &HashSet<PathBuf>,
|
installed_prettiers: &HashSet<PathBuf>,
|
||||||
locate_from: &Path,
|
locate_from: &Path,
|
||||||
) -> anyhow::Result<Option<PathBuf>> {
|
) -> anyhow::Result<ControlFlow<(), Option<PathBuf>>> {
|
||||||
let mut path_to_check = locate_from
|
let mut path_to_check = locate_from
|
||||||
.components()
|
.components()
|
||||||
.take_while(|component| component.as_os_str().to_string_lossy() != "node_modules")
|
.take_while(|component| component.as_os_str().to_string_lossy() != "node_modules")
|
||||||
.collect::<PathBuf>();
|
.collect::<PathBuf>();
|
||||||
|
if path_to_check != locate_from {
|
||||||
|
log::debug!(
|
||||||
|
"Skipping prettier location for path {path_to_check:?} that is inside node_modules"
|
||||||
|
);
|
||||||
|
return Ok(ControlFlow::Break(()));
|
||||||
|
}
|
||||||
let path_to_check_metadata = fs
|
let path_to_check_metadata = fs
|
||||||
.metadata(&path_to_check)
|
.metadata(&path_to_check)
|
||||||
.await
|
.await
|
||||||
|
@ -76,14 +83,14 @@ impl Prettier {
|
||||||
loop {
|
loop {
|
||||||
if installed_prettiers.contains(&path_to_check) {
|
if installed_prettiers.contains(&path_to_check) {
|
||||||
log::debug!("Found prettier path {path_to_check:?} in installed prettiers");
|
log::debug!("Found prettier path {path_to_check:?} in installed prettiers");
|
||||||
return Ok(Some(path_to_check));
|
return Ok(ControlFlow::Continue(Some(path_to_check)));
|
||||||
} else if let Some(package_json_contents) =
|
} else if let Some(package_json_contents) =
|
||||||
read_package_json(fs, &path_to_check).await?
|
read_package_json(fs, &path_to_check).await?
|
||||||
{
|
{
|
||||||
if has_prettier_in_package_json(&package_json_contents) {
|
if has_prettier_in_package_json(&package_json_contents) {
|
||||||
if has_prettier_in_node_modules(fs, &path_to_check).await? {
|
if has_prettier_in_node_modules(fs, &path_to_check).await? {
|
||||||
log::debug!("Found prettier path {path_to_check:?} in both package.json and node_modules");
|
log::debug!("Found prettier path {path_to_check:?} in both package.json and node_modules");
|
||||||
return Ok(Some(path_to_check));
|
return Ok(ControlFlow::Continue(Some(path_to_check)));
|
||||||
} else if project_path_with_prettier_dependency.is_none() {
|
} else if project_path_with_prettier_dependency.is_none() {
|
||||||
project_path_with_prettier_dependency = Some(path_to_check.clone());
|
project_path_with_prettier_dependency = Some(path_to_check.clone());
|
||||||
}
|
}
|
||||||
|
@ -109,7 +116,7 @@ impl Prettier {
|
||||||
}) {
|
}) {
|
||||||
anyhow::ensure!(has_prettier_in_node_modules(fs, &path_to_check).await?, "Found prettier path {path_to_check:?} in the workspace root for project in {project_path_with_prettier_dependency:?}, but it's not installed into workspace root's node_modules");
|
anyhow::ensure!(has_prettier_in_node_modules(fs, &path_to_check).await?, "Found prettier path {path_to_check:?} in the workspace root for project in {project_path_with_prettier_dependency:?}, but it's not installed into workspace root's node_modules");
|
||||||
log::info!("Found prettier path {path_to_check:?} in the workspace root for project in {project_path_with_prettier_dependency:?}");
|
log::info!("Found prettier path {path_to_check:?} in the workspace root for project in {project_path_with_prettier_dependency:?}");
|
||||||
return Ok(Some(path_to_check));
|
return Ok(ControlFlow::Continue(Some(path_to_check)));
|
||||||
} else {
|
} else {
|
||||||
log::warn!("Skipping path {path_to_check:?} that has prettier in its 'node_modules' subdirectory, but is not included in its package.json workspaces {workspaces:?}");
|
log::warn!("Skipping path {path_to_check:?} that has prettier in its 'node_modules' subdirectory, but is not included in its package.json workspaces {workspaces:?}");
|
||||||
}
|
}
|
||||||
|
@ -132,7 +139,7 @@ impl Prettier {
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
log::debug!("Found no prettier in ancestors of {locate_from:?}");
|
log::debug!("Found no prettier in ancestors of {locate_from:?}");
|
||||||
return Ok(None);
|
return Ok(ControlFlow::Continue(None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -497,37 +504,40 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
Prettier::locate_prettier_installation(
|
matches!(
|
||||||
fs.as_ref(),
|
Prettier::locate_prettier_installation(
|
||||||
&HashSet::default(),
|
fs.as_ref(),
|
||||||
Path::new("/root/.config/zed/settings.json"),
|
&HashSet::default(),
|
||||||
)
|
Path::new("/root/.config/zed/settings.json"),
|
||||||
.await
|
)
|
||||||
.unwrap()
|
.await,
|
||||||
.is_none(),
|
Ok(ControlFlow::Continue(None))
|
||||||
|
),
|
||||||
"Should successfully find no prettier for path hierarchy without it"
|
"Should successfully find no prettier for path hierarchy without it"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
Prettier::locate_prettier_installation(
|
matches!(
|
||||||
fs.as_ref(),
|
Prettier::locate_prettier_installation(
|
||||||
&HashSet::default(),
|
fs.as_ref(),
|
||||||
Path::new("/root/work/project/src/index.js")
|
&HashSet::default(),
|
||||||
)
|
Path::new("/root/work/project/src/index.js")
|
||||||
.await
|
)
|
||||||
.unwrap()
|
.await,
|
||||||
.is_none(),
|
Ok(ControlFlow::Continue(None))
|
||||||
|
),
|
||||||
"Should successfully find no prettier for path hierarchy that has node_modules with prettier, but no package.json mentions of it"
|
"Should successfully find no prettier for path hierarchy that has node_modules with prettier, but no package.json mentions of it"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
Prettier::locate_prettier_installation(
|
matches!(
|
||||||
fs.as_ref(),
|
Prettier::locate_prettier_installation(
|
||||||
&HashSet::default(),
|
fs.as_ref(),
|
||||||
Path::new("/root/work/project/node_modules/expect/build/print.js")
|
&HashSet::default(),
|
||||||
)
|
Path::new("/root/work/project/node_modules/expect/build/print.js")
|
||||||
.await
|
)
|
||||||
.unwrap()
|
.await,
|
||||||
.is_none(),
|
Ok(ControlFlow::Break(()))
|
||||||
"Even though it has package.json with prettier in it and no prettier on node_modules along the path, nothing should fail since declared inside node_modules"
|
),
|
||||||
|
"Should not format files inside node_modules/"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -580,7 +590,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
Some(PathBuf::from("/root/web_blog")),
|
ControlFlow::Continue(Some(PathBuf::from("/root/web_blog"))),
|
||||||
"Should find a preinstalled prettier in the project root"
|
"Should find a preinstalled prettier in the project root"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -591,8 +601,8 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
Some(PathBuf::from("/root/web_blog")),
|
ControlFlow::Break(()),
|
||||||
"Should find a preinstalled prettier in the project root even for node_modules files"
|
"Should not allow formatting node_modules/ contents"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,6 +614,18 @@ mod tests {
|
||||||
json!({
|
json!({
|
||||||
"work": {
|
"work": {
|
||||||
"web_blog": {
|
"web_blog": {
|
||||||
|
"node_modules": {
|
||||||
|
"expect": {
|
||||||
|
"build": {
|
||||||
|
"print.js": "// print.js file contents",
|
||||||
|
},
|
||||||
|
"package.json": r#"{
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "2.5.1"
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
},
|
||||||
|
},
|
||||||
"pages": {
|
"pages": {
|
||||||
"[slug].tsx": "// [slug].tsx file contents",
|
"[slug].tsx": "// [slug].tsx file contents",
|
||||||
},
|
},
|
||||||
|
@ -624,33 +646,55 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let path = "/root/work/web_blog/node_modules/pages/[slug].tsx";
|
|
||||||
match Prettier::locate_prettier_installation(
|
match Prettier::locate_prettier_installation(
|
||||||
fs.as_ref(),
|
fs.as_ref(),
|
||||||
&HashSet::default(),
|
&HashSet::default(),
|
||||||
Path::new(path)
|
Path::new("/root/work/web_blog/pages/[slug].tsx")
|
||||||
)
|
)
|
||||||
.await {
|
.await {
|
||||||
Ok(path) => panic!("Expected to fail for prettier in package.json but not in node_modules found, but got path {path:?}"),
|
Ok(path) => panic!("Expected to fail for prettier in package.json but not in node_modules found, but got path {path:?}"),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let message = e.to_string();
|
let message = e.to_string();
|
||||||
assert!(message.contains(path), "Error message should mention which start file was used for location");
|
assert!(message.contains("/root/work/web_blog"), "Error message should mention which project had prettier defined");
|
||||||
assert!(message.contains("/root/work/web_blog"), "Error message should mention potential candidates without prettier node_modules contents");
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Prettier::locate_prettier_installation(
|
Prettier::locate_prettier_installation(
|
||||||
fs.as_ref(),
|
fs.as_ref(),
|
||||||
&HashSet::from_iter(
|
&HashSet::from_iter(
|
||||||
[PathBuf::from("/root"), PathBuf::from("/root/work")].into_iter()
|
[PathBuf::from("/root"), PathBuf::from("/root/work")].into_iter()
|
||||||
),
|
),
|
||||||
Path::new("/root/work/web_blog/node_modules/pages/[slug].tsx")
|
Path::new("/root/work/web_blog/pages/[slug].tsx")
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
Some(PathBuf::from("/root/work")),
|
ControlFlow::Continue(Some(PathBuf::from("/root/work"))),
|
||||||
"Should return first cached value found without path checks"
|
"Should return closest cached value found without path checks"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Prettier::locate_prettier_installation(
|
||||||
|
fs.as_ref(),
|
||||||
|
&HashSet::default(),
|
||||||
|
Path::new("/root/work/web_blog/node_modules/expect/build/print.js")
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
ControlFlow::Break(()),
|
||||||
|
"Should not allow formatting files inside node_modules/"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Prettier::locate_prettier_installation(
|
||||||
|
fs.as_ref(),
|
||||||
|
&HashSet::from_iter(
|
||||||
|
[PathBuf::from("/root"), PathBuf::from("/root/work")].into_iter()
|
||||||
|
),
|
||||||
|
Path::new("/root/work/web_blog/node_modules/expect/build/print.js")
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
ControlFlow::Break(()),
|
||||||
|
"Should ignore cache lookup for files inside node_modules/"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,7 +718,9 @@ mod tests {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"node_modules": {},
|
"node_modules": {
|
||||||
|
"test.js": "// test.js contents",
|
||||||
|
},
|
||||||
"package.json": r#"{
|
"package.json": r#"{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "^3.0.3"
|
"prettier": "^3.0.3"
|
||||||
|
@ -703,9 +749,32 @@ mod tests {
|
||||||
&HashSet::default(),
|
&HashSet::default(),
|
||||||
Path::new("/root/work/full-stack-foundations/exercises/03.loading/01.problem.loader/app/routes/users+/$username_+/notes.tsx"),
|
Path::new("/root/work/full-stack-foundations/exercises/03.loading/01.problem.loader/app/routes/users+/$username_+/notes.tsx"),
|
||||||
).await.unwrap(),
|
).await.unwrap(),
|
||||||
Some(PathBuf::from("/root/work/full-stack-foundations")),
|
ControlFlow::Continue(Some(PathBuf::from("/root/work/full-stack-foundations"))),
|
||||||
"Should ascend to the multi-workspace root and find the prettier there",
|
"Should ascend to the multi-workspace root and find the prettier there",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Prettier::locate_prettier_installation(
|
||||||
|
fs.as_ref(),
|
||||||
|
&HashSet::default(),
|
||||||
|
Path::new("/root/work/full-stack-foundations/node_modules/prettier/index.js")
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
ControlFlow::Break(()),
|
||||||
|
"Should not allow formatting files inside root node_modules/"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Prettier::locate_prettier_installation(
|
||||||
|
fs.as_ref(),
|
||||||
|
&HashSet::default(),
|
||||||
|
Path::new("/root/work/full-stack-foundations/exercises/03.loading/01.problem.loader/node_modules/test.js")
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
ControlFlow::Break(()),
|
||||||
|
"Should not allow formatting files inside submodule's node_modules/"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
|
|
@ -7,6 +7,7 @@ use lsp::{LanguageServer, LanguageServerId};
|
||||||
use node_runtime::NodeRuntime;
|
use node_runtime::NodeRuntime;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
|
ops::ControlFlow,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
@ -58,11 +59,17 @@ impl Prettier {
|
||||||
fs: &dyn Fs,
|
fs: &dyn Fs,
|
||||||
installed_prettiers: &HashSet<PathBuf>,
|
installed_prettiers: &HashSet<PathBuf>,
|
||||||
locate_from: &Path,
|
locate_from: &Path,
|
||||||
) -> anyhow::Result<Option<PathBuf>> {
|
) -> anyhow::Result<ControlFlow<(), Option<PathBuf>>> {
|
||||||
let mut path_to_check = locate_from
|
let mut path_to_check = locate_from
|
||||||
.components()
|
.components()
|
||||||
.take_while(|component| component.as_os_str().to_string_lossy() != "node_modules")
|
.take_while(|component| component.as_os_str().to_string_lossy() != "node_modules")
|
||||||
.collect::<PathBuf>();
|
.collect::<PathBuf>();
|
||||||
|
if path_to_check != locate_from {
|
||||||
|
log::debug!(
|
||||||
|
"Skipping prettier location for path {path_to_check:?} that is inside node_modules"
|
||||||
|
);
|
||||||
|
return Ok(ControlFlow::Break(()));
|
||||||
|
}
|
||||||
let path_to_check_metadata = fs
|
let path_to_check_metadata = fs
|
||||||
.metadata(&path_to_check)
|
.metadata(&path_to_check)
|
||||||
.await
|
.await
|
||||||
|
@ -76,14 +83,14 @@ impl Prettier {
|
||||||
loop {
|
loop {
|
||||||
if installed_prettiers.contains(&path_to_check) {
|
if installed_prettiers.contains(&path_to_check) {
|
||||||
log::debug!("Found prettier path {path_to_check:?} in installed prettiers");
|
log::debug!("Found prettier path {path_to_check:?} in installed prettiers");
|
||||||
return Ok(Some(path_to_check));
|
return Ok(ControlFlow::Continue(Some(path_to_check)));
|
||||||
} else if let Some(package_json_contents) =
|
} else if let Some(package_json_contents) =
|
||||||
read_package_json(fs, &path_to_check).await?
|
read_package_json(fs, &path_to_check).await?
|
||||||
{
|
{
|
||||||
if has_prettier_in_package_json(&package_json_contents) {
|
if has_prettier_in_package_json(&package_json_contents) {
|
||||||
if has_prettier_in_node_modules(fs, &path_to_check).await? {
|
if has_prettier_in_node_modules(fs, &path_to_check).await? {
|
||||||
log::debug!("Found prettier path {path_to_check:?} in both package.json and node_modules");
|
log::debug!("Found prettier path {path_to_check:?} in both package.json and node_modules");
|
||||||
return Ok(Some(path_to_check));
|
return Ok(ControlFlow::Continue(Some(path_to_check)));
|
||||||
} else if project_path_with_prettier_dependency.is_none() {
|
} else if project_path_with_prettier_dependency.is_none() {
|
||||||
project_path_with_prettier_dependency = Some(path_to_check.clone());
|
project_path_with_prettier_dependency = Some(path_to_check.clone());
|
||||||
}
|
}
|
||||||
|
@ -109,7 +116,7 @@ impl Prettier {
|
||||||
}) {
|
}) {
|
||||||
anyhow::ensure!(has_prettier_in_node_modules(fs, &path_to_check).await?, "Found prettier path {path_to_check:?} in the workspace root for project in {project_path_with_prettier_dependency:?}, but it's not installed into workspace root's node_modules");
|
anyhow::ensure!(has_prettier_in_node_modules(fs, &path_to_check).await?, "Found prettier path {path_to_check:?} in the workspace root for project in {project_path_with_prettier_dependency:?}, but it's not installed into workspace root's node_modules");
|
||||||
log::info!("Found prettier path {path_to_check:?} in the workspace root for project in {project_path_with_prettier_dependency:?}");
|
log::info!("Found prettier path {path_to_check:?} in the workspace root for project in {project_path_with_prettier_dependency:?}");
|
||||||
return Ok(Some(path_to_check));
|
return Ok(ControlFlow::Continue(Some(path_to_check)));
|
||||||
} else {
|
} else {
|
||||||
log::warn!("Skipping path {path_to_check:?} that has prettier in its 'node_modules' subdirectory, but is not included in its package.json workspaces {workspaces:?}");
|
log::warn!("Skipping path {path_to_check:?} that has prettier in its 'node_modules' subdirectory, but is not included in its package.json workspaces {workspaces:?}");
|
||||||
}
|
}
|
||||||
|
@ -132,7 +139,7 @@ impl Prettier {
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
log::debug!("Found no prettier in ancestors of {locate_from:?}");
|
log::debug!("Found no prettier in ancestors of {locate_from:?}");
|
||||||
return Ok(None);
|
return Ok(ControlFlow::Continue(None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -527,37 +534,40 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
Prettier::locate_prettier_installation(
|
matches!(
|
||||||
fs.as_ref(),
|
Prettier::locate_prettier_installation(
|
||||||
&HashSet::default(),
|
fs.as_ref(),
|
||||||
Path::new("/root/.config/zed/settings.json"),
|
&HashSet::default(),
|
||||||
)
|
Path::new("/root/.config/zed/settings.json"),
|
||||||
.await
|
)
|
||||||
.unwrap()
|
.await,
|
||||||
.is_none(),
|
Ok(ControlFlow::Continue(None))
|
||||||
|
),
|
||||||
"Should successfully find no prettier for path hierarchy without it"
|
"Should successfully find no prettier for path hierarchy without it"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
Prettier::locate_prettier_installation(
|
matches!(
|
||||||
fs.as_ref(),
|
Prettier::locate_prettier_installation(
|
||||||
&HashSet::default(),
|
fs.as_ref(),
|
||||||
Path::new("/root/work/project/src/index.js")
|
&HashSet::default(),
|
||||||
)
|
Path::new("/root/work/project/src/index.js")
|
||||||
.await
|
)
|
||||||
.unwrap()
|
.await,
|
||||||
.is_none(),
|
Ok(ControlFlow::Continue(None))
|
||||||
|
),
|
||||||
"Should successfully find no prettier for path hierarchy that has node_modules with prettier, but no package.json mentions of it"
|
"Should successfully find no prettier for path hierarchy that has node_modules with prettier, but no package.json mentions of it"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
Prettier::locate_prettier_installation(
|
matches!(
|
||||||
fs.as_ref(),
|
Prettier::locate_prettier_installation(
|
||||||
&HashSet::default(),
|
fs.as_ref(),
|
||||||
Path::new("/root/work/project/node_modules/expect/build/print.js")
|
&HashSet::default(),
|
||||||
)
|
Path::new("/root/work/project/node_modules/expect/build/print.js")
|
||||||
.await
|
)
|
||||||
.unwrap()
|
.await,
|
||||||
.is_none(),
|
Ok(ControlFlow::Break(()))
|
||||||
"Even though it has package.json with prettier in it and no prettier on node_modules along the path, nothing should fail since declared inside node_modules"
|
),
|
||||||
|
"Should not format files inside node_modules/"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,7 +620,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
Some(PathBuf::from("/root/web_blog")),
|
ControlFlow::Continue(Some(PathBuf::from("/root/web_blog"))),
|
||||||
"Should find a preinstalled prettier in the project root"
|
"Should find a preinstalled prettier in the project root"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -621,8 +631,8 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
Some(PathBuf::from("/root/web_blog")),
|
ControlFlow::Break(()),
|
||||||
"Should find a preinstalled prettier in the project root even for node_modules files"
|
"Should not allow formatting node_modules/ contents"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,6 +644,18 @@ mod tests {
|
||||||
json!({
|
json!({
|
||||||
"work": {
|
"work": {
|
||||||
"web_blog": {
|
"web_blog": {
|
||||||
|
"node_modules": {
|
||||||
|
"expect": {
|
||||||
|
"build": {
|
||||||
|
"print.js": "// print.js file contents",
|
||||||
|
},
|
||||||
|
"package.json": r#"{
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "2.5.1"
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
},
|
||||||
|
},
|
||||||
"pages": {
|
"pages": {
|
||||||
"[slug].tsx": "// [slug].tsx file contents",
|
"[slug].tsx": "// [slug].tsx file contents",
|
||||||
},
|
},
|
||||||
|
@ -654,18 +676,16 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let path = "/root/work/web_blog/node_modules/pages/[slug].tsx";
|
|
||||||
match Prettier::locate_prettier_installation(
|
match Prettier::locate_prettier_installation(
|
||||||
fs.as_ref(),
|
fs.as_ref(),
|
||||||
&HashSet::default(),
|
&HashSet::default(),
|
||||||
Path::new(path)
|
Path::new("/root/work/web_blog/pages/[slug].tsx")
|
||||||
)
|
)
|
||||||
.await {
|
.await {
|
||||||
Ok(path) => panic!("Expected to fail for prettier in package.json but not in node_modules found, but got path {path:?}"),
|
Ok(path) => panic!("Expected to fail for prettier in package.json but not in node_modules found, but got path {path:?}"),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let message = e.to_string();
|
let message = e.to_string();
|
||||||
assert!(message.contains(path), "Error message should mention which start file was used for location");
|
assert!(message.contains("/root/work/web_blog"), "Error message should mention which project had prettier defined");
|
||||||
assert!(message.contains("/root/work/web_blog"), "Error message should mention potential candidates without prettier node_modules contents");
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -675,12 +695,37 @@ mod tests {
|
||||||
&HashSet::from_iter(
|
&HashSet::from_iter(
|
||||||
[PathBuf::from("/root"), PathBuf::from("/root/work")].into_iter()
|
[PathBuf::from("/root"), PathBuf::from("/root/work")].into_iter()
|
||||||
),
|
),
|
||||||
Path::new("/root/work/web_blog/node_modules/pages/[slug].tsx")
|
Path::new("/root/work/web_blog/pages/[slug].tsx")
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
Some(PathBuf::from("/root/work")),
|
ControlFlow::Continue(Some(PathBuf::from("/root/work"))),
|
||||||
"Should return first cached value found without path checks"
|
"Should return closest cached value found without path checks"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Prettier::locate_prettier_installation(
|
||||||
|
fs.as_ref(),
|
||||||
|
&HashSet::default(),
|
||||||
|
Path::new("/root/work/web_blog/node_modules/expect/build/print.js")
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
ControlFlow::Break(()),
|
||||||
|
"Should not allow formatting files inside node_modules/"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Prettier::locate_prettier_installation(
|
||||||
|
fs.as_ref(),
|
||||||
|
&HashSet::from_iter(
|
||||||
|
[PathBuf::from("/root"), PathBuf::from("/root/work")].into_iter()
|
||||||
|
),
|
||||||
|
Path::new("/root/work/web_blog/node_modules/expect/build/print.js")
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
ControlFlow::Break(()),
|
||||||
|
"Should ignore cache lookup for files inside node_modules/"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -704,7 +749,9 @@ mod tests {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"node_modules": {},
|
"node_modules": {
|
||||||
|
"test.js": "// test.js contents",
|
||||||
|
},
|
||||||
"package.json": r#"{
|
"package.json": r#"{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "^3.0.3"
|
"prettier": "^3.0.3"
|
||||||
|
@ -733,9 +780,32 @@ mod tests {
|
||||||
&HashSet::default(),
|
&HashSet::default(),
|
||||||
Path::new("/root/work/full-stack-foundations/exercises/03.loading/01.problem.loader/app/routes/users+/$username_+/notes.tsx"),
|
Path::new("/root/work/full-stack-foundations/exercises/03.loading/01.problem.loader/app/routes/users+/$username_+/notes.tsx"),
|
||||||
).await.unwrap(),
|
).await.unwrap(),
|
||||||
Some(PathBuf::from("/root/work/full-stack-foundations")),
|
ControlFlow::Continue(Some(PathBuf::from("/root/work/full-stack-foundations"))),
|
||||||
"Should ascend to the multi-workspace root and find the prettier there",
|
"Should ascend to the multi-workspace root and find the prettier there",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Prettier::locate_prettier_installation(
|
||||||
|
fs.as_ref(),
|
||||||
|
&HashSet::default(),
|
||||||
|
Path::new("/root/work/full-stack-foundations/node_modules/prettier/index.js")
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
ControlFlow::Break(()),
|
||||||
|
"Should not allow formatting files inside root node_modules/"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Prettier::locate_prettier_installation(
|
||||||
|
fs.as_ref(),
|
||||||
|
&HashSet::default(),
|
||||||
|
Path::new("/root/work/full-stack-foundations/exercises/03.loading/01.problem.loader/node_modules/test.js")
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
ControlFlow::Break(()),
|
||||||
|
"Should not allow formatting files inside submodule's node_modules/"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
|
|
@ -69,7 +69,7 @@ use std::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
mem,
|
mem,
|
||||||
num::NonZeroU32,
|
num::NonZeroU32,
|
||||||
ops::Range,
|
ops::{ControlFlow, Range},
|
||||||
path::{self, Component, Path, PathBuf},
|
path::{self, Component, Path, PathBuf},
|
||||||
process::Stdio,
|
process::Stdio,
|
||||||
str,
|
str,
|
||||||
|
@ -8442,7 +8442,10 @@ impl Project {
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(None) => {
|
Ok(ControlFlow::Break(())) => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Ok(ControlFlow::Continue(None)) => {
|
||||||
let started_default_prettier =
|
let started_default_prettier =
|
||||||
project.update(&mut cx, |project, _| {
|
project.update(&mut cx, |project, _| {
|
||||||
project
|
project
|
||||||
|
@ -8466,7 +8469,7 @@ impl Project {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Some(prettier_dir)) => {
|
Ok(ControlFlow::Continue(Some(prettier_dir))) => {
|
||||||
project.update(&mut cx, |project, _| {
|
project.update(&mut cx, |project, _| {
|
||||||
project
|
project
|
||||||
.prettiers_per_worktree
|
.prettiers_per_worktree
|
||||||
|
@ -8593,7 +8596,7 @@ impl Project {
|
||||||
.await
|
.await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
None => Task::ready(Ok(None)),
|
None => Task::ready(Ok(ControlFlow::Break(()))),
|
||||||
};
|
};
|
||||||
let mut plugins_to_install = prettier_plugins;
|
let mut plugins_to_install = prettier_plugins;
|
||||||
let previous_installation_process =
|
let previous_installation_process =
|
||||||
|
@ -8622,8 +8625,9 @@ impl Project {
|
||||||
.context("locate prettier installation")
|
.context("locate prettier installation")
|
||||||
.map_err(Arc::new)?
|
.map_err(Arc::new)?
|
||||||
{
|
{
|
||||||
Some(_non_default_prettier) => return Ok(()),
|
ControlFlow::Break(()) => return Ok(()),
|
||||||
None => {
|
ControlFlow::Continue(Some(_non_default_prettier)) => return Ok(()),
|
||||||
|
ControlFlow::Continue(None) => {
|
||||||
let mut needs_install = match previous_installation_process {
|
let mut needs_install = match previous_installation_process {
|
||||||
Some(previous_installation_process) => {
|
Some(previous_installation_process) => {
|
||||||
previous_installation_process.await.is_err()
|
previous_installation_process.await.is_err()
|
||||||
|
|
|
@ -69,7 +69,7 @@ use std::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
mem,
|
mem,
|
||||||
num::NonZeroU32,
|
num::NonZeroU32,
|
||||||
ops::Range,
|
ops::{ControlFlow, Range},
|
||||||
path::{self, Component, Path, PathBuf},
|
path::{self, Component, Path, PathBuf},
|
||||||
process::Stdio,
|
process::Stdio,
|
||||||
str,
|
str,
|
||||||
|
@ -8488,7 +8488,10 @@ impl Project {
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(None) => {
|
Ok(ControlFlow::Break(())) => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Ok(ControlFlow::Continue(None)) => {
|
||||||
match project.update(&mut cx, |project, _| {
|
match project.update(&mut cx, |project, _| {
|
||||||
project
|
project
|
||||||
.prettiers_per_worktree
|
.prettiers_per_worktree
|
||||||
|
@ -8520,7 +8523,7 @@ impl Project {
|
||||||
.shared())),
|
.shared())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Some(prettier_dir)) => {
|
Ok(ControlFlow::Continue(Some(prettier_dir))) => {
|
||||||
match project.update(&mut cx, |project, _| {
|
match project.update(&mut cx, |project, _| {
|
||||||
project
|
project
|
||||||
.prettiers_per_worktree
|
.prettiers_per_worktree
|
||||||
|
@ -8662,7 +8665,7 @@ impl Project {
|
||||||
.await
|
.await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
None => Task::ready(Ok(None)),
|
None => Task::ready(Ok(ControlFlow::Break(()))),
|
||||||
};
|
};
|
||||||
let mut plugins_to_install = prettier_plugins;
|
let mut plugins_to_install = prettier_plugins;
|
||||||
let previous_installation_process =
|
let previous_installation_process =
|
||||||
|
@ -8692,8 +8695,9 @@ impl Project {
|
||||||
.context("locate prettier installation")
|
.context("locate prettier installation")
|
||||||
.map_err(Arc::new)?
|
.map_err(Arc::new)?
|
||||||
{
|
{
|
||||||
Some(_non_default_prettier) => return Ok(()),
|
ControlFlow::Break(()) => return Ok(()),
|
||||||
None => {
|
ControlFlow::Continue(Some(_non_default_prettier)) => return Ok(()),
|
||||||
|
ControlFlow::Continue(None) => {
|
||||||
let mut needs_install = match previous_installation_process {
|
let mut needs_install = match previous_installation_process {
|
||||||
Some(previous_installation_process) => {
|
Some(previous_installation_process) => {
|
||||||
previous_installation_process.await.is_err()
|
previous_installation_process.await.is_err()
|
||||||
|
|
|
@ -19,8 +19,7 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
|
||||||
let refineable_attr = attrs.iter().find(|attr| attr.path.is_ident("refineable"));
|
let refineable_attr = attrs.iter().find(|attr| attr.path.is_ident("refineable"));
|
||||||
|
|
||||||
let mut impl_debug_on_refinement = false;
|
let mut impl_debug_on_refinement = false;
|
||||||
let mut derive_serialize_on_refinement = false;
|
let mut refinement_traits_to_derive = vec![];
|
||||||
let mut derive_deserialize_on_refinement = false;
|
|
||||||
|
|
||||||
if let Some(refineable_attr) = refineable_attr {
|
if let Some(refineable_attr) = refineable_attr {
|
||||||
if let Ok(syn::Meta::List(meta_list)) = refineable_attr.parse_meta() {
|
if let Ok(syn::Meta::List(meta_list)) = refineable_attr.parse_meta() {
|
||||||
|
@ -29,16 +28,10 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
if path.is_ident("debug") {
|
if path.is_ident("Debug") {
|
||||||
impl_debug_on_refinement = true;
|
impl_debug_on_refinement = true;
|
||||||
}
|
} else {
|
||||||
|
refinement_traits_to_derive.push(path);
|
||||||
if path.is_ident("serialize") {
|
|
||||||
derive_serialize_on_refinement = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if path.is_ident("deserialize") {
|
|
||||||
derive_deserialize_on_refinement = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,22 +252,14 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
|
||||||
quote! {}
|
quote! {}
|
||||||
};
|
};
|
||||||
|
|
||||||
let derive_serialize = if derive_serialize_on_refinement {
|
let mut derive_stream = quote! {};
|
||||||
quote! { #[derive(serde::Serialize)]}
|
for trait_to_derive in refinement_traits_to_derive {
|
||||||
} else {
|
derive_stream.extend(quote! { #[derive(#trait_to_derive)] })
|
||||||
quote! {}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let derive_deserialize = if derive_deserialize_on_refinement {
|
|
||||||
quote! { #[derive(serde::Deserialize)]}
|
|
||||||
} else {
|
|
||||||
quote! {}
|
|
||||||
};
|
|
||||||
|
|
||||||
let gen = quote! {
|
let gen = quote! {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
#derive_serialize
|
#derive_stream
|
||||||
#derive_deserialize
|
|
||||||
pub struct #refinement_ident #impl_generics {
|
pub struct #refinement_ident #impl_generics {
|
||||||
#( #field_visibilities #field_names: #wrapped_types ),*
|
#( #field_visibilities #field_names: #wrapped_types ),*
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use std::sync::Arc;
|
use crate::{PlayerColors, SyntaxTheme};
|
||||||
|
|
||||||
use gpui::Hsla;
|
use gpui::Hsla;
|
||||||
use refineable::Refineable;
|
use refineable::Refineable;
|
||||||
|
use std::sync::Arc;
|
||||||
use crate::{PlayerColors, SyntaxTheme};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SystemColors {
|
pub struct SystemColors {
|
||||||
|
@ -14,7 +12,7 @@ pub struct SystemColors {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Refineable, Clone, Debug)]
|
#[derive(Refineable, Clone, Debug)]
|
||||||
#[refineable(debug)]
|
#[refineable(Debug)]
|
||||||
pub struct StatusColors {
|
pub struct StatusColors {
|
||||||
pub conflict: Hsla,
|
pub conflict: Hsla,
|
||||||
pub created: Hsla,
|
pub created: Hsla,
|
||||||
|
@ -30,7 +28,7 @@ pub struct StatusColors {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Refineable, Clone, Debug)]
|
#[derive(Refineable, Clone, Debug)]
|
||||||
#[refineable(debug)]
|
#[refineable(Debug)]
|
||||||
pub struct GitStatusColors {
|
pub struct GitStatusColors {
|
||||||
pub conflict: Hsla,
|
pub conflict: Hsla,
|
||||||
pub created: Hsla,
|
pub created: Hsla,
|
||||||
|
@ -41,7 +39,7 @@ pub struct GitStatusColors {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Refineable, Clone, Debug)]
|
#[derive(Refineable, Clone, Debug)]
|
||||||
#[refineable(debug, deserialize)]
|
#[refineable(Debug, serde::Deserialize)]
|
||||||
pub struct ThemeColors {
|
pub struct ThemeColors {
|
||||||
pub border: Hsla,
|
pub border: Hsla,
|
||||||
/// Border color. Used for deemphasized borders, like a visual divider between two sections
|
/// Border color. Used for deemphasized borders, like a visual divider between two sections
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
|
use crate::{Appearance, ThemeColors, ThemeColorsRefinement};
|
||||||
use refineable::Refineable;
|
use refineable::Refineable;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{Appearance, ThemeColors, ThemeColorsRefinement};
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct UserThemeFamily {
|
pub struct UserThemeFamily {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -18,7 +17,7 @@ pub struct UserTheme {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Refineable, Clone)]
|
#[derive(Refineable, Clone)]
|
||||||
#[refineable(deserialize)]
|
#[refineable(Deserialize)]
|
||||||
pub struct UserThemeStyles {
|
pub struct UserThemeStyles {
|
||||||
#[refineable]
|
#[refineable]
|
||||||
pub colors: ThemeColors,
|
pub colors: ThemeColors,
|
||||||
|
|
|
@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
|
||||||
description = "The fast, collaborative code editor."
|
description = "The fast, collaborative code editor."
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
name = "zed"
|
name = "zed"
|
||||||
version = "0.112.0"
|
version = "0.112.3"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
dev
|
stable
|
|
@ -8,6 +8,11 @@
|
||||||
[
|
[
|
||||||
(jsx_element)
|
(jsx_element)
|
||||||
(jsx_fragment)
|
(jsx_fragment)
|
||||||
|
] @element
|
||||||
|
|
||||||
|
[
|
||||||
|
(jsx_opening_element)
|
||||||
|
(jsx_closing_element)
|
||||||
(jsx_self_closing_element)
|
(jsx_self_closing_element)
|
||||||
(jsx_expression)
|
(jsx_expression)
|
||||||
] @element
|
] @default
|
||||||
|
|
|
@ -8,6 +8,11 @@
|
||||||
[
|
[
|
||||||
(jsx_element)
|
(jsx_element)
|
||||||
(jsx_fragment)
|
(jsx_fragment)
|
||||||
|
] @element
|
||||||
|
|
||||||
|
[
|
||||||
|
(jsx_opening_element)
|
||||||
|
(jsx_closing_element)
|
||||||
(jsx_self_closing_element)
|
(jsx_self_closing_element)
|
||||||
(jsx_expression)
|
(jsx_expression)
|
||||||
] @element
|
] @default
|
||||||
|
|
|
@ -2,6 +2,7 @@ use anyhow::{anyhow, Result};
|
||||||
use async_compression::futures::bufread::GzipDecoder;
|
use async_compression::futures::bufread::GzipDecoder;
|
||||||
use async_tar::Archive;
|
use async_tar::Archive;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use collections::HashMap;
|
||||||
use futures::{future::BoxFuture, FutureExt};
|
use futures::{future::BoxFuture, FutureExt};
|
||||||
use gpui::AppContext;
|
use gpui::AppContext;
|
||||||
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||||
|
@ -20,12 +21,7 @@ use util::{fs::remove_matching, github::latest_github_release};
|
||||||
use util::{github::GitHubLspBinaryVersion, ResultExt};
|
use util::{github::GitHubLspBinaryVersion, ResultExt};
|
||||||
|
|
||||||
fn typescript_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
fn typescript_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||||
vec![
|
vec![server_path.into(), "--stdio".into()]
|
||||||
server_path.into(),
|
|
||||||
"--stdio".into(),
|
|
||||||
"--tsserver-path".into(),
|
|
||||||
"node_modules/typescript/lib".into(),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eslint_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
fn eslint_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||||
|
@ -158,9 +154,20 @@ impl LspAdapter for TypeScriptLspAdapter {
|
||||||
|
|
||||||
async fn initialization_options(&self) -> Option<serde_json::Value> {
|
async fn initialization_options(&self) -> Option<serde_json::Value> {
|
||||||
Some(json!({
|
Some(json!({
|
||||||
"provideFormatter": true
|
"provideFormatter": true,
|
||||||
|
"tsserver": {
|
||||||
|
"path": "node_modules/typescript/lib",
|
||||||
|
},
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn language_ids(&self) -> HashMap<String, String> {
|
||||||
|
HashMap::from_iter([
|
||||||
|
("TypeScript".into(), "typescript".into()),
|
||||||
|
("JavaScript".into(), "javascript".into()),
|
||||||
|
("TSX".into(), "typescriptreact".into()),
|
||||||
|
])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_cached_ts_server_binary(
|
async fn get_cached_ts_server_binary(
|
||||||
|
|
|
@ -8,6 +8,11 @@
|
||||||
[
|
[
|
||||||
(jsx_element)
|
(jsx_element)
|
||||||
(jsx_fragment)
|
(jsx_fragment)
|
||||||
|
] @element
|
||||||
|
|
||||||
|
[
|
||||||
|
(jsx_opening_element)
|
||||||
|
(jsx_closing_element)
|
||||||
(jsx_self_closing_element)
|
(jsx_self_closing_element)
|
||||||
(jsx_expression)
|
(jsx_expression)
|
||||||
] @element
|
] @default
|
||||||
|
|
|
@ -8,6 +8,11 @@
|
||||||
[
|
[
|
||||||
(jsx_element)
|
(jsx_element)
|
||||||
(jsx_fragment)
|
(jsx_fragment)
|
||||||
|
] @element
|
||||||
|
|
||||||
|
[
|
||||||
|
(jsx_opening_element)
|
||||||
|
(jsx_closing_element)
|
||||||
(jsx_self_closing_element)
|
(jsx_self_closing_element)
|
||||||
(jsx_expression)
|
(jsx_expression)
|
||||||
] @element
|
] @default
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue