コード例 #1
0
        public static RdTask <T> ToRdTask <T>(this IRdTask <T> task, Lifetime lifetime)
        {
            if (task is RdTask <T> rdTask)
            {
                return(rdTask);
            }

            var newRdTask = new RdTask <T>();

            task.Result.Advise(lifetime, result =>
            {
                switch (result.Status)
                {
                case RdTaskStatus.Success:
                    newRdTask.Set(result.Result);
                    break;

                case RdTaskStatus.Canceled:
                    newRdTask.SetCancelled();
                    break;

                case RdTaskStatus.Faulted:
                    newRdTask.Set(result.Error);
                    break;
                }
            });
            return(newRdTask);
        }
コード例 #2
0
        private void SupportAbort(object runner)
        {
            var unityTestAssemblyRunnerField =
                runner.GetType().GetField("m_Runner", BindingFlags.Instance | BindingFlags.NonPublic);

            if (unityTestAssemblyRunnerField != null)
            {
                var unityTestAssemblyRunner = unityTestAssemblyRunnerField.GetValue(runner);
                var stopRunMethod           = unityTestAssemblyRunner.GetType()
                                              .GetMethod("StopRun", BindingFlags.Instance | BindingFlags.Public);
                myLaunch.Abort.Set((lifetime, _) =>
                {
                    ourLogger.Verbose("Call StopRun method via reflection.");
                    var task = new RdTask <bool>();
                    try
                    {
                        stopRunMethod.Invoke(unityTestAssemblyRunner, null);
                        task.Set(true);
                    }
                    catch (Exception)
                    {
                        ourLogger.Verbose("Call StopRun method failed.");
                        task.Set(false);
                    }
                    return(task);
                });
            }
        }
コード例 #3
0
        private static void AdviseRunMethod(EditorPluginModel model)
        {
            model.RunMethodInUnity.Set((lifetime, data) =>
            {
                var task = new RdTask <RunMethodResult>();
                MainThreadDispatcher.Instance.Queue(() =>
                {
                    if (!lifetime.IsAlive)
                    {
                        task.SetCancelled();
                        return;
                    }

                    try
                    {
                        ourLogger.Verbose($"Attempt to execute {data.MethodName}");
                        var assemblies = AppDomain.CurrentDomain.GetAssemblies();
                        var assembly   = assemblies
                                         .FirstOrDefault(a => a.GetName().Name.Equals(data.AssemblyName));
                        if (assembly == null)
                        {
                            throw new Exception($"Could not find {data.AssemblyName} assembly in current AppDomain");
                        }

                        var type = assembly.GetType(data.TypeName);
                        if (type == null)
                        {
                            throw new Exception($"Could not find {data.TypeName} in assembly {data.AssemblyName}.");
                        }

                        var method = type.GetMethod(data.MethodName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);

                        if (method == null)
                        {
                            throw new Exception($"Could not find {data.MethodName} in type {data.TypeName}");
                        }

                        try
                        {
                            method.Invoke(null, null);
                        }
                        catch (Exception e)
                        {
                            Debug.LogException(e);
                        }

                        task.Set(new RunMethodResult(true, string.Empty, string.Empty));
                    }
                    catch (Exception e)
                    {
                        ourLogger.Log(LoggingLevel.WARN, $"Execute {data.MethodName} failed.", e);
                        task.Set(new RunMethodResult(false, e.Message, e.StackTrace));
                    }
                });
                return(task);
            });
        }
