Merge branch 'master' into help-command

pull/997/head
Omnikar 3 years ago
commit c039c9b6fd
No known key found for this signature in database
GPG Key ID: 7DE6694CDA7885ED

4
.gitmodules vendored

@ -178,6 +178,10 @@
path = helix-syntax/languages/tree-sitter-git-diff
url = https://github.com/the-mikedavis/tree-sitter-git-diff.git
shallow = true
[submodule "helix-syntax/languages/tree-sitter-tablegen"]
path = helix-syntax/languages/tree-sitter-tablegen
url = https://github.com/Flakebi/tree-sitter-tablegen
shallow = true
[submodule "helix-syntax/languages/tree-sitter-git-rebase"]
path = helix-syntax/languages/tree-sitter-git-rebase
url = https://github.com/the-mikedavis/tree-sitter-git-rebase.git

@ -40,6 +40,7 @@
| rust | ✓ | ✓ | ✓ | `rust-analyzer` |
| scala | ✓ | | ✓ | `metals` |
| svelte | ✓ | | ✓ | `svelteserver` |
| tablegen | ✓ | ✓ | ✓ | |
| toml | ✓ | | | |
| tsq | ✓ | | | |
| tsx | ✓ | | | `typescript-language-server` |

@ -20,6 +20,7 @@
| `:quit-all`, `:qa` | Close all views. |
| `:quit-all!`, `:qa!` | Close all views forcefully (ignoring unsaved changes). |
| `:cquit`, `:cq` | Quit with exit code (default 1). Accepts an optional integer exit code (:cq 2). |
| `:cquit!`, `:cq!` | Quit with exit code (default 1) forcefully (ignoring unsaved changes). Accepts an optional integer exit code (:cq! 2). |
| `:theme` | Change the editor theme. |
| `:clipboard-yank` | Yank main selection into system clipboard. |
| `:clipboard-yank-join` | Yank joined selections into system clipboard. A separator can be provided as first argument. Default value is newline. |

