Skip to content

Commit 4ff9882

Browse files
committed
refactor: reorganize modules and split files
1 parent 1317e2f commit 4ff9882

File tree

21 files changed

+1105
-1022
lines changed

21 files changed

+1105
-1022
lines changed

cli/src/args.rs

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
use core::fmt;
2+
use std::path::{Path, PathBuf};
3+
4+
use clap::{Parser, Subcommand, ValueEnum};
5+
use reflexo_typst::error_once;
6+
7+
use crate::{utils::make_absolute, version::VersionFormat};
8+
9+
#[derive(Debug, Parser)]
10+
#[clap(name = "shiroa", version = "0.3.1-rc4")]
11+
pub struct Opts {
12+
/// Print Version
13+
#[arg(short = 'V', long, group = "version-dump")]
14+
pub version: bool,
15+
16+
/// Print Version in format
17+
#[arg(long = "VV", alias = "version-fmt", group = "version-dump", default_value_t = VersionFormat::None)]
18+
pub vv: VersionFormat,
19+
20+
/// Print Verbose Log
21+
#[arg(short = 'v', long, global = true)]
22+
pub verbose: bool,
23+
24+
#[clap(subcommand)]
25+
pub sub: Option<Subcommands>,
26+
}
27+
28+
#[derive(Debug, Subcommand)]
29+
#[clap(
30+
about = "The cli for shiroa.",
31+
after_help = "",
32+
next_display_order = None
33+
)]
34+
#[allow(clippy::large_enum_variant)]
35+
pub enum Subcommands {
36+
#[clap(about = "init book.")]
37+
Init(InitArgs),
38+
#[clap(about = "build book.")]
39+
Build(BuildArgs),
40+
#[clap(about = "serve book.")]
41+
Serve(ServeArgs),
42+
}
43+
44+
/// Determine the approach to retrieving metadata of a book project.
45+
#[derive(ValueEnum, Debug, Clone, Copy, Eq, PartialEq, Default)]
46+
#[value(rename_all = "kebab-case")]
47+
pub enum MetaSource {
48+
/// Strictly retrieve the project's meta by label queries.
49+
/// - retrieve the book meta from `<shiroa-book-meta>`
50+
/// - retrieve the build meta from `<shiroa-build-meta>`
51+
Strict,
52+
/// Infer the project's meta from the outline of main file.
53+
/// Note: if the main file also contains `<shiroa-book-meta>` or
54+
/// `<shiroa-build-meta>`, the manual-set meta will be used first.
55+
#[default]
56+
Outline,
57+
}
58+
59+
impl fmt::Display for MetaSource {
60+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61+
f.write_str(self.to_possible_value().unwrap().get_name())
62+
}
63+
}
64+
65+
#[derive(ValueEnum, Debug, Clone, Copy, Eq, PartialEq, Default)]
66+
#[value(rename_all = "kebab-case")]
67+
pub enum RenderMode {
68+
/// Dynamically render as paged document.
69+
#[default]
70+
DynPaged,
71+
/// Statically render html parts as much as possible, and leave frames
72+
/// rendered dynamically.
73+
StaticHtmlDynPaged,
74+
/// Statically render the whole document, the embedded frames are not
75+
/// resizable.
76+
StaticHtml,
77+
}
78+
79+
const ENV_PATH_SEP: char = if cfg!(windows) { ';' } else { ':' };
80+
81+
#[derive(Default, Debug, Clone, Parser)]
82+
#[clap(next_help_heading = "Compile options")]
83+
pub struct CompileArgs {
84+
/// The directory storing the `book.typ` file.
85+
/// (Defaults to the current directory when omitted)
86+
#[clap(default_value = "")]
87+
pub dir: String,
88+
89+
/// Determine the approach to retrieving metadata of the book project.
90+
#[clap(long, default_value = "strict")]
91+
pub meta_source: MetaSource,
92+
93+
/// The mode to render typst document. The dynamically rendering means that
94+
/// some elements will be rendered by a wasm module in the browser.
95+
#[clap(long, default_value = "dyn-paged")]
96+
pub mode: RenderMode,
97+
98+
/// Deprecated: use `--root` instead.
99+
///
100+
/// Root directory for the typst workspace, which is same as the
101+
/// `typst-cli`'s `--root` flag. (Defaults to the root directory for the
102+
/// book when omitted)
103+
#[clap(long, short, default_value = "")]
104+
pub workspace: String,
105+
106+
/// Configure the project root (for absolute paths in typst source files).
107+
#[clap(long = "root", value_name = "DIR")]
108+
pub root: Option<String>,
109+
110+
/// Add additional directories that are recursively searched for fonts.
111+
///
112+
/// If multiple paths are specified, they are separated by the system's path
113+
/// separator (`:` on Unix-like systems and `;` on Windows).
114+
#[clap(
115+
long = "font-path",
116+
value_name = "DIR",
117+
action = clap::ArgAction::Append,
118+
env = "TYPST_FONT_PATHS",
119+
value_delimiter = ENV_PATH_SEP
120+
)]
121+
pub font_paths: Vec<PathBuf>,
122+
123+
/// Output to directory, default in the same directory as the entry file.
124+
/// Relative paths are interpreted relative to the book's root directory.
125+
/// If omitted, shiroa uses #build-meta.build-dir from book.typ or defaults
126+
/// to `./dist`.
127+
#[clap(long, short, default_value = "")]
128+
pub dest_dir: String,
129+
130+
/// Reset path to root in html files.
131+
#[clap(long, default_value = "/")]
132+
pub path_to_root: String,
133+
134+
/// Specify a filter to only load files with a specific extension.
135+
#[clap(long, default_value = "^(player.bilibili.com)$")]
136+
pub allowed_url_source: Option<String>,
137+
}
138+
139+
impl CompileArgs {
140+
pub fn compat(&mut self) {
141+
if !self.workspace.is_empty() {
142+
eprintln!("warning: the --workspace flag is deprecated, use --root instead");
143+
}
144+
if let Some(root) = self.root.take() {
145+
self.workspace = root;
146+
}
147+
}
148+
149+
pub fn canonicalize(&mut self) -> crate::error::Result<()> {
150+
if !self.path_to_root.starts_with('/') {
151+
self.path_to_root.insert(0, '/');
152+
}
153+
154+
if !self.path_to_root.ends_with('/') {
155+
self.path_to_root.push('/');
156+
}
157+
158+
make_absolute(Path::new(&self.dir))
159+
.to_str()
160+
.unwrap()
161+
.clone_into(&mut self.dir);
162+
163+
let dir = Path::new(&self.dir);
164+
if dir.is_file() {
165+
if self.meta_source == MetaSource::Strict {
166+
return Err(error_once!("project dir is a file", dir: dir.display()));
167+
}
168+
let w = dir.parent().unwrap().to_str().unwrap().to_owned();
169+
self.dir = w;
170+
}
171+
172+
if self.workspace.is_empty() {
173+
self.workspace.clone_from(&self.dir);
174+
}
175+
176+
Ok(())
177+
}
178+
}
179+
180+
#[derive(Default, Debug, Clone, Parser)]
181+
#[clap(next_help_heading = "Init options")]
182+
pub struct InitArgs {
183+
/// arguments for compile setting.
184+
#[clap(flatten)]
185+
pub compile: CompileArgs,
186+
}
187+
188+
#[derive(Default, Debug, Clone, Parser)]
189+
#[clap(next_help_heading = "Build options")]
190+
pub struct BuildArgs {
191+
/// arguments for compile setting.
192+
#[clap(flatten)]
193+
pub compile: CompileArgs,
194+
}
195+
196+
#[derive(Default, Debug, Clone, Parser)]
197+
#[clap(next_help_heading = "Compile options")]
198+
pub struct ServeArgs {
199+
/// arguments for compile setting.
200+
#[clap(flatten)]
201+
pub compile: CompileArgs,
202+
203+
/// Do not build the book before serving.
204+
#[clap(long)]
205+
pub no_build: bool,
206+
207+
/// Listen address.
208+
#[clap(long, default_value = "127.0.0.1:25520")]
209+
pub addr: String,
210+
}

