Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

suggest: refactoring outline to parser based #919

Closed
wants to merge 12 commits into from
2 changes: 2 additions & 0 deletions compiler/ast/ast_parsed_types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,9 @@ const
pnkIntKinds* = {pnkCharLit..pnkUInt64Lit}
pnkStrKinds* = {pnkStrLit..pnkTripleStrLit}
pnkDeclarativeDefs* = {pnkProcDef, pnkFuncDef, pnkMethodDef, pnkIteratorDef, pnkConverterDef}
pnkLambdaKinds* = {pnkLambda, pnkDo}
pnkRoutineDefs* = pnkDeclarativeDefs + {pnkMacroDef, pnkTemplateDef}
pnkDeclarations* = pnkRoutineDefs + {pnkIdentDefs, pnkConstDef, pnkTypeDef, pnkVarTuple}

func len*(node: ParsedNode): int =
## Number of sons of the parsed node
Expand Down
4 changes: 4 additions & 0 deletions compiler/front/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ type

CfileList* = seq[Cfile]

SuggestFlag* {.pure.} = enum
deprecated = 1

Suggest* = ref object
section*: IdeCmd
qualifiedPath*: seq[string]
Expand All @@ -158,6 +161,7 @@ type
scope*:int
localUsages*, globalUsages*: int # usage counters
tokenLen*: int
flags*: set[SuggestFlag]

Suggestions* = seq[Suggest]

Expand Down
132 changes: 117 additions & 15 deletions compiler/tools/suggest.nim
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,13 @@ import
compiler/ast/[
ast,
astalgo,
ast_parsed_types,
ast_types,
lexer,
lineinfos,
linter,
parser,
syntaxes,
types,
typesrenderer,
wordrecg,
Expand All @@ -67,7 +71,8 @@ import
prefixmatches,
astrepr,
debugutils,
pathutils
pathutils,
idioms,
]


Expand Down Expand Up @@ -477,6 +482,116 @@ proc findTrackedSym*(g: ModuleGraph;): PSym =
# xxx: the node finding should be specialized per symbol kind
result = findTrackedNode(m.ast, g.config.m.trackPos)

proc getSymNode(node: ParsedNode): ParsedNode =
case node.kind
of pnkPostfix: node[^1]
of pnkPragmaExpr: getSymNode(node[0])
of pnkIdent, pnkAccQuoted: node
else: unreachable(node.kind)

proc pnkToSymKind(kind: ParsedNodeKind): TSymKind =
case kind
of pnkConstSection, pnkConstDef: skConst
of pnkLetSection: skLet
of pnkVarSection: skVar
of pnkProcDef: skProc
of pnkFuncDef: skFunc
of pnkMethodDef: skMethod
of pnkConverterDef: skConverter
of pnkIteratorDef: skIterator
of pnkMacroDef: skMacro
of pnkTemplateDef: skTemplate
of pnkTypeDef, pnkTypeSection: skType
else: skUnknown

proc getName(node: ParsedNode): string =
if node.kind == pnkIdent:
result = node.startToken.ident.s
elif node.kind == pnkAccQuoted:
result = "`"
for t in node.idents:
result.add t.ident.s
result.add "`"

proc processFlags(sug: Suggest; n: ParsedNode) =
var
identDeprecated: bool
colonDeprecated: bool
for s in n.sons:
identDeprecated = s.kind == pnkIdent and getName(s) == "deprecated"
colonDeprecated = s.kind == pnkExprColonExpr and getName(s[0]) == "deprecated"
if identDeprecated or colonDeprecated:
sug.flags.incl SuggestFlag.deprecated

proc parsedNodeToSugget(n: ParsedNode; originKind: ParsedNodeKind; module: string): Suggest =
if n.kind in {pnkError, pnkEmpty}: return
if n.kind notin pnkDeclarations: return
new(result)
var
token = getToken(n)
name = ""

if n.kind in pnkRoutineDefs and n[pragmasPos].kind == pnkPragma:
processFlags(result, n[pragmasPos])
elif n[0].kind == pnkPragmaExpr and n[0][^1].kind == pnkPragma:
processFlags(result, n[0][^1])

if n.kind != pnkVarTuple:
var node: ParsedNode = getSymNode(n[0])
token = getToken(node)
if node.kind != pnkError:
name = getName(node)
when false:
if n.kind in pnkRoutineDefs and node.kind == pnkAccQuoted:
let identsLen = n[paramsPos].sons.len
for i in countup(1, identsLen - 1):
name.add getName(n[paramsPos][i][1])
if i != identsLen - 1:
name.add ","
else:
name.add "("
for c in n.items:
if c.kind == pnkEmpty: break
name.add getName(c) & ","
name.add ")"

if name != "":
result.qualifiedPath = @[module, name]
result.line = token.line.int
result.column = token.col.int
result.tokenLen = name.len
result.symkind = byte pnkToSymKind(originKind)

proc outline*(graph: ModuleGraph; fileIdx: FileIndex) =
let conf = graph.config
var parser: Parser
var sug: Suggest
var parsedNode: ParsedNode
let name = splitFile(AbsoluteFile toFilename(conf, fileIdx)).name

