diff options
author | Araq <rumpf_a@web.de> | 2017-02-28 09:03:40 +0100 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2017-02-28 09:03:40 +0100 |
commit | 3d46600a902bae72b63e855d797e774cc4785d74 (patch) | |
tree | 2456d11d36f413e2e7bf9a8fd946e9e8c4f92344 /compiler | |
parent | e78bd69619289eb8a981a0d33f17a1ca0e233bee (diff) | |
download | Nim-3d46600a902bae72b63e855d797e774cc4785d74.tar.gz |
nimsuggest supports prefix matching (first version)
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/sem.nim | 9 | ||||
-rw-r--r-- | compiler/semdata.nim | 1 | ||||
-rw-r--r-- | compiler/suggest.nim | 109 |
3 files changed, 84 insertions, 35 deletions
diff --git a/compiler/sem.nim b/compiler/sem.nim index 8da5d4707..21a5c435a 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -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) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 77d461007..f9a7c0cda 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -111,6 +111,7 @@ type graph*: ModuleGraph signatures*: TStrTable recursiveDep*: string + suggestionsMade*: bool proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = result.genericSym = s diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 9ae427583..7366b7ffd 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -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 filterSymNoOpr(s: PSym): bool {.inline.} = - result = s.kind != skModule and s.name.s[0] in lexer.SymChars and +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; 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) |