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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
open Forester_prelude
open Forester_core
open struct module T = Types end
open State.Syntax
let load (tree_dirs : Eio.Fs.dir_ty Eio.Path.t list) =
Logs.debug (fun m -> m "loading trees from %i directories" (List.length tree_dirs));
Dir_scanner.scan_directories tree_dirs
|> Seq.map Imports.load_tree
let parse (forest : State.t) =
let trees = forest.index |> URI.Tbl.to_seq_values |> List.of_seq in
let results =
let@ tree = List.filter_map @~ trees in
match tree with
| Document doc -> Some (Parse.parse_document ~config: forest.config doc)
| Parsed _
| Expanded _
| Resource _ ->
None
in
let@ result = List.partition_map @~ results in
match result with
| Ok t -> Right t
| Error d -> Left d
let reparse (doc : Lsp.Text_document.t) (forest : State.t) =
Logs.debug (fun m -> m "reparsing");
let uri = URI_scheme.lsp_uri_to_uri ~base: forest.config.url @@ Lsp.Text_document.documentUri doc in
begin
match Parse.parse_document ~config: forest.config doc with
| Ok code ->
forest.={uri} <- Parsed code;
Imports.fixup code forest
| Error d ->
forest.?{uri} <- [d]
end;
forest
let build_import_graph (forest : State.t) =
let@ () = Reporter.trace "when resolving imports" in
let errors = ref [] in
let push d = errors := d :: !errors in
let new_graph =
Reporter.run
~emit: push
~fatal: (fun d ->
push d;
Reporter.Tty.display d;
forest.import_graph
)
@@ fun () ->
Imports.build forest
in
!errors, new_graph
let expand (forest : State.t) =
Expand.expand_tree ~forest
let expand_all (forest : State.t) =
let diagnostics = ref [] in
let task vertex =
match vertex with
| T.Content_vertex _ -> assert false
| T.Uri_vertex uri ->
match forest.={uri} with
| None ->
()
| Some tree ->
match Tree.to_code tree with
| Some tree ->
let expanded, errors = Expand.expand_tree ~forest tree in
diagnostics := errors @ !diagnostics;
forest.={uri} <- Expanded expanded;
forest.?{uri} <- errors
| None ->
Logs.debug (fun m -> m "expanding: no source code for %a" URI.pp uri);
assert false;
in
Forest_graph.topo_iter task forest.import_graph;
!diagnostics
let run_jobs (forest : State.t) jobs =
Logs.debug (fun m -> m "Running %d jobs" (List.length jobs));
let resources_to_plant =
let@ Range.{value; loc} = Eio.Fiber.List.map ~max_fibers: 20 @~ jobs in
let@ () = Reporter.easy_run in
match value with
| Job.LaTeX_to_svg job ->
let svg = Build_latex.latex_to_svg ~env: forest.env ?loc job.source in
let uri = Job.uri_for_latex_to_svg_job ~base: forest.config.url job in
T.Asset {uri; content = svg}
| Job.Syndicate syndication ->
T.Syndication syndication
in
begin
let@ resource = List.iter @~ resources_to_plant in
State.plant_resource resource forest
end
let eval (forest : State.t) =
let result =
State.get_all_unevaluated forest
|> Seq.filter Tree.is_expanded
|> Seq.map (fun tree ->
let tree = Option.get @@ Tree.to_syn tree in
match identity_to_uri tree.identity with
| None -> Reporter.fatal Internal_error ~extra_remarks: [Asai.Diagnostic.loctext "can't evaluate a tree with no URI"]
| Some uri ->
let source_path =
if forest.dev then
URI.Tbl.find_opt forest.resolver uri
else None
in
Eval.eval_tree
~config: forest.config
~source_path
~uri
tree.nodes
)
in
result
let eval_only (uri : URI.t) (forest : State.t) =
match forest.={uri} with
| None -> assert false
| Some (Document _) -> assert false
| Some (Parsed _) | Some (Resource _) -> assert false
| Some (Expanded expanded) ->
let Eval.{articles; jobs = _}, diagnostics =
Eval.eval_tree
~config: forest.config
~source_path: None
~uri
expanded.nodes
in
begin
let@ article = List.iter @~ articles in
State.plant_resource (Article article) forest
end;
forest, diagnostics
let check_status _uri (forest : State.t) =
match forest with
| {dependency_cache = _; _} ->
forest, None
let implant_foreign (state : State.t) : State.t * _ =
begin
Logs.debug (fun m -> m "implanting %i foreign forests" (List.length state.config.foreign));
let module EP = Eio.Path in
let@ foreign = List.iter @~ state.config.foreign in
let path = Eio_util.path_of_file ~env: state.env foreign.path in
let path_str = EP.native_exn path in
Reporter.log Format.pp_print_string (Format.sprintf "Implant foreign forest from `%s'" path_str);
let blob = try EP.load path with _ -> Reporter.fatal IO_error ~extra_remarks: [Asai.Diagnostic.loctextf "Could not read foreign forest blob at `%s`" path_str] in
match Repr.of_json_string (T.forest_t T.content_t) blob with
| Ok foreign_forest ->
let@ r = List.iter @~ foreign_forest in
State.plant_resource ~route_locally: foreign.route_locally r state
| Error (`Msg err) ->
Reporter.fatal Parse_error ~extra_remarks: [Asai.Diagnostic.loctextf "Could not parse foreign forest blob: %s" err]
| exception exn ->
Reporter.fatal Parse_error ~extra_remarks: [Asai.Diagnostic.loctextf "Encountered unknown error while decoding foreign forest blob: %s" (Printexc.to_string exn)]
end;
state, []