summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2017-02-28 09:03:40 +0100
committerAraq <rumpf_a@web.de>2017-02-28 09:03:40 +0100
commit3d46600a902bae72b63e855d797e774cc4785d74 (patch)
tree2456d11d36f413e2e7bf9a8fd946e9e8c4f92344 /compiler
parente78bd69619289eb8a981a0d33f17a1ca0e233bee (diff)
downloadNim-3d46600a902bae72b63e855d797e774cc4785d74.tar.gz
nimsuggest supports prefix matching (first version)
Diffstat (limited to 'compiler')
-rw-r--r--compiler/sem.nim9
-rw-r--r--compiler/semdata.nim1
-rw-r--r--compiler/suggest.nim109
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)