Skip to content

Commit

Permalink
Merge branch 'helix-editor:master' into shebang-filetype
Browse files Browse the repository at this point in the history
  • Loading branch information
ath3 authored Nov 8, 2021
2 parents 91b922c + 3074464 commit e057337
Show file tree
Hide file tree
Showing 15 changed files with 271 additions and 53 deletions.
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,7 @@
path = helix-syntax/languages/tree-sitter-cmake
url = https://github.com/uyha/tree-sitter-cmake
shallow = true
[submodule "helix-syntax/languages/tree-sitter-perl"]
path = helix-syntax/languages/tree-sitter-perl
url = https://github.com/ganezdragon/tree-sitter-perl
shallow = true
23 changes: 15 additions & 8 deletions book/src/guides/adding_languages.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

## Submodules

To add a new langauge, you should first add a tree-sitter submodule. To do this, you can run the command
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>
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
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
Expand All @@ -19,15 +20,18 @@ or you can manually add `shallow = true` to `.gitmodules`.

## languages.toml

Next, you need to add the language to the `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).
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 site. [link](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-injection) |
| 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` |
Expand All @@ -43,11 +47,14 @@ For a language to have syntax-highlighting and indentation among other things, y
## 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
```
```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
2 changes: 1 addition & 1 deletion book/src/guides/textobject.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ require an accompanying tree-sitter grammar and a `textobjects.scm` query file
to work properly. Tree-sitter allows us to query the source code syntax tree
and capture specific parts of it. The queries are written in a lisp dialect.
More information on how to write queries can be found in the [official tree-sitter
documentation](tree-sitter-queries).
documentation][tree-sitter-queries].

