PageRenderTime 15ms CodeModel.GetById 2ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/test/formatting.coffee

http://github.com/jashkenas/coffee-script
CoffeeScript | 497 lines | 429 code | 47 blank | 21 comment | 15 complexity | 610d45795b6915100d88143d87d5cf86 MD5 | raw file
  1# Formatting
  2# ----------
  3
  4# TODO: maybe this file should be split up into their respective sections:
  5#   operators -> operators
  6#   array literals -> array literals
  7#   string literals -> string literals
  8#   function invocations -> function invocations
  9
 10doesNotThrowCompileError "a = then b"
 11
 12test "multiple semicolon-separated statements in parentheticals", ->
 13  nonce = {}
 14  eq nonce, (1; 2; nonce)
 15  eq nonce, (-> return (1; 2; nonce))()
 16
 17# * Line Continuation
 18#   * Property Accesss
 19#   * Operators
 20#   * Array Literals
 21#   * Function Invocations
 22#   * String Literals
 23
 24# Property Access
 25
 26test "chained accesses split on period/newline, backwards and forwards", ->
 27  str = 'abc'
 28  result = str.
 29    split('').
 30    reverse().
 31    reverse().
 32    reverse()
 33  arrayEq ['c','b','a'], result
 34  arrayEq ['c','b','a'], str.
 35    split('').
 36    reverse().
 37    reverse().
 38    reverse()
 39  result = str
 40    .split('')
 41    .reverse()
 42    .reverse()
 43    .reverse()
 44  arrayEq ['c','b','a'], result
 45  arrayEq ['c','b','a'],
 46    str
 47    .split('')
 48    .reverse()
 49    .reverse()
 50    .reverse()
 51  arrayEq ['c','b','a'],
 52    str.
 53    split('')
 54    .reverse().
 55    reverse()
 56    .reverse()
 57
 58# Operators
 59
 60test "newline suppression for operators", ->
 61  six =
 62    1 +
 63    2 +
 64    3
 65  eq 6, six
 66
 67test "`?.` and `::` should continue lines", ->
 68  ok not (
 69    Date
 70    ::
 71    ?.foo
 72  )
 73
 74  ok not (
 75    Date
 76    ?::
 77    ?.foo
 78  )
 79  #eq Object::toString, Date?.
 80  #prototype
 81  #::
 82  #?.foo
 83
 84doesNotThrowCompileError """
 85  oh. yes
 86  oh?. true
 87  oh:: return
 88  """
 89
 90doesNotThrowCompileError """
 91  a?[b..]
 92  a?[...b]
 93  a?[b..c]
 94  """
 95
 96test "#1768: space between `::` and index is ignored", ->
 97  eq 'function', typeof String:: ['toString']
 98
 99# Array Literals
