mirror of https://github.com/helix-editor/helix
refactor: extract a separate toggle_comment_impl function
parent
093805b62c
commit
e9683381b6
|
@ -25,6 +25,50 @@ pub fn get_comment_token<'a, S: AsRef<str>>(
|
||||||
.max_by_key(|token| token.len())
|
.max_by_key(|token| token.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_injected_tokens(
|
||||||
|
syntax: Option<&Syntax>,
|
||||||
|
start: usize,
|
||||||
|
end: usize,
|
||||||
|
) -> (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.
|
||||||
|
if let Some(syntax) = &syntax {
|
||||||
|
for (layer_id, layer) in &syntax.layers {
|
||||||
|
for ts_range in &layer.ranges {
|
||||||
|
let is_encompassing = ts_range.start_byte <= start && ts_range.end_byte >= end;
|
||||||
|
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!(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.block_comment_tokens.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(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:
|
||||||
/// - Whether the given lines should be considered commented
|
/// - Whether the given lines should be considered commented
|
||||||
/// - If any of the lines are uncommented, all lines are considered as such.
|
/// - If any of the lines are uncommented, all lines are considered as such.
|
||||||
|
|
|
@ -5091,68 +5091,22 @@ pub fn completion(cx: &mut Context) {
|
||||||
|
|
||||||
// comments
|
// comments
|
||||||
|
|
||||||
pub type CommentTransactionFn<'a> =
|
|
||||||
Box<dyn FnMut(&mut Context, comment::GetInjectedTokens<'a>) + 'a>;
|
|
||||||
|
|
||||||
fn toggle_comments_impl<'a>(
|
|
||||||
cx: &'a mut Context,
|
|
||||||
mut comment_transaction: CommentTransactionFn<'a>,
|
|
||||||
) {
|
|
||||||
comment_transaction(
|
|
||||||
cx,
|
|
||||||
Box::new(move |syntax: Option<&Syntax>, start: usize, end: usize| {
|
|
||||||
let mut best_fit = None;
|
|
||||||
let mut min_gap = usize::MAX;
|
|
||||||
|
|
||||||
// Find the injection with the most tightly encompassing range.
|
|
||||||
if let Some(syntax) = &syntax {
|
|
||||||
for (layer_id, layer) in &syntax.layers {
|
|
||||||
for ts_range in &layer.ranges {
|
|
||||||
let is_encompassing =
|
|
||||||
ts_range.start_byte <= start && ts_range.end_byte >= end;
|
|
||||||
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!(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.block_comment_tokens.clone(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(None, None)
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// commenting behavior, for each range in selection:
|
/// commenting behavior, for each range in selection:
|
||||||
/// 1. only line comment tokens -> line comment
|
/// 1. only line comment tokens -> line comment
|
||||||
/// 2. each line block commented -> uncomment all lines
|
/// 2. each line block commented -> uncomment all lines
|
||||||
/// 3. whole selection block commented -> uncomment selection
|
/// 3. whole selection block commented -> uncomment selection
|
||||||
/// 4. all lines not commented and block tokens -> comment uncommented lines
|
/// 4. all lines not commented and block tokens -> comment uncommented lines
|
||||||
/// 5. no comment tokens and not block commented -> line comment
|
/// 5. no comment tokens and not block commented -> line comment
|
||||||
fn toggle_comments(cx: &mut Context) {
|
fn toggle_comments_impl<F>(cx: &mut Context, comments_transaction: F)
|
||||||
toggle_comments_impl(
|
where
|
||||||
cx,
|
F: Fn(
|
||||||
Box::new(|cx, mut get_comment_tokens| {
|
&Rope,
|
||||||
|
&Selection,
|
||||||
|
Option<&str>,
|
||||||
|
Option<&[BlockCommentToken]>,
|
||||||
|
Option<&Syntax>,
|
||||||
|
) -> Transaction,
|
||||||
|
{
|
||||||
let (view, doc) = current!(cx.editor);
|
let (view, doc) = current!(cx.editor);
|
||||||
let syntax = doc.syntax();
|
let syntax = doc.syntax();
|
||||||
let rope = doc.text();
|
let rope = doc.text();
|
||||||
|
@ -5169,12 +5123,23 @@ fn toggle_comments(cx: &mut Context) {
|
||||||
.and_then(|lc| lc.block_comment_tokens.as_ref())
|
.and_then(|lc| lc.block_comment_tokens.as_ref())
|
||||||
.map(|tc| &tc[..]);
|
.map(|tc| &tc[..]);
|
||||||
|
|
||||||
let transaction = Transaction::change(
|
// Call the custom logic provided by the caller (the original functions).
|
||||||
|
let transaction =
|
||||||
|
comments_transaction(rope, selection, doc_line_token, doc_block_tokens, syntax);
|
||||||
|
|
||||||
|
doc.apply(&transaction, view.id);
|
||||||
|
exit_select_mode(cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle_comments(cx: &mut Context) {
|
||||||
|
toggle_comments_impl(
|
||||||
|
cx,
|
||||||
|
|rope, selection, doc_line_token, doc_block_tokens, syntax| {
|
||||||
|
Transaction::change(
|
||||||
rope,
|
rope,
|
||||||
selection.iter().flat_map(|range| {
|
selection.iter().flat_map(|range| {
|
||||||
let text = rope.slice(..);
|
|
||||||
let (injected_line_tokens, injected_block_tokens) =
|
let (injected_line_tokens, injected_block_tokens) =
|
||||||
get_comment_tokens(syntax, range.from(), range.to());
|
comment::get_injected_tokens(syntax, range.from(), range.to());
|
||||||
|
|
||||||
let line_token = injected_line_tokens
|
let line_token = injected_line_tokens
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -5184,20 +5149,21 @@ fn toggle_comments(cx: &mut Context) {
|
||||||
|
|
||||||
let block_tokens = injected_block_tokens.as_deref().or(doc_block_tokens);
|
let block_tokens = injected_block_tokens.as_deref().or(doc_block_tokens);
|
||||||
|
|
||||||
// only have line tokens
|
|
||||||
if line_token.is_some() && block_tokens.is_none() {
|
if line_token.is_some() && block_tokens.is_none() {
|
||||||
return comment::toggle_line_comments(rope, range, line_token);
|
return comment::toggle_line_comments(rope, range, line_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
let split_lines = comment::split_lines_of_range(text, range);
|
let split_lines = comment::split_lines_of_range(rope.slice(..), range);
|
||||||
|
|
||||||
let default_block_tokens = &[BlockCommentToken::default()];
|
let default_block_tokens = &[BlockCommentToken::default()];
|
||||||
let block_comment_tokens = block_tokens.unwrap_or(default_block_tokens);
|
let block_comment_tokens = block_tokens.unwrap_or(default_block_tokens);
|
||||||
|
|
||||||
let (line_commented, line_comment_changes) =
|
let (line_commented, line_comment_changes) = comment::find_block_comments(
|
||||||
comment::find_block_comments(block_comment_tokens, text, &split_lines);
|
block_comment_tokens,
|
||||||
|
rope.slice(..),
|
||||||
|
&split_lines,
|
||||||
|
);
|
||||||
|
|
||||||
// block commented by line would also be block commented so check this first
|
|
||||||
if line_commented {
|
if line_commented {
|
||||||
return comment::create_block_comment_transaction(
|
return comment::create_block_comment_transaction(
|
||||||
&split_lines,
|
&split_lines,
|
||||||
|
@ -5207,10 +5173,12 @@ fn toggle_comments(cx: &mut Context) {
|
||||||
.0;
|
.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (block_commented, comment_changes) =
|
let (block_commented, comment_changes) = comment::find_block_comments(
|
||||||
comment::find_block_comments(block_comment_tokens, text, &vec![*range]);
|
block_comment_tokens,
|
||||||
|
rope.slice(..),
|
||||||
|
&vec![*range],
|
||||||
|
);
|
||||||
|
|
||||||
// check if selection has block comments
|
|
||||||
if block_commented {
|
if block_commented {
|
||||||
return comment::create_block_comment_transaction(
|
return comment::create_block_comment_transaction(
|
||||||
&[*range],
|
&[*range],
|
||||||
|
@ -5218,9 +5186,8 @@ fn toggle_comments(cx: &mut Context) {
|
||||||
comment_changes,
|
comment_changes,
|
||||||
)
|
)
|
||||||
.0;
|
.0;
|
||||||
};
|
}
|
||||||
|
|
||||||
// not commented and only have block comment tokens
|
|
||||||
if line_token.is_none() && block_tokens.is_some() {
|
if line_token.is_none() && block_tokens.is_some() {
|
||||||
return comment::create_block_comment_transaction(
|
return comment::create_block_comment_transaction(
|
||||||
&split_lines,
|
&split_lines,
|
||||||
|
@ -5230,47 +5197,26 @@ fn toggle_comments(cx: &mut Context) {
|
||||||
.0;
|
.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not block commented at all and don't have any tokens
|
|
||||||
comment::toggle_line_comments(rope, range, line_token)
|
comment::toggle_line_comments(rope, range, line_token)
|
||||||
}),
|
}),
|
||||||
);
|
|
||||||
|
|
||||||
doc.apply(&transaction, view.id);
|
|
||||||
exit_select_mode(cx);
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_line_comments(cx: &mut Context) {
|
fn toggle_line_comments(cx: &mut Context) {
|
||||||
toggle_comments_impl(
|
toggle_comments_impl(
|
||||||
cx,
|
cx,
|
||||||
Box::new(|cx, mut get_comment_tokens| {
|
|rope, selection, doc_line_token, doc_block_tokens, syntax| {
|
||||||
let (view, doc) = current!(cx.editor);
|
|
||||||
let syntax = doc.syntax();
|
|
||||||
let rope = doc.text();
|
|
||||||
let selection = doc.selection(view.id);
|
|
||||||
|
|
||||||
// The comment tokens to fallback to if no comment tokens are found for the injection layer.
|
|
||||||
let doc_line_token: Option<&str> = doc
|
|
||||||
.language_config()
|
|
||||||
.and_then(|lc| lc.comment_tokens.as_ref())
|
|
||||||
.and_then(|tc| tc.first())
|
|
||||||
.map(|tc| tc.as_str());
|
|
||||||
let doc_block_tokens: Option<&[BlockCommentToken]> = doc
|
|
||||||
.language_config()
|
|
||||||
.and_then(|lc| lc.block_comment_tokens.as_ref())
|
|
||||||
.map(|tc| &tc[..]);
|
|
||||||
|
|
||||||
// when we add comment tokens, we want to extend our selection to
|
|
||||||
// also include the added tokens.
|
|
||||||
let mut selections = SmallVec::new();
|
let mut selections = SmallVec::new();
|
||||||
let mut added_chars = 0;
|
let mut added_chars = 0;
|
||||||
let mut removed_chars = 0;
|
let mut removed_chars = 0;
|
||||||
|
|
||||||
let transaction = Transaction::change(
|
let transaction = Transaction::change(
|
||||||
rope,
|
rope,
|
||||||
selection.iter().flat_map(|range| {
|
selection.iter().flat_map(|range| {
|
||||||
let (injected_line_tokens, injected_block_tokens) =
|
let (injected_line_tokens, injected_block_tokens) =
|
||||||
get_comment_tokens(syntax, range.from(), range.to());
|
comment::get_injected_tokens(syntax, range.from(), range.to());
|
||||||
|
|
||||||
let line_token = injected_line_tokens
|
let line_token = injected_line_tokens
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -5298,43 +5244,24 @@ fn toggle_line_comments(cx: &mut Context) {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
let transaction =
|
transaction.with_selection(Selection::new(selections, selection.primary_index()))
|
||||||
transaction.with_selection(Selection::new(selections, selection.primary_index()));
|
},
|
||||||
|
|
||||||
doc.apply(&transaction, view.id);
|
|
||||||
exit_select_mode(cx);
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_block_comments(cx: &mut Context) {
|
fn toggle_block_comments(cx: &mut Context) {
|
||||||
toggle_comments_impl(
|
toggle_comments_impl(
|
||||||
cx,
|
cx,
|
||||||
Box::new(|cx, mut get_injected_tokens| {
|
|rope, selection, doc_line_token, doc_block_tokens, syntax| {
|
||||||
let (view, doc) = current!(cx.editor);
|
|
||||||
let syntax = doc.syntax();
|
|
||||||
let rope = doc.text();
|
|
||||||
let selection = doc.selection(view.id);
|
|
||||||
|
|
||||||
// The comment tokens to fallback to if no comment tokens are found for the injection layer.
|
|
||||||
let doc_line_token: Option<&str> = doc
|
|
||||||
.language_config()
|
|
||||||
.and_then(|lc| lc.comment_tokens.as_ref())
|
|
||||||
.and_then(|tc| tc.first())
|
|
||||||
.map(|tc| tc.as_str());
|
|
||||||
let doc_block_tokens: Option<&[BlockCommentToken]> = doc
|
|
||||||
.language_config()
|
|
||||||
.and_then(|lc| lc.block_comment_tokens.as_ref())
|
|
||||||
.map(|tc| &tc[..]);
|
|
||||||
|
|
||||||
let mut selections = SmallVec::new();
|
let mut selections = SmallVec::new();
|
||||||
let mut added_chars = 0;
|
let mut added_chars = 0;
|
||||||
let mut removed_chars = 0;
|
let mut removed_chars = 0;
|
||||||
|
|
||||||
let transaction = Transaction::change(
|
let transaction = Transaction::change(
|
||||||
rope,
|
rope,
|
||||||
selection.iter().flat_map(|range| {
|
selection.iter().flat_map(|range| {
|
||||||
let (injected_line_tokens, injected_block_tokens) =
|
let (injected_line_tokens, injected_block_tokens) =
|
||||||
get_injected_tokens(syntax, range.from(), range.to());
|
comment::get_injected_tokens(syntax, range.from(), range.to());
|
||||||
|
|
||||||
let line_token = injected_line_tokens
|
let line_token = injected_line_tokens
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -5362,12 +5289,8 @@ fn toggle_block_comments(cx: &mut Context) {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
let transaction =
|
transaction.with_selection(Selection::new(selections, selection.primary_index()))
|
||||||
transaction.with_selection(Selection::new(selections, selection.primary_index()));
|
},
|
||||||
|
|
||||||
doc.apply(&transaction, view.id);
|
|
||||||
exit_select_mode(cx);
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue