From 8902ab1080eec46eff33706a384258318e282268 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Tue, 19 Dec 2023 16:37:57 +0100 Subject: [PATCH 1/4] the stable comment These changes allow `nph` to maintain formatting stability between one run and the other - important when formatting all of a codebase before committing to git so that we don't get spurious reformats. * better support for doc-comment-only statement lists * more empty line stability fixes * support comments in nkidentdefs symbol lists * check for formatting instability in CI * format all given files even if some formatting fails --- .github/workflows/ci.yml | 8 +- src/astcmp.nim | 30 +++- src/nph.nim | 98 +++++++---- src/phastyaml.nim | 7 +- src/phparser.nim | 41 +++-- src/phrenderer.nim | 252 +++++++++++------------------ tests/after/comments.nim | 43 +++-- tests/after/comments.nim.nph.yaml | 68 ++++++-- tests/after/exprs.nim | 15 ++ tests/after/exprs.nim.nph.yaml | 66 ++++++++ tests/before/comments.nim | 15 ++ tests/before/comments.nim.nph.yaml | 65 +++++++- tests/before/exprs.nim | 6 + tests/before/exprs.nim.nph.yaml | 67 ++++++++ 14 files changed, 537 insertions(+), 244 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f37631a..17862f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,8 @@ jobs: - name: Check formatting run: | - find src tests/after -name "*.nim" -print0 | xargs -0 -n1 ./nph - git diff - # find -name "*.nim" ! -path "./tests/before/*" ! -path "./nimbledeps/*" -print0 | xargs -0 -n1 ./nph --check + ! ./nph --check tests/before + ./nph --check tests/after + + ./nph src + git diff --exit-code diff --git a/src/astcmp.nim b/src/astcmp.nim index b1b92b0..90444ce 100644 --- a/src/astcmp.nim +++ b/src/astcmp.nim @@ -10,11 +10,12 @@ from std/math import isNaN type Equivalence* = enum Same + ParseError Different Outcome* = object case kind*: Equivalence - of Same: + of Same, ParseError: discard of Different: a*, b*: PNode @@ -71,10 +72,16 @@ proc equivalent*(a, b: PNode): Outcome = of nkIdent: a.ident.s == b.ident.s else: + let skipped = + if a.kind == nkStmtListExpr: + # When inserting a `;`, we might get some extra empty statements (?) + {nkEmpty, nkCommentStmt} + else: + {nkCommentStmt} # TODO don't break comments! let - af = a.sons.filterIt(it.kind != nkCommentStmt) - bf = b.sons.filterIt(it.kind != nkCommentStmt) + af = a.sons.filterIt(it.kind notin skipped) + bf = b.sons.filterIt(it.kind notin skipped) if af.len() != bf.len(): false @@ -91,8 +98,17 @@ proc equivalent*(a, b: PNode): Outcome = else: Outcome(kind: Same) +proc makeConfigRef(): ConfigRef = + let conf = newConfigRef() + conf.errorMax = int.high + conf + proc equivalent*(a, afile, b, bfile: string): Outcome = - equivalent( - parseString(a, newIdentCache(), newConfigRef(), afile), - parseString(b, newIdentCache(), newConfigRef(), bfile), - ) + let + conf = makeConfigRef() + aa = parseString(a, newIdentCache(), conf, afile) + bb = parseString(b, newIdentCache(), conf, bfile) + if conf.errorCounter > 0: + Outcome(kind: ParseError) + else: + equivalent(aa, bb) diff --git a/src/nph.nim b/src/nph.nim index 4cb1647..e8169ca 100644 --- a/src/nph.nim +++ b/src/nph.nim @@ -17,7 +17,7 @@ static: "nph needs to be compiled with nim 2.0.0 exactly for now" const - Version = "0.1" + Version = gorge("git describe --long --dirty --always") Usage = "nph - Nim formatter " & Version & """ @@ -30,6 +30,9 @@ Options: --version show the version --help show this help """ + ErrCheckFailed = 1 + ErrParseFailed = 2 + ErrEqFailed = 3 proc writeHelp() = stdout.write(Usage) @@ -55,16 +58,28 @@ func isNimFile(file: string): bool = let (_, _, ext) = file.splitFile() ext in [".nim", ".nims", ".nimble"] -proc prettyPrint(infile, outfile: string; debug, check, printTokens: bool): bool = +proc makeConfigRef(): ConfigRef = + let conf = newConfigRef() + conf.errorMax = int.high + conf + +proc prettyPrint(infile, outfile: string; debug, check, printTokens: bool): int = let - conf = newConfigRef() + conf = makeConfigRef() input = if infile == "-": readAll(stdin) else: readFile(infile) node = parse(input, infile, printTokens, conf) - output = renderTree(node, conf) & "\n" + + if conf.errorCounter > 0: + return ErrParseFailed + + let output = renderTree(node, conf) & "\n" + + if conf.errorCounter > 0: + return ErrParseFailed if infile != "-": if debug: @@ -78,7 +93,7 @@ proc prettyPrint(infile, outfile: string; debug, check, printTokens: bool): bool ) elif fileExists(outFile) and output == readFile(outFile): # No formatting difference - don't touch file modificuation date - return true + return QuitSuccess let eq = equivalent( @@ -92,7 +107,41 @@ proc prettyPrint(infile, outfile: string; debug, check, printTokens: bool): bool , ) - if eq.kind == Different: + template writeUnformatted() = + if not debug and (infile != outfile or infile == "-"): + # Write unformatted content + if not check: + if infile == "-": + write(stdout, input) + else: + writeFile(outfile, input) + + case eq.kind + of Same: + if check: + ErrCheckFailed # We failed the equivalence check above + else: + # Formatting changed the file + if not debug or infile == "-": + if infile == "-": + write(stdout, output) + else: + writeFile(outfile, output) + + QuitSuccess + of ParseError: + writeUnformatted() + + localError( + conf, + TLineInfo(fileIndex: FileIndex(0)), + "Skipped file, formatted output cannot be parsed (bug! " & Version & ")", + ) + + ErrEqFailed + of Different: + writeUnformatted() + stderr.writeLine "--- Input ---" stderr.writeLine input stderr.writeLine "--- Formatted ---" @@ -102,32 +151,13 @@ proc prettyPrint(infile, outfile: string; debug, check, printTokens: bool): bool stderr.writeLine "--- POST ---" stderr.writeLine treeToYaml(nil, eq.b) - internalError( + localError( conf, TLineInfo(fileIndex: FileIndex(0)), - "Formatted output does not match input, report bug!", + "Skipped file, formatted output does not match input (bug! " & Version & ")", ) - if infile != outfile or infile == "-": - # Write unformatted content - if not check: - if infile == "-": - write(stdout, input) - else: - writeFile(outfile, input) - - quit 2 - - if check: - false # We failed the equivalence check above - else: - # Formatting changed the file - if not debug or infile == "-": - if infile == "-": - write(stdout, output) - else: - writeFile(outfile, output) - true + ErrEqFailed proc main() = var @@ -194,14 +224,20 @@ proc main() = elif outdir.len != 0: outfiles = infiles.mapIt($(joinPath(outdir, it))) + var res = QuitSuccess for (infile, outfile) in zip(infiles, outfiles): let (dir, _, _) = splitFile(outfile) createDir(dir) - if not prettyPrint(infile, outfile, debug, check, printTokens): - quit 1 + let err = prettyPrint(infile, outfile, debug, check, printTokens) + case err + of ErrCheckFailed: + quit ErrCheckFailed + else: + # Keep going on source code errors but fail the program eventually + res = max(res, err) - quit 0 + quit res when isMainModule: main() diff --git a/src/phastyaml.nim b/src/phastyaml.nim index bc75c69..b3a4049 100644 --- a/src/phastyaml.nim +++ b/src/phastyaml.nim @@ -11,9 +11,10 @@ # * yaml formatting for the nph-specific fields -import "."/[phast, phlexer, phlineinfos, phoptions, phmsgs] -import "$nim"/compiler/[rodutils] -import std/[intsets, strutils] +import + "."/[phast, phlexer, phlineinfos, phoptions, phmsgs], + "$nim"/compiler/[rodutils], + std/[intsets, strutils] proc addYamlString*(res: var string; s: string) = # We have to split long strings into many ropes. Otherwise diff --git a/src/phparser.nim b/src/phparser.nim index e17480d..0f9c76f 100644 --- a/src/phparser.nim +++ b/src/phparser.nim @@ -138,14 +138,17 @@ proc parMessage(p: Parser; arg: string) = ## Produce and emit the parser message `arg` to output. lexMessageTok(p.lex, errGenerated, p.tok, arg) -template withInd(p, body: untyped) = +template withInd(p, indent, body: untyped) = let oldInd = p.currInd - p.currInd = p.tok.indent + p.currInd = indent body p.currInd = oldInd +template withInd(p, body: untyped) = + withInd(p, p.tok.indent, body) + template realInd(p): bool = p.tok.indent > p.currInd @@ -229,7 +232,7 @@ proc drainSkipped(p: var Parser; indent: int): seq[Token] = for c in p.skipped: if c.indent == -1 or c.indent > indent: result.add(c) - p.lineNumberPrevious = int c.prevLine + p.lineNumberPrevious = int c.lineB p.skipped.delete(0..result.high) @@ -358,8 +361,16 @@ proc isOperator(tok: Token): bool = } proc parseComStmt(p: var Parser; n: PNode; commentLoc = clPostfix): PNode = - splitLookahead(p, n, commentLoc) - parseStmt(p) + splitLookahead(p, n, int.high, commentLoc) + + # Let parseStmt deal with statement lists that contain only doc comments + if not ( + p.tok.indent >= 0 and p.tok.indent < p.currInd and p.skipped.len > 0 and + p.skipped[0].indent > p.currInd + ): + splitLookahead(p, n, commentLoc) + + parseStmt(p, allowEmpty = true) proc parseColComStmt(p: var Parser; n: PNode; commentLoc = clPostfix): PNode = # `:` followed by a list of statements, with comments interspresed @@ -1109,6 +1120,9 @@ proc parseOperators( a.add(opNode) a.add(result) a.add(b) + # Reset the "beginning" of the infix to capture empty lines correctly + a.info = result.info + a.endInfo = b.endInfo result = a opPrec = getPrecedence(p.tok) @@ -1232,7 +1246,7 @@ proc parseIdentColonEquals(p: var Parser; flags: DeclaredIdentFlags): PNode = if p.tok.tokType != tkComma: break getTok(p) - + splitLookahead(p, a, clPostfix) optInd(p, a) # We let comments sit as prefixes to whatever comes next - this works well @@ -2501,7 +2515,6 @@ proc parseSection( #| section(RULE) = COMMENT? RULE / (IND{>} (RULE / COMMENT)^+IND{=} DED) if result.kind != nkTypeSection: getTok(p) - splitLookahead(p, result, clMid) if realInd(p): withInd(p): @@ -2838,7 +2851,6 @@ proc parseTypeDef(p: var Parser): PNode = #| typeDef = identVisDot genericParamList? pragma '=' optInd typeDefValue #| indAndComment? result = newNodeP(nkTypeDef, p) - var identifier = identVis(p, allowDot = true) var identPragma = identifier var pragma: PNode @@ -3119,11 +3131,17 @@ proc complexOrSimpleStmt(p: var Parser): PNode = proc parseStmt(p: var Parser; allowEmpty: bool): PNode = #| stmt = (IND{>} complexOrSimpleStmt^+(IND{=} / ';') DED) #| / simpleStmt ^+ ';' - if p.tok.indent > p.currInd: + + let nextInd = + if p.tok.indent < p.currInd and p.skipped.len > 0 and p.skipped[0].indent > p.currInd: + p.skipped[0].indent + else: + p.tok.indent + if nextInd > p.currInd: result = newNodeP(nkStmtList, p, withPrefix = false) - addSkipped(p, result) - withInd(p): + withInd(p, nextInd): + addSkipped(p, result) splitLookahead(p, result, clPrefix) while true: if p.tok.indent == p.currInd: @@ -3172,6 +3190,7 @@ proc parseStmt(p: var Parser; allowEmpty: bool): PNode = tkMacro, tkType, tkConst, tkWhen, tkVar: if allowEmpty: result = newNodeP(nkStmtList, p, withPrefix = false) + addSkipped(p, result) else: parMessage(p, "nestable statement requires indentation") result = p.emptyNode diff --git a/src/phrenderer.nim b/src/phrenderer.nim index 8f73e09..61e7d57 100644 --- a/src/phrenderer.nim +++ b/src/phrenderer.nim @@ -86,6 +86,7 @@ type lfSepAtEnd ## Always add separator at end lfLongSepAtEnd ## Add separator at end in one-per-line mode lfSkipPushComma ## Hack to remove first comma in pragma push + lfFirstCommentSticky ## Render the first comment on the same line if line-breaking ListFlags = set[ListFlag] SubFlag = enum @@ -202,8 +203,10 @@ proc addTok(g: var TSrcGen; kind: TokType; s: string) = proc outputLine(g: TSrcGen): int = ## The line it would be added if we were to add a token g.line + ( - if g.pendingNL >= 0: 1 + ord(g.pendingNewline) - else: 0 + if g.pendingNL >= 0: + 1 + ord(g.pendingNewline) + else: + 0 ) proc outputLine(g: TSrcLen): int = @@ -255,6 +258,16 @@ proc optNL(g: var TSrcGen; a, b: PNode) = proc blankLine(g: var TSrcGen; v = true) = g.pendingNewline = v or g.pendingNewline +proc optIndent(g: var TSrcGen; wid = IndentWidth) = + # Set up an indent increase, if a newline were to happen + g.indent += wid + if g.pendingNL >= 0: + g.pendingNL = g.indent + g.lineLen = g.indent + +proc optIndent(g: var TSrcLen; wid = IndentWidth) = + g.indent += wid + proc indentNL(g: var TSrcGen; wid = IndentWidth) = g.indent += wid g.pendingNL = g.indent @@ -610,69 +623,6 @@ proc nlsub(g: TOutput; n: PNode; flags: SubFlags = {}): int = else: lsub(g, n, flags) -# proc lcomma(g: TSrcGen; n: PNode; start: int = 0; theEnd: int = -1, flags: ListFlags = {}, subFlags: SubFlags = {}): int = -# assert(theEnd < 0) -# # if n.prefix.len > 0 or n.mid.len > 0 or n.postfix.len > 0: -# # return MaxLineLen + 1 -# result = 0 -# for i in start..n.len + theEnd: -# result += lsub(g, n[i], subFlags) + len(", ") - -# if result > 0 and lfSepAtEnd notin flags: -# result -= len(", ") # last does not get a comma! - -# proc llist( -# g: TSrcGen; -# n: PNode; -# brOpen = tkParLe; -# separator = tkComma; -# extra = 0; -# start = 0; -# theEnd = -1; -# indentNL = IndentWidth; -# flags: ListFlags = {}; -# subFlags: SubFlags = {}; -# ): int = -# let -# brLen = if brCloseOf(brOpen) == tkInvalid: 0 else: len($brOpen) * 2 -# subLen = lcomma(g, n, start = start, theEnd = theEnd) - -# subLen + brLen + extra - -# proc lsons(g: TSrcGen; n: PNode; start: int = 0; theEnd: int = -1): int = -# assert(theEnd < 0) - -# result = 0 -# # if n.prefix.len > 0 or n.mid.len > 0 or n.postfix.len > 0: -# # return MaxLineLen + 1 -# for i in start..n.len + theEnd: -# inc(result, lsub(g, n[i])) - -# proc lpostStatements(g: var TSrcLen, n: PNode; start: int; skipDo: bool; skipColon = false): int = -# var i = start -# if n[i].kind in {nkStmtList, nkStmtListExpr}: -# if skipDo: -# if not skipColon: -# put(g, tkColon, ":") -# else: -# result += len(Space) -# put(g, tkSpaces, Space) -# put(g, tkDo, "do") -# put(g, tkColon, ":") - -# lsub(g, n[i]) - -# i.inc -# for j in i.. 1 or overflows(g, len($toks[0])) - var hadPending = false - if long: - if indented: - indentNL(g) - else: - optNL(g) - elif indented: - if g.pendingNL >= 0: - hadPending = true - g.indent += IndentWidth - g.pendingNL += IndentWidth - g.lineLen += IndentWidth + if indented: + optIndent(g) + + if long and not firstSticky: + optNL(g) for tok in toks: g.pendingNewline = g.pendingNewline or tok.prevLine + 1 < tok.line @@ -714,22 +657,19 @@ proc gextras(g: var TSrcGen; toks: openArray[Token]; indented: bool) = put(g, tok.tokType, $tok) optNL(g) - if long and indented: + if indented: dedent(g) - elif indented: - if hadPending: - g.indent -= IndentWidth - if g.pendingNL >= IndentWidth: - g.pendingNL -= IndentWidth - g.lineLen -= IndentWidth proc gprefixes(g: var TSrcGen; n: PNode) = - gextras(g, n.prefix, false) + if n.prefix.len > 0: + # Putting prefixes on the same line as the node helps comment stability + g.optNL() + gextras(g, n.prefix, false, false) proc gmids(g: var TSrcGen; n: PNode; indented = false) = - gextras(g, n.mid, indented) + gextras(g, n.mid, indented, false) -proc gpostfixes(g: var TSrcGen; n: PNode) = +proc gpostfixes(g: var TSrcGen; n: PNode; firstSticky = false) = # Postfixes are indented to increase chances that they stick # with the same node on re-parse since in most cases, we split # comments between prefix and postfix indent being greater than the @@ -738,7 +678,7 @@ proc gpostfixes(g: var TSrcGen; n: PNode) = # Suspent kind-specific blanks while rendering postfixes let blank = g.pendingNewline g.pendingNewline = false - gextras(g, n.postfix, true) + gextras(g, n.postfix, true, firstSticky) g.pendingNewline = blank proc gprefixes(g: var TSrcLen; n: PNode) = @@ -747,7 +687,7 @@ proc gprefixes(g: var TSrcLen; n: PNode) = proc gmids(g: var TSrcLen; n: PNode; indented = false) = discard -proc gpostfixes(g: var TSrcLen; n: PNode) = +proc gpostfixes(g: var TSrcLen; n: PNode; firstSticky = false) = discard proc eqIdent(n: PNode; s: string): bool = @@ -782,7 +722,7 @@ proc gcomma( var indented = false defer: if indented: - g.indent -= indentNL + g.dedent(indentNL) let (start, count) = if lfFirstSticky in flags: @@ -793,11 +733,12 @@ proc gcomma( # The first item must be rendered without newlines, so we start with that gsub(g, n[start], flags = subFlags + {sfSkipPostfix}) - gpostfixes(g, n[start]) - if count > 1: putWithSpace(g, separator, $separator) + # Postfixes after separator! + gpostfixes(g, n[start], lfFirstCommentSticky in flags) + # If we can't fit everything on the current line, start over at a fresh one if lfFirstAlone in flags and overflows( @@ -812,11 +753,9 @@ proc gcomma( flags - {lfFirstSticky, lfFirstAlone}, subFlags, ), - ): + ) or n[start].postfix.len > 0: indented = true - g.indent += indentNL - - optNL(g) + g.indentNL(indentNL) (start + 1, count - 1) else: @@ -868,7 +807,7 @@ proc gcomma( else: put(g, tkSpaces, " ") - gpostfixes(g, n[i]) + gpostfixes(g, n[i], lfFirstCommentSticky in flags) if lfSepAtEnd in flags or onePerLine and lfLongSepAtEnd in flags: put(g, separator, $separator) @@ -907,25 +846,31 @@ proc glist( brClose = brCloseOf(brOpen) len = llist(g, n, brOpen, separator, extra, start, theEnd, indentNL, flags, subFlags) - ind = g.indent + indentNL - withNL = n.len + theEnd >= start and overflows(g, len) or n.mid.len > 0 + withNL = n.len + theEnd >= start and (overflows(g, len) or n.mid.len > 1) if brClose != tkInvalid: # TODO stack all opening brackets on one line if there are many put(g, brOpen, $brOpen) - let oldInd = g.indent - g.indent = ind + optIndent(g, indentNL) + if n.mid.len == 1: + # Rendering the mid before the rest of the list helps comment stability + gmids(g, n) + if withNL: g.optNL() - gmids(g, n) + if n.mid.len != 1: + gmids(g, n) + gcomma( g, n, start, theEnd, separator, indentNL = 0, flags = flags, subFlags = subFlags ) - g.indent = oldInd - if withNL: + dedent(g, indentNL) + + # If comments caused a newline, make sure we end with a newline as well + if withNL or n.mid.len > 0: g.optNL() if brClose != tkInvalid: @@ -967,6 +912,15 @@ proc gstmts(g: var TOutput; n: PNode; flags: SubFlags = {}; doIndent = true) = if needsPar: put(g, tkParLe, $tkParLe) + # Sometimes a semi-colon is needed to get the right parsing + if not ( + n.len > 0 and + n[0].kind in { + nkIfStmt, nkWhenStmt, nkWhileStmt, nkDiscardStmt, nkTryStmt, nkBlockStmt, + nkLetSection, nkVarSection, nkConstSection, nkCaseStmt + } + ): + put(g, tkSemiColon, $tkSemiColon) if doIndent or needsPar: indentNL(g) @@ -1009,15 +963,23 @@ proc gstmts(g: var TOutput; n: PNode; flags: SubFlags = {}; doIndent = true) = # No EOL after `)` or things will get misparsed! put(g, tkParRi, $tkParRi) +proc gcolcoms(g: var TOutput; n, stmts: PNode; useSub = false) = + putWithSpace(g, tkColon, ":") + if stmts.kind == nkStmtList and stmts.len == 0 and n.mid.len > 0: + g.optNL() # doc-comment-only + gmids(g, n, true) + if useSub: + gsub(g, stmts) + else: + gstmts(g, stmts) # Adds a newline after each child + proc gcond(g: var TOutput; n: PNode; flags: SubFlags = {}) = gsub(g, n, flags) proc gif(g: var TOutput; n: PNode; flags: SubFlags) = gprefixes(g, n[0]) gcond(g, n[0][0], {sfLongIndent}) - putWithSpace(g, tkColon, ":") - gmids(g, n[0], true) - gsub(g, n[0][1]) + gcolcoms(g, n[0], n[0][1], true) optNL(g) if sfSkipPostfix notin flags: gpostfixes(g, n[0]) @@ -1029,9 +991,7 @@ proc gif(g: var TOutput; n: PNode; flags: SubFlags) = proc gwhile(g: var TOutput; n: PNode) = putWithSpace(g, tkWhile, "while") gcond(g, n[0], {sfLongIndent}) - putWithSpace(g, tkColon, ":") - gmids(g, n, true) - gstmts(g, n[1]) + gcolcoms(g, n, n[1]) proc gpattern(g: var TOutput; n: PNode) = put(g, tkCurlyLe, "{") @@ -1041,15 +1001,11 @@ proc gpattern(g: var TOutput; n: PNode) = proc gpragmaBlock(g: var TOutput; n: PNode) = gsub(g, n[0]) - putWithSpace(g, tkColon, ":") - gmids(g, n, true) - gstmts(g, n[1]) + gcolcoms(g, n, n[1]) proc gtry(g: var TOutput; n: PNode) = put(g, tkTry, "try") - putWithSpace(g, tkColon, ":") - gmids(g, n, true) - gstmts(g, n[0]) + gcolcoms(g, n, n[0]) gsons(g, n, start = 1) proc gfor(g: var TOutput; n: PNode) = @@ -1058,9 +1014,7 @@ proc gfor(g: var TOutput; n: PNode) = put(g, tkSpaces, Space) putWithSpace(g, tkIn, "in") gsub(g, n[^2], flags = {sfLongIndent}) - putWithSpace(g, tkColon, ":") - gmids(g, n, true) - gstmts(g, n[^1]) + gcolcoms(g, n, n[^1]) proc gcase(g: var TOutput; n: PNode) = if n.len == 0: @@ -1134,15 +1088,11 @@ proc gblock(g: var TOutput; n: PNode) = if n.len == 1: return - putWithSpace(g, tkColon, ":") - gmids(g, n, true) - gstmts(g, n[1]) + gcolcoms(g, n, n[1]) proc gstaticStmt(g: var TOutput; n: PNode) = put(g, tkStatic, "static") - putWithSpace(g, tkColon, ":") - gmids(g, n, true) - gstmts(g, n[0]) + gcolcoms(g, n, n[0]) proc gasm(g: var TOutput; n: PNode) = putWithSpace(g, tkAsm, "asm") @@ -1286,8 +1236,8 @@ proc gsub(g: var TOutput; n: PNode; flags: SubFlags) = if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}: gstmts(g, n) - return + # When adding blanks after certain nodes, we only do so if there's a body let currLine = g.outputLine() gprefixes(g, n) @@ -1499,11 +1449,11 @@ proc gsub(g: var TOutput; n: PNode; flags: SubFlags) = doParamsAux(g, n[paramsPos]) gsub(g, n[pragmasPos]) - put(g, tkColon, ":") - gmids(g, n, true) - gsub(g, n[bodyPos]) + gcolcoms(g, n, n[bodyPos]) of nkIdentDefs: - gcomma(g, n, theEnd = -3, indentNL = 2, flags = {lfFirstSticky}) + gcomma( + g, n, theEnd = -3, indentNL = 2, flags = {lfFirstSticky, lfFirstCommentSticky} + ) if n.len >= 2 and n[^2].kind != nkEmpty: putWithSpace(g, tkColon, ":") @@ -1653,28 +1603,18 @@ proc gsub(g: var TOutput; n: PNode; flags: SubFlags) = put(g, tkAccent, "`") of nkIfExpr: putWithSpace(g, tkIf, "if") - if n.len > 0: - gcond(g, n[0][0]) - - putWithSpace(g, tkColon, ":") - if n.len > 0: - gmids(g, n[0]) - gsub(g, n[0][1]) - optNL(g) - + gcond(g, n[0][0]) + gcolcoms(g, n[0], n[0][1], true) + optNL(g) gsons(g, n, 1) of nkElifExpr: putWithSpace(g, tkElif, "elif") gcond(g, n[0]) - putWithSpace(g, tkColon, ":") - gmids(g, n) - gsub(g, n[1]) + gcolcoms(g, n, n[1], true) optNL(g) of nkElseExpr: put(g, tkElse, "else") - putWithSpace(g, tkColon, ":") - gmids(g, n) - gsub(g, n[0]) + gcolcoms(g, n, n[0], true) optNL(g) of nkTypeOfExpr: if g.inConcept > 0: @@ -1964,9 +1904,7 @@ proc gsub(g: var TOutput; n: PNode; flags: SubFlags) = optNL(g) putWithSpace(g, tkOf, "of") gcomma(g, n, 0, -2, indentNL = longIndentWid) - putWithSpace(g, tkColon, ":") - gmids(g, n) - gstmts(g, lastSon(n)) + gcolcoms(g, n, n[^1]) of nkImportAs: gsub(g, n[0]) put(g, tkSpaces, Space) @@ -1982,15 +1920,11 @@ proc gsub(g: var TOutput; n: PNode; flags: SubFlags) = optNL(g) putWithSpace(g, tkElif, "elif") gcond(g, n[0], flags = {sfLongIndent}) - putWithSpace(g, tkColon, ":") - gmids(g, n) - gstmts(g, n[1]) + gcolcoms(g, n, n[1]) of nkElse: optNL(g) put(g, tkElse, "else") - putWithSpace(g, tkColon, ":") - gmids(g, n) - gstmts(g, n[0]) + gcolcoms(g, n, n[0]) of nkFinally, nkDefer: optNL(g) if n.kind == nkFinally: @@ -1998,9 +1932,7 @@ proc gsub(g: var TOutput; n: PNode; flags: SubFlags) = else: put(g, tkDefer, "defer") - putWithSpace(g, tkColon, ":") - gmids(g, n) - gstmts(g, n[0]) + gcolcoms(g, n, n[0]) of nkExceptBranch: optNL(g) if n.len != 1: @@ -2009,9 +1941,7 @@ proc gsub(g: var TOutput; n: PNode; flags: SubFlags) = put(g, tkExcept, "except") gcomma(g, n, 0, -2, indentNL = longIndentWid) - putWithSpace(g, tkColon, ":") - gmids(g, n) - gstmts(g, lastSon(n)) + gcolcoms(g, n, n[^1]) of nkGenericParams: glist(g, n, tkBracketLe, tkSemiColon, indentNL = flagIndent(flags)) of nkFormalParams: diff --git a/tests/after/comments.nim b/tests/after/comments.nim index 4424eb1..7ff5328 100644 --- a/tests/after/comments.nim +++ b/tests/after/comments.nim @@ -68,6 +68,11 @@ type fiiiiiiiiiiiiiiiiiiiiiiiieeeeeeeeeld: int # loooooooooooooooooooong comment past the max line length + docfield, ## Doc comment after comma + docfield2, ## Doc comment again + ## Multiline + docfield3: int ## here came the type + # and here NewlineObject = object field: int ## doc comment after field @@ -91,7 +96,8 @@ type SomeAlias2 {.nodecl.} = int ## alias2 eol SomeAlias3 # alias after symbol - [T] = # alias after equals + [T] = + # alias after equals int # alias after type SomeAlias4 = SomeAlias3[int] @@ -119,6 +125,16 @@ else: # else colon line # else first line discard +if true: + ## doc-comment-only if + +block: + if true: + ## doc-comment-only if nested + +while false: + ## doc-comment-only while + if true: # if next line discard @@ -203,8 +219,7 @@ static: # static colon line # static first line discard -discard Object( - # object eol +discard Object( # object eol # object first line field: 0, # field line field2: @@ -222,17 +237,21 @@ block: ## Doc comment after indented statement ## needs to be double -abc and # dedented comment in infix +abc and +# dedented comment in infix def -abc and # indented comment in infix +abc and +# indented comment in infix def -if abc and # dedented comment in infix +if abc and +# dedented comment in infix def: discard -if abc and # indented comment in infix +if abc and +# indented comment in infix def: discard @@ -258,7 +277,8 @@ var let # let eol v # let ident after symbol - : # let ident after colon + : + # let ident after colon int = # let ident after type # let ident after equals @@ -266,7 +286,8 @@ let # let eol const # const eol v # const ident after symbol - : # const ident after colon + : + # const ident after colon int = # const ident after type # const ident after equals @@ -333,8 +354,8 @@ proc a(v #[block]# command "a", "b", "c" # command eol comment -command "first arg" # first arg comment -, "second arg", # second arg comment +command "first arg", # first arg comment + "second arg", # second arg comment "third arg" # third arg comment command "first arg" diff --git a/tests/after/comments.nim.nph.yaml b/tests/after/comments.nim.nph.yaml index cecd100..ca7b5ef 100644 --- a/tests/after/comments.nim.nph.yaml +++ b/tests/after/comments.nim.nph.yaml @@ -199,6 +199,24 @@ sons: - kind: "nkEmpty" postfix: - "# loooooooooooooooooooong comment past the max line length" + - kind: "nkIdentDefs" + sons: + - kind: "nkIdent" + ident: "docfield" + postfix: + - "## Doc comment after comma" + - kind: "nkIdent" + ident: "docfield2" + postfix: + - "## Doc comment again" + - "## Multiline" + - kind: "nkIdent" + ident: "docfield3" + - kind: "nkIdent" + ident: "int" + - kind: "nkEmpty" + postfix: + - "## here came the type" - kind: "nkTypeDef" prefix: - "# and here" @@ -460,6 +478,37 @@ sons: - kind: "nkDiscardStmt" sons: - kind: "nkEmpty" + - kind: "nkIfStmt" + sons: + - kind: "nkElifBranch" + mid: + - "## doc-comment-only if" + sons: + - kind: "nkIdent" + ident: "true" + - kind: "nkStmtList" + - kind: "nkBlockStmt" + sons: + - kind: "nkEmpty" + - kind: "nkStmtList" + sons: + - kind: "nkIfStmt" + sons: + - kind: "nkElifBranch" + sons: + - kind: "nkIdent" + ident: "true" + - kind: "nkStmtList" + sons: + - kind: "nkCommentStmt" + "comment": "## doc-comment-only if nested" + - kind: "nkWhileStmt" + mid: + - "## doc-comment-only while" + sons: + - kind: "nkIdent" + ident: "false" + - kind: "nkStmtList" - kind: "nkIfStmt" sons: - kind: "nkElifBranch" @@ -765,6 +814,8 @@ sons: - kind: "nkDiscardStmt" sons: - kind: "nkObjConstr" + mid: + - "# object eol" sons: - kind: "nkIdent" ident: "Object" @@ -772,7 +823,6 @@ sons: sons: - kind: "nkIdent" prefix: - - "# object eol" - "# object first line" ident: "field" - kind: "nkIntLit" @@ -819,21 +869,21 @@ sons: sons: - kind: "nkIdent" ident: "and" - postfix: - - "# dedented comment in infix" - kind: "nkIdent" ident: "abc" - kind: "nkIdent" + prefix: + - "# dedented comment in infix" ident: "def" - kind: "nkInfix" sons: - kind: "nkIdent" ident: "and" - postfix: - - "# indented comment in infix" - kind: "nkIdent" ident: "abc" - kind: "nkIdent" + prefix: + - "# indented comment in infix" ident: "def" - kind: "nkIfStmt" sons: @@ -843,11 +893,11 @@ sons: sons: - kind: "nkIdent" ident: "and" - postfix: - - "# dedented comment in infix" - kind: "nkIdent" ident: "abc" - kind: "nkIdent" + prefix: + - "# dedented comment in infix" ident: "def" - kind: "nkStmtList" sons: @@ -862,11 +912,11 @@ sons: sons: - kind: "nkIdent" ident: "and" - postfix: - - "# indented comment in infix" - kind: "nkIdent" ident: "abc" - kind: "nkIdent" + prefix: + - "# indented comment in infix" ident: "def" - kind: "nkStmtList" sons: diff --git a/tests/after/exprs.nim b/tests/after/exprs.nim index 5488d69..f8d5909 100644 --- a/tests/after/exprs.nim +++ b/tests/after/exprs.nim @@ -12,6 +12,21 @@ var c3 = else: 0 ) +while (; + node = node.next + + node != nil +): + discard + +while ( + var a = 0 + inc a + a += 42 + a > 0 +): + discard + for a in 0..<1: discard diff --git a/tests/after/exprs.nim.nph.yaml b/tests/after/exprs.nim.nph.yaml index 786bdba..d8d124e 100644 --- a/tests/after/exprs.nim.nph.yaml +++ b/tests/after/exprs.nim.nph.yaml @@ -78,6 +78,72 @@ sons: sons: - kind: "nkIntLit" intVal: 0 + - kind: "nkWhileStmt" + sons: + - kind: "nkStmtListExpr" + sons: + - kind: "nkAsgn" + sons: + - kind: "nkIdent" + ident: "node" + - kind: "nkDotExpr" + sons: + - kind: "nkIdent" + ident: "node" + - kind: "nkIdent" + ident: "next" + - kind: "nkInfix" + sons: + - kind: "nkIdent" + ident: "!=" + - kind: "nkIdent" + ident: "node" + - kind: "nkNilLit" + - kind: "nkStmtList" + sons: + - kind: "nkDiscardStmt" + sons: + - kind: "nkEmpty" + - kind: "nkWhileStmt" + sons: + - kind: "nkStmtListExpr" + sons: + - kind: "nkVarSection" + sons: + - kind: "nkIdentDefs" + sons: + - kind: "nkIdent" + ident: "a" + - kind: "nkEmpty" + - kind: "nkIntLit" + intVal: 0 + - kind: "nkCommand" + sons: + - kind: "nkIdent" + ident: "inc" + - kind: "nkIdent" + ident: "a" + - kind: "nkInfix" + sons: + - kind: "nkIdent" + ident: "+=" + - kind: "nkIdent" + ident: "a" + - kind: "nkIntLit" + intVal: 42 + - kind: "nkInfix" + sons: + - kind: "nkIdent" + ident: ">" + - kind: "nkIdent" + ident: "a" + - kind: "nkIntLit" + intVal: 0 + - kind: "nkStmtList" + sons: + - kind: "nkDiscardStmt" + sons: + - kind: "nkEmpty" - kind: "nkForStmt" sons: - kind: "nkIdent" diff --git a/tests/before/comments.nim b/tests/before/comments.nim index 68b0c30..f261392 100644 --- a/tests/before/comments.nim +++ b/tests/before/comments.nim @@ -64,6 +64,11 @@ type # comment between fields field2: int ## Field comment again fiiiiiiiiiiiiiiiiiiiiiiiieeeeeeeeeld: int # loooooooooooooooooooong comment past the max line length + + docfield, ## Doc comment after comma + docfield2, ## Doc comment again + ## Multiline + docfield3: int ## here came the type # and here NewlineObject = object @@ -114,6 +119,16 @@ else: # else colon line # else first line discard +if true: + ## doc-comment-only if + +block: + if true: + ## doc-comment-only if nested + +while false: + ## doc-comment-only while + if true: # if next line discard diff --git a/tests/before/comments.nim.nph.yaml b/tests/before/comments.nim.nph.yaml index fd38c40..19213f9 100644 --- a/tests/before/comments.nim.nph.yaml +++ b/tests/before/comments.nim.nph.yaml @@ -199,6 +199,24 @@ sons: - kind: "nkEmpty" postfix: - "# loooooooooooooooooooong comment past the max line length" + - kind: "nkIdentDefs" + sons: + - kind: "nkIdent" + ident: "docfield" + postfix: + - "## Doc comment after comma" + - kind: "nkIdent" + ident: "docfield2" + postfix: + - "## Doc comment again" + - "## Multiline" + - kind: "nkIdent" + ident: "docfield3" + - kind: "nkIdent" + ident: "int" + - kind: "nkEmpty" + postfix: + - "## here came the type" - kind: "nkTypeDef" prefix: - "# and here" @@ -462,6 +480,37 @@ sons: - kind: "nkDiscardStmt" sons: - kind: "nkEmpty" + - kind: "nkIfStmt" + sons: + - kind: "nkElifBranch" + mid: + - "## doc-comment-only if" + sons: + - kind: "nkIdent" + ident: "true" + - kind: "nkStmtList" + - kind: "nkBlockStmt" + sons: + - kind: "nkEmpty" + - kind: "nkStmtList" + sons: + - kind: "nkIfStmt" + sons: + - kind: "nkElifBranch" + sons: + - kind: "nkIdent" + ident: "true" + - kind: "nkStmtList" + sons: + - kind: "nkCommentStmt" + "comment": "## doc-comment-only if nested" + - kind: "nkWhileStmt" + mid: + - "## doc-comment-only while" + sons: + - kind: "nkIdent" + ident: "false" + - kind: "nkStmtList" - kind: "nkIfStmt" sons: - kind: "nkElifBranch" @@ -514,9 +563,9 @@ sons: ident: "true" - kind: "nkStmtList" sons: - - kind: "nkCommentStmt" - "comment": "# if dedented colon line" - kind: "nkDiscardStmt" + prefix: + - "# if dedented colon line" sons: - kind: "nkEmpty" - kind: "nkElse" @@ -587,11 +636,11 @@ sons: sons: - kind: "nkStmtList" sons: - - kind: "nkCommentStmt" - "comment": "# try first dedent line" - kind: "nkCall" sons: - kind: "nkIdent" + prefix: + - "# try first dedent line" ident: "f" - kind: "nkExceptBranch" prefix: @@ -599,9 +648,9 @@ sons: sons: - kind: "nkStmtList" sons: - - kind: "nkCommentStmt" - "comment": "# except dedent first line" - kind: "nkDiscardStmt" + prefix: + - "# except dedent first line" sons: - kind: "nkEmpty" - kind: "nkFinally" @@ -610,9 +659,9 @@ sons: sons: - kind: "nkStmtList" sons: - - kind: "nkCommentStmt" - "comment": "# finally first dedent line" - kind: "nkDiscardStmt" + prefix: + - "# finally first dedent line" sons: - kind: "nkEmpty" - kind: "nkCommentStmt" diff --git a/tests/before/exprs.nim b/tests/before/exprs.nim index 9562e72..dde0262 100644 --- a/tests/before/exprs.nim +++ b/tests/before/exprs.nim @@ -5,6 +5,12 @@ var c3 = row[p] + (if runeA != runeB: var c3 = row[p] + (if runeA != runeB: 1 else: 0) +while (node = node.next; node != nil): + discard + +while (var a = 0; inc a; a += 42; a > 0): + discard + for a in 0..<1: discard diff --git a/tests/before/exprs.nim.nph.yaml b/tests/before/exprs.nim.nph.yaml index 24db33f..236034f 100644 --- a/tests/before/exprs.nim.nph.yaml +++ b/tests/before/exprs.nim.nph.yaml @@ -78,6 +78,73 @@ sons: sons: - kind: "nkIntLit" intVal: 0 + - kind: "nkWhileStmt" + sons: + - kind: "nkStmtListExpr" + sons: + - kind: "nkAsgn" + sons: + - kind: "nkIdent" + ident: "node" + - kind: "nkDotExpr" + sons: + - kind: "nkIdent" + ident: "node" + - kind: "nkIdent" + ident: "next" + - kind: "nkEmpty" + - kind: "nkInfix" + sons: + - kind: "nkIdent" + ident: "!=" + - kind: "nkIdent" + ident: "node" + - kind: "nkNilLit" + - kind: "nkStmtList" + sons: + - kind: "nkDiscardStmt" + sons: + - kind: "nkEmpty" + - kind: "nkWhileStmt" + sons: + - kind: "nkStmtListExpr" + sons: + - kind: "nkVarSection" + sons: + - kind: "nkIdentDefs" + sons: + - kind: "nkIdent" + ident: "a" + - kind: "nkEmpty" + - kind: "nkIntLit" + intVal: 0 + - kind: "nkCommand" + sons: + - kind: "nkIdent" + ident: "inc" + - kind: "nkIdent" + ident: "a" + - kind: "nkInfix" + sons: + - kind: "nkIdent" + ident: "+=" + - kind: "nkIdent" + ident: "a" + - kind: "nkIntLit" + intVal: 42 + - kind: "nkInfix" + sons: + - kind: "nkIdent" + ident: ">" + - kind: "nkIdent" + ident: "a" + - kind: "nkIntLit" + intVal: 0 + - kind: "nkStmtList" + sons: + - kind: "nkDiscardStmt" + sons: + - kind: "nkEmpty" - kind: "nkForStmt" sons: - kind: "nkIdent" From 37385187b3c32259456b4e978ffaecafc469d286 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Tue, 19 Dec 2023 16:51:55 +0100 Subject: [PATCH 2/4] ignore nimble files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index f4b97ac..730c6c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ nimbledeps /nph +nimble.develop +nimble.paths From 83aaf57f7ad61c515cb783501e3aa80b45a0634c Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Tue, 19 Dec 2023 17:07:58 +0100 Subject: [PATCH 3/4] windows ignores --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 730c6c4..e6ce687 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ nimbledeps /nph nimble.develop nimble.paths +*.exe From 0084fd6b91f1f5d9d449afe86189af0a990a7981 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Tue, 19 Dec 2023 17:12:13 +0100 Subject: [PATCH 4/4] eol for windows ci --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17862f1..98c7067 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,10 @@ jobs: name: CI '${{ matrix.target.name }}' runs-on: ${{ matrix.builder }} steps: + - name: Set git to use LF + run: | + git config --global core.autocrlf false + git config --global core.eol lf - name: Checkout uses: actions/checkout@v3