コード例 #4
0
        private static void AdviseRefresh(EditorPluginModel model)
        {
            model.Refresh.Set((l, force) =>
            {
                var refreshTask = new RdTask <Unit>();
                void SendResult()
                {
                    if (!EditorApplication.isCompiling)
                    {
                        // ReSharper disable once DelegateSubtraction
                        EditorApplication.update -= SendResult;
                        ourLogger.Verbose("Refresh: SyncSolution Completed");
                        refreshTask.Set(Unit.Instance);
                    }
                }

                ourLogger.Verbose("Refresh: SyncSolution Enqueue");
                MainThreadDispatcher.Instance.Queue(() =>
                {
                    if (!EditorApplication.isPlaying && EditorPrefsWrapper.AutoRefresh || force != RefreshType.Normal)
                    {
                        try
                        {
                            if (force == RefreshType.ForceRequestScriptReload)
                            {
                                ourLogger.Verbose("Refresh: RequestScriptReload");
                                UnityEditorInternal.InternalEditorUtility.RequestScriptReload();
                            }

                            ourLogger.Verbose("Refresh: SyncSolution Started");
                            UnityUtils.SyncSolution();
                        }
                        catch (Exception e)
                        {
                            ourLogger.Error("Refresh failed with exception", e);
                        }
                        finally
                        {
                            EditorApplication.update += SendResult;
                        }
                    }
                    else
                    {
                        refreshTask.Set(Unit.Instance);
                        ourLogger.Verbose("AutoRefresh is disabled via Unity settings.");
                    }
                });
                return(refreshTask);
            });
        }
コード例 #5
0
        public LambdaPsiHost(ISolution solution, ISymbolCache symbolCache, PsiIconManager psiIconManager, IconHost iconHost,
                             ProjectModelViewHost projectModelViewHost, IShellLocks locks, ILogger logger)
        {
            _symbolCache          = symbolCache;
            _psiIconManager       = psiIconManager;
            _iconHost             = iconHost;
            _projectModelViewHost = projectModelViewHost;
            _locks  = locks;
            _logger = logger;

            var model = solution.GetProtocolSolution().GetLambdaPsiModel();

            model.DetermineHandlers.Set((lifetime, unit) =>
            {
                var task = new RdTask <List <HandlerCompletionItem> >();
                task.Set(DetermineHandlers(solution));
                return(task);
            });

            model.IsHandlerExists.Set((lifetime, request) =>
            {
                var task       = new RdTask <bool>();
                var className  = request.ClassName;
                var methodName = request.MethodName;
                var projectId  = request.ProjectId;

                var handlerExists = IsHandlerExists(lifetime, projectId, className, methodName);
                task.Set(handlerExists);
                return(task);
            });
        }
コード例 #6
0
        private static void AdviseRefresh(EditorPluginModel model)
        {
            model.Refresh.Set((l, force) =>
            {
                var task = new RdTask <Unit>();
                ourLogger.Verbose("Refresh: SyncSolution Enqueue");
                MainThreadDispatcher.Instance.Queue(() =>
                {
                    if (!EditorApplication.isPlaying && EditorPrefsWrapper.AutoRefresh || force != RefreshType.Normal)
                    {
                        if (force == RefreshType.ForceRequestScriptReload)
                        {
                            ourLogger.Verbose("Refresh: RequestScriptReload");
                            UnityEditorInternal.InternalEditorUtility.RequestScriptReload();
                        }

                        ourLogger.Verbose("Refresh: SyncSolution Started");
                        UnityUtils.SyncSolution();
                        ourLogger.Verbose("Refresh: SyncSolution Completed");
                    }
                    else
                    {
                        ourLogger.Verbose("AutoRefresh is disabled via Unity settings.");
                    }
                    task.Set(Unit.Instance);
                });
                return(task);
            });
        }
コード例 #7
0
 private static void AdviseExitUnity(EditorPluginModel model)
 {
     model.ExitUnity.Set((_, rdVoid) =>
     {
         var task = new RdTask <bool>();
         MainThreadDispatcher.Instance.Queue(() =>
         {
             try
             {
                 ourLogger.Verbose("ExitUnity: Started");
                 EditorApplication.Exit(0);
                 ourLogger.Verbose("ExitUnity: Completed");
                 task.Set(true);
             }
             catch (Exception e)
             {
                 ourLogger.Log(LoggingLevel.WARN, "EditorApplication.Exit failed.", e);
                 task.Set(false);
             }
         });
         return(task);
     });
 }
