///|
pub fn markdown_editor_selected_link_target(
source : String,
selection : MarkdownEditorSelection,
) -> String? {
match markdown_editor_link_context(source, selection) {
Some(context) => markdown_editor_reference_target(source, context.bounds)
None => None
}
}
///|
pub fn markdown_editor_selected_image_target(
source : String,
selection : MarkdownEditorSelection,
) -> String? {
match markdown_editor_image_context(source, selection) {
Some(context) => markdown_editor_reference_target(source, context.bounds)
None => None
}
}
///|
pub(all) struct MarkdownReferenceContext {
range : @core.TextRange
bounds : MarkdownLinkBounds
} derive(Eq, Debug)
///|
pub fn markdown_editor_link_context(
source : String,
selection : MarkdownEditorSelection,
) -> MarkdownReferenceContext? {
match markdown_editor_active_selection(selection.selection) {
Some(range) => {
let range = range.normalized()
match markdown_editor_range_link_bounds(source, range) {
Some(bounds) => Some({ range, bounds })
None => None
}
}
None => markdown_editor_link_context_at_caret(source, selection.caret)
}
}
///|
pub fn markdown_editor_image_context(
source : String,
selection : MarkdownEditorSelection,
) -> MarkdownReferenceContext? {
match markdown_editor_active_selection(selection.selection) {
Some(range) => {
let range = range.normalized()
match markdown_editor_range_image_bounds(source, range) {
Some(bounds) => Some({ range, bounds })
None => None
}
}
None => markdown_editor_image_context_at_caret(source, selection.caret)
}
}
///|
fn markdown_editor_link_context_at_caret(
source : String,
caret : Int,
) -> MarkdownReferenceContext? {
let chars = source.to_array()
let caret = markdown_editor_clamp_int(caret, 0, chars.length())
match markdown_editor_autolink_context_at_caret(source, chars, caret) {
Some(context) => return Some(context)
None => ()
}
match
markdown_editor_bracketed_reference_range_at_caret(
chars,
caret,
image=false,
) {
Some(range) =>
match markdown_editor_range_link_bounds(source, range) {
Some(bounds) => Some({ range, bounds })
None => None
}
None => None
}
}
///|
fn markdown_editor_image_context_at_caret(
source : String,
caret : Int,
) -> MarkdownReferenceContext? {
let chars = source.to_array()
let caret = markdown_editor_clamp_int(caret, 0, chars.length())
match
markdown_editor_bracketed_reference_range_at_caret(chars, caret, image=true) {
Some(range) =>
match markdown_editor_range_image_bounds(source, range) {
Some(bounds) => Some({ range, bounds })
None => None
}
None => None
}
}
///|
fn markdown_editor_autolink_context_at_caret(
source : String,
chars : Array[Char],
caret : Int,
) -> MarkdownReferenceContext? {
let mut open = caret
while open > 0 && chars[open - 1] != '<' && chars[open - 1] != '\n' {
open = open - 1
}
if open <= 0 || chars[open - 1] != '<' {
return None
}
let mut close = caret
while close < chars.length() && chars[close] != '>' && chars[close] != '\n' {
close = close + 1
}
if close >= chars.length() || chars[close] != '>' {
return None
}
let range = @core.TextRange::new(start=open, end=close)
match markdown_editor_range_link_bounds(source, range) {
Some(bounds) => Some({ range, bounds })
None => None
}
}
///|
fn markdown_editor_bracketed_reference_range_at_caret(
chars : Array[Char],
caret : Int,
image~ : Bool,
) -> @core.TextRange? {
let mut open = caret
while open > 0 && chars[open - 1] != '[' && chars[open - 1] != '\n' {
open = open - 1
}
if open <= 0 || chars[open - 1] != '[' {
return None
}
let open_start = open - 1
if image {
if open_start <= 0 || chars[open_start - 1] != '!' {
return None
}
} else if open_start > 0 && chars[open_start - 1] == '!' {
return None
}
let mut close = open
while close < chars.length() && chars[close] != ']' && chars[close] != '\n' {
close = close + 1
}
if close >= chars.length() || chars[close] != ']' || caret > close {
return None
}
Some(@core.TextRange::new(start=open, end=close))
}
///|
pub fn markdown_editor_selected_footnote_text(
source : String,
selection : MarkdownEditorSelection,
) -> String? {
match markdown_editor_footnote_label_at_selection(source, selection) {
Some(label) => markdown_editor_footnote_definition_text(source, label)
None => None
}
}
///|
pub fn markdown_editor_selected_html_block_text(
source : String,
selection : MarkdownEditorSelection,
) -> String? {
match markdown_editor_html_block_at_selection(source, selection) {
Some(block) => Some(block.block.text)
None => None
}
}
///|
pub fn markdown_editor_active_block_label(
source : String,
selection : MarkdownEditorSelection,
) -> String {
let anchor = match markdown_editor_active_selection(selection.selection) {
Some(range) => range.start
None => selection.caret
}
let anchor = markdown_editor_clamp_int(
anchor,
0,
markdown_editor_text_length(source),
)
for block in parse_markdown_editor_blocks(source) {
if anchor >= block.source_range.start && anchor <= block.source_range.end {
return markdown_editor_block_kind_label(block.block.kind)
}
}
"Paragraph"
}
///|
fn markdown_editor_code_block_at_content_offset(
source : String,
caret : Int,
selection : @core.TextRange?,
) -> MarkdownEditorBlock? {
if markdown_editor_active_selection(selection) is Some(_) {
return None
}
let caret = markdown_editor_clamp_int(
caret,
0,
markdown_editor_text_length(source),
)
for block in parse_markdown_editor_blocks(source) {
if block.block.kind is MarkdownBlockKind::CodeBlock &&
caret >= block.content_range.start &&
caret <= block.content_range.end {
return Some(block)
}
}
None
}
///|
fn markdown_editor_code_block_at_selection(
source : String,
selection : MarkdownEditorSelection,
) -> MarkdownEditorBlock? {
if markdown_editor_active_selection(selection.selection) is Some(_) {
return None
}
let caret = markdown_editor_clamp_int(
selection.caret,
0,
markdown_editor_text_length(source),
)
for block in parse_markdown_editor_blocks(source) {
if block.block.kind is MarkdownBlockKind::CodeBlock &&
caret >= block.source_range.start &&
caret <= block.source_range.end {
return Some(block)
}
}
None
}
///|
fn markdown_editor_html_block_at_selection(
source : String,
selection : MarkdownEditorSelection,
) -> MarkdownEditorBlock? {
if markdown_editor_active_selection(selection.selection) is Some(_) {
return None
}
let caret = markdown_editor_clamp_int(
selection.caret,
0,
markdown_editor_text_length(source),
)
for block in parse_markdown_editor_blocks(source) {
if block.block.kind is MarkdownBlockKind::HtmlBlock &&
caret >= block.source_range.start &&
caret <= block.source_range.end {
return Some(block)
}
}
None
}
///|
fn markdown_editor_code_block_enter(
source : String,
caret : Int,
) -> RichTextInputTransform {
let chars = source.to_array()
let caret = markdown_editor_clamp_int(caret, 0, chars.length())
let line_start = markdown_editor_line_start(chars, caret)
let indent = markdown_editor_line_indent(chars, line_start, caret)
let insert = "\n" + indent
markdown_editor_transform_result(
text=String::from_array(chars[:caret]) +
insert +
String::from_array(chars[caret:]),
caret=caret + markdown_editor_text_length(insert),
)
}
///|
pub(all) struct MarkdownLinkBounds {
open_start : Int
close_start : Int
target_end : Int
target_start : Int
definition_target_start : Int
definition_target_end : Int
reference_label : String
} derive(Eq, Debug)
///|
fn markdown_editor_range_link_bounds(
source : String,
range : @core.TextRange,
) -> MarkdownLinkBounds? {
let chars = source.to_array()
match markdown_editor_range_autolink_bounds(chars, range) {
Some(bounds) => return Some(bounds)
None => ()
}
if range.start < 1 || range.end + 2 > chars.length() {
return None
}
let open_start = range.start - 1
if open_start > 0 && chars[open_start - 1] == '!' {
return None
}
if chars[open_start] != '[' || chars[range.end] != ']' {
return None
}
if chars[range.end + 1] == '[' {
return markdown_editor_reference_link_bounds(
source,
chars,
range,
open_start,
bang=false,
)
}
if chars[range.end + 1] != '(' {
return markdown_editor_shortcut_reference_link_bounds(
source, chars, range, open_start,
)
}
let mut index = range.end + 2
while index < chars.length() && chars[index] != ')' && chars[index] != '\n' {
index = index + 1
}
if index < chars.length() && chars[index] == ')' {
Some({
open_start,
close_start: range.end,
target_start: range.end + 2,
target_end: index,
definition_target_start: -1,
definition_target_end: -1,
reference_label: "",
})
} else {
None
}
}
///|
fn markdown_editor_range_autolink_bounds(
chars : Array[Char],
range : @core.TextRange,
) -> MarkdownLinkBounds? {
if range.start < 1 || range.end >= chars.length() {
return None
}
if chars[range.start - 1] != '<' || chars[range.end] != '>' {
return None
}
let target = String::from_array(chars[range.start:range.end])
if !markdown_editor_is_autolink_target(target) {
return None
}
Some({
open_start: range.start - 1,
close_start: range.end,
target_start: range.start,
target_end: range.end,
definition_target_start: -1,
definition_target_end: -1,
reference_label: "",
})
}
///|
fn markdown_editor_shortcut_reference_link_bounds(
source : String,
chars : Array[Char],
range : @core.TextRange,
open_start : Int,
) -> MarkdownLinkBounds? {
let label = String::from_array(chars[range.start:range.end])
if label == "" {
return None
}
match markdown_editor_reference_definition_target_range(source, label) {
Some(target) =>
Some({
open_start,
close_start: range.end,
target_start: range.end,
target_end: range.end,
definition_target_start: target.start,
definition_target_end: target.end,
reference_label: label,
})
None =>
Some({
open_start,
close_start: range.end,
target_start: range.end,
target_end: range.end,
definition_target_start: -1,
definition_target_end: -1,
reference_label: label,
})
}
}
///|
fn markdown_editor_range_image_bounds(
source : String,
range : @core.TextRange,
) -> MarkdownLinkBounds? {
let chars = source.to_array()
if range.start < 2 || range.end + 2 > chars.length() {
return None
}
let bang_start = range.start - 2
let open_start = range.start - 1
if chars[bang_start] != '!' ||
chars[open_start] != '[' ||
chars[range.end] != ']' {
return None
}
if chars[range.end + 1] == '[' {
return markdown_editor_reference_link_bounds(
source,
chars,
range,
bang_start,
bang=true,
)
}
if chars[range.end + 1] != '(' {
return None
}
let mut index = range.end + 2
while index < chars.length() && chars[index] != ')' && chars[index] != '\n' {
index = index + 1
}
if index < chars.length() && chars[index] == ')' {
Some({
open_start: bang_start,
close_start: range.end,
target_start: range.end + 2,
target_end: index,
definition_target_start: -1,
definition_target_end: -1,
reference_label: "",
})
} else {
None
}
}
///|
fn markdown_editor_reference_link_bounds(
source : String,
chars : Array[Char],
range : @core.TextRange,
open_start : Int,
bang~ : Bool,
) -> MarkdownLinkBounds? {
let label_start = range.end + 2
let mut label_end = label_start
while label_end < chars.length() &&
chars[label_end] != ']' &&
chars[label_end] != '\n' {
label_end = label_end + 1
}
if label_end >= chars.length() || chars[label_end] != ']' {
return None
}
let fallback_start = if bang { open_start + 2 } else { open_start + 1 }
let label = if label_end == label_start {
String::from_array(chars[fallback_start:range.end])
} else {
String::from_array(chars[label_start:label_end])
}
match markdown_editor_reference_definition_target_range(source, label) {
Some(target) =>
Some({
open_start,
close_start: range.end,
target_start: label_start,
target_end: label_end,
definition_target_start: target.start,
definition_target_end: target.end,
reference_label: label,
})
None =>
Some({
open_start,
close_start: range.end,
target_start: label_start,
target_end: label_end,
definition_target_start: -1,
definition_target_end: -1,
reference_label: label,
})
}
}
///|
fn markdown_editor_reference_definition_target_range(
source : String,
label : String,
) -> MarkdownSourceRange? {
let key = markdown_reference_label_key(label)
for line in markdown_editor_lines(source) {
match markdown_editor_reference_definition_line_target_range(line) {
Some((definition_label, target)) =>
if markdown_reference_label_key(definition_label) == key {
return Some(target)
}
None => ()
}
}
None
}
///|
fn markdown_editor_reference_definition_line_target_range(
line : MarkdownLine,
) -> (String, MarkdownSourceRange)? {
let chars = line.text.to_array()
let start = markdown_editor_leading_indent_length(chars)
if start >= chars.length() || chars[start] != '[' {
return None
}
let mut close = start + 1
while close < chars.length() && chars[close] != ']' {
close = close + 1
}
if close <= start + 1 ||
close + 1 >= chars.length() ||
chars[close + 1] != ':' {
return None
}
let mut target_start = close + 2
while target_start < chars.length() &&
(chars[target_start] == ' ' || chars[target_start] == '\t') {
target_start = target_start + 1
}
if target_start >= chars.length() {
return None
}
let mut target_end = target_start
if chars[target_start] == '<' {
target_start = target_start + 1
target_end = target_start
while target_end < chars.length() && chars[target_end] != '>' {
target_end = target_end + 1
}
} else {
while target_end < chars.length() &&
chars[target_end] != ' ' &&
chars[target_end] != '\t' {
target_end = target_end + 1
}
}
if target_end <= target_start {
return None
}
Some(
(
String::from_array(chars[start + 1:close]),
{ start: line.start + target_start, end: line.start + target_end },
),
)
}
///|
fn markdown_editor_wrap_link_range(
source : String,
range : @core.TextRange,
) -> MarkdownEditorEditResult {
markdown_editor_wrap_link_range_with_target(
source, range, "https://example.com",
)
}
///|
fn markdown_editor_wrap_link_range_with_target(
source : String,
range : @core.TextRange,
target : String,
) -> MarkdownEditorEditResult {
let source_length = markdown_editor_text_length(source)
let start = markdown_editor_clamp_int(range.start, 0, source_length)
let end = markdown_editor_clamp_int(range.end, start, source_length)
let selected = markdown_editor_substring(source, start, end)
let text = markdown_editor_substring(source, 0, start) +
"[" +
selected +
"](" +
target +
")" +
markdown_editor_substring(source, end, source_length)
let next_selection = @core.TextRange::new(
start=start + 1,
end=start + 1 + end - start,
)
{ source: text, caret: next_selection.end, selection: Some(next_selection) }
}
///|
fn markdown_editor_wrap_image_range(
source : String,
range : @core.TextRange,
) -> MarkdownEditorEditResult {
markdown_editor_wrap_image_range_with_target(source, range, "image.png")
}
///|
fn markdown_editor_wrap_image_range_with_target(
source : String,
range : @core.TextRange,
target : String,
) -> MarkdownEditorEditResult {
let source_length = markdown_editor_text_length(source)
let start = markdown_editor_clamp_int(range.start, 0, source_length)
let end = markdown_editor_clamp_int(range.end, start, source_length)
let selected = markdown_editor_substring(source, start, end)
let text = markdown_editor_substring(source, 0, start) +
"" +
markdown_editor_substring(source, end, source_length)
let next_selection = @core.TextRange::new(
start=start + 2,
end=start + 2 + end - start,
)
{ source: text, caret: next_selection.end, selection: Some(next_selection) }
}
///|
fn markdown_editor_replace_reference_target(
source : String,
range : @core.TextRange,
bounds : MarkdownLinkBounds,
target : String,
) -> MarkdownEditorEditResult {
if bounds.definition_target_start < 0 && bounds.reference_label != "" {
let text = source +
markdown_editor_reference_definition_append_prefix(source) +
"[" +
bounds.reference_label +
"]: " +
target
let next_selection = @core.TextRange::new(start=range.start, end=range.end)
return {
source: text,
caret: next_selection.end,
selection: Some(next_selection),
}
}
let target_start = if bounds.definition_target_start >= 0 {
bounds.definition_target_start
} else {
bounds.target_start
}
let target_end = if bounds.definition_target_end >= 0 {
bounds.definition_target_end
} else {
bounds.target_end
}
let source_length = markdown_editor_text_length(source)
let text = markdown_editor_substring(source, 0, target_start) +
target +
markdown_editor_substring(source, target_end, source_length)
let next_selection = @core.TextRange::new(start=range.start, end=range.end)
{ source: text, caret: next_selection.end, selection: Some(next_selection) }
}
///|
fn markdown_editor_reference_definition_append_prefix(
source : String,
) -> String {
if source == "" {
""
} else if source.has_suffix("\n\n") {
""
} else if source.has_suffix("\n") {
"\n"
} else {
"\n\n"
}
}
///|
fn markdown_editor_footnote_label_at_selection(
source : String,
selection : MarkdownEditorSelection,
) -> String? {
let anchor = match markdown_editor_active_selection(selection.selection) {
Some(range) => range.start
None => selection.caret
}
let anchor = markdown_editor_clamp_int(
anchor,
0,
markdown_editor_text_length(source),
)
match markdown_editor_footnote_reference_label_at(source, anchor) {
Some(label) => Some(label)
None => markdown_editor_footnote_definition_label_at(source, anchor)
}
}
///|
fn markdown_editor_footnote_reference_label_at(
source : String,
anchor : Int,
) -> String? {
let chars = source.to_array()
if chars.length() == 0 {
return None
}
let mut open = markdown_editor_clamp_int(anchor, 0, chars.length() - 1)
while open > 0 && chars[open] != '[' {
open = open - 1
}
if open + 3 >= chars.length() || chars[open] != '[' || chars[open + 1] != '^' {
return None
}
let mut close = open + 2
while close < chars.length() && chars[close] != ']' && chars[close] != '\n' {
close = close + 1
}
if close >= chars.length() || chars[close] != ']' {
return None
}
if anchor < open + 2 || anchor > close {
return None
}
Some(String::from_array(chars[open + 2:close]))
}
///|
fn markdown_editor_footnote_definition_label_at(
source : String,
anchor : Int,
) -> String? {
for line in markdown_editor_lines(source) {
if anchor >= line.start && anchor <= line.end {
match markdown_editor_footnote_definition_line_text_range(line) {
Some((label, _)) => return Some(label)
None => return None
}
}
}
None
}
///|
fn markdown_editor_footnote_definition_text(
source : String,
label : String,
) -> String? {
let key = markdown_reference_label_key(label)
let chars = source.to_array()
for line in markdown_editor_lines(source) {
match markdown_editor_footnote_definition_line_text_range(line) {
Some((definition_label, range)) =>
if markdown_reference_label_key(definition_label) == key {
return Some(String::from_array(chars[range.start:range.end]))
}
None => ()
}
}
None
}
///|
fn markdown_editor_footnote_definition_line_text_range(
line : MarkdownLine,
) -> (String, MarkdownSourceRange)? {
let chars = line.text.to_array()
let start = markdown_editor_leading_indent_length(chars)
if start + 4 >= chars.length() ||
chars[start] != '[' ||
chars[start + 1] != '^' {
return None
}
let mut close = start + 2
while close < chars.length() && chars[close] != ']' {
close = close + 1
}
if close <= start + 2 ||
close + 1 >= chars.length() ||
chars[close + 1] != ':' {
return None
}
let mut text_start = close + 2
while text_start < chars.length() &&
(chars[text_start] == ' ' || chars[text_start] == '\t') {
text_start = text_start + 1
}
Some(
(
String::from_array(chars[start + 2:close]),
{ start: line.start + text_start, end: line.end },
),
)
}
///|
fn markdown_editor_set_footnote_text(
source : String,
selection : MarkdownEditorSelection,
text : String,
) -> MarkdownEditorEditResult {
match markdown_editor_footnote_label_at_selection(source, selection) {
Some(label) =>
match
markdown_editor_replace_footnote_definition_text(source, label, text) {
Some(result) => result
None =>
markdown_editor_append_footnote_definition(
source, label, text, selection,
)
}
None => markdown_editor_append_snippet(source, SetFootnoteText(text))
}
}
///|
fn markdown_editor_replace_footnote_definition_text(
source : String,
label : String,
text : String,
) -> MarkdownEditorEditResult? {
let key = markdown_reference_label_key(label)
let chars = source.to_array()
for line in markdown_editor_lines(source) {
match markdown_editor_footnote_definition_line_text_range(line) {
Some((definition_label, range)) =>
if markdown_reference_label_key(definition_label) == key {
let next_source = String::from_array(chars[:range.start]) +
text +
String::from_array(chars[range.end:])
return Some({
source: next_source,
caret: range.start + markdown_editor_text_length(text),
selection: None,
})
}
None => ()
}
}
None
}
///|
fn markdown_editor_append_footnote_definition(
source : String,
label : String,
text : String,
selection : MarkdownEditorSelection,
) -> MarkdownEditorEditResult {
let next_source = source +
markdown_editor_reference_definition_append_prefix(source) +
"[^" +
label +
"]: " +
text
{
source: next_source,
caret: selection.caret,
selection: selection.selection,
}
}
///|
fn markdown_editor_reference_target(
source : String,
bounds : MarkdownLinkBounds,
) -> String? {
let start = if bounds.definition_target_start >= 0 {
bounds.definition_target_start
} else {
bounds.target_start
}
let end = if bounds.definition_target_end >= 0 {
bounds.definition_target_end
} else {
bounds.target_end
}
let source_length = markdown_editor_text_length(source)
if start > end || end > source_length {
None
} else {
Some(markdown_editor_substring(source, start, end))
}
}
///|
fn markdown_editor_unwrap_link_range(
source : String,
range : @core.TextRange,
bounds : MarkdownLinkBounds,
) -> MarkdownEditorEditResult {
let source_length = markdown_editor_text_length(source)
let text = markdown_editor_substring(source, 0, bounds.open_start) +
markdown_editor_substring(source, range.start, range.end) +
markdown_editor_substring(source, bounds.target_end + 1, source_length)
let next_selection = @core.TextRange::new(
start=bounds.open_start,
end=bounds.open_start + range.end - range.start,
)
{ source: text, caret: next_selection.end, selection: Some(next_selection) }
}
///|
fn markdown_editor_unwrap_image_range(
source : String,
range : @core.TextRange,
bounds : MarkdownLinkBounds,
) -> MarkdownEditorEditResult {
let source_length = markdown_editor_text_length(source)
let text = markdown_editor_substring(source, 0, bounds.open_start) +
markdown_editor_substring(source, range.start, range.end) +
markdown_editor_substring(source, bounds.target_end + 1, source_length)
let next_selection = @core.TextRange::new(
start=bounds.open_start,
end=bounds.open_start + range.end - range.start,
)
{ source: text, caret: next_selection.end, selection: Some(next_selection) }
}
///|
fn markdown_editor_selected_line_range(
source : String,
selection : MarkdownEditorSelection,
) -> MarkdownSourceRange {
let chars = source.to_array()
match markdown_editor_active_selection(selection.selection) {
Some(active) => {
let start_anchor = markdown_editor_clamp_int(
active.start,
0,
chars.length(),
)
let mut end_anchor = markdown_editor_clamp_int(
active.end,
start_anchor,
chars.length(),
)
if end_anchor > start_anchor && chars[end_anchor - 1] == '\n' {
end_anchor = end_anchor - 1
}
markdown_editor_line_range_around(chars, start_anchor, end_anchor)
}
None => markdown_editor_current_line_range(source, selection)
}
}
///|
fn markdown_editor_line_range_around(
chars : Array[Char],
start_anchor : Int,
end_anchor : Int,
) -> MarkdownSourceRange {
let mut start = start_anchor
while start > 0 && chars[start - 1] != '\n' {
start = start - 1
}
let mut end = end_anchor
while end < chars.length() && chars[end] != '\n' {
end = end + 1
}
{ start, end }
}
///|
fn markdown_editor_current_line_range(
source : String,
selection : MarkdownEditorSelection,
) -> MarkdownSourceRange {
let chars = source.to_array()
markdown_editor_current_line_range_from_chars(chars, selection)
}
///|
fn markdown_editor_current_line_range_from_chars(
chars : Array[Char],
selection : MarkdownEditorSelection,
) -> MarkdownSourceRange {
let active = markdown_editor_active_selection(selection.selection)
let caret = match active {
Some(range) => range.start
None => selection.caret
}
let caret = markdown_editor_clamp_int(caret, 0, chars.length())
let mut start = caret
while start > 0 && chars[start - 1] != '\n' {
start = start - 1
}
let mut end = caret
while end < chars.length() && chars[end] != '\n' {
end = end + 1
}
{ start, end }
}
///|
pub fn markdown_editor_active_selection(
selection : @core.TextRange?,
) -> @core.TextRange? {
match selection {
Some(selection) => {
let normalized = selection.normalized()
if normalized.is_collapsed() {
None
} else {
Some(normalized)
}
}
None => None
}
}
///|