mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-26 10:40:54 +00:00
Find the layer with the smallest enclosing node in language_scope_at
This commit is contained in:
parent
bf9dfa3b51
commit
2e2333107a
6 changed files with 151 additions and 69 deletions
|
@ -2145,23 +2145,27 @@ impl BufferSnapshot {
|
|||
|
||||
pub fn language_scope_at<D: ToOffset>(&self, position: D) -> Option<LanguageScope> {
|
||||
let offset = position.to_offset(self);
|
||||
let mut range = 0..self.len();
|
||||
let mut scope = self.language.clone().map(|language| LanguageScope {
|
||||
language,
|
||||
override_id: None,
|
||||
});
|
||||
|
||||
if let Some(layer_info) = self
|
||||
.syntax
|
||||
.layers_for_range(offset..offset, &self.text)
|
||||
.filter(|l| l.node().end_byte() > offset)
|
||||
.last()
|
||||
{
|
||||
Some(LanguageScope {
|
||||
language: layer_info.language.clone(),
|
||||
override_id: layer_info.override_id(offset, &self.text),
|
||||
})
|
||||
} else {
|
||||
self.language.clone().map(|language| LanguageScope {
|
||||
language,
|
||||
override_id: None,
|
||||
})
|
||||
// Use the layer that has the smallest node intersecting the given point.
|
||||
for layer in self.syntax.layers_for_range(offset..offset, &self.text) {
|
||||
let mut cursor = layer.node().walk();
|
||||
while cursor.goto_first_child_for_byte(offset).is_some() {}
|
||||
let node_range = cursor.node().byte_range();
|
||||
if node_range.to_inclusive().contains(&offset) && node_range.len() < range.len() {
|
||||
range = node_range;
|
||||
scope = Some(LanguageScope {
|
||||
language: layer.language.clone(),
|
||||
override_id: layer.override_id(offset, &self.text),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
scope
|
||||
}
|
||||
|
||||
pub fn surrounding_word<T: ToOffset>(&self, start: T) -> (Range<usize>, Option<CharKind>) {
|
||||
|
|
|
@ -1533,47 +1533,9 @@ fn test_autoindent_with_injected_languages(cx: &mut AppContext) {
|
|||
])
|
||||
});
|
||||
|
||||
let html_language = Arc::new(
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
name: "HTML".into(),
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_html::language()),
|
||||
)
|
||||
.with_indents_query(
|
||||
"
|
||||
(element
|
||||
(start_tag) @start
|
||||
(end_tag)? @end) @indent
|
||||
",
|
||||
)
|
||||
.unwrap()
|
||||
.with_injection_query(
|
||||
r#"
|
||||
(script_element
|
||||
(raw_text) @content
|
||||
(#set! "language" "javascript"))
|
||||
"#,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
let html_language = Arc::new(html_lang());
|
||||
|
||||
let javascript_language = Arc::new(
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
name: "JavaScript".into(),
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_javascript::language()),
|
||||
)
|
||||
.with_indents_query(
|
||||
r#"
|
||||
(object "}" @end) @indent
|
||||
"#,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
let javascript_language = Arc::new(javascript_lang());
|
||||
|
||||
let language_registry = Arc::new(LanguageRegistry::test());
|
||||
language_registry.add(html_language.clone());
|
||||
|
@ -1669,7 +1631,7 @@ fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) {
|
|||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_language_config_at(cx: &mut AppContext) {
|
||||
fn test_language_scope_at(cx: &mut AppContext) {
|
||||
init_settings(cx, |_| {});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
|
@ -1756,6 +1718,54 @@ fn test_language_config_at(cx: &mut AppContext) {
|
|||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) {
|
||||
init_settings(cx, |_| {});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let text = r#"
|
||||
<ol>
|
||||
<% people.each do |person| %>
|
||||
<li>
|
||||
<%= person.name %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ol>
|
||||
"#
|
||||
.unindent();
|
||||
|
||||
let language_registry = Arc::new(LanguageRegistry::test());
|
||||
language_registry.add(Arc::new(ruby_lang()));
|
||||
language_registry.add(Arc::new(html_lang()));
|
||||
language_registry.add(Arc::new(erb_lang()));
|
||||
|
||||
let mut buffer = Buffer::new(0, text, cx);
|
||||
buffer.set_language_registry(language_registry.clone());
|
||||
buffer.set_language(
|
||||
language_registry
|
||||
.language_for_name("ERB")
|
||||
.now_or_never()
|
||||
.unwrap()
|
||||
.ok(),
|
||||
cx,
|
||||
);
|
||||
|
||||
let snapshot = buffer.snapshot();
|
||||
let html_config = snapshot.language_scope_at(Point::new(2, 4)).unwrap();
|
||||
assert_eq!(html_config.line_comment_prefix(), None);
|
||||
assert_eq!(
|
||||
html_config.block_comment_delimiters(),
|
||||
Some((&"<!--".into(), &"-->".into()))
|
||||
);
|
||||
|
||||
let ruby_config = snapshot.language_scope_at(Point::new(3, 12)).unwrap();
|
||||
assert_eq!(ruby_config.line_comment_prefix().unwrap().as_ref(), "# ");
|
||||
assert_eq!(ruby_config.block_comment_delimiters(), None);
|
||||
|
||||
buffer
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_serialization(cx: &mut gpui::AppContext) {
|
||||
let mut now = Instant::now();
|
||||
|
@ -2143,6 +2153,7 @@ fn ruby_lang() -> Language {
|
|||
LanguageConfig {
|
||||
name: "Ruby".into(),
|
||||
path_suffixes: vec!["rb".to_string()],
|
||||
line_comment: Some("# ".into()),
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_ruby::language()),
|
||||
|
@ -2158,6 +2169,61 @@ fn ruby_lang() -> Language {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn html_lang() -> Language {
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
name: "HTML".into(),
|
||||
block_comment: Some(("<!--".into(), "-->".into())),
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_html::language()),
|
||||
)
|
||||
.with_indents_query(
|
||||
"
|
||||
(element
|
||||
(start_tag) @start
|
||||
(end_tag)? @end) @indent
|
||||
",
|
||||
)
|
||||
.unwrap()
|
||||
.with_injection_query(
|
||||
r#"
|
||||
(script_element
|
||||
(raw_text) @content
|
||||
(#set! "language" "javascript"))
|
||||
"#,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn erb_lang() -> Language {
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
name: "ERB".into(),
|
||||
path_suffixes: vec!["erb".to_string()],
|
||||
block_comment: Some(("<%#".into(), "%>".into())),
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_embedded_template::language()),
|
||||
)
|
||||
.with_injection_query(
|
||||
r#"
|
||||
(
|
||||
(code) @content
|
||||
(#set! "language" "ruby")
|
||||
(#set! "combined")
|
||||
)
|
||||
|
||||
(
|
||||
(content) @content
|
||||
(#set! "language" "html")
|
||||
(#set! "combined")
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn rust_lang() -> Language {
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
|
@ -2236,6 +2302,12 @@ fn javascript_lang() -> Language {
|
|||
"#,
|
||||
)
|
||||
.unwrap()
|
||||
.with_indents_query(
|
||||
r#"
|
||||
(object "}" @end) @indent
|
||||
"#,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
|
||||
|
|
|
@ -771,8 +771,10 @@ impl SyntaxSnapshot {
|
|||
range: Range<T>,
|
||||
buffer: &'a BufferSnapshot,
|
||||
) -> impl 'a + Iterator<Item = SyntaxLayerInfo> {
|
||||
let start = buffer.anchor_before(range.start.to_offset(buffer));
|
||||
let end = buffer.anchor_after(range.end.to_offset(buffer));
|
||||
let start_offset = range.start.to_offset(buffer);
|
||||
let end_offset = range.end.to_offset(buffer);
|
||||
let start = buffer.anchor_before(start_offset);
|
||||
let end = buffer.anchor_after(end_offset);
|
||||
|
||||
let mut cursor = self.layers.filter::<_, ()>(move |summary| {
|
||||
if summary.max_depth > summary.min_depth {
|
||||
|
@ -787,20 +789,21 @@ impl SyntaxSnapshot {
|
|||
cursor.next(buffer);
|
||||
iter::from_fn(move || {
|
||||
while let Some(layer) = cursor.item() {
|
||||
let mut info = None;
|
||||
if let SyntaxLayerContent::Parsed { tree, language } = &layer.content {
|
||||
let info = SyntaxLayerInfo {
|
||||
let layer_start_offset = layer.range.start.to_offset(buffer);
|
||||
let layer_start_point = layer.range.start.to_point(buffer).to_ts_point();
|
||||
|
||||
info = Some(SyntaxLayerInfo {
|
||||
tree,
|
||||
language,
|
||||
depth: layer.depth,
|
||||
offset: (
|
||||
layer.range.start.to_offset(buffer),
|
||||
layer.range.start.to_point(buffer).to_ts_point(),
|
||||
),
|
||||
};
|
||||
cursor.next(buffer);
|
||||
return Some(info);
|
||||
} else {
|
||||
cursor.next(buffer);
|
||||
offset: (layer_start_offset, layer_start_point),
|
||||
});
|
||||
}
|
||||
cursor.next(buffer);
|
||||
if info.is_some() {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
|
@ -3045,6 +3045,8 @@ impl Project {
|
|||
) -> Task<(Option<PathBuf>, Vec<WorktreeId>)> {
|
||||
let key = (worktree_id, adapter_name);
|
||||
if let Some(server_id) = self.language_server_ids.remove(&key) {
|
||||
log::info!("stopping language server {}", key.1 .0);
|
||||
|
||||
// Remove other entries for this language server as well
|
||||
let mut orphaned_worktrees = vec![worktree_id];
|
||||
let other_keys = self.language_server_ids.keys().cloned().collect::<Vec<_>>();
|
||||
|
|
|
@ -397,6 +397,7 @@ impl Worktree {
|
|||
}))
|
||||
}
|
||||
|
||||
// abcdefghi
|
||||
pub fn remote(
|
||||
project_remote_id: u64,
|
||||
replica_id: ReplicaId,
|
||||
|
|
|
@ -4,4 +4,4 @@ autoclose_before = ">})"
|
|||
brackets = [
|
||||
{ start = "<", end = ">", close = true, newline = true },
|
||||
]
|
||||
block_comment = ["<%#", "%>"]
|
||||
block_comment = ["<%!--", "--%>"]
|
||||
|
|
Loading…
Reference in a new issue