///|
fn markdown_editor_append_snippet(
source : String,
command : MarkdownEditorCommand,
) -> MarkdownEditorEditResult {
let snippet = markdown_editor_command_snippet(command)
let text = if source == "" { snippet } else { source + "\n\n" + snippet }
{ source: text, caret: markdown_editor_text_length(text), selection: None }
}
///|
fn markdown_editor_insert_table_of_contents(
source : String,
selection : MarkdownEditorSelection,
) -> MarkdownEditorEditResult {
let toc = markdown_editor_table_of_contents_markdown(source)
let range = markdown_editor_active_selection(selection.selection)
.unwrap_or(@core.TextRange::new(start=selection.caret, end=selection.caret))
.normalized()
markdown_editor_replace_with_block(source, range, toc)
}
///|
fn markdown_editor_insert_front_matter(
source : String,
) -> MarkdownEditorEditResult {
let blocks = parse_markdown_editor_blocks(source)
if blocks.length() > 0 && blocks[0].block.kind is FrontMatter {
return {
source,
caret: blocks[0].content_range.end,
selection: Some(
@core.TextRange::new(
start=blocks[0].content_range.start,
end=blocks[0].content_range.end,
),
),
}
}
let snippet = markdown_editor_command_snippet(InsertFrontMatter)
let text = if source == "" { snippet } else { snippet + "\n\n" + source }
{ source: text, caret: markdown_editor_text_length(snippet), selection: None }
}
///|
fn markdown_editor_table_of_contents_markdown(source : String) -> String {
let outline = MarkdownEditorSnapshot::parse(source).outline
if outline.length() == 0 {
return markdown_editor_command_snippet(InsertTableOfContents)
}
let rows : Array[String] = ["## Contents"]
for item in outline {
let title = if item.title.trim().to_owned() == "" {
"Untitled"
} else {
item.title.trim().to_owned()
}
let indent = markdown_editor_toc_indent(item.level)
rows.push(
indent +
"- [" +
markdown_editor_toc_escape_link_text(title) +
"](#" +
markdown_editor_toc_slug(title) +
")",
)
}
markdown_join_lines(rows)
}
///|
fn markdown_editor_replace_with_block(
source : String,
range : @core.TextRange,
block : String,
) -> MarkdownEditorEditResult {
let chars = source.to_array()
let start = markdown_editor_clamp_int(range.start, 0, chars.length())
let end = markdown_editor_clamp_int(range.end, start, chars.length())
let before = String::from_array(chars[:start])
let after = String::from_array(chars[end:])
let prefix = markdown_editor_block_insert_prefix(chars, start)
let suffix = markdown_editor_block_insert_suffix(chars, end)
let text = before + prefix + block + suffix + after
let caret = markdown_editor_text_length(before + prefix + block)
{ source: text, caret, selection: None }
}
///|
fn markdown_editor_toc_indent(level : Int) -> String {
let spaces : Array[Char] = []
let count = markdown_editor_max_int(0, level - 1) * 2
for _ in 0.. String {
let chars : Array[Char] = []
for ch in text {
if ch == '[' || ch == ']' || ch == '\\' {
chars.push('\\')
}
chars.push(ch)
}
String::from_array(chars)
}
///|
fn markdown_editor_toc_slug(text : String) -> String {
let chars : Array[Char] = []
let mut previous_dash = false
for ch in text.to_lower() {
if markdown_editor_toc_slug_char(ch) {
chars.push(ch)
previous_dash = false
} else if ch.is_ascii_whitespace() || ch == '-' {
if !previous_dash && chars.length() > 0 {
chars.push('-')
previous_dash = true
}
}
}
while chars.length() > 0 && chars[chars.length() - 1] == '-' {
ignore(chars.pop())
}
if chars.length() == 0 {
"section"
} else {
String::from_array(chars)
}
}
///|
fn markdown_editor_toc_slug_char(ch : Char) -> Bool {
(ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_'
}
///|
fn markdown_editor_toggle_link_range(
source : String,
range : @core.TextRange,
) -> MarkdownEditorEditResult {
let normalized = range.normalized()
match markdown_editor_range_link_bounds(source, normalized) {
Some(bounds) =>
markdown_editor_unwrap_link_range(source, normalized, bounds)
None => markdown_editor_wrap_link_range(source, normalized)
}
}
///|
fn markdown_editor_toggle_image_range(
source : String,
range : @core.TextRange,
) -> MarkdownEditorEditResult {
let normalized = range.normalized()
match markdown_editor_range_image_bounds(source, normalized) {
Some(bounds) =>
markdown_editor_unwrap_image_range(source, normalized, bounds)
None => markdown_editor_wrap_image_range(source, normalized)
}
}
///|
fn markdown_editor_toggle_reference_at_caret(
source : String,
selection : MarkdownEditorSelection,
command : MarkdownEditorCommand,
) -> MarkdownEditorEditResult {
if command == Image {
match markdown_editor_image_context(source, selection) {
Some(context) =>
markdown_editor_unwrap_image_range(
source,
context.range,
context.bounds,
)
None => markdown_editor_append_snippet(source, command)
}
} else {
match markdown_editor_link_context(source, selection) {
Some(context) =>
markdown_editor_unwrap_link_range(source, context.range, context.bounds)
None => markdown_editor_append_snippet(source, command)
}
}
}
///|
fn markdown_editor_set_reference_target(
source : String,
selection : MarkdownEditorSelection,
command : MarkdownEditorCommand,
target : String,
) -> MarkdownEditorEditResult {
let active = markdown_editor_active_selection(selection.selection)
let range = active
.unwrap_or(@core.TextRange::new(start=selection.caret, end=selection.caret))
.normalized()
if command == SetImageTarget(target) {
match markdown_editor_image_context(source, selection) {
Some(context) =>
return markdown_editor_replace_reference_target(
source,
context.range,
context.bounds,
target,
)
None =>
match active {
Some(_) =>
return markdown_editor_wrap_image_range_with_target(
source, range, target,
)
None => return markdown_editor_append_snippet(source, command)
}
}
}
match markdown_editor_link_context(source, selection) {
Some(context) =>
markdown_editor_replace_reference_target(
source,
context.range,
context.bounds,
target,
)
None =>
match active {
Some(_) =>
markdown_editor_wrap_link_range_with_target(source, range, target)
None => markdown_editor_append_snippet(source, command)
}
}
}
///|