mirror of https://github.com/helix-editor/helix
Merge remote-tracking branch 'origin/master' into debug
commit
d1854d8e6a
@ -0,0 +1,60 @@
|
|||||||
|
# Adding languages
|
||||||
|
|
||||||
|
## Submodules
|
||||||
|
|
||||||
|
To add a new langauge, you should first add a tree-sitter submodule. To do this,
|
||||||
|
you can run the command
|
||||||
|
```sh
|
||||||
|
git submodule add -f <repository> helix-syntax/languages/tree-sitter-<name>
|
||||||
|
```
|
||||||
|
For example, to add tree-sitter-ocaml you would run
|
||||||
|
```sh
|
||||||
|
git submodule add -f https://github.com/tree-sitter/tree-sitter-ocaml helix-syntax/languages/tree-sitter-ocaml
|
||||||
|
```
|
||||||
|
Make sure the submodule is shallow by doing
|
||||||
|
```sh
|
||||||
|
git config -f .gitmodules submodule.helix-syntax/languages/tree-sitter-<name>.shallow true
|
||||||
|
```
|
||||||
|
|
||||||
|
or you can manually add `shallow = true` to `.gitmodules`.
|
||||||
|
|
||||||
|
## languages.toml
|
||||||
|
|
||||||
|
Next, you need to add the language to the [`languages.toml`][languages.toml] found in the root of
|
||||||
|
the repository; this `languages.toml` file is included at compilation time, and
|
||||||
|
is distinct from the `language.toml` file in the user's [configuration
|
||||||
|
directory](../configuration.md).
|
||||||
|
|
||||||
|
These are the available keys and descriptions for the file.
|
||||||
|
|
||||||
|
| Key | Description |
|
||||||
|
| ---- | ----------- |
|
||||||
|
| name | The name of the language |
|
||||||
|
| scope | A string like `source.js` that identifies the language. Currently, we strive to match the scope names used by popular TextMate grammars and by the Linguist library. Usually `source.<name>` or `text.<name>` in case of markup languages |
|
||||||
|
| injection-regex | regex pattern that will be tested against a language name in order to determine whether this language should be used for a potential [language injection][treesitter-language-injection] site. |
|
||||||
|
| file-types | The filetypes of the language, for example `["yml", "yaml"]` |
|
||||||
|
| shebangs | The interpreters from the shebang line, for example `["sh", "bash"]` |
|
||||||
|
| roots | A set of marker files to look for when trying to find the workspace root. For example `Cargo.lock`, `yarn.lock` |
|
||||||
|
| auto-format | Whether to autoformat this language when saving |
|
||||||
|
| comment-token | The token to use as a comment-token |
|
||||||
|
| indent | The indent to use. Has sub keys `tab-width` and `unit` |
|
||||||
|
| config | Language server configuration |
|
||||||
|
|
||||||
|
## Queries
|
||||||
|
|
||||||
|
For a language to have syntax-highlighting and indentation among other things, you have to add queries. Add a directory for your language with the path `runtime/queries/<name>/`. The tree-sitter [website](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#queries) gives more info on how to write queries.
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
|
||||||
|
- If you get errors when building after switching branches, you may have to remove or update tree-sitter submodules. You can update submodules by running
|
||||||
|
```sh
|
||||||
|
git submodule sync; git submodule update --init
|
||||||
|
```
|
||||||
|
- Make sure to not use the `--remote` flag. To remove submodules look inside the `.gitmodules` and remove directories that are not present inside of it.
|
||||||
|
|
||||||
|
- If a parser is segfaulting or you want to remove the parser, make sure to remove the submodule *and* the compiled parser in `runtime/grammar/<name>.so`
|
||||||
|
|
||||||
|
- The indents query is `indents.toml`, *not* `indents.scm`. See [this](https://github.com/helix-editor/helix/issues/114) issue for more information.
|
||||||
|
|
||||||
|
[treesitter-language-injection]: https://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-injection
|
||||||
|
[languages.toml]: https://github.com/helix-editor/helix/blob/master/languages.toml
|
@ -0,0 +1,14 @@
|
|||||||
|
# Languages
|
||||||
|
|
||||||
|
Language-specific settings and settings for particular language servers can be configured in a `languages.toml` file placed in your [configuration directory](./configuration.md). Helix actually uses two `languages.toml` files, the [first one](https://github.com/helix-editor/helix/blob/master/languages.toml) is in the main helix repository; it contains the default settings for each language and is included in the helix binary at compile time. Users who want to see the available settings and options can either reference the helix repo's `languages.toml` file, or consult the table in the [adding languages](./guides/adding_languages.md) section.
|
||||||
|
|
||||||
|
Changes made to the `languages.toml` file in a user's [configuration directory](./configuration.md) are merged with helix's defaults on start-up, such that a user's settings will take precedence over defaults in the event of a collision. For example, the default `languages.toml` sets rust's `auto-format` to `true`. If a user wants to disable auto-format, they can change the `languages.toml` in their [configuration directory](./configuration.md) to make the rust entry read like the example below; the new key/value pair `auto-format = false` will override the default when the two sets of settings are merged on start-up:
|
||||||
|
|
||||||
|
```
|
||||||
|
# in <config_dir>/helix/languages.toml
|
||||||
|
|
||||||
|
[[language]]
|
||||||
|
name = "rust"
|
||||||
|
auto-format = false
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,499 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use ropey::RopeSlice;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
textobject::{textobject_word, TextObject},
|
||||||
|
Range, Tendril,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct NumberIncrementor<'a> {
|
||||||
|
pub range: Range,
|
||||||
|
pub value: i64,
|
||||||
|
pub radix: u32,
|
||||||
|
|
||||||
|
text: RopeSlice<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NumberIncrementor<'a> {
|
||||||
|
/// Return information about number under rang if there is one.
|
||||||
|
pub fn from_range(text: RopeSlice, range: Range) -> Option<NumberIncrementor> {
|
||||||
|
// If the cursor is on the minus sign of a number we want to get the word textobject to the
|
||||||
|
// right of it.
|
||||||
|
let range = if range.to() < text.len_chars()
|
||||||
|
&& range.to() - range.from() <= 1
|
||||||
|
&& text.char(range.from()) == '-'
|
||||||
|
{
|
||||||
|
Range::new(range.from() + 1, range.to() + 1)
|
||||||
|
} else {
|
||||||
|
range
|
||||||
|
};
|
||||||
|
|
||||||
|
let range = textobject_word(text, range, TextObject::Inside, 1, false);
|
||||||
|
|
||||||
|
// If there is a minus sign to the left of the word object, we want to include it in the range.
|
||||||
|
let range = if range.from() > 0 && text.char(range.from() - 1) == '-' {
|
||||||
|
range.extend(range.from() - 1, range.from())
|
||||||
|
} else {
|
||||||
|
range
|
||||||
|
};
|
||||||
|
|
||||||
|
let word: String = text
|
||||||
|
.slice(range.from()..range.to())
|
||||||
|
.chars()
|
||||||
|
.filter(|&c| c != '_')
|
||||||
|
.collect();
|
||||||
|
let (radix, prefixed) = if word.starts_with("0x") {
|
||||||
|
(16, true)
|
||||||
|
} else if word.starts_with("0o") {
|
||||||
|
(8, true)
|
||||||
|
} else if word.starts_with("0b") {
|
||||||
|
(2, true)
|
||||||
|
} else {
|
||||||
|
(10, false)
|
||||||
|
};
|
||||||
|
|
||||||
|
let number = if prefixed { &word[2..] } else { &word };
|
||||||
|
|
||||||
|
let value = i128::from_str_radix(number, radix).ok()?;
|
||||||
|
if (value.is_positive() && value.leading_zeros() < 64)
|
||||||
|
|| (value.is_negative() && value.leading_ones() < 64)
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = value as i64;
|
||||||
|
Some(NumberIncrementor {
|
||||||
|
range,
|
||||||
|
value,
|
||||||
|
radix,
|
||||||
|
text,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add `amount` to the number and return the formatted text.
|
||||||
|
pub fn incremented_text(&self, amount: i64) -> Tendril {
|
||||||
|
let old_text: Cow<str> = self.text.slice(self.range.from()..self.range.to()).into();
|
||||||
|
let old_length = old_text.len();
|
||||||
|
let new_value = self.value.wrapping_add(amount);
|
||||||
|
|
||||||
|
// Get separator indexes from right to left.
|
||||||
|
let separator_rtl_indexes: Vec<usize> = old_text
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(i, c)| if c == '_' { Some(i) } else { None })
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let format_length = if self.radix == 10 {
|
||||||
|
match (self.value.is_negative(), new_value.is_negative()) {
|
||||||
|
(true, false) => old_length - 1,
|
||||||
|
(false, true) => old_length + 1,
|
||||||
|
_ => old_text.len(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
old_text.len() - 2
|
||||||
|
} - separator_rtl_indexes.len();
|
||||||
|
|
||||||
|
let mut new_text = match self.radix {
|
||||||
|
2 => format!("0b{:01$b}", new_value, format_length),
|
||||||
|
8 => format!("0o{:01$o}", new_value, format_length),
|
||||||
|
10 if old_text.starts_with('0') || old_text.starts_with("-0") => {
|
||||||
|
format!("{:01$}", new_value, format_length)
|
||||||
|
}
|
||||||
|
10 => format!("{}", new_value),
|
||||||
|
16 => {
|
||||||
|
let (lower_count, upper_count): (usize, usize) =
|
||||||
|
old_text.chars().skip(2).fold((0, 0), |(lower, upper), c| {
|
||||||
|
(
|
||||||
|
lower + c.is_ascii_lowercase().then(|| 1).unwrap_or(0),
|
||||||
|
upper + c.is_ascii_uppercase().then(|| 1).unwrap_or(0),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
if upper_count > lower_count {
|
||||||
|
format!("0x{:01$X}", new_value, format_length)
|
||||||
|
} else {
|
||||||
|
format!("0x{:01$x}", new_value, format_length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unimplemented!("radix not supported: {}", self.radix),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add separators from original number.
|
||||||
|
for &rtl_index in &separator_rtl_indexes {
|
||||||
|
if rtl_index < new_text.len() {
|
||||||
|
let new_index = new_text.len() - rtl_index;
|
||||||
|
new_text.insert(new_index, '_');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add in additional separators if necessary.
|
||||||
|
if new_text.len() > old_length && !separator_rtl_indexes.is_empty() {
|
||||||
|
let spacing = match separator_rtl_indexes.as_slice() {
|
||||||
|
[.., b, a] => a - b - 1,
|
||||||
|
_ => separator_rtl_indexes[0],
|
||||||
|
};
|
||||||
|
|
||||||
|
let prefix_length = if self.radix == 10 { 0 } else { 2 };
|
||||||
|
if let Some(mut index) = new_text.find('_') {
|
||||||
|
while index - prefix_length > spacing {
|
||||||
|
index -= spacing;
|
||||||
|
new_text.insert(index, '_');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new_text.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::Rope;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decimal_at_point() {
|
||||||
|
let rope = Rope::from_str("Test text 12345 more text.");
|
||||||
|
let range = Range::point(12);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range),
|
||||||
|
Some(NumberIncrementor {
|
||||||
|
range: Range::new(10, 15),
|
||||||
|
value: 12345,
|
||||||
|
radix: 10,
|
||||||
|
text: rope.slice(..),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_uppercase_hexadecimal_at_point() {
|
||||||
|
let rope = Rope::from_str("Test text 0x123ABCDEF more text.");
|
||||||
|
let range = Range::point(12);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range),
|
||||||
|
Some(NumberIncrementor {
|
||||||
|
range: Range::new(10, 21),
|
||||||
|
value: 0x123ABCDEF,
|
||||||
|
radix: 16,
|
||||||
|
text: rope.slice(..),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lowercase_hexadecimal_at_point() {
|
||||||
|
let rope = Rope::from_str("Test text 0xfa3b4e more text.");
|
||||||
|
let range = Range::point(12);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range),
|
||||||
|
Some(NumberIncrementor {
|
||||||
|
range: Range::new(10, 18),
|
||||||
|
value: 0xfa3b4e,
|
||||||
|
radix: 16,
|
||||||
|
text: rope.slice(..),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_octal_at_point() {
|
||||||
|
let rope = Rope::from_str("Test text 0o1074312 more text.");
|
||||||
|
let range = Range::point(12);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range),
|
||||||
|
Some(NumberIncrementor {
|
||||||
|
range: Range::new(10, 19),
|
||||||
|
value: 0o1074312,
|
||||||
|
radix: 8,
|
||||||
|
text: rope.slice(..),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_binary_at_point() {
|
||||||
|
let rope = Rope::from_str("Test text 0b10111010010101 more text.");
|
||||||
|
let range = Range::point(12);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range),
|
||||||
|
Some(NumberIncrementor {
|
||||||
|
range: Range::new(10, 26),
|
||||||
|
value: 0b10111010010101,
|
||||||
|
radix: 2,
|
||||||
|
text: rope.slice(..),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_negative_decimal_at_point() {
|
||||||
|
let rope = Rope::from_str("Test text -54321 more text.");
|
||||||
|
let range = Range::point(12);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range),
|
||||||
|
Some(NumberIncrementor {
|
||||||
|
range: Range::new(10, 16),
|
||||||
|
value: -54321,
|
||||||
|
radix: 10,
|
||||||
|
text: rope.slice(..),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decimal_with_leading_zeroes_at_point() {
|
||||||
|
let rope = Rope::from_str("Test text 000045326 more text.");
|
||||||
|
let range = Range::point(12);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range),
|
||||||
|
Some(NumberIncrementor {
|
||||||
|
range: Range::new(10, 19),
|
||||||
|
value: 45326,
|
||||||
|
radix: 10,
|
||||||
|
text: rope.slice(..),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_negative_decimal_cursor_on_minus_sign() {
|
||||||
|
let rope = Rope::from_str("Test text -54321 more text.");
|
||||||
|
let range = Range::point(10);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range),
|
||||||
|
Some(NumberIncrementor {
|
||||||
|
range: Range::new(10, 16),
|
||||||
|
value: -54321,
|
||||||
|
radix: 10,
|
||||||
|
text: rope.slice(..),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_number_under_range_start_of_rope() {
|
||||||
|
let rope = Rope::from_str("100");
|
||||||
|
let range = Range::point(0);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range),
|
||||||
|
Some(NumberIncrementor {
|
||||||
|
range: Range::new(0, 3),
|
||||||
|
value: 100,
|
||||||
|
radix: 10,
|
||||||
|
text: rope.slice(..),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_number_under_range_end_of_rope() {
|
||||||
|
let rope = Rope::from_str("100");
|
||||||
|
let range = Range::point(2);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range),
|
||||||
|
Some(NumberIncrementor {
|
||||||
|
range: Range::new(0, 3),
|
||||||
|
value: 100,
|
||||||
|
radix: 10,
|
||||||
|
text: rope.slice(..),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_number_surrounded_by_punctuation() {
|
||||||
|
let rope = Rope::from_str(",100;");
|
||||||
|
let range = Range::point(1);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range),
|
||||||
|
Some(NumberIncrementor {
|
||||||
|
range: Range::new(1, 4),
|
||||||
|
value: 100,
|
||||||
|
radix: 10,
|
||||||
|
text: rope.slice(..),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_not_a_number_point() {
|
||||||
|
let rope = Rope::from_str("Test text 45326 more text.");
|
||||||
|
let range = Range::point(6);
|
||||||
|
assert_eq!(NumberIncrementor::from_range(rope.slice(..), range), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_number_too_large_at_point() {
|
||||||
|
let rope = Rope::from_str("Test text 0xFFFFFFFFFFFFFFFFF more text.");
|
||||||
|
let range = Range::point(12);
|
||||||
|
assert_eq!(NumberIncrementor::from_range(rope.slice(..), range), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_number_cursor_one_right_of_number() {
|
||||||
|
let rope = Rope::from_str("100 ");
|
||||||
|
let range = Range::point(3);
|
||||||
|
assert_eq!(NumberIncrementor::from_range(rope.slice(..), range), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_number_cursor_one_left_of_number() {
|
||||||
|
let rope = Rope::from_str(" 100");
|
||||||
|
let range = Range::point(0);
|
||||||
|
assert_eq!(NumberIncrementor::from_range(rope.slice(..), range), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_increment_basic_decimal_numbers() {
|
||||||
|
let tests = [
|
||||||
|
("100", 1, "101"),
|
||||||
|
("100", -1, "99"),
|
||||||
|
("99", 1, "100"),
|
||||||
|
("100", 1000, "1100"),
|
||||||
|
("100", -1000, "-900"),
|
||||||
|
("-1", 1, "0"),
|
||||||
|
("-1", 2, "1"),
|
||||||
|
("1", -1, "0"),
|
||||||
|
("1", -2, "-1"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (original, amount, expected) in tests {
|
||||||
|
let rope = Rope::from_str(original);
|
||||||
|
let range = Range::point(0);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range)
|
||||||
|
.unwrap()
|
||||||
|
.incremented_text(amount),
|
||||||
|
expected.into()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_increment_basic_hexadedimal_numbers() {
|
||||||
|
let tests = [
|
||||||
|
("0x0100", 1, "0x0101"),
|
||||||
|
("0x0100", -1, "0x00ff"),
|
||||||
|
("0x0001", -1, "0x0000"),
|
||||||
|
("0x0000", -1, "0xffffffffffffffff"),
|
||||||
|
("0xffffffffffffffff", 1, "0x0000000000000000"),
|
||||||
|
("0xffffffffffffffff", 2, "0x0000000000000001"),
|
||||||
|
("0xffffffffffffffff", -1, "0xfffffffffffffffe"),
|
||||||
|
("0xABCDEF1234567890", 1, "0xABCDEF1234567891"),
|
||||||
|
("0xabcdef1234567890", 1, "0xabcdef1234567891"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (original, amount, expected) in tests {
|
||||||
|
let rope = Rope::from_str(original);
|
||||||
|
let range = Range::point(0);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range)
|
||||||
|
.unwrap()
|
||||||
|
.incremented_text(amount),
|
||||||
|
expected.into()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_increment_basic_octal_numbers() {
|
||||||
|
let tests = [
|
||||||
|
("0o0107", 1, "0o0110"),
|
||||||
|
("0o0110", -1, "0o0107"),
|
||||||
|
("0o0001", -1, "0o0000"),
|
||||||
|
("0o7777", 1, "0o10000"),
|
||||||
|
("0o1000", -1, "0o0777"),
|
||||||
|
("0o0107", 10, "0o0121"),
|
||||||
|
("0o0000", -1, "0o1777777777777777777777"),
|
||||||
|
("0o1777777777777777777777", 1, "0o0000000000000000000000"),
|
||||||
|
("0o1777777777777777777777", 2, "0o0000000000000000000001"),
|
||||||
|
("0o1777777777777777777777", -1, "0o1777777777777777777776"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (original, amount, expected) in tests {
|
||||||
|
let rope = Rope::from_str(original);
|
||||||
|
let range = Range::point(0);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range)
|
||||||
|
.unwrap()
|
||||||
|
.incremented_text(amount),
|
||||||
|
expected.into()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_increment_basic_binary_numbers() {
|
||||||
|
let tests = [
|
||||||
|
("0b00000100", 1, "0b00000101"),
|
||||||
|
("0b00000100", -1, "0b00000011"),
|
||||||
|
("0b00000100", 2, "0b00000110"),
|
||||||
|
("0b00000100", -2, "0b00000010"),
|
||||||
|
("0b00000001", -1, "0b00000000"),
|
||||||
|
("0b00111111", 10, "0b01001001"),
|
||||||
|
("0b11111111", 1, "0b100000000"),
|
||||||
|
("0b10000000", -1, "0b01111111"),
|
||||||
|
(
|
||||||
|
"0b0000",
|
||||||
|
-1,
|
||||||
|
"0b1111111111111111111111111111111111111111111111111111111111111111",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"0b1111111111111111111111111111111111111111111111111111111111111111",
|
||||||
|
1,
|
||||||
|
"0b0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"0b1111111111111111111111111111111111111111111111111111111111111111",
|
||||||
|
2,
|
||||||
|
"0b0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"0b1111111111111111111111111111111111111111111111111111111111111111",
|
||||||
|
-1,
|
||||||
|
"0b1111111111111111111111111111111111111111111111111111111111111110",
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (original, amount, expected) in tests {
|
||||||
|
let rope = Rope::from_str(original);
|
||||||
|
let range = Range::point(0);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range)
|
||||||
|
.unwrap()
|
||||||
|
.incremented_text(amount),
|
||||||
|
expected.into()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_increment_with_separators() {
|
||||||
|
let tests = [
|
||||||
|
("999_999", 1, "1_000_000"),
|
||||||
|
("1_000_000", -1, "999_999"),
|
||||||
|
("-999_999", -1, "-1_000_000"),
|
||||||
|
("0x0000_0000_0001", 0x1_ffff_0000, "0x0001_ffff_0001"),
|
||||||
|
("0x0000_0000_0001", 0x1_ffff_0000, "0x0001_ffff_0001"),
|
||||||
|
("0x0000_0000_0001", 0x1_ffff_0000, "0x0001_ffff_0001"),
|
||||||
|
("0x0000_0000", -1, "0xffff_ffff_ffff_ffff"),
|
||||||
|
("0x0000_0000_0000", -1, "0xffff_ffff_ffff_ffff"),
|
||||||
|
("0b01111111_11111111", 1, "0b10000000_00000000"),
|
||||||
|
("0b11111111_11111111", 1, "0b1_00000000_00000000"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (original, amount, expected) in tests {
|
||||||
|
let rope = Rope::from_str(original);
|
||||||
|
let range = Range::point(0);
|
||||||
|
assert_eq!(
|
||||||
|
NumberIncrementor::from_range(rope.slice(..), range)
|
||||||
|
.unwrap()
|
||||||
|
.incremented_text(amount),
|
||||||
|
expected.into()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 88408ffc5e27abcffced7010fc77396ae3636d7e
|
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 0ac2c6da562c7a2c26ed7e8691d4a590f7e8b90a
|
@ -0,0 +1,12 @@
|
|||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let git_hash = Command::new("git")
|
||||||
|
.args(&["describe", "--dirty"])
|
||||||
|
.output()
|
||||||
|
.map(|x| String::from_utf8(x.stdout).ok())
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.unwrap_or_else(|| String::from(env!("CARGO_PKG_VERSION")));
|
||||||
|
println!("cargo:rustc-env=VERSION_AND_GIT_HASH={}", git_hash);
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1 @@
|
|||||||
|
; inherits: c
|
@ -0,0 +1,37 @@
|
|||||||
|
; inherits: c
|
||||||
|
|
||||||
|
[
|
||||||
|
"in"
|
||||||
|
"out"
|
||||||
|
"inout"
|
||||||
|
"uniform"
|
||||||
|
"shared"
|
||||||
|
"layout"
|
||||||
|
"attribute"
|
||||||
|
"varying"
|
||||||
|
"buffer"
|
||||||
|
"coherent"
|
||||||
|
"readonly"
|
||||||
|
"writeonly"
|
||||||
|
"precision"
|
||||||
|
"highp"
|
||||||
|
"mediump"
|
||||||
|
"lowp"
|
||||||
|
"centroid"
|
||||||
|
"sample"
|
||||||
|
"patch"
|
||||||
|
"smooth"
|
||||||
|
"flat"
|
||||||
|
"noperspective"
|
||||||
|
"invariant"
|
||||||
|
"precise"
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
"subroutine" @keyword.function
|
||||||
|
|
||||||
|
(extension_storage_class) @attribute
|
||||||
|
|
||||||
|
(
|
||||||
|
(identifier) @variable.builtin
|
||||||
|
(#match? @variable.builtin "^gl_")
|
||||||
|
)
|
@ -0,0 +1,19 @@
|
|||||||
|
indent = [
|
||||||
|
"init_declarator",
|
||||||
|
"compound_statement",
|
||||||
|
"preproc_arg",
|
||||||
|
"field_declaration_list",
|
||||||
|
"case_statement",
|
||||||
|
"conditional_expression",
|
||||||
|
"enumerator_list",
|
||||||
|
"struct_specifier",
|
||||||
|
"compound_literal_expression"
|
||||||
|
]
|
||||||
|
|
||||||
|
outdent = [
|
||||||
|
"#define",
|
||||||
|
"#ifdef",
|
||||||
|
"#endif",
|
||||||
|
"{",
|
||||||
|
"}"
|
||||||
|
]
|
@ -0,0 +1,3 @@
|
|||||||
|
(preproc_arg) @glsl
|
||||||
|
|
||||||
|
(comment) @comment
|
@ -0,0 +1 @@
|
|||||||
|
; inherits: c
|
@ -0,0 +1,9 @@
|
|||||||
|
indent = [
|
||||||
|
"object",
|
||||||
|
"array"
|
||||||
|
]
|
||||||
|
|
||||||
|
outdent = [
|
||||||
|
"]",
|
||||||
|
"}"
|
||||||
|
]
|
@ -0,0 +1,181 @@
|
|||||||
|
; Variables
|
||||||
|
(variable_declaration
|
||||||
|
.
|
||||||
|
(scope) @keyword)
|
||||||
|
[
|
||||||
|
(single_var_declaration)
|
||||||
|
(scalar_variable)
|
||||||
|
(array_variable)
|
||||||
|
(hash_variable)
|
||||||
|
(hash_variable)
|
||||||
|
] @variable
|
||||||
|
|
||||||
|
|
||||||
|
[
|
||||||
|
(package_name)
|
||||||
|
(special_scalar_variable)
|
||||||
|
(special_array_variable)
|
||||||
|
(special_hash_variable)
|
||||||
|
(special_literal)
|
||||||
|
(super)
|
||||||
|
] @constant
|
||||||
|
|
||||||
|
(
|
||||||
|
[
|
||||||
|
(package_name)
|
||||||
|
(super)
|
||||||
|
]
|
||||||
|
.
|
||||||
|
("::" @operator)
|
||||||
|
)
|
||||||
|
|
||||||
|
(comments) @comment
|
||||||
|
(pod_statement) @comment.block.documentation
|
||||||
|
|
||||||
|
[
|
||||||
|
(use_no_statement)
|
||||||
|
(use_no_feature_statement)
|
||||||
|
(use_no_if_statement)
|
||||||
|
(use_no_version)
|
||||||
|
(use_constant_statement)
|
||||||
|
(use_parent_statement)
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
(use_constant_statement
|
||||||
|
constant: (identifier) @constant)
|
||||||
|
|
||||||
|
[
|
||||||
|
"require"
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
(method_invocation
|
||||||
|
.
|
||||||
|
(identifier) @variable)
|
||||||
|
|
||||||
|
(method_invocation
|
||||||
|
(arrow_operator)
|
||||||
|
.
|
||||||
|
(identifier) @function)
|
||||||
|
(method_invocation
|
||||||
|
function_name: (identifier) @function)
|
||||||
|
(named_block_statement
|
||||||
|
function_name: (identifier) @function)
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
function_name: (identifier) @function)
|
||||||
|
(function_definition
|
||||||
|
name: (identifier) @function)
|
||||||
|
[
|
||||||
|
(function)
|
||||||
|
(map)
|
||||||
|
(grep)
|
||||||
|
(bless)
|
||||||
|
] @function
|
||||||
|
|
||||||
|
[
|
||||||
|
"return"
|
||||||
|
"sub"
|
||||||
|
"package"
|
||||||
|
"BEGIN"
|
||||||
|
"END"
|
||||||
|
] @keyword.function
|
||||||
|
|
||||||
|
[
|
||||||
|
"("
|
||||||
|
")"
|
||||||
|
"["
|
||||||
|
"]"
|
||||||
|
"{"
|
||||||
|
"}"
|
||||||
|
] @punctuation.bracket
|
||||||
|
(standard_input_to_variable) @punctuation.bracket
|
||||||
|
|
||||||
|
[
|
||||||
|
"=~"
|
||||||
|
"or"
|
||||||
|
"="
|
||||||
|
"=="
|
||||||
|
"+"
|
||||||
|
"-"
|
||||||
|
"."
|
||||||
|
"//"
|
||||||
|
"||"
|
||||||
|
(arrow_operator)
|
||||||
|
(hash_arrow_operator)
|
||||||
|
(array_dereference)
|
||||||
|
(hash_dereference)
|
||||||
|
(to_reference)
|
||||||
|
(type_glob)
|
||||||
|
(hash_access_variable)
|
||||||
|
(ternary_expression)
|
||||||
|
(ternary_expression_in_hash)
|
||||||
|
] @operator
|
||||||
|
|
||||||
|
[
|
||||||
|
(regex_option)
|
||||||
|
(regex_option_for_substitution)
|
||||||
|
(regex_option_for_transliteration)
|
||||||
|
] @variable.parameter
|
||||||
|
|
||||||
|
(type_glob
|
||||||
|
(identifier) @variable)
|
||||||
|
(
|
||||||
|
(scalar_variable)
|
||||||
|
.
|
||||||
|
("->" @operator))
|
||||||
|
|
||||||
|
[
|
||||||
|
(word_list_qw)
|
||||||
|
(command_qx_quoted)
|
||||||
|
(string_single_quoted)
|
||||||
|
(string_double_quoted)
|
||||||
|
(string_qq_quoted)
|
||||||
|
(bareword)
|
||||||
|
(transliteration_tr_or_y)
|
||||||
|
] @string
|
||||||
|
|
||||||
|
[
|
||||||
|
(regex_pattern_qr)
|
||||||
|
(patter_matcher_m)
|
||||||
|
(substitution_pattern_s)
|
||||||
|
] @string.regexp
|
||||||
|
|
||||||
|
(escape_sequence) @string.special
|
||||||
|
|
||||||
|
[
|
||||||
|
","
|
||||||
|
(semi_colon)
|
||||||
|
(start_delimiter)
|
||||||
|
(end_delimiter)
|
||||||
|
(ellipsis_statement)
|
||||||
|
] @punctuation.delimiter
|
||||||
|
|
||||||
|
[
|
||||||
|
(integer)
|
||||||
|
(floating_point)
|
||||||
|
(scientific_notation)
|
||||||
|
(hexadecimal)
|
||||||
|
] @constant.numeric
|
||||||
|
|
||||||
|
[
|
||||||
|
; (if_statement)
|
||||||
|
(unless_statement)
|
||||||
|
(if_simple_statement)
|
||||||
|
(unless_simple_statement)
|
||||||
|
] @keyword.control.conditional
|
||||||
|
|
||||||
|
[
|
||||||
|
"if"
|
||||||
|
"elsif"
|
||||||
|
"else"
|
||||||
|
] @keyword.control.conditional
|
||||||
|
|
||||||
|
(foreach_statement) @keyword.control.repeat
|
||||||
|
(foreach_statement
|
||||||
|
.
|
||||||
|
(scope) @keyword)
|
||||||
|
|
||||||
|
(function_attribute) @label
|
||||||
|
|
||||||
|
(function_signature) @type
|
||||||
|
|
@ -0,0 +1,8 @@
|
|||||||
|
(function_definition
|
||||||
|
(identifier) (_) @function.inside) @function.around
|
||||||
|
|
||||||
|
(anonymous_function
|
||||||
|
(_) @function.inside) @function.around
|
||||||
|
|
||||||
|
(argument
|
||||||
|
(_) @parameter.inside)
|
@ -0,0 +1,65 @@
|
|||||||
|
# Author : Koen Van der Auwera <atog@hey.com>
|
||||||
|
# Based on SpaceBones Light https://github.com/chipotle/spacebones
|
||||||
|
# https://github.com/chipotle/spacebones/blob/main/SpaceBones%20Light.bbColorScheme
|
||||||
|
|
||||||
|
"attribute" = "#b1951d"
|
||||||
|
"keyword" = { fg = "#3a81c3" }
|
||||||
|
"keyword.directive" = "#3a81c3"
|
||||||
|
"namespace" = "#b1951d"
|
||||||
|
"punctuation" = "#6c3163"
|
||||||
|
"punctuation.delimiter" = "#6c3163"
|
||||||
|
"operator" = "#ba2f59"
|
||||||
|
"special" = "#ba2f59"
|
||||||
|
"property" = "#7590db"
|
||||||
|
"variable.property" = "#7590db"
|
||||||
|
"variable" = "#715ab1"
|
||||||
|
"variable.builtin" = "#715ab1"
|
||||||
|
"variable.parameter" = "#7590db"
|
||||||
|
"type" = "#6c3163"
|
||||||
|
"type.builtin" = "#6c3163"
|
||||||
|
"constructor" = { fg = "#4e3163", modifiers = ["bold"] }
|
||||||
|
"function" = { fg = "#715ab1", modifiers = ["bold"] }
|
||||||
|
"function.macro" = "#b1951d"
|
||||||
|
"function.builtin" = "#b1951d"
|
||||||
|
"comment" = { fg = "#a49da5", modifiers = ["italic"] }
|
||||||
|
"constant" = { fg = "#6c3163" }
|
||||||
|
"constant.builtin" = { fg = "#6c3163", modifiers = ["bold"] }
|
||||||
|
"string" = "#2d9574"
|
||||||
|
"number" = "#6c3163"
|
||||||
|
"escape" = { fg = "fg2", modifiers = ["bold"] }
|
||||||
|
"label" = "#b1951d"
|
||||||
|
"module" = "#b1951d"
|
||||||
|
|
||||||
|
"warning" = { fg = "#da8b55" }
|
||||||
|
"error" = { fg = "#e0211d" }
|
||||||
|
"info" = { fg = "#b1951d" }
|
||||||
|
"hint" = { fg = "#d1dcdf" }
|
||||||
|
|
||||||
|
"ui.background" = { bg = "bg0" }
|
||||||
|
"ui.linenr" = { fg = "bg3" }
|
||||||
|
"ui.linenr.selected" = { fg = "#b1951d" }
|
||||||
|
"ui.statusline" = { fg = "fg1", bg = "bg2" }
|
||||||
|
"ui.statusline.inactive" = { fg = "fg4", bg = "bg1" }
|
||||||
|
"ui.popup" = { bg = "bg1" }
|
||||||
|
"ui.window" = { bg = "bg1" }
|
||||||
|
"ui.help" = { bg = "bg1", fg = "fg1" }
|
||||||
|
"ui.text" = { fg = "fg1" }
|
||||||
|
"ui.text.focus" = { fg = "fg1" }
|
||||||
|
"ui.selection" = { bg = "bg3", modifiers = ["reversed"] }
|
||||||
|
"ui.cursor.primary" = { modifiers = ["reversed"] }
|
||||||
|
"ui.cursor.match" = { modifiers = ["reversed"] }
|
||||||
|
"ui.menu" = { fg = "fg1", bg = "bg2" }
|
||||||
|
"ui.menu.selected" = { fg = "#655370", bg = "#d1dcdf", modifiers = ["bold"] }
|
||||||
|
|
||||||
|
"diagnostic" = { modifiers = ["underlined"] }
|
||||||
|
|
||||||
|
[palette]
|
||||||
|
bg0 = "#fbf8ef"
|
||||||
|
bg1 = "#efeae9"
|
||||||
|
bg2 = "#d1dcdf"
|
||||||
|
bg3 = "#b4c6cb"
|
||||||
|
|
||||||
|
fg1 = "#655370"
|
||||||
|
fg2 = "#5f3bc4"
|
||||||
|
fg3 = "#bdae93"
|
||||||
|
fg4 = "#a89984"
|
Loading…
Reference in New Issue