@@ -338,7 +338,7 @@ structure SetupImportsResult where
338
338
register_builtin_option internal.minimalSnapshots : Bool := {
339
339
defValue := false
340
340
descr := "reduce information stored in snapshots to the minimum necessary for the cmdline \
341
- driver: diagnostics and environment per command "
341
+ driver: diagnostics per command and final full snapshot "
342
342
}
343
343
344
344
/--
@@ -369,16 +369,16 @@ where
369
369
parseHeader (old? : Option HeaderParsedSnapshot) : LeanProcessingM HeaderParsedSnapshot := do
370
370
let ctx ← read
371
371
let ictx := ctx.toInputContext
372
- let unchanged old newParserState :=
372
+ let unchanged old newStx newParserState :=
373
373
-- when header syntax is unchanged, reuse import processing task as is and continue with
374
374
-- parsing the first command, synchronously if possible
375
- -- NOTE: even if the syntax tree is functionally unchanged, the new parser state may still
376
- -- have changed because of trailing whitespace and comments etc., so it is passed separately
377
- -- from `old`
375
+ -- NOTE: even if the syntax tree is functionally unchanged, its concrete structure and the new
376
+ -- parser state may still have changed because of trailing whitespace and comments etc., so
377
+ -- they are passed separately from `old`
378
378
if let some oldSuccess := old.result? then
379
379
return {
380
380
ictx
381
- stx := old.stx
381
+ stx := newStx
382
382
diagnostics := old.diagnostics
383
383
cancelTk? := ctx.newCancelTk
384
384
result? := some { oldSuccess with
@@ -452,31 +452,47 @@ where
452
452
let setup ← match (← setupImports stx) with
453
453
| .ok setup => pure setup
454
454
| .error snap => return snap
455
+
456
+ let startTime := (← IO.monoNanosNow).toFloat / 1000000000
455
457
-- allows `headerEnv` to be leaked, which would live until the end of the process anyway
456
458
let (headerEnv, msgLog) ← Elab.processHeader (leakEnv := true ) stx setup.opts .empty
457
459
ctx.toInputContext setup.trustLevel
460
+ let stopTime := (← IO.monoNanosNow).toFloat / 1000000000
458
461
let diagnostics := (← Snapshot.Diagnostics.ofMessageLog msgLog)
459
462
if msgLog.hasErrors then
460
463
return { diagnostics, result? := none }
461
464
462
465
let headerEnv := headerEnv.setMainModule setup.mainModuleName
466
+ let mut traceState := default
467
+ if trace.profiler.output.get? setup.opts |>.isSome then
468
+ traceState := {
469
+ traces := #[{
470
+ ref := .missing,
471
+ msg := .trace { cls := `Import, startTime, stopTime }
472
+ (.ofFormat "importing" ) #[]
473
+ : TraceElem
474
+ }].toPArray'
475
+ }
463
476
let cmdState := Elab.Command.mkState headerEnv msgLog setup.opts
464
- let cmdState := { cmdState with infoState := {
465
- enabled := true
466
- trees := #[Elab.InfoTree.context (.commandCtx {
467
- env := headerEnv
468
- fileMap := ctx.fileMap
469
- ngen := { namePrefix := `_import }
470
- }) (Elab.InfoTree.node
471
- (Elab.Info.ofCommandInfo { elaborator := `header, stx })
472
- (stx[1 ].getArgs.toList.map (fun importStx =>
473
- Elab.InfoTree.node (Elab.Info.ofCommandInfo {
474
- elaborator := `import
475
- stx := importStx
476
- }) #[].toPArray'
477
- )).toPArray'
478
- )].toPArray'
479
- }}
477
+ let cmdState := { cmdState with
478
+ infoState := {
479
+ enabled := true
480
+ trees := #[Elab.InfoTree.context (.commandCtx {
481
+ env := headerEnv
482
+ fileMap := ctx.fileMap
483
+ ngen := { namePrefix := `_import }
484
+ }) (Elab.InfoTree.node
485
+ (Elab.Info.ofCommandInfo { elaborator := `header, stx })
486
+ (stx[1 ].getArgs.toList.map (fun importStx =>
487
+ Elab.InfoTree.node (Elab.Info.ofCommandInfo {
488
+ elaborator := `import
489
+ stx := importStx
490
+ }) #[].toPArray'
491
+ )).toPArray'
492
+ )].toPArray'
493
+ }
494
+ traceState
495
+ }
480
496
let prom ← IO.Promise.new
481
497
-- The speedup of these `markPersistent`s is negligible but they help in making unexpected
482
498
-- `inc_ref_cold`s more visible
@@ -540,12 +556,16 @@ where
540
556
env := cmdState.env, options := scope.opts, currNamespace := scope.currNamespace
541
557
openDecls := scope.openDecls
542
558
}
543
- let (stx, parserState, msgLog) := Parser.parseCommand ctx.toInputContext pmctx parserState
544
- .empty
559
+ let (stx, parserState, msgLog) :=
560
+ profileit "parsing" scope.opts fun _ =>
561
+ Parser.parseCommand ctx.toInputContext pmctx parserState .empty
545
562
546
563
-- semi-fast path
547
564
if let some old := old? then
548
- if (← isBeforeEditPos parserState.pos ctx) && old.data.stx == stx then
565
+ -- NOTE: as `parserState.pos` includes trailing whitespace, this forces reprocessing even if
566
+ -- only that whitespace changes, which is wasteful but still necessary because it may
567
+ -- influence the range of error messages such as from a trailing `exact`
568
+ if stx.eqWithInfo old.data.stx then
549
569
-- Here we must make sure to pass the *new* parser state; see NOTE in `unchanged`
550
570
return (← unchanged old parserState)
551
571
-- on first change, make sure to cancel old invocation
@@ -567,23 +587,27 @@ where
567
587
let next? ← if Parser.isTerminalCommand stx then pure none
568
588
-- for now, wait on "command finished" snapshot before parsing next command
569
589
else some <$> IO.Promise.new
570
- prom.resolve <| .mk (nextCmdSnap? := next?.map ({ range? := some ⟨parserState.pos, ctx.input.endPos⟩, task := ·.result })) {
571
- diagnostics := (← Snapshot.Diagnostics.ofMessageLog msgLog)
572
- stx := if minimalSnapshots then .missing else stx
573
- parserState := if minimalSnapshots then {} else parserState
590
+ let diagnostics ← Snapshot.Diagnostics.ofMessageLog msgLog
591
+ let data := if minimalSnapshots && !Parser.isTerminalCommand stx then {
592
+ diagnostics
593
+ stx := .missing
594
+ parserState := {}
574
595
elabSnap := { range? := stx.getRange?, task := elabPromise.result }
575
- finishedSnap := .pure <|
576
- if minimalSnapshots then
577
- { finishedSnap with
578
- infoTree? := none
579
- cmdState := {
580
- env := Runtime.markPersistent (if Parser.isTerminalCommand stx then finishedSnap.cmdState.env else initEnv)
581
- maxRecDepth := 0
582
- }
583
- }
584
- else finishedSnap
596
+ finishedSnap := .pure {
597
+ diagnostics := finishedSnap.diagnostics
598
+ infoTree? := none
599
+ cmdState := {
600
+ env := initEnv
601
+ maxRecDepth := 0
602
+ }
603
+ }
585
604
tacticCache
605
+ } else {
606
+ diagnostics, stx, parserState, tacticCache
607
+ elabSnap := { range? := stx.getRange?, task := elabPromise.result }
608
+ finishedSnap := .pure finishedSnap
586
609
}
610
+ prom.resolve <| .mk (nextCmdSnap? := next?.map ({ range? := some ⟨parserState.pos, ctx.input.endPos⟩, task := ·.result })) data
587
611
if let some next := next? then
588
612
parseCmd none parserState finishedSnap.cmdState initEnv next ctx
589
613
@@ -652,16 +676,15 @@ def processCommands (inputCtx : Parser.InputContext) (parserState : Parser.Modul
652
676
|>.run { inputCtx with }
653
677
return prom.result
654
678
655
-
656
- /-- Waits for and returns final environment, if importing was successful. -/
657
- partial def waitForFinalEnv? (snap : InitialSnapshot) : Option Environment := do
679
+ /-- Waits for and returns final command state, if importing was successful. -/
680
+ partial def waitForFinalCmdState? (snap : InitialSnapshot) : Option Command.State := do
658
681
let snap ← snap.result?
659
682
let snap ← snap.processedSnap.get.result?
660
683
goCmd snap.firstCmdSnap.get
661
684
where goCmd snap :=
662
685
if let some next := snap.nextCmdSnap? then
663
686
goCmd next.get
664
687
else
665
- snap.data.finishedSnap.get.cmdState.env
688
+ snap.data.finishedSnap.get.cmdState
666
689
667
690
end Lean
0 commit comments