IC navigator: added support for include files (#17784)

* ic fixed navigator crash when track wrong/missed

Also fixed an issue with getNimcacheDir not observing the outDir.

* closer, but not sure how to test[skip ci][ci skip]

* IC navigator: added support for include files

* update

* make posix happy via expandFilename

* update

Co-authored-by: Saem Ghani <saemghani+github@gmail.com>
This commit is contained in:
Andreas Rumpf
2021-04-19 22:37:09 +02:00
committed by GitHub
parent 24abe10aa8
commit 3b80f0dc8e
9 changed files with 111 additions and 33 deletions

View File

@@ -28,7 +28,7 @@ bootSwitch(usedNoGC, defined(nogc), "--gc:none")
import
os, msgs, options, nversion, condsyms, strutils, extccomp, platform,
wordrecg, parseutils, nimblecmd, parseopt, sequtils, lineinfos,
pathutils, strtabs
pathutils, strtabs, pathnorm
from ast import eqTypeFlags, tfGcSafe, tfNoSideEffect
@@ -359,31 +359,52 @@ proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): AbsoluteDir
const
errInvalidNumber = "$1 is not a valid number"
proc makeAbsolute(s: string): AbsoluteFile =
if isAbsolute(s):
AbsoluteFile pathnorm.normalizePath(s)
else:
AbsoluteFile pathnorm.normalizePath(os.getCurrentDir() / s)
proc setTrackingInfo(conf: ConfigRef; dirty, file, line, column: string,
info: TLineInfo) =
## set tracking info, common code for track, trackDirty, & ideTrack
var ln, col: int
if parseUtils.parseInt(line, ln) <= 0:
localError(conf, info, errInvalidNumber % line)
if parseUtils.parseInt(column, col) <= 0:
localError(conf, info, errInvalidNumber % column)
let a = makeAbsolute(file)
if dirty == "":
conf.m.trackPos = newLineInfo(conf, a, ln, col)
else:
let dirtyOriginalIdx = fileInfoIdx(conf, a)
if dirtyOriginalIdx.int32 >= 0:
msgs.setDirtyFile(conf, dirtyOriginalIdx, makeAbsolute(dirty))
conf.m.trackPos = newLineInfo(dirtyOriginalIdx, ln, col)
proc trackDirty(conf: ConfigRef; arg: string, info: TLineInfo) =
var a = arg.split(',')
if a.len != 4: localError(conf, info,
"DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN expected")
var line, column: int
if parseUtils.parseInt(a[2], line) <= 0:
localError(conf, info, errInvalidNumber % a[1])
if parseUtils.parseInt(a[3], column) <= 0:
localError(conf, info, errInvalidNumber % a[2])
let dirtyOriginalIdx = fileInfoIdx(conf, AbsoluteFile a[1])
if dirtyOriginalIdx.int32 >= 0:
msgs.setDirtyFile(conf, dirtyOriginalIdx, AbsoluteFile a[0])
conf.m.trackPos = newLineInfo(dirtyOriginalIdx, line, column)
setTrackingInfo(conf, a[0], a[1], a[2], a[3], info)
proc track(conf: ConfigRef; arg: string, info: TLineInfo) =
var a = arg.split(',')
if a.len != 3: localError(conf, info, "FILE,LINE,COLUMN expected")
var line, column: int
if parseUtils.parseInt(a[1], line) <= 0:
localError(conf, info, errInvalidNumber % a[1])
if parseUtils.parseInt(a[2], column) <= 0:
localError(conf, info, errInvalidNumber % a[2])
conf.m.trackPos = newLineInfo(conf, AbsoluteFile a[0], line, column)
setTrackingInfo(conf, "", a[0], a[1], a[2], info)
proc trackIde(conf: ConfigRef; cmd: IdeCmd, arg: string, info: TLineInfo) =
## set the tracking info related to an ide cmd, supports optional dirty file
var a = arg.split(',')
case a.len
of 4:
setTrackingInfo(conf, a[0], a[1], a[2], a[3], info)
of 3:
setTrackingInfo(conf, "", a[0], a[1], a[2], info)
else:
localError(conf, info, "[DIRTY_BUFFER,]ORIGINAL_FILE,LINE,COLUMN expected")
conf.ideCmd = cmd
proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if pass in {passCmd2, passPP}:
@@ -851,17 +872,17 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
expectNoArg(conf, switch, arg, pass, info)
conf.ideCmd = ideSug
of "def":
expectNoArg(conf, switch, arg, pass, info)
conf.ideCmd = ideDef
expectArg(conf, switch, arg, pass, info)
trackIde(conf, ideDef, arg, info)
of "context":
expectNoArg(conf, switch, arg, pass, info)
conf.ideCmd = ideCon
of "usages":
expectNoArg(conf, switch, arg, pass, info)
conf.ideCmd = ideUse
expectArg(conf, switch, arg, pass, info)
trackIde(conf, ideUse, arg, info)
of "defusages":
expectNoArg(conf, switch, arg, pass, info)
conf.ideCmd = ideDus
expectArg(conf, switch, arg, pass, info)
trackIde(conf, ideDus, arg, info)
of "stdout":
processOnOffSwitchG(conf, {optStdout}, arg, pass, info)
of "listfullpaths":

View File