cli/src/book.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
pub mod meta;
2+
pub mod outline;
3+
4+
use reflexo_typst::ImmutStr;
5+
use typst::ecow::EcoString;
6+
7+
pub struct ChapterItem {
8+
pub title: EcoString,
9+
pub path: Option<ImmutStr>,
10+
}
File renamed without changes.
Lines changed: 9 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
use std::num::NonZeroUsize;
2-
use std::{ops::DerefMut, sync::Arc};
32

4-
use indexmap::IndexSet;
5-
use reflexo_typst::debug_loc::{DocumentPosition, SourceSpan};
6-
use reflexo_typst::TypstDocument;
3+
use reflexo_typst::{debug_loc::DocumentPosition, TypstDocument};
74
use serde::{Deserialize, Serialize};
8-
use tokio::sync::RwLock;
9-
use typst::foundations::{Content, NativeElement, Packed, StyleChain};
10-
use typst::introspection::Introspector;
11-
use typst::model::HeadingElem;
12-
use typst::syntax::Span;
5+
use typst::{
6+
foundations::{Content, NativeElement, Packed, StyleChain},
7+
introspection::Introspector,
8+
model::HeadingElem,
9+
syntax::Span,
10+
};
11+
12+
use crate::utils::interner::SpanInternerImpl;
1313