コード例 #8
0
        public LambdaHost(ISolution solution, ISymbolCache symbolCache, PsiIconManager psiIconManager, IconHost iconHost)
        {
            _model          = solution.GetProtocolSolution().GetLambdaModel();
            _symbolCache    = symbolCache;
            _psiIconManager = psiIconManager;
            _iconHost       = iconHost;

            _model.DetermineHandlers.Set((lifetime, unit) =>
            {
                var task = new RdTask <List <HandlerCompletionItem> >();
                task.Set(DetermineHandlers(solution));
                return(task);
            });
        }
コード例 #9
0
 private static void AdviseRefresh(EditorPluginModel model)
 {
     model.Refresh.Set((l, force) =>
     {
         var task = new RdTask <RdVoid>();
         MainThreadDispatcher.Instance.Queue(() =>
         {
             if (!EditorApplication.isPlaying && EditorPrefsWrapper.AutoRefresh || force)
             {
                 UnityUtils.SyncSolution();
             }
             else
             {
                 ourLogger.Verbose("AutoRefresh is disabled via Unity settings.");
             }
             task.Set(RdVoid.Instance);
         });
         return(task);
     });
 }
コード例 #10
0
        public AwsProjectHost(ISolution solution)
        {
            var model = solution.GetProtocolSolution().GetAwsProjectModel();

            model.GetProjectOutput.Set((lifetime, request) =>
            {
                var task = new RdTask <AwsProjectOutput>();
                var assemblyPathPrefix = FileSystemPath.Parse(request.ProjectPath);

                using (ReadLockCookie.Create())
                {
                    var allProjects = solution.GetAllProjects();

                    foreach (var project in allProjects)
                    {
                        var targetFrameworks = project.GetAllTargetFrameworks();
                        foreach (var targetFramework in targetFrameworks)
                        {
                            var assembly = project.GetOutputAssemblyInfo(targetFramework.FrameworkId);
                            if (assembly == null)
                            {
                                continue;
                            }

                            if (assembly.Location.FullPath.StartsWith(assemblyPathPrefix.FullPath))
                            {
                                task.Set(new AwsProjectOutput(assembly.AssemblyNameInfo.Name, assembly.Location.FullPath));
                                return(task);
                            }
                        }
                    }

                    task.SetCancelled();
                    return(task);
                }
            });
        }
コード例 #11
0
        private static UnityModel CreateModel(Protocol protocol, Lifetime lt)
        {
            var isPlayingAction = new Action(() =>
            {
                MainThreadDispatcher.Instance.Queue(() =>
                {
                    var isPlaying = EditorApplication.isPlayingOrWillChangePlaymode && EditorApplication.isPlaying;
                    ourModel?.Maybe.ValueOrDefault?.Play.SetValue(isPlaying);

                    var isPaused = EditorApplication.isPaused;
                    ourModel?.Maybe.ValueOrDefault?.Pause.SetValue(isPaused);
                });
            });
            var model = new UnityModel(lt, protocol);

            isPlayingAction(); // get Unity state
            model.Play.Advise(lt, play =>
            {
                MainThreadDispatcher.Instance.Queue(() =>
                {
                    var res = EditorApplication.isPlayingOrWillChangePlaymode && EditorApplication.isPlaying;
                    if (res != play)
                    {
                        EditorApplication.isPlaying = play;
                    }
                });
            });

            model.Pause.Advise(lt, pause =>
            {
                MainThreadDispatcher.Instance.Queue(() =>
                {
                    EditorApplication.isPaused = pause;
                });
            });
            model.LogModelInitialized.SetValue(new UnityLogModelInitialized());
            model.Refresh.Set((l, force) =>
            {
                var task = new RdTask <RdVoid>();
                MainThreadDispatcher.Instance.Queue(() =>
                {
                    if (EditorPrefsWrapper.AutoRefresh || force)
                    {
                        UnityUtils.SyncSolution();
                    }
                    else
                    {
                        ourLogger.Verbose("AutoRefresh is disabled via Unity settings.");
                    }
                    task.Set(RdVoid.Instance);
                });
                return(task);
            });
            model.Step.Set((l, x) =>
            {
                var task = new RdTask <RdVoid>();
                MainThreadDispatcher.Instance.Queue(() =>
                {
                    EditorApplication.Step();
                    task.Set(RdVoid.Instance);
                });
                return(task);
            });

            var isPlayingHandler = new EditorApplication.CallbackFunction(() => isPlayingAction());

// left for compatibility with Unity <= 5.5
#pragma warning disable 618
            lt.AddBracket(() => { EditorApplication.playmodeStateChanged += isPlayingHandler; },
                          () => { EditorApplication.playmodeStateChanged -= isPlayingHandler; });
#pragma warning restore 618
            //isPlayingHandler();

            // new api - not present in Unity 5.5
            //lt.AddBracket(() => { EditorApplication.pauseStateChanged+= IsPauseStateChanged(model);},
            //  () => { EditorApplication.pauseStateChanged -= IsPauseStateChanged(model); });

            ourLogger.Verbose("CreateModel finished.");

            return(model);
        }
