author | wenzelm |
Sun, 05 Mar 2017 13:34:35 +0100 | |
changeset 65111 | 3224a6f7bd6b |
parent 65107 | 70b0113fa4ef |
child 65112 | b3904f683ef5 |
permissions | -rw-r--r-- |
64623 | 1 |
/* Title: Tools/VSCode/src/vscode_resources.scala |
64605 | 2 |
Author: Makarius |
3 |
||
64729 | 4 |
Resources for VSCode Language Server: file-system access and global state. |
64605 | 5 |
*/ |
6 |
||
7 |
package isabelle.vscode |
|
8 |
||
9 |
||
10 |
import isabelle._ |
|
11 |
||
12 |
import java.io.{File => JFile} |
|
13 |
||
64824 | 14 |
import scala.util.parsing.input.Reader |
64727
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
15 |
|
64605 | 16 |
|
64623 | 17 |
object VSCode_Resources |
64605 | 18 |
{ |
64703 | 19 |
/* internal state */ |
20 |
||
21 |
sealed case class State( |
|
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
22 |
models: Map[JFile, Document_Model] = Map.empty, |
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
23 |
pending_input: Set[JFile] = Set.empty, |
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
24 |
pending_output: Set[JFile] = Set.empty) |
64800 | 25 |
{ |
65111
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
26 |
def pending(changed: Traversable[JFile]) = |
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
27 |
copy( |
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
28 |
pending_input = pending_input ++ changed, |
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
29 |
pending_output = pending_output ++ changed) |
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
30 |
|
64800 | 31 |
lazy val document_blobs: Document.Blobs = |
32 |
Document.Blobs( |
|
33 |
(for { |
|
34 |
(_, model) <- models.iterator |
|
35 |
blob <- model.get_blob |
|
36 |
} yield (model.node_name -> blob)).toMap) |
|
37 |
} |
|
64605 | 38 |
} |
39 |
||
64623 | 40 |
class VSCode_Resources( |
64854 | 41 |
val options: Options, |
42 |
val text_length: Text.Length, |
|
64856 | 43 |
base: Sessions.Base, |
64854 | 44 |
log: Logger = No_Logger) extends Resources(base, log) |
64605 | 45 |
{ |
64703 | 46 |
private val state = Synchronized(VSCode_Resources.State()) |
47 |
||
48 |
||
49 |
/* document node name */ |
|
50 |
||
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
51 |
def node_file(name: Document.Node.Name): JFile = new JFile(name.node) |
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
52 |
|
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
53 |
def node_name(file: JFile): Document.Node.Name = |
64605 | 54 |
{ |
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
55 |
val node = file.getPath |
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
56 |
val theory = Thy_Header.thy_name_bootstrap(node).getOrElse("") |
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
57 |
val master_dir = if (theory == "") "" else file.getParent |
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
58 |
Document.Node.Name(node, master_dir, theory) |
64605 | 59 |
} |
64703 | 60 |
|
64759 | 61 |
override def append(dir: String, source_path: Path): String = |
64716 | 62 |
{ |
64759 | 63 |
val path = source_path.expand |
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
64 |
if (dir == "" || path.is_absolute) File.platform_path(path) |
64759 | 65 |
else if (path.is_current) dir |
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
66 |
else if (path.is_basic && !dir.endsWith("/") && !dir.endsWith(JFile.separator)) |
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
67 |
dir + JFile.separator + File.platform_path(path) |
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
68 |
else if (path.is_basic) dir + File.platform_path(path) |
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
69 |
else new JFile(dir + JFile.separator + File.platform_path(path)).getCanonicalPath |
64716 | 70 |
} |
71 |
||
64834 | 72 |
def get_model(file: JFile): Option[Document_Model] = state.value.models.get(file) |
73 |
def get_model(name: Document.Node.Name): Option[Document_Model] = get_model(node_file(name)) |
|
74 |
||
75 |
||
76 |
/* file content */ |
|
77 |
||
78 |
def read_file_content(file: JFile): Option[String] = |
|
79 |
try { Some(Line.normalize(File.read(file))) } |
|
80 |
catch { case ERROR(_) => None } |
|
81 |
||
82 |
def get_file_content(file: JFile): Option[String] = |
|
83 |
get_model(file) match { |
|
84 |
case Some(model) => Some(model.content.text) |
|
85 |
case None => read_file_content(file) |
|
86 |
} |
|
87 |
||
88 |
def bibtex_entries_iterator(): Iterator[Text.Info[(String, Document_Model)]] = |
|
89 |
for { |
|
90 |
(_, model) <- state.value.models.iterator |
|
91 |
Text.Info(range, entry) <- model.content.bibtex_entries.iterator |
|
92 |
} yield Text.Info(range, (entry, model)) |
|
93 |
||
64727
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
94 |
override def with_thy_reader[A](name: Document.Node.Name, f: Reader[Char] => A): A = |
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
95 |
{ |
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
96 |
val file = node_file(name) |
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
97 |
get_model(file) match { |
64830 | 98 |
case Some(model) => f(Scan.char_reader(model.content.text)) |
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
99 |
case None if file.isFile => |
64727
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
100 |
val reader = Scan.byte_reader(file) |
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
101 |
try { f(reader) } finally { reader.close } |
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
102 |
case None => |
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
103 |
error("No such file: " + quote(file.toString)) |
64727
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
104 |
} |
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
105 |
} |
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
106 |
|
64703 | 107 |
|
108 |
/* document models */ |
|
109 |
||
64800 | 110 |
def visible_node(name: Document.Node.Name): Boolean = |
111 |
get_model(name) match { |
|
112 |
case Some(model) => model.node_visible |
|
113 |
case None => false |
|
114 |
} |
|
115 |
||
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
116 |
def update_model(session: Session, file: JFile, text: String) |
64703 | 117 |
{ |
118 |
state.change(st => |
|
64709 | 119 |
{ |
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
120 |
val model = st.models.getOrElse(file, Document_Model.init(session, node_name(file))) |
64727
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
121 |
val model1 = (model.update_text(text) getOrElse model).external(false) |
64709 | 122 |
st.copy( |
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
123 |
models = st.models + (file -> model1), |
65111
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
124 |
pending_input = st.pending_input + file, |
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
125 |
pending_output = st.pending_output ++ |
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
126 |
(if (model.node_visible == model1.node_visible) None else Some(file))) |
64709 | 127 |
}) |
64703 | 128 |
} |
129 |
||
65111
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
130 |
def close_model(file: JFile): Boolean = |
64710 | 131 |
state.change_result(st => |
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
132 |
st.models.get(file) match { |
65111
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
133 |
case None => (false, st) |
64710 | 134 |
case Some(model) => |
65111
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
135 |
val model1 = model.external(true) |
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
136 |
(true, st.copy(models = st.models + (file -> model1)).pending(Some(file))) |
64710 | 137 |
}) |
138 |
||
64722 | 139 |
def sync_models(changed_files: Set[JFile]): Boolean = |
64710 | 140 |
state.change_result(st => |
141 |
{ |
|
64719 | 142 |
val changed_models = |
64710 | 143 |
(for { |
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
144 |
(file, model) <- st.models.iterator |
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
145 |
if changed_files(file) && model.external_file |
64812 | 146 |
text <- read_file_content(file) |
64779 | 147 |
model1 <- model.update_text(text) |
65111
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
148 |
} yield (file, model1)).toMap |
64719 | 149 |
if (changed_models.isEmpty) (false, st) |
65111
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
150 |
else (true, st.copy(models = st.models ++ changed_models).pending(changed_models.keySet)) |
64710 | 151 |
}) |
152 |
||
64703 | 153 |
|
64727
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
154 |
/* resolve dependencies */ |
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
155 |
|
64857 | 156 |
def resolve_dependencies(session: Session, file_watcher: File_Watcher): (Boolean, Boolean) = |
64727
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
157 |
{ |
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
158 |
state.change_result(st => |
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
159 |
{ |
64800 | 160 |
/* theory files */ |
161 |
||
64731
84192ecae582
just one synchronized access to global state: works recursively on JVM;
wenzelm
parents:
64729
diff
changeset
|
162 |
val thys = |
84192ecae582
just one synchronized access to global state: works recursively on JVM;
wenzelm
parents:
64729
diff
changeset
|
163 |
(for ((_, model) <- st.models.iterator if model.is_theory) |
84192ecae582
just one synchronized access to global state: works recursively on JVM;
wenzelm
parents:
64729
diff
changeset
|
164 |
yield (model.node_name, Position.none)).toList |
84192ecae582
just one synchronized access to global state: works recursively on JVM;
wenzelm
parents:
64729
diff
changeset
|
165 |
|
64800 | 166 |
val thy_files = thy_info.dependencies("", thys).deps.map(_.name) |
167 |
||
168 |
||
169 |
/* auxiliary files */ |
|
170 |
||
171 |
val stable_tip_version = |
|
64815 | 172 |
if (st.models.forall(entry => entry._2.is_stable)) |
64800 | 173 |
session.current_state().stable_tip_version |
174 |
else None |
|
175 |
||
176 |
val aux_files = |
|
177 |
stable_tip_version match { |
|
178 |
case Some(version) => undefined_blobs(version.nodes) |
|
179 |
case None => Nil |
|
180 |
} |
|
181 |
||
182 |
||
183 |
/* loaded models */ |
|
184 |
||
64727
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
185 |
val loaded_models = |
64731
84192ecae582
just one synchronized access to global state: works recursively on JVM;
wenzelm
parents:
64729
diff
changeset
|
186 |
(for { |
64800 | 187 |
node_name <- thy_files.iterator ++ aux_files.iterator |
188 |
file = node_file(node_name) |
|
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
189 |
if !st.models.isDefinedAt(file) |
64857 | 190 |
text <- { file_watcher.register_parent(file); read_file_content(file) } |
64727
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
191 |
} |
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
192 |
yield { |
64800 | 193 |
val model = Document_Model.init(session, node_name) |
64727
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
194 |
val model1 = (model.update_text(text) getOrElse model).external(true) |
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
195 |
(file, model1) |
65111
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
196 |
}).toMap |
64731
84192ecae582
just one synchronized access to global state: works recursively on JVM;
wenzelm
parents:
64729
diff
changeset
|
197 |
|
64800 | 198 |
val invoke_input = loaded_models.nonEmpty |
199 |
val invoke_load = stable_tip_version.isEmpty |
|
200 |
||
201 |
((invoke_input, invoke_load), |
|
65111
3224a6f7bd6b
more robust treatment of pending input/output: these are often correlated;
wenzelm
parents:
65107
diff
changeset
|
202 |
st.copy(models = st.models ++ loaded_models).pending(loaded_models.keySet)) |
64727
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
203 |
}) |
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
204 |
} |
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
205 |
|
13e37567a0d6
automatically resolve dependencies from document models and file-system;
wenzelm
parents:
64726
diff
changeset
|
206 |
|
64703 | 207 |
/* pending input */ |
208 |
||
209 |
def flush_input(session: Session) |
|
210 |
{ |
|
211 |
state.change(st => |
|
212 |
{ |
|
64719 | 213 |
val changed_models = |
64703 | 214 |
(for { |
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
215 |
file <- st.pending_input.iterator |
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
216 |
model <- st.models.get(file) |
64800 | 217 |
(edits, model1) <- model.flush_edits(st.document_blobs) |
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
218 |
} yield (edits, (file, model1))).toList |
64707 | 219 |
|
64800 | 220 |
session.update(st.document_blobs, changed_models.flatMap(_._1)) |
64703 | 221 |
st.copy( |
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
222 |
models = (st.models /: changed_models.iterator.map(_._2))(_ + _), |
64703 | 223 |
pending_input = Set.empty) |
224 |
}) |
|
225 |
} |
|
226 |
||
227 |
||
228 |
/* pending output */ |
|
229 |
||
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
230 |
def update_output(changed_nodes: List[JFile]): Unit = |
64703 | 231 |
state.change(st => st.copy(pending_output = st.pending_output ++ changed_nodes)) |
232 |
||
233 |
def flush_output(channel: Channel) |
|
234 |
{ |
|
235 |
state.change(st => |
|
236 |
{ |
|
237 |
val changed_iterator = |
|
238 |
for { |
|
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
239 |
file <- st.pending_output.iterator |
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
240 |
model <- st.models.get(file) |
64704 | 241 |
rendering = model.rendering() |
65095 | 242 |
((diagnostics, decorations), model1) <- model.publish(rendering) |
243 |
} |
|
244 |
yield { |
|
245 |
if (diagnostics.nonEmpty) |
|
246 |
channel.write( |
|
247 |
Protocol.PublishDiagnostics(file, rendering.diagnostics_output(diagnostics))) |
|
248 |
||
249 |
for (decoration <- decorations) |
|
250 |
channel.write(rendering.decoration_output(decoration).json(file)) |
|
251 |
||
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
252 |
(file, model1) |
64703 | 253 |
} |
254 |
st.copy( |
|
64777
ca09695eb43c
clarified Document.Node.Name (again): canonical platform file;
wenzelm
parents:
64775
diff
changeset
|
255 |
models = (st.models /: changed_iterator)(_ + _), |
64703 | 256 |
pending_output = Set.empty) |
257 |
} |
|
258 |
) |
|
259 |
} |
|
64870 | 260 |
|
261 |
||
262 |
/* output text */ |
|
263 |
||
64877 | 264 |
def decode_text: Boolean = options.bool("vscode_unicode_symbols") |
65107 | 265 |
def tooltip_margin: Int = options.int("vscode_tooltip_margin") |
266 |
def message_margin: Int = options.int("vscode_message_margin") |
|
64877 | 267 |
|
64870 | 268 |
def output_text(s: String): String = |
64877 | 269 |
if (decode_text) Symbol.decode(s) else Symbol.encode(s) |
64870 | 270 |
|
65107 | 271 |
def output_xml(xml: XML.Tree): String = |
272 |
output_text(XML.content(xml)) |
|
273 |
||
64870 | 274 |
def output_pretty(body: XML.Body, margin: Int): String = |
275 |
output_text(Pretty.string_of(body, margin)) |
|
65107 | 276 |
def output_pretty_tooltip(body: XML.Body): String = output_pretty(body, tooltip_margin) |
277 |
def output_pretty_message(body: XML.Body): String = output_pretty(body, message_margin) |
|
64605 | 278 |
} |