diff --git a/Cargo.lock b/Cargo.lock index 8b8821bba573..6bc3012b843d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -584,6 +584,13 @@ dependencies = [ [[package]] name = "biome_grit_formatter" version = "0.0.0" +dependencies = [ + "biome_formatter", + "biome_grit_syntax", + "biome_rowan", + "serde", + "serde_json", +] [[package]] name = "biome_grit_parser" diff --git a/crates/biome_formatter/CONTRIBUTING.md b/crates/biome_formatter/CONTRIBUTING.md index e2d0cf9b20dd..0d36e8f2bf9a 100644 --- a/crates/biome_formatter/CONTRIBUTING.md +++ b/crates/biome_formatter/CONTRIBUTING.md @@ -161,7 +161,7 @@ where -Then, you'll have to create three types: +Then, you'll have to create four types: 1. `HtmlCommentStyle` 1. `HtmlFormatContext` 1. `FormatHtmlSyntaxNode` @@ -195,12 +195,12 @@ Usually, the type context must contain `comments` and `source_map` fields: ```rust pub struct HtmlFormatContext { /// The comments of the nodes and tokens in the program. - comments: Rc, - source_map: Option, + comments: Rc, + source_map: Option, } impl HtmlFormatContext { - pub fn new(comments: CssComments) -> Self { + pub fn new(comments: HtmlComments) -> Self { Self { comments: Rc::new(comments), source_map: None, @@ -244,7 +244,7 @@ impl FormatRule for FormatHtmlSyntaxNode { } } -impl AsFormat for FormatHtmlSyntaxNode { +impl AsFormat for HtmlSyntaxNode { type Format<'a> = FormatRefWithRule<'a, HtmlSyntaxNode, FormatHtmlSyntaxNode>; fn format(&self) -> Self::Format<'_> { @@ -252,7 +252,7 @@ impl AsFormat for FormatHtmlSyntaxNode { } } -impl IntoFormat for FormatHtmlSyntaxNode { +impl IntoFormat for HtmlSyntaxNode { type Format = FormatOwnedWithRule; fn into_format(self) -> Self::Format { @@ -268,7 +268,7 @@ impl IntoFormat for FormatHtmlSyntaxNode { This is small type that you need to instruct the formatter infra about a certain language. This type needs to implement the trait `biome_formatter::FormatLanguage` ```rust -impl FormatLanguage for HtmlLanguage { +impl FormatLanguage for HtmlFormatLanguage { type SyntaxLanguage = HtmlLanguage; type Context = HtmlFormatContext; type FormatRule = FormatHtmlSyntaxNode; @@ -294,7 +294,7 @@ where N: AstNode, { // this is the method that actually start the formatting - fn fmt(&self, node: &N, f: &mut HtmlForamtter) -> FormatResult<()> { + fn fmt(&self, node: &N, f: &mut HtmlFormatter) -> FormatResult<()> { if self.is_suppressed(node, f) { return write!(f, [format_suppressed_node(node.syntax())]); } @@ -305,10 +305,10 @@ where self.fmt_trailing_comments(node, f) } - fn fmt_fields(&self, node: &N, f: &mut HtmlForamtter) -> FormatResult<()>; + fn fmt_fields(&self, node: &N, f: &mut HtmlFormatter) -> FormatResult<()>; /// Returns `true` if the node has a suppression comment and should use the same formatting as in the source document. - fn is_suppressed(&self, node: &N, f: &JsonFormatter) -> bool { + fn is_suppressed(&self, node: &N, f: &HtmlFormatter) -> bool { f.context().comments().is_suppressed(node.syntax()) } @@ -316,7 +316,7 @@ where /// /// You may want to override this method if you want to manually handle the formatting of comments /// inside of the `fmt_fields` method or customize the formatting of the leading comments. - fn fmt_leading_comments(&self, node: &N, f: &mut HtmlForamtter) -> FormatResult<()> { + fn fmt_leading_comments(&self, node: &N, f: &mut HtmlFormatter) -> FormatResult<()> { format_leading_comments(node.syntax()).fmt(f) } @@ -327,7 +327,7 @@ where /// no comments are dropped. /// /// A node can have dangling comments if all its children are tokens or if all node childrens are optional. - fn fmt_dangling_comments(&self, node: &N, f: &mut HtmlForamtter) -> FormatResult<()> { + fn fmt_dangling_comments(&self, node: &N, f: &mut HtmlFormatter) -> FormatResult<()> { format_dangling_comments(node.syntax()) .with_soft_block_indent() .fmt(f) @@ -337,7 +337,7 @@ where /// /// You may want to override this method if you want to manually handle the formatting of comments /// inside of the `fmt_fields` method or customize the formatting of the trailing comments. - fn fmt_trailing_comments(&self, node: &N, f: &mut HtmlForamtter) -> FormatResult<()> { + fn fmt_trailing_comments(&self, node: &N, f: &mut HtmlFormatter) -> FormatResult<()> { format_trailing_comments(node.syntax()).fmt(f) } } @@ -349,9 +349,10 @@ Now that everything is wired, you just needs to expose a public method that does ```rust pub fn format_node( + options: HtmlFormatOptions, root: &HtmlSyntaxNode, ) -> FormatResult> { - biome_formatter::format_node(root, HtmlFormatLanguage {}) + biome_formatter::format_node(root, HtmlFormatLanguage::new(options)) } ``` @@ -379,7 +380,7 @@ Updated the `Cargo.toml` file to import some testing utility: ```toml [dev-dependencies] biome_formatter_test = { path = "../biome_formatter_test" } -biome_html_factory = { path = "../biome_html_parser" } +biome_html_factory = { path = "../biome_html_factory" } biome_html_parser = { path = "../biome_html_parser" } biome_parser = { path = "../biome_parser" } biome_service = { path = "../biome_service" } diff --git a/crates/biome_grit_formatter/Cargo.toml b/crates/biome_grit_formatter/Cargo.toml index 91f0705dd274..6d79810951d0 100644 --- a/crates/biome_grit_formatter/Cargo.toml +++ b/crates/biome_grit_formatter/Cargo.toml @@ -13,13 +13,13 @@ version = "0.0.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -#biome_formatter = { workspace = true } -#biome_grit_syntax = { workspace = true } -#biome_rowan = { workspace = true } +biome_formatter = { workspace = true } +biome_grit_syntax = { workspace = true } +biome_rowan = { workspace = true } [dev-dependencies] -#serde = { workspace = true, features = ["derive"] } -#serde_json = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } # cargo-workspaces metadata [package.metadata.workspaces] diff --git a/crates/biome_grit_formatter/src/comments.rs b/crates/biome_grit_formatter/src/comments.rs new file mode 100644 index 000000000000..6e72b0684c94 --- /dev/null +++ b/crates/biome_grit_formatter/src/comments.rs @@ -0,0 +1,83 @@ +use crate::GritFormatContext; + +use biome_formatter::{ + comments::{is_doc_comment, CommentStyle, Comments, SourceComment}, + prelude::*, + prelude::{align, dynamic_text, format_once, hard_line_break, Formatter}, + write, FormatResult, FormatRule, +}; +use biome_grit_syntax::GritLanguage; +use biome_rowan::TextLen; + +pub type GritComments = Comments; + +#[derive(Eq, PartialEq, Copy, Clone, Debug, Default)] +pub struct GritCommentStyle; + +impl CommentStyle for GritCommentStyle { + type Language = GritLanguage; + + fn is_suppression(_text: &str) -> bool { + false + } + + fn get_comment_kind( + _comment: &biome_rowan::SyntaxTriviaPieceComments, + ) -> biome_formatter::comments::CommentKind { + todo!() + } + + fn place_comment( + &self, + comment: biome_formatter::comments::DecoratedComment, + ) -> biome_formatter::comments::CommentPlacement { + biome_formatter::comments::CommentPlacement::Default(comment) + } +} + +#[derive(Default)] +pub struct FormatGritLeadingComment; + +impl FormatRule> for FormatGritLeadingComment { + type Context = GritFormatContext; + // Copied and pasted this from the css formatter, not sure how much this needs to change. + fn fmt( + &self, + comment: &SourceComment, + f: &mut Formatter, + ) -> FormatResult<()> { + if is_doc_comment(comment.piece()) { + let mut source_offset = comment.piece().text_range().start(); + + let mut lines = comment.piece().text().lines(); + + // SAFETY: Safe, `is_doc_comment` only returns `true` for multiline comments + let first_line = lines.next().unwrap(); + write!(f, [dynamic_text(first_line.trim_end(), source_offset)])?; + + source_offset += first_line.text_len(); + + // Indent the remaining lines by one space so that all `*` are aligned. + write!( + f, + [align( + 1, + &format_once(|f| { + for line in lines { + write!( + f, + [hard_line_break(), dynamic_text(line.trim(), source_offset)] + )?; + + source_offset += line.text_len(); + } + + Ok(()) + }) + )] + ) + } else { + write!(f, [comment.piece().as_piece()]) + } + } +} diff --git a/crates/biome_grit_formatter/src/context.rs b/crates/biome_grit_formatter/src/context.rs new file mode 100644 index 000000000000..b4a8e093459a --- /dev/null +++ b/crates/biome_grit_formatter/src/context.rs @@ -0,0 +1,151 @@ +use crate::comments::{FormatGritLeadingComment, GritCommentStyle, GritComments}; +use biome_formatter::{ + CstFormatContext, FormatContext, FormatOptions, IndentStyle, IndentWidth, LineEnding, + LineWidth, QuoteStyle, TransformSourceMap, +}; +use biome_grit_syntax::GritLanguage; +use std::rc::Rc; + +#[allow(dead_code)] +#[derive(Debug, Clone)] +pub struct GritFormatContext { + comments: Rc, + source_map: Option, +} + +impl GritFormatContext { + pub fn new(comments: GritComments) -> Self { + Self { + comments: Rc::new(comments), + source_map: None, + } + } + + pub fn with_source_map(mut self, source_map: Option) -> Self { + self.source_map = source_map; + self + } +} + +impl FormatContext for GritFormatContext { + type Options = GritFormatOptions; + + fn options(&self) -> &Self::Options { + todo!() + } + + fn source_map(&self) -> Option<&TransformSourceMap> { + todo!() + } +} +impl CstFormatContext for GritFormatContext { + type Language = GritLanguage; + + type Style = GritCommentStyle; + + type CommentRule = FormatGritLeadingComment; + + fn comments(&self) -> &biome_formatter::comments::Comments { + todo!() + } +} + +#[derive(Debug, Default, Clone, PartialEq)] + +pub struct GritFormatOptions { + indent_style: IndentStyle, + indent_width: IndentWidth, + line_ending: LineEnding, + line_width: LineWidth, + quote_style: QuoteStyle, +} + +impl GritFormatOptions { + pub fn new() -> Self { + Self { + indent_style: IndentStyle::default(), + indent_width: IndentWidth::default(), + line_ending: LineEnding::default(), + line_width: LineWidth::default(), + quote_style: QuoteStyle::default(), + } + } + pub fn with_indent_style(mut self, indent_style: IndentStyle) -> Self { + self.indent_style = indent_style; + self + } + + pub fn with_indent_width(mut self, indent_width: IndentWidth) -> Self { + self.indent_width = indent_width; + self + } + + pub fn with_line_ending(mut self, line_ending: LineEnding) -> Self { + self.line_ending = line_ending; + self + } + + pub fn with_line_width(mut self, line_width: LineWidth) -> Self { + self.line_width = line_width; + self + } + + pub fn with_quote_style(mut self, quote_style: QuoteStyle) -> Self { + self.quote_style = quote_style; + self + } + + pub fn set_indent_style(&mut self, indent_style: IndentStyle) { + self.indent_style = indent_style; + } + + pub fn set_indent_width(&mut self, indent_width: IndentWidth) { + self.indent_width = indent_width; + } + + pub fn set_line_ending(&mut self, line_ending: LineEnding) { + self.line_ending = line_ending; + } + + pub fn set_line_width(&mut self, line_width: LineWidth) { + self.line_width = line_width; + } + + pub fn set_quote_style(&mut self, quote_style: QuoteStyle) { + self.quote_style = quote_style; + } + + pub fn quote_style(&self) -> QuoteStyle { + self.quote_style + } +} + +impl FormatOptions for GritFormatOptions { + fn indent_style(&self) -> IndentStyle { + todo!() + } + + fn indent_width(&self) -> IndentWidth { + todo!() + } + + fn line_width(&self) -> LineWidth { + todo!() + } + + fn line_ending(&self) -> LineEnding { + todo!() + } + + fn attribute_position(&self) -> biome_formatter::AttributePosition { + todo!() + } + + fn bracket_spacing(&self) -> biome_formatter::BracketSpacing { + todo!() + } + + fn as_print_options(&self) -> biome_formatter::prelude::PrinterOptions { + todo!() + } +} diff --git a/crates/biome_grit_formatter/src/cst.rs b/crates/biome_grit_formatter/src/cst.rs new file mode 100644 index 000000000000..58595b25d5a5 --- /dev/null +++ b/crates/biome_grit_formatter/src/cst.rs @@ -0,0 +1,30 @@ +use crate::prelude::*; +use biome_formatter::{FormatOwnedWithRule, FormatRefWithRule, FormatResult}; +use biome_grit_syntax::{map_syntax_node, GritSyntaxNode}; + +#[derive(Debug, Copy, Clone, Default)] +pub struct FormatGritSyntaxNode; + +impl FormatRule for FormatGritSyntaxNode { + type Context = GritFormatContext; + + fn fmt(&self, node: &GritSyntaxNode, f: &mut GritFormatter) -> FormatResult<()> { + map_syntax_node!(node.clone(), node => node.format().fmt(f)) + } +} + +impl AsFormat for GritSyntaxNode { + type Format<'a> = FormatRefWithRule<'a, GritSyntaxNode, FormatGritSyntaxNode>; + + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new(self, FormatGritSyntaxNode) + } +} + +impl IntoFormat for GritSyntaxNode { + type Format = FormatOwnedWithRule; + + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new(self, FormatGritSyntaxNode) + } +} diff --git a/crates/biome_grit_formatter/src/lib.rs b/crates/biome_grit_formatter/src/lib.rs index 7d12d9af8195..c935554e575c 100644 --- a/crates/biome_grit_formatter/src/lib.rs +++ b/crates/biome_grit_formatter/src/lib.rs @@ -1,14 +1,284 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right +mod comments; +pub mod context; +mod cst; +mod generated; +mod grit; +mod prelude; + +use biome_formatter::{ + prelude::*, + trivia::{format_dangling_comments, format_leading_comments, format_trailing_comments}, + write, CstFormatContext, Format, FormatLanguage, FormatResult, Formatted, +}; +use biome_grit_syntax::{GritLanguage, GritSyntaxNode}; + +pub(crate) use crate::context::GritFormatContext; + +use biome_rowan::AstNode; +use context::GritFormatOptions; +use cst::FormatGritSyntaxNode; + +pub(crate) type GritFormatter<'buf> = Formatter<'buf, GritFormatContext>; + +/// Formats a GritQL file based on its features. +/// +/// It returns a [Formatted] result, which the user can use to override a file. +pub fn format_node( + options: GritFormatOptions, + root: &GritSyntaxNode, +) -> FormatResult> { + biome_formatter::format_node(root, GritFormatLanguage::new(options)) +} + +pub(crate) trait FormatNodeRule +where + N: AstNode, +{ + // this is the method that actually start the formatting + fn fmt(&self, node: &N, f: &mut GritFormatter) -> FormatResult<()> { + if self.is_suppressed(node, f) { + return write!(f, [format_suppressed_node(node.syntax())]); + } + + self.fmt_leading_comments(node, f)?; + self.fmt_fields(node, f)?; + self.fmt_dangling_comments(node, f)?; + self.fmt_trailing_comments(node, f) + } + + fn fmt_fields(&self, node: &N, f: &mut GritFormatter) -> FormatResult<()>; + + /// Returns `true` if the node has a suppression comment and should use the same formatting as in the source document. + fn is_suppressed(&self, node: &N, f: &GritFormatter) -> bool { + f.context().comments().is_suppressed(node.syntax()) + } + + /// Formats the [leading comments](biome_formatter::comments#leading-comments) of the node. + /// + /// You may want to override this method if you want to manually handle the formatting of comments + /// inside of the `fmt_fields` method or customize the formatting of the leading comments. + fn fmt_leading_comments(&self, node: &N, f: &mut GritFormatter) -> FormatResult<()> { + format_leading_comments(node.syntax()).fmt(f) + } + + /// Formats the [dangling comments](biome_formatter::comments#dangling-comments) of the node. + /// + /// You should override this method if the node handled by this rule can have dangling comments because the + /// default implementation formats the dangling comments at the end of the node, which isn't ideal but ensures that + /// no comments are dropped. + /// + /// A node can have dangling comments if all its children are tokens or if all node childrens are optional. + fn fmt_dangling_comments(&self, node: &N, f: &mut GritFormatter) -> FormatResult<()> { + format_dangling_comments(node.syntax()) + .with_soft_block_indent() + .fmt(f) + } + + /// Formats the [trailing comments](biome_formatter::comments#trailing-comments) of the node. + /// + /// You may want to override this method if you want to manually handle the formatting of comments + /// inside of the `fmt_fields` method or customize the formatting of the trailing comments. + fn fmt_trailing_comments(&self, node: &N, f: &mut GritFormatter) -> FormatResult<()> { + format_trailing_comments(node.syntax()).fmt(f) + } +} + +#[allow(dead_code)] +#[derive(Debug, Clone, Default)] +pub struct GritFormatLanguage { + options: GritFormatOptions, +} + +impl GritFormatLanguage { + pub fn new(options: GritFormatOptions) -> Self { + Self { options } + } +} + +impl FormatLanguage for GritFormatLanguage { + type SyntaxLanguage = GritLanguage; + + type Context = GritFormatContext; + + type FormatRule = FormatGritSyntaxNode; + + fn transform( + &self, + _root: &biome_rowan::SyntaxNode, + ) -> Option<( + biome_rowan::SyntaxNode, + biome_formatter::TransformSourceMap, + )> { + None + } + + fn is_range_formatting_node( + &self, + _node: &biome_rowan::SyntaxNode, + ) -> bool { + true + } + + fn options(&self) -> &::Options { + todo!() + } + + fn create_context( + self, + _root: &biome_rowan::SyntaxNode, + _source_map: Option, + ) -> Self::Context { + todo!() + } +} + +/// Used to get an object that knows how to format this object. +pub(crate) trait AsFormat { + type Format<'a>: biome_formatter::Format + where + Self: 'a; + + /// Returns an object that is able to format this object. + fn format(&self) -> Self::Format<'_>; +} + +/// Implement [AsFormat] for references to types that implement [AsFormat]. +impl AsFormat for &T +where + T: AsFormat, +{ + type Format<'a> = T::Format<'a> where Self: 'a; + + fn format(&self) -> Self::Format<'_> { + AsFormat::format(&**self) + } } -#[cfg(test)] -mod tests { - use super::*; +/// Implement [AsFormat] for [SyntaxResult] where `T` implements [AsFormat]. +/// +/// Useful to format mandatory AST fields without having to unwrap the value first. +impl AsFormat for biome_rowan::SyntaxResult +where + T: AsFormat, +{ + type Format<'a> = biome_rowan::SyntaxResult> where Self: 'a; + + fn format(&self) -> Self::Format<'_> { + match self { + Ok(value) => Ok(value.format()), + Err(err) => Err(*err), + } + } +} + +/// Implement [AsFormat] for [Option] when `T` implements [AsFormat] +/// +/// Allows to call format on optional AST fields without having to unwrap the field first. +impl AsFormat for Option +where + T: AsFormat, +{ + type Format<'a> = Option> where Self: 'a; + + fn format(&self) -> Self::Format<'_> { + self.as_ref().map(|value| value.format()) + } +} + +/// Used to convert this object into an object that can be formatted. +/// +/// The difference to [AsFormat] is that this trait takes ownership of `self`. +#[allow(dead_code)] +pub(crate) trait IntoFormat { + type Format: biome_formatter::Format; + + fn into_format(self) -> Self::Format; +} + +impl IntoFormat for biome_rowan::SyntaxResult +where + T: IntoFormat, +{ + type Format = biome_rowan::SyntaxResult; + + fn into_format(self) -> Self::Format { + self.map(IntoFormat::into_format) + } +} + +/// Implement [IntoFormat] for [Option] when `T` implements [IntoFormat] +/// +/// Allows to call format on optional AST fields without having to unwrap the field first. +impl IntoFormat for Option +where + T: IntoFormat, +{ + type Format = Option; + + fn into_format(self) -> Self::Format { + self.map(IntoFormat::into_format) + } +} + +/// Formatting specific [Iterator] extensions +#[allow(dead_code)] +pub(crate) trait FormattedIterExt { + /// Converts every item to an object that knows how to format it. + fn formatted(self) -> FormattedIter + where + Self: Iterator + Sized, + Self::Item: IntoFormat, + { + FormattedIter { + inner: self, + options: std::marker::PhantomData, + } + } +} + +impl FormattedIterExt for I where I: std::iter::Iterator {} + +#[allow(dead_code)] +pub(crate) struct FormattedIter +where + Iter: Iterator, +{ + inner: Iter, + options: std::marker::PhantomData, +} + +impl std::iter::Iterator for FormattedIter +where + Iter: Iterator, + Item: IntoFormat, +{ + type Item = Item::Format; + + fn next(&mut self) -> Option { + Some(self.inner.next()?.into_format()) + } +} + +impl std::iter::FusedIterator for FormattedIter +where + Iter: std::iter::FusedIterator, + Item: IntoFormat, +{ +} + +impl std::iter::ExactSizeIterator for FormattedIter +where + Iter: Iterator + std::iter::ExactSizeIterator, + Item: IntoFormat, +{ +} - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); +/// Rule for formatting an bogus nodes. +pub(crate) trait FormatBogusNodeRule +where + N: AstNode, +{ + fn fmt(&self, node: &N, f: &mut GritFormatter) -> FormatResult<()> { + format_bogus_node(node.syntax()).fmt(f) } } diff --git a/crates/biome_grit_formatter/src/prelude.rs b/crates/biome_grit_formatter/src/prelude.rs new file mode 100644 index 000000000000..3dc661422125 --- /dev/null +++ b/crates/biome_grit_formatter/src/prelude.rs @@ -0,0 +1,12 @@ +//! This module provides important and useful traits to help to format tokens and nodes +//! when implementing the [crate::FormatNodeRule] trait. + +#[allow(unused_imports)] +pub(crate) use crate::{ + AsFormat, FormatNodeRule, FormattedIterExt as _, GritFormatContext, GritFormatter, IntoFormat, +}; +pub(crate) use biome_formatter::prelude::*; +#[allow(unused_imports)] +pub(crate) use biome_rowan::{ + AstNode as _, AstNodeList as _, AstNodeSlotMap as _, AstSeparatedList as _, +}; diff --git a/crates/biome_unicode_table/src/tables.rs b/crates/biome_unicode_table/src/tables.rs index 2521478eca81..2016f15b83d8 100644 --- a/crates/biome_unicode_table/src/tables.rs +++ b/crates/biome_unicode_table/src/tables.rs @@ -71,7 +71,7 @@ pub mod derived_property { ('ࡠ', 'ࡪ'), ('ࡰ', 'ࢇ'), ('ࢉ', 'ࢎ'), - ('\u{898}', '\u{8e1}'), + ('\u{897}', '\u{8e1}'), ('\u{8e3}', '\u{963}'), ('०', '९'), ('ॱ', 'ঃ'), @@ -294,7 +294,7 @@ pub mod derived_property { ('ᰀ', '\u{1c37}'), ('᱀', '᱉'), ('ᱍ', 'ᱽ'), - ('ᲀ', 'ᲈ'), + ('ᲀ', '\u{1c8a}'), ('Ა', 'Ჺ'), ('Ჽ', 'Ჿ'), ('\u{1cd0}', '\u{1cd2}'), @@ -378,10 +378,10 @@ pub mod derived_property { ('ꙿ', '\u{a6f1}'), ('ꜗ', 'ꜟ'), ('Ꜣ', 'ꞈ'), - ('Ꞌ', 'ꟊ'), + ('Ꞌ', '\u{a7cd}'), ('Ꟑ', 'ꟑ'), ('ꟓ', 'ꟓ'), - ('ꟕ', 'ꟙ'), + ('ꟕ', '\u{a7dc}'), ('ꟲ', 'ꠧ'), ('\u{a82c}', '\u{a82c}'), ('ꡀ', 'ꡳ'), @@ -479,6 +479,7 @@ pub mod derived_property { ('𐖣', '𐖱'), ('𐖳', '𐖹'), ('𐖻', '𐖼'), + ('\u{105c0}', '\u{105f3}'), ('𐘀', '𐜶'), ('𐝀', '𐝕'), ('𐝠', '𐝧'), @@ -519,10 +520,14 @@ pub mod derived_property { ('𐳀', '𐳲'), ('𐴀', '\u{10d27}'), ('𐴰', '𐴹'), + ('\u{10d40}', '\u{10d65}'), + ('\u{10d69}', '\u{10d6d}'), + ('\u{10d6f}', '\u{10d85}'), ('𐺀', '𐺩'), ('\u{10eab}', '\u{10eac}'), ('𐺰', '𐺱'), - ('\u{10efd}', '𐼜'), + ('\u{10ec2}', '\u{10ec4}'), + ('\u{10efc}', '𐼜'), ('𐼧', '𐼧'), ('𐼰', '\u{10f50}'), ('𐽰', '\u{10f85}'), @@ -568,6 +573,16 @@ pub mod derived_property { ('𑍝', '𑍣'), ('\u{11366}', '\u{1136c}'), ('\u{11370}', '\u{11374}'), + ('\u{11380}', '\u{11389}'), + ('\u{1138b}', '\u{1138b}'), + ('\u{1138e}', '\u{1138e}'), + ('\u{11390}', '\u{113b5}'), + ('\u{113b7}', '\u{113c0}'), + ('\u{113c2}', '\u{113c2}'), + ('\u{113c5}', '\u{113c5}'), + ('\u{113c7}', '\u{113ca}'), + ('\u{113cc}', '\u{113d3}'), + ('\u{113e1}', '\u{113e2}'), ('𑐀', '𑑊'), ('𑑐', '𑑙'), ('\u{1145e}', '𑑡'), @@ -582,6 +597,7 @@ pub mod derived_property { ('𑙐', '𑙙'), ('𑚀', '𑚸'), ('𑛀', '𑛉'), + ('\u{116d0}', '\u{116e3}'), ('𑜀', '𑜚'), ('\u{1171d}', '\u{1172b}'), ('𑜰', '𑜹'), @@ -605,6 +621,8 @@ pub mod derived_property { ('𑩐', '\u{11a99}'), ('𑪝', '𑪝'), ('𑪰', '𑫸'), + ('\u{11bc0}', '\u{11be0}'), + ('\u{11bf0}', '\u{11bf9}'), ('𑰀', '𑰈'), ('𑰊', '\u{11c36}'), ('\u{11c38}', '𑱀'), @@ -629,7 +647,7 @@ pub mod derived_property { ('\u{11f00}', '𑼐'), ('𑼒', '\u{11f3a}'), ('𑼾', '\u{11f42}'), - ('𑽐', '𑽙'), + ('𑽐', '\u{11f5a}'), ('𑾰', '𑾰'), ('𒀀', '𒎙'), ('𒐀', '𒑮'), @@ -637,7 +655,9 @@ pub mod derived_property { ('𒾐', '𒿰'), ('𓀀', '𓐯'), ('\u{13440}', '\u{13455}'), + ('\u{13460}', '\u{143fa}'), ('𔐀', '𔙆'), + ('\u{16100}', '\u{16139}'), ('𖠀', '𖨸'), ('𖩀', '𖩞'), ('𖩠', '𖩩'), @@ -650,6 +670,8 @@ pub mod derived_property { ('𖭐', '𖭙'), ('𖭣', '𖭷'), ('𖭽', '𖮏'), + ('\u{16d40}', '\u{16d6c}'), + ('\u{16d70}', '\u{16d79}'), ('𖹀', '𖹿'), ('𖼀', '𖽊'), ('\u{16f4f}', '𖾇'), @@ -659,7 +681,7 @@ pub mod derived_property { ('𖿰', '𖿱'), ('𗀀', '𘟷'), ('𘠀', '𘳕'), - ('𘴀', '𘴈'), + ('\u{18cff}', '𘴈'), ('𚿰', '𚿳'), ('𚿵', '𚿻'), ('𚿽', '𚿾'), @@ -674,6 +696,7 @@ pub mod derived_property { ('𛲀', '𛲈'), ('𛲐', '𛲙'), ('\u{1bc9d}', '\u{1bc9e}'), + ('\u{1ccf0}', '\u{1ccf9}'), ('\u{1cf00}', '\u{1cf2d}'), ('\u{1cf30}', '\u{1cf46}'), ('\u{1d165}', '\u{1d169}'), @@ -735,6 +758,7 @@ pub mod derived_property { ('𞊐', '\u{1e2ae}'), ('𞋀', '𞋹'), ('𞓐', '𞓹'), + ('\u{1e5d0}', '\u{1e5fa}'), ('𞟠', '𞟦'), ('𞟨', '𞟫'), ('𞟭', '𞟮'), @@ -1016,7 +1040,7 @@ pub mod derived_property { ('ᰀ', 'ᰣ'), ('ᱍ', 'ᱏ'), ('ᱚ', 'ᱽ'), - ('ᲀ', 'ᲈ'), + ('ᲀ', '\u{1c8a}'), ('Ა', 'Ჺ'), ('Ჽ', 'Ჿ'), ('ᳩ', 'ᳬ'), @@ -1099,10 +1123,10 @@ pub mod derived_property { ('ꚠ', 'ꛯ'), ('ꜗ', 'ꜟ'), ('Ꜣ', 'ꞈ'), - ('Ꞌ', 'ꟊ'), + ('Ꞌ', '\u{a7cd}'), ('Ꟑ', 'ꟑ'), ('ꟓ', 'ꟓ'), - ('ꟕ', 'ꟙ'), + ('ꟕ', '\u{a7dc}'), ('ꟲ', 'ꠁ'), ('ꠃ', 'ꠅ'), ('ꠇ', 'ꠊ'), @@ -1200,6 +1224,7 @@ pub mod derived_property { ('𐖣', '𐖱'), ('𐖳', '𐖹'), ('𐖻', '𐖼'), + ('\u{105c0}', '\u{105f3}'), ('𐘀', '𐜶'), ('𐝀', '𐝕'), ('𐝠', '𐝧'), @@ -1236,8 +1261,11 @@ pub mod derived_property { ('𐲀', '𐲲'), ('𐳀', '𐳲'), ('𐴀', '𐴣'), + ('\u{10d4a}', '\u{10d65}'), + ('\u{10d6f}', '\u{10d85}'), ('𐺀', '𐺩'), ('𐺰', '𐺱'), + ('\u{10ec2}', '\u{10ec4}'), ('𐼀', '𐼜'), ('𐼧', '𐼧'), ('𐼰', '𐽅'), @@ -1276,6 +1304,13 @@ pub mod derived_property { ('𑌽', '𑌽'), ('𑍐', '𑍐'), ('𑍝', '𑍡'), + ('\u{11380}', '\u{11389}'), + ('\u{1138b}', '\u{1138b}'), + ('\u{1138e}', '\u{1138e}'), + ('\u{11390}', '\u{113b5}'), + ('\u{113b7}', '\u{113b7}'), + ('\u{113d1}', '\u{113d1}'), + ('\u{113d3}', '\u{113d3}'), ('𑐀', '𑐴'), ('𑑇', '𑑊'), ('𑑟', '𑑡'), @@ -1310,6 +1345,7 @@ pub mod derived_property { ('𑩜', '𑪉'), ('𑪝', '𑪝'), ('𑪰', '𑫸'), + ('\u{11bc0}', '\u{11be0}'), ('𑰀', '𑰈'), ('𑰊', '𑰮'), ('𑱀', '𑱀'), @@ -1333,7 +1369,9 @@ pub mod derived_property { ('𒾐', '𒿰'), ('𓀀', '𓐯'), ('𓑁', '𓑆'), + ('\u{13460}', '\u{143fa}'), ('𔐀', '𔙆'), + ('\u{16100}', '\u{1611d}'), ('𖠀', '𖨸'), ('𖩀', '𖩞'), ('𖩰', '𖪾'), @@ -1342,6 +1380,7 @@ pub mod derived_property { ('𖭀', '𖭃'), ('𖭣', '𖭷'), ('𖭽', '𖮏'), + ('\u{16d40}', '\u{16d6c}'), ('𖹀', '𖹿'), ('𖼀', '𖽊'), ('𖽐', '𖽐'), @@ -1350,7 +1389,7 @@ pub mod derived_property { ('𖿣', '𖿣'), ('𗀀', '𘟷'), ('𘠀', '𘳕'), - ('𘴀', '𘴈'), + ('\u{18cff}', '𘴈'), ('𚿰', '𚿳'), ('𚿵', '𚿻'), ('𚿽', '𚿾'), @@ -1403,6 +1442,8 @@ pub mod derived_property { ('𞊐', '𞊭'), ('𞋀', '𞋫'), ('𞓐', '𞓫'), + ('\u{1e5d0}', '\u{1e5ed}'), + ('\u{1e5f0}', '\u{1e5f0}'), ('𞟠', '𞟦'), ('𞟨', '𞟫'), ('𞟭', '𞟮'),