mirror of
https://github.com/helix-editor/helix.git
synced 2025-10-06 08:23:27 +02:00
Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7e8603247d | ||
|
7140908f6e | ||
|
6dba1e7ec7 | ||
|
c0332bd935 | ||
|
3c7729906c | ||
|
1b67fae9f4 | ||
|
f0018280cb | ||
|
7202953e69 | ||
|
7761c88d61 | ||
|
68f5031dcc | ||
|
83031564db | ||
|
eab6e53511 | ||
|
f5f46b1fed | ||
|
5f49bafbe8 | ||
|
2719a35123 | ||
|
0a6672c626 | ||
|
b51111a364 | ||
|
78980f575b | ||
|
0bb375bafa | ||
|
c960bcfc24 | ||
|
e88383d990 | ||
|
312b29f712 | ||
|
f4560cb68a | ||
|
ca042a4bde |
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -82,3 +82,7 @@
|
||||
path = helix-syntax/languages/tree-sitter-toml
|
||||
url = https://github.com/ikatyang/tree-sitter-toml
|
||||
shallow = true
|
||||
[submodule "helix-syntax/languages/tree-sitter-elixir"]
|
||||
path = helix-syntax/languages/tree-sitter-elixir
|
||||
url = https://github.com/IceDragon200/tree-sitter-elixir
|
||||
shallow = true
|
||||
|
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -326,7 +326,6 @@ dependencies = [
|
||||
"log",
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"pico-args",
|
||||
"pulldown-cmark",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -607,12 +606,6 @@ version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d7afeb98c5a10e0bffcc7fc16e105b04d06729fac5fd6384aebf7ff5cb5a67d"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.6"
|
||||
|
@@ -65,6 +65,13 @@ Some suggestions to get started:
|
||||
We provide an [architecture.md](./docs/architecture.md) that should give you
|
||||
a good overview of the internals.
|
||||
|
||||
## Usage
|
||||
|
||||
### Keyboard shortcuts / Keymap
|
||||
|
||||
All shortcuts/keymaps can be found in the documentation on the website:
|
||||
- https://docs.helix-editor.com/keymap.html
|
||||
|
||||
# Getting help
|
||||
|
||||
Discuss the project on the community [Matrix channel](https://matrix.to/#/#helix-community:matrix.org).
|
||||
|
@@ -65,9 +65,7 @@ impl History {
|
||||
self.cursor == 0
|
||||
}
|
||||
|
||||
// TODO: I'd like to pass Transaction by reference but it fights with the borrowck
|
||||
|
||||
pub fn undo(&mut self) -> Option<Transaction> {
|
||||
pub fn undo(&mut self) -> Option<&Transaction> {
|
||||
if self.at_root() {
|
||||
// We're at the root of undo, nothing to do.
|
||||
return None;
|
||||
@@ -77,17 +75,17 @@ impl History {
|
||||
|
||||
self.cursor = current_revision.parent;
|
||||
|
||||
Some(current_revision.revert.clone())
|
||||
Some(¤t_revision.revert)
|
||||
}
|
||||
|
||||
pub fn redo(&mut self) -> Option<Transaction> {
|
||||
pub fn redo(&mut self) -> Option<&Transaction> {
|
||||
let current_revision = &self.revisions[self.cursor];
|
||||
|
||||
// for now, simply pick the latest child (linear undo / redo)
|
||||
if let Some((index, transaction)) = current_revision.children.last() {
|
||||
self.cursor = *index;
|
||||
|
||||
return Some(transaction.clone());
|
||||
return Some(&transaction);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@@ -64,7 +64,7 @@ pub fn move_next_word_start(slice: RopeSlice, mut begin: usize, count: usize) ->
|
||||
let mut end = begin;
|
||||
|
||||
for _ in 0..count {
|
||||
if begin + 2 > slice.len_chars() {
|
||||
if begin + 1 == slice.len_chars() {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -76,8 +76,9 @@ pub fn move_next_word_start(slice: RopeSlice, mut begin: usize, count: usize) ->
|
||||
begin += 1;
|
||||
}
|
||||
|
||||
// return if not skip while?
|
||||
skip_over_next(slice, &mut begin, |ch| ch == '\n');
|
||||
if !skip_over_next(slice, &mut begin, |ch| ch == '\n') {
|
||||
return None;
|
||||
};
|
||||
ch = slice.char(begin);
|
||||
|
||||
end = begin + 1;
|
||||
@@ -134,7 +135,7 @@ pub fn move_next_word_end(slice: RopeSlice, mut begin: usize, count: usize) -> O
|
||||
let mut end = begin;
|
||||
|
||||
for _ in 0..count {
|
||||
if begin + 2 > slice.len_chars() {
|
||||
if begin + 2 >= slice.len_chars() {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -145,8 +146,9 @@ pub fn move_next_word_end(slice: RopeSlice, mut begin: usize, count: usize) -> O
|
||||
begin += 1;
|
||||
}
|
||||
|
||||
// return if not skip while?
|
||||
skip_over_next(slice, &mut begin, |ch| ch == '\n');
|
||||
if !skip_over_next(slice, &mut begin, |ch| ch == '\n') {
|
||||
return None;
|
||||
};
|
||||
|
||||
end = begin;
|
||||
|
||||
@@ -199,18 +201,20 @@ fn categorize(ch: char) -> Category {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn skip_over_next<F>(slice: RopeSlice, pos: &mut usize, fun: F)
|
||||
/// Returns true if there are more characters left after the new position.
|
||||
pub fn skip_over_next<F>(slice: RopeSlice, pos: &mut usize, fun: F) -> bool
|
||||
where
|
||||
F: Fn(char) -> bool,
|
||||
{
|
||||
let mut chars = slice.chars_at(*pos);
|
||||
|
||||
for ch in chars {
|
||||
while let Some(ch) = chars.next() {
|
||||
if !fun(ch) {
|
||||
break;
|
||||
}
|
||||
*pos += 1;
|
||||
}
|
||||
chars.next().is_some()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@@ -415,7 +415,7 @@ impl ChangeSet {
|
||||
|
||||
/// Transaction represents a single undoable unit of changes. Several changes can be grouped into
|
||||
/// a single transaction.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Transaction {
|
||||
changes: ChangeSet,
|
||||
selection: Option<Selection>,
|
||||
|
1
helix-syntax/languages/tree-sitter-elixir
Submodule
1
helix-syntax/languages/tree-sitter-elixir
Submodule
Submodule helix-syntax/languages/tree-sitter-elixir added at 295e62a43b
@@ -72,6 +72,7 @@ mk_langs!(
|
||||
(CSharp, tree_sitter_c_sharp),
|
||||
(Cpp, tree_sitter_cpp),
|
||||
(Css, tree_sitter_css),
|
||||
(Elixir, tree_sitter_elixir),
|
||||
(Go, tree_sitter_go),
|
||||
// (Haskell, tree_sitter_haskell),
|
||||
(Html, tree_sitter_html),
|
||||
|
@@ -24,7 +24,6 @@ tokio = { version = "1", features = ["full"] }
|
||||
num_cpus = "1"
|
||||
tui = { path = "../helix-tui", package = "helix-tui", default-features = false, features = ["crossterm"] }
|
||||
crossterm = { version = "0.19", features = ["event-stream"] }
|
||||
pico-args = "0.4"
|
||||
|
||||
futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
|
||||
|
||||
|
@@ -122,9 +122,17 @@ impl Compositor {
|
||||
}
|
||||
|
||||
pub fn render(&mut self, cx: &mut Context) {
|
||||
let area = self.size();
|
||||
let area = self
|
||||
.terminal
|
||||
.autoresize()
|
||||
.expect("Unable to determine terminal size");
|
||||
|
||||
// TODO: need to recalculate view tree if necessary
|
||||
|
||||
let surface = self.terminal.current_buffer_mut();
|
||||
|
||||
let area = *surface.area();
|
||||
|
||||
for layer in &self.layers {
|
||||
layer.render(area, surface, cx)
|
||||
}
|
||||
|
@@ -8,9 +8,11 @@ mod ui;
|
||||
|
||||
use application::Application;
|
||||
|
||||
use helix_core::config_dir;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{Context, Error, Result};
|
||||
|
||||
fn setup_logging(verbosity: u64) -> Result<()> {
|
||||
let mut base_config = fern::Dispatch::new();
|
||||
@@ -27,8 +29,6 @@ fn setup_logging(verbosity: u64) -> Result<()> {
|
||||
_3_or_more => base_config.level(log::LevelFilter::Trace),
|
||||
};
|
||||
|
||||
let home = dirs_next::home_dir().context("can't find the home directory")?;
|
||||
|
||||
// Separate file config so we can include year, month and day in file logs
|
||||
let file_config = fern::Dispatch::new()
|
||||
.format(|out, message, record| {
|
||||
@@ -40,7 +40,7 @@ fn setup_logging(verbosity: u64) -> Result<()> {
|
||||
message
|
||||
))
|
||||
})
|
||||
.chain(fern::log_file(home.join("helix.log"))?);
|
||||
.chain(fern::log_file(config_dir().join("helix.log"))?);
|
||||
|
||||
base_config.chain(file_config).apply()?;
|
||||
|
||||
@@ -48,10 +48,54 @@ fn setup_logging(verbosity: u64) -> Result<()> {
|
||||
}
|
||||
|
||||
pub struct Args {
|
||||
display_help: bool,
|
||||
display_version: bool,
|
||||
verbosity: u64,
|
||||
files: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
fn parse_args(mut args: Args) -> Result<Args> {
|
||||
let argv: Vec<String> = std::env::args().collect();
|
||||
let mut iter = argv.iter();
|
||||
|
||||
iter.next(); // skip the program, we don't care about that
|
||||
|
||||
while let Some(arg) = iter.next() {
|
||||
match arg.as_str() {
|
||||
"--" => break, // stop parsing at this point treat the remaining as files
|
||||
"--version" => args.display_version = true,
|
||||
"--help" => args.display_help = true,
|
||||
arg if arg.starts_with("--") => {
|
||||
return Err(Error::msg(format!(
|
||||
"unexpected double dash argument: {}",
|
||||
arg
|
||||
)))
|
||||
}
|
||||
arg if arg.starts_with('-') => {
|
||||
let arg = arg.get(1..).unwrap().chars();
|
||||
for chr in arg {
|
||||
match chr {
|
||||
'v' => args.verbosity += 1,
|
||||
'V' => args.display_version = true,
|
||||
'h' => args.display_help = true,
|
||||
_ => return Err(Error::msg(format!("unexpected short arg {}", chr))),
|
||||
}
|
||||
}
|
||||
}
|
||||
arg => args.files.push(PathBuf::from(arg)),
|
||||
}
|
||||
}
|
||||
|
||||
// push the remaining args, if any to the files
|
||||
for filename in iter {
|
||||
args.files.push(PathBuf::from(filename));
|
||||
}
|
||||
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let help = format!(
|
||||
"\
|
||||
{} {}
|
||||
@@ -75,28 +119,35 @@ FLAGS:
|
||||
env!("CARGO_PKG_DESCRIPTION"),
|
||||
);
|
||||
|
||||
let mut pargs = pico_args::Arguments::from_env();
|
||||
let mut args: Args = Args {
|
||||
display_help: false,
|
||||
display_version: false,
|
||||
verbosity: 0,
|
||||
files: [].to_vec(),
|
||||
};
|
||||
|
||||
args = parse_args(args).context("could not parse arguments")?;
|
||||
|
||||
// Help has a higher priority and should be handled separately.
|
||||
if pargs.contains(["-h", "--help"]) {
|
||||
if args.display_help {
|
||||
print!("{}", help);
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
let mut verbosity: u64 = 0;
|
||||
|
||||
if pargs.contains("-v") {
|
||||
verbosity = 1;
|
||||
if args.display_version {
|
||||
println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
setup_logging(verbosity).context("failed to initialize logging")?;
|
||||
let conf_dir = config_dir();
|
||||
|
||||
let args = Args {
|
||||
files: pargs.finish().into_iter().map(|arg| arg.into()).collect(),
|
||||
};
|
||||
if !conf_dir.exists() {
|
||||
std::fs::create_dir(&conf_dir);
|
||||
}
|
||||
|
||||
setup_logging(args.verbosity).context("failed to initialize logging")?;
|
||||
|
||||
// initialize language registry
|
||||
use helix_core::config_dir;
|
||||
use helix_core::syntax::{Loader, LOADER};
|
||||
|
||||
// load $HOME/.config/helix/languages.toml, fallback to default config
|
||||
@@ -108,13 +159,9 @@ FLAGS:
|
||||
let config = toml::from_slice(toml).context("Could not parse languages.toml")?;
|
||||
LOADER.get_or_init(|| Loader::new(config));
|
||||
|
||||
let runtime = tokio::runtime::Runtime::new().context("unable to start tokio runtime")?;
|
||||
|
||||
// TODO: use the thread local executor to spawn the application task separately from the work pool
|
||||
let mut app = Application::new(args).context("unable to create new appliction")?;
|
||||
runtime.block_on(async move {
|
||||
app.run().await;
|
||||
});
|
||||
app.run().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@@ -240,7 +240,7 @@ impl EditorView {
|
||||
for selection in doc
|
||||
.selection(view.id)
|
||||
.iter()
|
||||
.filter(|range| range.overlaps(&screen))
|
||||
.filter(|range| screen.overlaps(&range))
|
||||
{
|
||||
// TODO: render also if only one of the ranges is in viewport
|
||||
let mut start = view.screen_coords_at_pos(doc, text, selection.anchor);
|
||||
@@ -261,7 +261,7 @@ impl EditorView {
|
||||
Rect::new(
|
||||
viewport.x + start.col as u16,
|
||||
viewport.y + start.row as u16,
|
||||
(end.col - start.col) as u16 + 1,
|
||||
((end.col - start.col) as u16 + 1).min(viewport.width),
|
||||
1,
|
||||
),
|
||||
selection_style,
|
||||
@@ -633,6 +633,10 @@ impl Component for EditorView {
|
||||
// clear with background color
|
||||
surface.set_style(area, cx.editor.theme.get("ui.background"));
|
||||
|
||||
// if the terminal size suddenly changed, we need to trigger a resize
|
||||
cx.editor
|
||||
.resize(Rect::new(area.x, area.y, area.width, area.height - 1)); // - 1 to account for commandline
|
||||
|
||||
for (view, is_focused) in cx.editor.tree.views() {
|
||||
let doc = cx.editor.document(view.doc).unwrap();
|
||||
self.render_view(doc, view, area, surface, &cx.editor.theme, is_focused);
|
||||
|
@@ -160,7 +160,13 @@ impl Prompt {
|
||||
if let Some(doc) = (self.doc_fn)(&self.line) {
|
||||
let text = ui::Text::new(doc.to_string());
|
||||
|
||||
let area = Rect::new(completion_area.x, completion_area.y - 3, BASE_WIDTH * 3, 3);
|
||||
let viewport = area;
|
||||
let area = viewport.intersection(Rect::new(
|
||||
completion_area.x,
|
||||
completion_area.y - 3,
|
||||
BASE_WIDTH * 3,
|
||||
3,
|
||||
));
|
||||
|
||||
let background = theme.get("ui.help");
|
||||
surface.clear_with(area, background);
|
||||
|
@@ -137,14 +137,12 @@ where
|
||||
}
|
||||
|
||||
/// Queries the backend for size and resizes if it doesn't match the previous size.
|
||||
pub fn autoresize(&mut self) -> io::Result<()> {
|
||||
if self.viewport.resize_behavior == ResizeBehavior::Auto {
|
||||
let size = self.size()?;
|
||||
if size != self.viewport.area {
|
||||
self.resize(size)?;
|
||||
}
|
||||
pub fn autoresize(&mut self) -> io::Result<Rect> {
|
||||
let size = self.size()?;
|
||||
if size != self.viewport.area {
|
||||
self.resize(size)?;
|
||||
};
|
||||
Ok(())
|
||||
Ok(size)
|
||||
}
|
||||
|
||||
/// Synchronizes terminal size, calls the rendering closure, flushes the current internal state
|
||||
|
@@ -1,4 +1,5 @@
|
||||
use anyhow::{Context, Error};
|
||||
use std::cell::Cell;
|
||||
use std::future::Future;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
@@ -40,7 +41,10 @@ pub struct Document {
|
||||
/// State at last commit. Used for calculating reverts.
|
||||
old_state: Option<State>,
|
||||
/// Undo tree.
|
||||
history: History,
|
||||
// It can be used as a cell where we will take it out to get some parts of the history and put
|
||||
// it back as it separated from the edits. We could split out the parts manually but that will
|
||||
// be more troublesome.
|
||||
history: Cell<History>,
|
||||
last_saved_revision: usize,
|
||||
version: i32, // should be usize?
|
||||
|
||||
@@ -121,7 +125,7 @@ impl Document {
|
||||
old_state,
|
||||
diagnostics: Vec::new(),
|
||||
version: 0,
|
||||
history: History::default(),
|
||||
history: Cell::new(History::default()),
|
||||
last_saved_revision: 0,
|
||||
language_server: None,
|
||||
}
|
||||
@@ -190,7 +194,9 @@ impl Document {
|
||||
let language_server = self.language_server.clone();
|
||||
|
||||
// reset the modified flag
|
||||
self.last_saved_revision = self.history.current_revision();
|
||||
let history = self.history.take();
|
||||
self.last_saved_revision = history.current_revision();
|
||||
self.history.set(history);
|
||||
|
||||
async move {
|
||||
use tokio::{fs::File, io::AsyncWriteExt};
|
||||
@@ -335,7 +341,8 @@ impl Document {
|
||||
}
|
||||
|
||||
pub fn undo(&mut self, view_id: ViewId) -> bool {
|
||||
if let Some(transaction) = self.history.undo() {
|
||||
let mut history = self.history.take();
|
||||
if let Some(transaction) = history.undo() {
|
||||
let success = self._apply(&transaction, view_id);
|
||||
|
||||
// reset changeset to fix len
|
||||
@@ -343,11 +350,13 @@ impl Document {
|
||||
|
||||
return success;
|
||||
}
|
||||
self.history.set(history);
|
||||
false
|
||||
}
|
||||
|
||||
pub fn redo(&mut self, view_id: ViewId) -> bool {
|
||||
if let Some(transaction) = self.history.redo() {
|
||||
let mut history = self.history.take();
|
||||
if let Some(transaction) = history.redo() {
|
||||
let success = self._apply(&transaction, view_id);
|
||||
|
||||
// reset changeset to fix len
|
||||
@@ -355,6 +364,7 @@ impl Document {
|
||||
|
||||
return success;
|
||||
}
|
||||
self.history.set(history);
|
||||
false
|
||||
}
|
||||
|
||||
@@ -373,7 +383,9 @@ impl Document {
|
||||
// HAXX: we need to reconstruct the state as it was before the changes..
|
||||
let old_state = self.old_state.take().expect("no old_state available");
|
||||
|
||||
self.history.commit_revision(&transaction, &old_state);
|
||||
let mut history = self.history.take();
|
||||
history.commit_revision(&transaction, &old_state);
|
||||
self.history.set(history);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -383,9 +395,11 @@ impl Document {
|
||||
|
||||
#[inline]
|
||||
pub fn is_modified(&self) -> bool {
|
||||
let history = self.history.take();
|
||||
let current_revision = history.current_revision();
|
||||
self.history.set(history);
|
||||
self.path.is_some()
|
||||
&& (self.history.current_revision() != self.last_saved_revision
|
||||
|| !self.changes.is_empty())
|
||||
&& (current_revision != self.last_saved_revision || !self.changes.is_empty())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@@ -194,8 +194,9 @@ impl Editor {
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, area: Rect) {
|
||||
self.tree.resize(area);
|
||||
self._refresh();
|
||||
if self.tree.resize(area) {
|
||||
self._refresh();
|
||||
};
|
||||
}
|
||||
|
||||
pub fn focus_next(&mut self) {
|
||||
|
@@ -293,9 +293,13 @@ impl Tree {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, area: Rect) {
|
||||
self.area = area;
|
||||
self.recalculate();
|
||||
pub fn resize(&mut self, area: Rect) -> bool {
|
||||
if self.area != area {
|
||||
self.area = area;
|
||||
self.recalculate();
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn recalculate(&mut self) {
|
||||
|
@@ -143,8 +143,9 @@ impl View {
|
||||
}
|
||||
}
|
||||
|
||||
let row = line - self.first_line as usize;
|
||||
let col = col - self.first_col as usize;
|
||||
// It is possible for underflow to occur if the buffer length is larger than the terminal width.
|
||||
let row = line.saturating_sub(self.first_line);
|
||||
let col = col.saturating_sub(self.first_col);
|
||||
|
||||
Some(Position::new(row, col))
|
||||
}
|
||||
|
@@ -17,6 +17,15 @@ roots = []
|
||||
|
||||
indent = { tab-width = 2, unit = " " }
|
||||
|
||||
[[language]]
|
||||
name = "elixir"
|
||||
scope = "source.elixir"
|
||||
injection-regex = "elixir"
|
||||
file-types = ["ex", "exs"]
|
||||
roots = []
|
||||
|
||||
indent = { tab-width = 2, unit = " " }
|
||||
|
||||
[[language]]
|
||||
name = "json"
|
||||
scope = "source.json"
|
||||
|
146
runtime/queries/elixir/highlights.scm
Normal file
146
runtime/queries/elixir/highlights.scm
Normal file
@@ -0,0 +1,146 @@
|
||||
["when" "and" "or" "not in" "not" "in" "fn" "do" "end" "catch" "rescue" "after" "else"] @keyword
|
||||
|
||||
[(true) (false) (nil)] @constant.builtin
|
||||
|
||||
(keyword
|
||||
[(keyword_literal)
|
||||
":"] @tag)
|
||||
|
||||
(keyword
|
||||
(keyword_string
|
||||
[(string_start)
|
||||
(string_content)
|
||||
(string_end)] @tag))
|
||||
|
||||
[(atom_literal)
|
||||
(atom_start)
|
||||
(atom_content)
|
||||
(atom_end)] @tag
|
||||
|
||||
(comment) @comment
|
||||
|
||||
(escape_sequence) @escape
|
||||
|
||||
(call function: (function_identifier) @keyword
|
||||
(#match? @keyword "^(defmodule|defexception|defp|def|with|case|cond|raise|import|require|use|defmacrop|defmacro|defguardp|defguard|defdelegate|defstruct|alias|defimpl|defprotocol|defoverridable|receive|if|for|try|throw|unless|reraise|super|quote|unquote|unquote_splicing)$"))
|
||||
|
||||
(call function: (function_identifier) @keyword
|
||||
[(call
|
||||
function: (function_identifier) @function
|
||||
(arguments
|
||||
[(identifier) @variable.parameter
|
||||
(_ (identifier) @variable.parameter)
|
||||
(_ (_ (identifier) @variable.parameter))
|
||||
(_ (_ (_ (identifier) @variable.parameter)))
|
||||
(_ (_ (_ (_ (identifier) @variable.parameter))))
|
||||
(_ (_ (_ (_ (_ (identifier) @variable.parameter)))))]))
|
||||
(binary_op
|
||||
left:
|
||||
(call
|
||||
function: (function_identifier) @function
|
||||
(arguments
|
||||
[(identifier) @variable.parameter
|
||||
(_ (identifier) @variable.parameter)
|
||||
(_ (_ (identifier) @variable.parameter))
|
||||
(_ (_ (_ (identifier) @variable.parameter)))
|
||||
(_ (_ (_ (_ (identifier) @variable.parameter))))
|
||||
(_ (_ (_ (_ (_ (identifier) @variable.parameter)))))]))
|
||||
operator: "when")
|
||||
(binary_op
|
||||
left: (identifier) @variable.parameter
|
||||
operator: _ @function
|
||||
right: (identifier) @variable.parameter)]
|
||||
(#match? @keyword "^(defp|def|defmacrop|defmacro|defguardp|defguard|defdelegate)$")
|
||||
(#match? @variable.parameter "^[^_]"))
|
||||
|
||||
(call (function_identifier) @keyword
|
||||
[(call
|
||||
function: (function_identifier) @function)
|
||||
(identifier) @function
|
||||
(binary_op
|
||||
left:
|
||||
[(call
|
||||
function: (function_identifier) @function)
|
||||
(identifier) @function]
|
||||
operator: "when")]
|
||||
(#match? @keyword "^(defp|def|defmacrop|defmacro|defguardp|defguard|defdelegate)$"))
|
||||
|
||||
(anonymous_function
|
||||
(stab_expression
|
||||
left: (bare_arguments
|
||||
[(identifier) @variable.parameter
|
||||
(_ (identifier) @variable.parameter)
|
||||
(_ (_ (identifier) @variable.parameter))
|
||||
(_ (_ (_ (identifier) @variable.parameter)))
|
||||
(_ (_ (_ (_ (identifier) @variable.parameter))))
|
||||
(_ (_ (_ (_ (_ (identifier) @variable.parameter)))))]))
|
||||
(#match? @variable.parameter "^[^_]"))
|
||||
|
||||
(unary_op
|
||||
operator: "@"
|
||||
(call (identifier) @attribute
|
||||
(heredoc
|
||||
[(heredoc_start)
|
||||
(heredoc_content)
|
||||
(heredoc_end)] @doc))
|
||||
(#match? @attribute "^(doc|moduledoc)$"))
|
||||
|
||||
(module) @type
|
||||
|
||||
(unary_op
|
||||
operator: "@" @attribute
|
||||
[(call
|
||||
function: (function_identifier) @attribute)
|
||||
(identifier) @attribute])
|
||||
|
||||
(unary_op
|
||||
operator: _ @operator)
|
||||
|
||||
(binary_op
|
||||
operator: _ @operator)
|
||||
|
||||
(heredoc
|
||||
[(heredoc_start)
|
||||
(heredoc_content)
|
||||
(heredoc_end)] @string)
|
||||
|
||||
(string
|
||||
[(string_start)
|
||||
(string_content)
|
||||
(string_end)] @string)
|
||||
|
||||
(sigil_start) @string.special
|
||||
(sigil_content) @string
|
||||
(sigil_end) @string.special
|
||||
|
||||
(interpolation
|
||||
"#{" @punctuation.special
|
||||
"}" @punctuation.special)
|
||||
|
||||
[
|
||||
","
|
||||
"->"
|
||||
"."
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
"<<"
|
||||
">>"
|
||||
] @punctuation.bracket
|
||||
|
||||
[(identifier) @function.special
|
||||
(#match? @function.special "^__.+__$")]
|
||||
|
||||
[(remote_identifier) @function.special
|
||||
(#match? @function.special "^__.+__$")]
|
||||
|
||||
[(identifier) @comment
|
||||
(#match? @comment "^_")]
|
||||
|
||||
(ERROR) @warning
|
Reference in New Issue
Block a user