コード例 #12
0
        internal static void SupportAbortNew(UnitTestLaunch launch)
        {
            try
            {
                const string assemblyName = "UnityEditor.TestRunner";
                const string typeName     = "UnityEditor.TestTools.TestRunner.Api.TestRunnerApi";
                const string methodName   = "CancelAllTestRuns";

                MethodInfo stopRunMethod = null;

                var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name.Equals(assemblyName));
                if (assembly == null)
                {
                    ourLogger.Verbose($"Could not find {assemblyName} in the AppDomain.");
                }
                else
                {
                    var apiType = assembly.GetType(typeName);
                    if (apiType == null)
                    {
                        ourLogger.Verbose($"Could not find {typeName} in the {assemblyName}.");
                    }
                    else
                    {
                        stopRunMethod = apiType.GetMethod(methodName);
                        if (stopRunMethod == null)
                        {
                            ourLogger.Verbose($"Could not find {methodName} in the {typeName}.");
                        }
                    }
                }

                launch.Abort.Set((lifetime, _) =>
                {
                    var task = new RdTask <bool>();

                    if (stopRunMethod != null)
                    {
                        ourLogger.Verbose($"Call {methodName} method via reflection.");
                        try
                        {
                            stopRunMethod.Invoke(null, null);
                            task.Set(true);
                            if (!launch.RunStarted.HasTrueValue()) // if RunStarted never happened
                            {
                                launch.RunResult(new RunResult(false));
                            }
                        }
                        catch (Exception)
                        {
                            ourLogger.Verbose($"Call {methodName} method failed.");
                            task.Set(false);
                        }
                    }
                    else
                    {
                        task.Set(false);
                    }

                    return(task);
                });
            }
            catch (Exception e)
            {
                ourLogger.Error(e, "Unexpected exception in SupportAbortNew");
            }
        }
