Skip to content

Commit cfa4ede

Browse files
committed
Add tests for v3 and implement ideSug
1 parent b89a3eb commit cfa4ede

File tree

7 files changed

+134
-95
lines changed

7 files changed

+134
-95
lines changed

compiler/modulegraphs.nim

+3-4
Original file line numberDiff line numberDiff line change
@@ -393,8 +393,7 @@ else:
393393

394394
template onDef*(info: TLineInfo; s: PSym) =
395395
let c = getPContext()
396-
if c.graph.config.suggestVersion == 3 and
397-
not c.graph.suggestSymbols.getOrDefault(info.fileIndex, @[]).contains (s, info):
396+
if c.graph.config.suggestVersion == 3:
398397
suggestSym(c.graph, info, s, c.graph.usageSym)
399398

400399
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
@@ -606,8 +605,8 @@ proc needsCompilation*(g: ModuleGraph): bool =
606605
return true
607606

608607
proc needsCompilation*(g: ModuleGraph, fileIdx: FileIndex): bool =
609-
let module = g.ifaces[fileIdx.int32].module
610-
if module == nil or g.isDirty(module):
608+
let module = g.getModule(fileIdx)
609+
if module != nil and g.isDirty(module):
611610
return true
612611

613612
for i in 0i32..<g.ifaces.len.int32:

compiler/suggest.nim

+3-1
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,9 @@ proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; i
497497
## misnamed: should be 'symDeclared'
498498
let conf = g.config
499499
when defined(nimsuggest):
500-
g.suggestSymbols.mgetOrPut(info.fileIndex, @[]).add (s, info)
500+
if not g.suggestSymbols.getOrDefault(info.fileIndex, @[]).contains (s, info):
501+
g.suggestSymbols.mgetOrPut(info.fileIndex, @[]).add (s, info)
502+
501503
if conf.suggestVersion == 0:
502504
if s.allUsages.len == 0:
503505
s.allUsages = @[info]

nimsuggest/nimsuggest.nim

+103-71
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
import compiler/renderer
1111
import strformat
1212
import tables
13-
import re
1413
import std/sha1
1514
import segfaults
15+
import times
16+
1617
## Nimsuggest is a tool that helps to give editors IDE like capabilities.
1718

1819
when not defined(nimcore):
@@ -28,6 +29,7 @@ import compiler / [options, commands, modules, sem,
2829
idents, modulegraphs, prefixmatches, lineinfos, cmdlinehelper,
2930
pathutils]
3031

32+
3133
when defined(windows):
3234
import winlean
3335
else:
@@ -173,11 +175,12 @@ proc symFromInfo(graph: ModuleGraph; trackPos: TLineInfo): PSym =
173175

174176
proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
175177
graph: ModuleGraph) =
176-
if graph.config.suggestVersion == 3:
178+
let conf = graph.config
179+
180+
if conf.suggestVersion == 3:
177181
executeNoHooksV3(cmd, file, dirtyfile, line, col, graph)
178182
return
179183

180-
let conf = graph.config
181184
myLog("cmd: " & $cmd & ", file: " & file.string &
182185
", dirtyFile: " & dirtyfile.string &
183186
"[" & $line & ":" & $col & "]")
@@ -481,14 +484,23 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
481484
execute(conf.ideCmd, AbsoluteFile orig, AbsoluteFile dirtyfile, line, col, graph)
482485
sentinel()
483486

487+
template benchmark(benchmarkName: string, code: untyped) =
488+
block:
489+
myLog "Started [" & benchmarkName & "]..."
490+
let t0 = epochTime()
491+
code
492+
let elapsed = epochTime() - t0
493+
let elapsedStr = elapsed.formatFloat(format = ffDecimal, precision = 3)
494+
myLog "CPU Time [" & benchmarkName & "] " & elapsedStr & "s"
495+
484496
proc recompileFullProject(graph: ModuleGraph) =
485-
#echo "recompiling full project"
486-
resetSystemArtifacts(graph)
487-
graph.vm = nil
488-
graph.resetAllModules()
489-
GC_fullCollect()
490-
compileProject(graph)
491-
#echo GC_getStatistics()
497+
benchmark "Recompilation(clean)":
498+
graph.resetForBackend()
499+
graph.resetSystemArtifacts()
500+
graph.vm = nil
501+
GC_fullCollect()
502+
graph.resetAllModules()
503+
graph.compileProject()
492504

