Skip to content

Commit

Permalink
feat(hl): detect syntax based on file extension
Browse files Browse the repository at this point in the history
  • Loading branch information
elkowar committed Feb 5, 2023
1 parent f54ca17 commit 30c18eb
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 8 deletions.
30 changes: 28 additions & 2 deletions crates/bazed-core/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@

use nonempty::NonEmpty;
use syntect::parsing::ScopeStack;
use tap::TapOptional;
use xi_rope::{engine::Engine, spans::Spans, DeltaBuilder, LinesMetric, Rope, RopeDelta, RopeInfo};

use self::{
buffer_regions::BufferRegions, movement::apply_motion_to_region, position::Position,
undo_history::UndoHistory,
};
use crate::{
highlighting::{self, Annotations, Parser},
highlighting::{self, Annotations, Parser, SyntaxQuery},
region::Region,
user_buffer_op::{BufferOp, EditType, Motion},
view::Viewport,
Expand All @@ -39,9 +40,23 @@ pub struct Buffer {
last_edit_type: EditType,
syntax_parser: highlighting::Parser,
annotations: Annotations,
file_extension: Option<String>,
}

impl Buffer {
pub fn new(s: String, file_extension: Option<String>) -> Self {
let rope = Rope::from(s);
Self {
engine: Engine::new(rope.clone()),
text: rope,
regions: BufferRegions::default(),
undo_history: UndoHistory::default(),
last_edit_type: EditType::Other,
syntax_parser: Parser::new(),
annotations: Annotations::default(),
file_extension,
}
}
pub fn new_from_string(s: String) -> Self {
let rope = Rope::from(s);
Self {
Expand All @@ -52,6 +67,7 @@ impl Buffer {
last_edit_type: EditType::Other,
syntax_parser: Parser::new(),
annotations: Annotations::default(),
file_extension: None,
}
}

Expand Down Expand Up @@ -140,7 +156,17 @@ impl Buffer {
}

pub fn reparse_text(&mut self) {
self.annotations.set(self.syntax_parser.parse(&self.text))
let syntax = self
.file_extension
.as_ref()
.and_then(|ext| {
self.syntax_parser
.find_syntax(SyntaxQuery::Extension(ext))
.tap_none(|| tracing::warn!("No syntax definition found for {ext} files"))
})
.unwrap_or_else(|| self.syntax_parser.plain_text_syntax());
self.annotations
.set(self.syntax_parser.parse(&self.text, syntax))
}

/// Snap all regions to the closest valid points in the buffer.
Expand Down
3 changes: 2 additions & 1 deletion crates/bazed-core/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ impl Document {

pub fn open_file(path: PathBuf) -> std::io::Result<Document> {
let content = std::fs::read_to_string(&path)?;
let extension = path.extension().map(|x| x.to_string_lossy().to_string());
Ok(Self {
path: Some(path),
buffer: Buffer::new_from_string(content),
buffer: Buffer::new(content, extension),
})
}

Expand Down
46 changes: 41 additions & 5 deletions crates/bazed-core/src/highlighting.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use syntect::parsing::{ParseState, ScopeStack, SyntaxSet};
use syntect::parsing::{ParseState, ScopeStack, SyntaxReference, SyntaxSet};
use xi_rope::{
spans::{Spans, SpansBuilder},
tree::NodeInfo,
Expand All @@ -25,6 +25,22 @@ impl Annotations {
}
}

#[allow(unused)]
#[derive(Debug, Eq, PartialEq)]
pub(crate) enum SyntaxQuery<'a> {
/// Find a syntax by the file extension it corresponds to
Extension(&'a str),
/// Find a syntax by name
Name(&'a str),
/// Find a syntax by trying to identify based on the first line in the file
FirstLine(&'a str),
/// Try first by extension and then case-insensitive name
/// Useful for things like markdown codeblocks, where you may get `rust`, `rs`, etc
Token(&'a str),
/// Use the plain-text syntax
PlainText,
}

#[derive(Debug, Default)]
pub(crate) struct Parser {
syntax_set: SyntaxSet,
Expand All @@ -37,8 +53,25 @@ impl Parser {
}
}

pub(crate) fn parse(&self, rope: &Rope) -> Spans<ScopeStack> {
let syntax_reference = self.syntax_set.find_syntax_by_extension("rs").unwrap();
pub(crate) fn find_syntax(&self, query: SyntaxQuery) -> Option<&SyntaxReference> {
match query {
SyntaxQuery::Extension(x) => self.syntax_set.find_syntax_by_extension(x),
SyntaxQuery::Name(x) => self.syntax_set.find_syntax_by_name(x),
SyntaxQuery::FirstLine(x) => self.syntax_set.find_syntax_by_first_line(x),
SyntaxQuery::Token(x) => self.syntax_set.find_syntax_by_token(x),
SyntaxQuery::PlainText => Some(self.syntax_set.find_syntax_plain_text()),
}
}

pub(crate) fn plain_text_syntax(&self) -> &SyntaxReference {
self.syntax_set.find_syntax_plain_text()
}

pub(crate) fn parse(
&self,
rope: &Rope,
syntax_reference: &SyntaxReference,
) -> Spans<ScopeStack> {
let mut state = ParseState::new(syntax_reference);
let mut spans: SpansBuilder<ScopeStack> = SpansBuilder::new(rope.len());
let mut start_of_line = 0;
Expand Down Expand Up @@ -70,7 +103,7 @@ mod test {
use syntect::parsing::ScopeStack;
use xi_rope::Rope;

use crate::highlighting::Parser;
use crate::highlighting::{Parser, SyntaxQuery};

macro_rules! scopes {
($($x:literal),*) => { ScopeStack::from_vec(vec![$($x.parse().unwrap()),*]) }
Expand All @@ -96,7 +129,10 @@ mod test {
]),
((11..11), scopes!["source.rust"]),
];
let actual = parser.parse(&text);
let actual = parser.parse(
&text,
parser.find_syntax(SyntaxQuery::Name("Rust")).unwrap(),
);
let actual = actual
.iter()
.map(|(a, b)| ((a.start..a.end), b.clone()))
Expand Down

0 comments on commit 30c18eb

Please sign in to comment.