diff options
-rw-r--r-- | compiler/ast.nim | 8 | ||||
-rw-r--r-- | compiler/lexer.nim | 6 | ||||
-rw-r--r-- | compiler/options.nim | 14 | ||||
-rw-r--r-- | compiler/parser.nim | 70 | ||||
-rw-r--r-- | compiler/semexprs.nim | 30 | ||||
-rw-r--r-- | compiler/sempass2.nim | 3 | ||||
-rw-r--r-- | compiler/semstmts.nim | 2 | ||||
-rw-r--r-- | compiler/suggest.nim | 12 | ||||
-rw-r--r-- | compiler/vm.nim | 3 | ||||
-rw-r--r-- | nimsuggest/nimsuggest.nim | 193 | ||||
-rw-r--r-- | nimsuggest/tester.nim | 15 | ||||
-rw-r--r-- | nimsuggest/tests/tv3.nim | 4 | ||||
-rw-r--r-- | nimsuggest/tests/tv3_outline.nim | 45 |
13 files changed, 345 insertions, 60 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index aa0ca98c9..aa70560ed 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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 diff --git a/compiler/lexer.nim b/compiler/lexer.nim index ec27add8a..a62d40e54 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -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 diff --git a/compiler/options.nim b/compiler/options.nim index fc970e420..8c477f0a5 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -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" diff --git a/compiler/parser.nim b/compiler/parser.nim index 0c8edd2a8..14882d415 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -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() diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 612af482a..7454c7f8e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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() diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 747540c78..e989eb4a6 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -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? diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 96883255c..c7e48db4c 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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): diff --git a/compiler/suggest.nim b/compiler/suggest.nim index e0b0fb516..637010ad5 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -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) diff --git a/compiler/vm.nim b/compiler/vm.nim index 4f5f8485b..424685e40 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -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) diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index c5e015ce9..a0a3b8e82 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -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,17 +742,21 @@ func deduplicateSymInfoPair[SymInfoPair](xs: seq[SymInfoPair]): seq[SymInfoPair] result.add(itm) result.reverse() -proc findSymData(graph: ModuleGraph, file: AbsoluteFile; line, col: int): +proc findSymData(graph: ModuleGraph, trackPos: TLineInfo): ref SymInfoPair = - let - fileIdx = fileInfoIdx(graph.config, file) - trackPos = newLineInfo(fileIdx, line, col) - for s in graph.fileSymbols(fileIdx).deduplicateSymInfoPair: + 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) + result = findSymData(graph, trackPos) + proc markDirtyIfNeeded(graph: ModuleGraph, file: string, originalFileIdx: FileIndex) = let sha = $sha1.secureHashFile(file) if graph.config.m.fileInfos[originalFileIdx.int32].hash != sha or graph.config.ideCmd == ideSug: @@ -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: @[]) diff --git a/nimsuggest/tester.nim b/nimsuggest/tester.nim index 6e068e067..060335959 100644 --- a/nimsuggest/tester.nim +++ b/nimsuggest/tester.nim @@ -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: diff --git a/nimsuggest/tests/tv3.nim b/nimsuggest/tests/tv3.nim index 9d8b1ef2d..fd736a1d8 100644 --- a/nimsuggest/tests/tv3.nim +++ b/nimsuggest/tests/tv3.nim @@ -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 diff --git a/nimsuggest/tests/tv3_outline.nim b/nimsuggest/tests/tv3_outline.nim new file mode 100644 index 000000000..6370948d9 --- /dev/null +++ b/nimsuggest/tests/tv3_outline.nim @@ -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 +""" |