493505
proc mainThread(graph: ModuleGraph) =
494506
let conf = graph.config
@@ -546,11 +558,17 @@ proc mainCommand(graph: ModuleGraph) =
546558
conf.setErrorMaxHighMaybe # honor --errorMax even if it may not make sense here
547559
# do not print errors, but log them
548560
conf.writelnHook = proc (msg: string) = discard
549-
conf.structuredErrorHook = nil
561+
562+
if graph.config.suggestVersion == 3:
563+
graph.config.structuredErrorHook = proc (conf: ConfigRef; info: TLineInfo; msg: string; sev: Severity) =
564+
let suggest = Suggest(section: ideChk, filePath: toFullPath(conf, info),
565+
line: toLinenumber(info), column: toColumn(info), doc: msg, forth: $sev)
566+
graph.suggestErrors.mgetOrPut(info.fileIndex, @[]).add suggest
550567

551568
# compile the project before showing any input so that we already
552569
# can answer questions right away:
553-
compileProject(graph)
570+
benchmark "Initial compilation":
571+
compileProject(graph)
554572

555573
open(requests)
556574
open(results)
@@ -668,35 +686,28 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
668686

669687
# v3 start
670688

671-
proc recompileFullProject_v3(graph: ModuleGraph) =
672-
myLog "Clean recompile project"
673-
graph.resetForBackend()
674-
graph.resetSystemArtifacts()
675-
graph.vm = nil
676-
GC_fullCollect()
677-
graph.resetAllModules()
678-
graph.compileProject()
679-
myLog fmt "Recompilation finished with the following GC stats \n{GC_getStatistics()}"
680-
681689
proc recompilePartially(graph: ModuleGraph, projectFileIdx = InvalidFileIdx) =
682690
if projectFileIdx == InvalidFileIdx:
683691
myLog "Recompiling partially from root"
684692
else:
685693
myLog fmt "Recompiling partially starting from {graph.getModule(projectFileIdx)}"
686694

695+
# inst caches are breaking incremental compilation when the cache caches stuff
696+
# from changed buffer
687697
graph.typeInstCache.clear()
688698
graph.procInstCache.clear()
699+
689700
GC_fullCollect()
701+
690702
try:
691-
graph.compileProject(projectFileIdx)
692-
myLog fmt "Recompilation finished with the following GC stats\n{GC_getStatistics()}"
703+
benchmark "Recompilation":
704+
graph.compileProject(projectFileIdx)
693705
except Exception as e:
694-
myLog fmt "Faield to recompile partially with the following error:\n {e.msg} \n\n {e.getStackTrace()}"
695-
graph.recompileFullProject()
696-
697-
proc fileInfoIdx(conf: ConfigRef; filename: AbsoluteFile): FileIndex =
698-
var isKnownFile: bool;
699-
result = fileInfoIdx(conf, filename, isKnownFile)
706+
myLog fmt "Failed to recompile partially with the following error:\n {e.msg} \n\n {e.getStackTrace()}"
707+
try:
708+
graph.recompileFullProject()
709+
except Exception as e:
710+
myLog fmt "Failed clean recompilation:\n {e.msg} \n\n {e.getStackTrace()}"
700711

701712
proc findSymData(graph: ModuleGraph, file: AbsoluteFile; line, col: int):
702713
tuple[sym: PSym, info: TLineInfo] =
@@ -709,42 +720,58 @@ proc findSymData(graph: ModuleGraph, file: AbsoluteFile; line, col: int):
709720

710721
proc markDirtyIfNeeded(graph: ModuleGraph, file: string, originalFileIdx: FileIndex) =
711722
let sha = $sha1.secureHashFile(file)
712-
if graph.config.m.fileInfos[originalFileIdx.int32].hash != sha:
723+
if graph.config.m.fileInfos[originalFileIdx.int32].hash != sha or graph.config.ideCmd == ideSug:
713724
myLog fmt "{file} changed compared to last compilation"
714725
graph.markDirty originalFileIdx
715726
graph.markClientsDirty originalFileIdx
716727
else:
717728
myLog fmt "No changes in file {file} compared to last compilation"
718729