コード例 #13
0
        private void CreateProtocols(FileSystemPath protocolInstancePath)
        {
            if (!protocolInstancePath.ExistsFile)
            {
                return;
            }

            List <ProtocolInstance> protocolInstanceList;

            try
            {
                protocolInstanceList = ProtocolInstance.FromJson(protocolInstancePath.ReadAllText2().Text);
            }
            catch (Exception e)
            {
                myLogger.Warn($"Unable to parse {protocolInstancePath}" + Environment.NewLine + e);
                return;
            }

            var protocolInstance = protocolInstanceList?.SingleOrDefault(a => a.SolutionName == mySolution.SolutionFilePath.NameWithoutExtension);

            if (protocolInstance == null)
            {
                return;
            }

            myLogger.Info($"EditorPlugin protocol port {protocolInstance.Port} for Solution: {protocolInstance.SolutionName}.");

            try
            {
                var lifetime = mySessionLifetimes.Next();
                myLogger.Info("Create protocol...");

                myLogger.Info("Creating SocketWire with port = {0}", protocolInstance.Port);
                var wire = new SocketWire.Client(lifetime, myDispatcher, protocolInstance.Port, "UnityClient");
                UnityWire.Value = wire;
                wire.BackwardsCompatibleWireFormat = true;

                var protocol = new Protocol("UnityEditorPlugin", new Serializers(),
                                            new Identities(IdKind.Client), myDispatcher, wire, lifetime);

                protocol.ThrowErrorOnOutOfSyncModels = false;

                protocol.OutOfSyncModels.AdviseOnce(lifetime, e =>
                {
                    if (myPluginInstallations.Contains(mySolution.SolutionFilePath))
                    {
                        return;
                    }

                    myPluginInstallations.Add(mySolution.SolutionFilePath); // avoid displaying Notification multiple times on each AppDomain.Reload in Unity

                    var appVersion = myUnityVersion.ActualVersionForSolution.Value;
                    if (appVersion < new Version(2019, 2))
                    {
                        var entry     = myBoundSettingsStore.Schema.GetScalarEntry((UnitySettings s) => s.InstallUnity3DRiderPlugin);
                        var isEnabled = myBoundSettingsStore.GetValueProperty <bool>(lifetime, entry, null).Value;
                        if (!isEnabled)
                        {
                            myHost.PerformModelAction(model => model.OnEditorModelOutOfSync());
                        }
                    }
                    else
                    {
                        var notification = new NotificationModel("Advanced Unity integration is unavailable",
                                                                 $"Please update External Editor to {myHostProductInfo.VersionMarketingString} in Unity Preferences.",
                                                                 true, RdNotificationEntryType.WARN);
                        mySolution.Locks.ExecuteOrQueue(lifetime, "OutOfSyncModels.Notify", () => myNotificationsModel.Notification(notification));
                    }
                });

                wire.Connected.WhenTrue(lifetime, lf =>
                {
                    myLogger.Info("WireConnected.");

                    var editor = new EditorPluginModel(lf, protocol);
                    editor.IsBackendConnected.Set(rdVoid => true);

                    if (PlatformUtil.RuntimePlatform == PlatformUtil.Platform.Windows)
                    {
                        var frontendProcess = Process.GetCurrentProcess().GetParent(); // RiderProcessId is not used on non-Windows, but this line gives bad warning in the log
                        if (frontendProcess != null)
                        {
                            editor.RiderProcessId.SetValue(frontendProcess.Id);
                        }
                    }

                    myHost.PerformModelAction(m => m.SessionInitialized.Value = true);

                    SubscribeToLogs(lf, editor);
                    SubscribeToOpenFile(editor);

                    editor.Play.Advise(lf, b => myHost.PerformModelAction(rd => rd.Play.SetValue(b)));
                    editor.Pause.Advise(lf, b => myHost.PerformModelAction(rd => rd.Pause.SetValue(b)));
                    editor.LastPlayTime.Advise(lf, time => myHost.PerformModelAction(rd => rd.LastPlayTime.SetValue(time)));
                    editor.LastInitTime.Advise(lf, time => myHost.PerformModelAction(rd => rd.LastInitTime.SetValue(time)));

                    editor.UnityProcessId.View(lf, (_, pid) => myHost.PerformModelAction(t => t.UnityProcessId.Set(pid)));

                    // I have split this into groups, because want to use async api for finding reference and pass them via groups to Unity
                    myHost.PerformModelAction(t => t.ShowFileInUnity.Advise(lf, v => editor.ShowFileInUnity.Fire(v)));
                    myHost.PerformModelAction(t => t.ShowPreferences.Advise(lf, v =>
                    {
                        editor.ShowPreferences.Fire();
                    }));

                    editor.EditorLogPath.Advise(lifetime,
                                                s => myHost.PerformModelAction(a => a.EditorLogPath.SetValue(s)));
                    editor.PlayerLogPath.Advise(lifetime,
                                                s => myHost.PerformModelAction(a => a.PlayerLogPath.SetValue(s)));

                    // Note that these are late-init properties. Once set, they are always set and do not allow nulls.
                    // This means that if/when the Unity <-> Backend protocol closes, they still retain the last value
                    // they had - so the front end will retain the log and application paths of the just-closed editor.
                    // Opening a new editor instance will reconnect and push a new value through to the front end
                    editor.UnityApplicationData.Advise(lifetime,
                                                       s => myHost.PerformModelAction(a =>
                    {
                        var version = UnityVersion.Parse(s.ApplicationVersion);
                        a.UnityApplicationData.SetValue(new UnityApplicationData(s.ApplicationPath,
                                                                                 s.ApplicationContentsPath, s.ApplicationVersion, UnityVersion.RequiresRiderPackage(version)));
                    }));
                    editor.ScriptCompilationDuringPlay.Advise(lifetime,
                                                              s => myHost.PerformModelAction(a => a.ScriptCompilationDuringPlay.Set(ConvertToScriptCompilationEnum(s))));

                    myHost.PerformModelAction(rd =>
                    {
                        rd.GenerateUIElementsSchema.Set((l, u) =>
                                                        editor.GenerateUIElementsSchema.Start(l, u).ToRdTask(l));
                    });

                    editor.BuildLocation.Advise(lf, b => myHost.PerformModelAction(rd => rd.BuildLocation.SetValue(b)));

                    myHost.PerformModelAction(rd =>
                    {
                        rd.RunMethodInUnity.Set((l, data) =>
                        {
                            var editorRdTask = editor.RunMethodInUnity.Start(l, new RunMethodData(data.AssemblyName, data.TypeName, data.MethodName)).ToRdTask(l);
                            var frontendRes  = new RdTask <JetBrains.Rider.Model.RunMethodResult>();

                            editorRdTask.Result.Advise(l, r =>
                            {
                                frontendRes.Set(new JetBrains.Rider.Model.RunMethodResult(r.Result.Success, r.Result.Message, r.Result.StackTrace));
                            });
                            return(frontendRes);
                        });
                    });

                    TrackActivity(editor, lf);

                    if (!myComponentLifetime.IsTerminated)
                    {
                        myLocks.ExecuteOrQueueEx(myComponentLifetime, "setModel",
                                                 () => { UnityModel.SetValue(editor); });
                    }

                    lf.AddAction(() =>
                    {
                        if (!myComponentLifetime.IsTerminated)
                        {
                            myLocks.ExecuteOrQueueEx(myComponentLifetime, "clearModel", () =>
                            {
                                myLogger.Info("Wire disconnected.");
                                myHost.PerformModelAction(m => m.SessionInitialized.Value = false);
                                UnityModel.SetValue(null);
                            });
                        }
                    });
                });
            }
            catch (Exception ex)
            {
                myLogger.Error(ex);
            }
        }
