Skip to content
This repository has been archived by the owner on Feb 12, 2018. It is now read-only.

Commit

Permalink
Merge pull request trishume#6 from trishume/highlight-files-struct
Browse files Browse the repository at this point in the history
Highlight files struct
  • Loading branch information
trishume authored Jun 16, 2016
2 parents 5d21b9a + 0fc75cd commit e841371
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 20 deletions.
4 changes: 2 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ It is currently mostly complete and can parse, interpret and highlight based on
- [x] Work with many languages (accomplished through using existing grammar formats)
- [ ] Highlight super quickly, as fast as Sublime Text (not there yet but matching most editors)
- [x] Load up quickly, currently in around 23ms but could potentially be even faster.
- [ ] Include easy to use API for basic cases
- [x] Include easy to use API for basic cases
- [x] API allows use in fancy text editors with piece tables and incremental re-highlighting and the like.
- [x] Expose internals of the parsing process so text editors can do things like cache parse states and use semantic info for code intelligence
- [x] High quality highlighting, supporting things like heredocs and complex syntaxes (like Rust's).
Expand All @@ -34,7 +34,7 @@ There's currently an example program called `syncat` that prints one of the sour
- [x] Highlight a scope-annotated iterator into a colour-annotated iterator for display.
- [x] Ability to dump loaded packages as binary file and load them with lazy regex compilation for fast start up times.
- [x] Bundle dumped default syntaxes into the library binary so library users don't need an assets folder with Sublime Text packages.
- [ ] Add nice API wrappers for simple use cases. The base APIs are designed for deep high performance integration with arbitrary text editor data structures.
- [x] Add nice API wrappers for simple use cases. The base APIs are designed for deep high performance integration with arbitrary text editor data structures.
- [ ] Make syncat a better demo, and maybe more demo programs
- [ ] Document the API better and make things private that don't need to be public
- [ ] Add sRGB colour correction (not sure if this is necessary, could be the job of the text editor)
Expand Down
Binary file modified assets/default_newlines.packdump
Binary file not shown.
Binary file modified assets/default_nonewlines.packdump
Binary file not shown.
2 changes: 2 additions & 0 deletions examples/gendata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ use syntect::dumps::*;

fn main() {
let mut ps = SyntaxSet::new();
ps.load_plain_text_syntax();
ps.load_syntaxes("testdata/Packages", true).unwrap();
dump_to_file(&ps, "assets/default_newlines.packdump").unwrap();

let mut ps2 = SyntaxSet::new();
ps2.load_plain_text_syntax();
ps2.load_syntaxes("testdata/Packages", false).unwrap();
dump_to_file(&ps2, "assets/default_nonewlines.packdump").unwrap();

Expand Down
22 changes: 6 additions & 16 deletions examples/syncat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ extern crate syntect;
use syntect::parsing::SyntaxSet;
use syntect::highlighting::{ThemeSet, Style};
use syntect::util::as_24_bit_terminal_escaped;
use syntect::easy::HighlightLines;
use syntect::easy::HighlightFile;

use std::io::BufReader;
use std::io::BufRead;
use std::path::Path;
use std::fs::File;

fn main() {
let ps = SyntaxSet::load_defaults_nonewlines();
let ss = SyntaxSet::load_defaults_nonewlines();
let ts = ThemeSet::load_defaults();

let args: Vec<String> = std::env::args().collect();
Expand All @@ -19,17 +16,10 @@ fn main() {
return;
}

let path = Path::new(&args[1]);
let extension = path.extension().unwrap().to_str().unwrap();
let f = File::open(path).unwrap();
let file = BufReader::new(&f);

let syntax = ps.find_syntax_by_extension(extension).unwrap();
let mut highlighter = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]);
for maybe_line in file.lines() {
let mut highlighter = HighlightFile::new(&args[1], &ss, &ts.themes["base16-ocean.dark"]).unwrap();
for maybe_line in highlighter.reader.lines() {
let line = maybe_line.unwrap();
let regions: Vec<(Style, &str)> = highlighter.highlight(&line);
let escaped = as_24_bit_terminal_escaped(&regions[..], true);
println!("{}", escaped);
let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight(&line);
println!("{}", as_24_bit_terminal_escaped(&regions[..], true));
}
}
57 changes: 56 additions & 1 deletion src/easy.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use parsing::{ScopeStack, ParseState, SyntaxDefinition};
use parsing::{ScopeStack, ParseState, SyntaxDefinition, SyntaxSet};
use highlighting::{Highlighter, HighlightState, HighlightIterator, Theme, Style};
use std::io::{BufReader, self};
use std::fs::File;
use std::path::Path;
// use util::debug_print_ops;

/// Simple way to go directly from lines of text to coloured
Expand Down Expand Up @@ -58,6 +61,51 @@ impl<'a> HighlightLines<'a> {
}
}

/// Convenience struct containing everything you need to highlight a file.
/// Use the `reader` to get the lines of the file and the `highlight_lines` to highlight them.
/// See the `new` method docs for more information.
pub struct HighlightFile<'a> {
pub reader: BufReader<File>,
pub highlight_lines: HighlightLines<'a>,
}

