mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
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:
@@ -796,6 +796,8 @@ type
|
||||
ident*: PIdent
|
||||
else:
|
||||
sons*: TNodeSeq
|
||||
when defined(nimsuggest):
|
||||
endInfo*: TLineInfo
|
||||
|
||||
TStrTable* = object # a table[PIdent] of PSym
|
||||
counter*: int
|
||||
@@ -892,6 +894,8 @@ type
|
||||
typ*: PType
|
||||
name*: PIdent
|
||||
info*: TLineInfo
|
||||
when defined(nimsuggest):
|
||||
endInfo*: TLineInfo
|
||||
owner*: PSym
|
||||
flags*: TSymFlags
|
||||
ast*: PNode # syntax tree of proc, iterator, etc.:
|
||||
@@ -1690,6 +1694,8 @@ proc copyNode*(src: PNode): PNode =
|
||||
of nkIdent: result.ident = src.ident
|
||||
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
|
||||
else: discard
|
||||
when defined(nimsuggest):
|
||||
result.endInfo = src.endInfo
|
||||
|
||||
template transitionNodeKindCommon(k: TNodeKind) =
|
||||
let obj {.inject.} = n[]
|
||||
@@ -1742,6 +1748,8 @@ template copyNodeImpl(dst, src, processSonsStmt) =
|
||||
if src == nil: return
|
||||
dst = newNode(src.kind)
|
||||
dst.info = src.info
|
||||
when defined(nimsuggest):
|
||||
result.endInfo = src.endInfo
|
||||
dst.typ = src.typ
|
||||
dst.flags = src.flags * PersistentNodeFlags
|
||||
dst.comment = src.comment
|
||||
|
||||
@@ -127,6 +127,8 @@ type
|
||||
cache*: IdentCache
|
||||
when defined(nimsuggest):
|
||||
previousToken: TLineInfo
|
||||
tokenEnd*: TLineInfo
|
||||
previousTokenEnd*: TLineInfo
|
||||
config*: ConfigRef
|
||||
|
||||
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) =
|
||||
template atTokenEnd() {.dirty.} =
|
||||
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
|
||||
if tok.tokType notin weakTokens:
|
||||
L.previousToken.line = tok.line.uint16
|
||||
|
||||
@@ -195,7 +195,7 @@ type
|
||||
IdeCmd* = enum
|
||||
ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideChkFile, ideMod,
|
||||
ideHighlight, ideOutline, ideKnown, ideMsg, ideProject, ideGlobalSymbols,
|
||||
ideRecompile, ideChanged, ideType, ideDeclaration
|
||||
ideRecompile, ideChanged, ideType, ideDeclaration, ideExpand
|
||||
|
||||
Feature* = enum ## experimental features; DO NOT RENAME THESE!
|
||||
dotOperators,
|
||||
@@ -278,6 +278,9 @@ type
|
||||
scope*, localUsages*, globalUsages*: int # more usages is better
|
||||
tokenLen*: int
|
||||
version*: int
|
||||
endLine*: uint16
|
||||
endCol*: int
|
||||
|
||||
Suggestions* = seq[Suggest]
|
||||
|
||||
ProfileInfo* = object
|
||||
@@ -408,6 +411,11 @@ type
|
||||
nimMainPrefix*: string
|
||||
vmProfileData*: ProfileData
|
||||
|
||||
expandProgress*: bool
|
||||
expandLevels*: int
|
||||
expandNodeResult*: string
|
||||
expandPosition*: TLineInfo
|
||||
|
||||
proc parseNimVersion*(a: string): NimVer =
|
||||
# could be moved somewhere reusable
|
||||
if a.len > 0:
|
||||
@@ -996,6 +1004,9 @@ proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
|
||||
result = optDynlibOverrideAll in conf.globalOptions or
|
||||
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 =
|
||||
case s:
|
||||
of "sug": ideSug
|
||||
@@ -1035,6 +1046,7 @@ proc `$`*(c: IdeCmd): string =
|
||||
of ideProject: "project"
|
||||
of ideGlobalSymbols: "globalSymbols"
|
||||
of ideDeclaration: "declaration"
|
||||
of ideExpand: "expand"
|
||||
of ideRecompile: "recompile"
|
||||
of ideChanged: "changed"
|
||||
of ideType: "type"
|
||||
|
||||
@@ -354,6 +354,12 @@ proc colcom(p: var Parser, n: PNode) =
|
||||
|
||||
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 =
|
||||
#| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
|
||||
#| | IDENT | KEYW
|
||||
@@ -406,6 +412,7 @@ proc parseSymbol(p: var Parser, mode = smNormal): PNode =
|
||||
# if it is a keyword:
|
||||
#if not isKeyword(p.tok.tokType): getTok(p)
|
||||
result = p.emptyNode
|
||||
setEndInfo()
|
||||
|
||||
proc equals(p: var Parser, a: PNode): PNode =
|
||||
if p.tok.tokType == tkEquals:
|
||||
@@ -577,6 +584,7 @@ proc parseCast(p: var Parser): PNode =
|
||||
result.add(exprColonEqExpr(p))
|
||||
optPar(p)
|
||||
eat(p, tkParRi)
|
||||
setEndInfo()
|
||||
|
||||
proc setBaseFlags(n: PNode, base: NumericalBase) =
|
||||
case base
|
||||
@@ -599,6 +607,7 @@ proc parseGStrLit(p: var Parser, a: PNode): PNode =
|
||||
getTok(p)
|
||||
else:
|
||||
result = a
|
||||
setEndInfo()
|
||||
|
||||
proc complexOrSimpleStmt(p: var Parser): PNode
|
||||
proc simpleExpr(p: var Parser, mode = pmNormal): PNode
|
||||
@@ -703,6 +712,7 @@ proc parsePar(p: var Parser): PNode =
|
||||
skipComment(p, a)
|
||||
optPar(p)
|
||||
eat(p, tkParRi)
|
||||
setEndInfo()
|
||||
|
||||
proc identOrLiteral(p: var Parser, mode: PrimaryMode): PNode =
|
||||
#| 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)
|
||||
result = a
|
||||
opPrec = getPrecedence(p.tok)
|
||||
setEndInfo()
|
||||
|
||||
proc simpleExprAux(p: var Parser, limit: int, mode: PrimaryMode): PNode =
|
||||
var mode = mode
|
||||
@@ -990,6 +1001,7 @@ proc parsePragma(p: var Parser): PNode =
|
||||
when defined(nimpretty):
|
||||
dec p.em.doIndentMore
|
||||
dec p.em.keepIndents
|
||||
setEndInfo()
|
||||
|
||||
proc identVis(p: var Parser; allowDot=false): PNode =
|
||||
#| identVis = symbol OPR? # postfix position
|
||||
@@ -1058,6 +1070,7 @@ proc parseIdentColonEquals(p: var Parser, flags: DeclaredIdentFlags): PNode =
|
||||
result.add(parseExpr(p))
|
||||
else:
|
||||
result.add(newNodeP(nkEmpty, p))
|
||||
setEndInfo()
|
||||
|
||||
proc parseTuple(p: var Parser, indentAllowed = false): PNode =
|
||||
#| 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(...)'")
|
||||
else:
|
||||
result = newNodeP(nkTupleClassTy, p)
|
||||
setEndInfo()
|
||||
|
||||
proc parseParamList(p: var Parser, retColon = true): PNode =
|
||||
#| paramList = '(' declColonEquals ^* (comma/semicolon) ')'
|
||||
@@ -1150,6 +1164,7 @@ proc parseParamList(p: var Parser, retColon = true): PNode =
|
||||
when defined(nimpretty):
|
||||
dec p.em.doIndentMore
|
||||
dec p.em.keepIndents
|
||||
setEndInfo()
|
||||
|
||||
proc optPragmas(p: var Parser): PNode =
|
||||
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,
|
||||
body = result, params = params, name = p.emptyNode, pattern = p.emptyNode,
|
||||
genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
|
||||
setEndInfo()
|
||||
|
||||
proc parseProcExpr(p: var Parser; isExpr: bool; kind: TNodeKind): PNode =
|
||||
#| 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:
|
||||
parMessage(p, "func keyword is not allowed in type descriptions, use proc with {.noSideEffect.} pragma instead")
|
||||
result.add(pragmas)
|
||||
setEndInfo()
|
||||
|
||||
proc isExprStart(p: Parser): bool =
|
||||
case p.tok.tokType
|
||||
@@ -1211,6 +1228,7 @@ proc parseSymbolList(p: var Parser, result: PNode) =
|
||||
if p.tok.tokType != tkComma: break
|
||||
getTok(p)
|
||||
optInd(p, s)
|
||||
setEndInfo()
|
||||
|
||||
proc parseTypeDescKAux(p: var Parser, kind: TNodeKind,
|
||||
mode: PrimaryMode): PNode =
|
||||
@@ -1239,6 +1257,7 @@ proc parseTypeDescKAux(p: var Parser, kind: TNodeKind,
|
||||
parseSymbolList(p, list)
|
||||
if mode == pmTypeDef and not isTypedef:
|
||||
result = parseOperators(p, result, -1, mode)
|
||||
setEndInfo()
|
||||
|
||||
proc parseVarTuple(p: var Parser): PNode
|
||||
|
||||
@@ -1264,6 +1283,7 @@ proc parseFor(p: var Parser): PNode =
|
||||
result.add(parseExpr(p))
|
||||
colcom(p, result)
|
||||
result.add(parseStmt(p))
|
||||
setEndInfo()
|
||||
|
||||
template nimprettyDontTouch(body) =
|
||||
when defined(nimpretty):
|
||||
@@ -1302,6 +1322,7 @@ proc parseExpr(p: var Parser): PNode =
|
||||
nimprettyDontTouch:
|
||||
result = parseTry(p, isExpr=true)
|
||||
else: result = simpleExpr(p)
|
||||
setEndInfo()
|
||||
|
||||
proc parseEnum(p: var Parser): PNode
|
||||
proc parseObject(p: var Parser): PNode
|
||||
@@ -1411,6 +1432,7 @@ proc parseTypeDesc(p: var Parser, fullExpr = false): PNode =
|
||||
else:
|
||||
result = simpleExpr(p, pmTypeDesc)
|
||||
result = binaryNot(p, result)
|
||||
setEndInfo()
|
||||
|
||||
proc parseTypeDefValue(p: var Parser): PNode =
|
||||
#| typeDefValue = ((tupleDecl | enumDecl | objectDecl | conceptDecl |
|
||||
@@ -1441,6 +1463,7 @@ proc parseTypeDefValue(p: var Parser): PNode =
|
||||
result.add(commandParam(p, isFirstParam, pmTypeDef))
|
||||
result = postExprBlocks(p, result)
|
||||
result = binaryNot(p, result)
|
||||
setEndInfo()
|
||||
|
||||
proc makeCall(n: PNode): PNode =
|
||||
## Creates a call if the given node isn't already a call.
|
||||
@@ -1561,6 +1584,7 @@ proc parseExprStmt(p: var Parser): PNode =
|
||||
else:
|
||||
result = a
|
||||
result = postExprBlocks(p, result)
|
||||
setEndInfo()
|
||||
|
||||
proc parseModuleName(p: var Parser, kind: TNodeKind): PNode =
|
||||
result = parseExpr(p)
|
||||
@@ -1572,6 +1596,7 @@ proc parseModuleName(p: var Parser, kind: TNodeKind): PNode =
|
||||
getTok(p)
|
||||
result.add(a)
|
||||
result.add(parseExpr(p))
|
||||
setEndInfo()
|
||||
|
||||
proc parseImport(p: var Parser, kind: TNodeKind): PNode =
|
||||
#| importStmt = 'import' optInd expr
|
||||
@@ -1600,6 +1625,7 @@ proc parseImport(p: var Parser, kind: TNodeKind): PNode =
|
||||
getTok(p)
|
||||
optInd(p, a)
|
||||
#expectNl(p)
|
||||
setEndInfo()
|
||||
|
||||
proc parseIncludeStmt(p: var Parser): PNode =
|
||||
#| includeStmt = 'include' optInd expr ^+ comma
|
||||
@@ -1616,6 +1642,7 @@ proc parseIncludeStmt(p: var Parser): PNode =
|
||||
getTok(p)
|
||||
optInd(p, a)
|
||||
#expectNl(p)
|
||||
setEndInfo()
|
||||
|
||||
proc parseFromStmt(p: var Parser): PNode =
|
||||
#| fromStmt = 'from' expr 'import' optInd expr (comma expr)*
|
||||
@@ -1636,6 +1663,7 @@ proc parseFromStmt(p: var Parser): PNode =
|
||||
getTok(p)
|
||||
optInd(p, a)
|
||||
#expectNl(p)
|
||||
setEndInfo()
|
||||
|
||||
proc parseReturnOrRaise(p: var Parser, kind: TNodeKind): PNode =
|
||||
#| returnStmt = 'return' optInd expr?
|
||||
@@ -1657,6 +1685,7 @@ proc parseReturnOrRaise(p: var Parser, kind: TNodeKind): PNode =
|
||||
var e = parseExpr(p)
|
||||
e = postExprBlocks(p, e)
|
||||
result.add(e)
|
||||
setEndInfo()
|
||||
|
||||
proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode =
|
||||
#| condStmt = expr colcom stmt COMMENT?
|
||||
@@ -1681,6 +1710,7 @@ proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode =
|
||||
colcom(p, branch)
|
||||
branch.add(parseStmt(p))
|
||||
result.add(branch)
|
||||
setEndInfo()
|
||||
|
||||
proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode =
|
||||
#| condExpr = expr colcom expr optInd
|
||||
@@ -1705,6 +1735,7 @@ proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode =
|
||||
colcom(p, branch)
|
||||
branch.add(parseStmt(p))
|
||||
result.add(branch)
|
||||
setEndInfo()
|
||||
|
||||
proc parseWhile(p: var Parser): PNode =
|
||||
#| whileStmt = 'while' expr colcom stmt
|
||||
@@ -1714,6 +1745,7 @@ proc parseWhile(p: var Parser): PNode =
|
||||
result.add(parseExpr(p))
|
||||
colcom(p, result)
|
||||
result.add(parseStmt(p))
|
||||
setEndInfo()
|
||||
|
||||
proc parseCase(p: var Parser): PNode =
|
||||
#| ofBranch = 'of' exprList colcom stmt
|
||||
@@ -1761,6 +1793,7 @@ proc parseCase(p: var Parser): PNode =
|
||||
|
||||
if wasIndented:
|
||||
p.currInd = oldInd
|
||||
setEndInfo()
|
||||
|
||||
proc parseTry(p: var Parser; isExpr: bool): PNode =
|
||||
#| tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally')
|
||||
@@ -1789,12 +1822,14 @@ proc parseTry(p: var Parser; isExpr: bool): PNode =
|
||||
b.add(parseStmt(p))
|
||||
result.add(b)
|
||||
if b == nil: parMessage(p, "expected 'except'")
|
||||
setEndInfo()
|
||||
|
||||
proc parseExceptBlock(p: var Parser, kind: TNodeKind): PNode =
|
||||
result = newNodeP(kind, p)
|
||||
getTok(p)
|
||||
colcom(p, result)
|
||||
result.add(parseStmt(p))
|
||||
setEndInfo()
|
||||
|
||||
proc parseBlock(p: var Parser): PNode =
|
||||
#| blockStmt = 'block' symbol? colcom stmt
|
||||
@@ -1805,6 +1840,7 @@ proc parseBlock(p: var Parser): PNode =
|
||||
else: result.add(parseSymbol(p))
|
||||
colcom(p, result)
|
||||
result.add(parseStmt(p))
|
||||
setEndInfo()
|
||||
|
||||
proc parseStaticOrDefer(p: var Parser; k: TNodeKind): PNode =
|
||||
#| staticStmt = 'static' colcom stmt
|
||||
@@ -1813,6 +1849,7 @@ proc parseStaticOrDefer(p: var Parser; k: TNodeKind): PNode =
|
||||
getTok(p)
|
||||
colcom(p, result)
|
||||
result.add(parseStmt(p))
|
||||
setEndInfo()
|
||||
|
||||
proc parseAsm(p: var Parser): PNode =
|
||||
#| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
|
||||
@@ -1829,6 +1866,7 @@ proc parseAsm(p: var Parser): PNode =
|
||||
result.add(p.emptyNode)
|
||||
return
|
||||
getTok(p)
|
||||
setEndInfo()
|
||||
|
||||
proc parseGenericParam(p: var Parser): PNode =
|
||||
#| genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)?
|
||||
@@ -1864,6 +1902,7 @@ proc parseGenericParam(p: var Parser): PNode =
|
||||
result.add(parseExpr(p))
|
||||
else:
|
||||
result.add(p.emptyNode)
|
||||
setEndInfo()
|
||||
|
||||
proc parseGenericParamList(p: var Parser): PNode =
|
||||
#| genericParamList = '[' optInd
|
||||
@@ -1882,12 +1921,14 @@ proc parseGenericParamList(p: var Parser): PNode =
|
||||
skipComment(p, a)
|
||||
optPar(p)
|
||||
eat(p, tkBracketRi)
|
||||
setEndInfo()
|
||||
|
||||
proc parsePattern(p: var Parser): PNode =
|
||||
#| pattern = '{' stmt '}'
|
||||
eat(p, tkCurlyLe)
|
||||
result = parseStmt(p)
|
||||
eat(p, tkCurlyRi)
|
||||
setEndInfo()
|
||||
|
||||
proc parseRoutine(p: var Parser, kind: TNodeKind): PNode =
|
||||
#| indAndComment = (IND{>} COMMENT)? | COMMENT?
|
||||
@@ -1932,6 +1973,7 @@ proc parseRoutine(p: var Parser, kind: TNodeKind): PNode =
|
||||
#else:
|
||||
# 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?
|
||||
setEndInfo()
|
||||
|
||||
proc newCommentStmt(p: var Parser): PNode =
|
||||
#| commentStmt = COMMENT
|
||||
@@ -1967,6 +2009,7 @@ proc parseSection(p: var Parser, kind: TNodeKind,
|
||||
result.add(defparser(p))
|
||||
else:
|
||||
parMessage(p, errIdentifierExpected, p.tok)
|
||||
setEndInfo()
|
||||
|
||||
proc parseEnum(p: var Parser): PNode =
|
||||
#| enumDecl = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+
|
||||
@@ -2013,6 +2056,7 @@ proc parseEnum(p: var Parser): PNode =
|
||||
break
|
||||
if result.len <= 1:
|
||||
parMessage(p, errIdentifierExpected, p.tok)
|
||||
setEndInfo()
|
||||
|
||||
proc parseObjectPart(p: var Parser): PNode
|
||||
proc parseObjectWhen(p: var Parser): PNode =
|
||||
@@ -2038,6 +2082,7 @@ proc parseObjectWhen(p: var Parser): PNode =
|
||||
branch.add(parseObjectPart(p))
|
||||
flexComment(p, branch)
|
||||
result.add(branch)
|
||||
setEndInfo()
|
||||
|
||||
proc parseObjectCase(p: var Parser): PNode =
|
||||
#| objectBranch = 'of' exprList colcom objectPart
|
||||
@@ -2079,6 +2124,7 @@ proc parseObjectCase(p: var Parser): PNode =
|
||||
if b.kind == nkElse: break
|
||||
if wasIndented:
|
||||
p.currInd = oldInd
|
||||
setEndInfo()
|
||||
|
||||
proc parseObjectPart(p: var Parser): PNode =
|
||||
#| objectPart = IND{>} objectPart^+IND{=} DED
|
||||
@@ -2111,6 +2157,7 @@ proc parseObjectPart(p: var Parser): PNode =
|
||||
result = p.emptyNode
|
||||
else:
|
||||
result = p.emptyNode
|
||||
setEndInfo()
|
||||
|
||||
proc parseObject(p: var Parser): PNode =
|
||||
#| objectDecl = 'object' ('of' typeDesc)? COMMENT? objectPart
|
||||
@@ -2131,6 +2178,7 @@ proc parseObject(p: var Parser): PNode =
|
||||
result.add(p.emptyNode)
|
||||
else:
|
||||
result.add(parseObjectPart(p))
|
||||
setEndInfo()
|
||||
|
||||
proc parseTypeClassParam(p: var Parser): PNode =
|
||||
let modifier =
|
||||
@@ -2148,6 +2196,7 @@ proc parseTypeClassParam(p: var Parser): PNode =
|
||||
result.add(p.parseSymbol)
|
||||
else:
|
||||
result = p.parseSymbol
|
||||
setEndInfo()
|
||||
|
||||
proc parseTypeClass(p: var Parser): PNode =
|
||||
#| conceptParam = ('var' | 'out')? symbol
|
||||
@@ -2191,6 +2240,7 @@ proc parseTypeClass(p: var Parser): PNode =
|
||||
result.add(p.emptyNode)
|
||||
else:
|
||||
result.add(parseStmt(p))
|
||||
setEndInfo()
|
||||
|
||||
proc parseTypeDef(p: var Parser): PNode =
|
||||
#|
|
||||
@@ -2224,6 +2274,7 @@ proc parseTypeDef(p: var Parser): PNode =
|
||||
else:
|
||||
result.add(p.emptyNode)
|
||||
indAndComment(p, result) # special extension!
|
||||
setEndInfo()
|
||||
|
||||
proc parseVarTuple(p: var Parser): PNode =
|
||||
#| varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr
|
||||
@@ -2240,6 +2291,7 @@ proc parseVarTuple(p: var Parser): PNode =
|
||||
result.add(p.emptyNode) # no type desc
|
||||
optPar(p)
|
||||
eat(p, tkParRi)
|
||||
setEndInfo()
|
||||
|
||||
proc parseVariable(p: var Parser): PNode =
|
||||
#| colonBody = colcom stmt postExprBlocks?
|
||||
@@ -2252,6 +2304,7 @@ proc parseVariable(p: var Parser): PNode =
|
||||
else: result = parseIdentColonEquals(p, {withPragma, withDot})
|
||||
result[^1] = postExprBlocks(p, result[^1])
|
||||
indAndComment(p, result)
|
||||
setEndInfo()
|
||||
|
||||
proc parseConstant(p: var Parser): PNode =
|
||||
#| constant = (varTuple / identWithPragma) (colon typeDesc)? '=' optInd expr indAndComment
|
||||
@@ -2271,6 +2324,7 @@ proc parseConstant(p: var Parser): PNode =
|
||||
result.add(parseExpr(p))
|
||||
result[^1] = postExprBlocks(p, result[^1])
|
||||
indAndComment(p, result)
|
||||
setEndInfo()
|
||||
|
||||
proc parseBind(p: var Parser, k: TNodeKind): PNode =
|
||||
#| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
|
||||
@@ -2286,6 +2340,7 @@ proc parseBind(p: var Parser, k: TNodeKind): PNode =
|
||||
getTok(p)
|
||||
optInd(p, a)
|
||||
#expectNl(p)
|
||||
setEndInfo()
|
||||
|
||||
proc parseStmtPragma(p: var Parser): PNode =
|
||||
#| pragmaStmt = pragma (':' COMMENT? stmt)?
|
||||
@@ -2297,6 +2352,7 @@ proc parseStmtPragma(p: var Parser): PNode =
|
||||
skipComment(p, result)
|
||||
result.add a
|
||||
result.add parseStmt(p)
|
||||
setEndInfo()
|
||||
|
||||
proc simpleStmt(p: var Parser): PNode =
|
||||
#| simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt
|
||||
@@ -2439,6 +2495,7 @@ proc parseStmt(p: var Parser): PNode =
|
||||
if p.tok.tokType != tkSemiColon: break
|
||||
getTok(p)
|
||||
if err and p.tok.tokType == tkEof: break
|
||||
setEndInfo()
|
||||
|
||||
proc parseAll(p: var Parser): 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)
|
||||
if p.tok.indent != 0:
|
||||
parMessage(p, errInvalidIndentation)
|
||||
setEndInfo()
|
||||
|
||||
proc checkFirstLineIndentation*(p: var Parser) =
|
||||
if p.tok.indent != 0 and p.tok.strongSpaceA:
|
||||
@@ -2487,6 +2545,7 @@ proc parseTopLevelStmt(p: var Parser): PNode =
|
||||
result = complexOrSimpleStmt(p)
|
||||
if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
||||
break
|
||||
setEndInfo()
|
||||
|
||||
proc parseString*(s: string; cache: IdentCache; config: ConfigRef;
|
||||
filename: string = ""; line: int = 0;
|
||||
@@ -2498,9 +2557,10 @@ proc parseString*(s: string; cache: IdentCache; config: ConfigRef;
|
||||
var stream = llStreamOpen(s)
|
||||
stream.lineOffset = line
|
||||
|
||||
var parser: Parser
|
||||
parser.lex.errorHandler = errorHandler
|
||||
openParser(parser, AbsoluteFile filename, stream, cache, config)
|
||||
var p: Parser
|
||||
p.lex.errorHandler = errorHandler
|
||||
openParser(p, AbsoluteFile filename, stream, cache, config)
|
||||
|
||||
result = parser.parseAll
|
||||
closeParser(parser)
|
||||
result = p.parseAll
|
||||
closeParser(p)
|
||||
setEndInfo()
|
||||
|
||||
@@ -980,6 +980,14 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy
|
||||
return errorNode(c, 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
|
||||
case callee.kind
|
||||
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")
|
||||
|
||||
proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
when defined(nimsuggest):
|
||||
if c.graph.config.expandDone():
|
||||
return n
|
||||
openScope(c)
|
||||
result = semExpr(c, n, expectedType = expectedType)
|
||||
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:
|
||||
if isCompilerDebug():
|
||||
echo ("<", c.config$n.info, n, ?.result.typ)
|
||||
|
||||
template directLiteral(typeKind: TTypeKind) =
|
||||
if result.typ == nil:
|
||||
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 = 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 nfSem in n.flags: return
|
||||
case n.kind
|
||||
@@ -3234,3 +3257,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
localError(c.config, n.info, "invalid expression: " &
|
||||
renderTree(n, {renderNoComments}))
|
||||
if result != nil: incl(result.flags, nfSem)
|
||||
|
||||
when defined(nimsuggest):
|
||||
if expandStarted:
|
||||
c.config.expandNodeResult = $result
|
||||
suggestQuit()
|
||||
|
||||
@@ -1450,6 +1450,9 @@ proc hasRealBody(s: PSym): bool =
|
||||
|
||||
proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
let g = c.graph
|
||||
when defined(nimsuggest):
|
||||
if g.config.expandDone():
|
||||
return
|
||||
var effects = s.typ.n[0]
|
||||
if effects.kind != nkEffectList: return
|
||||
# effects already computed?
|
||||
|
||||
@@ -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)
|
||||
|
||||
maybeAddResult(c, s, n)
|
||||
let resultType =
|
||||
let resultType =
|
||||
if s.kind == skMacro:
|
||||
sysTypeFromName(c.graph, n.info, "NimNode")
|
||||
elif not isInlineIterator(s.typ):
|
||||
|
||||
@@ -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;
|
||||
quality: range[0..100]; prefix: PrefixMatch;
|
||||
inTypeContext: bool; scope: int;
|
||||
useSuppliedInfo = false): Suggest =
|
||||
useSuppliedInfo = false,
|
||||
endLine: uint16 = 0,
|
||||
endCol = 0): Suggest =
|
||||
new(result)
|
||||
result.section = section
|
||||
result.quality = quality
|
||||
@@ -176,6 +178,8 @@ proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info
|
||||
else:
|
||||
getTokenLenFromSource(g.config, s.name.s, infox)
|
||||
result.version = g.config.suggestVersion
|
||||
result.endLine = endLine
|
||||
result.endCol = endCol
|
||||
|
||||
proc `$`*(suggest: Suggest): string =
|
||||
result = $suggest.section
|
||||
@@ -216,6 +220,12 @@ proc `$`*(suggest: Suggest): string =
|
||||
result.add(sep)
|
||||
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) =
|
||||
if not isNil(conf.suggestionResultHook):
|
||||
conf.suggestionResultHook(s)
|
||||
|
||||
@@ -2333,6 +2333,9 @@ const evalPass* = makePass(myOpen, myProcess, myClose)
|
||||
proc evalConstExprAux(module: PSym; idgen: IdGenerator;
|
||||
g: ModuleGraph; prc: PSym, n: PNode,
|
||||
mode: TEvalMode): PNode =
|
||||
when defined(nimsuggest):
|
||||
if g.config.expandDone():
|
||||
return n
|
||||
#if g.config.errorCounter > 0: return n
|
||||
let n = transformExpr(g, idgen, module, n)
|
||||
setupGlobalCtx(module, g, idgen)
|
||||
|
||||
@@ -27,7 +27,7 @@ import compiler / [options, commands, modules, sem,
|
||||
passes, passaux, msgs,
|
||||
sigmatch, ast,
|
||||
idents, modulegraphs, prefixmatches, lineinfos, cmdlinehelper,
|
||||
pathutils, condsyms]
|
||||
pathutils, condsyms, syntaxes]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/typedthreads
|
||||
@@ -88,7 +88,7 @@ var
|
||||
requests: Channel[string]
|
||||
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);
|
||||
|
||||
proc writelnToChannel(line: string) =
|
||||
@@ -140,6 +140,9 @@ proc sexp(s: Suggest): SexpNode =
|
||||
])
|
||||
if s.section == ideSug:
|
||||
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 =
|
||||
result = newSList()
|
||||
@@ -175,12 +178,23 @@ proc symFromInfo(graph: ModuleGraph; trackPos: TLineInfo): PSym =
|
||||
if m != nil and m.ast != nil:
|
||||
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) =
|
||||
let conf = graph.config
|
||||
|
||||
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
|
||||
|
||||
myLog("cmd: " & $cmd & ", file: " & file.string &
|
||||
@@ -219,7 +233,10 @@ proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
|
||||
else:
|
||||
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) =
|
||||
if cmd == ideChk:
|
||||
graph.config.structuredErrorHook = errorHook
|
||||
@@ -227,7 +244,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
|
||||
else:
|
||||
graph.config.structuredErrorHook = nil
|
||||
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;
|
||||
graph: ModuleGraph) =
|
||||
@@ -238,7 +255,7 @@ proc executeEpc(cmd: IdeCmd, args: SexpNode;
|
||||
var dirtyfile = AbsoluteFile""
|
||||
if len(args) > 3:
|
||||
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,
|
||||
returnSymbol = "return") =
|
||||
@@ -459,6 +476,7 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
|
||||
of "changed": conf.ideCmd = ideChanged
|
||||
of "globalsymbols": conf.ideCmd = ideGlobalSymbols
|
||||
of "declaration": conf.ideCmd = ideDeclaration
|
||||
of "expand": conf.ideCmd = ideExpand
|
||||
of "chkfile": conf.ideCmd = ideChkFile
|
||||
of "recompile": conf.ideCmd = ideRecompile
|
||||
of "type": conf.ideCmd = ideType
|
||||
@@ -478,6 +496,7 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
|
||||
i += parseInt(cmd, line, i)
|
||||
i += skipWhile(cmd, seps, i)
|
||||
i += parseInt(cmd, col, i)
|
||||
let tag = substr(cmd, i)
|
||||
|
||||
if conf.ideCmd == ideKnown:
|
||||
results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, AbsoluteFile orig))))
|
||||
@@ -486,18 +505,9 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
|
||||
else:
|
||||
if conf.ideCmd == ideChk:
|
||||
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()
|
||||
|
||||
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) =
|
||||
benchmark "Recompilation(clean)":
|
||||
graph.resetForBackend()
|
||||
@@ -509,9 +519,9 @@ proc recompileFullProject(graph: ModuleGraph) =
|
||||
|
||||
proc mainThread(graph: ModuleGraph) =
|
||||
let conf = graph.config
|
||||
if gLogging:
|
||||
for it in conf.searchPaths:
|
||||
log(it.string)
|
||||
myLog "searchPaths: "
|
||||
for it in conf.searchPaths:
|
||||
myLog(" " & it.string)
|
||||
|
||||
proc wrHook(line: string) {.closure.} =
|
||||
if gMode == mepc:
|
||||
@@ -732,16 +742,20 @@ func deduplicateSymInfoPair[SymInfoPair](xs: seq[SymInfoPair]): seq[SymInfoPair]
|
||||
result.add(itm)
|
||||
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):
|
||||
ref SymInfoPair =
|
||||
let
|
||||
fileIdx = fileInfoIdx(graph.config, file)
|
||||
trackPos = newLineInfo(fileIdx, line, col)
|
||||
for s in graph.fileSymbols(fileIdx).deduplicateSymInfoPair:
|
||||
if isTracked(s.info, trackPos, s.sym.name.s.len):
|
||||
new(result)
|
||||
result[] = s
|
||||
break
|
||||
result = findSymData(graph, trackPos)
|
||||
|
||||
proc markDirtyIfNeeded(graph: ModuleGraph, file: string, originalFileIdx: FileIndex) =
|
||||
let sha = $sha1.secureHashFile(file)
|
||||
@@ -752,7 +766,8 @@ proc markDirtyIfNeeded(graph: ModuleGraph, file: string, originalFileIdx: FileIn
|
||||
else:
|
||||
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:
|
||||
defaultSection
|
||||
elif sym.info.exactEquals(info):
|
||||
@@ -760,7 +775,8 @@ proc suggestResult(graph: ModuleGraph, sym: PSym, info: TLineInfo, defaultSectio
|
||||
else:
|
||||
ideUse
|
||||
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)
|
||||
|
||||
const
|
||||
@@ -771,7 +787,75 @@ proc symbolEqual(left, right: PSym): bool =
|
||||
# More relaxed symbol comparison
|
||||
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) =
|
||||
let conf = graph.config
|
||||
conf.writelnHook = proc (s: string) = discard
|
||||
@@ -783,7 +867,7 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
|
||||
|
||||
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
|
||||
|
||||
@@ -811,7 +895,7 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
|
||||
graph.unmarkAllDirty()
|
||||
|
||||
# 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):
|
||||
# for ideSug use v2 implementation
|
||||
if cmd == ideSug:
|
||||
@@ -861,16 +945,8 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
|
||||
# future calls.
|
||||
graph.markDirtyIfNeeded(file.string, fileIndex)
|
||||
of ideOutline:
|
||||
let
|
||||
module = graph.getModule fileIndex
|
||||
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)
|
||||
let n = parseFile(fileIndex, graph.cache, graph.config)
|
||||
graph.iterateOutlineNodes(n, graph.fileSymbols(fileIndex).deduplicateSymInfoPair)
|
||||
of ideChk:
|
||||
myLog fmt "Reporting errors for {graph.suggestErrors.len} file(s)"
|
||||
for sug in graph.suggestErrorsIter:
|
||||
@@ -933,6 +1009,39 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
|
||||
else:
|
||||
# we are on definition or usage, look for declaration
|
||||
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:
|
||||
myLog fmt "Discarding {cmd}"
|
||||
|
||||
@@ -1018,9 +1127,9 @@ else:
|
||||
if self.loadConfigsAndProcessCmdLine(cache, conf, graph):
|
||||
mockCommand(graph)
|
||||
if gLogging:
|
||||
log("Search paths:")
|
||||
myLog("Search paths:")
|
||||
for it in conf.searchPaths:
|
||||
log(" " & it.string)
|
||||
myLog(" " & it.string)
|
||||
|
||||
retval.doStopCompile = proc (): bool = false
|
||||
return NimSuggest(graph: retval, idle: 0, cachedMsgs: @[])
|
||||
|
||||
@@ -66,7 +66,7 @@ proc parseTest(filename: string; epcMode=false): Test =
|
||||
elif x.startsWith(">"):
|
||||
# since 'markers' here are not complete yet, we do the $substitutions
|
||||
# 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:
|
||||
# expected output line:
|
||||
let x = x % ["file", filename, "lib", libpath]
|
||||
@@ -218,7 +218,12 @@ proc sexpToAnswer(s: SexpNode): string =
|
||||
result.add doc
|
||||
result.add '\t'
|
||||
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 a[9].getStr
|
||||
result.add '\L'
|
||||
@@ -229,8 +234,8 @@ proc doReport(filename, answer, resp: string; report: var string) =
|
||||
var hasDiff = false
|
||||
for i in 0..min(resp.len-1, answer.len-1):
|
||||
if resp[i] != answer[i]:
|
||||
report.add "\n Expected: " & resp.substr(i, i+200)
|
||||
report.add "\n But got: " & answer.substr(i, i+200)
|
||||
report.add "\n Expected:\n" & resp
|
||||
report.add "\n But got:\n" & answer
|
||||
hasDiff = true
|
||||
break
|
||||
if not hasDiff:
|
||||
@@ -342,8 +347,8 @@ proc main() =
|
||||
if os.paramCount() > 0:
|
||||
let x = os.paramStr(1)
|
||||
let xx = expandFilename x
|
||||
# run only stdio when running single test
|
||||
failures += runTest(xx)
|
||||
failures += runEpcTest(xx)
|
||||
else:
|
||||
let files = toSeq(walkFiles(tpath / "t*.nim"))
|
||||
for i, x in files:
|
||||
|
||||
@@ -16,10 +16,6 @@ def skField tv3.Foo.bar string $file 5 4 "" 100
|
||||
use skField tv3.Foo.bar string $file 8 9 "" 100
|
||||
>def $1
|
||||
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 skField bar string $file 5 4 "" 100 Prefix
|
||||
>globalSymbols test
|
||||
|
||||
45
nimsuggest/tests/tv3_outline.nim
Normal file
45
nimsuggest/tests/tv3_outline.nim
Normal 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
|
||||
"""
|
||||
Reference in New Issue
Block a user