@ -1,6 +1,5 @@
use crate::{
chars::{char_is_line_ending, char_is_whitespace},
find_first_non_whitespace_char,
syntax::{IndentQuery, LanguageConfiguration, Syntax},
tree_sitter::Node,
Rope, RopeSlice,
@ -174,8 +173,7 @@ pub fn auto_detect_indent_style(document_text: &Rope) -> Option<IndentStyle> {
/// To determine indentation of a newly inserted line, figure out the indentation at the last col
/// of the previous line.
#[allow(dead_code)]
fn indent_level_for_line(line: RopeSlice, tab_width: usize) -> usize {
pub fn indent_level_for_line(line: RopeSlice, tab_width: usize) -> usize {
let mut len = 0;
for ch in line.chars() {
match ch {
@ -210,10 +208,15 @@ fn get_highest_syntax_node_at_bytepos(syntax: &Syntax, pos: usize) -> Option<Nod
Some(node)
}
fn calculate_indentation(query: &IndentQuery, node: Option<Node>, newline: bool) -> usize {
// NOTE: can't use contains() on query because of comparing Vec<String> and &str
// https://doc.rust-lang.org/std/vec/struct.Vec.html#method.contains
/// Calculate the indentation at a given treesitter node.
/// If newline is false, then any "indent" nodes on the line are ignored ("outdent" still applies).
/// This is because the indentation is only increased starting at the second line of the node.
fn calculate_indentation(
query: &IndentQuery,
node: Option<Node>,
line: usize,
newline: bool,
) -> usize {
let mut increment: isize = 0;
let mut node = match node {
@ -221,70 +224,45 @@ fn calculate_indentation(query: &IndentQuery, node: Option<Node>, newline: bool)
None => return 0,
};
let mut prev_start = node.start_position().row;
let mut current_line = line;
let mut consider_indent = newline;
let mut increment_from_line: isize = 0;
// if we're calculating indentation for a brand new line then the current node will become the
// parent node. We need to take it's indentation level into account too.
loop {
let node_kind = node.kind();
if newline && query.indent.contains(node_kind) {
increment += 1;
}
while let Some(parent) = node.parent() {
let parent_kind = parent.kind();
let start = parent.start_position().row;
// detect deeply nested indents in the same line
let start = node.start_position().row;
if current_line != start {
// Indent/dedent by at most one per line:
// .map(|a| { <-- ({ is two scopes
// let len = 1; <-- indents one level
// }) <-- }) is two scopes
let starts_same_line = start == prev_start;
if query.outdent.contains(node.kind()) && !starts_same_line {
// we outdent by skipping the rules for the current level and jumping up
// node = parent;
increment -= 1;
// continue;
if consider_indent || increment_from_line < 0 {
increment += increment_from_line.signum();
}
if query.indent.contains(parent_kind) // && not_first_or_last_sibling
&& !starts_same_line
{
// println!("is_scope {}", parent_kind);
prev_start = start;
increment += 1
increment_from_line = 0;
current_line = start;
consider_indent = true;
}
// if last_scope && increment > 0 && ...{ ignore }
if query.outdent.contains(node_kind) {
increment_from_line -= 1;
}
if query.indent.contains(node_kind) {
increment_from_line += 1;
}
if let Some(parent) = node.parent() {
node = parent;
} else {
break;
}
}
if consider_indent || increment_from_line < 0 {
increment += increment_from_line.signum();
}
increment.max(0) as usize
}
#[allow(dead_code)]
fn suggested_indent_for_line(
language_config: &LanguageConfiguration,
syntax: Option<&Syntax>,
text: RopeSlice,
line_num: usize,
_tab_width: usize,
) -> usize {
if let Some(start) = find_first_non_whitespace_char(text.line(line_num)) {
return suggested_indent_for_pos(
Some(language_config),
syntax,
text,
start + text.line_to_char(line_num),
false,
);
};
// if the line is blank, indent should be zero
0
}
// TODO: two usecases: if we are triggering this for a new, blank line:
// - it should return 0 when mass indenting stuff
// - it should look up the wrapper node and count it too when we press o/O
@ -293,23 +271,20 @@ pub fn suggested_indent_for_pos(
syntax: Option<&Syntax>,
text: RopeSlice,
pos: usize,
line: usize,
new_line: bool,
) -> usize {
) -> Option<usize> {
if let (Some(query), Some(syntax)) = (
language_config.and_then(|config| config.indent_query()),
syntax,
) {
let byte_start = text.char_to_byte(pos);
let node = get_highest_syntax_node_at_bytepos(syntax, byte_start);
// let config = load indentation query config from Syntax(should contain language_config)
// TODO: special case for comments
// TODO: if preserve_leading_whitespace
calculate_indentation(query, node, new_line)
Some(calculate_indentation(query, node, line, new_line))
} else {
// TODO: heuristics for non-tree sitter grammars
0
None
}
}
@ -484,14 +459,23 @@ where
for i in 0..doc.len_lines() {
let line = text.line(i);
if let Some(pos) = crate::find_first_non_whitespace_char(line) {
let indent = indent_level_for_line(line, tab_width);
assert_eq!(
suggested_indent_for_line(&language_config, Some(&syntax), text, i, tab_width),
indent,
"line {}: {}",
suggested_indent_for_pos(
Some(&language_config),
Some(&syntax),
text,
text.line_to_char(i) + pos,
i,
false
),
Some(indent),
"line {}: \"{}\"",
i,
line
);
}
}
}
}

@ -66,39 +66,26 @@ pub mod util {
pos: lsp::Position,
offset_encoding: OffsetEncoding,
) -> Option<usize> {
let max_line = doc.lines().count().saturating_sub(1);
let pos_line = pos.line as usize;
let pos_line = if pos_line > max_line {
if pos_line > doc.len_lines() - 1 {
return None;
} else {
pos_line
};
}
match offset_encoding {
OffsetEncoding::Utf8 => {
let max_char = doc
.line_to_char(max_line)
.checked_add(doc.line(max_line).len_chars())?;
let line = doc.line_to_char(pos_line);
let pos = line.checked_add(pos.character as usize)?;
if pos <= max_char {
if pos <= doc.len_chars() {
Some(pos)
} else {
None
}
}
OffsetEncoding::Utf16 => {
let max_char = doc
.line_to_char(max_line)
.checked_add(doc.line(max_line).len_chars())?;
let max_cu = doc.char_to_utf16_cu(max_char);
let line = doc.line_to_char(pos_line);
let line_start = doc.char_to_utf16_cu(line);
let pos = line_start.checked_add(pos.character as usize)?;
if pos <= max_cu {
Some(doc.utf16_cu_to_char(pos))
} else {
None
}
doc.try_utf16_cu_to_char(pos).ok()
}
}
}

@ -0,0 +1 @@
Subproject commit 568dd8a937347175fd58db83d4c4cdaeb6069bd2

@ -1282,6 +1282,8 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
.max(view.offset.row + scrolloff)
.min(last_line.saturating_sub(scrolloff));
// If cursor needs moving, replace primary selection
if line != cursor.row {
let head = pos_at_coords(text, Position::new(line, cursor.col), true); // this func will properly truncate to line end
let anchor = if doc.mode == Mode::Select {
@ -1290,8 +1292,13 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
head
};
// TODO: only manipulate main selection
doc.set_selection(view.id, Selection::single(anchor, head));
// replace primary selection with an empty selection at cursor pos
let prim_sel = Range::new(anchor, head);
let mut sel = doc.selection(view.id).clone();
let idx = sel.primary_index();
sel = sel.replace(idx, prim_sel);
doc.set_selection(view.id, sel);
}
}
fn page_up(cx: &mut Context) {
@ -1545,7 +1552,7 @@ fn searcher(cx: &mut Context, direction: Direction) {
let reg = cx.register.unwrap_or('/');
let scrolloff = cx.editor.config.scrolloff;
let (_, doc) = current!(cx.editor);
let doc = doc!(cx.editor);
// TODO: could probably share with select_on_matches?
@ -2046,7 +2053,7 @@ pub mod cmd {
fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::Result<()> {
let jobs = &mut cx.jobs;
let (_, doc) = current!(cx.editor);
let doc = doc_mut!(cx.editor);
if let Some(ref path) = path {
doc.set_path(Some(path.as_ref().as_ref()))
@ -2099,8 +2106,7 @@ pub mod cmd {
_args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
let (_, doc) = current!(cx.editor);
let doc = doc!(cx.editor);
if let Some(format) = doc.format() {
let callback =
make_format_callback(doc.id(), doc.version(), Modified::LeaveModified, format);
@ -2323,12 +2329,7 @@ pub mod cmd {
write_all_impl(cx, args, event, true, true)
}
fn quit_all_impl(
editor: &mut Editor,
_args: &[Cow<str>],
_event: PromptEvent,
force: bool,
) -> anyhow::Result<()> {
fn quit_all_impl(editor: &mut Editor, force: bool) -> anyhow::Result<()> {
if !force {
buffers_remaining_impl(editor)?;
}
@ -2344,18 +2345,18 @@ pub mod cmd {
fn quit_all(
cx: &mut compositor::Context,
args: &[Cow<str>],
event: PromptEvent,
_args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
quit_all_impl(cx.editor, args, event, false)
quit_all_impl(cx.editor, false)
}
fn force_quit_all(
cx: &mut compositor::Context,
args: &[Cow<str>],
event: PromptEvent,
_args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
quit_all_impl(cx.editor, args, event, true)
quit_all_impl(cx.editor, true)
}
fn cquit(
@ -2369,12 +2370,21 @@ pub mod cmd {
.unwrap_or(1);
cx.editor.exit_code = exit_code;
let views: Vec<_> = cx.editor.tree.views().map(|(view, _)| view.id).collect();
for view_id in views {
cx.editor.close(view_id);
quit_all_impl(cx.editor, false)
}
Ok(())
fn force_cquit(
cx: &mut compositor::Context,
args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
let exit_code = args
.first()
.and_then(|code| code.parse::<i32>().ok())
.unwrap_or(1);
cx.editor.exit_code = exit_code;
quit_all_impl(cx.editor, true)
}
fn theme(
@ -2409,7 +2419,7 @@ pub mod cmd {
args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
let (_, doc) = current!(cx.editor);
let doc = doc!(cx.editor);
let default_sep = Cow::Borrowed(doc.line_ending.as_str());
let separator = args.first().unwrap_or(&default_sep);
yank_joined_to_clipboard_impl(cx.editor, separator, ClipboardType::Clipboard)
@ -2428,7 +2438,7 @@ pub mod cmd {
args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
let (_, doc) = current!(cx.editor);
let doc = doc!(cx.editor);
let default_sep = Cow::Borrowed(doc.line_ending.as_str());
let separator = args.first().unwrap_or(&default_sep);
yank_joined_to_clipboard_impl(cx.editor, separator, ClipboardType::Selection)
@ -2555,7 +2565,7 @@ pub mod cmd {
args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
let (_, doc) = current!(cx.editor);
let doc = doc_mut!(cx.editor);
if let Some(label) = args.first() {
doc.set_encoding(label)
} else {
@ -3017,6 +3027,13 @@ pub mod cmd {
fun: cquit,
completer: None,
},
TypableCommand {
name: "cquit!",
aliases: &["cq!"],
doc: "Quit with exit code (default 1) forcefully (ignoring unsaved changes). Accepts an optional integer exit code (:cq! 2).",
fun: force_cquit,
completer: None,
},
TypableCommand {
name: "theme",
aliases: &[],
@ -3393,7 +3410,7 @@ fn symbol_picker(cx: &mut Context) {
nested_to_flat(list, file, child);
}
}
let (_, doc) = current!(cx.editor);
let doc = doc!(cx.editor);
let language_server = match doc.language_server() {
Some(language_server) => language_server,
@ -3414,7 +3431,7 @@ fn symbol_picker(cx: &mut Context) {
let symbols = match symbols {
lsp::DocumentSymbolResponse::Flat(symbols) => symbols,
lsp::DocumentSymbolResponse::Nested(symbols) => {
let (_view, doc) = current!(editor);
let doc = doc!(editor);
let mut flat_symbols = Vec::new();
for symbol in symbols {
nested_to_flat(&mut flat_symbols, &doc.identifier(), symbol)
@ -3456,17 +3473,15 @@ fn symbol_picker(cx: &mut Context) {
}
fn workspace_symbol_picker(cx: &mut Context) {
let (_, doc) = current!(cx.editor);
let doc = doc!(cx.editor);
let current_path = doc.path().cloned();
let language_server = match doc.language_server() {
Some(language_server) => language_server,
None => return,
};
let offset_encoding = language_server.offset_encoding();
let future = language_server.workspace_symbols("".to_string());
let current_path = doc_mut!(cx.editor).path().cloned();
cx.callback(
future,
move |_editor: &mut Editor,
@ -3576,8 +3591,7 @@ pub fn code_action(cx: &mut Context) {
}
pub fn execute_lsp_command(editor: &mut Editor, cmd: lsp::Command) {
let (_view, doc) = current!(editor);
let doc = doc!(editor);
let language_server = match doc.language_server() {
Some(language_server) => language_server,
None => return,
@ -3839,22 +3853,22 @@ fn open(cx: &mut Context, open: Open) {
let mut offs = 0;
let mut transaction = Transaction::change_by_selection(contents, selection, |range| {
let line = range.cursor_line(text);
let cursor_line = range.cursor_line(text);
let line = match open {
let new_line = match open {
// adjust position to the end of the line (next line - 1)
Open::Below => line + 1,
Open::Below => cursor_line + 1,
// adjust position to the end of the previous line (current line - 1)
Open::Above => line,
Open::Above => cursor_line,
};
// Index to insert newlines after, as well as the char width
// to use to compensate for those inserted newlines.
let (line_end_index, line_end_offset_width) = if line == 0 {
let (line_end_index, line_end_offset_width) = if new_line == 0 {
(0, 0)
} else {
(
line_end_char_index(&doc.text().slice(..), line.saturating_sub(1)),
line_end_char_index(&doc.text().slice(..), new_line.saturating_sub(1)),
doc.line_ending.len_chars(),
)
};
@ -3865,8 +3879,10 @@ fn open(cx: &mut Context, open: Open) {
doc.syntax(),
text,
line_end_index,
new_line.saturating_sub(1),
true,
);
)
.unwrap_or_else(|| indent::indent_level_for_line(text.line(cursor_line), doc.tab_width()));
let indent = doc.indent_unit().repeat(indent_level);
let indent_len = indent.len();
let mut text = String::with_capacity(1 + indent_len);
@ -4331,27 +4347,21 @@ fn goto_pos(editor: &mut Editor, pos: usize) {
}
fn goto_first_diag(cx: &mut Context) {
let editor = &mut cx.editor;
let (_, doc) = current!(editor);
let doc = doc!(cx.editor);
let pos = match doc.diagnostics().first() {
Some(diag) => diag.range.start,
None => return,
};
goto_pos(editor, pos);
goto_pos(cx.editor, pos);
}
fn goto_last_diag(cx: &mut Context) {
let editor = &mut cx.editor;
let (_, doc) = current!(editor);
let doc = doc!(cx.editor);
let pos = match doc.diagnostics().last() {
Some(diag) => diag.range.start,
None => return,
};
goto_pos(editor, pos);
goto_pos(cx.editor, pos);
}
fn goto_next_diag(cx: &mut Context) {
@ -4607,48 +4617,48 @@ pub mod insert {
};
let curr = contents.get_char(pos).unwrap_or(' ');
// TODO: offset range.head by 1? when calculating?
let current_line = text.char_to_line(pos);
let indent_level = indent::suggested_indent_for_pos(
doc.language_config(),
doc.syntax(),
text,
pos.saturating_sub(1),
pos,
current_line,
true,
);
)
.unwrap_or_else(|| {
indent::indent_level_for_line(text.line(current_line), doc.tab_width())
});
let indent = doc.indent_unit().repeat(indent_level);
let mut text = String::with_capacity(1 + indent.len());
let mut text = String::new();
// If we are between pairs (such as brackets), we want to insert an additional line which is indented one level more and place the cursor there
let new_head_pos = if helix_core::auto_pairs::PAIRS.contains(&(prev, curr)) {
let inner_indent = doc.indent_unit().repeat(indent_level + 1);
text.reserve_exact(2 + indent.len() + inner_indent.len());
text.push_str(doc.line_ending.as_str());
text.push_str(&inner_indent);
let new_head_pos = pos + offs + text.chars().count();
text.push_str(doc.line_ending.as_str());
text.push_str(&indent);
let head = pos + offs + text.chars().count();
// TODO: range replace or extend
// range.replace(|range| range.is_empty(), head); -> fn extend if cond true, new head pos
// can be used with cx.mode to do replace or extend on most changes
ranges.push(Range::new(
if range.is_empty() {
head
new_head_pos
} else {
range.anchor + offs
},
head,
));
// if between a bracket pair
if helix_core::auto_pairs::PAIRS.contains(&(prev, curr)) {
// another newline, indent the end bracket one level less
let indent = doc.indent_unit().repeat(indent_level.saturating_sub(1));
text.reserve_exact(1 + indent.len());
text.push_str(doc.line_ending.as_str());
text.push_str(&indent);
}
pos + offs + text.chars().count()
};
// TODO: range replace or extend
// range.replace(|range| range.is_empty(), head); -> fn extend if cond true, new head pos
// can be used with cx.mode to do replace or extend on most changes
ranges.push(Range::new(new_head_pos, new_head_pos));
offs += text.chars().count();
(pos, pos, Some(text.into()))
});
transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index()));
//
doc.apply(&transaction, view.id);
}
@ -5416,7 +5426,7 @@ pub fn completion(cx: &mut Context) {
move |editor: &mut Editor,
compositor: &mut Compositor,
response: Option<lsp::CompletionResponse>| {
let (_, doc) = current!(editor);
let doc = doc!(editor);
if doc.mode() != Mode::Insert {
// we're not in insert mode anymore
return;

@ -781,8 +781,9 @@ impl EditorView {
pub fn clear_completion(&mut self, editor: &mut Editor) {
self.completion = None;
// Clear any savepoints
let (_, doc) = current!(editor);
let doc = doc_mut!(editor);
doc.savepoint = None;
editor.clear_idle_timer(); // don't retrigger
}
@ -940,14 +941,18 @@ impl EditorView {
}
impl Component for EditorView {
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
let mut cxt = commands::Context {
editor: cx.editor,
fn handle_event(
&mut self,
event: Event,
context: &mut crate::compositor::Context,
) -> EventResult {
let mut cx = commands::Context {
editor: context.editor,
count: None,
register: None,
callback: None,
on_next_key_callback: None,
jobs: cx.jobs,
jobs: context.jobs,
};
match event {
@ -957,18 +962,19 @@ impl Component for EditorView {
EventResult::Consumed(None)
}
Event::Key(key) => {
cxt.editor.reset_idle_timer();
cx.editor.reset_idle_timer();
let mut key = KeyEvent::from(key);
canonicalize_key(&mut key);
// clear status
cxt.editor.status_msg = None;
cx.editor.status_msg = None;
let (_, doc) = current!(cxt.editor);
let doc = doc!(cx.editor);
let mode = doc.mode();
if let Some(on_next_key) = self.on_next_key.take() {
// if there's a command waiting input, do that first
on_next_key(&mut cxt, key);
on_next_key(&mut cx, key);
} else {
match mode {
Mode::Insert => {
@ -980,8 +986,8 @@ impl Component for EditorView {
if let Some(completion) = &mut self.completion {
// use a fake context here
let mut cx = Context {
editor: cxt.editor,
jobs: cxt.jobs,
editor: cx.editor,
jobs: cx.jobs,
scroll: None,
};
let res = completion.handle_event(event, &mut cx);
@ -991,40 +997,40 @@ impl Component for EditorView {
if callback.is_some() {
// assume close_fn
self.clear_completion(cxt.editor);
self.clear_completion(cx.editor);
}
}
}
// if completion didn't take the event, we pass it onto commands
if !consumed {
self.insert_mode(&mut cxt, key);
self.insert_mode(&mut cx, key);
// lastly we recalculate completion
if let Some(completion) = &mut self.completion {
completion.update(&mut cxt);
completion.update(&mut cx);
if completion.is_empty() {
self.clear_completion(cxt.editor);
self.clear_completion(cx.editor);
}
}
}
}
mode => self.command_mode(mode, &mut cxt, key),
mode => self.command_mode(mode, &mut cx, key),
}
}
self.on_next_key = cxt.on_next_key_callback.take();
self.on_next_key = cx.on_next_key_callback.take();
// appease borrowck
let callback = cxt.callback.take();
let callback = cx.callback.take();
// if the command consumed the last view, skip the render.
// on the next loop cycle the Application will then terminate.
if cxt.editor.should_close() {
if cx.editor.should_close() {
return EventResult::Ignored;
}
let (view, doc) = current!(cxt.editor);
view.ensure_cursor_in_view(doc, cxt.editor.config.scrolloff);
let (view, doc) = current!(cx.editor);
view.ensure_cursor_in_view(doc, cx.editor.config.scrolloff);
// mode transitions
match (mode, doc.mode()) {
@ -1053,7 +1059,7 @@ impl Component for EditorView {
EventResult::Consumed(callback)
}
Event::Mouse(event) => self.handle_mouse_event(event, &mut cxt),
Event::Mouse(event) => self.handle_mouse_event(event, &mut cx),
}
}

@ -436,6 +436,15 @@ file-types = ["ll"]
comment-token = ";"
indent = { tab-width = 2, unit = " " }
[[language]]
name = "tablegen"
scope = "source.tablegen"
roots = []
file-types = ["td"]
comment-token = "//"
indent = { tab-width = 2, unit = " " }
injection-regex = "tablegen"
[[language]]
name = "markdown"
scope = "source.md"

@ -1,6 +1,7 @@
(fenced_code_block
(info_string) @injection.language
(code_fence_content) @injection.content)
(code_fence_content) @injection.content
(#set! injection.include-children))
((html_block) @injection.content
(#set! injection.language "html"))

@ -9,6 +9,7 @@ indent = [
"field_initializer_list",
"struct_pattern",
"tuple_pattern",
"unit_expression",
"enum_variant_list",
"call_expression",
"binary_expression",

@ -0,0 +1,90 @@
[
(comment)
(multiline_comment)
] @comment
[
"("
")"
"["
"]"
"{"
"}"
"<"
">"
] @punctuation.bracket
[
","
";"
"."
] @punctuation.delimiter
[
"#"
"-"
"..."
":"
] @operator
[
"="
"!cond"
(operator_keyword)
] @function
[
"true"
"false"
] @constant.builtin.boolean
[
"?"
] @constant.builtin
(var) @variable
(template_arg (identifier) @variable.parameter)
(_ argument: (value (identifier) @variable.parameter))
(type) @type
"code" @type.builtin
(number) @constant.numeric.integer
[
(string_string)
(code_string)
] @string
(preprocessor) @keyword.directive
[
"class"
"field"
"let"
"defvar"
"def"
"defset"
"defvar"
"assert"
] @keyword
[
"let"
"in"
"foreach"
"if"
"then"
"else"
] @keyword.operator
"include" @keyword.control.import
[
"multiclass"
"defm"
] @namespace
(ERROR) @error

@ -0,0 +1,7 @@
indent = [
"statement",
]
outdent = [
"}",
]

@ -0,0 +1,2 @@
([ (comment) (multiline_comment)] @injection.content
(#set! injection.language "comment"))

@ -0,0 +1,7 @@
(class
body: (_) @class.inside) @class.around
(multiclass
body: (_) @class.inside) @class.around
(_ argument: _ @parameter.inside)

@ -53,3 +53,6 @@
"error" = "#dc597f"
"info" = "#59dcb7"
"hint" = "#59c0dc"
# make diagnostic underlined, to distinguish with selection text.
diagnostic = { modifiers = ["underlined"] }

@ -12,7 +12,7 @@
"type" = "yellow"
"constant" = "purple"
"constant.numeric" = "purple"
"string" = "grey2"
"string" = "green"
"comment" = "grey0"
"variable" = "fg"
"variable.builtin" = "blue"

@ -0,0 +1,90 @@
# Everforest (Dark Hard)
# Author: CptPotato
# Original Author:
# URL: https://github.com/sainnhe/everforest
# Filename: autoload/everforest.vim
# Author: sainnhe
# Email: sainnhe@gmail.com
# License: MIT License
"constant.character.escape" = "orange"
"type" = "yellow"
"constant" = "purple"
"constant.numeric" = "purple"
"string" = "green"
"comment" = "grey0"
"variable" = "fg"
"variable.builtin" = "blue"
"variable.parameter" = "fg"
"variable.other.member" = "fg"
"label" = "aqua"
"punctuation" = "grey2"
"punctuation.delimiter" = "grey2"
"punctuation.bracket" = "fg"
"keyword" = "red"
"operator" = "orange"
"function" = "green"
"function.builtin" = "blue"
"function.macro" = "aqua"
"tag" = "yellow"
"namespace" = "aqua"
"attribute" = "aqua"
"constructor" = "yellow"
"module" = "blue"
"special" = "orange"
"diff.plus" = "green"
"diff.delta" = "orange"
"diff.minus" = "red"
"ui.background" = { bg = "bg0" }
"ui.cursor" = { fg = "bg0", bg = "fg" }
"ui.cursor.match" = { fg = "orange", bg = "bg_yellow" }
"ui.cursor.insert" = { fg = "bg0", bg = "grey1" }
"ui.cursor.select" = { fg = "bg0", bg = "blue" }
"ui.linenr" = "grey0"
"ui.linenr.selected" = "fg"
"ui.statusline" = { fg = "grey2", bg = "bg2" }
"ui.statusline.inactive" = { fg = "grey0", bg = "bg1" }
"ui.popup" = { fg = "grey2", bg = "bg1" }
"ui.window" = { fg = "grey2", bg = "bg1" }
"ui.help" = { fg = "fg", bg = "bg1" }
"ui.text" = "fg"
"ui.text.focus" = "fg"
"ui.menu" = { fg = "fg", bg = "bg2" }
"ui.menu.selected" = { fg = "bg0", bg = "green" }
"ui.selection" = { bg = "bg3" }
"hint" = "blue"
"info" = "aqua"
"warning" = "yellow"
"error" = "red"
"diagnostic" = { modifiers = ["underlined"] }
[palette]
bg0 = "#fff9e8"
bg1 = "#f7f4e0"
bg2 = "#f0eed9"
bg3 = "#e9e8d2"
bg4 = "#e1ddcb"
bg5 = "#bec5b2"
bg_visual = "#edf0cd"
bg_red = "#fce5dc"
bg_green = "#f1f3d4"
bg_blue = "#eaf2eb"
bg_yellow = "#fbefd0"
fg = "#5c6a72"
red = "#f85552"
orange = "#f57d26"
yellow = "#dfa000"
green = "#8da101"
aqua = "#35a77c"
blue = "#3a94c5"
purple = "#df69ba"
grey0 = "#a6b0a0"
grey1 = "#939f91"
grey2 = "#829181"
Loading…
Cancel
Save