summary refs log tree commit diff stats
path: root/tools/nimsuggest
diff options
context:
space:
mode:
Diffstat (limited to 'tools/nimsuggest')
-rw-r--r--tools/nimsuggest/nimsuggest.nim10
-rw-r--r--tools/nimsuggest/tester.nim86
-rw-r--r--tools/nimsuggest/tests/dep_v1.nim8
-rw-r--r--tools/nimsuggest/tests/dep_v2.nim9
-rw-r--r--tools/nimsuggest/tests/tdot2.nim2
-rw-r--r--tools/nimsuggest/tests/tdot3.nim27
6 files changed, 127 insertions, 15 deletions
diff --git a/tools/nimsuggest/nimsuggest.nim b/tools/nimsuggest/nimsuggest.nim
index f00d9b636..6b7ee74e2 100644
--- a/tools/nimsuggest/nimsuggest.nim
+++ b/tools/nimsuggest/nimsuggest.nim
@@ -60,7 +60,7 @@ var
 
 const
   seps = {':', ';', ' ', '\t'}
-  Help = "usage: sug|con|def|use|dus|chk|highlight|outline file.nim[;dirtyfile.nim]:line:col\n" &
+  Help = "usage: sug|con|def|use|dus|chk|mod|highlight|outline file.nim[;dirtyfile.nim]:line:col\n" &
          "type 'quit' to quit\n" &
          "type 'debug' to toggle debug mode on/off\n" &
          "type 'terse' to toggle terse mode on/off"
@@ -104,13 +104,13 @@ proc sexp(s: seq[Suggest]): SexpNode =
   for sug in s:
     result.add(sexp(sug))
 
-proc listEPC(): SexpNode =
+proc listEpc(): SexpNode =
   # This function is called from Emacs to show available options.
   let
     argspecs = sexp("file line column dirtyfile".split(" ").map(newSSymbol))
     docstring = sexp("line starts at 1, column at 0, dirtyfile is optional")
   result = newSList()
-  for command in ["sug", "con", "def", "use", "dus", "chk"]:
+  for command in ["sug", "con", "def", "use", "dus", "chk", "mod"]:
     let
       cmd = sexp(command)
       methodDesc = newSList()
@@ -162,7 +162,8 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
     #  resetModule gProjectMainIdx
     graph.markDirty dirtyIdx
     graph.markClientsDirty dirtyIdx
-    graph.compileProject(cache, dirtyIdx)
+    if gIdeCmd != ideMod:
+      graph.compileProject(cache, dirtyIdx)
   if gIdeCmd in {ideUse, ideDus}:
     let u = if suggestVersion >= 2: graph.symFromInfo(gTrackPos) else: usageSym
     if u != nil:
@@ -238,6 +239,7 @@ proc parseCmdLine(cmd: string; graph: ModuleGraph; cache: IdentCache) =
   of "def": gIdeCmd = ideDef
   of "use": gIdeCmd = ideUse
   of "dus": gIdeCmd = ideDus
+  of "mod": gIdeCmd = ideMod
   of "chk":
     gIdeCmd = ideChk
     incl(gGlobalOptions, optIdeDebug)
diff --git a/tools/nimsuggest/tester.nim b/tools/nimsuggest/tester.nim
index b4e1f8f7f..c90afe3db 100644
--- a/tools/nimsuggest/tester.nim
+++ b/tools/nimsuggest/tester.nim
@@ -8,18 +8,22 @@ import os, osproc, strutils, streams, re
 type
   Test = object
     cmd, dest: string
+    startup: seq[string]
     script: seq[(string, string)]
 
 const
   curDir = when defined(windows): "" else: ""
   DummyEof = "!EOF!"
 
+template tpath(): untyped = getAppDir() / "tests"
+
 proc parseTest(filename: string): Test =
   const cursorMarker = "#[!]#"
   let nimsug = curDir & addFileExt("nimsuggest", ExeExt)
   result.dest = getTempDir() / extractFilename(filename)
   result.cmd = nimsug & " --tester " & result.dest
   result.script = @[]
+  result.startup = @[]
   var tmp = open(result.dest, fmWrite)
   var specSection = 0
   var markers = newSeq[string]()
@@ -36,25 +40,56 @@ proc parseTest(filename: string): Test =
     elif specSection == 1:
       if x.startsWith("$nimsuggest"):
         result.cmd = x % ["nimsuggest", nimsug, "file", filename]
-      elif x.startsWith("!edit"):
-        result.script.add((x, ""))
+      elif x.startsWith("!"):
+        if result.cmd.len == 0:
+          result.startup.add x
+        else:
+          result.script.add((x, ""))
       elif x.startsWith(">"):
         # since 'markers' here are not complete yet, we do the $substitutions
         # afterwards
-        result.script.add((x.substr(1), ""))
-      else:
+        result.script.add((x.substr(1).replaceWord("$path", tpath()), ""))
+      elif x.len > 0:
         # expected output line:
         let x = x % ["file", filename]
         result.script[^1][1].add x.replace(";;", "\t") & '\L'
+        # else: ignore empty lines for better readability of the specs
     inc i
   tmp.close()
   # now that we know the markers, substitute them:
   for a in mitems(result.script):
     a[0] = a[0] % markers
 
