From dac496e6c2682c0d61f329efb0b600c4bcc686b5 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 24 Apr 2020 20:42:30 -0600 Subject: [PATCH 01/19] Add comments for enums and procs --- nimterop/ast2.nim | 36 +++++++++++++++++++++++++----------- nimterop/getters.nim | 21 ++++++++++++++++++++- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/nimterop/ast2.nim b/nimterop/ast2.nim index 4d609cb..17fe216 100644 --- a/nimterop/ast2.nim +++ b/nimterop/ast2.nim @@ -1393,14 +1393,18 @@ proc addEnum(gState: State, node: TSNode) = fnames: HashSet[string] # Hold all of field information so that we can add all of them # after the const identifiers has been updated - fieldDeclarations: seq[tuple[fname: string, fval: string, cexpr: Option[TSNode]]] + fieldDeclarations: seq[tuple[fname: string, fval: string, cexpr: Option[TSNode], comment: Option[TSNode]]] for i in 0 .. enumlist.len - 1: let en = enumlist[i] if en.getName() == "comment": continue + let - fname = gState.getIdentifier(gState.getNodeVal(en.getAtom()), nskEnumField) + atom = en.getAtom() + commentNode = en.getInlineCommentNode() + fname = gState.getIdentifier(gState.getNodeVal(atom), nskEnumField) + if fname.nBl and gState.addNewIdentifer(fname): var fval = "" @@ -1412,9 +1416,9 @@ proc addEnum(gState: State, node: TSNode) = fval = &"({prev} + 1).{name}" if en.len > 1 and en[1].getName() in gEnumVals: - fieldDeclarations.add((fname, "", some(en[1]))) + fieldDeclarations.add((fname, "", some(en[1]), commentNode)) else: - fieldDeclarations.add((fname, fval, none(TSNode))) + fieldDeclarations.add((fname, fval, none(TSNode), commentNode)) fnames.incl fname prev = fname @@ -1424,18 +1428,20 @@ proc addEnum(gState: State, node: TSNode) = gState.constIdentifiers.incl fnames # parseCExpression requires all const identifiers to be present for the enum - for (fname, fval, cexprNode) in fieldDeclarations: + for (fname, fval, cexprNode, commentNode) in fieldDeclarations: var fval = fval if cexprNode.isSome: fval = "(" & $gState.parseCExpression(gState.getNodeVal(cexprNode.get()), name) & ")." & name # Cannot use newConstDef() since parseString(fval) adds backticks to and/or - gState.constSection.add gState.parseString(&"const {fname}* = {fval}")[0][0] + let constNode = gState.parseString(&"const {fname}* = {fval}")[0][0] + constNode.comment = gState.getCommentVal(commentNode) + gState.constSection.add constNode # Add other names if node.getName() == "type_definition" and node.len > 1: gState.addTypeTyped(node, ftname = name, offset = offset) -proc addProcVar(gState: State, node, rnode: TSNode) = +proc addProcVar(gState: State, node, rnode: TSNode, commentNode: Option[TSNode]) = # Add a proc variable decho("addProcVar()") let @@ -1488,12 +1494,13 @@ proc addProcVar(gState: State, node, rnode: TSNode) = # nkEmpty() # ) + identDefs.comment = gState.getCommentVal(commentNode) # nkVarSection.add gState.varSection.add identDefs gState.printDebug(identDefs) -proc addProc(gState: State, node, rnode: TSNode) = +proc addProc(gState: State, node, rnode: TSNode, commentNode: Option[TSNode]) = # Add a proc # # `node` is the `nth` child of (declaration) @@ -1599,6 +1606,8 @@ proc addProc(gState: State, node, rnode: TSNode) = procDef.add newNode(nkEmpty) procDef.add newNode(nkEmpty) + procDef.comment = gState.getCommentVal(commentNode) + # nkProcSection.add gState.procSection.add procDef @@ -1609,18 +1618,20 @@ proc addDecl(gState: State, node: TSNode) = decho("addDecl()") gState.printDebug(node) + let start = getStartAtom(node) + commentNode = node.getCommentNode() for i in start+1 ..< node.len: if not node[i].firstChildInTree("function_declarator").isNil: # Proc declaration - var or actual proc if node[i].getAtom().getPxName(1) == "pointer_declarator": # proc var - gState.addProcVar(node[i], node[start]) + gState.addProcVar(node[i], node[start], commentNode) else: # proc - gState.addProc(node[i], node[start]) + gState.addProc(node[i], node[start], commentNode) else: # Regular var discard @@ -1632,11 +1643,14 @@ proc addDef(gState: State, node: TSNode) = # and will fail at link time decho("addDef()") gState.printDebug(node) + let start = getStartAtom(node) + commentNode = node.getCommentNode() + if node[start+1].getName() == "function_declarator": if gState.isIncludeHeader(): - gState.addProc(node[start+1], node[start]) + gState.addProc(node[start+1], node[start], commentNode) else: gecho &"\n# proc '$1' skipped - static inline procs require 'includeHeader'" % gState.getNodeVal(node[start+1].getAtom()) diff --git a/nimterop/getters.nim b/nimterop/getters.nim index 620c1ec..d03e65b 100644 --- a/nimterop/getters.nim +++ b/nimterop/getters.nim @@ -1,4 +1,4 @@ -import dynlib, macros, os, sequtils, sets, strformat, strutils, tables, times +import dynlib, macros, os, sequtils, sets, strformat, strutils, tables, times, options import regex @@ -456,6 +456,9 @@ proc printLisp*(gState: State, root: TSNode): string = proc getCommented*(str: string): string = "\n# " & str.strip().replace("\n", "\n# ") +proc getDocStrCommented*(str: string): string = + "\n## " & str.strip().replace("\n", "\n## ") + proc printTree*(gState: State, pnode: PNode, offset = ""): string = if not pnode.isNil and gState.debug and pnode.kind != nkNone: result &= "\n# " & offset & $pnode.kind & "(" @@ -634,6 +637,22 @@ proc getNameKind*(name: string): tuple[name: string, kind: Kind, recursive: bool if result.kind != exactlyOne: result.name = result.name[0 .. ^2] +proc getCommentVal*(gState: State, commentNode: Option[TSNode]): string = + if commentNode.isSome(): + result = gState.getNodeVal(commentNode.get()).replace(re"( *(/\*\*|\*\*/|\*/|\*))", "").strip() + +proc getCommentNode*(node: TSNode): Option[TSNode] = + result = none(TSNode) + let prevSibling = node.tsNodePrevNamedSibling() + if not prevSibling.isNil and prevSibling.getName() == "comment": + result = some(prevSibling) + +proc getInlineCommentNode*(node: TSNode): Option[TSNode] = + result = none(TSNode) + let nextSibling = node.tsNodeNextNamedSibling() + if not nextSibling.isNil and nextSibling.getName() == "comment": + result = some(nextSibling) + proc getTSNodeNamedChildNames*(node: TSNode): seq[string] = if node.tsNodeNamedChildCount() != 0: for i in 0 .. node.tsNodeNamedChildCount()-1: From 4ea1f0f3408d1b04be1271c531f1f637246531ce Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 24 Apr 2020 22:50:14 -0600 Subject: [PATCH 02/19] Add comments for fields and objects --- nimterop/ast2.nim | 26 ++++++++++++++++++++++---- nimterop/getters.nim | 24 ++++++++++++++---------- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/nimterop/ast2.nim b/nimterop/ast2.nim index 17fe216..2fb48c0 100644 --- a/nimterop/ast2.nim +++ b/nimterop/ast2.nim @@ -691,6 +691,7 @@ proc newRecListTree(gState: State, name: string, node: TSNode): PNode = let fdecl = node[i].anyChildInTree("field_declaration_list") edecl = node[i].anyChildInTree("enumerator_list") + commentNode = node[i].getNextCommentNode() # `tname` is name of nested struct / union / enum just # added, passed on as type name for field in `newIdentDefs()` @@ -716,6 +717,7 @@ proc newRecListTree(gState: State, name: string, node: TSNode): PNode = # Add nkIdentDefs for each field for field in gState.newIdentDefs(name, node[i], i, ftname = tname, exported = true): if not field.isNil: + field.comment = gState.getCommentVal(commentNode) result.add field proc addTypeObject(gState: State, node: TSNode, typeDef: PNode = nil, fname = "", istype = false, union = false) = @@ -725,6 +727,8 @@ proc addTypeObject(gState: State, node: TSNode, typeDef: PNode = nil, fname = "" # If `fname` is set, use it as the name when creating new PNode # If `istype` is set, this is a typedef, else struct/union decho("addTypeObject()") + let commentNode = node.tsNodeParent().getPrevCommentNode() + let # Object has fields or not fdlist = node.anyChildInTree("field_declaration_list") @@ -837,6 +841,7 @@ proc addTypeObject(gState: State, node: TSNode, typeDef: PNode = nil, fname = "" gState.addPragma(node, typeDef[0][1], pragmas) # nkTypeSection.add + typeDef.comment = gState.getCommentVal(commentNode) gState.typeSection.add typeDef gState.printDebug(typeDef) @@ -848,6 +853,7 @@ proc addTypeObject(gState: State, node: TSNode, typeDef: PNode = nil, fname = "" # Current node has fields let origname = gState.getNodeVal(node.getAtom()) + commentNode = node.getNextCommentNode() # Fix issue #185 name = @@ -859,6 +865,8 @@ proc addTypeObject(gState: State, node: TSNode, typeDef: PNode = nil, fname = "" if name.nBl and gState.identifierNodes.hasKey(name): let def = gState.identifierNodes[name] + def.comment = gState.getCommentVal(commentNode) + # Duplicate nkTypeDef for `name` with empty fields if def.kind == nkTypeDef and def.len == 3 and def[2].kind == nkObjectTy and def[2].len == 3 and @@ -890,6 +898,7 @@ proc addTypeTyped(gState: State, node: TSNode, ftname = "", offset = 0) = decho("addTypeTyped()") let start = getStartAtom(node) + commentNode = node.getPrevCommentNode() for i in start+1+offset ..< node.len: # Add a type of a specific type let @@ -897,6 +906,7 @@ proc addTypeTyped(gState: State, node: TSNode, ftname = "", offset = 0) = typeDef = gState.newXIdent(node[i], istype = true) if not typeDef.isNil: + typeDef.comment = gState.getCommentVal(commentNode) let name = typeDef.getIdentName() @@ -1386,7 +1396,15 @@ proc addEnum(gState: State, node: TSNode) = gState.typeSection.add eoverride elif gState.addNewIdentifer(name): # Add enum definition and helpers - gState.enumSection.add gState.parseString(&"defineEnum({name})") + let defineNode = gState.parseString(&"defineEnum({name})") + # nkStmtList( + # nkCall( + # nkIdent("defineEnum"), + # nkIdent(name) <- set the comment here + # ) + # ) + defineNode[0][1].comment = gState.getCommentVal(node.getPrevCommentNode()) + gState.enumSection.add defineNode # Create const for fields var @@ -1402,7 +1420,7 @@ proc addEnum(gState: State, node: TSNode) = let atom = en.getAtom() - commentNode = en.getInlineCommentNode() + commentNode = en.getNextCommentNode() fname = gState.getIdentifier(gState.getNodeVal(atom), nskEnumField) if fname.nBl and gState.addNewIdentifer(fname): @@ -1621,7 +1639,7 @@ proc addDecl(gState: State, node: TSNode) = let start = getStartAtom(node) - commentNode = node.getCommentNode() + commentNode = node.getPrevCommentNode() for i in start+1 ..< node.len: if not node[i].firstChildInTree("function_declarator").isNil: @@ -1646,7 +1664,7 @@ proc addDef(gState: State, node: TSNode) = let start = getStartAtom(node) - commentNode = node.getCommentNode() + commentNode = node.getPrevCommentNode() if node[start+1].getName() == "function_declarator": if gState.isIncludeHeader(): diff --git a/nimterop/getters.nim b/nimterop/getters.nim index d03e65b..5ad9e4e 100644 --- a/nimterop/getters.nim +++ b/nimterop/getters.nim @@ -639,19 +639,23 @@ proc getNameKind*(name: string): tuple[name: string, kind: Kind, recursive: bool proc getCommentVal*(gState: State, commentNode: Option[TSNode]): string = if commentNode.isSome(): - result = gState.getNodeVal(commentNode.get()).replace(re"( *(/\*\*|\*\*/|\*/|\*))", "").strip() + result = gState.getNodeVal(commentNode.get()).replace(re" *(/\*\*|\*\*/|\*/|\*)", "").strip() -proc getCommentNode*(node: TSNode): Option[TSNode] = +template findComment(procName: untyped): untyped = result = none(TSNode) - let prevSibling = node.tsNodePrevNamedSibling() - if not prevSibling.isNil and prevSibling.getName() == "comment": - result = some(prevSibling) + var sibling = node.`procName`() + var i = 0 + while not sibling.isNil and i < maxSearch: + if sibling.getName() == "comment": + return some(sibling) + sibling = sibling.`procName`() + i += 1 -proc getInlineCommentNode*(node: TSNode): Option[TSNode] = - result = none(TSNode) - let nextSibling = node.tsNodeNextNamedSibling() - if not nextSibling.isNil and nextSibling.getName() == "comment": - result = some(nextSibling) +proc getPrevCommentNode*(node: TSNode, maxSearch=4): Option[TSNode] = + findComment(tsNodePrevNamedSibling) + +proc getNextCommentNode*(node: TSNode, maxSearch=4): Option[TSNode] = + findComment(tsNodeNextNamedSibling) proc getTSNodeNamedChildNames*(node: TSNode): seq[string] = if node.tsNodeNamedChildCount() != 0: From f2975fde553c1203423498312ccf77c740435732 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 24 Apr 2020 23:41:56 -0600 Subject: [PATCH 03/19] Fix comments not being valid rst --- nimterop/getters.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nimterop/getters.nim b/nimterop/getters.nim index 5ad9e4e..ed2cf73 100644 --- a/nimterop/getters.nim +++ b/nimterop/getters.nim @@ -639,7 +639,7 @@ proc getNameKind*(name: string): tuple[name: string, kind: Kind, recursive: bool proc getCommentVal*(gState: State, commentNode: Option[TSNode]): string = if commentNode.isSome(): - result = gState.getNodeVal(commentNode.get()).replace(re" *(/\*\*|\*\*/|\*/|\*)", "").strip() + result = "::\n " & gState.getNodeVal(commentNode.get()).replace(re" *(/\*\*|\*\*/|\*/|\*)", "").replace("\n", "\n ").strip() template findComment(procName: untyped): untyped = result = none(TSNode) @@ -651,10 +651,10 @@ template findComment(procName: untyped): untyped = sibling = sibling.`procName`() i += 1 -proc getPrevCommentNode*(node: TSNode, maxSearch=4): Option[TSNode] = +proc getPrevCommentNode*(node: TSNode, maxSearch=1): Option[TSNode] = findComment(tsNodePrevNamedSibling) -proc getNextCommentNode*(node: TSNode, maxSearch=4): Option[TSNode] = +proc getNextCommentNode*(node: TSNode, maxSearch=1): Option[TSNode] = findComment(tsNodeNextNamedSibling) proc getTSNodeNamedChildNames*(node: TSNode): seq[string] = From 6a8d05dae2d43eb443cbfd6fc9f64c018a8a2c17 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 25 Apr 2020 13:08:52 -0600 Subject: [PATCH 04/19] Add ability to have multiple comments --- nimterop/ast2.nim | 49 ++++++++++++++++++++++---------------------- nimterop/getters.nim | 40 ++++++++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/nimterop/ast2.nim b/nimterop/ast2.nim index 2fb48c0..2e601db 100644 --- a/nimterop/ast2.nim +++ b/nimterop/ast2.nim @@ -691,7 +691,7 @@ proc newRecListTree(gState: State, name: string, node: TSNode): PNode = let fdecl = node[i].anyChildInTree("field_declaration_list") edecl = node[i].anyChildInTree("enumerator_list") - commentNode = node[i].getNextCommentNode() + commentNodes = node[i].getNextCommentNodes() # `tname` is name of nested struct / union / enum just # added, passed on as type name for field in `newIdentDefs()` @@ -717,7 +717,7 @@ proc newRecListTree(gState: State, name: string, node: TSNode): PNode = # Add nkIdentDefs for each field for field in gState.newIdentDefs(name, node[i], i, ftname = tname, exported = true): if not field.isNil: - field.comment = gState.getCommentVal(commentNode) + field.comment = gState.getCommentsStr(commentNodes) result.add field proc addTypeObject(gState: State, node: TSNode, typeDef: PNode = nil, fname = "", istype = false, union = false) = @@ -727,7 +727,7 @@ proc addTypeObject(gState: State, node: TSNode, typeDef: PNode = nil, fname = "" # If `fname` is set, use it as the name when creating new PNode # If `istype` is set, this is a typedef, else struct/union decho("addTypeObject()") - let commentNode = node.tsNodeParent().getPrevCommentNode() + let commentNodes = node.tsNodeParent().getPrevCommentNodes() let # Object has fields or not @@ -841,7 +841,7 @@ proc addTypeObject(gState: State, node: TSNode, typeDef: PNode = nil, fname = "" gState.addPragma(node, typeDef[0][1], pragmas) # nkTypeSection.add - typeDef.comment = gState.getCommentVal(commentNode) + typeDef.comment = gState.getCommentsStr(commentNodes) gState.typeSection.add typeDef gState.printDebug(typeDef) @@ -853,7 +853,7 @@ proc addTypeObject(gState: State, node: TSNode, typeDef: PNode = nil, fname = "" # Current node has fields let origname = gState.getNodeVal(node.getAtom()) - commentNode = node.getNextCommentNode() + commentNodes = node.getNextCommentNodes() # Fix issue #185 name = @@ -865,7 +865,7 @@ proc addTypeObject(gState: State, node: TSNode, typeDef: PNode = nil, fname = "" if name.nBl and gState.identifierNodes.hasKey(name): let def = gState.identifierNodes[name] - def.comment = gState.getCommentVal(commentNode) + def.comment = gState.getCommentsStr(commentNodes) # Duplicate nkTypeDef for `name` with empty fields if def.kind == nkTypeDef and def.len == 3 and @@ -898,7 +898,7 @@ proc addTypeTyped(gState: State, node: TSNode, ftname = "", offset = 0) = decho("addTypeTyped()") let start = getStartAtom(node) - commentNode = node.getPrevCommentNode() + commentNodes = node.getPrevCommentNodes() for i in start+1+offset ..< node.len: # Add a type of a specific type let @@ -906,7 +906,7 @@ proc addTypeTyped(gState: State, node: TSNode, ftname = "", offset = 0) = typeDef = gState.newXIdent(node[i], istype = true) if not typeDef.isNil: - typeDef.comment = gState.getCommentVal(commentNode) + typeDef.comment = gState.getCommentsStr(commentNodes) let name = typeDef.getIdentName() @@ -1403,7 +1403,7 @@ proc addEnum(gState: State, node: TSNode) = # nkIdent(name) <- set the comment here # ) # ) - defineNode[0][1].comment = gState.getCommentVal(node.getPrevCommentNode()) + defineNode[0][1].comment = gState.getCommentsStr(node.getPrevCommentNodes()) gState.enumSection.add defineNode # Create const for fields @@ -1411,7 +1411,7 @@ proc addEnum(gState: State, node: TSNode) = fnames: HashSet[string] # Hold all of field information so that we can add all of them # after the const identifiers has been updated - fieldDeclarations: seq[tuple[fname: string, fval: string, cexpr: Option[TSNode], comment: Option[TSNode]]] + fieldDeclarations: seq[tuple[fname: string, fval: string, cexpr: Option[TSNode], comment: seq[TSNode]]] for i in 0 .. enumlist.len - 1: let en = enumlist[i] @@ -1420,7 +1420,7 @@ proc addEnum(gState: State, node: TSNode) = let atom = en.getAtom() - commentNode = en.getNextCommentNode() + commentNodes = en.getNextCommentNodes() fname = gState.getIdentifier(gState.getNodeVal(atom), nskEnumField) if fname.nBl and gState.addNewIdentifer(fname): @@ -1434,9 +1434,9 @@ proc addEnum(gState: State, node: TSNode) = fval = &"({prev} + 1).{name}" if en.len > 1 and en[1].getName() in gEnumVals: - fieldDeclarations.add((fname, "", some(en[1]), commentNode)) + fieldDeclarations.add((fname, "", some(en[1]), commentNodes)) else: - fieldDeclarations.add((fname, fval, none(TSNode), commentNode)) + fieldDeclarations.add((fname, fval, none(TSNode), commentNodes)) fnames.incl fname prev = fname @@ -1446,20 +1446,20 @@ proc addEnum(gState: State, node: TSNode) = gState.constIdentifiers.incl fnames # parseCExpression requires all const identifiers to be present for the enum - for (fname, fval, cexprNode, commentNode) in fieldDeclarations: + for (fname, fval, cexprNode, commentNodes) in fieldDeclarations: var fval = fval if cexprNode.isSome: fval = "(" & $gState.parseCExpression(gState.getNodeVal(cexprNode.get()), name) & ")." & name # Cannot use newConstDef() since parseString(fval) adds backticks to and/or let constNode = gState.parseString(&"const {fname}* = {fval}")[0][0] - constNode.comment = gState.getCommentVal(commentNode) + constNode.comment = gState.getCommentsStr(commentNodes) gState.constSection.add constNode # Add other names if node.getName() == "type_definition" and node.len > 1: gState.addTypeTyped(node, ftname = name, offset = offset) -proc addProcVar(gState: State, node, rnode: TSNode, commentNode: Option[TSNode]) = +proc addProcVar(gState: State, node, rnode: TSNode, commentNodes: seq[TSNode]) = # Add a proc variable decho("addProcVar()") let @@ -1512,13 +1512,13 @@ proc addProcVar(gState: State, node, rnode: TSNode, commentNode: Option[TSNode]) # nkEmpty() # ) - identDefs.comment = gState.getCommentVal(commentNode) + identDefs.comment = gState.getCommentsStr(commentNodes) # nkVarSection.add gState.varSection.add identDefs gState.printDebug(identDefs) -proc addProc(gState: State, node, rnode: TSNode, commentNode: Option[TSNode]) = +proc addProc(gState: State, node, rnode: TSNode, commentNodes: seq[TSNode]) = # Add a proc # # `node` is the `nth` child of (declaration) @@ -1624,7 +1624,7 @@ proc addProc(gState: State, node, rnode: TSNode, commentNode: Option[TSNode]) = procDef.add newNode(nkEmpty) procDef.add newNode(nkEmpty) - procDef.comment = gState.getCommentVal(commentNode) + procDef.comment = gState.getCommentsStr(commentNodes) # nkProcSection.add gState.procSection.add procDef @@ -1636,20 +1636,19 @@ proc addDecl(gState: State, node: TSNode) = decho("addDecl()") gState.printDebug(node) - let start = getStartAtom(node) - commentNode = node.getPrevCommentNode() + commentNodes = node.getPrevCommentNodes() for i in start+1 ..< node.len: if not node[i].firstChildInTree("function_declarator").isNil: # Proc declaration - var or actual proc if node[i].getAtom().getPxName(1) == "pointer_declarator": # proc var - gState.addProcVar(node[i], node[start], commentNode) + gState.addProcVar(node[i], node[start], commentNodes) else: # proc - gState.addProc(node[i], node[start], commentNode) + gState.addProc(node[i], node[start], commentNodes) else: # Regular var discard @@ -1664,11 +1663,11 @@ proc addDef(gState: State, node: TSNode) = let start = getStartAtom(node) - commentNode = node.getPrevCommentNode() + commentNodes = node.getPrevCommentNodes() if node[start+1].getName() == "function_declarator": if gState.isIncludeHeader(): - gState.addProc(node[start+1], node[start], commentNode) + gState.addProc(node[start+1], node[start], commentNodes) else: gecho &"\n# proc '$1' skipped - static inline procs require 'includeHeader'" % gState.getNodeVal(node[start+1].getAtom()) diff --git a/nimterop/getters.nim b/nimterop/getters.nim index ed2cf73..1240130 100644 --- a/nimterop/getters.nim +++ b/nimterop/getters.nim @@ -1,4 +1,5 @@ import dynlib, macros, os, sequtils, sets, strformat, strutils, tables, times, options +import algorithm import regex @@ -637,12 +638,13 @@ proc getNameKind*(name: string): tuple[name: string, kind: Kind, recursive: bool if result.kind != exactlyOne: result.name = result.name[0 .. ^2] -proc getCommentVal*(gState: State, commentNode: Option[TSNode]): string = - if commentNode.isSome(): - result = "::\n " & gState.getNodeVal(commentNode.get()).replace(re" *(/\*\*|\*\*/|\*/|\*)", "").replace("\n", "\n ").strip() +proc getCommentsStr*(gState: State, commentNodes: seq[TSNode]): string = + if commentNodes.len > 0: + result = "::" + for commentNode in commentNodes: + result &= "\n " & gState.getNodeVal(commentNode).replace(re" *(//|/\*\*|\*\*/|/\*|\*/|\*)", "").replace("\n", "\n ").strip() template findComment(procName: untyped): untyped = - result = none(TSNode) var sibling = node.`procName`() var i = 0 while not sibling.isNil and i < maxSearch: @@ -651,11 +653,33 @@ template findComment(procName: untyped): untyped = sibling = sibling.`procName`() i += 1 -proc getPrevCommentNode*(node: TSNode, maxSearch=1): Option[TSNode] = - findComment(tsNodePrevNamedSibling) +proc getPrevCommentNodes*(node: TSNode, maxSearch=1): seq[TSNode] = + ## Here we want to go until the node we get is not a comment + ## for cases with multiple ``//`` comments instead of one ``/* */`` + ## section + var sibling = node.tsNodePrevNamedSibling() + var i = 0 + while not sibling.isNil and i < maxSearch: + while not sibling.isNil and sibling.getName() == "comment": + result.add(sibling) + sibling = sibling.tsNodePrevNamedSibling() + if sibling.isNil: + result.reverse + return + sibling = sibling.tsNodePrevNamedSibling() + i += 1 -proc getNextCommentNode*(node: TSNode, maxSearch=1): Option[TSNode] = - findComment(tsNodeNextNamedSibling) + result.reverse + +proc getNextCommentNodes*(node: TSNode, maxSearch=1): seq[TSNode] = + ## We only want to search for the next comment node (ie: inline) + var sibling = node.tsNodeNextNamedSibling() + var i = 0 + while not sibling.isNil and i < maxSearch: + if sibling.getName() == "comment": + return @[sibling] + sibling = sibling.tsNodeNextNamedSibling() + i += 1 proc getTSNodeNamedChildNames*(node: TSNode): seq[string] = if node.tsNodeNamedChildCount() != 0: From 8ecac1f09c516c29cc2510886fe540772a86c067 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 25 Apr 2020 20:47:28 -0600 Subject: [PATCH 05/19] Remove unnecessary proc --- nimterop/getters.nim | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/nimterop/getters.nim b/nimterop/getters.nim index 1240130..281e3a4 100644 --- a/nimterop/getters.nim +++ b/nimterop/getters.nim @@ -1,4 +1,4 @@ -import dynlib, macros, os, sequtils, sets, strformat, strutils, tables, times, options +import dynlib, macros, os, sequtils, sets, strformat, strutils, tables, times import algorithm import regex @@ -644,15 +644,6 @@ proc getCommentsStr*(gState: State, commentNodes: seq[TSNode]): string = for commentNode in commentNodes: result &= "\n " & gState.getNodeVal(commentNode).replace(re" *(//|/\*\*|\*\*/|/\*|\*/|\*)", "").replace("\n", "\n ").strip() -template findComment(procName: untyped): untyped = - var sibling = node.`procName`() - var i = 0 - while not sibling.isNil and i < maxSearch: - if sibling.getName() == "comment": - return some(sibling) - sibling = sibling.`procName`() - i += 1 - proc getPrevCommentNodes*(node: TSNode, maxSearch=1): seq[TSNode] = ## Here we want to go until the node we get is not a comment ## for cases with multiple ``//`` comments instead of one ``/* */`` From c78dfd087c72847e3c2baff229b5f4dcc39ac917 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 26 Apr 2020 09:28:58 -0600 Subject: [PATCH 06/19] Add comments explaining comment node procs --- nimterop/getters.nim | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/nimterop/getters.nim b/nimterop/getters.nim index 281e3a4..89fde10 100644 --- a/nimterop/getters.nim +++ b/nimterop/getters.nim @@ -639,6 +639,8 @@ proc getNameKind*(name: string): tuple[name: string, kind: Kind, recursive: bool result.name = result.name[0 .. ^2] proc getCommentsStr*(gState: State, commentNodes: seq[TSNode]): string = + ## Generate a comment from a set of comment nodes. Comment is guaranteed + ## to be able to be rendered using nim doc if commentNodes.len > 0: result = "::" for commentNode in commentNodes: @@ -650,25 +652,37 @@ proc getPrevCommentNodes*(node: TSNode, maxSearch=1): seq[TSNode] = ## section var sibling = node.tsNodePrevNamedSibling() var i = 0 + + # Search for the starting comment up to maxSearch nodes away while not sibling.isNil and i < maxSearch: + # Once a comment is found, find all of the comments right next to + # it so that we can get multiple // style comments while not sibling.isNil and sibling.getName() == "comment": result.add(sibling) sibling = sibling.tsNodePrevNamedSibling() + if sibling.isNil: - result.reverse - return + break + sibling = sibling.tsNodePrevNamedSibling() i += 1 + # reverse the comments because we got them in reverse order result.reverse proc getNextCommentNodes*(node: TSNode, maxSearch=1): seq[TSNode] = - ## We only want to search for the next comment node (ie: inline) + ## Searches the next nodes up to maxSearch nodes away for a comment + + # We only want to search for the next comment node (ie: inline) + # but we want to keep the same interface as getPrevCommentNodes, + # so we keep a returned seq but only store one element var sibling = node.tsNodeNextNamedSibling() var i = 0 + # Search for the comment up to maxSearch nodes away while not sibling.isNil and i < maxSearch: if sibling.getName() == "comment": - return @[sibling] + result.add sibling + break sibling = sibling.tsNodeNextNamedSibling() i += 1 From df3e73e9654295f95e6b4f7a4431bd47f24f7649 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 26 Apr 2020 09:56:18 -0600 Subject: [PATCH 07/19] Use gState.nocomments --- nimterop/ast2.nim | 16 ++++++++-------- nimterop/getters.nim | 12 +++++++++--- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/nimterop/ast2.nim b/nimterop/ast2.nim index 2e601db..0fd5a9d 100644 --- a/nimterop/ast2.nim +++ b/nimterop/ast2.nim @@ -691,7 +691,7 @@ proc newRecListTree(gState: State, name: string, node: TSNode): PNode = let fdecl = node[i].anyChildInTree("field_declaration_list") edecl = node[i].anyChildInTree("enumerator_list") - commentNodes = node[i].getNextCommentNodes() + commentNodes = gState.getNextCommentNodes(node[i]) # `tname` is name of nested struct / union / enum just # added, passed on as type name for field in `newIdentDefs()` @@ -727,7 +727,7 @@ proc addTypeObject(gState: State, node: TSNode, typeDef: PNode = nil, fname = "" # If `fname` is set, use it as the name when creating new PNode # If `istype` is set, this is a typedef, else struct/union decho("addTypeObject()") - let commentNodes = node.tsNodeParent().getPrevCommentNodes() + let commentNodes = gState.getPrevCommentNodes(node.tsNodeParent()) let # Object has fields or not @@ -853,7 +853,7 @@ proc addTypeObject(gState: State, node: TSNode, typeDef: PNode = nil, fname = "" # Current node has fields let origname = gState.getNodeVal(node.getAtom()) - commentNodes = node.getNextCommentNodes() + commentNodes = gState.getNextCommentNodes(node) # Fix issue #185 name = @@ -898,7 +898,7 @@ proc addTypeTyped(gState: State, node: TSNode, ftname = "", offset = 0) = decho("addTypeTyped()") let start = getStartAtom(node) - commentNodes = node.getPrevCommentNodes() + commentNodes = gState.getPrevCommentNodes(node) for i in start+1+offset ..< node.len: # Add a type of a specific type let @@ -1403,7 +1403,7 @@ proc addEnum(gState: State, node: TSNode) = # nkIdent(name) <- set the comment here # ) # ) - defineNode[0][1].comment = gState.getCommentsStr(node.getPrevCommentNodes()) + defineNode[0][1].comment = gState.getCommentsStr(gState.getPrevCommentNodes(node)) gState.enumSection.add defineNode # Create const for fields @@ -1420,7 +1420,7 @@ proc addEnum(gState: State, node: TSNode) = let atom = en.getAtom() - commentNodes = en.getNextCommentNodes() + commentNodes = gState.getNextCommentNodes(en) fname = gState.getIdentifier(gState.getNodeVal(atom), nskEnumField) if fname.nBl and gState.addNewIdentifer(fname): @@ -1638,7 +1638,7 @@ proc addDecl(gState: State, node: TSNode) = let start = getStartAtom(node) - commentNodes = node.getPrevCommentNodes() + commentNodes = gState.getPrevCommentNodes(node) for i in start+1 ..< node.len: if not node[i].firstChildInTree("function_declarator").isNil: @@ -1663,7 +1663,7 @@ proc addDef(gState: State, node: TSNode) = let start = getStartAtom(node) - commentNodes = node.getPrevCommentNodes() + commentNodes = gState.getPrevCommentNodes(node) if node[start+1].getName() == "function_declarator": if gState.isIncludeHeader(): diff --git a/nimterop/getters.nim b/nimterop/getters.nim index 89fde10..148c209 100644 --- a/nimterop/getters.nim +++ b/nimterop/getters.nim @@ -644,12 +644,16 @@ proc getCommentsStr*(gState: State, commentNodes: seq[TSNode]): string = if commentNodes.len > 0: result = "::" for commentNode in commentNodes: - result &= "\n " & gState.getNodeVal(commentNode).replace(re" *(//|/\*\*|\*\*/|/\*|\*/|\*)", "").replace("\n", "\n ").strip() + result &= "\n " & gState.getNodeVal(commentNode). + replace(re" *(//|/\*\*|\*\*/|/\*|\*/|\*)", "").replace("\n", "\n ").strip() -proc getPrevCommentNodes*(node: TSNode, maxSearch=1): seq[TSNode] = +proc getPrevCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] = ## Here we want to go until the node we get is not a comment ## for cases with multiple ``//`` comments instead of one ``/* */`` ## section + if gState.nocomments: + return + var sibling = node.tsNodePrevNamedSibling() var i = 0 @@ -670,9 +674,11 @@ proc getPrevCommentNodes*(node: TSNode, maxSearch=1): seq[TSNode] = # reverse the comments because we got them in reverse order result.reverse -proc getNextCommentNodes*(node: TSNode, maxSearch=1): seq[TSNode] = +proc getNextCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] = ## Searches the next nodes up to maxSearch nodes away for a comment + if gState.nocomments: + return # We only want to search for the next comment node (ie: inline) # but we want to keep the same interface as getPrevCommentNodes, # so we keep a returned seq but only store one element From 522178913e44b4942beecbaac6217433e11dd80b Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 26 Apr 2020 10:47:32 -0600 Subject: [PATCH 08/19] Remove unused proc --- nimterop/getters.nim | 3 --- 1 file changed, 3 deletions(-) diff --git a/nimterop/getters.nim b/nimterop/getters.nim index 148c209..28005e3 100644 --- a/nimterop/getters.nim +++ b/nimterop/getters.nim @@ -457,9 +457,6 @@ proc printLisp*(gState: State, root: TSNode): string = proc getCommented*(str: string): string = "\n# " & str.strip().replace("\n", "\n# ") -proc getDocStrCommented*(str: string): string = - "\n## " & str.strip().replace("\n", "\n## ") - proc printTree*(gState: State, pnode: PNode, offset = ""): string = if not pnode.isNil and gState.debug and pnode.kind != nkNone: result &= "\n# " & offset & $pnode.kind & "(" From c083b443e454188bd7505f25a99d6312a6e41119 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 26 Apr 2020 11:07:30 -0600 Subject: [PATCH 09/19] Add comment gen for array type --- nimterop/ast2.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nimterop/ast2.nim b/nimterop/ast2.nim index 0fd5a9d..ab7bb72 100644 --- a/nimterop/ast2.nim +++ b/nimterop/ast2.nim @@ -1017,6 +1017,7 @@ proc addTypeArray(gState: State, node: TSNode) = # node[start] = identifier = type name (tname, _, info) = gState.getNameInfo(node[start].getAtom(), nskType, parent = "addTypeArray") tident = gState.getIdent(tname, info, exported = false) + commentNodes = gState.getPrevCommentNodes(node) # Could have multiple types, comma separated for i in start+1 ..< node.len: @@ -1050,6 +1051,7 @@ proc addTypeArray(gState: State, node: TSNode) = # ) # ) + typeDef.comment = gState.getCommentsStr(commentNodes) # nkTypeSection.add gState.typeSection.add typeDef From 418e5db825f22c606a1444084bb239b37a703007 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Tue, 28 Apr 2020 22:52:24 -0600 Subject: [PATCH 10/19] Add more robust comment extraction --- nimterop/ast2.nim | 18 +++--- nimterop/getters.nim | 134 ++++++++++++++++++++++++++++++------------- 2 files changed, 102 insertions(+), 50 deletions(-) diff --git a/nimterop/ast2.nim b/nimterop/ast2.nim index ab7bb72..b51b252 100644 --- a/nimterop/ast2.nim +++ b/nimterop/ast2.nim @@ -691,7 +691,7 @@ proc newRecListTree(gState: State, name: string, node: TSNode): PNode = let fdecl = node[i].anyChildInTree("field_declaration_list") edecl = node[i].anyChildInTree("enumerator_list") - commentNodes = gState.getNextCommentNodes(node[i]) + commentNodes = gState.getCommentNodes(node[i]) # `tname` is name of nested struct / union / enum just # added, passed on as type name for field in `newIdentDefs()` @@ -727,7 +727,7 @@ proc addTypeObject(gState: State, node: TSNode, typeDef: PNode = nil, fname = "" # If `fname` is set, use it as the name when creating new PNode # If `istype` is set, this is a typedef, else struct/union decho("addTypeObject()") - let commentNodes = gState.getPrevCommentNodes(node.tsNodeParent()) + let commentNodes = gState.getCommentNodes(node.tsNodeParent()) let # Object has fields or not @@ -853,7 +853,7 @@ proc addTypeObject(gState: State, node: TSNode, typeDef: PNode = nil, fname = "" # Current node has fields let origname = gState.getNodeVal(node.getAtom()) - commentNodes = gState.getNextCommentNodes(node) + commentNodes = gState.getCommentNodes(node) # Fix issue #185 name = @@ -898,7 +898,7 @@ proc addTypeTyped(gState: State, node: TSNode, ftname = "", offset = 0) = decho("addTypeTyped()") let start = getStartAtom(node) - commentNodes = gState.getPrevCommentNodes(node) + commentNodes = gState.getCommentNodes(node) for i in start+1+offset ..< node.len: # Add a type of a specific type let @@ -1017,7 +1017,7 @@ proc addTypeArray(gState: State, node: TSNode) = # node[start] = identifier = type name (tname, _, info) = gState.getNameInfo(node[start].getAtom(), nskType, parent = "addTypeArray") tident = gState.getIdent(tname, info, exported = false) - commentNodes = gState.getPrevCommentNodes(node) + commentNodes = gState.getCommentNodes(node) # Could have multiple types, comma separated for i in start+1 ..< node.len: @@ -1405,7 +1405,7 @@ proc addEnum(gState: State, node: TSNode) = # nkIdent(name) <- set the comment here # ) # ) - defineNode[0][1].comment = gState.getCommentsStr(gState.getPrevCommentNodes(node)) + defineNode[0][1].comment = gState.getCommentsStr(gState.getCommentNodes(node)) gState.enumSection.add defineNode # Create const for fields @@ -1422,7 +1422,7 @@ proc addEnum(gState: State, node: TSNode) = let atom = en.getAtom() - commentNodes = gState.getNextCommentNodes(en) + commentNodes = gState.getCommentNodes(en) fname = gState.getIdentifier(gState.getNodeVal(atom), nskEnumField) if fname.nBl and gState.addNewIdentifer(fname): @@ -1640,7 +1640,7 @@ proc addDecl(gState: State, node: TSNode) = let start = getStartAtom(node) - commentNodes = gState.getPrevCommentNodes(node) + commentNodes = gState.getCommentNodes(node) for i in start+1 ..< node.len: if not node[i].firstChildInTree("function_declarator").isNil: @@ -1665,7 +1665,7 @@ proc addDef(gState: State, node: TSNode) = let start = getStartAtom(node) - commentNodes = gState.getPrevCommentNodes(node) + commentNodes = gState.getCommentNodes(node) if node[start+1].getName() == "function_declarator": if gState.isIncludeHeader(): diff --git a/nimterop/getters.nim b/nimterop/getters.nim index 28005e3..05d9448 100644 --- a/nimterop/getters.nim +++ b/nimterop/getters.nim @@ -1,5 +1,4 @@ import dynlib, macros, os, sequtils, sets, strformat, strutils, tables, times -import algorithm import regex @@ -572,30 +571,30 @@ proc getPreprocessor*(gState: State, fullpath: string): string = # Include content only from file for line in execAction(cmd).output.splitLines(): - if line.strip() != "": - if line.len > 1 and line[0 .. 1] == "# ": - start = false + # We want to keep blank lines here for comment processing + if line.len > 1 and line[0 .. 1] == "# ": + start = false + let + saniLine = line.sanitizePath(noQuote = true) + if sfile in saniLine: + start = true + elif not ("\\" in line) and not ("/" in line) and extractFilename(sfile) in line: + start = true + elif gState.recurse: let - saniLine = line.sanitizePath(noQuote = true) - if sfile in saniLine: + pDir = sfile.expandFilename().parentDir().sanitizePath(noQuote = true) + if pDir.Bl or pDir in saniLine: start = true - elif not ("\\" in line) and not ("/" in line) and extractFilename(sfile) in line: - start = true - elif gState.recurse: - let - pDir = sfile.expandFilename().parentDir().sanitizePath(noQuote = true) - if pDir.Bl or pDir in saniLine: - start = true - else: - for inc in gState.includeDirs: - if inc.absolutePath().sanitizePath(noQuote = true) in saniLine: - start = true - break - else: - if start: - if "#undef" in line: - continue - rdata.add line + else: + for inc in gState.includeDirs: + if inc.absolutePath().sanitizePath(noQuote = true) in saniLine: + start = true + break + else: + if start: + if "#undef" in line: + continue + rdata.add line return rdata.join("\n") converter toString*(kind: Kind): string = @@ -644,32 +643,85 @@ proc getCommentsStr*(gState: State, commentNodes: seq[TSNode]): string = result &= "\n " & gState.getNodeVal(commentNode). replace(re" *(//|/\*\*|\*\*/|/\*|\*/|\*)", "").replace("\n", "\n ").strip() -proc getPrevCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] = - ## Here we want to go until the node we get is not a comment - ## for cases with multiple ``//`` comments instead of one ``/* */`` - ## section +proc getCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] = + ## Get a set of comment nodes in order of priority. Will search up to ``maxSearch`` + ## nodes before and after the current node + ## + ## Priority is (closest line number) > comment before > comment after. + ## This priority might need to be changed based on the project, but + ## for now it is good enough + + # Skip this if we don't want comments if gState.nocomments: return - var sibling = node.tsNodePrevNamedSibling() - var i = 0 + let (line, _) = gState.getLineCol(node) - # Search for the starting comment up to maxSearch nodes away - while not sibling.isNil and i < maxSearch: - # Once a comment is found, find all of the comments right next to - # it so that we can get multiple // style comments - while not sibling.isNil and sibling.getName() == "comment": - result.add(sibling) - sibling = sibling.tsNodePrevNamedSibling() + # Keep track of both directions from a node + var + prevSibling = node.tsNodePrevNamedSibling() + nextSibling = node.tsNodeNextNamedSibling() + nilNode: TSNode - if sibling.isNil: + var + i = 0 + prevSiblingDistance, nextSiblingDistance: int + lowestDistance: int + commentsFound = false + + while not commentsFound and i < maxSearch: + + # Distance from the current node will tell us approximately if the + # comment belongs to the node. The closer it is in terms of line + # numbers, the more we can be sure it's the comment we want + if not prevSibling.isNil: + prevSiblingDistance = abs(gState.getLineCol(prevSibling)[0] - line) + if not nextSibling.isNil: + nextSiblingDistance = abs(gState.getLineCol(nextSibling)[0] - line) + + lowestDistance = min(prevSiblingDistance, nextSiblingDistance) + + if prevSiblingDistance > maxSearch: + # If the line is out of range, skip searching + prevSibling = nilNode # Can't do `= nil` + + if nextSiblingDistance > maxSearch: + # If the line is out of range, skip searching + prevSibling = nilNode + + while ( + not prevSibling.isNil and + prevSibling.getName() == "comment" and + prevSiblingDistance == lowestDistance + ): + # Put the previous nodes in reverse order so the comments + # make logical sense + result.insert(prevSibling, 0) + prevSibling = prevSibling.tsNodePrevNamedSibling() + commentsFound = true + + if commentsFound: break - sibling = sibling.tsNodePrevNamedSibling() - i += 1 + while ( + not nextSibling.isNil and + nextSibling.getName() == "comment" and + nextSiblingDistance == lowestDistance + ): + result.add(nextSibling) + nextSibling = nextSibling.tsNodeNextNamedSibling() + commentsFound = true - # reverse the comments because we got them in reverse order - result.reverse + if commentsFound: + break + + # Go to next sibling pair + if not prevSibling.isNil: + prevSibling = prevSibling.tsNodePrevNamedSibling() + if not nextSibling.isNil: + nextSibling = nextSibling.tsNodeNextNamedSibling() + + i += 1 proc getNextCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] = ## Searches the next nodes up to maxSearch nodes away for a comment From 63367a16a5adea6f3c5b9c59d76b73c8bb0ddbdd Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Wed, 29 Apr 2020 19:15:11 -0600 Subject: [PATCH 11/19] Add docgen runs in tests Fix paths import Skip docgen for rsa for now --- nimterop.nimble | 13 ++++++++++--- nimterop/cimport.nim | 2 +- nimterop/docs.nim | 4 ++-- tests/tnimterop_c.nim | 4 +++- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/nimterop.nimble b/nimterop.nimble index a6ece7c..8a47465 100644 --- a/nimterop.nimble +++ b/nimterop.nimble @@ -12,14 +12,21 @@ installDirs = @["nimterop"] requires "nim >= 0.20.2", "regex >= 0.14.1", "cligen >= 0.9.45" import nimterop/docs +import os proc execCmd(cmd: string) = exec "tests/timeit " & cmd -proc execTest(test: string, flags = "") = +proc execTest(test: string, flags = "", runDocs = true) = execCmd "nim c --hints:off -f " & flags & " -r " & test execCmd "nim cpp --hints:off " & flags & " -r " & test + if runDocs: + let docPath = "build/html_" & test.extractFileName.changeFileExt("") & "_docs" + rmDir docPath + mkDir docPath + buildDocs(@[test], docPath, compilerArgs = flags) + task buildToast, "build toast": execCmd("nim c --hints:off nimterop/toast.nim") @@ -54,8 +61,8 @@ task test, "Test": execTest "tests/tpcre.nim", "-d:FLAGS=\"-f:ast2\"" when defined(Linux): - execTest "tests/rsa.nim" - execTest "tests/rsa.nim", "-d:FLAGS=\"-H\"" + execTest "tests/rsa.nim", runDocs = false + execTest "tests/rsa.nim", "-d:FLAGS=\"-H\"", runDocs = false # Platform specific tests when defined(Windows): diff --git a/nimterop/cimport.nim b/nimterop/cimport.nim index e081633..99be1f5 100644 --- a/nimterop/cimport.nim +++ b/nimterop/cimport.nim @@ -434,7 +434,7 @@ proc cAddSearchDir*(dir: string) {.compileTime.} = ## Add directory `dir` to the search path used in calls to ## `cSearchPath() `_. runnableExamples: - import paths, os + import nimterop/paths, os static: cAddSearchDir testsIncludeDir() doAssert cSearchPath("test.h").existsFile diff --git a/nimterop/docs.nim b/nimterop/docs.nim index d808535..e0fef39 100644 --- a/nimterop/docs.nim +++ b/nimterop/docs.nim @@ -36,7 +36,7 @@ proc execAction(cmd: string): string = doAssert ret == 0, "Command failed: " & $ret & "\ncmd: " & ccmd & "\nresult:\n" & result proc buildDocs*(files: openArray[string], path: string, baseDir = getProjectPath() & $DirSep, - defines: openArray[string] = @[]) = + defines: openArray[string] = @[], compilerArgs = "") = ## Generate docs for all specified nim `files` to the specified `path` ## ## `baseDir` is the project path by default and `files` and `path` are relative @@ -70,7 +70,7 @@ proc buildDocs*(files: openArray[string], path: string, baseDir = getProjectPath defStr nim = getCurrentCompilerExe() for file in files: - echo execAction(&"{nim} doc {defStr} -o:{path} --project --index:on {baseDir & file}") + echo execAction(&"{nim} doc {defStr} {compilerArgs} -o:{path} --project --index:on {baseDir & file}") echo execAction(&"{nim} buildIndex -o:{path}/theindex.html {path}") when declared(getNimRootDir): diff --git a/tests/tnimterop_c.nim b/tests/tnimterop_c.nim index 20c1678..ef814ea 100644 --- a/tests/tnimterop_c.nim +++ b/tests/tnimterop_c.nim @@ -1,4 +1,6 @@ import std/unittest +import os +import macros import nimterop/cimport import nimterop/paths @@ -10,7 +12,7 @@ cDefine("FORCE") cIncludeDir testsIncludeDir() cCompile cSearchPath("test.c") -cPluginPath("tests/tnimterop_c_plugin.nim") +cPluginPath(getProjectPath() / "tnimterop_c_plugin.nim") cOverride: type From 2faebddcaac0f17f02cf4026f187c087149aee22 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 30 Apr 2020 08:43:40 -0600 Subject: [PATCH 12/19] Change install to develop for appveyor --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 5275844..d67b01d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -83,7 +83,7 @@ for: - /home/appveyor/binaries build_script: - - nimble --verbose install -y + - nimble --verbose develop -y test_script: - nimble --verbose test From 2bd8b297a904e8b88b79b3986066686223a9cfdf Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 30 Apr 2020 11:55:07 -0600 Subject: [PATCH 13/19] Run docs on rsa.nim --- nimterop.nimble | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nimterop.nimble b/nimterop.nimble index 8a47465..38a2d5d 100644 --- a/nimterop.nimble +++ b/nimterop.nimble @@ -61,8 +61,8 @@ task test, "Test": execTest "tests/tpcre.nim", "-d:FLAGS=\"-f:ast2\"" when defined(Linux): - execTest "tests/rsa.nim", runDocs = false - execTest "tests/rsa.nim", "-d:FLAGS=\"-H\"", runDocs = false + execTest "tests/rsa.nim" + execTest "tests/rsa.nim", "-d:FLAGS=\"-H\"" # Platform specific tests when defined(Windows): From e24ced7826bef387337719ab2d8a5ef1d5d432bd Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 30 Apr 2020 12:45:00 -0600 Subject: [PATCH 14/19] Remove unneeded proc, add more comments --- nimterop/getters.nim | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/nimterop/getters.nim b/nimterop/getters.nim index 05d9448..cae2d08 100644 --- a/nimterop/getters.nim +++ b/nimterop/getters.nim @@ -670,7 +670,6 @@ proc getCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] = commentsFound = false while not commentsFound and i < maxSearch: - # Distance from the current node will tell us approximately if the # comment belongs to the node. The closer it is in terms of line # numbers, the more we can be sure it's the comment we want @@ -689,6 +688,9 @@ proc getCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] = # If the line is out of range, skip searching prevSibling = nilNode + # Search above the current line for comments. When one is found + # keep going to retrieve successive comments for cases with multiple + # `//` style comments while ( not prevSibling.isNil and prevSibling.getName() == "comment" and @@ -700,9 +702,13 @@ proc getCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] = prevSibling = prevSibling.tsNodePrevNamedSibling() commentsFound = true + # If we've already found comments above the current line, quit if commentsFound: break + # Search below or at the current line for comments. When one is found + # keep going to retrieve successive comments for cases with multiple + # `//` style comments while ( not nextSibling.isNil and nextSibling.getName() == "comment" and @@ -723,24 +729,6 @@ proc getCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] = i += 1 -proc getNextCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] = - ## Searches the next nodes up to maxSearch nodes away for a comment - - if gState.nocomments: - return - # We only want to search for the next comment node (ie: inline) - # but we want to keep the same interface as getPrevCommentNodes, - # so we keep a returned seq but only store one element - var sibling = node.tsNodeNextNamedSibling() - var i = 0 - # Search for the comment up to maxSearch nodes away - while not sibling.isNil and i < maxSearch: - if sibling.getName() == "comment": - result.add sibling - break - sibling = sibling.tsNodeNextNamedSibling() - i += 1 - proc getTSNodeNamedChildNames*(node: TSNode): seq[string] = if node.tsNodeNamedChildCount() != 0: for i in 0 .. node.tsNodeNamedChildCount()-1: From 14e42a0a226e7d04eec3310ab0228f773d0974e1 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 30 Apr 2020 17:23:10 -0600 Subject: [PATCH 15/19] Add escaping for rst --- nimterop/getters.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nimterop/getters.nim b/nimterop/getters.nim index cae2d08..099faf4 100644 --- a/nimterop/getters.nim +++ b/nimterop/getters.nim @@ -638,9 +638,10 @@ proc getCommentsStr*(gState: State, commentNodes: seq[TSNode]): string = ## Generate a comment from a set of comment nodes. Comment is guaranteed ## to be able to be rendered using nim doc if commentNodes.len > 0: + const escapeRstReg = re"""(["!#$%&'()*+,-./:;<=>?@[\]^_`{|}~])""" result = "::" for commentNode in commentNodes: - result &= "\n " & gState.getNodeVal(commentNode). + result &= "\n " & gState.getNodeVal(commentNode).replace(escapeRstReg, r"\$1"). replace(re" *(//|/\*\*|\*\*/|/\*|\*/|\*)", "").replace("\n", "\n ").strip() proc getCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] = From 83a63881f1588f34f86eaecd41a505b25d7e90eb Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 30 Apr 2020 19:25:13 -0600 Subject: [PATCH 16/19] Fix bug with comment generation --- nimterop/getters.nim | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/nimterop/getters.nim b/nimterop/getters.nim index 099faf4..10c3b5e 100644 --- a/nimterop/getters.nim +++ b/nimterop/getters.nim @@ -387,6 +387,16 @@ proc getLineCol*(code: var string, node: TSNode): tuple[line, col: int] = proc getLineCol*(gState: State, node: TSNode): tuple[line, col: int] = getLineCol(gState.code, node) +proc getEndLineCol*(code: var string, node: TSNode): tuple[line, col: int] = + # Get line number and column info for node + let + point = node.tsNodeEndPoint() + result.line = point.row.int + 1 + result.col = point.column.int + 1 + +proc getEndLineCol*(gState: State, node: TSNode): tuple[line, col: int] = + getEndLineCol(gState.code, node) + proc getTSNodeNamedChildCountSansComments*(node: TSNode): int = for i in 0 ..< node.len: if node.getName() != "comment": @@ -638,11 +648,12 @@ proc getCommentsStr*(gState: State, commentNodes: seq[TSNode]): string = ## Generate a comment from a set of comment nodes. Comment is guaranteed ## to be able to be rendered using nim doc if commentNodes.len > 0: - const escapeRstReg = re"""(["!#$%&'()*+,-./:;<=>?@[\]^_`{|}~])""" result = "::" for commentNode in commentNodes: - result &= "\n " & gState.getNodeVal(commentNode).replace(escapeRstReg, r"\$1"). - replace(re" *(//|/\*\*|\*\*/|/\*|\*/|\*)", "").replace("\n", "\n ").strip() + result &= "\n " & gState.getNodeVal(commentNode).strip() + + result = result.replace(re" *(//|/\*\*|\*\*/|/\*|\*/|\*)", "") + result = result.multiReplace([("\n", "\n "), ("`", "")]).strip() proc getCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] = ## Get a set of comment nodes in order of priority. Will search up to ``maxSearch`` @@ -666,7 +677,7 @@ proc getCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] = var i = 0 - prevSiblingDistance, nextSiblingDistance: int + prevSiblingDistance, nextSiblingDistance: int = int.high lowestDistance: int commentsFound = false @@ -675,9 +686,15 @@ proc getCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] = # comment belongs to the node. The closer it is in terms of line # numbers, the more we can be sure it's the comment we want if not prevSibling.isNil: - prevSiblingDistance = abs(gState.getLineCol(prevSibling)[0] - line) + if prevSibling.getName() == "comment": + prevSiblingDistance = abs(gState.getEndLineCol(prevSibling)[0] - line) + else: + prevSiblingDistance = int.high if not nextSibling.isNil: - nextSiblingDistance = abs(gState.getLineCol(nextSibling)[0] - line) + if nextSibling.getName() == "comment": + nextSiblingDistance = abs(gState.getLineCol(nextSibling)[0] - line) + else: + nextSiblingDistance = int.high lowestDistance = min(prevSiblingDistance, nextSiblingDistance) @@ -687,7 +704,7 @@ proc getCommentNodes*(gState: State, node: TSNode, maxSearch=1): seq[TSNode] = if nextSiblingDistance > maxSearch: # If the line is out of range, skip searching - prevSibling = nilNode + nextSibling = nilNode # Search above the current line for comments. When one is found # keep going to retrieve successive comments for cases with multiple From 42fbaf4c4f41827be188927c7cea9626fd6621aa Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 3 May 2020 18:40:49 -0600 Subject: [PATCH 17/19] compilerArgs -> nimArgs, fix multi decl comments --- nimterop.nimble | 2 +- nimterop/ast2.nim | 17 ++++++++++++++++- nimterop/docs.nim | 4 ++-- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/nimterop.nimble b/nimterop.nimble index 38a2d5d..702c714 100644 --- a/nimterop.nimble +++ b/nimterop.nimble @@ -25,7 +25,7 @@ proc execTest(test: string, flags = "", runDocs = true) = let docPath = "build/html_" & test.extractFileName.changeFileExt("") & "_docs" rmDir docPath mkDir docPath - buildDocs(@[test], docPath, compilerArgs = flags) + buildDocs(@[test], docPath, nimArgs = flags) task buildToast, "build toast": execCmd("nim c --hints:off nimterop/toast.nim") diff --git a/nimterop/ast2.nim b/nimterop/ast2.nim index b51b252..759419c 100644 --- a/nimterop/ast2.nim +++ b/nimterop/ast2.nim @@ -1640,16 +1640,31 @@ proc addDecl(gState: State, node: TSNode) = let start = getStartAtom(node) - commentNodes = gState.getCommentNodes(node) + + var + firstDecl = true + commentNodes: seq[TSNode] for i in start+1 ..< node.len: if not node[i].firstChildInTree("function_declarator").isNil: # Proc declaration - var or actual proc if node[i].getAtom().getPxName(1) == "pointer_declarator": # proc var + if firstDecl: + # If + commentNodes = gState.getCommentNodes(node) + firstDecl = false + else: + commentNodes = gState.getCommentNodes(node[i]) gState.addProcVar(node[i], node[start], commentNodes) else: # proc + if firstDecl: + # If + commentNodes = gState.getCommentNodes(node) + firstDecl = false + else: + commentNodes = gState.getCommentNodes(node[i]) gState.addProc(node[i], node[start], commentNodes) else: # Regular var diff --git a/nimterop/docs.nim b/nimterop/docs.nim index e0fef39..798df52 100644 --- a/nimterop/docs.nim +++ b/nimterop/docs.nim @@ -36,7 +36,7 @@ proc execAction(cmd: string): string = doAssert ret == 0, "Command failed: " & $ret & "\ncmd: " & ccmd & "\nresult:\n" & result proc buildDocs*(files: openArray[string], path: string, baseDir = getProjectPath() & $DirSep, - defines: openArray[string] = @[], compilerArgs = "") = + defines: openArray[string] = @[], nimArgs = "") = ## Generate docs for all specified nim `files` to the specified `path` ## ## `baseDir` is the project path by default and `files` and `path` are relative @@ -70,7 +70,7 @@ proc buildDocs*(files: openArray[string], path: string, baseDir = getProjectPath defStr nim = getCurrentCompilerExe() for file in files: - echo execAction(&"{nim} doc {defStr} {compilerArgs} -o:{path} --project --index:on {baseDir & file}") + echo execAction(&"{nim} doc {defStr} {nimArgs} -o:{path} --project --index:on {baseDir & file}") echo execAction(&"{nim} buildIndex -o:{path}/theindex.html {path}") when declared(getNimRootDir): From 68951327492693432580be54b9192d9733900a04 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 4 May 2020 08:07:36 -0600 Subject: [PATCH 18/19] Add nimArgs to buildDocs docstring --- nimterop/docs.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nimterop/docs.nim b/nimterop/docs.nim index 798df52..d5bd50c 100644 --- a/nimterop/docs.nim +++ b/nimterop/docs.nim @@ -45,6 +45,8 @@ proc buildDocs*(files: openArray[string], path: string, baseDir = getProjectPath ## `defines` is a list of `-d:xxx` define flags (the `xxx` part) that should be passed ## to `nim doc` so that `getHeader()` is invoked correctly. ## + ## `nimArgs` is a string representing extra arguments to send to the `nim doc` call. + ## ## Use the `--publish` flag with nimble to publish docs contained in ## `path` to Github in the `gh-pages` branch. This requires the ghp-import ## package for Python: `pip install ghp-import` From ae5a3f7e7a918b26e69190ad23b69eb7d27faf3c Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 4 May 2020 08:10:46 -0600 Subject: [PATCH 19/19] Fix missing comment in ast2 --- nimterop/ast2.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nimterop/ast2.nim b/nimterop/ast2.nim index 759419c..fd70b46 100644 --- a/nimterop/ast2.nim +++ b/nimterop/ast2.nim @@ -1651,7 +1651,8 @@ proc addDecl(gState: State, node: TSNode) = if node[i].getAtom().getPxName(1) == "pointer_declarator": # proc var if firstDecl: - # If + # If it's the first declaration, use the whole node + # to get the comment above/below commentNodes = gState.getCommentNodes(node) firstDecl = false else: @@ -1660,7 +1661,8 @@ proc addDecl(gState: State, node: TSNode) = else: # proc if firstDecl: - # If + # If it's the first declaration, use the whole node + # to get the comment above/below commentNodes = gState.getCommentNodes(node) firstDecl = false else: