This commit is contained in:
ef3d0c3e 2024-12-01 08:42:22 +01:00
parent ac8862e459
commit 347fd6ce59
8 changed files with 65 additions and 27 deletions

4
src/cache/cache.rs vendored
View file

@ -33,9 +33,9 @@ pub trait Cached {
/// # Error /// # Error
/// ///
/// Will return an error if the database connection(s) fail, /// Will return an error if the database connection(s) fail,
/// or if not cached, an error from the generator [`f`] /// or if not cached, an error from the generator `f`
/// ///
/// Note that on error, [`f`] may still have been called /// Note that on error, `f` may still have been called
fn cached<E, F>( fn cached<E, F>(
&self, &self,
con: &Connection, con: &Connection,

View file

@ -300,7 +300,7 @@ pub struct CompiledDocument {
pub variables: HashMap<String, String>, pub variables: HashMap<String, String>,
/// All the referenceable elements in the document /// All the referenceable elements in the document
/// with values mapped by [`ReferenceableElement::refid()`] /// with values mapped by [`crate::document::element::ReferenceableElement::refid()`]
pub references: HashMap<String, String>, pub references: HashMap<String, String>,
/// Compiled document's header /// Compiled document's header

View file

@ -177,11 +177,11 @@ pub trait Document<'a>: core::fmt::Debug {
} }
} }
/// Merges [`other`] into [`self`] /// Merges another [`Document`]'s scope into [`self`]
/// ///
/// # Parameters /// # Parameters
/// ///
/// If [`merge_as`] is None, references and variables from the other document are not merged into self /// If `merge_as` is None, references and variables from the other document are not merged into self
fn merge( fn merge(
&self, &self,
content: &RefCell<Vec<Box<dyn Element>>>, content: &RefCell<Vec<Box<dyn Element>>>,

View file

@ -180,7 +180,7 @@ impl<'a, 'b> ParserState<'a, 'b> {
/// 2. (Optional) The winning match with it's match data /// 2. (Optional) The winning match with it's match data
/// If the winning match is None, it means that the document has no more /// If the winning match is None, it means that the document has no more
/// rule to match. I.e The rest of the content should be added as a /// rule to match. I.e The rest of the content should be added as a
/// [`Text`] element. /// [`crate::elements::text::Text`] element.
/// The match data should be passed to the [`Rule::on_match`] method. /// The match data should be passed to the [`Rule::on_match`] method.
/// ///
/// # Strategy /// # Strategy
@ -298,13 +298,13 @@ impl<'a, 'b> ParserState<'a, 'b> {
} }
/// Resets the position and the match_data for a given rule. This is used /// Resets the position and the match_data for a given rule. This is used
/// in order to have 'dynamic' rules that may not match at first, but their /// in order to have 'dynamic' rules that may not match at first, but may match
/// matching rule is modified through the parsing process. /// in the future when modified.
/// ///
/// This function also recursively calls itself on it's `parent`, in order /// This function also recursively calls itself on it's `parent`, in order
/// to fully reset the match. /// to fully reset the match.
/// ///
/// See [`CustomStyleRule`] for an example of how this is used. /// See [`crate::elements::customstyle::CustomStyleRule`] for an example of how this is used.
/// ///
/// # Error /// # Error
/// ///

View file

@ -70,7 +70,7 @@ pub trait Rule: Downcast {
/// The name of the rule that should come before this one /// The name of the rule that should come before this one
fn previous(&self) -> Option<&'static str>; fn previous(&self) -> Option<&'static str>;
/// Finds the next match starting from [`cursor`] /// Finds the next match starting from `cursor`
fn next_match( fn next_match(
&self, &self,
mode: &ParseMode, mode: &ParseMode,
@ -137,7 +137,7 @@ impl<T: RegexRule + 'static> Rule for T {
fn previous(&self) -> Option<&'static str> { RegexRule::previous(self) } fn previous(&self) -> Option<&'static str> { RegexRule::previous(self) }
/// Finds the next match starting from [`cursor`] /// Finds the next match starting from [`Cursor`]
fn next_match( fn next_match(
&self, &self,
mode: &ParseMode, mode: &ParseMode,

View file

@ -10,8 +10,13 @@ use unicode_segmentation::UnicodeSegmentation;
/// Trait for source content /// Trait for source content
pub trait Source: Downcast + Debug { pub trait Source: Downcast + Debug {
/// Gets the source's location /// Gets the source's location
///
/// This usually means the parent source.
/// If the source is a [`SourceFile`], this generally means the [`SourceFile`] that included it.
fn location(&self) -> Option<&Token>; fn location(&self) -> Option<&Token>;
/// Gets the source's name /// Gets the source's name
///
/// For [`SourceFile`] this means the path of the source. Note that some [`VirtualSource`] are prefixed with a special identifier such as `:LUA:`.
fn name(&self) -> &String; fn name(&self) -> &String;
/// Gets the source's content /// Gets the source's content
fn content(&self) -> &String; fn content(&self) -> &String;
@ -37,13 +42,19 @@ impl std::hash::Hash for dyn Source {
/// [`SourceFile`] is a type of [`Source`] that represents a real file. /// [`SourceFile`] is a type of [`Source`] that represents a real file.
#[derive(Debug)] #[derive(Debug)]
pub struct SourceFile { pub struct SourceFile {
/// The token that created this [`SourceFile`], empty if file comes from the executable's
/// options.
location: Option<Token>, location: Option<Token>,
/// Path to the file, used for the [`Self::name()`] method
path: String, path: String,
/// Content of the file
content: String, content: String,
} }
impl SourceFile { impl SourceFile {
// TODO: Create a SourceFileRegistry holding already loaded files to avoid reloading them // TODO: Create a SourceFileRegistry holding already loaded files to avoid reloading them
/// Creates a [`SourceFile`] from a `path`. This will read the content of the file at that
/// `path`. In case the file is not accessible or reading fails, an error is returned.
pub fn new(path: String, location: Option<Token>) -> Result<Self, String> { pub fn new(path: String, location: Option<Token>) -> Result<Self, String> {
match fs::read_to_string(&path) { match fs::read_to_string(&path) {
Err(_) => Err(format!("Unable to read file content: `{}`", path)), Err(_) => Err(format!("Unable to read file content: `{}`", path)),
@ -55,6 +66,7 @@ impl SourceFile {
} }
} }
/// Creates a [`SourceFile`] from a `String`
pub fn with_content(path: String, content: String, location: Option<Token>) -> Self { pub fn with_content(path: String, content: String, location: Option<Token>) -> Self {
Self { Self {
location, location,
@ -63,6 +75,7 @@ impl SourceFile {
} }
} }
/// Gets the path of this [`SourceFile`]
pub fn path(&self) -> &String { &self.path } pub fn path(&self) -> &String { &self.path }
} }
@ -79,8 +92,8 @@ impl Source for SourceFile {
/// ///
/// # Example /// # Example
/// ///
/// Let's say you make a virtual source from the following: "Con\]tent" -> "Con]tent" /// Let's say you make a virtual source from the following: "Con\\]tent" -> "Con]tent"
/// Then at position 3, an offset of 1 will be created to account for the removed '\' /// Then at position 3, an offset of 1 will be created to account for the removed '\\'
#[derive(Debug)] #[derive(Debug)]
struct SourceOffset { struct SourceOffset {
/// Stores the total offsets /// Stores the total offsets
@ -105,16 +118,25 @@ impl SourceOffset {
/// [`VirtualSource`] is a type of [`Source`] that represents a virtual file. [`VirtualSource`]s /// [`VirtualSource`] is a type of [`Source`] that represents a virtual file. [`VirtualSource`]s
/// can be created from other [`VirtualSource`]s but it must always come from a [`SourceFile`]. /// can be created from other [`VirtualSource`]s but it must always come from a [`SourceFile`].
///
/// # Offsets
///
/// [`VirtualSource`] will keep a list of offsets that were applied from the their parent source (available via [`Self::location`]).
/// For instance, if you consider the [`VirtualSource`] created by removing the '\\' from the following string: "He\\llo", then an offset is stored to account for the missing '\\'. This is required in order to keep diagnostics accurate.
#[derive(Debug)] #[derive(Debug)]
pub struct VirtualSource { pub struct VirtualSource {
/// Token that createrd this [`VirtualSource`]
location: Token, location: Token,
/// Name of the [`VirtualSource`]
name: String, name: String,
/// Content of the [`VirtualSource`]
content: String, content: String,
/// Offset relative to the [`location`]'s source /// Offsets relative to the [`Self::location`]'s source
offsets: Option<SourceOffset>, offsets: Option<SourceOffset>,
} }
impl VirtualSource { impl VirtualSource {
/// Creates a new [`VirtualSource`] from a `location`, `name` and `content`.
pub fn new(location: Token, name: String, content: String) -> Self { pub fn new(location: Token, name: String, content: String) -> Self {
Self { Self {
location, location,
@ -124,6 +146,11 @@ impl VirtualSource {
} }
} }
/// Creates a new [`VirtualSource`] from a `location`, `name`, `content` and `offsets`.
///
/// # Notes
///
/// This should be called by [`crate::parser::util::escape_source`]
pub fn new_offsets( pub fn new_offsets(
location: Token, location: Token,
name: String, name: String,
@ -233,7 +260,7 @@ pub struct Cursor {
impl Cursor { impl Cursor {
pub fn new(pos: usize, source: Rc<dyn Source>) -> Self { Self { pos, source } } pub fn new(pos: usize, source: Rc<dyn Source>) -> Self { Self { pos, source } }
/// Creates [`cursor`] at [`new_pos`] in the same [`file`] /// Creates [`Cursor`] at `new_pos` in the same [`Source`]
pub fn at(&self, new_pos: usize) -> Self { pub fn at(&self, new_pos: usize) -> Self {
Self { Self {
pos: new_pos, pos: new_pos,
@ -246,7 +273,7 @@ impl Cursor {
/// ///
/// # Notes /// # Notes
/// ///
/// Because the LSP uses UTF-16 encoded positions, field [`line_pos`] corresponds to the UTF-16 /// Because the LSP uses UTF-16 encoded positions, field [`Self::line_pos`] corresponds to the UTF-16
/// distance between the first character (position = 0 or after '\n') and the character at the /// distance between the first character (position = 0 or after '\n') and the character at the
/// current position. /// current position.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -273,10 +300,11 @@ impl LineCursor {
} }
/// Moves [`LineCursor`] to an absolute byte position /// Moves [`LineCursor`] to an absolute byte position
/// This function may only advance the position, as is required for the LSP semantics.
/// ///
/// # Error /// # Error
/// ///
/// This function will panic if [`pos`] is not utf8 aligned /// This function will panic if [`Self::pos`] is not UTF-88 aligned, or if trying to go to a previous position.
pub fn move_to(&mut self, pos: usize) { pub fn move_to(&mut self, pos: usize) {
if self.pos < pos { if self.pos < pos {
let start = self.pos; let start = self.pos;

View file

@ -5,18 +5,20 @@ use downcast_rs::impl_downcast;
use downcast_rs::Downcast; use downcast_rs::Downcast;
/// Styling for an element /// Styling for an element
///
/// Some elements have support for styling.
pub trait ElementStyle: Downcast + core::fmt::Debug { pub trait ElementStyle: Downcast + core::fmt::Debug {
/// The style key /// The style key
fn key(&self) -> &'static str; fn key(&self) -> &'static str;
/// Attempts to create a new style from a [`json`] string /// Attempts to create a new style from a `json` string
/// ///
/// # Errors /// # Errors
/// ///
/// Will fail if deserialization fails /// Will fail if deserialization fails
fn from_json(&self, json: &str) -> Result<Rc<dyn ElementStyle>, String>; fn from_json(&self, json: &str) -> Result<Rc<dyn ElementStyle>, String>;
/// Attempts to deserialize lua table into a new style /// Attempts to deserialize a `lua table` into a new style
fn from_lua( fn from_lua(
&self, &self,
lua: &mlua::Lua, lua: &mlua::Lua,
@ -25,6 +27,7 @@ pub trait ElementStyle: Downcast + core::fmt::Debug {
} }
impl_downcast!(ElementStyle); impl_downcast!(ElementStyle);
/// A structure that holds registered [`ElementStyle`]
#[derive(Default)] #[derive(Default)]
pub struct StyleHolder { pub struct StyleHolder {
styles: HashMap<String, Rc<dyn ElementStyle>>, styles: HashMap<String, Rc<dyn ElementStyle>>,
@ -35,13 +38,17 @@ impl StyleHolder {
pub fn is_registered(&self, style_key: &str) -> bool { self.styles.contains_key(style_key) } pub fn is_registered(&self, style_key: &str) -> bool { self.styles.contains_key(style_key) }
/// Gets the current active style for an element /// Gets the current active style for an element
/// NOTE: Will panic if a style is not defined for a given element /// If you need to process user input, use [`Self::is_registered`]
/// If you need to process user input, use [`is_registered`] ///
/// # Notes
///
/// Will panic if a style is not defined for a given element.
/// Elements should have their styles (when they support it) registered when the parser starts.
pub fn current(&self, style_key: &str) -> Rc<dyn ElementStyle> { pub fn current(&self, style_key: &str) -> Rc<dyn ElementStyle> {
self.styles.get(style_key).cloned().unwrap() self.styles.get(style_key).cloned().unwrap()
} }
/// Sets the [`style`] /// Sets the style
pub fn set_current(&mut self, style: Rc<dyn ElementStyle>) { pub fn set_current(&mut self, style: Rc<dyn ElementStyle>) {
self.styles.insert(style.key().to_string(), style); self.styles.insert(style.key().to_string(), style);
} }

View file

@ -90,12 +90,15 @@ pub fn process_text(document: &dyn Document, content: &str) -> String {
processed processed
} }
/// Transforms source into a new [`VirtualSource`]. Transforms range from source by /// Transforms source into a new [`VirtualSource`] using a `range`.
/// detecting escaped tokens. ///
/// This function will extract the sub-source using the specified `range`, then escape `token` using the specified `escape` character.
/// It will also keep a list of removed/added characters and build an offset list that will be passed to the newly created source, via [`VirtualSource::new_offsets`].
///
/// ///
/// # Notes /// # Notes
/// ///
/// If you only need to escape content that won't be parsed, use [`process_escaped`] instead. /// If you only need to escape content that won't be parsed, use [`escape_text`] instead.
pub fn escape_source( pub fn escape_source(
source: Rc<dyn Source>, source: Rc<dyn Source>,
range: Range<usize>, range: Range<usize>,
@ -147,11 +150,11 @@ pub fn escape_source(
} }
/// Processed a string and escapes a single token out of it /// Processed a string and escapes a single token out of it
/// Escaped characters other than the [`token`] will be not be treated as escaped /// Escaped characters other than the [`Token`] will be not be treated as escaped
/// ///
/// # Example /// # Example
/// ``` /// ```
/// assert_eq!(process_escaped('\\', "%", "escaped: \\%, also escaped: \\\\\\%, untouched: \\a"), /// assert_eq!(scape_text('\\', "%", "escaped: \\%, also escaped: \\\\\\%, untouched: \\a"),
/// "escaped: %, also escaped: \\%, untouched: \\a"); /// "escaped: %, also escaped: \\%, untouched: \\a");
/// ``` /// ```
/// ///