@@ -11,7 +11,7 @@ public import served.utils.async;
11
11
12
12
import core.time : msecs, seconds;
13
13
14
- import std.algorithm : any, canFind, endsWith, map;
14
+ import std.algorithm : any, canFind, endsWith, map, remove ;
15
15
import std.array : appender, array;
16
16
import std.conv : text, to;
17
17
import std.datetime.stopwatch : StopWatch;
@@ -463,6 +463,25 @@ struct RootSuggestion
463
463
bool useDub;
464
464
}
465
465
466
+ bool dubRecipeExists (string rootDir, bool testPackageJson = false )
467
+ {
468
+ if (fs.exists(chainPath(rootDir, " dub.json" )) || fs.exists(chainPath(rootDir, " dub.sdl" )))
469
+ return true ;
470
+ if (testPackageJson && fs.exists(chainPath(rootDir, " package.json" )))
471
+ {
472
+ try
473
+ {
474
+ scope packageJson = fs.readText(chainPath(rootDir, " package.json" ));
475
+ if (seemsLikeDubJson(packageJson))
476
+ return true ;
477
+ }
478
+ catch (Exception )
479
+ {
480
+ }
481
+ }
482
+ return false ;
483
+ }
484
+
466
485
RootSuggestion[] rootsForProject (string root, bool recursive, string [] blocked,
467
486
string [] extra)
468
487
{
@@ -478,19 +497,7 @@ RootSuggestion[] rootsForProject(string root, bool recursive, string[] blocked,
478
497
ret ~= RootSuggestion(dir, useDub);
479
498
}
480
499
481
- bool rootDub = fs.exists(chainPath(root, " dub.json" )) || fs.exists(chainPath(root, " dub.sdl" ));
482
- if (! rootDub && fs.exists(chainPath(root, " package.json" )))
483
- {
484
- try
485
- {
486
- auto packageJson = fs.readText(chainPath(root, " package.json" ));
487
- if (seemsLikeDubJson(packageJson))
488
- rootDub = true ;
489
- }
490
- catch (Exception )
491
- {
492
- }
493
- }
500
+ bool rootDub = dubRecipeExists(root, true );
494
501
addSuggestion(root, rootDub);
495
502
496
503
if (recursive)
@@ -511,7 +518,7 @@ RootSuggestion[] rootsForProject(string root, bool recursive, string[] blocked,
511
518
foreach (dir; extra)
512
519
{
513
520
string p = buildNormalizedPath(root, dir);
514
- addSuggestion(p, fs.exists(chainPath(p, " dub.json " )) || fs.exists(chainPath(p, " dub.sdl " ) ));
521
+ addSuggestion(p, dubRecipeExists (p ));
515
522
}
516
523
info(" Root Suggestions: " , ret);
517
524
return ret;
@@ -521,7 +528,7 @@ void doStartup(string workspaceUri, UserConfiguration userConfig)
521
528
{
522
529
ensureStartedUp(userConfig);
523
530
524
- Workspace* proj = &workspace(workspaceUri);
531
+ Workspace* proj = &workspace(workspaceUri, false );
525
532
if (proj is &fallbackWorkspace)
526
533
{
527
534
error(" Trying to do startup on unknown workspace " , workspaceUri, " ?" );
@@ -536,7 +543,6 @@ void doStartup(string workspaceUri, UserConfiguration userConfig)
536
543
WorkspaceD.Instance instance;
537
544
}
538
545
539
- bool gotOneDub;
540
546
scope roots = appender! (Root[]);
541
547
542
548
auto rootSuggestions = rootsForProject(workspaceUri.uriToFile, proj.config.d.scanAllFolders,
@@ -595,8 +601,102 @@ void doStartup(string workspaceUri, UserConfiguration userConfig)
595
601
trace(" Started all completion servers in " , dcdTimer.peek);
596
602
}
597
603
604
+ @protocolMethod(" served/forceLoadProjects" )
605
+ bool [] forceLoadProjects (string [] rootPaths)
606
+ {
607
+ struct Root
608
+ {
609
+ string uri;
610
+ WorkspaceD.Instance instance;
611
+ bool success;
612
+ }
613
+
614
+ Root[] roots;
615
+ roots.length = rootPaths.length;
616
+
617
+ foreach (i, rootPath; rootPaths)
618
+ {
619
+ string rootUri = rootPath.uriFromFile;
620
+
621
+ Workspace* proj = &workspace(rootUri, false );
622
+ if (proj is &fallbackWorkspace)
623
+ {
624
+ error(" Trying to do startup on unknown workspace " , rootUri, " ?" );
625
+ roots[i].success = false ;
626
+ continue ;
627
+ }
628
+ trace(" Initializing serve-d for " ~ rootUri);
629
+
630
+ bool isDub = dubRecipeExists(rootPath, true );
631
+ RootSuggestion suggestion;
632
+ suggestion.dir = rootPath;
633
+ suggestion.useDub = isDub;
634
+
635
+ reportProgress(ProgressType.workspaceStartup, i, rootPaths.length, rootUri);
636
+ info(" registering instance for root " , suggestion);
637
+
638
+ WConfiguration config;
639
+ config.base = [
640
+ " dcd" : WConfiguration.Section([
641
+ " clientPath" : WConfiguration.ValueT(proj.config.dcdClientPath.userPath),
642
+ " serverPath" : WConfiguration.ValueT(proj.config.dcdServerPath.userPath),
643
+ " port" : WConfiguration.ValueT(9166 )
644
+ ]),
645
+ " dmd" : WConfiguration.Section([
646
+ " path" : WConfiguration.ValueT(proj.config.d.dmdPath.userPath)
647
+ ])
648
+ ];
649
+ auto instance = backend.addInstance(rootPath, config);
650
+ if (! activeInstance)
651
+ activeInstance = instance;
652
+
653
+ roots[i].instance = instance;
654
+
655
+ emitExtensionEvent! onProjectAvailable(instance, rootPath, rootUri);
656
+
657
+ if (auto lazyInstance = cast (LazyWorkspaceD.LazyInstance)instance)
658
+ {
659
+ auto lazyLoadCallback (WorkspaceD.Instance instance, string rootPath, string rootUri, RootSuggestion suggestion)
660
+ {
661
+ return () => delayedProjectActivation(instance, rootPath, rootUri, suggestion, true );
662
+ }
663
+
664
+ lazyInstance.onLazyLoadInstance(lazyLoadCallback(instance, rootPath, rootUri, suggestion));
665
+ }
666
+ else
667
+ {
668
+ delayedProjectActivation(instance, rootPath, rootUri, suggestion, true );
669
+ }
670
+
671
+ roots[i].uri = rootUri;
672
+ roots[i].success = true ;
673
+ }
674
+
675
+ trace(" Starting auto completion service..." );
676
+ StopWatch dcdTimer;
677
+ dcdTimer.start();
678
+ foreach (i, root; roots)
679
+ {
680
+ if (root.success)
681
+ {
682
+ reportProgress(ProgressType.completionStartup, i, roots.length,
683
+ root.instance.cwd.uriFromFile);
684
+
685
+ lazyStartDCDServer(root.instance, root.uri);
686
+ }
687
+ }
688
+ dcdTimer.stop();
689
+ trace(" Started all completion servers in " , dcdTimer.peek);
690
+
691
+ return roots.map! " a.success" .array;
692
+ }
693
+
694
+ // / If true, "ask" will act as "skip", but will send coded/skippedLoads afterwards
695
+ __gshared bool bundleAskLoads;
598
696
shared int totalLoadedProjects;
599
- void delayedProjectActivation (WorkspaceD.Instance instance, string workspaceRoot, string workspaceUri, RootSuggestion root)
697
+ string [] skippedRoots;
698
+ void delayedProjectActivation (WorkspaceD.Instance instance, string workspaceRoot, string workspaceUri, RootSuggestion root,
699
+ bool skipManyProjectsAction = false )
600
700
{
601
701
import core.atomic ;
602
702
@@ -609,32 +709,43 @@ void delayedProjectActivation(WorkspaceD.Instance instance, string workspaceRoot
609
709
610
710
auto numLoaded = atomicOp! " +=" (totalLoadedProjects, 1 );
611
711
612
- auto manyProjectsAction = cast (ManyProjectsAction) proj.config.d.manyProjectsAction;
613
- auto manyThreshold = proj.config.d.manyProjectsThreshold;
614
- if (manyThreshold > 0 && numLoaded > manyThreshold)
712
+ if (! skipManyProjectsAction)
615
713
{
616
- switch (manyProjectsAction)
714
+ auto manyProjectsAction = cast (ManyProjectsAction) proj.config.d.manyProjectsAction;
715
+ auto manyThreshold = proj.config.d.manyProjectsThreshold;
716
+ if (manyThreshold > 0 && numLoaded > manyThreshold)
617
717
{
618
- case ManyProjectsAction.ask:
619
- auto loadButton = translate! " d.served.tooManySubprojects.load" ;
620
- auto skipButton = translate! " d.served.tooManySubprojects.skip" ;
621
- auto res = rpc.window.requestMessage(MessageType.warning,
622
- translate! " d.served.tooManySubprojects.path" (root.dir),
623
- [loadButton, skipButton]);
624
- if (res != loadButton)
625
- goto case ManyProjectsAction.skip;
626
- break ;
627
- case ManyProjectsAction.load:
628
- break ;
629
- default :
630
- error(" Ignoring invalid manyProjectsAction value " , manyProjectsAction, " , defaulting to skip" );
631
- goto case ;
632
- case ManyProjectsAction.skip:
633
- backend.removeInstance(workspaceRoot);
634
- throw new Exception (" skipping load of this instance" );
718
+ switch (manyProjectsAction)
719
+ {
720
+ case ManyProjectsAction.ask:
721
+ if (bundleAskLoads)
722
+ {
723
+ skippedRoots ~= workspaceRoot;
724
+ notifySkippedRoots();
725
+ goto case ManyProjectsAction.skip;
726
+ }
727
+ auto loadButton = translate! " d.served.tooManySubprojects.load" ;
728
+ auto skipButton = translate! " d.served.tooManySubprojects.skip" ;
729
+ auto res = rpc.window.requestMessage(MessageType.warning,
730
+ translate! " d.served.tooManySubprojects.path" (root.dir),
731
+ [loadButton, skipButton]);
732
+ if (res != loadButton)
733
+ goto case ManyProjectsAction.skip;
734
+ break ;
735
+ case ManyProjectsAction.load:
736
+ break ;
737
+ default :
738
+ error(" Ignoring invalid manyProjectsAction value " , manyProjectsAction, " , defaulting to skip" );
739
+ goto case ;
740
+ case ManyProjectsAction.skip:
741
+ backend.removeInstance(workspaceRoot);
742
+ throw new Exception (" skipping load of this instance" );
743
+ }
635
744
}
636
745
}
637
746
747
+ skippedRoots = skippedRoots.remove! (a => a == workspaceRoot);
748
+
638
749
info(" Initializing instance for root " , root);
639
750
StopWatch rootTimer;
640
751
rootTimer.start();
@@ -709,6 +820,15 @@ void delayedProjectActivation(WorkspaceD.Instance instance, string workspaceRoot
709
820
info(" Root " , root, " initialized in " , rootTimer.peek);
710
821
}
711
822
823
+ void notifySkippedRoots ()
824
+ {
825
+ static int timer;
826
+ clearTimeout(timer);
827
+ timer = setTimeout(delegate () {
828
+ rpc.notifyMethod(" coded/skippedLoads" , SkippedLoadsNotification(skippedRoots));
829
+ }, 500. msecs);
830
+ }
831
+
712
832
void didLoadDubProject ()
713
833
{
714
834
static bool loadedDub = false ;
0 commit comments