const Sections = {pnkTypeSection, pnkConstSection, pnkLetSection, pnkVarSection}
template suggestIt(parsedNode: ParsedNode; originKind: ParsedNodeKind) =
sug = parsedNodeToSugget(parsedNode, originKind, name)
if sug != nil:
sug.filePath = toFullPath(conf, fileIdx)
sug.forth = ""
sug.section = ideOutline
sug.quality = 100
conf.suggestionResultHook(sug)

if setupParser(parser, fileIdx, graph.cache, conf):
while true:
parsedNode = parser.parseTopLevelStmt()
case parsedNode.kind
of pnkEmpty:
break
of Sections:
for node in parsedNode.sons:
suggestIt(node, parsedNode.kind)
else:
suggestIt(parsedNode, parsedNode.kind)
closeParser(parser)

proc executeCmd*(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
graph: ModuleGraph) =
## executes the given suggest command, `cmd`, for a given `file`, at the
Expand All @@ -489,7 +604,7 @@ proc executeCmd*(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;

if dirtyfile.isEmpty: msgs.setDirtyFile(conf, dirtyIdx, AbsoluteFile"")
else: msgs.setDirtyFile(conf, dirtyIdx, dirtyfile)

if cmd == ideOutline: return
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be better if the logic above is moved into a separate procedure (e.g., markDirtyFile), with executeCmd only being called if cmd is not ideOutline.

conf.m.trackPos = newLineInfo(dirtyIdx, line, col)
conf.m.trackPosAttached = false
conf.errorCounter = 0
Expand Down Expand Up @@ -555,19 +670,6 @@ proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; i
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
elif conf.ideCmd == ideHighlight and info.fileIndex == conf.m.trackPos.fileIndex:
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
elif conf.ideCmd == ideOutline and isDecl:
# if a module is included then the info we have is inside the include and
# we need to walk up the owners until we find the outer most module,
# which will be the last skModule prior to an skPackage.
var
parentFileIndex = info.fileIndex # assume we're in the correct module
parentModule = s.owner
while parentModule != nil and parentModule.kind == skModule:
parentFileIndex = parentModule.info.fileIndex
parentModule = parentModule.owner

if parentFileIndex == conf.m.trackPos.fileIndex:
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))

proc safeSemExpr*(c: PContext, n: PNode): PNode =
# use only for idetools support!
Expand Down
9 changes: 7 additions & 2 deletions nimsuggest/nimsuggest.nim
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ from compiler/ast/reports import Report,

from compiler/front/main import customizeForBackend

from compiler/tools/suggest import findTrackedSym, executeCmd, listUsages, suggestSym, `$`
from compiler/tools/suggest import findTrackedSym, executeCmd, listUsages, outline, suggestSym, `$`

when defined(windows):
import winlean
Expand Down Expand Up @@ -216,12 +216,17 @@ proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
)

executeCmd(cmd, file, dirtyfile, line, col, graph)
if conf.ideCmd in {ideUse, ideDus}:
case conf.ideCmd
of ideUse, ideDus:
let u = graph.findTrackedSym()
if u != nil:
listUsages(graph, u)
else:
localReport(conf, conf.m.trackPos, reportSem(rsemSugNoSymbolAtPosition))
of ideOutline:
let dirtyIdx = fileInfoIdx(conf, file)
outline(graph, dirtyIdx)
else: discard

proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
graph: ModuleGraph) =
Expand Down
1 change: 1 addition & 0 deletions nimsuggest/tests/tinclude.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ proc go() =
go()

discard """
disabled:true
$nimsuggest --tester $file
>def $path/tinclude.nim:7:14
def;;skProc;;minclude_import.create;;proc (greeting: string, subject: string): Greet{.noSideEffect, gcsafe, locks: 0.};;*fixtures/minclude_include.nim;;3;;5;;"";;100
Expand Down
34 changes: 34 additions & 0 deletions nimsuggest/tests/toutline.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import std/[os, pathutils]
const
explicitSourcePath {.strdefine.} = getCurrentCompilerExe().parentDir.parentDir
(dir, name, ext) = splitFile(currentSourcePath)

type
TOutlineKind = enum
kind1,
kind2
TOutline = object
kind: TOutlineKind


# TODO: tester handle backtick proc `$`(k: TOutlineKind): string = discard

iterator xrange(fromm, to: int, step = 1): int = discard

template tmpa() = discard
macro tmpb() = discard
converter tmpc() = discard

discard """
$nimsuggest --tester $file
>outline $path/toutline.nim
outline;;skConst;;toutline.explicitSourcePath;;;;$file;;3;;2;;"";;100
outline;;skConst;;toutline.(dir,name,ext,);;;;$file;;4;;2;;"";;100
outline;;skType;;toutline.TOutlineKind;;;;$file;;7;;2;;"";;100
outline;;skType;;toutline.TOutline;;;;$file;;10;;2;;"";;100

outline;;skIterator;;toutline.xrange;;;;$file;;16;;9;;"";;100
outline;;skTemplate;;toutline.tmpa;;;;$file;;18;;9;;"";;100
outline;;skMacro;;toutline.tmpb;;;;$file;;19;;6;;"";;100
outline;;skConverter;;toutline.tmpc;;;;$file;;20;;10;;"";;100
"""