719-
proc suggestResultUseOrDef(graph: ModuleGraph, sym: PSym, info: TLineInfo) =
720-
let suggest = symToSuggest(graph, sym, isLocal=false,
721-
if sym.info == info:
722-
ideDef
723-
else:
724-
ideUse,
730+
proc suggestResult(graph: ModuleGraph, sym: PSym, info: TLineInfo, defaultSection = ideNone) =
731+
let section = if defaultSection != ideNone:
732+
defaultSection
733+
elif sym.info == info:
734+
ideDef
735+
else:
736+
ideUse
737+
let suggest = symToSuggest(graph, sym, isLocal=false, section,
725738
info, 100, PrefixMatch.None, false, 0)
726739
suggestResult(graph.config, suggest)
727740

741+
const
742+
# kinds for ideOutline and ideGlobalSymbols
743+
searchableSymKinds = {skField, skEnumField, skIterator, skMethod, skFunc, skProc, skConverter, skTemplate}
744+
728745
proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, line, col: int;
729746
graph: ModuleGraph) =
730-
graph.config.writelnHook = proc (s: string) = discard
731-
graph.config.structuredErrorHook = proc (conf: ConfigRef; info: TLineInfo; msg: string; sev: Severity) =
747+
let conf = graph.config
748+
conf.writelnHook = proc (s: string) = discard
749+
conf.structuredErrorHook = proc (conf: ConfigRef; info: TLineInfo;
750+
msg: string; sev: Severity) =
732751
let suggest = Suggest(section: ideChk, filePath: toFullPath(conf, info),
733752
line: toLinenumber(info), column: toColumn(info), doc: msg, forth: $sev)
734753
graph.suggestErrors.mgetOrPut(info.fileIndex, @[]).add suggest
735-
var isKnownFile = true
736-
let conf = graph.config
754+
737755
conf.ideCmd = cmd
738756

739757
myLog fmt "cmd: {cmd}, file: {file}[{line}:{col}], dirtyFile: {dirtyfile}"
740758

741-
msgs.setDirtyFile(
742-
conf,
743-
fileInfoIdx(conf, file),
744-
if dirtyfile.isEmpty: AbsoluteFile"" else: dirtyfile)
759+
var fileIndex: FileIndex
760+
761+
if not (cmd in {ideRecompile, ideGlobalSymbols}):
762+
if not fileInfoKnown(conf, file):
763+
myLog fmt "{file} is unknown, returning no results"
764+
return
745765

746-
if not dirtyfile.isEmpty:
747-
graph.markDirtyIfNeeded(dirtyFile.string, fileInfoIdx(conf, file))
766+
fileIndex = fileInfoIdx(conf, file)
767+
myLog fmt "File {file} mapped to {fileIndex.int}, module = {graph.getModule(fileIndex)}"
768+
msgs.setDirtyFile(
769+
conf,
770+
fileIndex,
771+
if dirtyfile.isEmpty: AbsoluteFile"" else: dirtyfile)
772+
773+
if not dirtyfile.isEmpty:
774+
graph.markDirtyIfNeeded(dirtyFile.string, fileInfoIdx(conf, file))
748775

749776
# these commands require fully compiled project
750777
if cmd in {ideUse, ideDus, ideGlobalSymbols, ideChk} and graph.needsCompilation():
@@ -756,47 +783,54 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
756783
graph.unmarkAllDirty()
757784

758785
# these commands require partially compiled project
759-
# TODO: we should should check only if there are downstream
760-
elif cmd in {ideSug, ideOutline, ideHighlight, ideDef}:
761-
let fileIndex = fileInfoIdx(conf, file)
762-
if graph.needsCompilation(fileIndex):
763-
graph.recompilePartially(fileIndex)
786+
elif cmd in {ideSug, ideOutline, ideHighlight, ideDef} and
787+
(graph.needsCompilation(fileIndex) or cmd == ideSug):
788+
# for ideSug use v2 implementation
789+
if cmd == ideSug:
790+
conf.m.trackPos = newLineInfo(fileIndex, line, col)
791+
conf.m.trackPosAttached = false
792+
else:
793+
conf.m.trackPos = newLineInfo(InvalidFileIdx, 0, 0)
794+
795+
graph.recompilePartially(fileIndex)
764796

