Example #1
0
        protected override async Task <int> ExecuteEngineAsync(
            CommandContext commandContext,
            PreviewCommandSettings commandSettings,
            IEngineManager engineManager)
        {
            ExitCode exitCode = ExitCode.Normal;
            ILogger  logger   = engineManager.Engine.Services.GetRequiredService <ILogger <Bootstrapper> >();

            // Execute the engine for the first time
            using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource())
            {
                if (!await engineManager.ExecuteAsync(cancellationTokenSource))
                {
                    return((int)ExitCode.ExecutionError);
                }
            }

            // Start the preview server
            Dictionary <string, string> contentTypes = commandSettings.ContentTypes?.Length > 0
                ? GetContentTypes(commandSettings.ContentTypes)
                : new Dictionary <string, string>();
            ILoggerProvider loggerProvider  = engineManager.Engine.Services.GetRequiredService <ILoggerProvider>();
            IDirectory      outputDirectory = engineManager.Engine.FileSystem.GetOutputDirectory();
            Server          previewServer   = null;

            if (outputDirectory.Exists)
            {
                previewServer = await StartPreviewServerAsync(
                    outputDirectory.Path,
                    commandSettings.Port,
                    commandSettings.ForceExt,
                    commandSettings.VirtualDirectory,
                    !commandSettings.NoReload,
                    contentTypes,
                    loggerProvider,
                    logger);
            }

            // Start the watchers
            ActionFileSystemWatcher inputFolderWatcher = null;

            if (!commandSettings.NoWatch)
            {
                logger.LogInformation("Watching paths(s) {0}", string.Join(", ", engineManager.Engine.FileSystem.InputPaths));
                inputFolderWatcher = new ActionFileSystemWatcher(
                    outputDirectory.Path,
                    engineManager.Engine.FileSystem.GetInputDirectories().Select(x => x.Path),
                    true,
                    "*.*",
                    path =>
                {
                    _changedFiles.Enqueue(path);
                    _messageEvent.Set();
                });
            }

            // Start the message pump

            // Only wait for a key if console input has not been redirected, otherwise it's on the caller to exit
            if (!Console.IsInputRedirected)
            {
                // Start the key listening thread
                Thread thread = new Thread(() =>
                {
                    logger.LogInformation("Hit Ctrl-C to exit");
                    Console.TreatControlCAsInput = true;
                    while (true)
                    {
                        // Would have preferred to use Console.CancelKeyPress, but that bubbles up to calling batch files
                        // The (ConsoleKey)3 check is to support a bug in VS Code: https://github.com/Microsoft/vscode/issues/9347
                        ConsoleKeyInfo consoleKey = Console.ReadKey(true);
                        if (consoleKey.Key == (ConsoleKey)3 || (consoleKey.Key == ConsoleKey.C && (consoleKey.Modifiers & ConsoleModifiers.Control) != 0))
                        {
                            _exit.Set();
                            _messageEvent.Set();
                            break;
                        }
                    }
                })
                {
                    IsBackground = true
                };
                thread.Start();
            }

            // Wait for activity
            while (true)
            {
                _messageEvent.WaitOne(); // Blocks the current thread until a signal
                if (_exit)
                {
                    break;
                }

                // Execute if files have changed
                HashSet <string> changedFiles = new HashSet <string>();
                while (_changedFiles.TryDequeue(out string changedFile))
                {
                    if (changedFiles.Add(changedFile))
                    {
                        logger.LogDebug($"{changedFile} has changed");
                    }
                }
                if (changedFiles.Count > 0)
                {
                    logger.LogInformation($"{changedFiles.Count} files have changed, re-executing");

                    // Reset caches when an error occurs during the previous preview
                    string existingResetCacheSetting = null;
                    bool   setResetCacheSetting      = false;
                    if (exitCode == ExitCode.ExecutionError)
                    {
                        existingResetCacheSetting = engineManager.Engine.Settings.GetString(Keys.ResetCache);
                        setResetCacheSetting      = true;
                        ConfigurationSettings[Keys.ResetCache] = "true";
                    }

                    // If there was an execution error due to reload, keep previewing but clear the cache
                    using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource())
                    {
                        exitCode = await engineManager.ExecuteAsync(cancellationTokenSource)
                            ? ExitCode.Normal
                            : ExitCode.ExecutionError;
                    }

                    // Reset the reset cache setting after removing it
                    if (setResetCacheSetting)
                    {
                        if (existingResetCacheSetting == null)
                        {
                            ConfigurationSettings.Remove(Keys.ResetCache);
                        }
                        {
                            ConfigurationSettings[Keys.ResetCache] = existingResetCacheSetting;
                        }
                    }

                    if (previewServer == null)
                    {
                        if (outputDirectory.Exists)
                        {
                            previewServer = await StartPreviewServerAsync(
                                outputDirectory.Path,
                                commandSettings.Port,
                                commandSettings.ForceExt,
                                commandSettings.VirtualDirectory,
                                !commandSettings.NoReload,
                                contentTypes,
                                loggerProvider,
                                logger);
                        }
                    }
                    else
                    {
                        await previewServer.TriggerReloadAsync();
                    }
                }

                // Check one more time for exit
                if (_exit)
                {
                    break;
                }
                logger.LogInformation("Hit Ctrl-C to exit");
                _messageEvent.Reset();
            }

            // Shutdown
            logger.LogInformation("Shutting down");
            inputFolderWatcher?.Dispose();
            previewServer?.Dispose();

            return((int)exitCode);
        }
        protected override async Task <int> ExecuteEngineAsync(
            CommandContext commandContext,
            PreviewCommandSettings commandSettings,
            IEngineManager engineManager)
        {
            SetPipelines(commandContext, commandSettings, engineManager);

            ExitCode exitCode = ExitCode.Normal;
            ILogger  logger   = engineManager.Engine.Services.GetRequiredService <ILogger <Bootstrapper> >();

            // Execute the engine for the first time
            using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource())
            {
                if (!await engineManager.ExecuteAsync(cancellationTokenSource))
                {
                    return((int)ExitCode.ExecutionError);
                }
            }

            // Start the preview server
            Dictionary <string, string> contentTypes = commandSettings.ContentTypes?.Length > 0
                ? GetContentTypes(commandSettings.ContentTypes)
                : new Dictionary <string, string>();
            ILoggerProvider loggerProvider  = engineManager.Engine.Services.GetRequiredService <ILoggerProvider>();
            IDirectory      outputDirectory = engineManager.Engine.FileSystem.GetOutputDirectory();
            Server          previewServer   = null;

            if (outputDirectory.Exists)
            {
                previewServer = await StartPreviewServerAsync(
                    outputDirectory.Path,
                    commandSettings.Port,
                    commandSettings.ForceExt,
                    commandSettings.VirtualDirectory,
                    !commandSettings.NoReload,
                    contentTypes,
                    loggerProvider,
                    logger);
            }

            // Start the watchers
            ActionFileSystemWatcher inputFolderWatcher = null;

            if (!commandSettings.NoWatch)
            {
                logger.LogInformation("Watching paths(s) {0}", string.Join(", ", engineManager.Engine.FileSystem.InputPaths));
                inputFolderWatcher = new ActionFileSystemWatcher(
                    outputDirectory.Path,
                    engineManager.Engine.FileSystem.GetInputDirectories().Select(x => x.Path),
                    true,
                    "*.*",
                    path =>
                {
                    _changedFiles.Enqueue(path);
                    _messageEvent.Set();
                });
            }

            // Start the message pump
            CommandUtilities.WaitForControlC(
                () =>
            {
                _exit.Set();
                _messageEvent.Set();
            },
                logger);

            // Wait for activity
            while (true)
            {
                _messageEvent.WaitOne(); // Blocks the current thread until a signal
                if (_exit)
                {
                    break;
                }

                // Execute if files have changed
                HashSet <string> changedFiles = new HashSet <string>();
                while (_changedFiles.TryDequeue(out string changedFile))
                {
                    if (changedFiles.Add(changedFile))
                    {
                        logger.LogDebug($"{changedFile} has changed");
                    }
                }
                if (changedFiles.Count > 0)
                {
                    logger.LogInformation($"{changedFiles.Count} files have changed, re-executing");

                    // Reset caches when an error occurs during the previous preview
                    string existingResetCacheSetting = null;
                    bool   setResetCacheSetting      = false;
                    if (exitCode == ExitCode.ExecutionError)
                    {
                        existingResetCacheSetting = engineManager.Engine.Settings.GetString(Keys.ResetCache);
                        setResetCacheSetting      = true;
                        ConfigurationSettings[Keys.ResetCache] = "true";
                    }

                    // If there was an execution error due to reload, keep previewing but clear the cache
                    using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource())
                    {
                        exitCode = await engineManager.ExecuteAsync(cancellationTokenSource)
                            ? ExitCode.Normal
                            : ExitCode.ExecutionError;
                    }

                    // Reset the reset cache setting after removing it
                    if (setResetCacheSetting)
                    {
                        if (existingResetCacheSetting == null)
                        {
                            ConfigurationSettings.Remove(Keys.ResetCache);
                        }
                        {
                            ConfigurationSettings[Keys.ResetCache] = existingResetCacheSetting;
                        }
                    }

                    if (previewServer == null)
                    {
                        if (outputDirectory.Exists)
                        {
                            previewServer = await StartPreviewServerAsync(
                                outputDirectory.Path,
                                commandSettings.Port,
                                commandSettings.ForceExt,
                                commandSettings.VirtualDirectory,
                                !commandSettings.NoReload,
                                contentTypes,
                                loggerProvider,
                                logger);
                        }
                    }
                    else
                    {
                        await previewServer.TriggerReloadAsync();
                    }
                }

                // Check one more time for exit
                if (_exit)
                {
                    break;
                }
                logger.LogInformation("Hit Ctrl-C to exit");
                _messageEvent.Reset();
            }

            // Shutdown
            logger.LogInformation("Shutting down");
            inputFolderWatcher?.Dispose();
            previewServer?.Dispose();

            return((int)exitCode);
        }