コード例 #14
0
        private static void AdviseUnityActions(EditorPluginModel model, Lifetime connectionLifetime)
        {
            var isPlayingAction = new Action(() =>
            {
                MainThreadDispatcher.Instance.Queue(() =>
                {
                    var isPlayOrWillChange = EditorApplication.isPlayingOrWillChangePlaymode;
                    var isPlaying          = isPlayOrWillChange && EditorApplication.isPlaying;
                    if (!model.Play.HasValue() || model.Play.HasValue() && model.Play.Value != isPlaying)
                    {
                        model.Play.SetValue(isPlaying);
                    }

                    var isPaused = EditorApplication.isPaused;
                    if (!model.Pause.HasValue() || model.Pause.HasValue() && model.Pause.Value != isPaused)
                    {
                        model.Pause.SetValue(isPaused);
                    }
                });
            });

            isPlayingAction(); // get Unity state
            model.Play.Advise(connectionLifetime, play =>
            {
                MainThreadDispatcher.Instance.Queue(() =>
                {
                    var res = EditorApplication.isPlayingOrWillChangePlaymode && EditorApplication.isPlaying;
                    if (res != play)
                    {
                        EditorApplication.isPlaying = play;
                    }
                });
            });

            model.Pause.Advise(connectionLifetime, pause =>
            {
                MainThreadDispatcher.Instance.Queue(() =>
                {
                    EditorApplication.isPaused = pause;
                });
            });

            model.Step.Set((l, x) =>
            {
                var task = new RdTask <RdVoid>();
                MainThreadDispatcher.Instance.Queue(() =>
                {
                    EditorApplication.Step();
                    task.Set(RdVoid.Instance);
                });
                return(task);
            });

            var isPlayingHandler = new EditorApplication.CallbackFunction(() => isPlayingAction());

// left for compatibility with Unity <= 5.5
#pragma warning disable 618
            connectionLifetime.AddBracket(() => { EditorApplication.playmodeStateChanged += isPlayingHandler; },
                                          () => { EditorApplication.playmodeStateChanged -= isPlayingHandler; });
#pragma warning restore 618
            // new api - not present in Unity 5.5
            // private static Action<PauseState> IsPauseStateChanged(UnityModel model)
            //    {
            //      return state => model?.Pause.SetValue(state == PauseState.Paused);
            //    }
        }