-proc edit(tmpfile, cmd: string) =
-  let x = cmd.splitWhitespace()
-  let f = if x.len >= 4: x[3] else: tmpfile
+proc parseCmd(c: string): seq[string] =
+  # we don't support double quotes for now so that
+  # we can later support them properly with escapes and stuff.
+  result = @[]
+  var i = 0
+  var a = ""
+  while true:
+    setLen(a, 0)
+    # eat all delimiting whitespace
+    while c[i] in {' ', '\t', '\l', '\r'}: inc(i)
+    case c[i]
+    of '"': raise newException(ValueError, "double quotes not yet supported: " & c)
+    of '\'':
+      var delim = c[i]
+      inc(i) # skip ' or "
+      while c[i] != '\0' and c[i] != delim:
+        add a, c[i]
+        inc(i)
+      if c[i] != '\0': inc(i)
+    of '\0': break
+    else:
+      while c[i] > ' ':
+        add(a, c[i])
+        inc(i)
+    add(result, a)
+
+proc edit(tmpfile: string; x: seq[string]) =
+  if x.len != 3 and x.len != 4:
+    quit "!edit takes two or three arguments"
+  let f = if x.len >= 4: tpath() / x[3] else: tmpfile
   try:
     let content = readFile(f)
     let newcontent = content.replace(x[1], x[2])
@@ -64,12 +99,45 @@ proc edit(tmpfile, cmd: string) =
   except IOError:
     quit "cannot edit file " & tmpfile
 
+proc exec(x: seq[string]) =
+  if x.len != 2: quit "!exec takes one argument"
+  if execShellCmd(x[1]) != 0:
+    quit "External program failed " & x[1]
+
+proc copy(x: seq[string]) =
+  if x.len != 3: quit "!copy takes two arguments"
+  let rel = tpath()
+  copyFile(rel / x[1], rel / x[2])
+
+proc del(x: seq[string]) =
+  if x.len != 2: quit "!del takes one argument"
+  removeFile(tpath() / x[1])
+
+proc runCmd(cmd, dest: string): bool =
+  result = cmd[0] == '!'
+  if not result: return
+  let x = cmd.parseCmd()
+  case x[0]
+  of "!edit":
+    edit(dest, x)
+  of "!exec":
+    exec(x)
+  of "!copy":
+    copy(x)
+  of "!del":
+    del(x)
+  else:
+    quit "unkown command: " & cmd
+
 proc smartCompare(pattern, x: string): bool =
   if pattern.contains('*'):
     result = match(x, re(escapeRe(pattern).replace("\\x2A","(.*)"), {}))
 
 proc runTest(filename: string): int =
   let s = parseTest filename
+  for cmd in s.startup:
+    if not runCmd(cmd, s.dest):
+      quit "invalid command: " & cmd
   let cl = parseCmdLine(s.cmd)
   var p = startProcess(command=cl[0], args=cl[1 .. ^1],
                        options={poStdErrToStdOut, poUsePath,
@@ -83,9 +151,7 @@ proc runTest(filename: string): int =
     while outp.readLine(a):
       if a == DummyEof: break
     for req, resp in items(s.script):
-      if req.startsWith("!edit"):
-        edit(s.dest, req)
-      else:
+      if not runCmd(req, s.dest):
         inp.writeLine(req)
         inp.flush()
         var answer = ""
diff --git a/tools/nimsuggest/tests/dep_v1.nim b/tools/nimsuggest/tests/dep_v1.nim
new file mode 100644
index 000000000..eae230e85
--- /dev/null
+++ b/tools/nimsuggest/tests/dep_v1.nim
@@ -0,0 +1,8 @@
+
+
+
+
+
+type
+  Foo* = object
+    x*, y*: int
diff --git a/tools/nimsuggest/tests/dep_v2.nim b/tools/nimsuggest/tests/dep_v2.nim
new file mode 100644
index 000000000..ab39721c4
--- /dev/null
+++ b/tools/nimsuggest/tests/dep_v2.nim
@@ -0,0 +1,9 @@
+
+
+
+
+
+type
+  Foo* = object
+    x*, y*: int
+    z*: string
diff --git a/tools/nimsuggest/tests/tdot2.nim b/tools/nimsuggest/tests/tdot2.nim
index 490e78451..a58ac818b 100644
--- a/tools/nimsuggest/tests/tdot2.nim
+++ b/tools/nimsuggest/tests/tdot2.nim
@@ -1,4 +1,4 @@
-# Test that basic editing. We replace the 'false' by 'true' to
+# Test basic editing. We replace the 'false' by 'true' to
 # see whether then the z field is suggested.
 
 const zField = 0i32
diff --git a/tools/nimsuggest/tests/tdot3.nim b/tools/nimsuggest/tests/tdot3.nim
new file mode 100644
index 000000000..5badde867
--- /dev/null
+++ b/tools/nimsuggest/tests/tdot3.nim
@@ -0,0 +1,27 @@
+# Test basic module dependency recompilations.
+
+import dep
+
+proc main(f: Foo) =
+  f.#[!]#
+
+# the tester supports the spec section at the bottom of the file and
+# this way, the line numbers more often stay the same
+
+discard """
+!copy dep_v1.nim dep.nim
+$nimsuggest --tester $file
+>sug $1
+sug;;skField;;x;;int;;*dep.nim;;8;;4;;"";;100
+sug;;skField;;y;;int;;*dep.nim;;8;;8;;"";;100
+sug;;skProc;;tdot3.main;;proc (f: Foo);;$file;;5;;5;;"";;100
+
+!copy dep_v2.nim dep.nim
+>mod $path/dep.nim
+>sug $1
+sug;;skField;;x;;int;;*dep.nim;;8;;4;;"";;100
+sug;;skField;;y;;int;;*dep.nim;;8;;8;;"";;100
+sug;;skField;;z;;string;;*dep.nim;;9;;4;;"";;100
+sug;;skProc;;tdot3.main;;proc (f: Foo);;$file;;5;;5;;"";;100
+!del dep.nim
+"""