Implemented basic macro expand functionality (#20579)

* Implemented level based macro expand functionality

- it can handle single macro call or expand whole function/proc/etc and it

- In addition, I have altered the parser to provide the endInfo for the node.
The usefulness of the `endInfo` is not limited to the `expandMacro`
functionality but also it is useful for `ideOutline` functionality and I have
altered the ideOutline functionality to use `endInfo`. Note `endInfo` most of
the time is lost during the AST transformation thus in `nimsuggest.nim` I am
using freshly parsed tree to get the location information.

* Make sure we stop expanding correctly

* Test CI

* Fix tv3_outline.nim
This commit is contained in:
Ivan Yonchovski
2023-01-27 08:11:30 +02:00
committed by GitHub
parent 4647c7b596
commit 7031ea65cd
13 changed files with 345 additions and 60 deletions

View File

@@ -796,6 +796,8 @@ type
ident*: PIdent ident*: PIdent
else: else:
sons*: TNodeSeq sons*: TNodeSeq
when defined(nimsuggest):
endInfo*: TLineInfo
TStrTable* = object # a table[PIdent] of PSym TStrTable* = object # a table[PIdent] of PSym
counter*: int counter*: int
@@ -892,6 +894,8 @@ type
typ*: PType typ*: PType
name*: PIdent name*: PIdent
info*: TLineInfo info*: TLineInfo
when defined(nimsuggest):
endInfo*: TLineInfo
owner*: PSym owner*: PSym
flags*: TSymFlags flags*: TSymFlags
ast*: PNode # syntax tree of proc, iterator, etc.: ast*: PNode # syntax tree of proc, iterator, etc.:
@@ -1690,6 +1694,8 @@ proc copyNode*(src: PNode): PNode =
of nkIdent: result.ident = src.ident of nkIdent: result.ident = src.ident
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
else: discard else: discard
when defined(nimsuggest):
result.endInfo = src.endInfo
template transitionNodeKindCommon(k: TNodeKind) = template transitionNodeKindCommon(k: TNodeKind) =
let obj {.inject.} = n[] let obj {.inject.} = n[]
@@ -1742,6 +1748,8 @@ template copyNodeImpl(dst, src, processSonsStmt) =
if src == nil: return if src == nil: return
dst = newNode(src.kind) dst = newNode(src.kind)
dst.info = src.info dst.info = src.info
when defined(nimsuggest):
result.endInfo = src.endInfo
dst.typ = src.typ dst.typ = src.typ
dst.flags = src.flags * PersistentNodeFlags dst.flags = src.flags * PersistentNodeFlags
dst.comment = src.comment dst.comment = src.comment

View File

@@ -127,6 +127,8 @@ type
cache*: IdentCache cache*: IdentCache
when defined(nimsuggest): when defined(nimsuggest):
previousToken: TLineInfo previousToken: TLineInfo
tokenEnd*: TLineInfo
previousTokenEnd*: TLineInfo
config*: ConfigRef config*: ConfigRef
proc getLineInfo*(L: Lexer, tok: Token): TLineInfo {.inline.} = proc getLineInfo*(L: Lexer, tok: Token): TLineInfo {.inline.} =
@@ -1224,6 +1226,10 @@ proc skip(L: var Lexer, tok: var Token) =
proc rawGetTok*(L: var Lexer, tok: var Token) = proc rawGetTok*(L: var Lexer, tok: var Token) =
template atTokenEnd() {.dirty.} = template atTokenEnd() {.dirty.} =
when defined(nimsuggest): when defined(nimsuggest):
L.previousTokenEnd.line = L.tokenEnd.line
L.previousTokenEnd.col = L.tokenEnd.col
L.tokenEnd.line = tok.line.uint16
L.tokenEnd.col = getColNumber(L, L.bufpos).int16
# we attach the cursor to the last *strong* token # we attach the cursor to the last *strong* token
if tok.tokType notin weakTokens: if tok.tokType notin weakTokens:
L.previousToken.line = tok.line.uint16 L.previousToken.line = tok.line.uint16

View File

@@ -195,7 +195,7 @@ type
IdeCmd* = enum IdeCmd* = enum
ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideChkFile, ideMod, ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideChkFile, ideMod,
ideHighlight, ideOutline, ideKnown, ideMsg, ideProject, ideGlobalSymbols, ideHighlight, ideOutline, ideKnown, ideMsg, ideProject, ideGlobalSymbols,
ideRecompile, ideChanged, ideType, ideDeclaration ideRecompile, ideChanged, ideType, ideDeclaration, ideExpand
Feature* = enum ## experimental features; DO NOT RENAME THESE! Feature* = enum ## experimental features; DO NOT RENAME THESE!
dotOperators, dotOperators,
@@ -278,6 +278,9 @@ type
scope*, localUsages*, globalUsages*: int # more usages is better scope*, localUsages*, globalUsages*: int # more usages is better
tokenLen*: int tokenLen*: int
version*: int version*: int
endLine*: uint16
endCol*: int
Suggestions* = seq[Suggest] Suggestions* = seq[Suggest]
ProfileInfo* = object ProfileInfo* = object
@@ -408,6 +411,11 @@ type
nimMainPrefix*: string nimMainPrefix*: string
vmProfileData*: ProfileData vmProfileData*: ProfileData
expandProgress*: bool
expandLevels*: int
expandNodeResult*: string
expandPosition*: TLineInfo
proc parseNimVersion*(a: string): NimVer = proc parseNimVersion*(a: string): NimVer =
# could be moved somewhere reusable # could be moved somewhere reusable
if a.len > 0: if a.len > 0:
@@ -996,6 +1004,9 @@ proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
result = optDynlibOverrideAll in conf.globalOptions or result = optDynlibOverrideAll in conf.globalOptions or
conf.dllOverrides.hasKey(lib.canonDynlibName) conf.dllOverrides.hasKey(lib.canonDynlibName)
proc expandDone*(conf: ConfigRef): bool =
result = conf.ideCmd == ideExpand and conf.expandLevels == 0 and conf.expandProgress
proc parseIdeCmd*(s: string): IdeCmd = proc parseIdeCmd*(s: string): IdeCmd =
case s: case s:
of "sug": ideSug of "sug": ideSug
@@ -1035,6 +1046,7 @@ proc `$`*(c: IdeCmd): string =
of ideProject: "project" of ideProject: "project"
of ideGlobalSymbols: "globalSymbols" of ideGlobalSymbols: "globalSymbols"
of ideDeclaration: "declaration" of ideDeclaration: "declaration"
of ideExpand: "expand"
of ideRecompile: "recompile" of ideRecompile: "recompile"
of ideChanged: "changed" of ideChanged: "changed"
of ideType: "type" of ideType: "type"

View File

@@ -354,6 +354,12 @@ proc colcom(p: var Parser, n: PNode) =
const tkBuiltInMagics = {tkType, tkStatic, tkAddr} const tkBuiltInMagics = {tkType, tkStatic, tkAddr}
template setEndInfo() =
when defined(nimsuggest):
result.endInfo = TLineInfo(fileIndex: p.lex.fileIdx,
line: p.lex.previousTokenEnd.line,
col: p.lex.previousTokenEnd.col)
proc parseSymbol(p: var Parser, mode = smNormal): PNode = proc parseSymbol(p: var Parser, mode = smNormal): PNode =
#| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
#| | IDENT | KEYW #| | IDENT | KEYW
@@ -406,6 +412,7 @@ proc parseSymbol(p: var Parser, mode = smNormal): PNode =
# if it is a keyword: # if it is a keyword:
#if not isKeyword(p.tok.tokType): getTok(p) #if not isKeyword(p.tok.tokType): getTok(p)
result = p.emptyNode result = p.emptyNode
setEndInfo()
proc equals(p: var Parser, a: PNode): PNode = proc equals(p: var Parser, a: PNode): PNode =
if p.tok.tokType == tkEquals: if p.tok.tokType == tkEquals:
@@ -577,6 +584,7 @@ proc parseCast(p: var Parser): PNode =
result.add(exprColonEqExpr(p)) result.add(exprColonEqExpr(p))
optPar(p) optPar(p)
eat(p, tkParRi) eat(p, tkParRi)
setEndInfo()
proc setBaseFlags(n: PNode, base: NumericalBase) = proc setBaseFlags(n: PNode, base: NumericalBase) =
case base case base
@@ -599,6 +607,7 @@ proc parseGStrLit(p: var Parser, a: PNode): PNode =
getTok(p) getTok(p)
else: else:
result = a result = a
setEndInfo()
proc complexOrSimpleStmt(p: var Parser): PNode proc complexOrSimpleStmt(p: var Parser): PNode
proc simpleExpr(p: var Parser, mode = pmNormal): PNode proc simpleExpr(p: var Parser, mode = pmNormal): PNode
@@ -703,6 +712,7 @@ proc parsePar(p: var Parser): PNode =
skipComment(p, a) skipComment(p, a)
optPar(p) optPar(p)
eat(p, tkParRi) eat(p, tkParRi)
setEndInfo()
proc identOrLiteral(p: var Parser, mode: PrimaryMode): PNode = proc identOrLiteral(p: var Parser, mode: PrimaryMode): PNode =
#| literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT #| literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
@@ -941,6 +951,7 @@ proc parseOperators(p: var Parser, headNode: PNode,
a.add(b) a.add(b)
result = a result = a
opPrec = getPrecedence(p.tok) opPrec = getPrecedence(p.tok)
setEndInfo()
proc simpleExprAux(p: var Parser, limit: int, mode: PrimaryMode): PNode = proc simpleExprAux(p: var Parser, limit: int, mode: PrimaryMode): PNode =
var mode = mode var mode = mode
@@ -990,6 +1001,7 @@ proc parsePragma(p: var Parser): PNode =
when defined(nimpretty): when defined(nimpretty):
dec p.em.doIndentMore dec p.em.doIndentMore
dec p.em.keepIndents dec p.em.keepIndents
setEndInfo()
proc identVis(p: var Parser; allowDot=false): PNode = proc identVis(p: var Parser; allowDot=false): PNode =
#| identVis = symbol OPR? # postfix position #| identVis = symbol OPR? # postfix position
@@ -1058,6 +1070,7 @@ proc parseIdentColonEquals(p: var Parser, flags: DeclaredIdentFlags): PNode =
result.add(parseExpr(p)) result.add(parseExpr(p))
else: else:
result.add(newNodeP(nkEmpty, p)) result.add(newNodeP(nkEmpty, p))
setEndInfo()
proc parseTuple(p: var Parser, indentAllowed = false): PNode = proc parseTuple(p: var Parser, indentAllowed = false): PNode =
#| tupleTypeBracket = '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']' #| tupleTypeBracket = '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']'
@@ -1102,6 +1115,7 @@ proc parseTuple(p: var Parser, indentAllowed = false): PNode =
parMessage(p, errGenerated, "the syntax for tuple types is 'tuple[...]', not 'tuple(...)'") parMessage(p, errGenerated, "the syntax for tuple types is 'tuple[...]', not 'tuple(...)'")
else: else:
result = newNodeP(nkTupleClassTy, p) result = newNodeP(nkTupleClassTy, p)
setEndInfo()
proc parseParamList(p: var Parser, retColon = true): PNode = proc parseParamList(p: var Parser, retColon = true): PNode =
#| paramList = '(' declColonEquals ^* (comma/semicolon) ')' #| paramList = '(' declColonEquals ^* (comma/semicolon) ')'
@@ -1150,6 +1164,7 @@ proc parseParamList(p: var Parser, retColon = true): PNode =
when defined(nimpretty): when defined(nimpretty):
dec p.em.doIndentMore dec p.em.doIndentMore
dec p.em.keepIndents dec p.em.keepIndents
setEndInfo()
proc optPragmas(p: var Parser): PNode = proc optPragmas(p: var Parser): PNode =
if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)): if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)):
@@ -1170,6 +1185,7 @@ proc parseDoBlock(p: var Parser; info: TLineInfo): PNode =
result = newProcNode(nkDo, info, result = newProcNode(nkDo, info,
body = result, params = params, name = p.emptyNode, pattern = p.emptyNode, body = result, params = params, name = p.emptyNode, pattern = p.emptyNode,
genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode) genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
setEndInfo()
proc parseProcExpr(p: var Parser; isExpr: bool; kind: TNodeKind): PNode = proc parseProcExpr(p: var Parser; isExpr: bool; kind: TNodeKind): PNode =
#| routineExpr = ('proc' | 'func' | 'iterator') paramListColon pragma? ('=' COMMENT? stmt)? #| routineExpr = ('proc' | 'func' | 'iterator') paramListColon pragma? ('=' COMMENT? stmt)?
@@ -1192,6 +1208,7 @@ proc parseProcExpr(p: var Parser; isExpr: bool; kind: TNodeKind): PNode =
if kind == nkFuncDef: if kind == nkFuncDef:
parMessage(p, "func keyword is not allowed in type descriptions, use proc with {.noSideEffect.} pragma instead") parMessage(p, "func keyword is not allowed in type descriptions, use proc with {.noSideEffect.} pragma instead")
result.add(pragmas) result.add(pragmas)
setEndInfo()
proc isExprStart(p: Parser): bool = proc isExprStart(p: Parser): bool =
case p.tok.tokType case p.tok.tokType
@@ -1211,6 +1228,7 @@ proc parseSymbolList(p: var Parser, result: PNode) =
if p.tok.tokType != tkComma: break if p.tok.tokType != tkComma: break
getTok(p) getTok(p)
optInd(p, s) optInd(p, s)
setEndInfo()
proc parseTypeDescKAux(p: var Parser, kind: TNodeKind, proc parseTypeDescKAux(p: var Parser, kind: TNodeKind,
mode: PrimaryMode): PNode = mode: PrimaryMode): PNode =
@@ -1239,6 +1257,7 @@ proc parseTypeDescKAux(p: var Parser, kind: TNodeKind,
parseSymbolList(p, list) parseSymbolList(p, list)
if mode == pmTypeDef and not isTypedef: if mode == pmTypeDef and not isTypedef:
result = parseOperators(p, result, -1, mode) result = parseOperators(p, result, -1, mode)
setEndInfo()
proc parseVarTuple(p: var Parser): PNode proc parseVarTuple(p: var Parser): PNode
@@ -1264,6 +1283,7 @@ proc parseFor(p: var Parser): PNode =
result.add(parseExpr(p)) result.add(parseExpr(p))
colcom(p, result) colcom(p, result)
result.add(parseStmt(p)) result.add(parseStmt(p))
setEndInfo()
template nimprettyDontTouch(body) = template nimprettyDontTouch(body) =
when defined(nimpretty): when defined(nimpretty):
@@ -1302,6 +1322,7 @@ proc parseExpr(p: var Parser): PNode =
nimprettyDontTouch: nimprettyDontTouch:
result = parseTry(p, isExpr=true) result = parseTry(p, isExpr=true)
else: result = simpleExpr(p) else: result = simpleExpr(p)
setEndInfo()
proc parseEnum(p: var Parser): PNode proc parseEnum(p: var Parser): PNode
proc parseObject(p: var Parser): PNode proc parseObject(p: var Parser): PNode
@@ -1411,6 +1432,7 @@ proc parseTypeDesc(p: var Parser, fullExpr = false): PNode =
else: else:
result = simpleExpr(p, pmTypeDesc) result = simpleExpr(p, pmTypeDesc)
result = binaryNot(p, result) result = binaryNot(p, result)
setEndInfo()
proc parseTypeDefValue(p: var Parser): PNode = proc parseTypeDefValue(p: var Parser): PNode =
#| typeDefValue = ((tupleDecl | enumDecl | objectDecl | conceptDecl | #| typeDefValue = ((tupleDecl | enumDecl | objectDecl | conceptDecl |
@@ -1441,6 +1463,7 @@ proc parseTypeDefValue(p: var Parser): PNode =
result.add(commandParam(p, isFirstParam, pmTypeDef)) result.add(commandParam(p, isFirstParam, pmTypeDef))
result = postExprBlocks(p, result) result = postExprBlocks(p, result)
result = binaryNot(p, result) result = binaryNot(p, result)
setEndInfo()
proc makeCall(n: PNode): PNode = proc makeCall(n: PNode): PNode =
## Creates a call if the given node isn't already a call. ## Creates a call if the given node isn't already a call.
@@ -1561,6 +1584,7 @@ proc parseExprStmt(p: var Parser): PNode =
else: else:
result = a result = a
result = postExprBlocks(p, result) result = postExprBlocks(p, result)
setEndInfo()
proc parseModuleName(p: var Parser, kind: TNodeKind): PNode = proc parseModuleName(p: var Parser, kind: TNodeKind): PNode =
result = parseExpr(p) result = parseExpr(p)
@@ -1572,6 +1596,7 @@ proc parseModuleName(p: var Parser, kind: TNodeKind): PNode =
getTok(p) getTok(p)
result.add(a) result.add(a)
result.add(parseExpr(p)) result.add(parseExpr(p))
setEndInfo()
proc parseImport(p: var Parser, kind: TNodeKind): PNode = proc parseImport(p: var Parser, kind: TNodeKind): PNode =
#| importStmt = 'import' optInd expr #| importStmt = 'import' optInd expr
@@ -1600,6 +1625,7 @@ proc parseImport(p: var Parser, kind: TNodeKind): PNode =
getTok(p) getTok(p)
optInd(p, a) optInd(p, a)
#expectNl(p) #expectNl(p)
setEndInfo()
proc parseIncludeStmt(p: var Parser): PNode = proc parseIncludeStmt(p: var Parser): PNode =
#| includeStmt = 'include' optInd expr ^+ comma #| includeStmt = 'include' optInd expr ^+ comma
@@ -1616,6 +1642,7 @@ proc parseIncludeStmt(p: var Parser): PNode =
getTok(p) getTok(p)
optInd(p, a) optInd(p, a)
#expectNl(p) #expectNl(p)
setEndInfo()
proc parseFromStmt(p: var Parser): PNode = proc parseFromStmt(p: var Parser): PNode =
#| fromStmt = 'from' expr 'import' optInd expr (comma expr)* #| fromStmt = 'from' expr 'import' optInd expr (comma expr)*
@@ -1636,6 +1663,7 @@ proc parseFromStmt(p: var Parser): PNode =
getTok(p) getTok(p)
optInd(p, a) optInd(p, a)
#expectNl(p) #expectNl(p)
setEndInfo()
proc parseReturnOrRaise(p: var Parser, kind: TNodeKind): PNode = proc parseReturnOrRaise(p: var Parser, kind: TNodeKind): PNode =
#| returnStmt = 'return' optInd expr? #| returnStmt = 'return' optInd expr?
@@ -1657,6 +1685,7 @@ proc parseReturnOrRaise(p: var Parser, kind: TNodeKind): PNode =
var e = parseExpr(p) var e = parseExpr(p)
e = postExprBlocks(p, e) e = postExprBlocks(p, e)
result.add(e) result.add(e)
setEndInfo()
proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode = proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode =
#| condStmt = expr colcom stmt COMMENT? #| condStmt = expr colcom stmt COMMENT?
@@ -1681,6 +1710,7 @@ proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode =
colcom(p, branch) colcom(p, branch)
branch.add(parseStmt(p)) branch.add(parseStmt(p))
result.add(branch) result.add(branch)
setEndInfo()
proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode = proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode =
#| condExpr = expr colcom expr optInd #| condExpr = expr colcom expr optInd
@@ -1705,6 +1735,7 @@ proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode =
colcom(p, branch) colcom(p, branch)
branch.add(parseStmt(p)) branch.add(parseStmt(p))
result.add(branch) result.add(branch)
setEndInfo()
proc parseWhile(p: var Parser): PNode = proc parseWhile(p: var Parser): PNode =
#| whileStmt = 'while' expr colcom stmt #| whileStmt = 'while' expr colcom stmt
@@ -1714,6 +1745,7 @@ proc parseWhile(p: var Parser): PNode =
result.add(parseExpr(p)) result.add(parseExpr(p))
colcom(p, result) colcom(p, result)
result.add(parseStmt(p)) result.add(parseStmt(p))
setEndInfo()
proc parseCase(p: var Parser): PNode = proc parseCase(p: var Parser): PNode =
#| ofBranch = 'of' exprList colcom stmt #| ofBranch = 'of' exprList colcom stmt
@@ -1761,6 +1793,7 @@ proc parseCase(p: var Parser): PNode =
if wasIndented: if wasIndented:
p.currInd = oldInd p.currInd = oldInd
setEndInfo()
proc parseTry(p: var Parser; isExpr: bool): PNode = proc parseTry(p: var Parser; isExpr: bool): PNode =
#| tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally') #| tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally')
@@ -1789,12 +1822,14 @@ proc parseTry(p: var Parser; isExpr: bool): PNode =
b.add(parseStmt(p)) b.add(parseStmt(p))
result.add(b) result.add(b)
if b == nil: parMessage(p, "expected 'except'") if b == nil: parMessage(p, "expected 'except'")
setEndInfo()
proc parseExceptBlock(p: var Parser, kind: TNodeKind): PNode = proc parseExceptBlock(p: var Parser, kind: TNodeKind): PNode =
result = newNodeP(kind, p) result = newNodeP(kind, p)
getTok(p) getTok(p)
colcom(p, result) colcom(p, result)
result.add(parseStmt(p)) result.add(parseStmt(p))
setEndInfo()
proc parseBlock(p: var Parser): PNode = proc parseBlock(p: var Parser): PNode =
#| blockStmt = 'block' symbol? colcom stmt #| blockStmt = 'block' symbol? colcom stmt
@@ -1805,6 +1840,7 @@ proc parseBlock(p: var Parser): PNode =
else: result.add(parseSymbol(p)) else: result.add(parseSymbol(p))
colcom(p, result) colcom(p, result)
result.add(parseStmt(p)) result.add(parseStmt(p))
setEndInfo()
proc parseStaticOrDefer(p: var Parser; k: TNodeKind): PNode = proc parseStaticOrDefer(p: var Parser; k: TNodeKind): PNode =
#| staticStmt = 'static' colcom stmt #| staticStmt = 'static' colcom stmt
@@ -1813,6 +1849,7 @@ proc parseStaticOrDefer(p: var Parser; k: TNodeKind): PNode =
getTok(p) getTok(p)
colcom(p, result) colcom(p, result)
result.add(parseStmt(p)) result.add(parseStmt(p))
setEndInfo()
proc parseAsm(p: var Parser): PNode = proc parseAsm(p: var Parser): PNode =
#| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLESTR_LIT) #| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
@@ -1829,6 +1866,7 @@ proc parseAsm(p: var Parser): PNode =
result.add(p.emptyNode) result.add(p.emptyNode)
return return
getTok(p) getTok(p)
setEndInfo()
proc parseGenericParam(p: var Parser): PNode = proc parseGenericParam(p: var Parser): PNode =
#| genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)? #| genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)?
@@ -1864,6 +1902,7 @@ proc parseGenericParam(p: var Parser): PNode =
result.add(parseExpr(p)) result.add(parseExpr(p))
else: else:
result.add(p.emptyNode) result.add(p.emptyNode)
setEndInfo()
proc parseGenericParamList(p: var Parser): PNode = proc parseGenericParamList(p: var Parser): PNode =
#| genericParamList = '[' optInd #| genericParamList = '[' optInd
@@ -1882,12 +1921,14 @@ proc parseGenericParamList(p: var Parser): PNode =
skipComment(p, a) skipComment(p, a)
optPar(p) optPar(p)
eat(p, tkBracketRi) eat(p, tkBracketRi)
setEndInfo()
proc parsePattern(p: var Parser): PNode = proc parsePattern(p: var Parser): PNode =
#| pattern = '{' stmt '}' #| pattern = '{' stmt '}'
eat(p, tkCurlyLe) eat(p, tkCurlyLe)
result = parseStmt(p) result = parseStmt(p)
eat(p, tkCurlyRi) eat(p, tkCurlyRi)
setEndInfo()
proc parseRoutine(p: var Parser, kind: TNodeKind): PNode = proc parseRoutine(p: var Parser, kind: TNodeKind): PNode =
#| indAndComment = (IND{>} COMMENT)? | COMMENT? #| indAndComment = (IND{>} COMMENT)? | COMMENT?
@@ -1932,6 +1973,7 @@ proc parseRoutine(p: var Parser, kind: TNodeKind): PNode =
#else: #else:
# assert false, p.lex.config$body.info # avoids hard to track bugs, fail early. # assert false, p.lex.config$body.info # avoids hard to track bugs, fail early.
# Yeah, that worked so well. There IS a bug in this logic, now what? # Yeah, that worked so well. There IS a bug in this logic, now what?
setEndInfo()
proc newCommentStmt(p: var Parser): PNode = proc newCommentStmt(p: var Parser): PNode =
#| commentStmt = COMMENT #| commentStmt = COMMENT
@@ -1967,6 +2009,7 @@ proc parseSection(p: var Parser, kind: TNodeKind,
result.add(defparser(p)) result.add(defparser(p))
else: else:
parMessage(p, errIdentifierExpected, p.tok) parMessage(p, errIdentifierExpected, p.tok)
setEndInfo()
proc parseEnum(p: var Parser): PNode = proc parseEnum(p: var Parser): PNode =
#| enumDecl = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+ #| enumDecl = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+
@@ -2013,6 +2056,7 @@ proc parseEnum(p: var Parser): PNode =
break break
if result.len <= 1: if result.len <= 1:
parMessage(p, errIdentifierExpected, p.tok) parMessage(p, errIdentifierExpected, p.tok)
setEndInfo()
proc parseObjectPart(p: var Parser): PNode proc parseObjectPart(p: var Parser): PNode
proc parseObjectWhen(p: var Parser): PNode = proc parseObjectWhen(p: var Parser): PNode =
@@ -2038,6 +2082,7 @@ proc parseObjectWhen(p: var Parser): PNode =
branch.add(parseObjectPart(p)) branch.add(parseObjectPart(p))
flexComment(p, branch) flexComment(p, branch)
result.add(branch) result.add(branch)
setEndInfo()
proc parseObjectCase(p: var Parser): PNode = proc parseObjectCase(p: var Parser): PNode =
#| objectBranch = 'of' exprList colcom objectPart #| objectBranch = 'of' exprList colcom objectPart
@@ -2079,6 +2124,7 @@ proc parseObjectCase(p: var Parser): PNode =
if b.kind == nkElse: break if b.kind == nkElse: break
if wasIndented: if wasIndented:
p.currInd = oldInd p.currInd = oldInd
setEndInfo()
proc parseObjectPart(p: var Parser): PNode = proc parseObjectPart(p: var Parser): PNode =
#| objectPart = IND{>} objectPart^+IND{=} DED #| objectPart = IND{>} objectPart^+IND{=} DED
@@ -2111,6 +2157,7 @@ proc parseObjectPart(p: var Parser): PNode =
result = p.emptyNode result = p.emptyNode
else: else:
result = p.emptyNode result = p.emptyNode
setEndInfo()
proc parseObject(p: var Parser): PNode = proc parseObject(p: var Parser): PNode =
#| objectDecl = 'object' ('of' typeDesc)? COMMENT? objectPart #| objectDecl = 'object' ('of' typeDesc)? COMMENT? objectPart
@@ -2131,6 +2178,7 @@ proc parseObject(p: var Parser): PNode =
result.add(p.emptyNode) result.add(p.emptyNode)
else: else:
result.add(parseObjectPart(p)) result.add(parseObjectPart(p))
setEndInfo()
proc parseTypeClassParam(p: var Parser): PNode = proc parseTypeClassParam(p: var Parser): PNode =
let modifier = let modifier =
@@ -2148,6 +2196,7 @@ proc parseTypeClassParam(p: var Parser): PNode =
result.add(p.parseSymbol) result.add(p.parseSymbol)
else: else:
result = p.parseSymbol result = p.parseSymbol
setEndInfo()
proc parseTypeClass(p: var Parser): PNode = proc parseTypeClass(p: var Parser): PNode =
#| conceptParam = ('var' | 'out')? symbol #| conceptParam = ('var' | 'out')? symbol
@@ -2191,6 +2240,7 @@ proc parseTypeClass(p: var Parser): PNode =
result.add(p.emptyNode) result.add(p.emptyNode)
else: else:
result.add(parseStmt(p)) result.add(parseStmt(p))
setEndInfo()
proc parseTypeDef(p: var Parser): PNode = proc parseTypeDef(p: var Parser): PNode =
#| #|
@@ -2224,6 +2274,7 @@ proc parseTypeDef(p: var Parser): PNode =
else: else:
result.add(p.emptyNode) result.add(p.emptyNode)
indAndComment(p, result) # special extension! indAndComment(p, result) # special extension!
setEndInfo()
proc parseVarTuple(p: var Parser): PNode = proc parseVarTuple(p: var Parser): PNode =
#| varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr #| varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr
@@ -2240,6 +2291,7 @@ proc parseVarTuple(p: var Parser): PNode =
result.add(p.emptyNode) # no type desc result.add(p.emptyNode) # no type desc
optPar(p) optPar(p)
eat(p, tkParRi) eat(p, tkParRi)
setEndInfo()
proc parseVariable(p: var Parser): PNode = proc parseVariable(p: var Parser): PNode =
#| colonBody = colcom stmt postExprBlocks? #| colonBody = colcom stmt postExprBlocks?
@@ -2252,6 +2304,7 @@ proc parseVariable(p: var Parser): PNode =
else: result = parseIdentColonEquals(p, {withPragma, withDot}) else: result = parseIdentColonEquals(p, {withPragma, withDot})
result[^1] = postExprBlocks(p, result[^1]) result[^1] = postExprBlocks(p, result[^1])
indAndComment(p, result) indAndComment(p, result)
setEndInfo()
proc parseConstant(p: var Parser): PNode = proc parseConstant(p: var Parser): PNode =
#| constant = (varTuple / identWithPragma) (colon typeDesc)? '=' optInd expr indAndComment #| constant = (varTuple / identWithPragma) (colon typeDesc)? '=' optInd expr indAndComment
@@ -2271,6 +2324,7 @@ proc parseConstant(p: var Parser): PNode =
result.add(parseExpr(p)) result.add(parseExpr(p))
result[^1] = postExprBlocks(p, result[^1]) result[^1] = postExprBlocks(p, result[^1])
indAndComment(p, result) indAndComment(p, result)
setEndInfo()
proc parseBind(p: var Parser, k: TNodeKind): PNode = proc parseBind(p: var Parser, k: TNodeKind): PNode =
#| bindStmt = 'bind' optInd qualifiedIdent ^+ comma #| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
@@ -2286,6 +2340,7 @@ proc parseBind(p: var Parser, k: TNodeKind): PNode =
getTok(p) getTok(p)
optInd(p, a) optInd(p, a)
#expectNl(p) #expectNl(p)
setEndInfo()
proc parseStmtPragma(p: var Parser): PNode = proc parseStmtPragma(p: var Parser): PNode =
#| pragmaStmt = pragma (':' COMMENT? stmt)? #| pragmaStmt = pragma (':' COMMENT? stmt)?
@@ -2297,6 +2352,7 @@ proc parseStmtPragma(p: var Parser): PNode =
skipComment(p, result) skipComment(p, result)
result.add a result.add a
result.add parseStmt(p) result.add parseStmt(p)
setEndInfo()
proc simpleStmt(p: var Parser): PNode = proc simpleStmt(p: var Parser): PNode =
#| simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt #| simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt
@@ -2439,6 +2495,7 @@ proc parseStmt(p: var Parser): PNode =
if p.tok.tokType != tkSemiColon: break if p.tok.tokType != tkSemiColon: break
getTok(p) getTok(p)
if err and p.tok.tokType == tkEof: break if err and p.tok.tokType == tkEof: break
setEndInfo()
proc parseAll(p: var Parser): PNode = proc parseAll(p: var Parser): PNode =
## Parses the rest of the input stream held by the parser into a PNode. ## Parses the rest of the input stream held by the parser into a PNode.
@@ -2454,6 +2511,7 @@ proc parseAll(p: var Parser): PNode =
getTok(p) getTok(p)
if p.tok.indent != 0: if p.tok.indent != 0:
parMessage(p, errInvalidIndentation) parMessage(p, errInvalidIndentation)
setEndInfo()
proc checkFirstLineIndentation*(p: var Parser) = proc checkFirstLineIndentation*(p: var Parser) =
if p.tok.indent != 0 and p.tok.strongSpaceA: if p.tok.indent != 0 and p.tok.strongSpaceA:
@@ -2487,6 +2545,7 @@ proc parseTopLevelStmt(p: var Parser): PNode =
result = complexOrSimpleStmt(p) result = complexOrSimpleStmt(p)
if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
break break
setEndInfo()
proc parseString*(s: string; cache: IdentCache; config: ConfigRef; proc parseString*(s: string; cache: IdentCache; config: ConfigRef;
filename: string = ""; line: int = 0; filename: string = ""; line: int = 0;
@@ -2498,9 +2557,10 @@ proc parseString*(s: string; cache: IdentCache; config: ConfigRef;
var stream = llStreamOpen(s) var stream = llStreamOpen(s)
stream.lineOffset = line stream.lineOffset = line
var parser: Parser var p: Parser
parser.lex.errorHandler = errorHandler p.lex.errorHandler = errorHandler
openParser(parser, AbsoluteFile filename, stream, cache, config) openParser(p, AbsoluteFile filename, stream, cache, config)
result = parser.parseAll result = p.parseAll
closeParser(parser) closeParser(p)
setEndInfo()

View File

@@ -980,6 +980,14 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy
return errorNode(c, n) return errorNode(c, n)
result = n result = n
when defined(nimsuggest):
if c.config.expandProgress:
if c.config.expandLevels == 0:
return n
else:
c.config.expandLevels -= 1
let callee = result[0].sym let callee = result[0].sym
case callee.kind case callee.kind
of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType) of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType)
@@ -1890,6 +1898,9 @@ proc semReturn(c: PContext, n: PNode): PNode =
localError(c.config, n.info, "'return' not allowed here") localError(c.config, n.info, "'return' not allowed here")
proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode = proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode =
when defined(nimsuggest):
if c.graph.config.expandDone():
return n
openScope(c) openScope(c)
result = semExpr(c, n, expectedType = expectedType) result = semExpr(c, n, expectedType = expectedType)
if c.p.resultSym != nil and not isEmptyType(result.typ): if c.p.resultSym != nil and not isEmptyType(result.typ):
@@ -2895,7 +2906,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
defer: defer:
if isCompilerDebug(): if isCompilerDebug():
echo ("<", c.config$n.info, n, ?.result.typ) echo ("<", c.config$n.info, n, ?.result.typ)
template directLiteral(typeKind: TTypeKind) = template directLiteral(typeKind: TTypeKind) =
if result.typ == nil: if result.typ == nil:
if expectedType != nil and ( if expectedType != nil and (
@@ -2907,6 +2917,19 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
result.typ = getSysType(c.graph, n.info, typeKind) result.typ = getSysType(c.graph, n.info, typeKind)
result = n result = n
when defined(nimsuggest):
var expandStarted = false
if c.config.ideCmd == ideExpand and not c.config.expandProgress and
((n.kind in {nkFuncDef, nkProcDef, nkIteratorDef, nkTemplateDef, nkMethodDef, nkConverterDef} and
n.info.exactEquals(c.config.expandPosition)) or
(n.kind in {nkCall, nkCommand} and
n[0].info.exactEquals(c.config.expandPosition))):
expandStarted = true
c.config.expandProgress = true
if c.config.expandLevels == 0:
c.config.expandNodeResult = $n
suggestQuit()
if c.config.cmd == cmdIdeTools: suggestExpr(c, n) if c.config.cmd == cmdIdeTools: suggestExpr(c, n)
if nfSem in n.flags: return if nfSem in n.flags: return
case n.kind case n.kind
@@ -3234,3 +3257,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
localError(c.config, n.info, "invalid expression: " & localError(c.config, n.info, "invalid expression: " &
renderTree(n, {renderNoComments})) renderTree(n, {renderNoComments}))
if result != nil: incl(result.flags, nfSem) if result != nil: incl(result.flags, nfSem)
when defined(nimsuggest):
if expandStarted:
c.config.expandNodeResult = $result
suggestQuit()

View File

@@ -1450,6 +1450,9 @@ proc hasRealBody(s: PSym): bool =
proc trackProc*(c: PContext; s: PSym, body: PNode) = proc trackProc*(c: PContext; s: PSym, body: PNode) =
let g = c.graph let g = c.graph
when defined(nimsuggest):
if g.config.expandDone():
return
var effects = s.typ.n[0] var effects = s.typ.n[0]
if effects.kind != nkEffectList: return if effects.kind != nkEffectList: return
# effects already computed? # effects already computed?

View File

@@ -2156,7 +2156,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if s.kind notin {skMacro, skTemplate} and s.magic == mNone: paramsTypeCheck(c, s.typ) if s.kind notin {skMacro, skTemplate} and s.magic == mNone: paramsTypeCheck(c, s.typ)
maybeAddResult(c, s, n) maybeAddResult(c, s, n)
let resultType = let resultType =
if s.kind == skMacro: if s.kind == skMacro:
sysTypeFromName(c.graph, n.info, "NimNode") sysTypeFromName(c.graph, n.info, "NimNode")
elif not isInlineIterator(s.typ): elif not isInlineIterator(s.typ):

View File

@@ -120,7 +120,9 @@ proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int
proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo; proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo;
quality: range[0..100]; prefix: PrefixMatch; quality: range[0..100]; prefix: PrefixMatch;
inTypeContext: bool; scope: int; inTypeContext: bool; scope: int;
useSuppliedInfo = false): Suggest = useSuppliedInfo = false,
endLine: uint16 = 0,
endCol = 0): Suggest =
new(result) new(result)
result.section = section result.section = section
result.quality = quality result.quality = quality
@@ -176,6 +178,8 @@ proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info
else: else:
getTokenLenFromSource(g.config, s.name.s, infox) getTokenLenFromSource(g.config, s.name.s, infox)
result.version = g.config.suggestVersion result.version = g.config.suggestVersion
result.endLine = endLine
result.endCol = endCol
proc `$`*(suggest: Suggest): string = proc `$`*(suggest: Suggest): string =
result = $suggest.section result = $suggest.section
@@ -216,6 +220,12 @@ proc `$`*(suggest: Suggest): string =
result.add(sep) result.add(sep)
result.add($suggest.prefix) result.add($suggest.prefix)
if (suggest.version == 3 and suggest.section in {ideOutline, ideExpand}):
result.add(sep)
result.add($suggest.endLine)
result.add(sep)
result.add($suggest.endCol)
proc suggestResult*(conf: ConfigRef; s: Suggest) = proc suggestResult*(conf: ConfigRef; s: Suggest) =
if not isNil(conf.suggestionResultHook): if not isNil(conf.suggestionResultHook):
conf.suggestionResultHook(s) conf.suggestionResultHook(s)

View File

@@ -2333,6 +2333,9 @@ const evalPass* = makePass(myOpen, myProcess, myClose)
proc evalConstExprAux(module: PSym; idgen: IdGenerator; proc evalConstExprAux(module: PSym; idgen: IdGenerator;
g: ModuleGraph; prc: PSym, n: PNode, g: ModuleGraph; prc: PSym, n: PNode,
mode: TEvalMode): PNode = mode: TEvalMode): PNode =
when defined(nimsuggest):
if g.config.expandDone():
return n
#if g.config.errorCounter > 0: return n #if g.config.errorCounter > 0: return n
let n = transformExpr(g, idgen, module, n) let n = transformExpr(g, idgen, module, n)
setupGlobalCtx(module, g, idgen) setupGlobalCtx(module, g, idgen)

View File

@@ -27,7 +27,7 @@ import compiler / [options, commands, modules, sem,
passes, passaux, msgs, passes, passaux, msgs,
sigmatch, ast, sigmatch, ast,
idents, modulegraphs, prefixmatches, lineinfos, cmdlinehelper, idents, modulegraphs, prefixmatches, lineinfos, cmdlinehelper,
pathutils, condsyms] pathutils, condsyms, syntaxes]
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/typedthreads import std/typedthreads
@@ -88,7 +88,7 @@ var
requests: Channel[string] requests: Channel[string]
results: Channel[Suggest] results: Channel[Suggest]
proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, line, col: int; proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, line, col: int; tag: string,
graph: ModuleGraph); graph: ModuleGraph);
proc writelnToChannel(line: string) = proc writelnToChannel(line: string) =
@@ -140,6 +140,9 @@ proc sexp(s: Suggest): SexpNode =
]) ])
if s.section == ideSug: if s.section == ideSug:
result.add convertSexp(s.prefix) result.add convertSexp(s.prefix)
if s.section in {ideOutline, ideExpand} and s.version == 3:
result.add convertSexp(s.endLine.int)
result.add convertSexp(s.endCol)
proc sexp(s: seq[Suggest]): SexpNode = proc sexp(s: seq[Suggest]): SexpNode =
result = newSList() result = newSList()
@@ -175,12 +178,23 @@ proc symFromInfo(graph: ModuleGraph; trackPos: TLineInfo): PSym =
if m != nil and m.ast != nil: if m != nil and m.ast != nil:
result = findNode(m.ast, trackPos) result = findNode(m.ast, trackPos)
proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; template benchmark(benchmarkName: untyped, code: untyped) =
block:
myLog "Started [" & benchmarkName & "]..."
let t0 = epochTime()
code
let elapsed = epochTime() - t0
let elapsedStr = elapsed.formatFloat(format = ffDecimal, precision = 3)
myLog "CPU Time [" & benchmarkName & "] " & elapsedStr & "s"
proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int, tag: string,
graph: ModuleGraph) = graph: ModuleGraph) =
let conf = graph.config let conf = graph.config
if conf.suggestVersion == 3: if conf.suggestVersion == 3:
executeNoHooksV3(cmd, file, dirtyfile, line, col, graph) let command = fmt "cmd = {cmd} {file}:{line}:{col}"
benchmark command:
executeNoHooksV3(cmd, file, dirtyfile, line, col, tag, graph)
return return
myLog("cmd: " & $cmd & ", file: " & file.string & myLog("cmd: " & $cmd & ", file: " & file.string &
@@ -219,7 +233,10 @@ proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
else: else:
localError(conf, conf.m.trackPos, "found no symbol at this position " & (conf $ conf.m.trackPos)) localError(conf, conf.m.trackPos, "found no symbol at this position " & (conf $ conf.m.trackPos))
proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int, graph: ModuleGraph) =
executeNoHooks(cmd, file, dirtyfile, line, col, graph)
proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; tag: string,
graph: ModuleGraph) = graph: ModuleGraph) =
if cmd == ideChk: if cmd == ideChk:
graph.config.structuredErrorHook = errorHook graph.config.structuredErrorHook = errorHook
@@ -227,7 +244,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
else: else:
graph.config.structuredErrorHook = nil graph.config.structuredErrorHook = nil
graph.config.writelnHook = myLog graph.config.writelnHook = myLog
executeNoHooks(cmd, file, dirtyfile, line, col, graph) executeNoHooks(cmd, file, dirtyfile, line, col, tag, graph)
proc executeEpc(cmd: IdeCmd, args: SexpNode; proc executeEpc(cmd: IdeCmd, args: SexpNode;
graph: ModuleGraph) = graph: ModuleGraph) =
@@ -238,7 +255,7 @@ proc executeEpc(cmd: IdeCmd, args: SexpNode;
var dirtyfile = AbsoluteFile"" var dirtyfile = AbsoluteFile""
if len(args) > 3: if len(args) > 3:
dirtyfile = AbsoluteFile args[3].getStr("") dirtyfile = AbsoluteFile args[3].getStr("")
execute(cmd, file, dirtyfile, int(line), int(column), graph) execute(cmd, file, dirtyfile, int(line), int(column), args[3].getStr, graph)
proc returnEpc(socket: Socket, uid: BiggestInt, s: SexpNode|string, proc returnEpc(socket: Socket, uid: BiggestInt, s: SexpNode|string,
returnSymbol = "return") = returnSymbol = "return") =
@@ -459,6 +476,7 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
of "changed": conf.ideCmd = ideChanged of "changed": conf.ideCmd = ideChanged
of "globalsymbols": conf.ideCmd = ideGlobalSymbols of "globalsymbols": conf.ideCmd = ideGlobalSymbols
of "declaration": conf.ideCmd = ideDeclaration of "declaration": conf.ideCmd = ideDeclaration
of "expand": conf.ideCmd = ideExpand
of "chkfile": conf.ideCmd = ideChkFile of "chkfile": conf.ideCmd = ideChkFile
of "recompile": conf.ideCmd = ideRecompile of "recompile": conf.ideCmd = ideRecompile
of "type": conf.ideCmd = ideType of "type": conf.ideCmd = ideType
@@ -478,6 +496,7 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
i += parseInt(cmd, line, i) i += parseInt(cmd, line, i)
i += skipWhile(cmd, seps, i) i += skipWhile(cmd, seps, i)
i += parseInt(cmd, col, i) i += parseInt(cmd, col, i)
let tag = substr(cmd, i)
if conf.ideCmd == ideKnown: if conf.ideCmd == ideKnown:
results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, AbsoluteFile orig)))) results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, AbsoluteFile orig))))
@@ -486,18 +505,9 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
else: else:
if conf.ideCmd == ideChk: if conf.ideCmd == ideChk:
for cm in cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev) for cm in cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev)
execute(conf.ideCmd, AbsoluteFile orig, AbsoluteFile dirtyfile, line, col, graph) execute(conf.ideCmd, AbsoluteFile orig, AbsoluteFile dirtyfile, line, col, tag, graph)
sentinel() sentinel()
template benchmark(benchmarkName: string, code: untyped) =
block:
myLog "Started [" & benchmarkName & "]..."
let t0 = epochTime()
code
let elapsed = epochTime() - t0
let elapsedStr = elapsed.formatFloat(format = ffDecimal, precision = 3)
myLog "CPU Time [" & benchmarkName & "] " & elapsedStr & "s"
proc recompileFullProject(graph: ModuleGraph) = proc recompileFullProject(graph: ModuleGraph) =
benchmark "Recompilation(clean)": benchmark "Recompilation(clean)":
graph.resetForBackend() graph.resetForBackend()
@@ -509,9 +519,9 @@ proc recompileFullProject(graph: ModuleGraph) =
proc mainThread(graph: ModuleGraph) = proc mainThread(graph: ModuleGraph) =
let conf = graph.config let conf = graph.config
if gLogging: myLog "searchPaths: "
for it in conf.searchPaths: for it in conf.searchPaths:
log(it.string) myLog(" " & it.string)
proc wrHook(line: string) {.closure.} = proc wrHook(line: string) {.closure.} =
if gMode == mepc: if gMode == mepc:
@@ -732,16 +742,20 @@ func deduplicateSymInfoPair[SymInfoPair](xs: seq[SymInfoPair]): seq[SymInfoPair]
result.add(itm) result.add(itm)
result.reverse() result.reverse()
proc findSymData(graph: ModuleGraph, trackPos: TLineInfo):
ref SymInfoPair =
for s in graph.fileSymbols(trackPos.fileIndex).deduplicateSymInfoPair:
if isTracked(s.info, trackPos, s.sym.name.s.len):
new(result)
result[] = s
break
proc findSymData(graph: ModuleGraph, file: AbsoluteFile; line, col: int): proc findSymData(graph: ModuleGraph, file: AbsoluteFile; line, col: int):
ref SymInfoPair = ref SymInfoPair =
let let
fileIdx = fileInfoIdx(graph.config, file) fileIdx = fileInfoIdx(graph.config, file)
trackPos = newLineInfo(fileIdx, line, col) trackPos = newLineInfo(fileIdx, line, col)
for s in graph.fileSymbols(fileIdx).deduplicateSymInfoPair: result = findSymData(graph, trackPos)
if isTracked(s.info, trackPos, s.sym.name.s.len):
new(result)
result[] = s
break
proc markDirtyIfNeeded(graph: ModuleGraph, file: string, originalFileIdx: FileIndex) = proc markDirtyIfNeeded(graph: ModuleGraph, file: string, originalFileIdx: FileIndex) =
let sha = $sha1.secureHashFile(file) let sha = $sha1.secureHashFile(file)
@@ -752,7 +766,8 @@ proc markDirtyIfNeeded(graph: ModuleGraph, file: string, originalFileIdx: FileIn
else: else:
myLog fmt "No changes in file {file} compared to last compilation" myLog fmt "No changes in file {file} compared to last compilation"
proc suggestResult(graph: ModuleGraph, sym: PSym, info: TLineInfo, defaultSection = ideNone) = proc suggestResult(graph: ModuleGraph, sym: PSym, info: TLineInfo,
defaultSection = ideNone, endLine: uint16 = 0, endCol = 0) =
let section = if defaultSection != ideNone: let section = if defaultSection != ideNone:
defaultSection defaultSection
elif sym.info.exactEquals(info): elif sym.info.exactEquals(info):
@@ -760,7 +775,8 @@ proc suggestResult(graph: ModuleGraph, sym: PSym, info: TLineInfo, defaultSectio
else: else:
ideUse ideUse
let suggest = symToSuggest(graph, sym, isLocal=false, section, let suggest = symToSuggest(graph, sym, isLocal=false, section,
info, 100, PrefixMatch.None, false, 0) info, 100, PrefixMatch.None, false, 0,
endLine = endLine, endCol = endCol)
suggestResult(graph.config, suggest) suggestResult(graph.config, suggest)
const const
@@ -771,7 +787,75 @@ proc symbolEqual(left, right: PSym): bool =
# More relaxed symbol comparison # More relaxed symbol comparison
return left.info.exactEquals(right.info) and left.name == right.name return left.info.exactEquals(right.info) and left.name == right.name
proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, line, col: int; proc findDef(n: PNode, line: uint16, col: int16): PNode =
if n.kind in {nkProcDef, nkIteratorDef, nkTemplateDef, nkMethodDef, nkMacroDef}:
if n.info.line == line:
return n
else:
for i in 0 ..< safeLen(n):
let res = findDef(n[i], line, col)
if res != nil: return res
proc findByTLineInfo(trackPos: TLineInfo, infoPairs: seq[SymInfoPair]):
ref SymInfoPair =
for s in infoPairs:
if s.info.exactEquals trackPos:
new(result)
result[] = s
break
proc outlineNode(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: seq[SymInfoPair]): bool =
proc checkSymbol(sym: PSym, info: TLineInfo): bool =
result = (sym.owner.kind in {skModule, skType} or sym.kind in {skProc, skMethod, skIterator, skTemplate, skType})
if n.kind == nkSym and n.sym.checkSymbol(n.info):
graph.suggestResult(n.sym, n.sym.info, ideOutline, endInfo.line, endInfo.col)
return true
elif n.kind == nkIdent:
let symData = findByTLineInfo(n.info, infoPairs)
if symData != nil and symData.sym.checkSymbol(symData.info):
let sym = symData.sym
graph.suggestResult(sym, sym.info, ideOutline, endInfo.line, endInfo.col)
return true
proc handleIdentOrSym(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: seq[SymInfoPair]): bool =
for child in n:
if child.kind in {nkIdent, nkSym}:
if graph.outlineNode(child, endInfo, infoPairs):
return true
elif child.kind == nkPostfix:
if graph.handleIdentOrSym(child, endInfo, infoPairs):
return true
proc iterateOutlineNodes(graph: ModuleGraph, n: PNode, infoPairs: seq[SymInfoPair]) =
var matched = true
if n.kind == nkIdent:
let symData = findByTLineInfo(n.info, infoPairs)
if symData != nil and symData.sym.kind == skEnumField and symData.info.exactEquals(symData.sym.info):
let sym = symData.sym
graph.suggestResult(sym, sym.info, ideOutline, n.endInfo.line, n.endInfo.col)
elif (n.kind in {nkFuncDef, nkProcDef, nkTypeDef, nkMacroDef, nkTemplateDef, nkConverterDef, nkEnumFieldDef, nkConstDef}):
matched = handleIdentOrSym(graph, n, n.endInfo, infoPairs)
else:
matched = false
if n.kind != nkFormalParams:
for child in n:
graph.iterateOutlineNodes(child, infoPairs)
proc calculateExpandRange(n: PNode, info: TLineInfo): TLineInfo =
if ((n.kind in {nkFuncDef, nkProcDef, nkIteratorDef, nkTemplateDef, nkMethodDef, nkConverterDef} and
n.info.exactEquals(info)) or
(n.kind in {nkCall, nkCommand} and n[0].info.exactEquals(info))):
result = n.endInfo
else:
for child in n:
result = child.calculateExpandRange(info)
if result != unknownLineInfo:
return result
result = unknownLineInfo
proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, line, col: int; tag: string,
graph: ModuleGraph) = graph: ModuleGraph) =
let conf = graph.config let conf = graph.config
conf.writelnHook = proc (s: string) = discard conf.writelnHook = proc (s: string) = discard
@@ -783,7 +867,7 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
conf.ideCmd = cmd conf.ideCmd = cmd
myLog fmt "cmd: {cmd}, file: {file}[{line}:{col}], dirtyFile: {dirtyfile}" myLog fmt "cmd: {cmd}, file: {file}[{line}:{col}], dirtyFile: {dirtyfile}, tag: {tag}"
var fileIndex: FileIndex var fileIndex: FileIndex
@@ -811,7 +895,7 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
graph.unmarkAllDirty() graph.unmarkAllDirty()
# these commands require partially compiled project # these commands require partially compiled project
elif cmd in {ideSug, ideOutline, ideHighlight, ideDef, ideChkFile, ideType, ideDeclaration} and elif cmd in {ideSug, ideOutline, ideHighlight, ideDef, ideChkFile, ideType, ideDeclaration, ideExpand} and
(graph.needsCompilation(fileIndex) or cmd == ideSug): (graph.needsCompilation(fileIndex) or cmd == ideSug):
# for ideSug use v2 implementation # for ideSug use v2 implementation
if cmd == ideSug: if cmd == ideSug:
@@ -861,16 +945,8 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
# future calls. # future calls.
graph.markDirtyIfNeeded(file.string, fileIndex) graph.markDirtyIfNeeded(file.string, fileIndex)
of ideOutline: of ideOutline:
let let n = parseFile(fileIndex, graph.cache, graph.config)
module = graph.getModule fileIndex graph.iterateOutlineNodes(n, graph.fileSymbols(fileIndex).deduplicateSymInfoPair)
symbols = graph.fileSymbols(fileIndex)
.deduplicateSymInfoPair
.filterIt(it.sym.info.exactEquals(it.info) and
(it.sym.owner == module or
it.sym.kind in searchableSymKinds))
for s in symbols:
graph.suggestResult(s.sym, s.info, ideOutline)
of ideChk: of ideChk:
myLog fmt "Reporting errors for {graph.suggestErrors.len} file(s)" myLog fmt "Reporting errors for {graph.suggestErrors.len} file(s)"
for sug in graph.suggestErrorsIter: for sug in graph.suggestErrorsIter:
@@ -933,6 +1009,39 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
else: else:
# we are on definition or usage, look for declaration # we are on definition or usage, look for declaration
graph.suggestResult(first.sym, first.info, ideDeclaration) graph.suggestResult(first.sym, first.info, ideDeclaration)
of ideExpand:
var level: int = high(int)
let index = skipWhitespace(tag, 0);
let trimmed = substr(tag, index)
if not (trimmed == "" or trimmed == "all"):
discard parseInt(trimmed, level, 0)
conf.expandPosition = newLineInfo(fileIndex, line, col)
conf.expandLevels = level
conf.expandProgress = false
conf.expandNodeResult = ""
graph.markDirty fileIndex
graph.markClientsDirty fileIndex
graph.recompilePartially()
var suggest = Suggest()
suggest.section = ideExpand
suggest.version = 3
suggest.line = line
suggest.column = col
suggest.doc = graph.config.expandNodeResult
if suggest.doc != "":
let
n = parseFile(fileIndex, graph.cache, graph.config)
endInfo = n.calculateExpandRange(conf.expandPosition)
suggest.endLine = endInfo.line
suggest.endCol = endInfo.col
suggestResult(graph.config, suggest)
graph.markDirty fileIndex
graph.markClientsDirty fileIndex
else: else:
myLog fmt "Discarding {cmd}" myLog fmt "Discarding {cmd}"
@@ -1018,9 +1127,9 @@ else:
if self.loadConfigsAndProcessCmdLine(cache, conf, graph): if self.loadConfigsAndProcessCmdLine(cache, conf, graph):
mockCommand(graph) mockCommand(graph)
if gLogging: if gLogging:
log("Search paths:") myLog("Search paths:")
for it in conf.searchPaths: for it in conf.searchPaths:
log(" " & it.string) myLog(" " & it.string)
retval.doStopCompile = proc (): bool = false retval.doStopCompile = proc (): bool = false
return NimSuggest(graph: retval, idle: 0, cachedMsgs: @[]) return NimSuggest(graph: retval, idle: 0, cachedMsgs: @[])

