Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support determining mode based on shebang interpreter directive #47

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions zee-grammar/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ libloading = "0.7.3"
log = "0.4.16"
once_cell = { version = "1.10.0", features = ["parking_lot"] }
rayon = "1.5.2"
regex = "1.5.5"
serde = "1.0.136"
serde_derive = "1.0.136"
tree-sitter = "0.20.6"
2 changes: 2 additions & 0 deletions zee-grammar/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub struct ModeConfig {
pub comment: Option<CommentConfig>,
pub indentation: IndentationConfig,
pub grammar: Option<GrammarConfig>,
#[serde(default)]
pub shebangs: Vec<String>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking whether this should be a new field, rather than a new variant of FilenamePattern -- granted, we should probably rename it to FilePattern as it will look inside the file too to determine if the mode applies. I.e. something like FilePattern::Shebang.

The structure I suggest may make it harder to avoid reading the file when the filename would suffice.

}

#[derive(Clone, Debug, Deserialize, Serialize)]
Expand Down
15 changes: 15 additions & 0 deletions zee-grammar/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ mod git;

use anyhow::Result;
use once_cell::sync::Lazy;
use regex::Regex;
use std::path::Path;
use tree_sitter::{Language, Query};

use self::config::{CommentConfig, FilenamePattern, IndentationConfig, ModeConfig};

static SHEBANG_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^#!\s*(?:\S*[/\\](?:env\s+(?:\-\S+\s+)*)?)?([^\s\.\d]+)").unwrap());

#[derive(Debug)]
pub struct Mode {
pub name: String,
Expand All @@ -19,6 +23,7 @@ pub struct Mode {
pub comment: Option<CommentConfig>,
pub indentation: IndentationConfig,
grammar: LazyGrammar,
pub shebangs: Vec<String>,
}

impl Mode {
Expand All @@ -31,6 +36,7 @@ impl Mode {
comment,
indentation,
grammar: grammar_config,
shebangs,
} = config;
Self {
name,
Expand All @@ -44,6 +50,7 @@ impl Mode {
.map(|grammar_config| grammar_config.grammar_id)
.map(builder::load_grammar)
})),
shebangs,
}
}

Expand All @@ -53,6 +60,13 @@ impl Mode {
.any(|pattern| pattern.matches(filename.as_ref()))
}

pub fn matches_by_shebang(&self, shebang: &str) -> bool {
SHEBANG_REGEX
.captures(shebang)
.and_then(|captures| self.shebangs.contains(&captures[1].into()).then(|| 0))
.is_some()
}

pub fn language(&self) -> Option<Result<Language, &anyhow::Error>> {
Some(self.grammar()?.map(|parser| parser.language))
}
Expand All @@ -74,6 +88,7 @@ impl Default for Mode {
comment: None,
indentation: Default::default(),
grammar: Lazy::new(Box::new(|| None)),
shebangs: vec![],
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions zee/config/config.ron
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
width: 4,
unit: Space,
),
shebangs: ["node"],
grammar: Some(
Grammar(
id: "javascript",
Expand Down Expand Up @@ -248,6 +249,7 @@
width: 4,
unit: Space,
),
shebangs: ["python"],
iainh marked this conversation as resolved.
Show resolved Hide resolved
grammar: Some(
Grammar(
id: "python",
Expand Down Expand Up @@ -464,6 +466,7 @@
width: 2,
unit: Space,
),
shebangs: ["sh", "bash", "dash", "zsh"],
grammar: Some(
Grammar(
id: "bash",
Expand Down
12 changes: 7 additions & 5 deletions zee/src/editor/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ use zee_grammar::Mode;

use super::{ContextHandle, Editor};
use crate::{
config::PLAIN_TEXT_MODE,
error::Result,
syntax::parse::{ParseTree, ParserPool, ParserStatus},
versioned::{Versioned, WeakHandle},
};

const MAX_SHEBANG_LENGTH: usize = 256;

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BufferId(usize);

Expand Down Expand Up @@ -173,10 +174,11 @@ impl Buffer {
file_path: Option<PathBuf>,
repo: Option<RepositoryRc>,
) -> Self {
let mode = file_path
.as_ref()
.map(|path| context.0.mode_by_filename(path))
.unwrap_or(&PLAIN_TEXT_MODE);
let shebang_range = 0..std::cmp::min(text.len_chars(), MAX_SHEBANG_LENGTH);

let mode = context
.0
.mode_by_file(file_path.as_ref(), &text.slice(shebang_range));

let mut parser = mode
.language()
Expand Down
23 changes: 16 additions & 7 deletions zee/src/editor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ mod windows;
pub use self::buffer::{BufferId, ModifiedStatus};

use git2::Repository;
use ropey::Rope;
use ropey::{Rope, RopeSlice};
use std::{
borrow::Cow,
fmt::Display,
fs::File,
io::{self, BufReader},
path::{Path, PathBuf},
path::PathBuf,
sync::Arc,
};
use zi::{
Expand Down Expand Up @@ -94,11 +94,20 @@ pub struct Context {
}

impl Context {
pub fn mode_by_filename(&self, filename: impl AsRef<Path>) -> &Mode {
self.modes
.iter()
.find(|&mode| mode.matches_by_filename(filename.as_ref()))
.unwrap_or(&PLAIN_TEXT_MODE)
pub fn mode_by_file(&self, path: Option<&PathBuf>, content: &RopeSlice) -> &Mode {
path.and_then(|path| {
self.modes
.iter()
.find(|&mode| mode.matches_by_filename(path))
})
.or_else(|| {
content.as_str().and_then(|shebang| {
self.modes
.iter()
.find(|&mode| mode.matches_by_shebang(shebang))
})
})
.unwrap_or(&PLAIN_TEXT_MODE)
}
}

Expand Down