コード例 #15
0
ファイル: T4TestHost.cs プロジェクト: JetBrains/ForTea
        public T4TestHost(
            [NotNull] T4TestModel model,
            [NotNull] SolutionsManager solutionsManager
            )
        {
            model.PreprocessFile.Set(location =>
            {
                using var cookie = ReadLockCookie.Create();
                var solution     = solutionsManager.Solution;
                if (solution == null)
                {
                    return(Unit.Instance);
                }

                var host        = solution.GetComponent <ProjectModelViewHost>();
                var projectFile = host.GetItemById <IProjectFile>(location.Id).NotNull();
                var sourceFile  = projectFile.ToSourceFile().NotNull();
                sourceFile.GetPsiServices().Files.CommitAllDocuments();
                var file = sourceFile.GetPsiFiles(T4Language.Instance).OfType <IT4File>().SingleItem().NotNull();

                var templatePreprocessingManager = solution.GetComponent <IT4TemplatePreprocessingManager>();
                templatePreprocessingManager.Preprocess(file);
                return(Unit.Instance);
            });
            model.WaitForIndirectInvalidation.Set((lifetime, unit) =>
            {
                using var cookie = ReadLockCookie.Create();
                var solution     = solutionsManager.Solution;
                if (solution == null)
                {
                    return(RdTask <Unit> .Successful(Unit.Instance));
                }
                var cache = solution.GetComponent <T4FileDependencyCache>();

                var result = new RdTask <Unit>();

                void Requeue(int n)
                {
                    if (n == 1)
                    {
                        result.Set(Unit.Instance);
                        return;
                    }
                    solution.Locks.Queue(lifetime, "T4TestHost::.ctor::lambda::Requeue", () =>
                    {
                        if (cache.HasDirtyFiles)
                        {
                            Commit(n - 1);
                        }
                        else
                        {
                            Requeue(n - 1);
                        }
                    });
                }

                void Commit(int n) => solution.GetPsiServices().Files.ExecuteAfterCommitAllDocuments(() => Requeue(n));
                // First commit applies all changes in files.
                // Then we re-queue the action to make sure that indirect invalidation happens,
                // then we commit the files again to apply the indirect changes
                // Commit(2) -> Requeue(2) -> Commit(1) -> Requeue(1)
                Commit(2);

                return(result);
            });
        }