10
10
import compiler/ renderer
11
11
import strformat
12
12
import tables
13
- import re
14
13
import std/ sha1
15
14
import segfaults
15
+ import times
16
+
16
17
# # Nimsuggest is a tool that helps to give editors IDE like capabilities.
17
18
18
19
when not defined (nimcore):
@@ -28,6 +29,7 @@ import compiler / [options, commands, modules, sem,
28
29
idents, modulegraphs, prefixmatches, lineinfos, cmdlinehelper,
29
30
pathutils]
30
31
32
+
31
33
when defined (windows):
32
34
import winlean
33
35
else :
@@ -173,11 +175,12 @@ proc symFromInfo(graph: ModuleGraph; trackPos: TLineInfo): PSym =
173
175
174
176
proc executeNoHooks (cmd: IdeCmd , file, dirtyfile: AbsoluteFile , line, col: int ;
175
177
graph: ModuleGraph ) =
176
- if graph.config.suggestVersion == 3 :
178
+ let conf = graph.config
179
+
180
+ if conf.suggestVersion == 3 :
177
181
executeNoHooksV3 (cmd, file, dirtyfile, line, col, graph)
178
182
return
179
183
180
- let conf = graph.config
181
184
myLog (" cmd: " & $ cmd & " , file: " & file.string &
182
185
" , dirtyFile: " & dirtyfile.string &
183
186
" [" & $ line & " :" & $ col & " ]" )
@@ -481,14 +484,23 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
481
484
execute (conf.ideCmd, AbsoluteFile orig, AbsoluteFile dirtyfile, line, col, graph)
482
485
sentinel ()
483
486
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
+
484
496
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 ()
492
504
493
505
proc mainThread (graph: ModuleGraph ) =
494
506
let conf = graph.config
@@ -546,11 +558,17 @@ proc mainCommand(graph: ModuleGraph) =
546
558
conf.setErrorMaxHighMaybe # honor --errorMax even if it may not make sense here
547
559
# do not print errors, but log them
548
560
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
550
567
551
568
# compile the project before showing any input so that we already
552
569
# can answer questions right away:
553
- compileProject (graph)
570
+ benchmark " Initial compilation" :
571
+ compileProject (graph)
554
572
555
573
open (requests)
556
574
open (results)
@@ -668,35 +686,28 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
668
686
669
687
# v3 start
670
688
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
-
681
689
proc recompilePartially (graph: ModuleGraph , projectFileIdx = InvalidFileIdx ) =
682
690
if projectFileIdx == InvalidFileIdx :
683
691
myLog " Recompiling partially from root"
684
692
else :
685
693
myLog fmt " Recompiling partially starting from {graph.getModule(projectFileIdx)}"
686
694
695
+ # inst caches are breaking incremental compilation when the cache caches stuff
696
+ # from changed buffer
687
697
graph.typeInstCache.clear ()
688
698
graph.procInstCache.clear ()
699
+
689
700
GC_fullCollect ()
701
+
690
702
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)
693
705
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()}"
700
711
701
712
proc findSymData (graph: ModuleGraph , file: AbsoluteFile ; line, col: int ):
702
713
tuple [sym: PSym , info: TLineInfo ] =
@@ -709,42 +720,58 @@ proc findSymData(graph: ModuleGraph, file: AbsoluteFile; line, col: int):
709
720
710
721
proc markDirtyIfNeeded (graph: ModuleGraph , file: string , originalFileIdx: FileIndex ) =
711
722
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 :
713
724
myLog fmt " {file} changed compared to last compilation"
714
725
graph.markDirty originalFileIdx
715
726
graph.markClientsDirty originalFileIdx
716
727
else :
717
728
myLog fmt " No changes in file {file} compared to last compilation"
718
729
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,
725
738
info, 100 , PrefixMatch .None , false , 0 )
726
739
suggestResult (graph.config, suggest)
727
740
741
+ const
742
+ # kinds for ideOutline and ideGlobalSymbols
743
+ searchableSymKinds = {skField, skEnumField, skIterator, skMethod, skFunc, skProc, skConverter, skTemplate}
744
+
728
745
proc executeNoHooksV3 (cmd: IdeCmd , file: AbsoluteFile , dirtyfile: AbsoluteFile , line, col: int ;
729
746
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 ) =
732
751
let suggest = Suggest (section: ideChk, filePath: toFullPath (conf, info),
733
752
line: toLinenumber (info), column: toColumn (info), doc: msg, forth: $ sev)
734
753
graph.suggestErrors.mgetOrPut (info.fileIndex, @ []).add suggest
735
- var isKnownFile = true
736
- let conf = graph.config
754
+
737
755
conf.ideCmd = cmd
738
756
739
757
myLog fmt " cmd: {cmd}, file: {file}[{line}:{col}], dirtyFile: {dirtyfile}"
740
758
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
745
765
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))
748
775
749
776
# these commands require fully compiled project
750
777
if cmd in {ideUse, ideDus, ideGlobalSymbols, ideChk} and graph.needsCompilation ():
@@ -756,47 +783,54 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
756
783
graph.unmarkAllDirty ()
757
784
758
785
# 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)
764
796
765
797
case cmd
766
798
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)
770
800
if sym != nil :
771
- graph.suggestResultUseOrDef (sym, sym.info)
801
+ graph.suggestResult (sym, sym.info)
772
802
of ideUse, ideDus:
773
803
let symbol = graph.findSymData (file, line, col).sym
774
804
if symbol != nil :
775
805
for (sym, info) in graph.suggestSymbolsIter:
776
806
if sym == symbol:
777
- graph.suggestResultUseOrDef (sym, info)
807
+ graph.suggestResult (sym, info)
778
808
of ideHighlight:
779
809
let sym = graph.findSymData (file, line, col).sym
780
810
if sym != nil :
781
811
let usages = graph
782
812
.suggestSymbols
783
- .getOrDefault (fileInfoIdx (conf, file) )
813
+ .getOrDefault (fileIndex, @ [] )
784
814
.filterIt (it.sym == sym)
785
815
myLog fmt " Found {usages.len} usages in {file.string}"
786
816
for (sym, info) in usages:
787
- graph.suggestResultUseOrDef (sym, info)
817
+ graph.suggestResult (sym, info)
788
818
of ideRecompile:
789
- if file.string != " clean" : graph.recompileFullProject_v3 ()
790
- else : graph.recompilePartially ()
819
+ graph.recompileFullProject ()
791
820
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)
793
826
of ideOutline:
794
827
let
795
- fileIdx = fileInfoIdx (conf, file)
796
- module = graph.getModule fileIdx
828
+ module = graph.getModule fileIndex
797
829
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))
800
834
for (sym, _) in symbols:
801
835
suggestResult (
802
836
conf,
@@ -808,10 +842,9 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
808
842
suggestResult (graph.config, sug)
809
843
of ideGlobalSymbols:
810
844
var counter = 0
811
- let reg = re (string (file))
812
845
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 ) :
815
848
inc counter
816
849
suggestResult (conf,
817
850
symToSuggest (graph, sym, isLocal= false ,
@@ -820,8 +853,7 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
820
853
if counter > 100 :
821
854
break
822
855
else :
823
- # TODO ideSug
824
- discard
856
+ myLog fmt " Discarding {cmd}"
825
857
826
858
# v3 end
827
859
when isMainModule :
0 commit comments