mirror of https://github.com/helix-editor/helix
feat: add typable command to get injection layer for current range
parent
99d16170dc
commit
d451077978
|
@ -30,43 +30,20 @@ pub fn get_injected_tokens(
|
||||||
start: usize,
|
start: usize,
|
||||||
end: usize,
|
end: usize,
|
||||||
) -> (Option<Vec<String>>, Option<Vec<BlockCommentToken>>) {
|
) -> (Option<Vec<String>>, Option<Vec<BlockCommentToken>>) {
|
||||||
let mut best_fit = None;
|
|
||||||
let mut min_gap = usize::MAX;
|
|
||||||
|
|
||||||
// Find the injection with the most tightly encompassing range.
|
// Find the injection with the most tightly encompassing range.
|
||||||
if let Some(syntax) = &syntax {
|
syntax
|
||||||
for (layer_id, layer) in &syntax.layers {
|
.and_then(|syntax| {
|
||||||
for ts_range in &layer.ranges {
|
syntax
|
||||||
let is_encompassing = ts_range.start_byte <= start && ts_range.end_byte >= end;
|
.injection_for_range(start, end)
|
||||||
if is_encompassing {
|
.map(|language_id| syntax.layer_config(language_id))
|
||||||
let this_gap = ts_range.end_byte - ts_range.start_byte;
|
.map(|config| {
|
||||||
if this_gap < min_gap
|
(
|
||||||
// ignore the "comment" language family
|
|
||||||
// as that would mean we can't uncomment anything, or
|
|
||||||
// the comments would be incorrect.
|
|
||||||
//
|
|
||||||
// Since uncommenting would attempt to use the comment
|
|
||||||
// language's non-existing comment tokens
|
|
||||||
// TODO: add this as a language configuration key?
|
|
||||||
&& !matches!(syntax.layer_config(layer_id).language_name.as_ref(), "jsdoc" | "comment")
|
|
||||||
{
|
|
||||||
best_fit = Some(layer_id);
|
|
||||||
min_gap = this_gap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(best_fit) = best_fit {
|
|
||||||
let config = syntax.layer_config(best_fit);
|
|
||||||
return (
|
|
||||||
config.comment_tokens.clone(),
|
config.comment_tokens.clone(),
|
||||||
config.block_comment_tokens.clone(),
|
config.block_comment_tokens.clone(),
|
||||||
);
|
)
|
||||||
}
|
})
|
||||||
}
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
(None, None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given text, a comment token, and a set of line indices, returns the following:
|
/// Given text, a comment token, and a set of line indices, returns the following:
|
||||||
|
|
|
@ -1437,6 +1437,37 @@ impl Syntax {
|
||||||
Arc::clone(&loader.language_configs[language_id])
|
Arc::clone(&loader.language_configs[language_id])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// For a given range in the document, get the most tightly encompassing
|
||||||
|
/// injection layer corresponding to that range.
|
||||||
|
pub fn injection_for_range(&self, from: usize, to: usize) -> Option<LayerId> {
|
||||||
|
let mut best_fit = None;
|
||||||
|
let mut min_gap = usize::MAX;
|
||||||
|
|
||||||
|
for (layer_id, layer) in &self.layers {
|
||||||
|
for ts_range in &layer.ranges {
|
||||||
|
let is_encompassing = ts_range.start_byte <= from && ts_range.end_byte >= to;
|
||||||
|
if is_encompassing {
|
||||||
|
let this_gap = ts_range.end_byte - ts_range.start_byte;
|
||||||
|
if this_gap < min_gap
|
||||||
|
// ignore the "comment" language family
|
||||||
|
// as that would mean we can't uncomment anything, or
|
||||||
|
// the comments would be incorrect.
|
||||||
|
//
|
||||||
|
// Since uncommenting would attempt to use the comment
|
||||||
|
// language's non-existing comment tokens
|
||||||
|
// TODO: add this as a language configuration key?
|
||||||
|
&& !matches!(self.layer_config(layer_id).language_name.as_ref(), "jsdoc" | "comment")
|
||||||
|
{
|
||||||
|
best_fit = Some(layer_id);
|
||||||
|
min_gap = this_gap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
best_fit
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tree(&self) -> &Tree {
|
pub fn tree(&self) -> &Tree {
|
||||||
self.layers[self.root].tree()
|
self.layers[self.root].tree()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1604,6 +1604,33 @@ fn tree_sitter_scopes(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tree_sitter_injection(
|
||||||
|
cx: &mut compositor::Context,
|
||||||
|
_args: &[Cow<str>],
|
||||||
|
event: PromptEvent,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
if event != PromptEvent::Validate {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let (view, doc) = current!(cx.editor);
|
||||||
|
|
||||||
|
let syntax = doc
|
||||||
|
.syntax()
|
||||||
|
.context("No tree-sitter grammar found for this file.")?;
|
||||||
|
|
||||||
|
let range = doc.selection(view.id).primary();
|
||||||
|
|
||||||
|
let language_name = syntax
|
||||||
|
.injection_for_range(range.from(), range.to())
|
||||||
|
.map(|language_id| syntax.layer_config(language_id).language_name.clone())
|
||||||
|
.context("No injection layer found for the current range.")?;
|
||||||
|
|
||||||
|
cx.editor.set_status(language_name);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn tree_sitter_highlight_name(
|
fn tree_sitter_highlight_name(
|
||||||
cx: &mut compositor::Context,
|
cx: &mut compositor::Context,
|
||||||
_args: Args,
|
_args: Args,
|
||||||
|
@ -3134,6 +3161,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
|
||||||
..Signature::DEFAULT
|
..Signature::DEFAULT
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
TypableCommand {
|
||||||
|
name: "tree-sitter-injection",
|
||||||
|
aliases: &[],
|
||||||
|
doc: "Display injected language for the primary range.",
|
||||||
|
fun: tree_sitter_injection,
|
||||||
|
signature: CommandSignature::none(),
|
||||||
|
},
|
||||||
TypableCommand {
|
TypableCommand {
|
||||||
name: "debug-start",
|
name: "debug-start",
|
||||||
aliases: &["dbg"],
|
aliases: &["dbg"],
|
||||||
|
|
Loading…
Reference in New Issue