100
101test "indented array literals don't trigger whitespace rewriting", ->
102  getArgs = -> arguments
103  result = getArgs(
104    [[[[[],
105                  []],
106                [[]]]],
107      []])
108  eq 1, result.length
109
110# Function Invocations
111
112doesNotThrowCompileError """
113  obj = then fn 1,
114    1: 1
115    a:
116      b: ->
117        fn c,
118          d: e
119    f: 1
120  """
121
122# String Literals
123
124test "indented heredoc", ->
125  result = ((_) -> _)(
126                """
127                abc
128                """)
129  eq "abc", result
130
131# Chaining - all open calls are closed by property access starting a new line
132# * chaining after
133#   * indented argument
134#   * function block
135#   * indented object
136#
137#   * single line arguments
138#   * inline function literal
139#   * inline object literal
140#
141# * chaining inside
142#   * implicit object literal
143
144test "chaining after outdent", ->
145  id = (x) -> x
146
147  # indented argument
148  ff = id parseInt "ff",
149    16
150  .toString()
151  eq '255', ff
152
153  # function block
154  str = 'abc'
155  zero = parseInt str.replace /\w/, (letter) ->
156    0
157  .toString()
158  eq '0', zero
159
160  # indented object
161  a = id id
162    a: 1
163  .a
164  eq 1, a
165
166test "#1495, method call chaining", ->
167  str = 'abc'
168
169  result = str.split ''
170              .join ', '
171  eq 'a, b, c', result
172
173  result = str
174  .split ''
175  .join ', '
176  eq 'a, b, c', result
177
178  eq 'a, b, c', (str
179    .split ''
180    .join ', '
181  )
182
183  eq 'abc',
184    'aaabbbccc'.replace /(\w)\1\1/g, '$1$1'
185               .replace /([abc])\1/g, '$1'
186
187  # Nested calls
188  result = [1..3]
189    .slice Math.max 0, 1
190    .concat [3]
191  arrayEq [2, 3, 3], result
192
193  # Single line function arguments
194  result = [1..6]
195    .map (x) -> x * x
196    .filter (x) -> x % 2 is 0
197    .reverse()
198  arrayEq [36, 16, 4], result
199
200  # Single line implicit objects
201  id = (x) -> x
202  result = id a: 1
203    .a
204  eq 1, result
205
206  # The parens are forced
207  result = str.split(''.
208    split ''
209    .join ''
210  ).join ', '
211  eq 'a, b, c', result
212
213test "chaining should not wrap spilling ternary", ->
214  throwsCompileError """
215    if 0 then 1 else g
216      a: 42
217    .h()
218  """
219
220test "chaining should wrap calls containing spilling ternary", ->
221  f = (x) -> h: x
222  id = (x) -> x
223  result = f if true then 42 else id
224      a: 2
225  .h
226  eq 42, result
227
228test "chaining should work within spilling ternary", ->
229  f = (x) -> h: x
230  id = (x) -> x
231  result = f if false then 1 else id
232      a: 3
233      .a
234  eq 3, result.h
235
236test "method call chaining inside objects", ->
237  f = (x) -> c: 42
238  result =
239    a: f 1
240    b: f a: 1
241      .c
242  eq 42, result.b
243
244test "#4568: refine sameLine implicit object tagging", ->
245  condition = yes
246  fn = -> yes
247
248  x =
249    fn bar: {
250      foo: 123
251    } if not condition
252  eq x, undefined
253
254# Nested blocks caused by paren unwrapping
255test "#1492: Nested blocks don't cause double semicolons", ->
256  js = CoffeeScript.compile '(0;0)'
257  eq -1, js.indexOf ';;'
258
259test "#1195 Ignore trailing semicolons (before newlines or as the last char in a program)", ->
260  preNewline = (numSemicolons) ->
261    """
262    nonce = {}; nonce2 = {}
263    f = -> nonce#{Array(numSemicolons+1).join(';')}
264    nonce2
265    unless f() is nonce then throw new Error('; before linebreak should = newline')
266    """
267  CoffeeScript.run(preNewline(n), bare: true) for n in [1,2,3]
268
269  lastChar = '-> lastChar;'
270  doesNotThrowCompileError lastChar, bare: true
271
272test "#1299: Disallow token misnesting", ->
273  try
274    CoffeeScript.compile '''
275      [{
276         ]}
277    '''
278    ok no
279  catch e
280    eq 'unmatched ]', e.message
281
282test "#2981: Enforce initial indentation", ->
283  try
284    CoffeeScript.compile '  a\nb-'
285    ok no
286  catch e
287    eq 'missing indentation', e.message
288
289test "'single-line' expression containing multiple lines", ->
290  doesNotThrowCompileError """
291    (a, b) -> if a
292      -a
293    else if b
294    then -b
295    else null
296  """
297
298test "#1275: allow indentation before closing brackets", ->
299  array = [
300      1
301      2
302      3
303    ]
304  eq array, array
305  do ->
306  (
307    a = 1
308   )
309  eq 1, a
310
311test "don’t allow mixing of spaces and tabs for indentation", ->
312  try
313    CoffeeScript.compile '''
314      new Layer
315       x: 0
316      	y: 1
317    '''
318    ok no
319  catch e
320    eq 'indentation mismatch', e.message
321
322test "each code block that starts at indentation 0 can use a different style", ->
323  doesNotThrowCompileError '''
324      new Layer
325       x: 0
326       y: 1
327      new Layer
328      	x: 0
329      	y: 1
330    '''
331
332test "tabs and spaces cannot be mixed for indentation", ->
333  try
334    CoffeeScript.compile '''
335      new Layer
336      	 x: 0
337      	 y: 1
338    '''
339    ok no
340  catch e
341    eq 'mixed indentation', e.message
342
343test "#4487: Handle unusual outdentation", ->
344  a =
345    switch 1
346      when 2
347          no
348         when 3 then no
349      when 1 then yes
350  eq yes, a
351
352  b = do ->
353    if no
354      if no
355            1
356       2
357      3
358  eq b, undefined
359
360test "#3906: handle further indentation inside indented chain", ->
361  eq 1, CoffeeScript.eval '''
362    z = b: -> d: 2
363    e = ->
364    f = 3
365
366    z
367        .b ->
368            c
369        .d
370
371    e(
372        f
373    )
374
375    1
376  '''
377
378  eq 1, CoffeeScript.eval '''
379    z = -> b: -> e: ->
380
381    z()
382        .b
383            c: 'd'
384        .e()
385
386    f = [
387        'g'
388    ]
389
390    1
391  '''
392
393  eq 1, CoffeeScript.eval '''
394    z = -> c: -> c: ->
395
396    z('b')
397      .c 'a',
398        {b: 'a'}
399      .c()
400    z(
401      'b'
402    )
403    1
404  '''
405
406test "#3199: throw multiline implicit object", ->
407  x = do ->
408    if no then throw
409      type: 'a'
410      msg: 'b'
411  eq undefined, x
412
413  y = do ->
414    if no then return
415      type: 'a'
416      msg: 'b'
417  eq undefined, y
418
419  y = do ->
420    yield
421      type: 'a'
422      msg: 'b'
423
424    if no then yield
425      type: 'c'
426      msg: 'd'
427
428    1
429  {value, done} = y.next()
430  ok value.type is 'a' and done is no
431
432  {value, done} = y.next()
433  ok value is 1 and done is yes
434
435test "#4576: multiple row function chaining", ->
436  ->
437    eq @a, 3
438  .call a: 3
439
440test "#4576: function chaining on separate rows", ->
441  do ->
442    Promise
443    .resolve()
444    .then ->
445      yes
446    .then ok
447
448test "#3736: chaining after do IIFE", ->
449  eq 3,
450    do ->
451      a: 3
452    .a
453
454  eq 3,
455    do (b = (c) -> c) -> a: 3
456    ?.a
457
458  b = 3
459  eq 3,
460    do (
461      b
462      {d} = {}
463    ) ->
464      a: b
465    .a
466
467  # preserve existing chaining behavior for non-IIFE `do`
468  b = c: -> 4
469  eq 4,
470    do b
471    .c
472
473test "#5168: allow indented property index", ->
474  a = b: 3
475
476  eq 3, a[
477    if yes
478      'b'
479    else
480      'c'
481  ]
482
483  d = [1, 2, 3]
484  arrayEq [1, 2], d[
485    ...2
486  ]
487
488  class A
489    b: -> 3
490
491  class B extends A
492    c: ->
493      super[
494        'b'
495      ]()
496
497  eq 3, new B().c()