All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
mdoc.internal.cli.MainOps.scala Maven / Gradle / Ivy
package mdoc.internal.cli
import com.vladsch.flexmark.parser.Parser
import com.vladsch.flexmark.util.options.MutableDataSet
import io.methvin.watcher.DirectoryChangeEvent
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.util.concurrent.Executors
import mdoc.Reporter
import mdoc.internal.BuildInfo
import mdoc.internal.io.IO
import mdoc.internal.io.MdocFileListener
import mdoc.internal.livereload.LiveReload
import mdoc.internal.livereload.UndertowLiveReload
import mdoc.internal.markdown.DocumentLinks
import mdoc.internal.markdown.LinkHygiene
import mdoc.internal.markdown.Markdown
import mdoc.internal.pos.DiffUtils
import metaconfig.Configured
import scala.meta.Input
import scala.meta.internal.io.FileIO
import scala.meta.internal.io.PathIO
import scala.meta.io.AbsolutePath
import scala.util.control.NonFatal
final class MainOps(
context: Context
) {
def settings: Settings = context.settings
def reporter: Reporter = context.reporter
private var livereload: Option[LiveReload] = None
private def startLivereload(): Unit = {
if (settings.noLivereload) ()
else {
val livereload = UndertowLiveReload(
settings.out.toNIO,
host = settings.host,
preferredPort = settings.port,
reporter = reporter
)
livereload.start()
this.livereload = Some(livereload)
}
}
def lint(): Unit = {
if (settings.out.isDirectory && !settings.noLinkHygiene) {
val docs = DocumentLinks.fromGeneratedSite(settings, reporter)
LinkHygiene.lint(docs, reporter, settings.verbose)
}
}
def handleMarkdown(file: InputFile): Exit = synchronized {
val originalErrors = reporter.errorCount
if (settings.verbose) {
reporter.info(s"Compiling ${file.in}")
}
val timer = new Timer
val source = FileIO.slurp(file.in, settings.charset)
val input = Input.VirtualFile(file.in.toString(), source)
val md = Markdown.toMarkdown(input, context, file.relpath, settings.site, reporter, settings)
val fileHasErrors = reporter.errorCount > originalErrors
if (!fileHasErrors) {
writePath(file, md)
if (settings.verbose) {
reporter.info(f" done => ${file.out} ($timer)")
}
livereload.foreach(_.reload(file.out.toNIO))
}
if (reporter.hasErrors) Exit.error
else Exit.success
}
def handleRegularFile(file: InputFile): Exit = {
Files.createDirectories(file.out.toNIO.getParent)
Files.copy(file.in.toNIO, file.out.toNIO, StandardCopyOption.REPLACE_EXISTING)
if (settings.verbose) {
reporter.info(s"Copied ${file.out.toNIO}")
}
Exit.success
}
def handleFile(file: InputFile): Exit = {
try {
if (!settings.isIncluded(file.relpath)) Exit.success
else {
val extension = PathIO.extension(file.in.toNIO)
if (settings.isMarkdownFileExtension(extension)) {
handleMarkdown(file)
} else {
handleRegularFile(file)
}
}
} catch {
case NonFatal(e) =>
new FileException(file.in, e).printStackTrace()
Exit.error
}
}
def writePath(file: InputFile, string: String): Unit = {
if (settings.check) {
if (!file.out.isFile) return
val expected = FileIO.slurp(file.out, settings.charset)
if (expected != string) {
val filename = file.out.toString()
val diff = DiffUtils.unifiedDiff(
s"$filename (on disk)",
s"$filename (expected output)",
expected.linesIterator.toList,
string.linesIterator.toList,
3
)
reporter.error(s"--test failed! To fix this problem, re-generate the documentation\n$diff")
}
} else {
Files.createDirectories(file.out.toNIO.getParent)
Files.write(file.out.toNIO, string.getBytes(settings.charset))
}
}
def generateCompleteSite(): Exit = {
val files = IO.inputFiles(settings)
val timer = new Timer()
val n = files.length
compilingFiles(n)
val exit = files.foldLeft(Exit.success) {
case (accum, file) =>
val fileExit = handleFile(file)
accum.merge(fileExit)
}
lint()
if (files.isEmpty) {
reporter.error(s"no input files: ${settings.in}")
} else {
compiledFiles(n, timer)
}
exit
}
def run(): Exit = {
if (settings.cleanTarget && Files.exists(settings.out.toNIO)) {
IO.cleanTarget(settings.out)
}
if (settings.watch) {
startLivereload()
}
val isOk = generateCompleteSite()
if (settings.isFileWatching) {
waitingForFileChanges()
runFileWatcher()
// exit code doesn't matter when file watching
Exit.success
} else {
isOk
}
}
def handleWatchEvent(event: DirectoryChangeEvent): Unit = {
if (PathIO.extension(event.path()) == "md") {
clearScreen()
}
val path = AbsolutePath(event.path())
settings.toInputFile(path) match {
case Some(inputFile) =>
reporter.reset()
val timer = new Timer()
compilingFiles(1)
handleFile(inputFile)
lint()
compiledFiles(1, timer)
waitingForFileChanges()
case None => ()
}
}
def runFileWatcher(): Unit = {
val executor = Executors.newFixedThreadPool(1)
val watcher = MdocFileListener.create(settings.in, executor, System.in)(handleWatchEvent)
watcher.watchUntilInterrupted()
this.livereload.foreach(_.stop())
}
def clearScreen(): Unit = {
print("\u001b[H\u001b[2J")
}
def waitingForFileChanges(): Unit = {
reporter.println(s"Waiting for file changes (press enter to interrupt)")
}
def compiledFiles(n: Int, timer: Timer): Unit = {
val errors = Messages.count("error", reporter.errorCount)
val warnings =
if (reporter.hasWarnings) {
s", " + Messages.count("warning", reporter.warningCount)
} else {
""
}
reporter.info(s"Compiled in $timer ($errors$warnings)")
}
def compilingFiles(n: Int): Unit = {
val files = Messages.count("file", n)
reporter.info(s"Compiling $files to ${settings.out}")
}
}
object MainOps {
def process(settings: Configured[Settings], reporter: Reporter): Int = {
settings match {
case Configured.Ok(setting) if setting.help =>
reporter.println(Settings.help(BuildInfo.version, 80))
0
case Configured.Ok(setting) if setting.usage =>
reporter.println(Settings.usage)
0
case Configured.Ok(setting) if setting.version =>
reporter.println(Settings.version(BuildInfo.version))
0
case els =>
els.andThen(_.validate(reporter)) match {
case Configured.NotOk(error) =>
error.all.foreach(message => reporter.error(message))
1
case Configured.Ok(ctx) =>
if (ctx.settings.verbose) {
ctx.reporter.setDebugEnabled(true)
}
val runner = new MainOps(ctx)
val exit = runner.run()
if (exit.isSuccess) {
0
} else {
1 // error
}
}
}
}
}