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
3d6f280 1
#![allow(non_snake_case)]
3d6f280 2
use plum_core::formatSource;
35bca24 3
35bca24 4
#[test]
3d6f280 5
fn formatsSimpleFunction() {
b48d3a3 6
    let input = "fun main() =\n  x = 1\n";
3d6f280 7
    let result = formatSource(input).expect("formatSource should succeed");
35bca24 8
    assert!(result.contains("main()"));
35bca24 9
    assert!(result.contains("x ="));
35bca24 10
}
35bca24 11
35bca24 12
#[test]
3d6f280 13
fn formatsBinaryOperatorSpacing() {
2b8194c 14
    let input = "fun add(a: Int, b: Int) -> Int =\n  a+b\n";
3d6f280 15
    let result = formatSource(input).expect("formatSource should succeed");
35bca24 16
    assert!(result.contains("a + b"));
35bca24 17
}
35bca24 18
35bca24 19
#[test]
3d6f280 20
fn rejectsSyntaxError() {
35bca24 21
    let input = "@@@@invalid@@@@\n";
3d6f280 22
    let result = formatSource(input);
35bca24 23
    assert!(result.is_err());
35bca24 24
}
35bca24 25
35bca24 26
#[test]
3d6f280 27
fn idempotentOnAlreadyFormatted() {
b48d3a3 28
    let input = "fun main() =\n  x = 1 + 2\n";
3d6f280 29
    let first = formatSource(input).expect("first pass");
3d6f280 30
    let second = formatSource(&first).expect("second pass");
35bca24 31
    assert_eq!(first, second, "formatting should be idempotent");
35bca24 32
}
0000000 33
0000000 34
/// Regression test for a real bug: Topiary inserts NO whitespace between
0000000 35
/// adjacent leaves unless a query says to, so keywords with no explicit
0000000 36
/// spacing rule glue to the next token ("module" + "test" -> "moduletest").
0000000 37
/// This exercises most of the keyword vocabulary in one pass.
0000000 38
#[test]
0000000 39
fn keywordsGetSpacedCorrectly() {
0000000 40
    let input = "\
0000000 41
module test
0000000 42
import std/os
0000000 43
0000000 44
type Cat(IAnimal) =
0000000 45
  name: Str
0000000 46
  age: Int
0000000 47
0000000 48
  fun speak(self) -> Str =
0000000 49
    \"meow\"
0000000 50
0000000 51
fun classify(x: Int) -> Str =
0000000 52
  if x < 0
0000000 53
    return \"neg\"
0000000 54
  else if x == 0
0000000 55
    return \"zero\"
0000000 56
  else
0000000 57
    return \"pos\"
0000000 58
0000000 59
fun loop() -> Int =
0000000 60
  i := 0
0000000 61
  while i < 5
0000000 62
    i = i + 1
0000000 63
  return i
0000000 64
0000000 65
fun main() =
0000000 66
  assert 1 == 1
0000000 67
  return
0000000 68
";
0000000 69
    let once = formatSource(input).expect("first pass");
0000000 70
    assert!(once.contains("module test"), "got: {once}");
0000000 71
    assert!(once.contains("import std/os"), "got: {once}");
0000000 72
    assert!(once.contains("type Cat"), "got: {once}");
0000000 73
    assert!(once.contains("fun speak"), "got: {once}");
0000000 74
    assert!(once.contains("if x < 0"), "got: {once}");
0000000 75
    assert!(once.contains("else if x == 0"), "got: {once}");
0000000 76
    let twice = formatSource(&once).expect("second pass");
0000000 77
    assert_eq!(once, twice, "formatting should be idempotent");
0000000 78
}
0000000 79
0000000 80
/// Regression test: `import std/os` used to format down to `import /` — the
0000000 81
/// `url` grammar rule matched each path segment with a bare inline regex (no
0000000 82
/// node of its own), and Topiary's leaf collection only ever copies a
0000000 83
/// child NODE's own span, silently dropping any parent text a bare regex
0000000 84
/// like that leaves uncovered.
0000000 85
#[test]
0000000 86
fn importPathSegmentsSurviveFormatting() {
0000000 87
    let input = "module test\nimport std/http/content_type\n";
0000000 88
    let once = formatSource(input).expect("first pass");
0000000 89
    assert!(once.contains("import std/http/content_type"), "got: {once}");
0000000 90
}
0000000 91
0000000 92
/// Regression test: the ternary `?`/`:` had no spacing rule at all, and enum
0000000 93
/// variants / match cases (`| Foo`, `match ... case`) live directly under
0000000 94
/// their parent node (no wrapping body), so they need their own hardline
0000000 95
/// rules distinct from a `fn` body's.
0000000 96
#[test]
0000000 97
fn ternaryEnumAndMatchFormatCorrectlyAndIdempotently() {
0000000 98
    let input = "\
0000000 99
module test
0000000 100
0000000 101
enum Color =
0000000 102
  | Red
0000000 103
  | Green
0000000 104
0000000 105
  fun isRed(self) -> Bool =
0000000 106
    match self
0000000 107
      Red => True
0000000 108
      Green => False
0000000 109
0000000 110
fun abs(x: Int) -> Int =
0000000 111
  x < 0 ? -x : x
0000000 112
";
0000000 113
    let once = formatSource(input).expect("first pass");
0000000 114
    assert!(once.contains("x < 0 ? -x : x"), "got: {once}");
0000000 115
    assert!(once.contains("| Red"), "got: {once}");
0000000 116
    let twice = formatSource(&once).expect("second pass");
0000000 117
    assert_eq!(once, twice, "formatting should be idempotent");
0000000 118
}
0000000 119
0000000 120
/// Regression test: a standalone comment between two statements/methods used
0000000 121
/// to glue onto the end of the previous line on a SECOND formatting pass
0000000 122
/// (the naive fix, `@prepend_hardline`, would instead wrongly force even a
0000000 123
/// TRAILING same-line comment onto its own line) — `@prepend_input_softline`
0000000 124
/// preserves whichever the source actually had.
0000000 125
#[test]
0000000 126
fn commentsFormatIdempotently() {
0000000 127
    let input = "\
0000000 128
fun abs(self) -> Int =
0000000 129
  self < 0 ? -self : self
0000000 130
0000000 131
# a standalone comment between two functions
0000000 132
fun square(self) -> Int =
0000000 133
  self * self # a trailing comment
0000000 134
";
0000000 135
    let once = formatSource(input).expect("first pass");
0000000 136
    let twice = formatSource(&once).expect("second pass");
0000000 137
    assert_eq!(once, twice, "formatting should be idempotent");
0000000 138
}