impl<'a> HighlightFile<'a> {
/// Constructs a file reader and a line highlighter to get you reading files as fast as possible.
/// Auto-detects the syntax from the extension and constructs a `HighlightLines` with the correct syntax and theme.
///
/// # Examples
///
/// ```
/// use syntect::parsing::SyntaxSet;
/// use syntect::highlighting::{ThemeSet, Style};
/// use syntect::util::as_24_bit_terminal_escaped;
/// use syntect::easy::HighlightFile;
/// use std::io::BufRead;
///
/// let ss = SyntaxSet::load_defaults_nonewlines();
/// let ts = ThemeSet::load_defaults();
///
/// let mut highlighter = HighlightFile::new("testdata/highlight_test.erb", &ss, &ts.themes["base16-ocean.dark"]).unwrap();
/// for maybe_line in highlighter.reader.lines() {
/// let line = maybe_line.unwrap();
/// let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight(&line);
/// println!("{}", as_24_bit_terminal_escaped(&regions[..], true));
/// }
/// ```
pub fn new<P: AsRef<Path>>(path_obj: P, ss: &SyntaxSet, theme: &'a Theme) -> io::Result<HighlightFile<'a>> {
let path: &Path = path_obj.as_ref();
let extension = path.extension().and_then(|x| x.to_str()).unwrap_or("");
let f = try!(File::open(path));
let reader = BufReader::new(f);
// TODO use first line detection
let syntax = ss.find_syntax_by_extension(extension).unwrap_or_else(|| ss.find_syntax_plain_text());
Ok(HighlightFile {
reader: reader,
highlight_lines: HighlightLines::new(syntax, theme),
})
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -72,4 +120,11 @@ mod tests {
let ranges = h.highlight("pub struct Wow { hi: u64 }");
assert!(ranges.len() > 4);
}

#[test]
fn can_highlight_file() {
let ss = SyntaxSet::load_defaults_nonewlines();
let ts = ThemeSet::load_defaults();
HighlightFile::new("testdata/highlight_test.erb", &ss, &ts.themes["base16-ocean.dark"]).unwrap();
}
}
32 changes: 31 additions & 1 deletion src/parsing/syntax_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ impl SyntaxSet {
Ok(())
}

/// Rarely useful method that loads in a syntax with no highlighting rules for plain text.
/// Exists mainly for adding the plain text syntax to syntax set dumps, because for some
/// reason the default Sublime plain text syntax is still in `.tmLanguage` format.
pub fn load_plain_text_syntax(&mut self) {
let s = "---\nname: Plain Text\nfile_extensions: [txt]\nscope: text.plain\ncontexts: {main: []}";
let syn = SyntaxDefinition::load_from_str(&s, false).unwrap();
self.syntaxes.push(syn);
}

pub fn find_syntax_by_scope<'a>(&'a self, scope: Scope) -> Option<&'a SyntaxDefinition> {
self.syntaxes.iter().find(|&s| s.scope == scope)
}
Expand All @@ -106,6 +115,25 @@ impl SyntaxSet {
self.syntaxes.iter().find(|&s| lower == s.name.to_ascii_lowercase())
}

/// Finds a syntax for plain text, which usually has no highlighting rules.
/// Good as a fallback when you can't find another syntax but you still want
/// to use the same highlighting pipeline code.
///
/// This syntax should always be present, if not this method will panic.
/// If the way you load syntaxes doesn't create one, use `load_plain_text_syntax`.
///
/// # Examples
/// ```
/// use syntect::parsing::SyntaxSet;
/// let mut ss = SyntaxSet::new();
/// ss.load_plain_text_syntax();
/// let syntax = ss.find_syntax_by_token("rs").unwrap_or_else(|| ss.find_syntax_plain_text());
/// assert_eq!(syntax.name, "Plain Text");
/// ```
pub fn find_syntax_plain_text<'a>(&'a self) -> &'a SyntaxDefinition {
self.find_syntax_by_name("Plain Text").expect("All syntax sets ought to have a plain text syntax")
}

/// This links all the syntaxes in this set directly with pointers for performance purposes.
/// It is necessary to do this before parsing anything with these syntaxes.
/// However, it is not possible to serialize a syntax set that has been linked,
Expand Down Expand Up @@ -182,9 +210,11 @@ mod tests {
use parsing::{Scope, syntax_definition};
#[test]
fn can_load() {
let ps = SyntaxSet::load_from_folder("testdata/Packages").unwrap();
let mut ps = SyntaxSet::load_from_folder("testdata/Packages").unwrap();
ps.load_plain_text_syntax();
let rails_scope = Scope::new("source.ruby.rails").unwrap();
let syntax = ps.find_syntax_by_name("Ruby on Rails").unwrap();
ps.find_syntax_plain_text();
assert_eq!(&ps.find_syntax_by_extension("rake").unwrap().name, "Ruby");
assert_eq!(&ps.find_syntax_by_token("ruby").unwrap().name, "Ruby");
// println!("{:#?}", syntax);
Expand Down

0 comments on commit e841371

Please sign in to comment.