nimsuggest & tester: many improvements, test new dependency tracking
This commit is contained in:
parent
a644471b12
commit
eb2455075e
6 changed files with 127 additions and 15 deletions
|
|
@ -60,7 +60,7 @@ var
|
||||||
|
|
||||||
const
|
const
|
||||||
seps = {':', ';', ' ', '\t'}
|
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 'quit' to quit\n" &
|
||||||
"type 'debug' to toggle debug mode on/off\n" &
|
"type 'debug' to toggle debug mode on/off\n" &
|
||||||
"type 'terse' to toggle terse mode on/off"
|
"type 'terse' to toggle terse mode on/off"
|
||||||
|
|
@ -104,13 +104,13 @@ proc sexp(s: seq[Suggest]): SexpNode =
|
||||||
for sug in s:
|
for sug in s:
|
||||||
result.add(sexp(sug))
|
result.add(sexp(sug))
|
||||||
|
|
||||||
proc listEPC(): SexpNode =
|
proc listEpc(): SexpNode =
|
||||||
# This function is called from Emacs to show available options.
|
# This function is called from Emacs to show available options.
|
||||||
let
|
let
|
||||||
argspecs = sexp("file line column dirtyfile".split(" ").map(newSSymbol))
|
argspecs = sexp("file line column dirtyfile".split(" ").map(newSSymbol))
|
||||||
docstring = sexp("line starts at 1, column at 0, dirtyfile is optional")
|
docstring = sexp("line starts at 1, column at 0, dirtyfile is optional")
|
||||||
result = newSList()
|
result = newSList()
|
||||||
for command in ["sug", "con", "def", "use", "dus", "chk"]:
|
for command in ["sug", "con", "def", "use", "dus", "chk", "mod"]:
|
||||||
let
|
let
|
||||||
cmd = sexp(command)
|
cmd = sexp(command)
|
||||||
methodDesc = newSList()
|
methodDesc = newSList()
|
||||||
|
|
@ -162,6 +162,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
|
||||||
# resetModule gProjectMainIdx
|
# resetModule gProjectMainIdx
|
||||||
graph.markDirty dirtyIdx
|
graph.markDirty dirtyIdx
|
||||||
graph.markClientsDirty dirtyIdx
|
graph.markClientsDirty dirtyIdx
|
||||||
|
if gIdeCmd != ideMod:
|
||||||
graph.compileProject(cache, dirtyIdx)
|
graph.compileProject(cache, dirtyIdx)
|
||||||
if gIdeCmd in {ideUse, ideDus}:
|
if gIdeCmd in {ideUse, ideDus}:
|
||||||
let u = if suggestVersion >= 2: graph.symFromInfo(gTrackPos) else: usageSym
|
let u = if suggestVersion >= 2: graph.symFromInfo(gTrackPos) else: usageSym
|
||||||
|
|
@ -238,6 +239,7 @@ proc parseCmdLine(cmd: string; graph: ModuleGraph; cache: IdentCache) =
|
||||||
of "def": gIdeCmd = ideDef
|
of "def": gIdeCmd = ideDef
|
||||||
of "use": gIdeCmd = ideUse
|
of "use": gIdeCmd = ideUse
|
||||||
of "dus": gIdeCmd = ideDus
|
of "dus": gIdeCmd = ideDus
|
||||||
|
of "mod": gIdeCmd = ideMod
|
||||||
of "chk":
|
of "chk":
|
||||||
gIdeCmd = ideChk
|
gIdeCmd = ideChk
|
||||||
incl(gGlobalOptions, optIdeDebug)
|
incl(gGlobalOptions, optIdeDebug)
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,22 @@ import os, osproc, strutils, streams, re
|
||||||
type
|
type
|
||||||
Test = object
|
Test = object
|
||||||
cmd, dest: string
|
cmd, dest: string
|
||||||
|
startup: seq[string]
|
||||||
script: seq[(string, string)]
|
script: seq[(string, string)]
|
||||||
|
|
||||||
const
|
const
|
||||||
curDir = when defined(windows): "" else: ""
|
curDir = when defined(windows): "" else: ""
|
||||||
DummyEof = "!EOF!"
|
DummyEof = "!EOF!"
|
||||||
|
|
||||||
|
template tpath(): untyped = getAppDir() / "tests"
|
||||||
|
|
||||||
proc parseTest(filename: string): Test =
|
proc parseTest(filename: string): Test =
|
||||||
const cursorMarker = "#[!]#"
|
const cursorMarker = "#[!]#"
|
||||||
let nimsug = curDir & addFileExt("nimsuggest", ExeExt)
|
let nimsug = curDir & addFileExt("nimsuggest", ExeExt)
|
||||||
result.dest = getTempDir() / extractFilename(filename)
|
result.dest = getTempDir() / extractFilename(filename)
|
||||||
result.cmd = nimsug & " --tester " & result.dest
|
result.cmd = nimsug & " --tester " & result.dest
|
||||||
result.script = @[]
|
result.script = @[]
|
||||||
|
result.startup = @[]
|
||||||
var tmp = open(result.dest, fmWrite)
|
var tmp = open(result.dest, fmWrite)
|
||||||
var specSection = 0
|
var specSection = 0
|
||||||
var markers = newSeq[string]()
|
var markers = newSeq[string]()
|
||||||
|
|
@ -36,25 +40,56 @@ proc parseTest(filename: string): Test =
|
||||||
elif specSection == 1:
|
elif specSection == 1:
|
||||||
if x.startsWith("$nimsuggest"):
|
if x.startsWith("$nimsuggest"):
|
||||||
result.cmd = x % ["nimsuggest", nimsug, "file", filename]
|
result.cmd = x % ["nimsuggest", nimsug, "file", filename]
|
||||||
elif x.startsWith("!edit"):
|
elif x.startsWith("!"):
|
||||||
|
if result.cmd.len == 0:
|
||||||
|
result.startup.add x
|
||||||
|
else:
|
||||||
result.script.add((x, ""))
|
result.script.add((x, ""))
|
||||||
elif x.startsWith(">"):
|
elif x.startsWith(">"):
|
||||||
# since 'markers' here are not complete yet, we do the $substitutions
|
# since 'markers' here are not complete yet, we do the $substitutions
|
||||||
# afterwards
|
# afterwards
|
||||||
result.script.add((x.substr(1), ""))
|
result.script.add((x.substr(1).replaceWord("$path", tpath()), ""))
|
||||||
else:
|
elif x.len > 0:
|
||||||
# expected output line:
|
# expected output line:
|
||||||
let x = x % ["file", filename]
|
let x = x % ["file", filename]
|
||||||
result.script[^1][1].add x.replace(";;", "\t") & '\L'
|
result.script[^1][1].add x.replace(";;", "\t") & '\L'
|
||||||
|
# else: ignore empty lines for better readability of the specs
|
||||||
inc i
|
inc i
|
||||||
tmp.close()
|
tmp.close()
|
||||||
# now that we know the markers, substitute them:
|
# now that we know the markers, substitute them:
|
||||||
for a in mitems(result.script):
|
for a in mitems(result.script):
|
||||||
a[0] = a[0] % markers
|
a[0] = a[0] % markers
|
||||||
|
|
||||||
proc edit(tmpfile, cmd: string) =
|
proc parseCmd(c: string): seq[string] =
|
||||||
let x = cmd.splitWhitespace()
|
# we don't support double quotes for now so that
|
||||||
let f = if x.len >= 4: x[3] else: tmpfile
|
# 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:
|
try:
|
||||||
let content = readFile(f)
|
let content = readFile(f)
|
||||||
let newcontent = content.replace(x[1], x[2])
|
let newcontent = content.replace(x[1], x[2])
|
||||||
|
|
@ -64,12 +99,45 @@ proc edit(tmpfile, cmd: string) =
|
||||||
except IOError:
|
except IOError:
|
||||||
quit "cannot edit file " & tmpfile
|
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 =
|
proc smartCompare(pattern, x: string): bool =
|
||||||
if pattern.contains('*'):
|
if pattern.contains('*'):
|
||||||
result = match(x, re(escapeRe(pattern).replace("\\x2A","(.*)"), {}))
|
result = match(x, re(escapeRe(pattern).replace("\\x2A","(.*)"), {}))
|
||||||
|
|
||||||
proc runTest(filename: string): int =
|
proc runTest(filename: string): int =
|
||||||
let s = parseTest filename
|
let s = parseTest filename
|
||||||
|
for cmd in s.startup:
|
||||||
|
if not runCmd(cmd, s.dest):
|
||||||
|
quit "invalid command: " & cmd
|
||||||
let cl = parseCmdLine(s.cmd)
|
let cl = parseCmdLine(s.cmd)
|
||||||
var p = startProcess(command=cl[0], args=cl[1 .. ^1],
|
var p = startProcess(command=cl[0], args=cl[1 .. ^1],
|
||||||
options={poStdErrToStdOut, poUsePath,
|
options={poStdErrToStdOut, poUsePath,
|
||||||
|
|
@ -83,9 +151,7 @@ proc runTest(filename: string): int =
|
||||||
while outp.readLine(a):
|
while outp.readLine(a):
|
||||||
if a == DummyEof: break
|
if a == DummyEof: break
|
||||||
for req, resp in items(s.script):
|
for req, resp in items(s.script):
|
||||||
if req.startsWith("!edit"):
|
if not runCmd(req, s.dest):
|
||||||
edit(s.dest, req)
|
|
||||||
else:
|
|
||||||
inp.writeLine(req)
|
inp.writeLine(req)
|
||||||
inp.flush()
|
inp.flush()
|
||||||
var answer = ""
|
var answer = ""
|
||||||
|
|
|
||||||
8
tools/nimsuggest/tests/dep_v1.nim
Normal file
8
tools/nimsuggest/tests/dep_v1.nim
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
Foo* = object
|
||||||
|
x*, y*: int
|
||||||
9
tools/nimsuggest/tests/dep_v2.nim
Normal file
9
tools/nimsuggest/tests/dep_v2.nim
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
Foo* = object
|
||||||
|
x*, y*: int
|
||||||
|
z*: string
|
||||||
|
|
@ -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.
|
# see whether then the z field is suggested.
|
||||||
|
|
||||||
const zField = 0i32
|
const zField = 0i32
|
||||||
|
|
|
||||||
27
tools/nimsuggest/tests/tdot3.nim
Normal file
27
tools/nimsuggest/tests/tdot3.nim
Normal file
|
|
@ -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
|
||||||
|
"""
|
||||||
Loading…
Add table
Add a link
Reference in a new issue