mirror of
https://github.com/typst/typst
synced 2025-08-24 11:44:12 +08:00
refactor: implement TryFrom<&Bytes> for Lines
- remove Bytes::load_str and impl From<Utf8Error> for LoadError
This commit is contained in:
parent
3bde9cf52d
commit
0ed6b31b70
@ -190,7 +190,7 @@ impl SystemWorld {
|
|||||||
source.lines()
|
source.lines()
|
||||||
} else if let Some(bytes) = slot.file.get() {
|
} else if let Some(bytes) = slot.file.get() {
|
||||||
let bytes = bytes.as_ref().expect("file is not valid");
|
let bytes = bytes.as_ref().expect("file is not valid");
|
||||||
Lines::from_bytes(bytes).expect("file is not valid utf-8")
|
Lines::try_from(bytes).expect("file is not valid utf-8")
|
||||||
} else {
|
} else {
|
||||||
panic!("file id does not point to any source file");
|
panic!("file id does not point to any source file");
|
||||||
}
|
}
|
||||||
|
@ -599,6 +599,18 @@ impl LoadError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Utf8Error> for LoadError {
|
||||||
|
fn from(err: Utf8Error) -> Self {
|
||||||
|
let start = err.valid_up_to();
|
||||||
|
let end = start + err.error_len().unwrap_or(0);
|
||||||
|
LoadError::new(
|
||||||
|
start..end,
|
||||||
|
"failed to convert to string",
|
||||||
|
"file is not valid utf-8",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert a [`LoadResult`] to a [`SourceResult`] by adding the [`Loaded`] context.
|
/// Convert a [`LoadResult`] to a [`SourceResult`] by adding the [`Loaded`] context.
|
||||||
pub trait LoadedWithin<T> {
|
pub trait LoadedWithin<T> {
|
||||||
/// Report an error, possibly in an external file.
|
/// Report an error, possibly in an external file.
|
||||||
@ -622,7 +634,7 @@ fn load_err_in_text(
|
|||||||
// This also does utf-8 validation. Only report an error in an external
|
// This also does utf-8 validation. Only report an error in an external
|
||||||
// file if it is human readable (valid utf-8), otherwise fall back to
|
// file if it is human readable (valid utf-8), otherwise fall back to
|
||||||
// `load_err_in_invalid_text`.
|
// `load_err_in_invalid_text`.
|
||||||
let lines = Lines::from_bytes(&loaded.data);
|
let lines = Lines::try_from(&loaded.data);
|
||||||
match (loaded.source.v, lines) {
|
match (loaded.source.v, lines) {
|
||||||
(LoadSource::Path(file_id), Ok(lines)) => {
|
(LoadSource::Path(file_id), Ok(lines)) => {
|
||||||
if let Some(range) = pos.range(&lines) {
|
if let Some(range) = pos.range(&lines) {
|
||||||
@ -784,6 +796,7 @@ impl LineCol {
|
|||||||
pub fn try_from_byte_pos(pos: usize, bytes: &[u8]) -> Option<Self> {
|
pub fn try_from_byte_pos(pos: usize, bytes: &[u8]) -> Option<Self> {
|
||||||
let bytes = &bytes[..pos];
|
let bytes = &bytes[..pos];
|
||||||
let mut line = 0;
|
let mut line = 0;
|
||||||
|
#[allow(clippy::double_ended_iterator_last)]
|
||||||
let line_start = memchr::memchr_iter(b'\n', bytes)
|
let line_start = memchr::memchr_iter(b'\n', bytes)
|
||||||
.inspect(|_| line += 1)
|
.inspect(|_| line += 1)
|
||||||
.last()
|
.last()
|
||||||
|
@ -7,9 +7,10 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use ecow::{eco_format, EcoString};
|
use ecow::{eco_format, EcoString};
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
|
use typst_syntax::Lines;
|
||||||
use typst_utils::LazyHash;
|
use typst_utils::LazyHash;
|
||||||
|
|
||||||
use crate::diag::{bail, LoadError, LoadResult, StrResult};
|
use crate::diag::{bail, StrResult};
|
||||||
use crate::foundations::{cast, func, scope, ty, Array, Reflect, Repr, Str, Value};
|
use crate::foundations::{cast, func, scope, ty, Array, Reflect, Repr, Str, Value};
|
||||||
|
|
||||||
/// A sequence of bytes.
|
/// A sequence of bytes.
|
||||||
@ -112,21 +113,6 @@ impl Bytes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_str(&self) -> LoadResult<&str> {
|
|
||||||
match self.inner().as_any().downcast_ref::<Str>() {
|
|
||||||
Some(string) => Ok(string.as_str()),
|
|
||||||
None => self.as_str().map_err(|err| {
|
|
||||||
let start = err.valid_up_to();
|
|
||||||
let end = start + err.error_len().unwrap_or(0);
|
|
||||||
LoadError::new(
|
|
||||||
start..end,
|
|
||||||
"failed to convert to string",
|
|
||||||
"file is not valid utf-8",
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolve an index or throw an out of bounds error.
|
/// Resolve an index or throw an out of bounds error.
|
||||||
fn locate(&self, index: i64) -> StrResult<usize> {
|
fn locate(&self, index: i64) -> StrResult<usize> {
|
||||||
self.locate_opt(index).ok_or_else(|| out_of_bounds(index, self.len()))
|
self.locate_opt(index).ok_or_else(|| out_of_bounds(index, self.len()))
|
||||||
@ -301,6 +287,16 @@ impl Serialize for Bytes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&Bytes> for Lines<String> {
|
||||||
|
type Error = Utf8Error;
|
||||||
|
|
||||||
|
#[comemo::memoize]
|
||||||
|
fn try_from(value: &Bytes) -> Result<Lines<String>, Utf8Error> {
|
||||||
|
let text = value.as_str()?;
|
||||||
|
Ok(Lines::new(text.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Any type that can back a byte buffer.
|
/// Any type that can back a byte buffer.
|
||||||
trait Bytelike: Send + Sync {
|
trait Bytelike: Send + Sync {
|
||||||
fn as_bytes(&self) -> &[u8];
|
fn as_bytes(&self) -> &[u8];
|
||||||
|
@ -133,7 +133,7 @@ impl Loaded {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_str(&self) -> SourceResult<&str> {
|
pub fn load_str(&self) -> SourceResult<&str> {
|
||||||
self.data.load_str().within(self)
|
self.data.as_str().map_err(Into::into).within(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,9 +413,9 @@ fn decode_library(loaded: &Loaded) -> SourceResult<Library> {
|
|||||||
|
|
||||||
match bib_errs {
|
match bib_errs {
|
||||||
Some(bib_errs) if biblatex >= yaml => {
|
Some(bib_errs) if biblatex >= yaml => {
|
||||||
Err(format_biblatex_error(bib_errs)).within(&loaded)
|
Err(format_biblatex_error(bib_errs)).within(loaded)
|
||||||
}
|
}
|
||||||
_ => Err(format_yaml_error(haya_err)).within(&loaded),
|
_ => Err(format_yaml_error(haya_err)).within(loaded),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -472,7 +472,7 @@ impl CslStyle {
|
|||||||
/// Load a CSL style from file contents.
|
/// Load a CSL style from file contents.
|
||||||
#[comemo::memoize]
|
#[comemo::memoize]
|
||||||
pub fn from_data(bytes: &Bytes) -> LoadResult<CslStyle> {
|
pub fn from_data(bytes: &Bytes) -> LoadResult<CslStyle> {
|
||||||
let text = bytes.load_str()?;
|
let text = bytes.as_str()?;
|
||||||
citationberg::IndependentStyle::from_xml(text)
|
citationberg::IndependentStyle::from_xml(text)
|
||||||
.map(|style| {
|
.map(|style| {
|
||||||
Self(Arc::new(ManuallyHash::new(
|
Self(Arc::new(ManuallyHash::new(
|
||||||
|
@ -553,7 +553,7 @@ impl RawSyntax {
|
|||||||
#[comemo::memoize]
|
#[comemo::memoize]
|
||||||
#[typst_macros::time(name = "load syntaxes")]
|
#[typst_macros::time(name = "load syntaxes")]
|
||||||
fn decode(bytes: &Bytes) -> LoadResult<RawSyntax> {
|
fn decode(bytes: &Bytes) -> LoadResult<RawSyntax> {
|
||||||
let str = bytes.load_str()?;
|
let str = bytes.as_str()?;
|
||||||
|
|
||||||
let syntax = SyntaxDefinition::load_from_str(str, false, None)
|
let syntax = SyntaxDefinition::load_from_str(str, false, None)
|
||||||
.map_err(format_syntax_error)?;
|
.map_err(format_syntax_error)?;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::iter::zip;
|
use std::iter::zip;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::str::Utf8Error;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::is_newline;
|
use crate::is_newline;
|
||||||
@ -11,9 +10,9 @@ use crate::is_newline;
|
|||||||
pub struct Lines<S>(Arc<Repr<S>>);
|
pub struct Lines<S>(Arc<Repr<S>>);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Repr<S> {
|
struct Repr<T> {
|
||||||
lines: Vec<Line>,
|
lines: Vec<Line>,
|
||||||
text: S,
|
text: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Metadata about a line.
|
/// Metadata about a line.
|
||||||
@ -25,12 +24,14 @@ pub struct Line {
|
|||||||
utf16_idx: usize,
|
utf16_idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: AsRef<str>> Lines<S> {
|
impl<T: AsRef<str>> Lines<T> {
|
||||||
pub fn new(text: S) -> Self {
|
/// Create from the text buffer and compute the line metadata.
|
||||||
|
pub fn new(text: T) -> Self {
|
||||||
let lines = lines(text.as_ref());
|
let lines = lines(text.as_ref());
|
||||||
Lines(Arc::new(Repr { lines, text }))
|
Lines(Arc::new(Repr { lines, text }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The text as a string slice.
|
||||||
pub fn text(&self) -> &str {
|
pub fn text(&self) -> &str {
|
||||||
self.0.text.as_ref()
|
self.0.text.as_ref()
|
||||||
}
|
}
|
||||||
@ -142,13 +143,6 @@ impl<S: AsRef<str>> Lines<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Lines<String> {
|
impl Lines<String> {
|
||||||
/// Tries to convert the bytes
|
|
||||||
#[comemo::memoize]
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Lines<String>, Utf8Error> {
|
|
||||||
let text = std::str::from_utf8(bytes)?;
|
|
||||||
Ok(Lines::new(text.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fully replace the source text.
|
/// Fully replace the source text.
|
||||||
///
|
///
|
||||||
/// This performs a naive (suffix/prefix-based) diff of the old and new text
|
/// This performs a naive (suffix/prefix-based) diff of the old and new text
|
||||||
|
@ -397,6 +397,8 @@ impl<'a> Parser<'a> {
|
|||||||
/// if the range is empty.
|
/// if the range is empty.
|
||||||
#[cfg(feature = "default")]
|
#[cfg(feature = "default")]
|
||||||
fn parse_range_external(&mut self, file: FileId) -> Option<Range<usize>> {
|
fn parse_range_external(&mut self, file: FileId) -> Option<Range<usize>> {
|
||||||
|
use typst::foundations::Bytes;
|
||||||
|
|
||||||
use crate::world::{read, system_path};
|
use crate::world::{read, system_path};
|
||||||
|
|
||||||
let path = match system_path(file) {
|
let path = match system_path(file) {
|
||||||
@ -407,8 +409,8 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let text = match read(&path) {
|
let bytes = match read(&path) {
|
||||||
Ok(text) => text,
|
Ok(data) => Bytes::new(data),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.error(err.to_string());
|
self.error(err.to_string());
|
||||||
return None;
|
return None;
|
||||||
@ -416,7 +418,7 @@ impl<'a> Parser<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let start = self.parse_line_col()?;
|
let start = self.parse_line_col()?;
|
||||||
let lines = Lines::from_bytes(text.as_ref()).expect("Errors shouldn't be annotated for files that aren't human readable (not valid utf-8)");
|
let lines = Lines::try_from(&bytes).expect("Errors shouldn't be annotated for files that aren't human readable (not valid utf-8)");
|
||||||
let range = if self.s.eat_if('-') {
|
let range = if self.s.eat_if('-') {
|
||||||
let (line, col) = start;
|
let (line, col) = start;
|
||||||
let start = lines.line_column_to_byte(line, col);
|
let start = lines.line_column_to_byte(line, col);
|
||||||
|
@ -95,7 +95,7 @@ impl TestWorld {
|
|||||||
source.lines()
|
source.lines()
|
||||||
} else if let Some(bytes) = slot.file.get() {
|
} else if let Some(bytes) = slot.file.get() {
|
||||||
let bytes = bytes.as_ref().expect("file is not valid");
|
let bytes = bytes.as_ref().expect("file is not valid");
|
||||||
Lines::from_bytes(bytes.as_slice()).expect("file is not valid utf-8")
|
Lines::try_from(bytes).expect("file is not valid utf-8")
|
||||||
} else {
|
} else {
|
||||||
panic!("file id does not point to any source file");
|
panic!("file id does not point to any source file");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user