Markdown Converter
Agent skill for markdown-converter
Refactor MoonBit code to be idiomatic: shrink public APIs, convert functions to methods, use pattern matching with views, add loop invariants, and ensure test coverage without regressions.
Loading actions...
Start broad, then refine locally:
moon doc, moon ide find-references).moon check, then moon test.Avoid local cleanups (renaming, pattern matching) until the high-level structure is sound.
Files in MoonBit are just organizational—move code freely within a package as long as each file stays focused on one concept.
When spinning off package A into A and B:
Create the new package and re-export temporarily:
// In package B
using @A { ... } // re-export A's APIs
Ensure moon check passes before proceeding.
Find and update all call sites:
moon ide find-references <symbol>
Replace bare f with @B.f.
Remove the use statement once all call sites are updated.
Audit and remove newly-unused pub APIs from both packages.
internal/ package for helpers that shouldn't leak.pub from helpers; keep only required exports.internal/ packages to block external imports... for fluent, mutating chains when it reads clearly.Example:
// Before
fn reader_next(r : Reader) -> Char? { ... }
let ch = reader_next(r)
// After
fn Reader::next(self : Reader) -> Char? { ... }
let ch = r.next()
Example (chaining):
buf..write_string("#\\")..write_char(ch)
@pkg.fn instead of using when clarity matters.Example:
let n = @parser.parse_number(token)
TypePath::Constr when the surrounding type is known.Example:
match tree { // the type of tree is known to be Tree
Leaf(x) => x // no need for Tree::Leaf
Node(left~, x, right~) => left.sum() + x + right.sum()
}
.. in the middle to match prefix and suffix at once.Array[Char].String/StringView indexing yields UInt16 code units. Use for ch in s for Unicode-aware iteration.Examples:
match items {
[] => ()
[head, ..tail] => handle(head, tail)
[..prefix, mid, ..suffix] => handle_mid(prefix, mid, suffix)
}
match s {
"" => ()
[.."let", ..rest] => handle_let(rest)
_ => ()
}
MoonBit allows char literal overloading for Char, UInt16, and Int, so the examples below work. This is handy when matching String indexing results (UInt16) against a char range.
test {
let a_int : Int = 'b'
if (a_int is 'a'..<'z') { () } else { () }
let a_u16 : UInt16 = 'b'
if (a_u16 is 'a'..<'z') { () } else { () }
let a_char : Char = 'b'
if (a_char is 'a'..<'z') { () } else { () }
}
isis patterns inside if/guard to keep branches concise.Example:
match token {
Some(Ident([.."@", ..rest])) if process(rest) is Some(x) => handle_at(rest)
Some(Ident(name)) => handle_ident(name)
None => ()
}
for i in start..<end { ... }, for i in start..<=end { ... }, for i in large>..small, or for i in large>=..small for simple index loops.for loops for algorithms that update state.Example:
// Before
for i = 0; i < len; {
items.push(fill)
continue i + 1
}
// After
for i in 0..<len {
items.push(fill)
}
for x in xs loops.Example:
for i = 0, acc = 0; i < xs.length(); {
acc = acc + xs[i]
i = i + 1
} else { acc }
where {
invariant: 0 <= i <= xs.length(),
reasoning: (
#| ... rigorous explanation ...
#| ...
)
}
*_test.mbt or *.mbt.md.mbt check for public APIs.Example:
///|
/// Return the last element of a non-empty array.
///
/// # Example
/// ```mbt check
/// test {
/// inspect(last([1, 2, 3]), content="3")
/// }
/// ```
pub fn last(xs : Array[Int]) -> Int { ... }
Commands:
moon coverage analyze -- -f summary
moon coverage analyze -- -f caret -F path/to/file.mbt
moon doc "<query>"
moon ide outline <dir|file>
moon ide find-references <symbol>
moon ide peek-def <symbol>
moon check
moon test
moon info
These commands are useful for reliable refactoring.
Example: spinning off package_b from package_a.
Temporary import in package_b:
using @package_a { a, type B }
Steps:
moon ide find-references <symbol> to find all call sites of a and B.@package_a.a and @package_a.B.using statement and run moon check.