summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/layouter.nim121
-rw-r--r--compiler/lexer.nim9
-rw-r--r--compiler/parser.nim2
-rw-r--r--nimpretty/tests/exhaustive.nim13
-rw-r--r--nimpretty/tests/expected/exhaustive.nim25
-rw-r--r--nimpretty/tests/expected/simple3.nim6
-rw-r--r--nimpretty/tests/wrong_ind.nim10
7 files changed, 113 insertions, 73 deletions
diff --git a/compiler/layouter.nim b/compiler/layouter.nim
index ccd0925bf..112c57542 100644
--- a/compiler/layouter.nim
+++ b/compiler/layouter.nim
@@ -16,6 +16,7 @@ from sequtils import delete
 
 const
   MaxLineLen = 80
+  MinLineLen = 10
 
 type
   SplitKind = enum
@@ -25,7 +26,7 @@ type
     detectSemicolonKind, useSemicolon, dontTouch
 
   LayoutToken = enum
-    ltSpaces, ltNewline, ltTab,
+    ltSpaces, ltNewline, ltTab, ltOptionalNewline,
     ltComment, ltLit, ltKeyword, ltExportMarker, ltIdent,
     ltOther, ltOpr,
     ltBeginSection, ltEndSection
@@ -88,6 +89,46 @@ proc computeRhs(em: Emitter; pos: int): int =
     inc result, em.tokens[p].len
     inc p
 
+proc isLongEnough(lineLen, startPos, endPos: int): bool =
+  result = lineLen > MinLineLen and endPos > startPos + 4
+
+proc findNewline(em: Emitter; p, lineLen: var int) =
+  while p < em.tokens.len and em.kinds[p] != ltNewline:
+    inc lineLen, em.tokens[p].len
+    inc p
+
+proc optionalIsGood(em: var Emitter; pos: int): bool =
+  let ourIndent = em.tokens[pos].len
+  var p = pos+1
+  var lineLen = 0
+  em.findNewline(p, lineLen)
+  if p+1 < em.tokens.len and em.kinds[p+1] == ltSpaces and
+      em.kinds[p-1] == ltOptionalNewline:
+    if em.tokens[p+1].len == ourIndent:
+      # concatenate lines with the same indententation
+      var nlPos = p
+      var lineLenTotal = lineLen
+      inc p
+      em.findNewline(p, lineLenTotal)
+      if isLongEnough(lineLenTotal, nlPos, p):
+        em.kinds[nlPos] = ltOptionalNewline
+        if em.kinds[nlPos+1] == ltSpaces:
+          # inhibit extra spaces when concatenating two lines
+          em.tokens[nlPos+1] = if em.tokens[nlPos-2] == ",": " " else: ""
+      result = true
+    elif em.tokens[p+1].len < ourIndent:
+      result = isLongEnough(lineLen, pos, p)
+  elif em.kinds[pos+1] == ltOther: # note: pos+1, not p+1
+    result = false
+  else:
+    result = isLongEnough(lineLen, pos, p)
+
+proc lenOfNextTokens(em: Emitter; pos: int): int =
+  result = 0
+  for i in 1 ..< em.tokens.len-pos:
+    if em.kinds[pos+i] in {ltNewline, ltOptionalNewline}: break
+    inc result, em.tokens[pos+i].len
+
 proc closeEmitter*(em: var Emitter) =
   let outFile = em.config.absOutFile
 
@@ -95,7 +136,8 @@ proc closeEmitter*(em: var Emitter) =
   var maxLhs = 0
   var lineLen = 0
   var lineBegin = 0
-  for i in 0..em.tokens.high:
+  var i = 0
+  while i <= em.tokens.high:
     case em.kinds[i]
     of ltBeginSection:
       maxLhs = computeMax(em, lineBegin)
@@ -113,9 +155,24 @@ proc closeEmitter*(em: var Emitter) =
       content.add em.tokens[i]
       lineLen = 0
       lineBegin = i+1
+    of ltOptionalNewline:
+      let totalLineLen = lineLen + lenOfNextTokens(em, i)
+      if totalLineLen > MaxLineLen + MinLineLen or
+         totalLineLen > MaxLineLen and optionalIsGood(em, i):
+        if i-1 >= 0 and em.kinds[i-1] == ltSpaces:
+          let spaces = em.tokens[i-1].len
+          content.setLen(content.len - spaces)
+        content.add "\L"
+        content.add em.tokens[i]
+        lineLen = em.tokens[i].len
+        lineBegin = i+1
+        if i+1 < em.kinds.len and em.kinds[i+1] == ltSpaces:
+          # inhibit extra spaces at the start of a new line
+          inc i
     else:
       content.add em.tokens[i]
       inc lineLen, em.tokens[i].len
+    inc i
 
   if fileExists(outFile) and readFile(outFile.string) == content:
     discard "do nothing, see #9499"
@@ -170,7 +227,6 @@ proc removeSpaces(em: var Emitter) =
     setLen(em.kinds, em.kinds.len-1)
     dec em.col, tokenLen
 
