summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim2
-rw-r--r--compiler/modules.nim2
-rw-r--r--compiler/msgs.nim31
-rw-r--r--compiler/options.nim11
-rw-r--r--compiler/sem.nim6
-rw-r--r--compiler/semexprs.nim5
-rw-r--r--compiler/sempass2.nim2
-rw-r--r--compiler/suggest.nim179
8 files changed, 167 insertions, 71 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 25958f580..e073ce3e1 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -819,6 +819,8 @@ type
     constraint*: PNode        # additional constraints like 'lit|result'; also
                               # misused for the codegenDecl pragma in the hope
                               # it won't cause problems
+    when defined(nimsuggest):
+      allUsages*: seq[TLineInfo]
 
   TTypeSeq* = seq[PType]
   TLockLevel* = distinct int16
diff --git a/compiler/modules.nim b/compiler/modules.nim
index 3893d377e..ef727e200 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -30,7 +30,7 @@ var
     ## XXX: we should implement recycling of file IDs
     ## if the user keeps renaming modules, the file IDs will keep growing
 
-proc getModule(fileIdx: int32): PSym =
+proc getModule*(fileIdx: int32): PSym =
   if fileIdx >= 0 and fileIdx < gCompiledModules.len:
     result = gCompiledModules[fileIdx]
 
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 7b44b4349..649e28cba 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -815,24 +815,33 @@ proc getMessageStr(msg: TMsgKind, arg: string): string =
 type
   TErrorHandling = enum doNothing, doAbort, doRaise
 
-proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
-  template quit =
-    if defined(debug) or msg == errInternal or hintStackTrace in gNotes:
-      if stackTraceAvailable() and isNil(writelnHook):
-        writeStackTrace()
-      else:
-        styledMsgWriteln(fgRed, "No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp " & options.command & " <file>")
-    quit 1
+proc quit(msg: TMsgKind) =
+  if defined(debug) or msg == errInternal or hintStackTrace in gNotes:
+    if stackTraceAvailable() and isNil(writelnHook):
+      writeStackTrace()
+    else:
+      styledMsgWriteln(fgRed, "No stack traceback available\n" &
+          "To create a stacktrace, rerun compilation with ./koch temp " &
+          options.command & " <file>")
+  quit 1
 
+proc log*(s: string) {.procvar.} =
+  var f: File
+  if open(f, "nimsuggest.log", fmAppend):
+    f.writeln(s)
+    close(f)
+
+proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
   if msg >= fatalMin and msg <= fatalMax:
-    quit()
+    if gCmd == cmdIdeTools: log(s)
+    quit(msg)
   if msg >= errMin and msg <= errMax:
     inc(gErrorCounter)
     options.gExitcode = 1'i8
     if gErrorCounter >= gErrorMax:
-      quit()
+      quit(msg)
     elif eh == doAbort and gCmd != cmdIdeTools:
-      quit()
+      quit(msg)
     elif eh == doRaise:
       raiseRecoverableError(s)
 
diff --git a/compiler/options.nim b/compiler/options.nim
index 6dd917ad4..f63757ac0 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -86,7 +86,8 @@ type                          # please make sure we have under 32 options
     gcNone, gcBoehm, gcGo, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
 
   IdeCmd* = enum
-    ideNone, ideSug, ideCon, ideDef, ideUse, ideDus
+    ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
+    ideHighlight, ideOutline
 
 var
   gIdeCmd*: IdeCmd
@@ -422,6 +423,10 @@ proc parseIdeCmd*(s: string): IdeCmd =
   of "def": ideDef
   of "use": ideUse
   of "dus": ideDus
+  of "chk": ideChk
+  of "mod": ideMod
+  of "highlight": ideHighlight
+  of "outline": ideOutline
   else: ideNone
 
 proc `$`*(c: IdeCmd): string =
@@ -431,4 +436,8 @@ proc `$`*(c: IdeCmd): string =
   of ideDef: "def"
   of ideUse: "use"
   of ideDus: "dus"
+  of ideChk: "chk"
+  of ideMod: "mod"
   of ideNone: "none"
+  of ideHighlight: "highlight"
+  of ideOutline: "outline"
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 041524f84..c6db6fbd3 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -61,8 +61,8 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
   when defined(nimsuggest):
     assert gCmd == cmdIdeTools
     if requiresCheck:
-      if optIdeDebug in gGlobalOptions:
-        echo "passing to safeSemExpr: ", renderTree(n)
+      #if optIdeDebug in gGlobalOptions:
+      #  echo "passing to safeSemExpr: ", renderTree(n)
       discard safeSemExpr(c, n)
 
 proc typeMismatch(n: PNode, formal, actual: PType) =
