From d8b813c0bf1c88a11f1f49ffbd4b7328a02807a6 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Sun, 8 Aug 2021 08:26:17 +0200 Subject: [PATCH] When returning a SyntaxReference, also return the SyntaxSet that contains it (#1776) To improve startup performance, we will later load smaller `SyntaxSet`s instead of one giant one. However, the current API assumes only one `SyntaxSet` is ever used, and that that implicitly is the `SyntaxSet` from which returned `SyntaxReference`s comes. This change changes the API to reflect that `SyntaxSet` and `SyntaxReference` are tightly coupled, and enables the use of several `SyntaxSet`. --- CHANGELOG.md | 2 +- src/assets.rs | 78 +++++++++++++++++++++++++++++---------------- src/bin/bat/main.rs | 6 ++-- src/printer.rs | 30 ++++++++++------- 4 files changed, 73 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b15745c8..e78f8147 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ ## `bat` as a library -- Deprecate `HighlightingAssets::syntaxes()` and `HighlightingAssets::syntax_for_file_name()`. Use `HighlightingAssets::get_syntaxes()` and `HighlightingAssets::get_syntax_for_file_name()` instead. They return a `Result` which is needed for upcoming lazy-loading work to improve startup performance. See #1747 and #1755 (@Enselic) +- Deprecate `HighlightingAssets::syntaxes()` and `HighlightingAssets::syntax_for_file_name()`. Use `HighlightingAssets::get_syntaxes()` and `HighlightingAssets::get_syntax_for_file_name()` instead. They return a `Result` which is needed for upcoming lazy-loading work to improve startup performance. They also return what `SyntaxSet` the returned `SyntaxReference` belongs to. See #1747, #1755 and #1776 (@Enselic) diff --git a/src/assets.rs b/src/assets.rs index 7c8273fb..ba60274d 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -25,6 +25,12 @@ pub struct HighlightingAssets { fallback_theme: Option<&'static str>, } +#[derive(Debug)] +pub struct SyntaxReferenceInSet<'a> { + pub syntax: &'a SyntaxReference, + pub syntax_set: &'a SyntaxSet, +} + const IGNORED_SUFFIXES: [&str; 10] = [ // Editor etc backups "~", @@ -217,21 +223,26 @@ impl HighlightingAssets { file_name: impl AsRef, mapping: &SyntaxMapping, ) -> Option<&SyntaxReference> { - self.get_syntax_for_file_name(file_name, mapping).expect( - ".syntax_for_file_name() is deprecated, use .get_syntax_for_file_name() instead", - ) + self.get_syntax_for_file_name(file_name, mapping) + .expect( + ".syntax_for_file_name() is deprecated, use .get_syntax_for_file_name() instead", + ) + .map(|syntax_in_set| syntax_in_set.syntax) } pub fn get_syntax_for_file_name( &self, file_name: impl AsRef, mapping: &SyntaxMapping, - ) -> Result> { + ) -> Result> { let file_name = file_name.as_ref(); Ok(match mapping.get_syntax_for(file_name) { Some(MappingTarget::MapToUnknown) => None, Some(MappingTarget::MapTo(syntax_name)) => { - self.get_syntax_set()?.find_syntax_by_name(syntax_name) + let syntax_set = self.get_syntax_set()?; + syntax_set + .find_syntax_by_name(syntax_name) + .map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }) } None => self.get_extension_syntax(file_name.as_os_str())?, }) @@ -259,10 +270,12 @@ impl HighlightingAssets { language: Option<&str>, input: &mut OpenedInput, mapping: &SyntaxMapping, - ) -> Result<&SyntaxReference> { + ) -> Result { if let Some(language) = language { - self.get_syntax_set()? + let syntax_set = self.get_syntax_set()?; + syntax_set .find_syntax_by_token(language) + .map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }) .ok_or_else(|| ErrorKind::UnknownSyntax(language.to_owned()).into()) } else { let line_syntax = self.get_first_line_syntax(&mut input.reader)?; @@ -293,10 +306,13 @@ impl HighlightingAssets { ErrorKind::UndetectedSyntax(path.to_string_lossy().into()).into() }), - Some(MappingTarget::MapTo(syntax_name)) => self - .get_syntax_set()? - .find_syntax_by_name(syntax_name) - .ok_or_else(|| ErrorKind::UnknownSyntax(syntax_name.to_owned()).into()), + Some(MappingTarget::MapTo(syntax_name)) => { + let syntax_set = self.get_syntax_set()?; + syntax_set + .find_syntax_by_name(syntax_name) + .map(|syntax| SyntaxReferenceInSet { syntax, syntax_set }) + .ok_or_else(|| ErrorKind::UnknownSyntax(syntax_name.to_owned()).into()) + } None => { let file_name = path.file_name().unwrap_or_default(); @@ -314,7 +330,7 @@ impl HighlightingAssets { } } - fn get_extension_syntax(&self, file_name: &OsStr) -> Result> { + fn get_extension_syntax(&self, file_name: &OsStr) -> Result> { let mut syntax = self.find_syntax_by_file_name(file_name)?; if syntax.is_none() { syntax = self.find_syntax_by_file_name_extension(file_name)?; @@ -325,23 +341,27 @@ impl HighlightingAssets { Ok(syntax) } - fn find_syntax_by_file_name(&self, file_name: &OsStr) -> Result> { - Ok(self - .get_syntax_set()? - .find_syntax_by_extension(file_name.to_str().unwrap_or_default())) + fn find_syntax_by_file_name(&self, file_name: &OsStr) -> Result> { + let syntax_set = self.get_syntax_set()?; + Ok(syntax_set + .find_syntax_by_extension(file_name.to_str().unwrap_or_default()) + .map(|syntax| SyntaxReferenceInSet { syntax, syntax_set })) } fn find_syntax_by_file_name_extension( &self, file_name: &OsStr, - ) -> Result> { + ) -> Result> { let file_path = Path::new(file_name); - Ok(self.get_syntax_set()?.find_syntax_by_extension( - file_path - .extension() - .and_then(|x| x.to_str()) - .unwrap_or_default(), - )) + let syntax_set = self.get_syntax_set()?; + Ok(syntax_set + .find_syntax_by_extension( + file_path + .extension() + .and_then(|x| x.to_str()) + .unwrap_or_default(), + ) + .map(|syntax| SyntaxReferenceInSet { syntax, syntax_set })) } /// If we find an ignored suffix on the file name, e.g. '~', we strip it and @@ -349,7 +369,7 @@ impl HighlightingAssets { fn get_extension_syntax_with_stripped_suffix( &self, file_name: &OsStr, - ) -> Result> { + ) -> Result> { let file_path = Path::new(file_name); let mut syntax = None; if let Some(file_str) = file_path.to_str() { @@ -363,11 +383,15 @@ impl HighlightingAssets { Ok(syntax) } - fn get_first_line_syntax(&self, reader: &mut InputReader) -> Result> { + fn get_first_line_syntax( + &self, + reader: &mut InputReader, + ) -> Result> { let syntax_set = self.get_syntax_set()?; Ok(String::from_utf8(reader.first_line.clone()) .ok() - .and_then(|l| syntax_set.find_syntax_by_first_line(&l))) + .and_then(|l| syntax_set.find_syntax_by_first_line(&l)) + .map(|syntax| SyntaxReferenceInSet { syntax, syntax_set })) } } @@ -462,7 +486,7 @@ mod tests { ) -> String { self.assets .get_syntax(language, input, mapping) - .map(|syntax| syntax.name.clone()) + .map(|syntax_in_set| syntax_in_set.syntax.name.clone()) .unwrap_or_else(|_| "!no syntax!".to_owned()) } diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index 7369dc03..173ac551 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -101,11 +101,11 @@ pub fn get_languages(config: &Config) -> Result { true } else { let test_file = Path::new("test").with_extension(extension); - let syntax = assets + let syntax_in_set = assets .get_syntax_for_file_name(test_file, &config.syntax_mapping) .unwrap(); // safe since .get_syntaxes() above worked - match syntax { - Some(syntax) => syntax.name == lang_name, + match syntax_in_set { + Some(syntax_in_set) => syntax_in_set.syntax.name == lang_name, None => false, } } diff --git a/src/printer.rs b/src/printer.rs index 22d7c22d..f07d2db1 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -18,7 +18,7 @@ use encoding::{DecoderTrap, Encoding}; use unicode_width::UnicodeWidthChar; -use crate::assets::HighlightingAssets; +use crate::assets::{HighlightingAssets, SyntaxReferenceInSet}; use crate::config::Config; #[cfg(feature = "git")] use crate::decorations::LineChangesDecoration; @@ -163,23 +163,29 @@ impl<'a> InteractivePrinter<'a> { panel_width = 0; } - let highlighter = if input + let (highlighter, syntax_set) = if input .reader .content_type .map_or(false, |c| c.is_binary() && !config.show_nonprintable) { - None + (None, assets.get_syntax_set()?) } else { // Determine the type of syntax for highlighting - let syntax = match assets.get_syntax(config.language, input, &config.syntax_mapping) { - Ok(syntax) => syntax, - Err(Error(ErrorKind::UndetectedSyntax(_), _)) => { - assets.get_syntax_set()?.find_syntax_plain_text() - } - Err(e) => return Err(e), - }; + let syntax_in_set = + match assets.get_syntax(config.language, input, &config.syntax_mapping) { + Ok(syntax_in_set) => syntax_in_set, + Err(Error(ErrorKind::UndetectedSyntax(_), _)) => { + let syntax_set = assets.get_syntax_set()?; + let syntax = syntax_set.find_syntax_plain_text(); + SyntaxReferenceInSet { syntax, syntax_set } + } + Err(e) => return Err(e), + }; - Some(HighlightLines::new(syntax, theme)) + ( + Some(HighlightLines::new(syntax_in_set.syntax, theme)), + syntax_in_set.syntax_set, + ) }; Ok(InteractivePrinter { @@ -192,7 +198,7 @@ impl<'a> InteractivePrinter<'a> { #[cfg(feature = "git")] line_changes, highlighter, - syntax_set: assets.get_syntax_set()?, + syntax_set, background_color_highlight, }) }