-template goodCol(col): bool = col in 40..MaxLineLen
 
 const
   openPars = {tkParLe, tkParDotLe,
@@ -184,49 +240,17 @@ const
   oprSet = {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
             tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor}
 
-template rememberSplit(kind) =
-  if goodCol(em.col):
-    em.altSplitPos[kind] = em.tokens.len
+template goodCol(col): bool = col >= MaxLineLen div 2
 
 template moreIndent(em): int =
-  (if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth)
-
-proc softLinebreak(em: var Emitter, lit: string) =
-  # XXX Use an algorithm that is outlined here:
-  # https://llvm.org/devmtg/2013-04/jasper-slides.pdf
-  # +2 because we blindly assume a comma or ' &' might follow
-  if not em.inquote and em.col+lit.len+2 >= MaxLineLen:
-    if em.lastTok in splitters:
-      # bug #10295, check first if even more indentation would help:
-      let spaces = em.indentLevel+moreIndent(em)
-      if spaces < em.col:
-        removeSpaces em
-        wrNewline(em)
-        em.col = 0
-        wrSpaces em, spaces
-    else:
-      # search backwards for a good split position:
-      for a in mitems(em.altSplitPos):
-        if a > em.fixedUntil:
-          var spaces = 0
-          while a+spaces < em.kinds.len and em.kinds[a+spaces] == ltSpaces:
-            inc spaces
-          if spaces > 0:
-            delete(em.tokens, a, a+spaces-1)
-            delete(em.kinds, a, a+spaces-1)
-          em.kinds.insert(ltNewline, a)
-          em.tokens.insert("\L", a)
-          em.kinds.insert(ltSpaces, a+1)
-          em.tokens.insert(repeat(' ', em.indentLevel+moreIndent(em)), a+1)
-          # recompute em.col:
-          var i = em.kinds.len-1
-          em.col = 0
-          while i >= 0 and em.kinds[i] != ltNewline:
-            inc em.col, em.tokens[i].len
-            dec i
-          # mark position as "already split here"
-          a = -1
-          break
+  if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth
+
+template rememberSplit(kind) =
+  if goodCol(em.col) and not em.inquote:
+    let spaces = em.indentLevel+moreIndent(em)
+    if spaces < em.col and spaces > 0:
+      wr(em, strutils.repeat(' ', spaces), ltOptionalNewline)
+    #em.altSplitPos[kind] = em.tokens.len
 
 proc emitMultilineComment(em: var Emitter, lit: string, col: int) =
   # re-align every line in the multi-line comment:
@@ -358,14 +382,9 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
 
     if not em.inquote:
       wr(em, TokTypeToStr[tok.tokType], ltKeyword)
-
-      case tok.tokType
-      of tkAnd: rememberSplit(splitAnd)
-      of tkOr: rememberSplit(splitOr)
-      of tkIn, tkNotin:
+      if tok.tokType in {tkAnd, tkOr, tkIn, tkNotin}:
         rememberSplit(splitIn)
         wrSpace em
-      else: discard
     else:
       # keywords in backticks are not normalized:
       wr(em, tok.ident.s, ltIdent)
@@ -422,7 +441,6 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
       emitComment(em, tok)
   of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit:
     let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
-    softLinebreak(em, lit)
     if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wrSpace(em)
     em.lineSpan = countNewlines(lit)
     if em.lineSpan > 0: calcCol(em, lit)
@@ -430,7 +448,6 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
   of tkEof: discard
   else:
     let lit = if tok.ident != nil: tok.ident.s else: tok.literal
-    softLinebreak(em, lit)
     if endsInAlpha(em): wrSpace(em)
     wr em, lit, ltIdent
 
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index dabbb651f..1eb936130 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -1334,10 +1334,13 @@ proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream;
   var tok: TToken
   initToken(tok)
   openLexer(lex, fileIdx, inputstream, cache, config)
-  while true:
+  var prevToken = tkEof
+  while tok.tokType != tkEof:
     rawGetTok(lex, tok)
-    result = tok.indent
-    if result > 0 or tok.tokType == tkEof: break
+    if tok.indent > 0 and prevToken in {tkColon, tkEquals, tkType, tkConst, tkLet, tkVar, tkUsing}:
+      result = tok.indent
+      if result > 0: break
+    prevToken = tok.tokType
   closeLexer(lex)
 
 proc getPrecedence*(ident: PIdent): int =
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 65fb2c5a3..f78197082 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -904,6 +904,7 @@ proc parsePragma(p: var TParser): PNode =
   result = newNodeP(nkPragma, p)
   inc p.inPragma
   when defined(nimpretty):
+    inc p.em.doIndentMore
     inc p.em.keepIndents
   getTok(p)
   optInd(p, result)
@@ -924,6 +925,7 @@ proc parsePragma(p: var TParser): PNode =
     parMessage(p, "expected '.}'")
   dec p.inPragma
   when defined(nimpretty):
+    dec p.em.doIndentMore
     dec p.em.keepIndents
 
 proc identVis(p: var TParser; allowDot=false): PNode =
diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim
index 39b5bf196..14c318495 100644
--- a/nimpretty/tests/exhaustive.nim
+++ b/nimpretty/tests/exhaustive.nim
@@ -122,7 +122,7 @@ type
     fixedUntil: int # marks where we must not go in the content
     altSplitPos: array[SplitKind, int] # alternative split positions
 
-proc openEmitter*[T, S](em: var Emitter; config: ConfigRef, fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd} =
+proc openEmitter*[T, S](em: var Emitter; config: ConfigRef; fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd} =
   let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim")
   em.f = llStreamOpen(outfile, fmWrite)
   em.config = config
@@ -607,3 +607,14 @@ type
     tagVar,        ## the HTML ``var`` element
     tagVideo,      ## the HTML ``video`` element
     tagWbr         ## the HTML ``wbr`` element
+
+
+# bug #11469
+const lookup: array[32, uint8] = [0'u8, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 16, 17,
+    25, 17, 4, 8, 31, 27, 13, 23]
+
+veryLongVariableName.createVar("future" & $node[1][0].toStrLit, node[1], futureValue1,
+    futureValue2, node)
+
+veryLongVariableName.createVar("future" & $node[1][0].toStrLit, node[1], futureValue1,
+                               futureValue2, node)
diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim
index 1b6cba082..21ff505dd 100644
--- a/nimpretty/tests/expected/exhaustive.nim
+++ b/nimpretty/tests/expected/exhaustive.nim
@@ -4,7 +4,7 @@ discard """
 """
 
 import verylongnamehere, verylongnamehere,
-  verylongnamehereverylongnamehereverylong, namehere, verylongnamehere
+    verylongnamehereverylongnamehereverylong, namehere, verylongnamehere
 
 proc `[]=`() = discard "index setter"
 proc `putter=`() = discard cast[pointer](cast[int](buffer) + size)
@@ -200,8 +200,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
     if em.lineSpan > 0: calcCol(em, lit)
     if not endsInWhite(em):
       wr(" ")
-      if em.lineSpan == 0 and max(em.col,
-          LineCommentColumn) + lit.len <= MaxLineLen:
+      if em.lineSpan == 0 and max(em.col, LineCommentColumn) + lit.len <= MaxLineLen:
         for i in 1 .. LineCommentColumn - em.col: wr(" ")
     wr lit
 
@@ -285,12 +284,10 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
   of tkComment:
     if not preventComment:
       emitComment(em, tok)
-  of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit,
-      tkGTripleStrLit, tkCharLit:
+  of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit:
     let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
     softLinebreak(em, lit)
-    if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wr(
-        " ")
+    if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wr(" ")
     em.lineSpan = countNewlines(lit)
     if em.lineSpan > 0: calcCol(em, lit)
     wr lit
@@ -383,8 +380,7 @@ import osproc
 let res = execProcess(
     "echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates")
 
-let res = execProcess(
-    "echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates")
+let res = execProcess("echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates")
 
 
 # bug #10177
@@ -617,3 +613,14 @@ type
     tagVar,         ## the HTML ``var`` element
     tagVideo,       ## the HTML ``video`` element
     tagWbr          ## the HTML ``wbr`` element
+
+
+# bug #11469
+const lookup: array[32, uint8] = [0'u8, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15,
+    16, 17, 25, 17, 4, 8, 31, 27, 13, 23]
+
+veryLongVariableName.createVar("future" & $node[1][0].toStrLit, node[1],
+    futureValue1, futureValue2, node)
+
+veryLongVariableName.createVar("future" & $node[1][0].toStrLit, node[1], futureValue1,
+                               futureValue2, node)
diff --git a/nimpretty/tests/expected/simple3.nim b/nimpretty/tests/expected/simple3.nim
index 7f17e0fc6..b7a02758a 100644
--- a/nimpretty/tests/expected/simple3.nim
+++ b/nimpretty/tests/expected/simple3.nim
@@ -8,9 +8,9 @@ foo bar
 proc fun() =
   echo "ok1"
 
-proc fun2(a = "fooo" & "bar" & "bar" & "bar" & "bar" & (
-    "bar" & "bar" & "bar") & "bar" & "bar" & "bar" & "bar" & "bar" & "bar" &
-    "bar" & "bar" & "bar"): auto =
+proc fun2(a = "fooo" & "bar" & "bar" & "bar" & "bar" & ("bar" & "bar" & "bar") &
+    "bar" & "bar" & "bar" & "bar" & "bar" & "bar" & "bar" & "bar" &
+    "bar"): auto =
   discard
 
 fun2()
diff --git a/nimpretty/tests/wrong_ind.nim b/nimpretty/tests/wrong_ind.nim
index eaa561b51..930f696bb 100644
--- a/nimpretty/tests/wrong_ind.nim
+++ b/nimpretty/tests/wrong_ind.nim
@@ -9,11 +9,11 @@ import pkg/[
 ]
 
 proc fun() =
-  let a = [
-    1,
-    2,
-  ]
-  discard
+    let a = [
+      1,
+      2,
+    ]
+    discard
 
 proc funB() =
   let a = [