@@ -441,6 +441,8 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
   result = hloStmt(c, result)
   if gCmd == cmdInteractive and not isEmptyType(result.typ):
     result = buildEchoStmt(c, result)
+  if gCmd == cmdIdeTools:
+    appendToModule(c.module, result)
   result = transformStmt(c.module, result)
 
 proc recoverContext(c: PContext) =
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index f1016595a..58c42d410 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -2083,11 +2083,6 @@ proc semExport(c: PContext, n: PNode): PNode =
           x.add(newSymNode(s, a.info))
           strTableAdd(c.module.tab, s)
         s = nextOverloadIter(o, c, a)
-  when false:
-    if c.module.ast.isNil:
-      c.module.ast = newNodeI(nkStmtList, n.info)
-    assert c.module.ast.kind == nkStmtList
-    c.module.ast.add x
   result = n
 
 proc shouldBeBracketExpr(n: PNode): bool =
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index ef014963c..29cce9247 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -699,7 +699,7 @@ proc track(tracked: PEffects, n: PNode) =
         if notGcSafe(op) and not importedFromC(a):
           # and it's not a recursive call:
           if not (a.kind == nkSym and a.sym == tracked.owner):
-            warnAboutGcUnsafe(n)
+            if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
             markGcUnsafe(tracked, a)
     for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
     if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 18d723315..bcab6b04a 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -13,6 +13,9 @@
 
 import algorithm, sequtils
 
+when defined(nimsuggest):
+  import passes, tables # importer
+
 const
   sep = '\t'
 
@@ -26,16 +29,24 @@ type
     doc*: string           # Not escaped (yet)
     symkind*: TSymKind
     forth*: string               # XXX TODO object on symkind
+    quality*: range[0..100]   # matching quality
+    isGlobal*: bool # is a global variable
+    tokenLen*: int
 
 var
   suggestionResultHook*: proc (result: Suggest) {.closure.}
+  suggestVersion*: int
 
 #template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n"
 
 template origModuleName(m: PSym): string = m.name.s
 
-proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo): Suggest =
+proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo;
+                  quality: range[0..100]): Suggest =
   result.section = parseIdeCmd(section)
+  result.quality = quality
+  result.isGlobal = sfGlobal in s.flags
+  result.tokenLen = s.name.s.len
   if optIdeTerse in gGlobalOptions:
     result.symkind = s.kind
     result.filePath = toFullPath(li)
@@ -65,23 +76,41 @@ proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo): Sugge
 proc `$`(suggest: Suggest): string =
   result = $suggest.section
   result.add(sep)
-  result.add($suggest.symkind)
-  result.add(sep)
-  result.add(suggest.qualifiedPath.join("."))
-  result.add(sep)
-  result.add(suggest.forth)
-  result.add(sep)
-  result.add(suggest.filePath)
-  result.add(sep)
-  result.add($suggest.line)
-  result.add(sep)
-  result.add($suggest.column)
-  result.add(sep)
-  when not defined(noDocgen):
-    result.add(suggest.doc.escape)
+  if suggest.section == ideHighlight:
+    if suggest.symkind == skVar and suggest.isGlobal:
+      result.add("skGlobalVar")
+    elif suggest.symkind == skLet and suggest.isGlobal:
+      result.add("skGlobalLet")
+    else:
+      result.add($suggest.symkind)
+    result.add(sep)
+    result.add($suggest.line)
+    result.add(sep)
+    result.add($suggest.column)
+    result.add(sep)
+    result.add($suggest.tokenLen)
+  else:
+    result.add($suggest.symkind)
+    result.add(sep)
+    result.add(suggest.qualifiedPath.join("."))
+    result.add(sep)
+    result.add(suggest.forth)
+    result.add(sep)
+    result.add(suggest.filePath)
+    result.add(sep)
+    result.add($suggest.line)
+    result.add(sep)
+    result.add($suggest.column)
+    result.add(sep)
+    when not defined(noDocgen):
+      result.add(suggest.doc.escape)
+    if suggestVersion == 2:
+      result.add(sep)
+      result.add($suggest.quality)
 
-proc symToSuggest(s: PSym, isLocal: bool, section: string): Suggest =
-  result = symToSuggest(s, isLocal, section, s.info)
+proc symToSuggest(s: PSym, isLocal: bool, section: string;
+                  quality: range[0..100]): Suggest =
+  result = symToSuggest(s, isLocal, section, s.info, quality)
 
 proc suggestResult(s: Suggest) =
   if not isNil(suggestionResultHook):
@@ -106,7 +135,7 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
 
 proc suggestField(c: PContext, s: PSym, outputs: var int) =
   if filterSym(s) and fieldVisible(c, s):