765797
case cmd
766798
of ideDef:
767-
let
768-
fileIdx = fileInfoIdx(conf, file)
769-
(sym, info) = graph.findSymData(file, line, col)
799+
let (sym, info) = graph.findSymData(file, line, col)
770800
if sym != nil:
771-
graph.suggestResultUseOrDef(sym, sym.info)
801+
graph.suggestResult(sym, sym.info)
772802
of ideUse, ideDus:
773803
let symbol = graph.findSymData(file, line, col).sym
774804
if symbol != nil:
775805
for (sym, info) in graph.suggestSymbolsIter:
776806
if sym == symbol:
777-
graph.suggestResultUseOrDef(sym, info)
807+
graph.suggestResult(sym, info)
778808
of ideHighlight:
779809
let sym = graph.findSymData(file, line, col).sym
780810
if sym != nil:
781811
let usages = graph
782812
.suggestSymbols
783-
.getOrDefault(fileInfoIdx(conf, file))
813+
.getOrDefault(fileIndex, @[])
784814
.filterIt(it.sym == sym)
785815
myLog fmt "Found {usages.len} usages in {file.string}"
786816
for (sym, info) in usages:
787-
graph.suggestResultUseOrDef(sym, info)
817+
graph.suggestResult(sym, info)
788818
of ideRecompile:
789-
if file.string != "clean": graph.recompileFullProject_v3()
790-
else: graph.recompilePartially()
819+
graph.recompileFullProject()
791820
of ideChanged:
792-
graph.markDirtyIfNeeded(file.string, fileInfoIdx(conf, file))
821+
graph.markDirtyIfNeeded(file.string, fileIndex)
822+
of ideSug:
823+
# ideSug performs partial build of the file, thus mark it dirty for the
824+
# future calls.
825+
graph.markDirtyIfNeeded(file.string, fileIndex)
793826
of ideOutline:
794827
let
795-
fileIdx = fileInfoIdx(conf, file)
796-
module = graph.getModule fileIdx
828+
module = graph.getModule fileIndex
797829
symbols = graph.suggestSymbols
798-
.getOrDefault(fileIdx)
799-
.filterIt(it.sym.info == it.info and it.sym.owner == module)
830+
.getOrDefault(fileIndex, @[])
831+
.filterIt(it.sym.info == it.info and
832+
(it.sym.owner == module or
833+
it.sym.kind in searchableSymKinds))
800834
for (sym, _) in symbols:
801835
suggestResult(
802836
conf,
@@ -808,10 +842,9 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
808842
suggestResult(graph.config, sug)
809843
of ideGlobalSymbols:
810844
var counter = 0
811-
let reg = re(string(file))
812845
for (sym, info) in graph.suggestSymbolsIter:
813-
if sfGlobal in sym.flags:
814-
if find(cstring(sym.name.s), reg, 0, sym.name.s.len) != -1:
846+
if sfGlobal in sym.flags or sym.kind in searchableSymKinds:
847+
if contains(sym.name.s, file.string):
815848
inc counter
816849
suggestResult(conf,
817850
symToSuggest(graph, sym, isLocal=false,
@@ -820,8 +853,7 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
820853
if counter > 100:
821854
break
822855
else:
823-
# TODO ideSug
824-
discard
856+
myLog fmt "Discarding {cmd}"
825857

826858
# v3 end
827859
when isMainModule:

nimsuggest/tests/fixtures/nimble.nim

-7
This file was deleted.

nimsuggest/tests/fixtures/packageinfotypes.nim

-3
This file was deleted.

nimsuggest/tests/tuse_def_struct_two_files.nim

-9
This file was deleted.

nimsuggest/tests/tv3.nim

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# tests v3
2+
3+
type
4+
Foo* = ref object of RootObj
5+
bar*: string
6+
7+
proc test(f: Foo) =
8+
echo f.ba#[!]#r
9+
10+
discard """
11+
$nimsuggest --v3 --tester $file
12+
>use $1
13+
def skField tv3.Foo.bar string $file 5 4 "" 100
14+
use skField tv3.Foo.bar string $file 8 9 "" 100
15+
>def $1
16+
def skField tv3.Foo.bar string $file 5 4 "" 100
17+
>outline $1
18+
outline skType tv3.Foo Foo $file 4 2 "" 100
19+
outline skField tv3.Foo.bar string $file 5 4 "" 100
20+
outline skProc tv3.test proc (f: Foo){.gcsafe, locks: 0.} $file 7 5 "" 100
21+
>sug $1
22+
sug skField bar string $file 5 4 "" 100 Prefix
23+
>globalSymbols test
24+
def skProc tv3.test proc (f: Foo){.gcsafe, locks: 0.} $file 7 5 "" 100
25+
"""

0 commit comments

Comments
 (0)