diff options
author | Miran <narimiran@disroot.org> | 2019-06-26 15:36:44 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2019-06-26 15:36:44 +0200 |
commit | f288e1b11ab3c796b213fe139f6dcb9c1897218f (patch) | |
tree | 620e0b96e9ddb34090b0bea36ab34111114eb6f6 /compiler/layouter.nim | |
parent | 0a81b038fd8b75d9d0e29a6b50567897583031ba (diff) | |
download | Nim-f288e1b11ab3c796b213fe139f6dcb9c1897218f.tar.gz |
[bugfix] fix #11469, new rules for a newline in nimpretty (#11512)
* [bugfix] fix #11469, new rules for a newline in nimpretty * concatenate two lines if they have the same indentation level
Diffstat (limited to 'compiler/layouter.nim')
-rw-r--r-- | compiler/layouter.nim | 121 |
1 files changed, 69 insertions, 52 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 |