mirror of https://github.com/helix-editor/helix
Merge branch 'helix-editor:master' into master
commit
ef646f0b40
|
@ -136,9 +136,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.28"
|
version = "1.1.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1"
|
checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
|
@ -47,7 +47,7 @@ Note: Only certain languages have indentation definitions at the moment. Check
|
||||||
|
|
||||||
[Installation documentation](https://docs.helix-editor.com/install.html).
|
[Installation documentation](https://docs.helix-editor.com/install.html).
|
||||||
|
|
||||||
[](https://repology.org/project/helix/versions)
|
[](https://repology.org/project/helix-editor/versions)
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@
|
||||||
| gjs | ✓ | ✓ | ✓ | `typescript-language-server`, `vscode-eslint-language-server`, `ember-language-server` |
|
| gjs | ✓ | ✓ | ✓ | `typescript-language-server`, `vscode-eslint-language-server`, `ember-language-server` |
|
||||||
| gleam | ✓ | ✓ | | `gleam` |
|
| gleam | ✓ | ✓ | | `gleam` |
|
||||||
| glimmer | ✓ | | | `ember-language-server` |
|
| glimmer | ✓ | | | `ember-language-server` |
|
||||||
| glsl | ✓ | ✓ | ✓ | |
|
| glsl | ✓ | ✓ | ✓ | `glsl_analyzer` |
|
||||||
| gn | ✓ | | | |
|
| gn | ✓ | | | |
|
||||||
| go | ✓ | ✓ | ✓ | `gopls`, `golangci-lint-langserver` |
|
| go | ✓ | ✓ | ✓ | `gopls`, `golangci-lint-langserver` |
|
||||||
| godot-resource | ✓ | ✓ | | |
|
| godot-resource | ✓ | ✓ | | |
|
||||||
|
@ -186,6 +186,7 @@
|
||||||
| smali | ✓ | | ✓ | |
|
| smali | ✓ | | ✓ | |
|
||||||
| smithy | ✓ | | | `cs` |
|
| smithy | ✓ | | | `cs` |
|
||||||
| sml | ✓ | | | |
|
| sml | ✓ | | | |
|
||||||
|
| snakemake | ✓ | | ✓ | `pylsp` |
|
||||||
| solidity | ✓ | ✓ | | `solc` |
|
| solidity | ✓ | ✓ | | `solc` |
|
||||||
| spicedb | ✓ | | | |
|
| spicedb | ✓ | | | |
|
||||||
| sql | ✓ | ✓ | | |
|
| sql | ✓ | ✓ | | |
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
- [Chocolatey](#chocolatey)
|
- [Chocolatey](#chocolatey)
|
||||||
- [MSYS2](#msys2)
|
- [MSYS2](#msys2)
|
||||||
|
|
||||||
[](https://repology.org/project/helix/versions)
|
[](https://repology.org/project/helix-editor/versions)
|
||||||
|
|
||||||
## Linux
|
## Linux
|
||||||
|
|
||||||
|
|
|
@ -283,7 +283,6 @@ These scopes are used for theming the editor interface:
|
||||||
| `ui.debug.active` | Indicator for the line at which debugging execution is paused at, found in the gutter |
|
| `ui.debug.active` | Indicator for the line at which debugging execution is paused at, found in the gutter |
|
||||||
| `ui.gutter` | Gutter |
|
| `ui.gutter` | Gutter |
|
||||||
| `ui.gutter.selected` | Gutter for the line the cursor is on |
|
| `ui.gutter.selected` | Gutter for the line the cursor is on |
|
||||||
| `ui.highlight.frameline` | Line at which debugging execution is paused at |
|
|
||||||
| `ui.linenr` | Line numbers |
|
| `ui.linenr` | Line numbers |
|
||||||
| `ui.linenr.selected` | Line number for the line the cursor is on |
|
| `ui.linenr.selected` | Line number for the line the cursor is on |
|
||||||
| `ui.statusline` | Statusline |
|
| `ui.statusline` | Statusline |
|
||||||
|
@ -297,7 +296,7 @@ These scopes are used for theming the editor interface:
|
||||||
| `ui.bufferline.background` | Style for bufferline background |
|
| `ui.bufferline.background` | Style for bufferline background |
|
||||||
| `ui.popup` | Documentation popups (e.g. Space + k) |
|
| `ui.popup` | Documentation popups (e.g. Space + k) |
|
||||||
| `ui.popup.info` | Prompt for multiple key options |
|
| `ui.popup.info` | Prompt for multiple key options |
|
||||||
| `ui.picker.header` | Header row area in pickers with multiple columns |
|
| `ui.picker.header` | Header row area in pickers with multiple columns |
|
||||||
| `ui.picker.header.column` | Column names in pickers with multiple columns |
|
| `ui.picker.header.column` | Column names in pickers with multiple columns |
|
||||||
| `ui.picker.header.column.active` | The column name in pickers with multiple columns where the cursor is entering into. |
|
| `ui.picker.header.column.active` | The column name in pickers with multiple columns where the cursor is entering into. |
|
||||||
| `ui.window` | Borderlines separating splits |
|
| `ui.window` | Borderlines separating splits |
|
||||||
|
@ -320,6 +319,7 @@ These scopes are used for theming the editor interface:
|
||||||
| `ui.selection` | For selections in the editing area |
|
| `ui.selection` | For selections in the editing area |
|
||||||
| `ui.selection.primary` | |
|
| `ui.selection.primary` | |
|
||||||
| `ui.highlight` | Highlighted lines in the picker preview |
|
| `ui.highlight` | Highlighted lines in the picker preview |
|
||||||
|
| `ui.highlight.frameline` | Line at which debugging execution is paused at |
|
||||||
| `ui.cursorline.primary` | The line of the primary cursor ([if cursorline is enabled][editor-section]) |
|
| `ui.cursorline.primary` | The line of the primary cursor ([if cursorline is enabled][editor-section]) |
|
||||||
| `ui.cursorline.secondary` | The lines of any other cursors ([if cursorline is enabled][editor-section]) |
|
| `ui.cursorline.secondary` | The lines of any other cursors ([if cursorline is enabled][editor-section]) |
|
||||||
| `ui.cursorcolumn.primary` | The column of the primary cursor ([if cursorcolumn is enabled][editor-section]) |
|
| `ui.cursorcolumn.primary` | The column of the primary cursor ([if cursorcolumn is enabled][editor-section]) |
|
||||||
|
|
|
@ -9,6 +9,24 @@ use crate::{
|
||||||
use helix_stdx::rope::RopeSliceExt;
|
use helix_stdx::rope::RopeSliceExt;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
pub const DEFAULT_COMMENT_TOKEN: &str = "//";
|
||||||
|
|
||||||
|
/// Returns the longest matching comment token of the given line (if it exists).
|
||||||
|
pub fn get_comment_token<'a, S: AsRef<str>>(
|
||||||
|
text: RopeSlice,
|
||||||
|
tokens: &'a [S],
|
||||||
|
line_num: usize,
|
||||||
|
) -> Option<&'a str> {
|
||||||
|
let line = text.line(line_num);
|
||||||
|
let start = line.first_non_whitespace_char()?;
|
||||||
|
|
||||||
|
tokens
|
||||||
|
.iter()
|
||||||
|
.map(AsRef::as_ref)
|
||||||
|
.filter(|token| line.slice(start..).starts_with(token))
|
||||||
|
.max_by_key(|token| token.len())
|
||||||
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
|
@ -28,21 +46,20 @@ fn find_line_comment(
|
||||||
let mut min = usize::MAX; // minimum col for first_non_whitespace_char
|
let mut min = usize::MAX; // minimum col for first_non_whitespace_char
|
||||||
let mut margin = 1;
|
let mut margin = 1;
|
||||||
let token_len = token.chars().count();
|
let token_len = token.chars().count();
|
||||||
|
|
||||||
for line in lines {
|
for line in lines {
|
||||||
let line_slice = text.line(line);
|
let line_slice = text.line(line);
|
||||||
if let Some(pos) = line_slice.first_non_whitespace_char() {
|
if let Some(pos) = line_slice.first_non_whitespace_char() {
|
||||||
let len = line_slice.len_chars();
|
let len = line_slice.len_chars();
|
||||||
|
|
||||||
if pos < min {
|
min = std::cmp::min(min, pos);
|
||||||
min = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// line can be shorter than pos + token len
|
// line can be shorter than pos + token len
|
||||||
let fragment = Cow::from(line_slice.slice(pos..std::cmp::min(pos + token.len(), len)));
|
let fragment = Cow::from(line_slice.slice(pos..std::cmp::min(pos + token.len(), len)));
|
||||||
|
|
||||||
|
// as soon as one of the non-blank lines doesn't have a comment, the whole block is
|
||||||
|
// considered uncommented.
|
||||||
if fragment != token {
|
if fragment != token {
|
||||||
// as soon as one of the non-blank lines doesn't have a comment, the whole block is
|
|
||||||
// considered uncommented.
|
|
||||||
commented = false;
|
commented = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +73,7 @@ fn find_line_comment(
|
||||||
to_change.push(line);
|
to_change.push(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(commented, to_change, min, margin)
|
(commented, to_change, min, margin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +81,7 @@ fn find_line_comment(
|
||||||
pub fn toggle_line_comments(doc: &Rope, selection: &Selection, token: Option<&str>) -> Transaction {
|
pub fn toggle_line_comments(doc: &Rope, selection: &Selection, token: Option<&str>) -> Transaction {
|
||||||
let text = doc.slice(..);
|
let text = doc.slice(..);
|
||||||
|
|
||||||
let token = token.unwrap_or("//");
|
let token = token.unwrap_or(DEFAULT_COMMENT_TOKEN);
|
||||||
let comment = Tendril::from(format!("{} ", token));
|
let comment = Tendril::from(format!("{} ", token));
|
||||||
|
|
||||||
let mut lines: Vec<usize> = Vec::with_capacity(selection.len());
|
let mut lines: Vec<usize> = Vec::with_capacity(selection.len());
|
||||||
|
@ -317,56 +335,87 @@ pub fn split_lines_of_selection(text: RopeSlice, selection: &Selection) -> Selec
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
mod find_line_comment {
|
||||||
fn test_find_line_comment() {
|
use super::*;
|
||||||
// four lines, two space indented, except for line 1 which is blank.
|
|
||||||
let mut doc = Rope::from(" 1\n\n 2\n 3");
|
|
||||||
// select whole document
|
|
||||||
let mut selection = Selection::single(0, doc.len_chars() - 1);
|
|
||||||
|
|
||||||
let text = doc.slice(..);
|
#[test]
|
||||||
|
fn not_commented() {
|
||||||
|
// four lines, two space indented, except for line 1 which is blank.
|
||||||
|
let doc = Rope::from(" 1\n\n 2\n 3");
|
||||||
|
|
||||||
let res = find_line_comment("//", text, 0..3);
|
let text = doc.slice(..);
|
||||||
// (commented = true, to_change = [line 0, line 2], min = col 2, margin = 0)
|
|
||||||
assert_eq!(res, (false, vec![0, 2], 2, 0));
|
|
||||||
|
|
||||||
// comment
|
let res = find_line_comment("//", text, 0..3);
|
||||||
let transaction = toggle_line_comments(&doc, &selection, None);
|
// (commented = false, to_change = [line 0, line 2], min = col 2, margin = 0)
|
||||||
transaction.apply(&mut doc);
|
assert_eq!(res, (false, vec![0, 2], 2, 0));
|
||||||
selection = selection.map(transaction.changes());
|
}
|
||||||
|
|
||||||
assert_eq!(doc, " // 1\n\n // 2\n // 3");
|
#[test]
|
||||||
|
fn is_commented() {
|
||||||
|
// three lines where the second line is empty.
|
||||||
|
let doc = Rope::from("// hello\n\n// there");
|
||||||
|
|
||||||
// uncomment
|
let res = find_line_comment("//", doc.slice(..), 0..3);
|
||||||
let transaction = toggle_line_comments(&doc, &selection, None);
|
|
||||||
transaction.apply(&mut doc);
|
|
||||||
selection = selection.map(transaction.changes());
|
|
||||||
assert_eq!(doc, " 1\n\n 2\n 3");
|
|
||||||
assert!(selection.len() == 1); // to ignore the selection unused warning
|
|
||||||
|
|
||||||
// 0 margin comments
|
// (commented = true, to_change = [line 0, line 2], min = col 0, margin = 1)
|
||||||
doc = Rope::from(" //1\n\n //2\n //3");
|
assert_eq!(res, (true, vec![0, 2], 0, 1));
|
||||||
// reset the selection.
|
}
|
||||||
selection = Selection::single(0, doc.len_chars() - 1);
|
}
|
||||||
|
|
||||||
let transaction = toggle_line_comments(&doc, &selection, None);
|
// TODO: account for uncommenting with uneven comment indentation
|
||||||
transaction.apply(&mut doc);
|
mod toggle_line_comment {
|
||||||
selection = selection.map(transaction.changes());
|
use super::*;
|
||||||
assert_eq!(doc, " 1\n\n 2\n 3");
|
|
||||||
assert!(selection.len() == 1); // to ignore the selection unused warning
|
|
||||||
|
|
||||||
// 0 margin comments, with no space
|
#[test]
|
||||||
doc = Rope::from("//");
|
fn comment() {
|
||||||
// reset the selection.
|
// four lines, two space indented, except for line 1 which is blank.
|
||||||
selection = Selection::single(0, doc.len_chars() - 1);
|
let mut doc = Rope::from(" 1\n\n 2\n 3");
|
||||||
|
// select whole document
|
||||||
|
let selection = Selection::single(0, doc.len_chars() - 1);
|
||||||
|
|
||||||
let transaction = toggle_line_comments(&doc, &selection, None);
|
let transaction = toggle_line_comments(&doc, &selection, None);
|
||||||
transaction.apply(&mut doc);
|
transaction.apply(&mut doc);
|
||||||
selection = selection.map(transaction.changes());
|
|
||||||
assert_eq!(doc, "");
|
|
||||||
assert!(selection.len() == 1); // to ignore the selection unused warning
|
|
||||||
|
|
||||||
// TODO: account for uncommenting with uneven comment indentation
|
assert_eq!(doc, " // 1\n\n // 2\n // 3");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn uncomment() {
|
||||||
|
let mut doc = Rope::from(" // 1\n\n // 2\n // 3");
|
||||||
|
let mut selection = Selection::single(0, doc.len_chars() - 1);
|
||||||
|
|
||||||
|
let transaction = toggle_line_comments(&doc, &selection, None);
|
||||||
|
transaction.apply(&mut doc);
|
||||||
|
selection = selection.map(transaction.changes());
|
||||||
|
|
||||||
|
assert_eq!(doc, " 1\n\n 2\n 3");
|
||||||
|
assert!(selection.len() == 1); // to ignore the selection unused warning
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn uncomment_0_margin_comments() {
|
||||||
|
let mut doc = Rope::from(" //1\n\n //2\n //3");
|
||||||
|
let mut selection = Selection::single(0, doc.len_chars() - 1);
|
||||||
|
|
||||||
|
let transaction = toggle_line_comments(&doc, &selection, None);
|
||||||
|
transaction.apply(&mut doc);
|
||||||
|
selection = selection.map(transaction.changes());
|
||||||
|
|
||||||
|
assert_eq!(doc, " 1\n\n 2\n 3");
|
||||||
|
assert!(selection.len() == 1); // to ignore the selection unused warning
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn uncomment_0_margin_comments_with_no_space() {
|
||||||
|
let mut doc = Rope::from("//");
|
||||||
|
let mut selection = Selection::single(0, doc.len_chars() - 1);
|
||||||
|
|
||||||
|
let transaction = toggle_line_comments(&doc, &selection, None);
|
||||||
|
transaction.apply(&mut doc);
|
||||||
|
selection = selection.map(transaction.changes());
|
||||||
|
assert_eq!(doc, "");
|
||||||
|
assert!(selection.len() == 1); // to ignore the selection unused warning
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -413,4 +462,32 @@ mod test {
|
||||||
transaction.apply(&mut doc);
|
transaction.apply(&mut doc);
|
||||||
assert_eq!(doc, "");
|
assert_eq!(doc, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test, if `get_comment_tokens` works, even if the content of the file includes chars, whose
|
||||||
|
/// byte size unequal the amount of chars
|
||||||
|
#[test]
|
||||||
|
fn test_get_comment_with_char_boundaries() {
|
||||||
|
let rope = Rope::from("··");
|
||||||
|
let tokens = ["//", "///"];
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
super::get_comment_token(rope.slice(..), tokens.as_slice(), 0),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test for `get_comment_token`.
|
||||||
|
///
|
||||||
|
/// Assuming the comment tokens are stored as `["///", "//"]`, `get_comment_token` should still
|
||||||
|
/// return `///` instead of `//` if the user is in a doc-comment section.
|
||||||
|
#[test]
|
||||||
|
fn test_use_longest_comment() {
|
||||||
|
let text = Rope::from(" /// amogus");
|
||||||
|
let tokens = ["///", "//"];
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
super::get_comment_token(text.slice(..), tokens.as_slice(), 0),
|
||||||
|
Some("///")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@ use helix_core::{
|
||||||
encoding, find_workspace,
|
encoding, find_workspace,
|
||||||
graphemes::{self, next_grapheme_boundary, RevRopeGraphemes},
|
graphemes::{self, next_grapheme_boundary, RevRopeGraphemes},
|
||||||
history::UndoKind,
|
history::UndoKind,
|
||||||
increment, indent,
|
increment,
|
||||||
indent::IndentStyle,
|
indent::{self, IndentStyle},
|
||||||
line_ending::{get_line_ending_of_str, line_end_char_index},
|
line_ending::{get_line_ending_of_str, line_end_char_index},
|
||||||
match_brackets,
|
match_brackets,
|
||||||
movement::{self, move_vertically_visual, Direction},
|
movement::{self, move_vertically_visual, Direction},
|
||||||
|
@ -3467,31 +3467,51 @@ fn open(cx: &mut Context, open: Open) {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let indent = indent::indent_for_newline(
|
let continue_comment_token = doc
|
||||||
doc.language_config(),
|
.language_config()
|
||||||
doc.syntax(),
|
.and_then(|config| config.comment_tokens.as_ref())
|
||||||
&doc.config.load().indent_heuristic,
|
.and_then(|tokens| comment::get_comment_token(text, tokens, cursor_line));
|
||||||
&doc.indent_style,
|
|
||||||
doc.tab_width(),
|
let line = text.line(cursor_line);
|
||||||
text,
|
let indent = match line.first_non_whitespace_char() {
|
||||||
line_num,
|
Some(pos) if continue_comment_token.is_some() => line.slice(..pos).to_string(),
|
||||||
line_end_index,
|
_ => indent::indent_for_newline(
|
||||||
cursor_line,
|
doc.language_config(),
|
||||||
);
|
doc.syntax(),
|
||||||
|
&doc.config.load().indent_heuristic,
|
||||||
|
&doc.indent_style,
|
||||||
|
doc.tab_width(),
|
||||||
|
text,
|
||||||
|
line_num,
|
||||||
|
line_end_index,
|
||||||
|
cursor_line,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
let indent_len = indent.len();
|
let indent_len = indent.len();
|
||||||
let mut text = String::with_capacity(1 + indent_len);
|
let mut text = String::with_capacity(1 + indent_len);
|
||||||
text.push_str(doc.line_ending.as_str());
|
text.push_str(doc.line_ending.as_str());
|
||||||
text.push_str(&indent);
|
text.push_str(&indent);
|
||||||
|
|
||||||
|
if let Some(token) = continue_comment_token {
|
||||||
|
text.push_str(token);
|
||||||
|
text.push(' ');
|
||||||
|
}
|
||||||
|
|
||||||
let text = text.repeat(count);
|
let text = text.repeat(count);
|
||||||
|
|
||||||
// calculate new selection ranges
|
// calculate new selection ranges
|
||||||
let pos = offs + line_end_index + line_end_offset_width;
|
let pos = offs + line_end_index + line_end_offset_width;
|
||||||
|
let comment_len = continue_comment_token
|
||||||
|
.map(|token| token.len() + 1) // `+ 1` for the extra space added
|
||||||
|
.unwrap_or_default();
|
||||||
for i in 0..count {
|
for i in 0..count {
|
||||||
// pos -> beginning of reference line,
|
// pos -> beginning of reference line,
|
||||||
// + (i * (1+indent_len)) -> beginning of i'th line from pos
|
// + (i * (1+indent_len + comment_len)) -> beginning of i'th line from pos (possibly including comment token)
|
||||||
// + indent_len -> -> indent for i'th line
|
// + indent_len + comment_len -> -> indent for i'th line
|
||||||
ranges.push(Range::point(pos + (i * (1 + indent_len)) + indent_len));
|
ranges.push(Range::point(
|
||||||
|
pos + (i * (1 + indent_len + comment_len)) + indent_len + comment_len,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
offs += text.chars().count();
|
offs += text.chars().count();
|
||||||
|
@ -3929,6 +3949,11 @@ pub mod insert {
|
||||||
|
|
||||||
let mut new_text = String::new();
|
let mut new_text = String::new();
|
||||||
|
|
||||||
|
let continue_comment_token = doc
|
||||||
|
.language_config()
|
||||||
|
.and_then(|config| config.comment_tokens.as_ref())
|
||||||
|
.and_then(|tokens| comment::get_comment_token(text, tokens, current_line));
|
||||||
|
|
||||||
// If the current line is all whitespace, insert a line ending at the beginning of
|
// If the current line is all whitespace, insert a line ending at the beginning of
|
||||||
// the current line. This makes the current line empty and the new line contain the
|
// the current line. This makes the current line empty and the new line contain the
|
||||||
// indentation of the old line.
|
// indentation of the old line.
|
||||||
|
@ -3938,17 +3963,22 @@ pub mod insert {
|
||||||
|
|
||||||
(line_start, line_start, new_text.chars().count())
|
(line_start, line_start, new_text.chars().count())
|
||||||
} else {
|
} else {
|
||||||
let indent = indent::indent_for_newline(
|
let line = text.line(current_line);
|
||||||
doc.language_config(),
|
|
||||||
doc.syntax(),
|
let indent = match line.first_non_whitespace_char() {
|
||||||
&doc.config.load().indent_heuristic,
|
Some(pos) if continue_comment_token.is_some() => line.slice(..pos).to_string(),
|
||||||
&doc.indent_style,
|
_ => indent::indent_for_newline(
|
||||||
doc.tab_width(),
|
doc.language_config(),
|
||||||
text,
|
doc.syntax(),
|
||||||
current_line,
|
&doc.config.load().indent_heuristic,
|
||||||
pos,
|
&doc.indent_style,
|
||||||
current_line,
|
doc.tab_width(),
|
||||||
);
|
text,
|
||||||
|
current_line,
|
||||||
|
pos,
|
||||||
|
current_line,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
// If we are between pairs (such as brackets), we want to
|
// If we are between pairs (such as brackets), we want to
|
||||||
// insert an additional line which is indented one level
|
// insert an additional line which is indented one level
|
||||||
|
@ -3958,19 +3988,30 @@ pub mod insert {
|
||||||
.and_then(|pairs| pairs.get(prev))
|
.and_then(|pairs| pairs.get(prev))
|
||||||
.map_or(false, |pair| pair.open == prev && pair.close == curr);
|
.map_or(false, |pair| pair.open == prev && pair.close == curr);
|
||||||
|
|
||||||
let local_offs = if on_auto_pair {
|
let local_offs = if let Some(token) = continue_comment_token {
|
||||||
|
new_text.push_str(doc.line_ending.as_str());
|
||||||
|
new_text.push_str(&indent);
|
||||||
|
new_text.push_str(token);
|
||||||
|
new_text.push(' ');
|
||||||
|
new_text.chars().count()
|
||||||
|
} else if on_auto_pair {
|
||||||
|
// line where the cursor will be
|
||||||
let inner_indent = indent.clone() + doc.indent_style.as_str();
|
let inner_indent = indent.clone() + doc.indent_style.as_str();
|
||||||
new_text.reserve_exact(2 + indent.len() + inner_indent.len());
|
new_text.reserve_exact(2 + indent.len() + inner_indent.len());
|
||||||
new_text.push_str(doc.line_ending.as_str());
|
new_text.push_str(doc.line_ending.as_str());
|
||||||
new_text.push_str(&inner_indent);
|
new_text.push_str(&inner_indent);
|
||||||
|
|
||||||
|
// line where the matching pair will be
|
||||||
let local_offs = new_text.chars().count();
|
let local_offs = new_text.chars().count();
|
||||||
new_text.push_str(doc.line_ending.as_str());
|
new_text.push_str(doc.line_ending.as_str());
|
||||||
new_text.push_str(&indent);
|
new_text.push_str(&indent);
|
||||||
|
|
||||||
local_offs
|
local_offs
|
||||||
} else {
|
} else {
|
||||||
new_text.reserve_exact(1 + indent.len());
|
new_text.reserve_exact(1 + indent.len());
|
||||||
new_text.push_str(doc.line_ending.as_str());
|
new_text.push_str(doc.line_ending.as_str());
|
||||||
new_text.push_str(&indent);
|
new_text.push_str(&indent);
|
||||||
|
|
||||||
new_text.chars().count()
|
new_text.chars().count()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ forth-lsp = { command = "forth-lsp" }
|
||||||
fortls = { command = "fortls", args = ["--lowercase_intrinsics"] }
|
fortls = { command = "fortls", args = ["--lowercase_intrinsics"] }
|
||||||
fsharp-ls = { command = "fsautocomplete", config = { AutomaticWorkspaceInit = true } }
|
fsharp-ls = { command = "fsautocomplete", config = { AutomaticWorkspaceInit = true } }
|
||||||
gleam = { command = "gleam", args = ["lsp"] }
|
gleam = { command = "gleam", args = ["lsp"] }
|
||||||
|
glsl_analyzer = { command = "glsl_analyzer" }
|
||||||
graphql-language-service = { command = "graphql-lsp", args = ["server", "-m", "stream"] }
|
graphql-language-service = { command = "graphql-lsp", args = ["server", "-m", "stream"] }
|
||||||
haskell-language-server = { command = "haskell-language-server-wrapper", args = ["--lsp"] }
|
haskell-language-server = { command = "haskell-language-server-wrapper", args = ["--lsp"] }
|
||||||
idris2-lsp = { command = "idris2-lsp" }
|
idris2-lsp = { command = "idris2-lsp" }
|
||||||
|
@ -1452,6 +1453,7 @@ file-types = ["glsl", "vert", "tesc", "tese", "geom", "frag", "comp" ]
|
||||||
comment-token = "//"
|
comment-token = "//"
|
||||||
block-comment-tokens = { start = "/*", end = "*/" }
|
block-comment-tokens = { start = "/*", end = "*/" }
|
||||||
indent = { tab-width = 4, unit = " " }
|
indent = { tab-width = 4, unit = " " }
|
||||||
|
language-servers = [ "glsl_analyzer" ]
|
||||||
injection-regex = "glsl"
|
injection-regex = "glsl"
|
||||||
|
|
||||||
[[grammar]]
|
[[grammar]]
|
||||||
|
@ -3270,7 +3272,7 @@ text-width = 72
|
||||||
|
|
||||||
[[grammar]]
|
[[grammar]]
|
||||||
name = "jjdescription"
|
name = "jjdescription"
|
||||||
source = { git = "https://github.com/kareigu/tree-sitter-jjdescription", rev = "2ddec6cad07b366aee276a608e1daa2c29d3caf2" }
|
source = { git = "https://github.com/kareigu/tree-sitter-jjdescription", rev = "23dd3dd18ee29bdd761642511aa314215801afd8" }
|
||||||
|
|
||||||
[[language]]
|
[[language]]
|
||||||
name = "jq"
|
name = "jq"
|
||||||
|
@ -3833,3 +3835,17 @@ language-servers = ["circom-lsp"]
|
||||||
[[grammar]]
|
[[grammar]]
|
||||||
name = "circom"
|
name = "circom"
|
||||||
source = { git = "https://github.com/Decurity/tree-sitter-circom", rev = "02150524228b1e6afef96949f2d6b7cc0aaf999e" }
|
source = { git = "https://github.com/Decurity/tree-sitter-circom", rev = "02150524228b1e6afef96949f2d6b7cc0aaf999e" }
|
||||||
|
|
||||||
|
[[language]]
|
||||||
|
name = "snakemake"
|
||||||
|
scope = "source.snakemake"
|
||||||
|
roots = ["Snakefile", "config.yaml", "environment.yaml", "workflow/"]
|
||||||
|
file-types = ["smk", "Snakefile"]
|
||||||
|
comment-tokens = ["#", "##"]
|
||||||
|
indent = { tab-width = 2, unit = " " }
|
||||||
|
language-servers = ["pylsp" ]
|
||||||
|
|
||||||
|
|
||||||
|
[[grammar]]
|
||||||
|
name = "snakemake"
|
||||||
|
source = { git = "https://github.com/osthomas/tree-sitter-snakemake", rev = "e909815acdbe37e69440261ebb1091ed52e1dec6" }
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
Copyright (c) 2016 Max Brunsfeld
|
||||||
|
Copyright (c) 2023 Oliver Thomas
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,8 @@
|
||||||
|
; inherits: python
|
||||||
|
|
||||||
|
[
|
||||||
|
(rule_definition)
|
||||||
|
(rule_inheritance)
|
||||||
|
(module_definition)
|
||||||
|
(checkpoint_definition)
|
||||||
|
] @fold
|
|
@ -0,0 +1,76 @@
|
||||||
|
; inherits: python
|
||||||
|
|
||||||
|
; Compound directives
|
||||||
|
[
|
||||||
|
"rule"
|
||||||
|
"checkpoint"
|
||||||
|
"module"
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
; Top level directives (eg. configfile, include)
|
||||||
|
(module
|
||||||
|
(directive
|
||||||
|
name: _ @keyword))
|
||||||
|
|
||||||
|
; Subordinate directives (eg. input, output)
|
||||||
|
((_)
|
||||||
|
body: (_
|
||||||
|
(directive
|
||||||
|
name: _ @label)))
|
||||||
|
|
||||||
|
; rule/module/checkpoint names
|
||||||
|
(rule_definition
|
||||||
|
name: (identifier) @type)
|
||||||
|
|
||||||
|
(module_definition
|
||||||
|
name: (identifier) @type)
|
||||||
|
|
||||||
|
(checkpoint_definition
|
||||||
|
name: (identifier) @type)
|
||||||
|
|
||||||
|
; Rule imports
|
||||||
|
(rule_import
|
||||||
|
"use" @keyword.import
|
||||||
|
"rule" @keyword.import
|
||||||
|
"from" @keyword.import
|
||||||
|
"exclude"? @keyword.import
|
||||||
|
"as"? @keyword.import
|
||||||
|
"with"? @keyword.import)
|
||||||
|
|
||||||
|
; Rule inheritance
|
||||||
|
(rule_inheritance
|
||||||
|
"use" @keyword
|
||||||
|
"rule" @keyword
|
||||||
|
"with" @keyword)
|
||||||
|
|
||||||
|
; Wildcard names
|
||||||
|
(wildcard (identifier) @variable)
|
||||||
|
(wildcard (flag) @variable.parameter.builtin)
|
||||||
|
|
||||||
|
; builtin variables
|
||||||
|
((identifier) @variable.builtin
|
||||||
|
(#any-of? @variable.builtin "checkpoints" "config" "gather" "rules" "scatter" "workflow"))
|
||||||
|
|
||||||
|
; References to directive labels in wildcard interpolations
|
||||||
|
; the #any-of? queries are moved above the #has-ancestor? queries to
|
||||||
|
; short-circuit the potentially expensive tree traversal, if possible
|
||||||
|
; see:
|
||||||
|
; https://github.com/nvim-treesitter/nvim-treesitter/pull/4302#issuecomment-1685789790
|
||||||
|
; directive labels in wildcard context
|
||||||
|
((wildcard
|
||||||
|
(identifier) @label)
|
||||||
|
(#any-of? @label "input" "log" "output" "params" "resources" "threads" "wildcards"))
|
||||||
|
|
||||||
|
((wildcard
|
||||||
|
(attribute
|
||||||
|
object: (identifier) @label))
|
||||||
|
(#any-of? @label "input" "log" "output" "params" "resources" "threads" "wildcards"))
|
||||||
|
|
||||||
|
((wildcard
|
||||||
|
(subscript
|
||||||
|
value: (identifier) @label))
|
||||||
|
(#any-of? @label "input" "log" "output" "params" "resources" "threads" "wildcards"))
|
||||||
|
|
||||||
|
; directive labels in block context (eg. within 'run:')
|
||||||
|
((identifier) @label
|
||||||
|
(#any-of? @label "input" "log" "output" "params" "resources" "threads" "wildcards"))
|
|
@ -0,0 +1,27 @@
|
||||||
|
; inherits: python
|
||||||
|
|
||||||
|
|
||||||
|
[
|
||||||
|
(rule_definition)
|
||||||
|
(checkpoint_definition)
|
||||||
|
(rule_inheritance)
|
||||||
|
(module_definition)
|
||||||
|
] @indent
|
||||||
|
|
||||||
|
[
|
||||||
|
(rule_definition)
|
||||||
|
(checkpoint_definition)
|
||||||
|
(rule_inheritance)
|
||||||
|
(module_definition)
|
||||||
|
] @extend
|
||||||
|
|
||||||
|
|
||||||
|
(directive) @indent
|
||||||
|
(directive) @extend
|
||||||
|
|
||||||
|
(rule_import
|
||||||
|
"with"
|
||||||
|
":") @indent
|
||||||
|
(rule_import
|
||||||
|
"with"
|
||||||
|
":") @extend
|
|
@ -0,0 +1,5 @@
|
||||||
|
; inherits: python
|
||||||
|
|
||||||
|
(wildcard
|
||||||
|
(constraint) @injection.content
|
||||||
|
(#set! injection.language "regex"))
|
|
@ -0,0 +1 @@
|
||||||
|
; inherits: python
|
Loading…
Reference in New Issue