-    suggestResult(symToSuggest(s, isLocal=true, $ideSug))
+    suggestResult(symToSuggest(s, isLocal=true, $ideSug, 100))
     inc outputs
 
 template wholeSymTab(cond, section: expr) {.immediate.} =
@@ -119,7 +148,7 @@ template wholeSymTab(cond, section: expr) {.immediate.} =
     for item in entries:
       let it {.inject.} = item
       if cond:
-        suggestResult(symToSuggest(it, isLocal = isLocal, section))
+        suggestResult(symToSuggest(it, isLocal = isLocal, section, 100))
         inc outputs
 
 proc suggestSymList(c: PContext, list: PNode, outputs: var int) =
@@ -188,7 +217,7 @@ proc suggestEverything(c: PContext, n: PNode, outputs: var int) =
     if scope == c.topLevelScope: isLocal = false
     for it in items(scope.symbols):
       if filterSym(it):
-        suggestResult(symToSuggest(it, isLocal = isLocal, $ideSug))
+        suggestResult(symToSuggest(it, isLocal = isLocal, $ideSug, 0))
         inc outputs
     if scope == c.topLevelScope: break
 
@@ -196,6 +225,23 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
   # special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but
   # ``myObj``.
   var typ = n.typ
+  when defined(nimsuggest):
+    if n.kind == nkSym and n.sym.kind == skError and suggestVersion == 2:
+      # consider 'foo.|' where 'foo' is some not imported module.
+      let fullPath = findModule(n.sym.name.s, n.info.toFullPath)
+      if fullPath.len == 0:
+        # error: no known module name:
+        typ = nil
+      else:
+        let m = gImportModule(c.module, fullpath.fileInfoIdx)
+        if m == nil: typ = nil
+        else:
+          for it in items(n.sym.tab):
+            if filterSym(it):
+              suggestResult(symToSuggest(it, isLocal=false, $ideSug, 100))
+              inc outputs
+          suggestResult(symToSuggest(m, isLocal=false, $ideMod, 100))
+
   if typ == nil:
     # a module symbol has no type for example:
     if n.kind == nkSym and n.sym.kind == skModule:
@@ -203,12 +249,12 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
         # all symbols accessible, because we are in the current module:
         for it in items(c.topLevelScope.symbols):
           if filterSym(it):
-            suggestResult(symToSuggest(it, isLocal=false, $ideSug))
+            suggestResult(symToSuggest(it, isLocal=false, $ideSug, 100))
             inc outputs
       else:
         for it in items(n.sym.tab):
           if filterSym(it):
-            suggestResult(symToSuggest(it, isLocal=false, $ideSug))
+            suggestResult(symToSuggest(it, isLocal=false, $ideSug, 100))
             inc outputs
     else:
       # fallback:
@@ -263,12 +309,11 @@ proc findClosestCall(n: PNode): PNode =
       result = findClosestCall(n.sons[i])
       if result != nil: return
 
-proc isTracked(current: TLineInfo, tokenLen: int): bool =
-  if current.fileIndex == gTrackPos.fileIndex:
-    if current.line == gTrackPos.line:
-      let col = gTrackPos.col
-      if col >= current.col and col <= current.col+tokenLen-1:
-        return true
+proc isTracked*(current: TLineInfo, tokenLen: int): bool =
+  if current.fileIndex==gTrackPos.fileIndex and current.line==gTrackPos.line:
+    let col = gTrackPos.col
+    if col >= current.col and col <= current.col+tokenLen-1:
+      return true
 
 proc findClosestSym(n: PNode): PNode =
   if n.kind == nkSym and inCheckpoint(n.info) == cpExact:
@@ -278,23 +323,43 @@ proc findClosestSym(n: PNode): PNode =
       result = findClosestSym(n.sons[i])
       if result != nil: return
 
+when defined(nimsuggest):
+  # Since TLineInfo defined a == operator that doesn't include the column,
+  # we map TLineInfo to a unique int here for this lookup table:
+  proc infoToInt(info: TLineInfo): int64 =
+    info.fileIndex + info.line.int64 shl 32 + info.col.int64 shl 48
+
+  proc addNoDup(s: PSym; info: TLineInfo) =
+    let infoAsInt = info.infoToInt
+    for infoB in s.allUsages:
+      if infoB.infoToInt == infoAsInt: return
+    s.allUsages.add(info)
+
 var
   usageSym*: PSym
-  lastLineInfo: TLineInfo
+  lastLineInfo*: TLineInfo
 
 proc findUsages(info: TLineInfo; s: PSym) =