View File

@@ -66,7 +66,7 @@ proc parseTest(filename: string; epcMode=false): Test =
elif x.startsWith(">"): elif x.startsWith(">"):
# since 'markers' here are not complete yet, we do the $substitutions # since 'markers' here are not complete yet, we do the $substitutions
# afterwards # afterwards
result.script.add((x.substr(1).replaceWord("$path", tpath), "")) result.script.add((x.substr(1).replaceWord("$path", tpath).replaceWord("$file", filename), ""))
elif x.len > 0: elif x.len > 0:
# expected output line: # expected output line:
let x = x % ["file", filename, "lib", libpath] let x = x % ["file", filename, "lib", libpath]
@@ -218,7 +218,12 @@ proc sexpToAnswer(s: SexpNode): string =
result.add doc result.add doc
result.add '\t' result.add '\t'
result.addInt a[8].getNum result.addInt a[8].getNum
if a.len >= 10: if a.len >= 11:
result.add '\t'
result.addInt a[9].getNum
result.add '\t'
result.addInt a[10].getNum
elif a.len >= 10:
result.add '\t' result.add '\t'
result.add a[9].getStr result.add a[9].getStr
result.add '\L' result.add '\L'
@@ -229,8 +234,8 @@ proc doReport(filename, answer, resp: string; report: var string) =
var hasDiff = false var hasDiff = false
for i in 0..min(resp.len-1, answer.len-1): for i in 0..min(resp.len-1, answer.len-1):
if resp[i] != answer[i]: if resp[i] != answer[i]:
report.add "\n Expected: " & resp.substr(i, i+200) report.add "\n Expected:\n" & resp
report.add "\n But got: " & answer.substr(i, i+200) report.add "\n But got:\n" & answer
hasDiff = true hasDiff = true
break break
if not hasDiff: if not hasDiff:
@@ -342,8 +347,8 @@ proc main() =
if os.paramCount() > 0: if os.paramCount() > 0:
let x = os.paramStr(1) let x = os.paramStr(1)
let xx = expandFilename x let xx = expandFilename x
# run only stdio when running single test
failures += runTest(xx) failures += runTest(xx)
failures += runEpcTest(xx)
else: else:
let files = toSeq(walkFiles(tpath / "t*.nim")) let files = toSeq(walkFiles(tpath / "t*.nim"))
for i, x in files: for i, x in files:

View File

@@ -16,10 +16,6 @@ def skField tv3.Foo.bar string $file 5 4 "" 100
use skField tv3.Foo.bar string $file 8 9 "" 100 use skField tv3.Foo.bar string $file 8 9 "" 100
>def $1 >def $1
def skField tv3.Foo.bar string $file 5 4 "" 100 def skField tv3.Foo.bar string $file 5 4 "" 100
>outline $1
outline skType tv3.Foo Foo $file 4 2 "" 100
outline skField tv3.Foo.bar string $file 5 4 "" 100
outline skProc tv3.test proc (f: Foo){.gcsafe.} $file 7 5 "" 100
>sug $1 >sug $1
sug skField bar string $file 5 4 "" 100 Prefix sug skField bar string $file 5 4 "" 100 Prefix
>globalSymbols test >globalSymbols test

View File

@@ -0,0 +1,45 @@
# tests v3 outline
type
Foo* = ref object of RootObj
bar*: string
FooEnum = enum value1, value2
FooPrivate = ref object of RootObj
barPrivate: string
macro m(arg: untyped): untyped = discard
template t(arg: untyped): untyped = discard
proc p(): void = discard
iterator i(): int = discard
converter c(s: string): int = discard
method m(f: Foo): void = discard
func f(): void = discard
let a = 1
var b = 2
const con = 2
proc outer(): void =
proc inner() = discard
proc procWithLocal(): void =
let local = 10
discard """
$nimsuggest --v3 --tester $file
>outline $file
outline skType tv3_outline.Foo Foo $file 4 2 "" 100 5 16
outline skType tv3_outline.FooEnum FooEnum $file 6 2 "" 100 6 31
outline skEnumField tv3_outline.FooEnum.value1 FooEnum $file 6 17 "" 100 6 23
outline skEnumField tv3_outline.FooEnum.value2 FooEnum $file 6 25 "" 100 6 31
outline skType tv3_outline.FooPrivate FooPrivate $file 7 2 "" 100 8 22
outline skMacro tv3_outline.m macro (arg: untyped): untyped{.noSideEffect, gcsafe.} $file 10 6 "" 100 10 40
outline skTemplate tv3_outline.t template (arg: untyped): untyped $file 11 9 "" 100 11 43
outline skProc tv3_outline.p proc (){.noSideEffect, gcsafe.} $file 12 5 "" 100 12 24
outline skConverter tv3_outline.c converter (s: string): int{.noSideEffect, gcsafe.} $file 14 10 "" 100 14 37
outline skFunc tv3_outline.f proc (){.noSideEffect, gcsafe.} $file 16 5 "" 100 16 24
outline skConst tv3_outline.con int literal(2) $file 20 6 "" 100 20 13
outline skProc tv3_outline.outer proc (){.noSideEffect, gcsafe.} $file 22 5 "" 100 23 24
outline skProc tv3_outline.outer.inner proc (){.noSideEffect, gcsafe.} $file 23 7 "" 100 23 24
outline skProc tv3_outline.procWithLocal proc (){.noSideEffect, gcsafe.} $file 25 5 "" 100 26 16
"""