| author | paulson <lp15@cam.ac.uk> | 
| Fri, 01 Nov 2024 14:10:52 +0000 | |
| changeset 81293 | 6f0cd46be030 | 
| parent 75394 | 42267c650205 | 
| child 81350 | 1818358373e2 | 
| permissions | -rw-r--r-- | 
| 52444 
2cfe6656d6d6
slightly improved "isabelle doc" based on Isabelle/Scala;
 wenzelm parents: 
52429diff
changeset | 1 | /* Title: Pure/Tools/doc.scala | 
| 52427 | 2 | Author: Makarius | 
| 3 | ||
| 56424 
7032378cc097
proper settings instead of hard-wired information;
 wenzelm parents: 
56423diff
changeset | 4 | Access to Isabelle documentation. | 
| 52427 | 5 | */ | 
| 6 | ||
| 7 | package isabelle | |
| 8 | ||
| 9 | ||
| 75115 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 10 | import scala.collection.mutable | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 11 | |
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 12 | |
| 75393 | 13 | object Doc {
 | 
| 52427 | 14 | /* dirs */ | 
| 15 | ||
| 16 | def dirs(): List[Path] = | |
| 67604 
02cf352cbc4c
permissive Doc.dirs: some entries may be absent due to distribution bootstrap, e.g. $JEDIT_HOME/dist/doc;
 wenzelm parents: 
67471diff
changeset | 17 |     Path.split(Isabelle_System.getenv("ISABELLE_DOCS"))
 | 
| 52740 | 18 | |
| 52427 | 19 | |
| 20 | /* contents */ | |
| 21 | ||
| 56422 | 22 | private def contents_lines(): List[(Path, String)] = | 
| 52444 
2cfe6656d6d6
slightly improved "isabelle doc" based on Isabelle/Scala;
 wenzelm parents: 
52429diff
changeset | 23 |     for {
 | 
| 
2cfe6656d6d6
slightly improved "isabelle doc" based on Isabelle/Scala;
 wenzelm parents: 
52429diff
changeset | 24 | dir <- dirs() | 
| 
2cfe6656d6d6
slightly improved "isabelle doc" based on Isabelle/Scala;
 wenzelm parents: 
52429diff
changeset | 25 |       catalog = dir + Path.basic("Contents")
 | 
| 
2cfe6656d6d6
slightly improved "isabelle doc" based on Isabelle/Scala;
 wenzelm parents: 
52429diff
changeset | 26 | if catalog.is_file | 
| 73276 | 27 | line <- Library.trim_split_lines(File.read(catalog)) | 
| 56422 | 28 | } yield (dir, line) | 
| 52444 
2cfe6656d6d6
slightly improved "isabelle doc" based on Isabelle/Scala;
 wenzelm parents: 
52429diff
changeset | 29 | |
| 75393 | 30 |   object Contents {
 | 
| 75118 | 31 | def apply(sections: List[Section]): Contents = new Contents(sections) | 
| 32 | ||
| 33 | def section(title: String, important: Boolean, entries: List[Entry]): Contents = | |
| 34 | apply(List(Section(title, important, entries))) | |
| 35 | } | |
| 75393 | 36 |   class Contents private(val sections: List[Section]) {
 | 
| 75118 | 37 | def ++ (other: Contents): Contents = new Contents(sections ::: other.sections) | 
| 38 | def entries: List[Entry] = sections.flatMap(_.entries) | |
| 39 |     def docs: List[Doc] = entries.collect({ case doc: Doc => doc })
 | |
| 40 | } | |
| 41 | ||
| 75115 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 42 | case class Section(title: String, important: Boolean, entries: List[Entry]) | 
| 75393 | 43 |   sealed abstract class Entry {
 | 
| 75118 | 44 | def name: String | 
| 45 | def path: Path | |
| 46 | } | |
| 56422 | 47 | case class Doc(name: String, title: String, path: Path) extends Entry | 
| 52542 
19d674acb764
more release notes according to availability in proper release vs. repository clone;
 wenzelm parents: 
52541diff
changeset | 48 | case class Text_File(name: String, path: Path) extends Entry | 
| 52427 | 49 | |
| 69283 | 50 | def text_file(path: Path): Option[Text_File] = | 
| 51 |     if (path.is_file) {
 | |
| 71397 | 52 | val a = path.implode | 
| 53 |       val b = Library.try_unprefix("$ISABELLE_HOME/", a).getOrElse(a)
 | |
| 54 | Some(Text_File(b, path)) | |
| 69283 | 55 | } | 
| 53777 | 56 | else None | 
| 57 | ||
| 75118 | 58 | def release_notes(): Contents = | 
| 59 |     Contents.section("Release Notes", true,
 | |
| 75115 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 60 |       Path.split(Isabelle_System.getenv_strict("ISABELLE_DOCS_RELEASE_NOTES")).flatMap(text_file))
 | 
| 52427 | 61 | |
| 75118 | 62 | def examples(): Contents = | 
| 63 |     Contents.section("Examples", true,
 | |
| 56424 
7032378cc097
proper settings instead of hard-wired information;
 wenzelm parents: 
56423diff
changeset | 64 |       Path.split(Isabelle_System.getenv_strict("ISABELLE_DOCS_EXAMPLES")).map(file =>
 | 
| 
7032378cc097
proper settings instead of hard-wired information;
 wenzelm parents: 
56423diff
changeset | 65 |         text_file(file) match {
 | 
| 
7032378cc097
proper settings instead of hard-wired information;
 wenzelm parents: 
56423diff
changeset | 66 | case Some(entry) => entry | 
| 56425 | 67 |           case None => error("Bad entry in ISABELLE_DOCS_EXAMPLES: " + file)
 | 
| 75115 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 68 | })) | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 69 | |
| 75393 | 70 |   def main_contents(): Contents = {
 | 
| 75115 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 71 | val result = new mutable.ListBuffer[Section] | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 72 | val entries = new mutable.ListBuffer[Entry] | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 73 | var section: Option[Section] = None | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 74 | |
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 75 | def flush(): Unit = | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 76 |       if (section.nonEmpty) {
 | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 77 | result += section.get.copy(entries = entries.toList) | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 78 | entries.clear() | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 79 | section = None | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 80 | } | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 81 | |
| 75393 | 82 |     def begin(s: Section): Unit = {
 | 
| 75115 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 83 | flush() | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 84 | section = Some(s) | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 85 | } | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 86 | |
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 87 | val Section_ = """^(\S.*)\s*$""".r | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 88 | val Doc_ = """^\s+(\S+)\s+(.+)\s*$""".r | 
| 53777 | 89 | |
| 75115 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 90 |     for ((dir, line) <- contents_lines()) {
 | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 91 |       line match {
 | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 92 | case Section_(text) => | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 93 |           Library.try_unsuffix("!", text) match {
 | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 94 | case None => begin(Section(text, false, Nil)) | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 95 | case Some(txt) => begin(Section(txt, true, Nil)) | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 96 | } | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 97 | case Doc_(name, title) => | 
| 75120 | 98 | entries += Doc(name, title, dir + Path.basic(name).pdf) | 
| 75115 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 99 | case _ => | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 100 | } | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 101 | } | 
| 75114 | 102 | |
| 75115 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 103 | flush() | 
| 75118 | 104 | Contents(result.toList) | 
| 75115 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 105 | } | 
| 
c212435866d6
clarified signature: more explicit section structure;
 wenzelm parents: 
75114diff
changeset | 106 | |
| 75393 | 107 |   def contents(): Contents = {
 | 
| 75118 | 108 | examples() ++ release_notes() ++ main_contents() | 
| 61157 | 109 | } | 
| 52427 | 110 | |
| 75393 | 111 |   object Doc_Names extends Scala.Fun_String("doc_names") {
 | 
| 72760 
042180540068
clarified protocol: Doc.check at run-time via Scala function;
 wenzelm parents: 
71601diff
changeset | 112 | val here = Scala_Project.here | 
| 
042180540068
clarified protocol: Doc.check at run-time via Scala function;
 wenzelm parents: 
71601diff
changeset | 113 | def apply(arg: String): String = | 
| 
042180540068
clarified protocol: Doc.check at run-time via Scala function;
 wenzelm parents: 
71601diff
changeset | 114 |       if (arg.nonEmpty) error("Bad argument: " + quote(arg))
 | 
| 75118 | 115 | else cat_lines((for (doc <- contents().docs) yield doc.name).sorted) | 
| 72760 
042180540068
clarified protocol: Doc.check at run-time via Scala function;
 wenzelm parents: 
71601diff
changeset | 116 | } | 
| 67471 | 117 | |
| 52427 | 118 | |
| 119 | /* view */ | |
| 120 | ||
| 75393 | 121 |   def view(path: Path): Unit = {
 | 
| 75120 | 122 |     if (!path.is_file) error("Bad Isabelle documentation file: " + path)
 | 
| 123 | else if (path.is_pdf) Isabelle_System.pdf_viewer(path) | |
| 124 | else Output.writeln(Library.trim_line(File.read(path)), stdout = true) | |
| 52427 | 125 | } | 
| 52444 
2cfe6656d6d6
slightly improved "isabelle doc" based on Isabelle/Scala;
 wenzelm parents: 
52429diff
changeset | 126 | |
| 
2cfe6656d6d6
slightly improved "isabelle doc" based on Isabelle/Scala;
 wenzelm parents: 
52429diff
changeset | 127 | |
| 62831 | 128 | /* Isabelle tool wrapper */ | 
| 52444 
2cfe6656d6d6
slightly improved "isabelle doc" based on Isabelle/Scala;
 wenzelm parents: 
52429diff
changeset | 129 | |
| 75116 | 130 |   val isabelle_tool = Isabelle_Tool("doc", "view Isabelle PDF documentation",
 | 
| 75394 | 131 | Scala_Project.here, | 
| 132 |     { args =>
 | |
| 133 |       val getopts = Getopts("""
 | |
| 62438 | 134 | Usage: isabelle doc [DOC ...] | 
| 135 | ||
| 75116 | 136 | View Isabelle PDF documentation. | 
| 62438 | 137 | """) | 
| 75394 | 138 | val docs = getopts(args) | 
| 62438 | 139 | |
| 75394 | 140 | if (docs.isEmpty) Output.writeln(cat_lines(contents_lines().map(_._2)), stdout = true) | 
| 141 |       else {
 | |
| 142 | docs.foreach(name => | |
| 143 |           contents().docs.find(_.name == name) match {
 | |
| 144 | case Some(doc) => view(doc.path) | |
| 145 |             case None => error("No Isabelle documentation entry: " + quote(name))
 | |
| 146 | } | |
| 147 | ) | |
| 148 | } | |
| 149 | }) | |
| 52427 | 150 | } |