mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-17 21:12:42 +00:00
nimsuggest supports prefix matching (first version)
This commit is contained in:
@@ -519,12 +519,17 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
|
||||
recoverContext(c)
|
||||
c.inGenericInst = oldInGenericInst
|
||||
msgs.setInfoContextLen(oldContextLen)
|
||||
if getCurrentException() of ESuggestDone: result = nil
|
||||
else: result = ast.emptyNode
|
||||
if getCurrentException() of ESuggestDone:
|
||||
c.suggestionsMade = true
|
||||
result = nil
|
||||
else:
|
||||
result = ast.emptyNode
|
||||
#if gCmd == cmdIdeTools: findSuggest(c, n)
|
||||
|
||||
proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
|
||||
var c = PContext(context)
|
||||
if gCmd == cmdIdeTools and not c.suggestionsMade:
|
||||
suggestSentinel(c)
|
||||
closeScope(c) # close module's scope
|
||||
rawCloseScope(c) # imported symbols; don't check for unused ones!
|
||||
result = newNode(nkStmtList)
|
||||
|
||||
@@ -111,6 +111,7 @@ type
|
||||
graph*: ModuleGraph
|
||||
signatures*: TStrTable
|
||||
recursiveDep*: string
|
||||
suggestionsMade*: bool
|
||||
|
||||
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
|
||||
result.genericSym = s
|
||||
|
||||
@@ -8,6 +8,20 @@
|
||||
#
|
||||
|
||||
## This file implements features required for IDE support.
|
||||
##
|
||||
## Due to Nim's natures and the fact that ``system.nim`` is always imported,
|
||||
## there are lots of potential symbols. Furthermore thanks to templates and
|
||||
## macros even context based analysis does not help much: In a context like
|
||||
## ``let x: |`` where a type has to follow, that type might be constructed from
|
||||
## a template like ``extractField(MyObject, fieldName)``. We deal with this
|
||||
## problem by smart sorting so that the likely symbols come first. This sorting
|
||||
## is done this way:
|
||||
##
|
||||
## - If there is a prefix (foo|), symbols starting with this prefix come first.
|
||||
## - If the prefix is part of the name (but the name doesn't start with it),
|
||||
## these symbols come second.
|
||||
## - Otherwise consider the context. We currently distinguish between type
|
||||
## and non-type contexts.
|
||||
|
||||
# included from sigmatch.nim
|
||||
|
||||
@@ -133,11 +147,20 @@ proc suggestResult(s: Suggest) =
|
||||
else:
|
||||
suggestWriteln($s)
|
||||
|
||||
proc filterSym(s: PSym): bool {.inline.} =
|
||||
result = s.kind != skModule
|
||||
proc filterSym(s: PSym; prefix: PNode): bool {.inline.} =
|
||||
proc prefixMatch(s: PSym; n: PNode): bool =
|
||||
case n.kind
|
||||
of nkIdent: result = s.name.s.startsWith(n.ident.s)
|
||||
of nkSym: result = s.name.s.startsWith(n.sym.name.s)
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkAccQuoted:
|
||||
if n.len > 0:
|
||||
result = prefixMatch(s, n[0])
|
||||
else: discard
|
||||
if s.kind != skModule:
|
||||
result = prefix.isNil or prefixMatch(s, prefix)
|
||||
|
||||
proc filterSymNoOpr(s: PSym): bool {.inline.} =
|
||||
result = s.kind != skModule and s.name.s[0] in lexer.SymChars and
|
||||
proc filterSymNoOpr(s: PSym; prefix: PNode): bool {.inline.} =
|
||||
result = filterSym(s, prefix) and s.name.s[0] in lexer.SymChars and
|
||||
not isKeyword(s.name)
|
||||
|
||||
proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
|
||||
@@ -148,8 +171,8 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
|
||||
result = true
|
||||
break
|
||||
|
||||
proc suggestField(c: PContext, s: PSym, outputs: var int) =
|
||||
if filterSym(s) and fieldVisible(c, s):
|
||||
proc suggestField(c: PContext, s: PSym; f: PNode; outputs: var int) =
|
||||
if filterSym(s, f) and fieldVisible(c, s):
|
||||
suggestResult(symToSuggest(s, isLocal=true, $ideSug, 100))
|
||||
inc outputs
|
||||
|
||||
@@ -166,22 +189,22 @@ template wholeSymTab(cond, section: untyped) =
|
||||
suggestResult(symToSuggest(it, isLocal = isLocal, section, 100))
|
||||
inc outputs
|
||||
|
||||
proc suggestSymList(c: PContext, list: PNode, outputs: var int) =
|
||||
proc suggestSymList(c: PContext, list, f: PNode, outputs: var int) =
|
||||
for i in countup(0, sonsLen(list) - 1):
|
||||
if list.sons[i].kind == nkSym:
|
||||
suggestField(c, list.sons[i].sym, outputs)
|
||||
suggestField(c, list.sons[i].sym, f, outputs)
|
||||
#else: InternalError(list.info, "getSymFromList")
|
||||
|
||||
proc suggestObject(c: PContext, n: PNode, outputs: var int) =
|
||||
proc suggestObject(c: PContext, n, f: PNode, outputs: var int) =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], outputs)
|
||||
for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], f, outputs)
|
||||
of nkRecCase:
|
||||
var L = sonsLen(n)
|
||||
if L > 0:
|
||||
suggestObject(c, n.sons[0], outputs)
|
||||
for i in countup(1, L-1): suggestObject(c, lastSon(n.sons[i]), outputs)
|
||||
of nkSym: suggestField(c, n.sym, outputs)
|
||||
suggestObject(c, n.sons[0], f, outputs)
|
||||
for i in countup(1, L-1): suggestObject(c, lastSon(n.sons[i]), f, outputs)
|
||||
of nkSym: suggestField(c, n.sym, f, outputs)
|
||||
else: discard
|
||||
|
||||
proc nameFits(c: PContext, s: PSym, n: PNode): bool =
|
||||
@@ -205,7 +228,7 @@ proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
|
||||
result = false
|
||||
|
||||
proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) =
|
||||
wholeSymTab(filterSym(it) and nameFits(c, it, n) and argsFit(c, it, n, nOrig),
|
||||
wholeSymTab(filterSym(it, nil) and nameFits(c, it, n) and argsFit(c, it, n, nOrig),
|
||||
$ideCon)
|
||||
|
||||
proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
|
||||
@@ -221,22 +244,22 @@ proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
|
||||
if exp.kind in {tyExpr, tyStmt, tyGenericParam, tyAnything}: return
|
||||
result = sigmatch.argtypeMatches(c, s.typ.sons[1], firstArg)
|
||||
|
||||
proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) =
|
||||
proc suggestOperations(c: PContext, n, f: PNode, typ: PType, outputs: var int) =
|
||||
assert typ != nil
|
||||
wholeSymTab(filterSymNoOpr(it) and typeFits(c, it, typ), $ideSug)
|
||||
wholeSymTab(filterSymNoOpr(it, f) and typeFits(c, it, typ), $ideSug)
|
||||
|
||||
proc suggestEverything(c: PContext, n: PNode, outputs: var int) =
|
||||
proc suggestEverything(c: PContext, n, f: PNode, outputs: var int) =
|
||||
# do not produce too many symbols:
|
||||
var isLocal = true
|
||||
for scope in walkScopes(c.currentScope):
|
||||
if scope == c.topLevelScope: isLocal = false
|
||||
for it in items(scope.symbols):
|
||||
if filterSym(it):
|
||||
if filterSym(it, f):
|
||||
suggestResult(symToSuggest(it, isLocal = isLocal, $ideSug, 0))
|
||||
inc outputs
|
||||
if scope == c.topLevelScope: break
|
||||
if scope == c.topLevelScope and f.isNil: break
|
||||
|
||||
proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
|
||||
proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var int) =
|
||||
# special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but
|
||||
# ``myObj``.
|
||||
var typ = n.typ
|
||||
@@ -252,7 +275,7 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
|
||||
if m == nil: typ = nil
|
||||
else:
|
||||
for it in items(n.sym.tab):
|
||||
if filterSym(it):
|
||||
if filterSym(it, field):
|
||||
suggestResult(symToSuggest(it, isLocal=false, $ideSug, 100))
|
||||
inc outputs
|
||||
suggestResult(symToSuggest(m, isLocal=false, $ideMod, 100))
|
||||
@@ -263,38 +286,38 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
|
||||
if n.sym == c.module:
|
||||
# all symbols accessible, because we are in the current module:
|
||||
for it in items(c.topLevelScope.symbols):
|
||||
if filterSym(it):
|
||||
if filterSym(it, field):
|
||||
suggestResult(symToSuggest(it, isLocal=false, $ideSug, 100))
|
||||
inc outputs
|
||||
else:
|
||||
for it in items(n.sym.tab):
|
||||
if filterSym(it):
|
||||
if filterSym(it, field):
|
||||
suggestResult(symToSuggest(it, isLocal=false, $ideSug, 100))
|
||||
inc outputs
|
||||
else:
|
||||
# fallback:
|
||||
suggestEverything(c, n, outputs)
|
||||
suggestEverything(c, n, field, outputs)
|
||||
elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType:
|
||||
# look up if the identifier belongs to the enum:
|
||||
var t = typ
|
||||
while t != nil:
|
||||
suggestSymList(c, t.n, outputs)
|
||||
suggestSymList(c, t.n, field, outputs)
|
||||
t = t.sons[0]
|
||||
suggestOperations(c, n, typ, outputs)
|
||||
suggestOperations(c, n, field, typ, outputs)
|
||||
else:
|
||||
let orig = skipTypes(typ, {tyGenericInst, tyAlias})
|
||||
typ = skipTypes(typ, {tyGenericInst, tyVar, tyPtr, tyRef, tyAlias})
|
||||
if typ.kind == tyObject:
|
||||
var t = typ
|
||||
while true:
|
||||
suggestObject(c, t.n, outputs)
|
||||
suggestObject(c, t.n, field, outputs)
|
||||
if t.sons[0] == nil: break
|
||||
t = skipTypes(t.sons[0], skipPtrs)
|
||||
elif typ.kind == tyTuple and typ.n != nil:
|
||||
suggestSymList(c, typ.n, outputs)
|
||||
suggestOperations(c, n, orig, outputs)
|
||||
suggestSymList(c, typ.n, field, outputs)
|
||||
suggestOperations(c, n, field, orig, outputs)
|
||||
if typ != orig:
|
||||
suggestOperations(c, n, typ, outputs)
|
||||
suggestOperations(c, n, field, typ, outputs)
|
||||
|
||||
type
|
||||
TCheckPointResult* = enum
|
||||
@@ -443,13 +466,19 @@ proc suggestExpr*(c: PContext, node: PNode) =
|
||||
if n == nil: n = node
|
||||
if n.kind == nkDotExpr:
|
||||
var obj = safeSemExpr(c, n.sons[0])
|
||||
suggestFieldAccess(c, obj, outputs)
|
||||
let prefix = if n.len == 2: n[1] else: nil
|
||||
suggestFieldAccess(c, obj, prefix, outputs)
|
||||
|
||||
#if optIdeDebug in gGlobalOptions:
|
||||
# echo "expression ", renderTree(obj), " has type ", typeToString(obj.typ)
|
||||
#writeStackTrace()
|
||||
else:
|
||||
suggestEverything(c, n, outputs)
|
||||
#let m = findClosestSym(node)
|
||||
#if m != nil:
|
||||
# suggestPrefix(c, m, outputs)
|
||||
#else:
|
||||
let prefix = if cp == cpExact: n else: nil
|
||||
suggestEverything(c, n, prefix, outputs)
|
||||
|
||||
elif gIdeCmd == ideCon:
|
||||
var n = findClosestCall(node)
|
||||
@@ -471,3 +500,17 @@ proc suggestExpr*(c: PContext, node: PNode) =
|
||||
|
||||
proc suggestStmt*(c: PContext, n: PNode) =
|
||||
suggestExpr(c, n)
|
||||
|
||||
proc suggestSentinel*(c: PContext) =
|
||||
if gIdeCmd != ideSug or c.module.position != gTrackPos.fileIndex: return
|
||||
if c.compilesContextId > 0: return
|
||||
inc(c.compilesContextId)
|
||||
# suggest everything:
|
||||
var isLocal = true
|
||||
for scope in walkScopes(c.currentScope):
|
||||
if scope == c.topLevelScope: isLocal = false
|
||||
for it in items(scope.symbols):
|
||||
if filterSymNoOpr(it, nil):
|
||||
suggestResult(symToSuggest(it, isLocal = isLocal, $ideSug, 0))
|
||||
|
||||
dec(c.compilesContextId)
|
||||
|
||||
Reference in New Issue
Block a user