aboutsummaryrefslogtreecommitdiff
path: root/Latex.thy
blob: d28fc829a8f7469339dc8a1ed2ae11010022ed3b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
theory Latex imports
 Main
keywords
 "export_snippets" :: thy_decl
begin  

text \<open>

Usage: invoke @{command export_snippets} in theories you want
to generate snippets of. They will end up in the document output;
you might need sth like @{verbatim "-e *:document/snippets.tex"} on
@{verbatim "isabelle build"}.

Otherwise, find the output under @{verbatim "isabelle-export:"}
in the file browser panel.
\<close>

ML \<open>

fun export_snippet searchterm lthy = Thy_Info.add_presentation (fn {options, segments, ...} => fn thy => 
  let
    val keywords = Thy_Header.get_keywords thy;
    val thy_name = Context.theory_base_name thy;

    val segments_grouped = fold
      (fn s => fn a::acc => if Toplevel.is_proof (#prev_state s)
        then (a @ [s]) :: acc else [s] :: a :: acc) (tl segments) [[hd segments]]
    |> rev
    |> filter (fn segments => not (Toplevel.is_ignored (#command (hd segments))))

    val segments_filtered = if searchterm = "" then segments_grouped else
      filter (fn segments => Toplevel.name_of (#command (hd segments)) = searchterm) segments_grouped;

    val segment_names = segments_filtered
      |> map (fn segments =>
        let
          val {span, command,...} = hd segments
          val nonblank = filter (not o Token.is_space) (Command_Span.content span)
          val name = case nonblank of
              _ :: name :: _ => Token.content_of name
            | _ => ""
          val symbols = name
            |> Symbol.explode
            |> map (fn sym => 
                 if Symbol.is_ascii_letter sym orelse Symbol.is_ascii_digit sym 
                 then sym else if Symbol.is_printable sym then "-" else "")
          val looks_like_name = not (exists (Symbol.is_blank) (Symbol.explode name)) andalso length symbols <> 0;
          val command_name = Toplevel.name_of command
        in (not looks_like_name, command_name ^ ":" ^ (if looks_like_name then String.concat symbols else "")) end);

    val markup = segments_filtered
      |> map (fn segments =>
           SOME (YXML.parse_body (XML.content_of (Scan.catch (Document_Output.present_thy options keywords thy_name) segments)))
           handle Fail _ => NONE)

    fun render_macro_lines snippet_name stack n kind attrs trees = 
      case AList.lookup (op =) attrs "name" of
      SOME name =>
        let 
          val macro = "isa" ^ kind ^ name ^ "\n";
          val (n', content) = fold (fn t => fn (n, ts) =>
             let val (n', t) = render_lines snippet_name (macro :: stack) n t
             in (n', t::ts) end) trees (n,[])
        in if name = "invisible" then (n,"") else (n', "\\" ^ macro 
           ^ String.concat (rev content)
           ^ "\\end" ^ macro) 
        end
      | NONE => error "terru misunderstood the YXML"
    and render_lines snippet_name stack n (XML.Text str) = 
         let
           val chunks = Metis_Useful.split "\\isanewline\n" str;
           val n' = length chunks;
           val breaker = fn n => fold (fn m => fn acc => "%\n\\end" ^ m ^ acc ^ "\\" ^ m) 
                  stack ("\\isanewline}%\n\\SNIP{" ^ snippet_name ^ "-" ^ string_of_int n ^ "}{%\n");
           val broken = chunks
             |> map_index (fn (i, a) => if i = length chunks - 1 then a else a ^ breaker (1+i+n));
         in (n+n'-1, String.concat broken) end
      | render_lines snippet_name stack n (XML.Elem ((name, attrs), trees)) =  case name of
           "latex_delim" => render_macro_lines snippet_name stack n "delim" attrs trees
         | "latex_tag" => render_macro_lines snippet_name stack n "tag" attrs trees
         | tag =>
            let
              val _ = warning ("unknown markup tag " ^ tag);
              val (n', content) = fold (fn t => fn (n, ts) =>
                let val (m, t) = render_lines snippet_name stack n t
                in (m, t::ts) end) trees (n,[]);
            in (n', String.concat (rev content)) end
    val snippets = markup
      |> Metis_Useful.zip segment_names (* why is this only in metis ?? *)
      |> map_filter (fn ((needs_hash, name), SOME texchunks) => 
          if length texchunks > 0 (* otherwise only header/footer, no content *)
          then let 
             val snippet_name = if needs_hash then let val hash = XML.content_of texchunks
               |> (SHA1.rep o SHA1.digest)
               |> Symbol.explode
               |> take 6
               |> String.concat
              in name ^ hash end else name
             val (_, latex) = fold (fn chunk => fn (n, lines) => 
                 let val (n', content) = render_lines snippet_name [] n chunk
                 in (n', content::lines) end) (( texchunks)) (0,[])
             val body = String.concat (rev latex)
               |> unprefix "%\n\\begin{isabellebody}%\n"
               |> unsuffix "%\n\\end{isabellebody}%\n"
            in SOME ("\\SNIP{" ^ snippet_name ^ "-0" ^ "}{%\n" ^ body ^ "}%\n") end
          else NONE 
        | (_, NONE) => NONE)
    val binding = \<^path_binding>\<open>snippets\<close>
      |> Path.map_binding (fn dir => Path.append dir (Path.basic ("snippets_"^thy_name^".tex")))
  in
    Export.export thy binding (map XML.Text (snippets))
  end
) lthy;

val _ =
  Outer_Syntax.local_theory \<^command_keyword>\<open>export_snippets\<close> "export LaTeX snippet"
    (Scan.optional Parse.string "" >> (fn name =>
       Local_Theory.background_theory (fn thy => (
         writeln (Export.message thy (Path.basic "snippets"));
         export_snippet name thy))));

\<close>

export_snippets

end