Query files should be placed in `runtime/queries/{language}/textobjects.scm`
when contributing. Note that to test the query files locally you should put
Expand Down
1 change: 1 addition & 0 deletions book/src/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Currently supported: `word`, `surround`, `function`, `class`, `parameter`.
| Key after `mi` or `ma` | Textobject selected |
| --- | --- |
| `w` | Word |
| `W` | WORD |
| `(`, `[`, `'`, etc | Specified surround pairs |
| `f` | Function |
| `c` | Class |
Expand Down
2 changes: 1 addition & 1 deletion helix-core/src/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub fn toggle_line_comments(doc: &Rope, selection: &Selection, token: Option<&st
let token = token.unwrap_or("//");
let comment = Tendril::from(format!("{} ", token));

let mut lines: Vec<usize> = Vec::new();
let mut lines: Vec<usize> = Vec::with_capacity(selection.len());

let mut min_next_line = 0;
for selection in selection {
Expand Down
55 changes: 21 additions & 34 deletions helix-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,25 +103,7 @@ pub fn cache_dir() -> std::path::PathBuf {
pub fn merge_toml_values(left: toml::Value, right: toml::Value) -> toml::Value {
use toml::Value;

fn get_name(v: &Value) -> Option<&str> {
v.get("name").and_then(Value::as_str)
}

match (left, right) {
(Value::Array(mut left_items), Value::Array(right_items)) => {
left_items.reserve(right_items.len());
for rvalue in right_items {
let lvalue = get_name(&rvalue)
.and_then(|rname| left_items.iter().position(|v| get_name(v) == Some(rname)))
.map(|lpos| left_items.remove(lpos));
let mvalue = match lvalue {
Some(lvalue) => merge_toml_values(lvalue, rvalue),
None => rvalue,
};
left_items.push(mvalue);
}
Value::Array(left_items)
}
(Value::Table(mut left_map), Value::Table(right_map)) => {
for (rname, rvalue) in right_map {
match left_map.remove(&rname) {
Expand Down Expand Up @@ -149,36 +131,41 @@ mod merge_toml_tests {
fn language_tomls() {
use toml::Value;

const USER: &str = "
const USER: &str = r#"
[[language]]
name = \"nix\"
test = \"bbb\"
indent = { tab-width = 4, unit = \" \", test = \"aaa\" }
";
name = "typescript"
test = "bbb"
indent = { tab-width = 4, unit = " ", test = "aaa" }
language-server = { command = "deno", args = ["lsp"] }
"#;

let base: Value = toml::from_slice(include_bytes!("../../languages.toml"))
.expect("Couldn't parse built-in langauges config");
let user: Value = toml::from_str(USER).unwrap();

let merged = merge_toml_values(base, user);
let languages = merged.get("language").unwrap().as_array().unwrap();
let nix = languages
let ts = languages
.iter()
.find(|v| v.get("name").unwrap().as_str().unwrap() == "nix")
.find(|v| v.get("name").unwrap().as_str().unwrap() == "typescript")
.unwrap();
let nix_indent = nix.get("indent").unwrap();
let ts_indent = ts.get("indent").unwrap();

// We changed tab-width and unit in indent so check them if they are the new values
assert_eq!(ts_indent.get("tab-width").unwrap().as_integer().unwrap(), 4);
assert_eq!(ts_indent.get("unit").unwrap().as_str().unwrap(), " ");
// We added a new keys, so check them
assert_eq!(ts.get("test").unwrap().as_str().unwrap(), "bbb");
assert_eq!(ts_indent.get("test").unwrap().as_str().unwrap(), "aaa");
assert_eq!(
nix_indent.get("tab-width").unwrap().as_integer().unwrap(),
4
ts.get("language-server")
.unwrap()
.get("args")
.unwrap()
.as_array()
.unwrap(),
&vec![Value::String("lsp".into())],
);
assert_eq!(nix_indent.get("unit").unwrap().as_str().unwrap(), " ");
// We added a new keys, so check them
assert_eq!(nix.get("test").unwrap().as_str().unwrap(), "bbb");
assert_eq!(nix_indent.get("test").unwrap().as_str().unwrap(), "aaa");
// We didn't change comment-token so it should be same
assert_eq!(nix.get("comment-token").unwrap().as_str().unwrap(), "#");
}
}

Expand Down
9 changes: 8 additions & 1 deletion helix-core/src/match_brackets.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
use crate::{Rope, Syntax};

const PAIRS: &[(char, char)] = &[('(', ')'), ('{', '}'), ('[', ']'), ('<', '>')];
const PAIRS: &[(char, char)] = &[
('(', ')'),
('{', '}'),
('[', ']'),
('<', '>'),
('\'', '\''),
('"', '"'),
];
// limit matching pairs to only ( ) { } [ ] < >

#[must_use]
Expand Down
11 changes: 6 additions & 5 deletions helix-core/src/textobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::surround;
use crate::syntax::LanguageConfiguration;
use crate::Range;

fn find_word_boundary(slice: RopeSlice, mut pos: usize, direction: Direction) -> usize {
fn find_word_boundary(slice: RopeSlice, mut pos: usize, direction: Direction, long: bool) -> usize {
use CharCategory::{Eol, Whitespace};

let iter = match direction {
Expand All @@ -33,7 +33,7 @@ fn find_word_boundary(slice: RopeSlice, mut pos: usize, direction: Direction) ->
match categorize_char(ch) {
Eol | Whitespace => return pos,
category => {
if category != prev_category && pos != 0 && pos != slice.len_chars() {
if !long && category != prev_category && pos != 0 && pos != slice.len_chars() {
return pos;
} else {
match direction {
Expand Down Expand Up @@ -70,13 +70,14 @@ pub fn textobject_word(
range: Range,
textobject: TextObject,
_count: usize,
long: bool,
) -> Range {
let pos = range.cursor(slice);

let word_start = find_word_boundary(slice, pos, Direction::Backward);
let word_start = find_word_boundary(slice, pos, Direction::Backward, long);
let word_end = match slice.get_char(pos).map(categorize_char) {
None | Some(CharCategory::Whitespace | CharCategory::Eol) => pos,
_ => find_word_boundary(slice, pos + 1, Direction::Forward),
_ => find_word_boundary(slice, pos + 1, Direction::Forward, long),
};

// Special case.
Expand Down Expand Up @@ -268,7 +269,7 @@ mod test {
let slice = doc.slice(..);
for &case in scenario {
let (pos, objtype, expected_range) = case;
let result = textobject_word(slice, Range::point(pos), objtype, 1);
let result = textobject_word(slice, Range::point(pos), objtype, 1, false);
assert_eq!(
result,
expected_range.into(),
Expand Down
1 change: 1 addition & 0 deletions helix-syntax/languages/tree-sitter-perl
Submodule tree-sitter-perl added at 0ac2c6
3 changes: 2 additions & 1 deletion helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4672,7 +4672,8 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) {

let selection = doc.selection(view.id).clone().transform(|range| {
match ch {
'w' => textobject::textobject_word(text, range, objtype, count),
'w' => textobject::textobject_word(text, range, objtype, count, false),
'W' => textobject::textobject_word(text, range, objtype, count, true),
'c' => textobject_treesitter("class", range),
'f' => textobject_treesitter("function", range),
'p' => textobject_treesitter("parameter", range),
Expand Down
1 change: 1 addition & 0 deletions helix-term/src/ui/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ impl Component for Prompt {
doc.selection(view.id).primary(),
textobject::TextObject::Inside,
1,
false,
);
let line = text.slice(range.from()..range.to()).to_string();
if !line.is_empty() {
Expand Down
19 changes: 19 additions & 0 deletions languages.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ comment-token = "#"
language-server = { command = "elixir-ls" }
indent = { tab-width = 2, unit = " " }

[[language]]
name = "mint"
scope = "source.mint"
injection-regex = "mint"
file-types = ["mint"]
roots = []
comment-token = "//"

language-server = { command = "mint", args = ["ls"] }
indent = { tab-width = 2, unit = " " }

[[language]]
name = "json"
scope = "source.json"
Expand Down Expand Up @@ -388,3 +399,11 @@ roots = []
comment-token = "#"
indent = { tab-width = 2, unit = " " }
language-server = { command = "cmake-language-server" }

[[language]]
name = "perl"
scope = "source.perl"
file-types = ["pl", "pm"]
roots = []
comment-token = "#"
indent = { tab-width = 2, unit = " " }
Loading

0 comments on commit e057337

Please sign in to comment.