mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Add basic support for ruby
Co-authored-by: Kay Simmons <kay@zed.dev>
This commit is contained in:
parent
9f3ea0c87f
commit
d222904471
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -3009,6 +3009,7 @@ dependencies = [
|
||||
"tree-sitter-javascript",
|
||||
"tree-sitter-json 0.19.0",
|
||||
"tree-sitter-python",
|
||||
"tree-sitter-ruby",
|
||||
"tree-sitter-rust",
|
||||
"tree-sitter-typescript",
|
||||
"unindent",
|
||||
@ -6491,6 +6492,16 @@ dependencies = [
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-ruby"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ac30cbb1560363ae76e1ccde543d6d99087421e228cc47afcec004b86bb711a"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-rust"
|
||||
version = "0.20.3"
|
||||
@ -7712,6 +7723,7 @@ dependencies = [
|
||||
"tree-sitter-json 0.20.0",
|
||||
"tree-sitter-markdown",
|
||||
"tree-sitter-python",
|
||||
"tree-sitter-ruby",
|
||||
"tree-sitter-rust",
|
||||
"tree-sitter-toml",
|
||||
"tree-sitter-typescript",
|
||||
|
@ -71,4 +71,5 @@ tree-sitter-json = "*"
|
||||
tree-sitter-rust = "*"
|
||||
tree-sitter-python = "*"
|
||||
tree-sitter-typescript = "*"
|
||||
tree-sitter-ruby = "*"
|
||||
unindent = "0.1.7"
|
||||
|
@ -1764,6 +1764,7 @@ impl BufferSnapshot {
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut indent_ranges = Vec::<Range<Point>>::new();
|
||||
let mut outdent_positions = Vec::<Point>::new();
|
||||
while let Some(mat) = matches.peek() {
|
||||
let mut start: Option<Point> = None;
|
||||
let mut end: Option<Point> = None;
|
||||
@ -1777,6 +1778,8 @@ impl BufferSnapshot {
|
||||
start = Some(Point::from_ts_point(capture.node.end_position()));
|
||||
} else if Some(capture.index) == config.end_capture_ix {
|
||||
end = Some(Point::from_ts_point(capture.node.start_position()));
|
||||
} else if Some(capture.index) == config.outdent_capture_ix {
|
||||
outdent_positions.push(Point::from_ts_point(capture.node.start_position()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1797,6 +1800,19 @@ impl BufferSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
outdent_positions.sort();
|
||||
for outdent_position in outdent_positions {
|
||||
// find the innermost indent range containing this outdent_position
|
||||
// set its end to the outdent position
|
||||
if let Some(range_to_truncate) = indent_ranges
|
||||
.iter_mut()
|
||||
.filter(|indent_range| indent_range.contains(&outdent_position))
|
||||
.last()
|
||||
{
|
||||
range_to_truncate.end = outdent_position;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the suggested indentation increases and decreased based on regexes.
|
||||
let mut indent_change_rows = Vec::<(u32, Ordering)>::new();
|
||||
self.for_each_line(
|
||||
|
@ -1150,6 +1150,49 @@ fn test_autoindent_with_injected_languages(cx: &mut MutableAppContext) {
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_autoindent_query_with_outdent_captures(cx: &mut MutableAppContext) {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.editor_defaults.tab_size = Some(2.try_into().unwrap());
|
||||
cx.set_global(settings);
|
||||
cx.add_model(|cx| {
|
||||
let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(ruby_lang()), cx);
|
||||
|
||||
let text = r#"
|
||||
class C
|
||||
def a(b, c)
|
||||
puts b
|
||||
puts c
|
||||
rescue
|
||||
puts "errored"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
"#
|
||||
.unindent();
|
||||
|
||||
buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
|
||||
|
||||
assert_eq!(
|
||||
buffer.text(),
|
||||
r#"
|
||||
class C
|
||||
def a(b, c)
|
||||
puts b
|
||||
puts c
|
||||
rescue
|
||||
puts "errored"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
"#
|
||||
.unindent()
|
||||
);
|
||||
|
||||
buffer
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_serialization(cx: &mut gpui::MutableAppContext) {
|
||||
let mut now = Instant::now();
|
||||
@ -1497,6 +1540,26 @@ impl Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
fn ruby_lang() -> Language {
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
name: "Ruby".into(),
|
||||
path_suffixes: vec!["rb".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_ruby::language()),
|
||||
)
|
||||
.with_indents_query(
|
||||
r#"
|
||||
(class "end" @end) @indent
|
||||
(method "end" @end) @indent
|
||||
(rescue) @outdent
|
||||
(then) @indent
|
||||
"#,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn rust_lang() -> Language {
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
|
@ -312,6 +312,7 @@ struct IndentConfig {
|
||||
indent_capture_ix: u32,
|
||||
start_capture_ix: Option<u32>,
|
||||
end_capture_ix: Option<u32>,
|
||||
outdent_capture_ix: Option<u32>,
|
||||
}
|
||||
|
||||
struct OutlineConfig {
|
||||
@ -670,12 +671,14 @@ impl Language {
|
||||
let mut indent_capture_ix = None;
|
||||
let mut start_capture_ix = None;
|
||||
let mut end_capture_ix = None;
|
||||
let mut outdent_capture_ix = None;
|
||||
get_capture_indices(
|
||||
&query,
|
||||
&mut [
|
||||
("indent", &mut indent_capture_ix),
|
||||
("start", &mut start_capture_ix),
|
||||
("end", &mut end_capture_ix),
|
||||
("outdent", &mut outdent_capture_ix),
|
||||
],
|
||||
);
|
||||
if let Some(indent_capture_ix) = indent_capture_ix {
|
||||
@ -684,6 +687,7 @@ impl Language {
|
||||
indent_capture_ix,
|
||||
start_capture_ix,
|
||||
end_capture_ix,
|
||||
outdent_capture_ix,
|
||||
});
|
||||
}
|
||||
Ok(self)
|
||||
|
@ -102,6 +102,7 @@ tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown",
|
||||
tree-sitter-python = "0.20.2"
|
||||
tree-sitter-toml = { git = "https://github.com/tree-sitter/tree-sitter-toml", rev = "342d9be207c2dba869b9967124c679b5e6fd0ebe" }
|
||||
tree-sitter-typescript = "0.20.1"
|
||||
tree-sitter-ruby = "0.20.0"
|
||||
tree-sitter-html = "0.19.0"
|
||||
url = "2.2"
|
||||
|
||||
|
@ -15,6 +15,15 @@ mod python;
|
||||
mod rust;
|
||||
mod typescript;
|
||||
|
||||
// 1. Add tree-sitter-{language} parser to zed crate
|
||||
// 2. Create a language directory in zed/crates/zed/src/languages and add the language to init function below
|
||||
// 3. Add config.toml to the newly created language directory using existing languages as a template
|
||||
// 4. Copy highlights from tree sitter repo for the language into a highlights.scm file.
|
||||
// Note: github highlights take the last match while zed takes the first
|
||||
// 5. Add indents.scm, outline.scm, and brackets.scm to implement indent on newline, outline/breadcrumbs,
|
||||
// and autoclosing brackets respectively
|
||||
// 6. If the language has injections add an injections.scm query file
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "src/languages"]
|
||||
#[exclude = "*.rs"]
|
||||
@ -107,6 +116,7 @@ pub async fn init(languages: Arc<LanguageRegistry>, _executor: Arc<Background>)
|
||||
tree_sitter_html::language(),
|
||||
Some(CachedLspAdapter::new(html::HtmlLspAdapter).await),
|
||||
),
|
||||
("ruby", tree_sitter_ruby::language(), None),
|
||||
] {
|
||||
languages.add(language(name, grammar, lsp_adapter));
|
||||
}
|
||||
|
14
crates/zed/src/languages/ruby/brackets.scm
Normal file
14
crates/zed/src/languages/ruby/brackets.scm
Normal file
@ -0,0 +1,14 @@
|
||||
("[" @open "]" @close)
|
||||
("{" @open "}" @close)
|
||||
("\"" @open "\"" @close)
|
||||
("do" @open "end" @close)
|
||||
|
||||
(block_parameters "|" @open "|" @close)
|
||||
(interpolation "#{" @open "}" @close)
|
||||
|
||||
(if "if" @open "end" @close)
|
||||
(unless "unless" @open "end" @close)
|
||||
(begin "begin" @open "end" @close)
|
||||
(module "module" @open "end" @close)
|
||||
(_ . "def" @open "end" @close)
|
||||
(_ . "class" @open "end" @close)
|
11
crates/zed/src/languages/ruby/config.toml
Normal file
11
crates/zed/src/languages/ruby/config.toml
Normal file
@ -0,0 +1,11 @@
|
||||
name = "Ruby"
|
||||
path_suffixes = ["rb", "Gemfile"]
|
||||
line_comment = "# "
|
||||
autoclose_before = ";:.,=}])>"
|
||||
brackets = [
|
||||
{ start = "{", end = "}", close = true, newline = true },
|
||||
{ start = "[", end = "]", close = true, newline = true },
|
||||
{ start = "(", end = ")", close = true, newline = true },
|
||||
{ start = "\"", end = "\"", close = true, newline = false },
|
||||
{ start = "'", end = "'", close = false, newline = false },
|
||||
]
|
181
crates/zed/src/languages/ruby/highlights.scm
Normal file
181
crates/zed/src/languages/ruby/highlights.scm
Normal file
@ -0,0 +1,181 @@
|
||||
; Keywords
|
||||
|
||||
[
|
||||
"alias"
|
||||
"and"
|
||||
"begin"
|
||||
"break"
|
||||
"case"
|
||||
"class"
|
||||
"def"
|
||||
"do"
|
||||
"else"
|
||||
"elsif"
|
||||
"end"
|
||||
"ensure"
|
||||
"for"
|
||||
"if"
|
||||
"in"
|
||||
"module"
|
||||
"next"
|
||||
"or"
|
||||
"rescue"
|
||||
"retry"
|
||||
"return"
|
||||
"then"
|
||||
"unless"
|
||||
"until"
|
||||
"when"
|
||||
"while"
|
||||
"yield"
|
||||
] @keyword
|
||||
|
||||
(identifier) @variable
|
||||
|
||||
((identifier) @keyword
|
||||
(#match? @keyword "^(private|protected|public)$"))
|
||||
|
||||
; Function calls
|
||||
|
||||
((identifier) @function.method.builtin
|
||||
(#eq? @function.method.builtin "require"))
|
||||
|
||||
"defined?" @function.method.builtin
|
||||
|
||||
(call
|
||||
method: [(identifier) (constant)] @function.method)
|
||||
|
||||
; Function definitions
|
||||
|
||||
(alias (identifier) @function.method)
|
||||
(setter (identifier) @function.method)
|
||||
(method name: [(identifier) (constant)] @function.method)
|
||||
(singleton_method name: [(identifier) (constant)] @function.method)
|
||||
|
||||
; Identifiers
|
||||
|
||||
[
|
||||
(class_variable)
|
||||
(instance_variable)
|
||||
] @property
|
||||
|
||||
((identifier) @constant.builtin
|
||||
(#match? @constant.builtin "^__(FILE|LINE|ENCODING)__$"))
|
||||
|
||||
(file) @constant.builtin
|
||||
(line) @constant.builtin
|
||||
(encoding) @constant.builtin
|
||||
|
||||
(hash_splat_nil
|
||||
"**" @operator
|
||||
) @constant.builtin
|
||||
|
||||
((constant) @constant
|
||||
(#match? @constant "^[A-Z\\d_]+$"))
|
||||
|
||||
(constant) @type
|
||||
|
||||
(self) @variable.special
|
||||
(super) @variable.special
|
||||
|
||||
; Literals
|
||||
|
||||
[
|
||||
(string)
|
||||
(bare_string)
|
||||
(subshell)
|
||||
(heredoc_body)
|
||||
(heredoc_beginning)
|
||||
] @string
|
||||
|
||||
[
|
||||
(simple_symbol)
|
||||
(delimited_symbol)
|
||||
(hash_key_symbol)
|
||||
(bare_symbol)
|
||||
] @string.special.symbol
|
||||
|
||||
(regex) @string.special.regex
|
||||
(escape_sequence) @escape
|
||||
|
||||
[
|
||||
(integer)
|
||||
(float)
|
||||
] @number
|
||||
|
||||
[
|
||||
(nil)
|
||||
(true)
|
||||
(false)
|
||||
] @constant.builtin
|
||||
|
||||
(interpolation
|
||||
"#{" @punctuation.special
|
||||
"}" @punctuation.special) @embedded
|
||||
|
||||
(comment) @comment
|
||||
|
||||
; Operators
|
||||
|
||||
[
|
||||
"!"
|
||||
"~"
|
||||
"+"
|
||||
"-"
|
||||
"**"
|
||||
"*"
|
||||
"/"
|
||||
"%"
|
||||
"<<"
|
||||
">>"
|
||||
"&"
|
||||
"|"
|
||||
"^"
|
||||
">"
|
||||
"<"
|
||||
"<="
|
||||
">="
|
||||
"=="
|
||||
"!="
|
||||
"=~"
|
||||
"!~"
|
||||
"<=>"
|
||||
"||"
|
||||
"&&"
|
||||
".."
|
||||
"..."
|
||||
"="
|
||||
"**="
|
||||
"*="
|
||||
"/="
|
||||
"%="
|
||||
"+="
|
||||
"-="
|
||||
"<<="
|
||||
">>="
|
||||
"&&="
|
||||
"&="
|
||||
"||="
|
||||
"|="
|
||||
"^="
|
||||
"=>"
|
||||
"->"
|
||||
(operator)
|
||||
] @operator
|
||||
|
||||
[
|
||||
","
|
||||
";"
|
||||
"."
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
"%w("
|
||||
"%i("
|
||||
] @punctuation.bracket
|
17
crates/zed/src/languages/ruby/indents.scm
Normal file
17
crates/zed/src/languages/ruby/indents.scm
Normal file
@ -0,0 +1,17 @@
|
||||
(method "end" @end) @indent
|
||||
(class "end" @end) @indent
|
||||
(module "end" @end) @indent
|
||||
(begin "end" @end) @indent
|
||||
(do_block "end" @end) @indent
|
||||
|
||||
(then) @indent
|
||||
(call) @indent
|
||||
|
||||
(ensure) @outdent
|
||||
(rescue) @outdent
|
||||
(else) @outdent
|
||||
|
||||
|
||||
(_ "[" "]" @end) @indent
|
||||
(_ "{" "}" @end) @indent
|
||||
(_ "(" ")" @end) @indent
|
11
crates/zed/src/languages/ruby/outline.scm
Normal file
11
crates/zed/src/languages/ruby/outline.scm
Normal file
@ -0,0 +1,11 @@
|
||||
(class
|
||||
"class" @context
|
||||
name: (_) @name) @item
|
||||
|
||||
(method
|
||||
"def" @context
|
||||
name: (_) @name) @item
|
||||
|
||||
(module
|
||||
"module" @context
|
||||
name: (_) @name) @item
|
@ -1,10 +1,6 @@
|
||||
import { fontWeights } from "../common";
|
||||
import { withOpacity } from "../utils/color";
|
||||
import {
|
||||
ColorScheme,
|
||||
Layer,
|
||||
StyleSets,
|
||||
} from "../themes/common/colorScheme";
|
||||
import { ColorScheme, Layer, StyleSets } from "../themes/common/colorScheme";
|
||||
import {
|
||||
background,
|
||||
border,
|
||||
@ -50,6 +46,11 @@ export default function editor(colorScheme: ColorScheme) {
|
||||
color: colorScheme.ramps.neutral(1).hex(),
|
||||
weight: fontWeights.normal,
|
||||
},
|
||||
"variable.special": {
|
||||
// Highlights for self, this, etc
|
||||
color: colorScheme.ramps.blue(0.7).hex(),
|
||||
weight: fontWeights.normal,
|
||||
},
|
||||
comment: {
|
||||
color: colorScheme.ramps.neutral(0.71).hex(),
|
||||
weight: fontWeights.normal,
|
||||
@ -270,9 +271,9 @@ export default function editor(colorScheme: ColorScheme) {
|
||||
background: withOpacity(background(layer, "inverted"), 0.4),
|
||||
border: {
|
||||
width: 1,
|
||||
color: borderColor(layer, 'variant'),
|
||||
color: borderColor(layer, "variant"),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
compositionMark: {
|
||||
underline: {
|
||||
|
Loading…
Reference in New Issue
Block a user