ndg_commonmark/syntax/
mod.rs1pub mod error;
9pub mod types;
10
11pub use error::{SyntaxError, SyntaxResult};
13pub use types::{SyntaxConfig, SyntaxHighlighter, SyntaxManager};
14
15#[cfg(all(feature = "syntastica", feature = "syntect"))]
17compile_error!(
18 "Cannot enable both 'syntastica' and 'syntect' features simultaneously. \
19 They are mutually exclusive."
20);
21
22#[cfg(feature = "syntastica")] mod syntastica;
24#[cfg(feature = "syntastica")] pub use syntastica::*;
25
26#[cfg(feature = "syntect")] mod syntect;
28#[cfg(feature = "syntect")] pub use syntect::*;
29
30pub fn create_default_manager(
46 syntax_queries_dir: Option<&std::path::Path>,
47) -> SyntaxResult<SyntaxManager> {
48 #[cfg(all(feature = "syntastica", feature = "syntect"))]
50 {
51 return Err(SyntaxError::MutuallyExclusiveBackends);
52 }
53
54 #[cfg(feature = "syntastica")]
55 {
56 create_syntastica_manager(syntax_queries_dir)
57 }
58
59 #[cfg(feature = "syntect")]
60 {
61 return create_syntect_manager();
62 }
63
64 #[cfg(not(any(feature = "syntastica", feature = "syntect")))]
65 {
66 Err(SyntaxError::NoBackendAvailable)
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::{types::*, *};
73
74 #[test]
75 fn test_syntax_config_default() {
76 let config = SyntaxConfig::default();
77 assert!(config.fallback_to_plain);
78 assert!(config.language_aliases.contains_key("js"));
79 assert_eq!(config.language_aliases["js"], "javascript");
80 }
81
82 #[cfg(feature = "syntect")]
83 #[test]
84 fn test_syntect_highlighter() {
85 let highlighter = SyntectHighlighter::default();
86 assert_eq!(highlighter.name(), "Syntect");
87 assert!(!highlighter.supported_languages().is_empty());
88 assert!(!highlighter.available_themes().is_empty());
89 }
90
91 #[cfg(feature = "syntect")]
92 #[test]
93 fn test_syntect_highlight_simple() {
94 let highlighter = SyntectHighlighter::default();
95 let result = highlighter.highlight("fn main() {}", "rust", None);
96 assert!(result.is_ok());
97 let html = result.expect("Failed to highlight code");
98 assert!(html.contains("main"));
99 }
100
101 #[cfg(feature = "syntastica")]
102 #[test]
103 fn test_syntastica_highlighter() {
104 let highlighter = SyntasticaHighlighter::new(None)
105 .expect("Failed to create SyntasticaHighlighter");
106 assert_eq!(highlighter.name(), "Syntastica");
107 assert!(!highlighter.supported_languages().is_empty());
108 assert!(!highlighter.available_themes().is_empty());
109 }
110
111 #[cfg(feature = "syntastica")]
112 #[test]
113 fn test_syntastica_highlight_simple() {
114 let highlighter = SyntasticaHighlighter::new(None)
115 .expect("Failed to create SyntasticaHighlighter");
116 let result = highlighter.highlight("fn main() {}", "rust", None);
117 assert!(result.is_ok());
118 let html = result.expect("Failed to highlight code");
119 assert!(html.contains("main"));
120 }
121
122 #[cfg(any(feature = "syntastica", feature = "syntect"))]
123 #[test]
124 fn test_syntax_manager() {
125 let manager = create_default_manager(None)
126 .expect("Failed to create default syntax manager");
127 assert!(!manager.highlighter().supported_languages().is_empty());
128
129 let resolved = manager.resolve_language("js");
130 assert_eq!(resolved, "javascript");
131 }
132
133 #[cfg(any(feature = "syntastica", feature = "syntect"))]
134 #[test]
135 fn test_language_resolution() {
136 let manager = create_default_manager(None)
137 .expect("Failed to create default syntax manager");
138
139 assert_eq!(manager.resolve_language("js"), "javascript");
141 assert_eq!(manager.resolve_language("py"), "python");
142 assert_eq!(manager.resolve_language("ts"), "typescript");
143
144 assert_eq!(manager.resolve_language("rust"), "rust");
146 assert_eq!(manager.resolve_language("nix"), "nix");
147 }
148
149 #[test]
150 fn test_modular_access_to_syntax_types() {
151 use super::{
153 error::{SyntaxError, SyntaxResult},
154 types::SyntaxConfig,
155 };
156
157 let error = SyntaxError::UnsupportedLanguage("test".to_string());
159 assert!(matches!(error, SyntaxError::UnsupportedLanguage(_)));
160
161 let result: SyntaxResult<String> = Err(SyntaxError::NoBackendAvailable);
163 assert!(result.is_err());
164
165 let config = SyntaxConfig::default();
167 assert!(config.fallback_to_plain);
168 assert!(config.language_aliases.contains_key("js"));
169
170 let _config2: SyntaxConfig = SyntaxConfig::default();
172 let _error2: SyntaxError = SyntaxError::BackendError("test".to_string());
173 }
174
175 #[cfg(any(feature = "syntastica", feature = "syntect"))]
176 #[test]
177 fn test_extended_theme_availability() {
178 let manager = create_default_manager(None)
179 .expect("Failed to create default syntax manager");
180 let themes = manager.highlighter().available_themes();
181
182 assert!(
184 themes.len() > 30,
185 "Expected > 30 themes, got {}",
186 themes.len()
187 );
188
189 #[cfg(feature = "syntastica")]
191 {
192 assert!(
193 themes.contains(&"github::dark".to_string()),
194 "Expected github::dark theme"
195 );
196 assert!(
197 themes.contains(&"gruvbox::dark".to_string()),
198 "Expected gruvbox::dark theme"
199 );
200 assert!(
201 themes.contains(&"nord::nord".to_string()),
202 "Expected nord::nord theme"
203 );
204 assert!(
205 themes.contains(&"dracula::dracula".to_string()),
206 "Expected dracula::dracula theme"
207 );
208 }
209
210 #[cfg(feature = "syntect")]
211 {
212 assert!(
213 themes.contains(&"Nord".to_string()),
214 "Expected Nord theme from two-face"
215 );
216 assert!(
217 themes.contains(&"Dracula".to_string()),
218 "Expected Dracula theme from two-face"
219 );
220 assert!(
221 themes.contains(&"GruvboxDark".to_string()),
222 "Expected GruvboxDark theme from two-face"
223 );
224 assert!(
225 themes.contains(&"VisualStudioDarkPlus".to_string()),
226 "Expected VisualStudioDarkPlus theme from two-face"
227 );
228 }
229
230 println!("Available themes ({}):", themes.len());
231 for theme in &themes {
232 println!(" - {theme}");
233 }
234 }
235
236 #[cfg(feature = "syntect")]
237 #[test]
238 fn test_nix_language_support() {
239 let manager = create_default_manager(None)
240 .expect("Failed to create default syntax manager");
241 let languages = manager.highlighter().supported_languages();
242
243 assert!(
245 languages.contains(&"nix".to_string()),
246 "Expected Nix language support via two-face"
247 );
248
249 let nix_code = r#"
253{ pkgs ? import <nixpkgs> {} }:
254
255pkgs.stdenv.mkDerivation rec {
256 pname = "hello";
257 version = "2.12";
258
259 src = pkgs.fetchurl {
260 url = "mirror://gnu/hello/${pname}-${version}.tar.gz";
261 sha256 = "1ayhp9v4m4rdhjmnl2bq3cibrbqqkgjbl3s7yk2nhlh8vj3ay16g";
262 };
263}
264"#;
265
266 let result = manager.highlight_code(nix_code, "nix", Some("Nord"));
267 assert!(
268 result.is_ok(),
269 "Failed to highlight Nix code: {:?}",
270 result.err()
271 );
272 }
273}