next_napi_bindings/
code_frame.rs1use napi::bindgen_prelude::*;
2use napi_derive::napi;
3use next_code_frame::{
4 CodeFrameColorMode, CodeFrameLocation, CodeFrameOptions, Language, Location, render_code_frame,
5};
6
7const DEFAULT_MAX_WIDTH: u32 = 240;
17
18#[napi(object)]
19pub struct NapiLocation {
20 pub line: u32,
21 pub column: Option<u32>,
22}
23
24impl From<NapiLocation> for Location {
25 fn from(loc: NapiLocation) -> Self {
26 Location {
27 line: loc.line as usize,
28 column: loc.column.map(|c| c as usize),
29 }
30 }
31}
32
33#[napi(object)]
34pub struct NapiCodeFrameLocation {
35 pub start: NapiLocation,
36 pub end: Option<NapiLocation>,
37}
38
39impl From<NapiCodeFrameLocation> for CodeFrameLocation {
40 fn from(loc: NapiCodeFrameLocation) -> Self {
41 CodeFrameLocation {
42 start: loc.start.into(),
43 end: loc.end.map(Into::into),
44 }
45 }
46}
47
48#[napi]
49#[derive(PartialEq, Eq)]
50pub enum NapiCodeFrameColorMode {
51 Error,
52 Warning,
53 Info,
54}
55
56#[napi(object)]
57#[derive(Default)]
58pub struct NapiCodeFrameOptions {
59 pub lines_above: Option<u32>,
61 pub lines_below: Option<u32>,
63 pub max_width: Option<u32>,
65 pub color: Option<Either<NapiCodeFrameColorMode, bool>>,
67 pub highlight_code: Option<bool>,
73 pub message: Option<String>,
75 pub language: Option<String>,
77}
78
79fn parse_language(s: &Option<String>) -> Language {
80 match s.as_deref() {
81 Some("css") => Language::Css,
82 _ => Language::JavaScript,
83 }
84}
85
86impl From<NapiCodeFrameOptions> for CodeFrameOptions {
87 fn from(opts: NapiCodeFrameOptions) -> Self {
88 let color = match opts.color {
89 None | Some(Either::B(false)) => CodeFrameColorMode::None,
90 Some(Either::A(NapiCodeFrameColorMode::Error)) | Some(Either::B(true)) => {
91 CodeFrameColorMode::Error
92 }
93 Some(Either::A(NapiCodeFrameColorMode::Warning)) => CodeFrameColorMode::Warning,
94 Some(Either::A(NapiCodeFrameColorMode::Info)) => CodeFrameColorMode::Info,
95 };
96 CodeFrameOptions {
97 lines_above: opts.lines_above.unwrap_or(2) as usize,
98 lines_below: opts.lines_below.unwrap_or(3) as usize,
99 max_width: opts.max_width.unwrap_or(DEFAULT_MAX_WIDTH) as usize,
100 color,
101 highlight_code: opts
102 .highlight_code
103 .unwrap_or(color != CodeFrameColorMode::None),
104 message: opts.message,
105 language: parse_language(&opts.language),
106 }
107 }
108}
109
110#[napi]
126pub fn code_frame_columns(
127 source: String,
128 location: NapiCodeFrameLocation,
129 options: Option<NapiCodeFrameOptions>,
130) -> Result<Option<String>> {
131 let code_frame_location: CodeFrameLocation = location.into();
132 let code_frame_options: CodeFrameOptions = options.unwrap_or_default().into();
133
134 render_code_frame(&source, &code_frame_location, &code_frame_options)
135 .map_err(|e| Error::from_reason(format!("Failed to render code frame: {e:?}")))
136}