plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


plum-core/tests/formatter_test.rs
#![allow(non_snake_case)]
use plum_core::formatSource;

#[test]
fn formatsSimpleFunction() {
    let input = "fun main() =\n  x = 1\n";
    let result = formatSource(input).expect("formatSource should succeed");
    assert!(result.contains("main()"));
    assert!(result.contains("x ="));
}

#[test]
fn formatsBinaryOperatorSpacing() {
    let input = "fun add(a: Int, b: Int) -> Int =\n  a+b\n";
    let result = formatSource(input).expect("formatSource should succeed");
    assert!(result.contains("a + b"));
}

#[test]
fn rejectsSyntaxError() {
    let input = "@@@@invalid@@@@\n";
    let result = formatSource(input);
    assert!(result.is_err());
}

#[test]
fn idempotentOnAlreadyFormatted() {
    let input = "fun main() =\n  x = 1 + 2\n";
    let first = formatSource(input).expect("first pass");
    let second = formatSource(&first).expect("second pass");
    assert_eq!(first, second, "formatting should be idempotent");
}

/// Regression test for a real bug: Topiary inserts NO whitespace between
/// adjacent leaves unless a query says to, so keywords with no explicit
/// spacing rule glue to the next token ("module" + "test" -> "moduletest").
/// This exercises most of the keyword vocabulary in one pass.
#[test]
fn keywordsGetSpacedCorrectly() {
    let input = "\
module test
import std/os

type Cat(IAnimal) =
  name: Str
  age: Int

  fun speak(self) -> Str =
    \"meow\"

fun classify(x: Int) -> Str =
  if x < 0
    return \"neg\"
  else if x == 0
    return \"zero\"
  else
    return \"pos\"

fun loop() -> Int =
  i := 0
  while i < 5
    i = i + 1
  return i

fun main() =
  assert 1 == 1
  return
";
    let once = formatSource(input).expect("first pass");
    assert!(once.contains("module test"), "got: {once}");
    assert!(once.contains("import std/os"), "got: {once}");
    assert!(once.contains("type Cat"), "got: {once}");
    assert!(once.contains("fun speak"), "got: {once}");
    assert!(once.contains("if x < 0"), "got: {once}");
    assert!(once.contains("else if x == 0"), "got: {once}");
    let twice = formatSource(&once).expect("second pass");
    assert_eq!(once, twice, "formatting should be idempotent");
}

/// Regression test: `import std/os` used to format down to `import /` — the
/// `url` grammar rule matched each path segment with a bare inline regex (no
/// node of its own), and Topiary's leaf collection only ever copies a
/// child NODE's own span, silently dropping any parent text a bare regex
/// like that leaves uncovered.
#[test]
fn importPathSegmentsSurviveFormatting() {
    let input = "module test\nimport std/http/content_type\n";
    let once = formatSource(input).expect("first pass");
    assert!(once.contains("import std/http/content_type"), "got: {once}");
}

/// Regression test: the ternary `?`/`:` had no spacing rule at all, and enum
/// variants / match cases (`| Foo`, `match ... case`) live directly under
/// their parent node (no wrapping body), so they need their own hardline
/// rules distinct from a `fn` body's.
#[test]
fn ternaryEnumAndMatchFormatCorrectlyAndIdempotently() {
    let input = "\
module test

enum Color =
  | Red
  | Green

  fun isRed(self) -> Bool =
    match self
      Red => True
      Green => False

fun abs(x: Int) -> Int =
  x < 0 ? -x : x
";
    let once = formatSource(input).expect("first pass");
    assert!(once.contains("x < 0 ? -x : x"), "got: {once}");
    assert!(once.contains("| Red"), "got: {once}");
    let twice = formatSource(&once).expect("second pass");
    assert_eq!(once, twice, "formatting should be idempotent");
}

/// Regression test: a standalone comment between two statements/methods used
/// to glue onto the end of the previous line on a SECOND formatting pass
/// (the naive fix, `@prepend_hardline`, would instead wrongly force even a
/// TRAILING same-line comment onto its own line) — `@prepend_input_softline`
/// preserves whichever the source actually had.
#[test]
fn commentsFormatIdempotently() {
    let input = "\
fun abs(self) -> Int =
  self < 0 ? -self : self

# a standalone comment between two functions
fun square(self) -> Int =
  self * self # a trailing comment
";
    let once = formatSource(input).expect("first pass");
    let twice = formatSource(&once).expect("second pass");
    assert_eq!(once, twice, "formatting should be idempotent");
}