-  if usageSym == nil and isTracked(info, s.name.s.len):
-    usageSym = s
-    suggestResult(symToSuggest(s, isLocal=false, $ideUse))
-  elif s == usageSym:
-    if lastLineInfo != info:
-      suggestResult(symToSuggest(s, isLocal=false, $ideUse, info))
-    lastLineInfo = info
+  if suggestVersion < 2:
+    if usageSym == nil and isTracked(info, s.name.s.len):
+      usageSym = s
+      suggestResult(symToSuggest(s, isLocal=false, $ideUse, 100))
+    elif s == usageSym:
+      if lastLineInfo != info:
+        suggestResult(symToSuggest(s, isLocal=false, $ideUse, info, 100))
+      lastLineInfo = info
+
+when defined(nimsuggest):
+  proc listUsages*(s: PSym) =
+    #echo "usages ", len(s.allUsages)
+    for info in s.allUsages:
+      let x = if info == s.info and info.col == s.info.col: "def" else: "use"
+      suggestResult(symToSuggest(s, isLocal=false, x, info, 100))
 
 proc findDefinition(info: TLineInfo; s: PSym) =
   if s.isNil: return
   if isTracked(info, s.name.s.len):
-    suggestResult(symToSuggest(s, isLocal=false, $ideDef))
+    suggestResult(symToSuggest(s, isLocal=false, $ideDef, 100))
     suggestQuit()
 
 proc ensureIdx[T](x: var T, y: int) =
@@ -303,23 +368,36 @@ proc ensureIdx[T](x: var T, y: int) =
 proc ensureSeq[T](x: var seq[T]) =
   if x == nil: newSeq(x, 0)
 
-proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} =
+proc suggestSym*(info: TLineInfo; s: PSym; isDecl=true) {.inline.} =
   ## misnamed: should be 'symDeclared'
-  if gIdeCmd == ideUse:
-    findUsages(info, s)
-  elif gIdeCmd == ideDef:
-    findDefinition(info, s)
-  elif gIdeCmd == ideDus and s != nil:
-    if isTracked(info, s.name.s.len):
-      suggestResult(symToSuggest(s, isLocal=false, $ideDef))
-    findUsages(info, s)
+  when defined(nimsuggest):
+    if suggestVersion == 2:
+      if s.allUsages.isNil:
+        s.allUsages = @[info]
+      else:
+        s.addNoDup(info)
+
+    if gIdeCmd == ideUse:
+      findUsages(info, s)
+    elif gIdeCmd == ideDef:
+      findDefinition(info, s)
+    elif gIdeCmd == ideDus and s != nil:
+      if isTracked(info, s.name.s.len):
+        suggestResult(symToSuggest(s, isLocal=false, $ideDef, 100))
+      findUsages(info, s)
+    elif gIdeCmd == ideHighlight and info.fileIndex == gTrackPos.fileIndex:
+      suggestResult(symToSuggest(s, isLocal=false, $ideHighlight, info, 100))
+    elif gIdeCmd == ideOutline and info.fileIndex == gTrackPos.fileIndex and
+        isDecl:
+      suggestResult(symToSuggest(s, isLocal=false, $ideOutline, info, 100))
 
 proc markUsed(info: TLineInfo; s: PSym) =
   incl(s.flags, sfUsed)
   if {sfDeprecated, sfError} * s.flags != {}:
     if sfDeprecated in s.flags: message(info, warnDeprecated, s.name.s)
     if sfError in s.flags: localError(info, errWrongSymbolX, s.name.s)
-  suggestSym(info, s)
+  when defined(nimsuggest):
+    suggestSym(info, s, false)
 
 proc useSym*(sym: PSym): PNode =
   result = newSymNode(sym)
@@ -348,8 +426,9 @@ proc suggestExpr*(c: PContext, node: PNode) =
     if n.kind == nkDotExpr:
       var obj = safeSemExpr(c, n.sons[0])
       suggestFieldAccess(c, obj, outputs)
-      if optIdeDebug in gGlobalOptions:
-        echo "expression ", renderTree(obj), " has type ", typeToString(obj.typ)
+
+      #if optIdeDebug in gGlobalOptions:
+      #  echo "expression ", renderTree(obj), " has type ", typeToString(obj.typ)
       #writeStackTrace()
     else:
       suggestEverything(c, n, outputs)
@@ -370,7 +449,7 @@ proc suggestExpr*(c: PContext, node: PNode) =
       suggestCall(c, a, n, outputs)
 
   dec(c.compilesContextId)
-  if outputs > 0 and gIdeCmd notin {ideUse, ideDus}: suggestQuit()
+  if outputs > 0 and gIdeCmd in {ideSug, ideCon, ideDef}: suggestQuit()
 
 proc suggestStmt*(c: PContext, n: PNode) =
   suggestExpr(c, n)