1414
/// A heading in the outline panel.
1515
#[derive(Debug, Clone)]
@@ -178,137 +178,3 @@ fn outline_item(interner: &mut SpanInternerImpl, src: &HeadingNode, res: &mut Ve
178178
children,
179179
});
180180
}
181-
182-
#[derive(Debug)]
183-
pub enum InternQuery<T> {
184-
Ok(Option<T>),
185-
UseAfterFree,
186-
}
187-
188-
pub struct InternId {
189-
lifetime: u32,
190-
id: u32,
191-
}
192-
193-
impl InternId {
194-
pub fn new(lifetime: usize, id: usize) -> Self {
195-
Self {
196-
lifetime: lifetime as u32,
197-
id: id as u32,
198-
}
199-
}
200-
201-
fn to_u64(&self) -> u64 {
202-
((self.lifetime as u64) << 32) | self.id as u64
203-
}
204-
205-
fn from_u64(id: u64) -> Self {
206-
Self {
207-
lifetime: (id >> 32) as u32,
208-
id: (id & 0xffffffff) as u32,
209-
}
210-
}
211-
212-
pub fn to_hex(&self) -> String {
213-
format!("{:x}", self.to_u64())
214-
}
215-
216-
pub fn from_hex(hex: &str) -> Self {
217-
Self::from_u64(u64::from_str_radix(hex, 16).unwrap())
218-
}
219-
}
220-
221-
/// Span interner
222-
///
223-
/// Interns spans and returns an intern id. Intern id can be converted to a
224-
/// span. Clone of the interner is cheap, and the clone shares the same interned
225-
/// spans.
226-
#[derive(Clone, Default)]
227-
pub struct SpanInterner {
228-
inner: Arc<RwLock<SpanInternerImpl>>,
229-
}
230-
231-
impl SpanInterner {
232-
pub fn new() -> Self {
233-
Self::default()
234-
}
235-
236-
#[allow(unused)]
237-
pub async fn reset(&self) {
238-
self.inner.write().await.reset();
239-
}
240-
241-
pub async fn span_by_str(&self, str: &str) -> InternQuery<SourceSpan> {
242-
self.inner.read().await.span_by_str(str)
243-
}
244-
245-
#[allow(unused)]
246-
pub async fn span(&self, id: InternId) -> InternQuery<SourceSpan> {
247-
self.inner.read().await.span(id)
248-
}
249-
250-
#[allow(unused)]
251-
pub async fn intern(&self, span: SourceSpan) -> InternId {
252-
self.inner.write().await.intern(span)
253-
}
254-
255-
pub async fn with_writer<F, R>(&self, f: F) -> R
256-
where
257-
F: FnOnce(&mut SpanInternerImpl) -> R,
258-
{
259-
f(self.inner.write().await.deref_mut())
260-
}
261-
}
262-
263-
pub struct SpanInternerImpl {
264-
lifetime: usize,
265-
span2id: IndexSet<(usize, SourceSpan)>,
266-
}
267-
268-
impl Default for SpanInternerImpl {
269-
fn default() -> Self {
270-
Self::new()
271-
}
272-
}
273-
274-
const GARAGE_COLLECT_THRESHOLD: usize = 30;
275-
276-
impl SpanInternerImpl {
277-
pub fn new() -> Self {
278-
Self {
279-
lifetime: 1,
280-
span2id: IndexSet::new(),
281-
}
282-
}
283-
284-
pub fn reset(&mut self) {
285-
self.lifetime += 1;
286-
self.span2id
287-
.retain(|(id, _)| self.lifetime - id < GARAGE_COLLECT_THRESHOLD);
288-
}
289-
290-
pub fn span_by_str(&self, str: &str) -> InternQuery<SourceSpan> {
291-
self.span(InternId::from_hex(str))
292-
}
293-
294-
pub fn span(&self, id: InternId) -> InternQuery<SourceSpan> {
295-
if (id.lifetime as usize + GARAGE_COLLECT_THRESHOLD) <= self.lifetime {
296-
InternQuery::UseAfterFree
297-
} else {
298-
InternQuery::Ok(
299-
self.span2id
300-
.get_index(id.id as usize)
301-
.map(|(_, span)| span)
302-
.copied(),
303-
)
304-
}
305-
}
306-
307-
pub fn intern(&mut self, span: SourceSpan) -> InternId {
308-
let item = (self.lifetime, span);
309-
let (idx, _) = self.span2id.insert_full(item);
310-
// combine lifetime
311-
312-
InternId::new(self.lifetime, idx)
313-
}
314-
}

cli/src/commands.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mod init;
2+
mod serve;
3+
4+
pub use init::init;
5+
pub use serve::serve;

0 commit comments

Comments
 (0)