@@ -29,7 +29,7 @@ type
PackedModule* = object ## the parts of a PackedEncoder that are part of the .rod file
definedSymbols: string
moduleFlags: TSymFlags
includes: seq[(LitId, string)] # first entry is the module filename itself
includes*: seq[(LitId, string)] # first entry is the module filename itself
imports: seq[LitId] # the modules this module depends on
toReplay*: PackedTree # pragmas and VM specific state to replay.
topLevel*: PackedTree # top level statements

View File

@@ -98,11 +98,31 @@ proc list(c: var NavContext; tree: PackedTree; sym: ItemId) =
usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i)))
else: discard
proc searchForIncludeFile(g: ModuleGraph; fullPath: string): int =
for i in 0..high(g.packed):
for k in 1..high(g.packed[i].fromDisk.includes):
# we start from 1 because the first "include" file is
# the module's filename.
if os.cmpPaths(g.packed[i].fromDisk.strings[g.packed[i].fromDisk.includes[k][0]], fullPath) == 0:
return i
return -1
proc nav(g: ModuleGraph) =
# translate the track position to a packed position:
let unpacked = g.config.m.trackPos
let mid = unpacked.fileIndex
let fileId = g.packed[int32 mid].fromDisk.strings.getKeyId(toFullPath(g.config, mid))
var mid = unpacked.fileIndex.int
let fullPath = toFullPath(g.config, unpacked.fileIndex)
if g.packed[mid].status == undefined:
# check if 'mid' is an include file of some other module:
mid = searchForIncludeFile(g, fullPath)
if mid < 0:
localError(g.config, unpacked, "unknown file name: " & fullPath)
return
let fileId = g.packed[mid].fromDisk.strings.getKeyId(fullPath)
if fileId == LitId(0):
internalError(g.config, unpacked, "cannot find a valid file ID")
@@ -114,9 +134,9 @@ proc nav(g: ModuleGraph) =
trackPos: PackedLineInfo(line: unpacked.line, col: unpacked.col, file: fileId),
outputSep: if isDefined(g.config, "nimIcNavigatorTests"): ' ' else: '\t'
)
var symId = search(c, g.packed[int32 mid].fromDisk.topLevel)
var symId = search(c, g.packed[mid].fromDisk.topLevel)
if symId == EmptyItemId:
symId = search(c, g.packed[int32 mid].fromDisk.bodies)
symId = search(c, g.packed[mid].fromDisk.bodies)
if symId == EmptyItemId:
localError(g.config, unpacked, "no symbol at this position")

View File

@@ -582,6 +582,9 @@ template globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "")
template globalError*(conf: ConfigRef; info: TLineInfo, arg: string) =
liMessage(conf, info, errGenerated, arg, doRaise, instLoc())
template globalError*(conf: ConfigRef; format: string, params: openArray[string]) =
liMessage(conf, unknownLineInfo, errGenerated, format % params, doRaise, instLoc())
template localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(conf, info, msg, arg, doNothing, instLoc())

View File

@@ -701,7 +701,10 @@ proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir =
result = if not conf.nimcacheDir.isEmpty:
conf.nimcacheDir
elif conf.backend == backendJs:
conf.projectPath / genSubDir
if conf.outDir.isEmpty:
conf.projectPath / genSubDir
else:
conf.outDir / genSubDir
else:
AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name &
nimcacheSuffix(conf))

View File

@@ -490,7 +490,7 @@ proc icTests(r: var TResults; testsDir: string, cat: Category, options: string;
writeOnly = " --incremental:writeonly "
readOnly = " --incremental:readonly "
incrementalOn = " --incremental:on -d:nimIcIntegrityChecks "
navTestConfig = " --ic:on --defusages -d:nimIcNavigatorTests --hint[Conf]:off --warnings:off "
navTestConfig = " --ic:on -d:nimIcNavigatorTests --hint[Conf]:off --warnings:off "
template test(x: untyped) =
testSpecWithNimcache(r, makeRawTest(file, x & options, cat), nimcache)

View File

@@ -0,0 +1,2 @@
# An include file.
foo(3)

View File

@@ -0,0 +1,29 @@
discard """
cmd: "nim check $options --defusages:$file,12,7 $file"
nimout: '''def tincludefile_temp.nim(11, 10)
usage tincludefile_temp.nim(12, 8)
'''
"""
proc foo(x: int) =
echo x
foo(3)
echo "yes", 1 != 3
#!EDIT!#
discard """
cmd: "nim check $options --defusages:$file/../minclude.nim,2,2 $file"
nimout: '''def tincludefile_temp.nim(10, 6)
usage minclude.nim(2, 1)
'''
"""
proc foo(x: int) =
echo x
include minclude

View File

@@ -1,5 +1,5 @@
discard """
cmd: "nim check $options --track:$file,12,7 $file"
cmd: "nim check $options --defusages:$file,12,7 $file"
nimout: '''def tnav1_temp.nim(11, 10)
usage tnav1_temp.nim(12, 8)
'''
@@ -16,7 +16,7 @@ echo "yes", 1 != 3
#!EDIT!#
discard """
cmd: "nim check $options --track:$file,15,2 $file"
cmd: "nim check $options --defusages:$file,15,2 $file"
nimout: '''def tnav1_temp.nim(12, 6)
usage tnav1_temp.nim(15, 1)
'''