1use crate::ast::*;
10use crate::checker::{check_module, CheckHooks};
11use crate::fmt::{format, u16len};
12use crate::infer::{resolve_in, std_path, type_text, Target, Ty, STD};
13use crate::module::Module;
14use crate::parse::parse_source;
15use crate::semantics::{
16 is_rec, json_str, parse_path, read_json, rec_members, seg_text, Diag, MKind, RTk, Seg,
17 SlotState, Value, RT,
18};
19use crate::session::{fmt_diag, BindSource, Mode, Op, Run, Session};
20use regex::Regex;
21use std::cell::RefCell;
22use std::collections::HashMap;
23use std::io::{Read, Write};
24use std::path::{Path, PathBuf};
25use std::rc::Rc;
26
27#[derive(Clone)]
29enum J {
30 Null,
31 Bool(bool),
32 Num(i64),
33 Str(String),
34 Arr(Vec<J>),
35 Obj(Vec<(String, J)>),
36}
37impl J {
38 fn obj(pairs: Vec<(&str, J)>) -> J {
39 J::Obj(pairs.into_iter().map(|(k, v)| (k.to_string(), v)).collect())
40 }
41 fn s(v: impl Into<String>) -> J {
42 J::Str(v.into())
43 }
44 fn text(&self) -> String {
45 match self {
46 J::Null => "null".into(),
47 J::Bool(b) => b.to_string(),
48 J::Num(n) => n.to_string(),
49 J::Str(s) => json_str(s),
50 J::Arr(items) => format!(
51 "[{}]",
52 items.iter().map(J::text).collect::<Vec<_>>().join(",")
53 ),
54 J::Obj(es) => format!(
55 "{{{}}}",
56 es.iter()
57 .map(|(k, v)| format!("{}:{}", json_str(k), v.text()))
58 .collect::<Vec<_>>()
59 .join(",")
60 ),
61 }
62 }
63}
64
65fn get<'a>(v: &'a Value, key: &str) -> Option<&'a Value> {
67 match v {
68 Value::JObj(es) => es.iter().find(|(k, _)| k == key).map(|(_, x)| x),
69 _ => None,
70 }
71}
72fn as_str(v: Option<&Value>) -> Option<&str> {
73 match v {
74 Some(Value::Str(s)) => Some(s.as_str()),
75 _ => None,
76 }
77}
78fn as_usize(v: Option<&Value>) -> Option<usize> {
79 match v {
80 Some(Value::Int(i)) => i.to_string().parse().ok(),
81 Some(Value::Float(f)) => Some(*f as usize),
82 _ => None,
83 }
84}
85fn as_bool(v: Option<&Value>) -> Option<bool> {
86 match v {
87 Some(Value::Bool(b)) => Some(*b),
88 _ => None,
89 }
90}
91fn json_of(v: &Value) -> String {
92 match v {
93 Value::Null | Value::Undef | Value::Absent => "null".into(),
94 Value::Bool(b) => b.to_string(),
95 Value::Int(i) => i.to_string(),
96 Value::Float(f) => crate::semantics::js_num_str(*f),
97 Value::Str(s) => json_str(s),
98 Value::JArr(items) => format!(
99 "[{}]",
100 items.iter().map(json_of).collect::<Vec<_>>().join(",")
101 ),
102 Value::JObj(es) => format!(
103 "{{{}}}",
104 es.iter()
105 .map(|(k, x)| format!("{}:{}", json_str(k), json_of(x)))
106 .collect::<Vec<_>>()
107 .join(",")
108 ),
109 other => json_str(&format!("{other:?}")),
110 }
111}
112
113fn send(body: &str) {
115 let mut out = std::io::stdout().lock();
116 let _ = write!(out, "Content-Length: {}\r\n\r\n{}", body.len(), body);
117 let _ = out.flush();
118}
119fn reply(id: Option<&Value>, result: J) {
120 if let Some(id) = id {
121 send(&format!(
122 "{{\"jsonrpc\":\"2.0\",\"id\":{},\"result\":{}}}",
123 json_of(id),
124 result.text()
125 ));
126 }
127}
128fn notify(method: &str, params: J) {
129 send(&format!(
130 "{{\"jsonrpc\":\"2.0\",\"method\":{},\"params\":{}}}",
131 json_str(method),
132 params.text()
133 ));
134}
135
136pub fn path_of(uri: &str) -> PathBuf {
138 let raw = uri.strip_prefix("file://").unwrap_or(uri);
139 let raw = raw.split(['?', '#']).next().unwrap_or(raw);
140 let bytes = raw.as_bytes();
141 let mut out: Vec<u8> = Vec::with_capacity(bytes.len());
142 let mut i = 0;
143 while i < bytes.len() {
144 if bytes[i] == b'%' && i + 2 < bytes.len() && i + 2 < bytes.len() {
145 if let Ok(v) = u8::from_str_radix(&raw[i + 1..i + 3], 16) {
146 out.push(v);
147 i += 3;
148 continue;
149 }
150 }
151 out.push(bytes[i]);
152 i += 1;
153 }
154 PathBuf::from(String::from_utf8_lossy(&out).to_string())
155}
156pub fn uri_of(path: &Path) -> String {
157 let s = path.to_string_lossy();
158 let mut out = String::from("file://");
159 for b in s.bytes() {
160 if b.is_ascii_alphanumeric() || b"-._~/".contains(&b) {
161 out.push(b as char);
162 } else {
163 out.push_str(&format!("%{:02X}", b));
164 }
165 }
166 out
167}
168
169#[derive(Clone, Copy)]
170struct Pos {
171 line: usize,
172 character: usize,
173}
174fn range_json(l: Loc) -> J {
175 J::obj(vec![
176 (
177 "start",
178 J::obj(vec![
179 ("line", J::Num(l.sl as i64)),
180 ("character", J::Num(l.sc as i64)),
181 ]),
182 ),
183 (
184 "end",
185 J::obj(vec![
186 ("line", J::Num(l.el as i64)),
187 ("character", J::Num(l.ec as i64)),
188 ]),
189 ),
190 ])
191}
192fn contains(l: Loc, p: Pos) -> bool {
193 (l.sl < p.line || (l.sl == p.line && l.sc <= p.character))
194 && (p.line < l.el || (p.line == l.el && p.character <= l.ec))
195}
196fn span(l: Loc) -> usize {
197 (l.el - l.sl) * 100000 + l.ec.saturating_sub(l.sc)
198}
199
200fn u16_col(line: &str, byte: usize) -> usize {
202 u16len(line.get(..byte).unwrap_or(line))
203}
204fn byte_col(line: &str, col: usize) -> usize {
205 let mut units = 0;
206 for (i, ch) in line.char_indices() {
207 if units >= col {
208 return i;
209 }
210 units += ch.len_utf16();
211 }
212 line.len()
213}
214fn find16(line: &str, needle: &str, from: usize) -> Option<usize> {
216 let b = byte_col(line, from);
217 line.get(b..)?.find(needle).map(|i| u16_col(line, b + i))
218}
219fn rfind16(line: &str, needle: &str, from: usize) -> Option<usize> {
221 let b = byte_col(line, from);
222 let mut end = (b + needle.len()).min(line.len());
223 while !line.is_char_boundary(end) {
224 end -= 1;
225 }
226 line[..end].rfind(needle).map(|i| u16_col(line, i))
227}
228fn slice16(line: &str, a: usize, b: usize) -> &str {
229 let (ba, bb) = (byte_col(line, a), byte_col(line, b));
230 line.get(ba..bb.max(ba)).unwrap_or("")
231}
232
233struct Tables {
238 types: HashMap<usize, Ty>,
239 res: HashMap<usize, Option<Target>>,
240}
241fn key_of(e: &Rc<Expr>) -> usize {
242 Rc::as_ptr(e) as *const u8 as usize
243}
244struct Analysis {
245 text: String,
246 session: Session,
247 run: Run,
248 tables: RefCell<HashMap<PathBuf, Rc<Tables>>>,
249}
250
251struct State {
252 docs: Vec<(String, String)>,
254 overlay: HashMap<PathBuf, String>,
256 analyses: HashMap<String, Rc<Analysis>>,
257 last_good: HashMap<String, Rc<Analysis>>,
259 inputs: Vec<(String, String)>,
261 hint_types: bool,
263 hint_parameter_names: bool,
264 hint_values: bool,
265 hint_units: bool,
266 hint_context_variables: bool,
267 progress_supported: bool,
269 progress_seq: usize,
270}
271impl Default for State {
272 fn default() -> Self {
273 State {
274 docs: vec![],
275 overlay: HashMap::new(),
276 analyses: HashMap::new(),
277 last_good: HashMap::new(),
278 inputs: vec![],
279 hint_types: true,
280 hint_parameter_names: true,
281 hint_values: false,
282 hint_units: true,
283 hint_context_variables: false,
284 progress_supported: false,
285 progress_seq: 0,
286 }
287 }
288}
289impl State {
290 fn set(&mut self, uri: &str, text: String) {
291 if let Some(d) = self.docs.iter_mut().find(|(u, _)| u == uri) {
292 d.1 = text.clone();
293 } else {
294 self.docs.push((uri.to_string(), text.clone()));
295 }
296 self.overlay.insert(path_of(uri), text);
297 }
298 fn text(&self, uri: &str) -> Option<&String> {
299 self.docs.iter().find(|(u, _)| u == uri).map(|(_, t)| t)
300 }
301 fn analysis_of(&mut self, uri: &str) -> Option<Rc<Analysis>> {
302 let text = self.text(uri)?.clone();
303 if let Some(a) = self.analyses.get(uri) {
304 if a.text == text {
305 return Some(a.clone());
306 }
307 }
308 let path = path_of(uri);
309 if !parse_source(&text).errors.is_empty() {
310 return None;
311 }
312 let session = Session::with_overlay(Some(&path.to_string_lossy()), Some(&self.overlay));
313 let title = format!(
314 "Decl: evaluating {}",
315 path.file_name()
316 .map(|f| f.to_string_lossy().to_string())
317 .unwrap_or_default()
318 );
319 let token = if self.progress_supported {
320 self.progress_seq += 1;
321 let token = format!("decl-{}", self.progress_seq);
322 send(&format!("{{\"jsonrpc\":\"2.0\",\"id\":{},\"method\":\"window/workDoneProgress/create\",\"params\":{{\"token\":{}}}}}", self.progress_seq, json_str(&token)));
324 notify(
325 "$/progress",
326 J::obj(vec![
327 ("token", J::s(token.clone())),
328 (
329 "value",
330 J::obj(vec![
331 ("kind", J::s("begin")),
332 ("title", J::s(title)),
333 ("cancellable", J::Bool(false)),
334 ]),
335 ),
336 ]),
337 );
338 Some(token)
339 } else {
340 None
341 };
342 let run = session.run(Mode::Full);
343 if let Some(token) = token {
344 notify(
345 "$/progress",
346 J::obj(vec![
347 ("token", J::s(token)),
348 ("value", J::obj(vec![("kind", J::s("end"))])),
349 ]),
350 );
351 }
352 let a = Rc::new(Analysis {
353 text,
354 session,
355 run,
356 tables: RefCell::new(HashMap::new()),
357 });
358 self.analyses.insert(uri.to_string(), a.clone());
359 self.last_good.insert(uri.to_string(), a.clone());
360 Some(a)
361 }
362}
363fn tables_of(a: &Analysis, m: &Rc<Module>) -> Rc<Tables> {
364 if let Some(t) = a.tables.borrow().get(&m.path) {
365 return t.clone();
366 }
367 let types: Rc<RefCell<HashMap<usize, Ty>>> = Rc::new(RefCell::new(HashMap::new()));
368 let res: Rc<RefCell<HashMap<usize, Option<Target>>>> = Rc::new(RefCell::new(HashMap::new()));
369 let hooks = CheckHooks {
370 record: Some({
371 let types = types.clone();
372 Rc::new(move |e: &Rc<Expr>, ty: &Ty| {
373 types.borrow_mut().insert(key_of(e), ty.clone());
374 })
375 }),
376 resolve_hook: Some({
377 let res = res.clone();
378 Rc::new(move |e: &Rc<Expr>, target: Option<Target>| {
379 res.borrow_mut().insert(key_of(e), target);
380 })
381 }),
382 };
383 check_module(&m.decls, Some(m.env.clone()), Some(&hooks));
384 let t = Rc::new(Tables {
385 types: types.borrow().clone(),
386 res: res.borrow().clone(),
387 });
388 a.tables.borrow_mut().insert(m.path.clone(), t.clone());
389 t
390}
391fn module_of(a: &Analysis, path: &Path) -> Option<Rc<Module>> {
392 a.run.modules.iter().find(|m| m.path == path).cloned()
393}
394fn text_of(st: &State, m: &Module) -> String {
395 st.overlay
396 .get(&m.path)
397 .cloned()
398 .unwrap_or_else(|| read_text(&m.path))
399}
400fn read_text(path: &Path) -> String {
401 std::fs::read_to_string(path).unwrap_or_default()
402}
403
404fn anchor_for(src: &str, message: &str) -> Loc {
406 let names = Regex::new(r"[A-Za-z_][A-Za-z0-9_.]*").unwrap();
407 let lines: Vec<&str> = src.split('\n').collect();
408 for m in names.find_iter(message) {
409 let n = m.as_str();
410 if [
411 "error", "in", "the", "a", "is", "not", "std", "module", "import", "type", "name",
412 ]
413 .contains(&n)
414 {
415 continue;
416 }
417 let re = Regex::new(&format!(r"\b{}\b", regex::escape(n))).unwrap();
418 for (i, line) in lines.iter().enumerate() {
419 if let Some(mm) = re.find(line) {
420 let a = u16_col(line, mm.start());
421 return Loc {
422 sl: i,
423 sc: a,
424 el: i,
425 ec: a + u16len(n),
426 };
427 }
428 }
429 }
430 Loc {
431 sl: 0,
432 sc: 0,
433 el: 0,
434 ec: u16len(lines.first().copied().unwrap_or("")).max(1),
435 }
436}
437fn loc_of_path(decls: &[Decl], segs: &[Seg]) -> Option<Loc> {
440 let root = seg_text(segs.first()?);
441 let decl = decls.iter().find(|d| matches!(&d.body, DeclBody::Output { name, .. } | DeclBody::Input { name, .. } if *name == root))?;
442 let mut best = decl.loc?;
443 let mut e: Option<Rc<Expr>> = match &decl.body {
444 DeclBody::Output { expr, .. } => Some(expr.clone()),
445 DeclBody::Input { fallback, .. } => fallback.clone(),
446 _ => None,
447 };
448 for s in &segs[1..] {
449 let Some(mut cur) = e.clone() else { break };
450 if let Expr::Paren(x) = &*cur {
451 cur = x.clone();
452 }
453 let next: Option<Rc<Expr>> = match (&*cur, s) {
454 (Expr::Obj(entries), Seg::Name(k)) | (Expr::Obj(entries), Seg::Key(k)) => entries
455 .iter()
456 .find(|(kk, _)| kk == k)
457 .map(|(_, v)| v.clone()),
458 (Expr::Arr(items), Seg::Idx(i)) => items.get(*i).map(|(_, v)| v.clone()),
459 (Expr::With { base, .. }, _) => {
460 e = Some(base.clone());
461 continue;
462 }
463 _ => None,
464 };
465 let Some(next) = next else { break };
466 if let Some(l) = expr_loc(&next) {
467 best = l;
468 }
469 e = Some(next);
470 }
471 Some(best)
472}
473fn severity_of(s: &str) -> i64 {
474 match s {
475 "error" => 1,
476 "warning" => 2,
477 _ => 3,
478 }
479}
480fn diag_json(loc: Loc, d: &Diag) -> J {
481 let mut item = vec![
482 ("range", range_json(loc)),
483 ("severity", J::Num(severity_of(&d.severity))),
484 ("source", J::s("decl")),
485 ];
486 if d.code.is_some() || d.id.is_some() {
487 item.push((
488 "code",
489 J::s(d.id.clone().or_else(|| d.code.clone()).unwrap_or_default()),
490 ));
491 }
492 item.push((
493 "message",
494 J::s(if d.path.is_empty() {
495 d.message.clone()
496 } else {
497 format!("{} (at {})", d.message, d.path)
498 }),
499 ));
500 J::obj(item)
501}
502
503fn analyze(st: &mut State, uri: &str) {
504 let src = st.text(uri).cloned().unwrap_or_default();
505 let path = path_of(uri);
506 let mut out: Vec<J> = vec![];
507 let parsed = parse_source(&src);
508 if !parsed.errors.is_empty() {
509 for (row, col) in &parsed.errors {
510 out.push(J::obj(vec![
511 (
512 "range",
513 range_json(Loc {
514 sl: *row,
515 sc: *col,
516 el: *row,
517 ec: col + 1,
518 }),
519 ),
520 ("severity", J::Num(1)),
521 ("source", J::s("decl")),
522 ("code", J::s("E2001")),
523 ("message", J::s("syntax error")),
524 ]));
525 }
526 } else if let Some(a) = st.analysis_of(uri) {
527 let r = &a.run;
528 for d in &r.load_diags {
529 let imp = parsed.decls.iter().find(|x| match &x.body {
531 DeclBody::Import { from, .. } | DeclBody::ReExport { from, .. } => {
532 x.loc.is_some()
533 && d.message.contains(
534 from.strip_prefix("./")
535 .unwrap_or(from)
536 .strip_suffix(".decl")
537 .unwrap_or(from.strip_prefix("./").unwrap_or(from)),
538 )
539 }
540 _ => false,
541 });
542 out.push(diag_json(
543 imp.and_then(|x| x.loc)
544 .unwrap_or_else(|| anchor_for(&src, &d.message)),
545 d,
546 ));
547 }
548 for (file, d) in &r.checks {
549 if std::path::Path::new(file) != path {
551 continue;
552 }
553 out.push(diag_json(
554 d.loc.unwrap_or_else(|| anchor_for(&src, &d.message)),
555 d,
556 ));
557 }
558 for d in &r.diags {
559 if d.severity == "information" {
560 continue;
561 }
562 let segs = if d.path.is_empty() {
563 None
564 } else {
565 parse_path(&d.path, "").ok()
566 };
567 let Some(loc) = segs.and_then(|s| loc_of_path(&parsed.decls, &s)) else {
568 continue;
569 }; out.push(diag_json(loc, d));
571 }
572 }
573 notify(
574 "textDocument/publishDiagnostics",
575 J::obj(vec![("uri", J::s(uri)), ("diagnostics", J::Arr(out))]),
576 );
577}
578
579#[derive(Clone)]
581enum NodeRef<'a> {
582 Decl(&'a Decl),
583 Member(&'a MemberAst),
584 Type(&'a TypeAst),
585 Expr(&'a Rc<Expr>),
586}
587impl<'a> NodeRef<'a> {
588 fn loc(&self) -> Option<Loc> {
589 match self {
590 NodeRef::Decl(d) => d.loc,
591 NodeRef::Member(m) => m.loc(),
592 NodeRef::Type(t) => t.loc(),
593 NodeRef::Expr(e) => expr_loc(e),
594 }
595 }
596}
597struct Hit<'a> {
598 node: NodeRef<'a>,
599 loc: Loc,
600 parents: Vec<NodeRef<'a>>,
601}
602struct Finder<'a> {
603 pos: Pos,
604 best: Option<Hit<'a>>,
605}
606impl<'a> Finder<'a> {
607 fn enter(&mut self, node: NodeRef<'a>, parents: &[NodeRef<'a>]) -> Vec<NodeRef<'a>> {
610 let own = node.loc().filter(|l| contains(*l, self.pos));
611 if let Some(l) = own {
612 if self
613 .best
614 .as_ref()
615 .map(|b| span(l) <= span(b.loc))
616 .unwrap_or(true)
617 {
618 self.best = Some(Hit {
619 node: node.clone(),
620 loc: l,
621 parents: parents.to_vec(),
622 });
623 }
624 let mut p = parents.to_vec();
625 p.push(node);
626 p
627 } else {
628 parents.to_vec()
629 }
630 }
631 fn decls(&mut self, decls: &'a [Decl]) {
632 for d in decls {
633 self.decl(d, &[]);
634 }
635 }
636 fn decl(&mut self, d: &'a Decl, parents: &[NodeRef<'a>]) {
637 let p = self.enter(NodeRef::Decl(d), parents);
638 match &d.body {
639 DeclBody::Type {
640 params, ty, tail, ..
641 } => {
642 for pr in params {
643 if let Some(t) = &pr.ty {
644 self.ty(t, &p);
645 }
646 }
647 self.ty(ty, &p);
648 if let Some(t) = tail {
649 self.tail(t, &p);
650 }
651 }
652 DeclBody::Const { ty, expr, .. } => {
653 if let Some(t) = ty {
654 self.ty(t, &p);
655 }
656 self.expr(expr, &p);
657 }
658 DeclBody::Func {
659 params, ret, body, ..
660 } => {
661 for pr in params {
662 if let Some(t) = &pr.ty {
663 self.ty(t, &p);
664 }
665 }
666 if let Some(t) = ret {
667 self.ty(t, &p);
668 }
669 self.expr(body, &p);
670 }
671 DeclBody::Output { ty, expr, .. } => {
672 self.ty(ty, &p);
673 self.expr(expr, &p);
674 }
675 DeclBody::Input { ty, fallback, .. } => {
676 self.ty(ty, &p);
677 if let Some(f) = fallback {
678 self.expr(f, &p);
679 }
680 }
681 DeclBody::Diagnostic {
682 params, template, ..
683 } => {
684 for pr in params {
685 if let Some(t) = &pr.ty {
686 self.ty(t, &p);
687 }
688 }
689 self.template(template, &p);
690 }
691 DeclBody::Unit {
692 factor: Some(f), ..
693 } => {
694 self.expr(f, &p);
695 }
696 DeclBody::Unit { factor: None, .. } => {}
697 DeclBody::Dimension { .. } | DeclBody::Import { .. } | DeclBody::ReExport { .. } => {}
698 }
699 }
700 fn tail(&mut self, t: &'a Tail, p: &Vec<NodeRef<'a>>) {
701 match t {
702 Tail::Inline { template, .. } => self.template(template, p),
703 Tail::Ref { args, .. } => {
704 for a in args {
705 self.expr(a, p);
706 }
707 }
708 }
709 }
710 fn template(&mut self, parts: &'a [TPart], p: &Vec<NodeRef<'a>>) {
711 for part in parts {
712 if let TPart::Expr(x) = part {
713 self.expr(x, p);
714 }
715 }
716 }
717 fn ty(&mut self, t: &'a TypeAst, parents: &[NodeRef<'a>]) {
718 let p = self.enter(NodeRef::Type(t), parents);
719 match t {
720 TypeAst::Record { members, .. } => {
721 for m in members {
722 self.member(m, &p);
723 }
724 }
725 TypeAst::Map { key, val, .. } => {
726 self.ty(key, &p);
727 self.ty(val, &p);
728 }
729 TypeAst::Array { elem, .. } => self.ty(elem, &p),
730 TypeAst::Union { arms, .. } | TypeAst::Isect { arms, .. } => {
731 for a in arms {
732 self.ty(a, &p);
733 }
734 }
735 TypeAst::Func { params, ret, .. } => {
736 for a in params {
737 self.ty(a, &p);
738 }
739 self.ty(ret, &p);
740 }
741 TypeAst::Named {
742 args, preds, ext, ..
743 } => {
744 for a in args {
745 self.ty(a, &p);
746 }
747 for x in preds.iter().flatten() {
748 self.expr(x, &p);
749 }
750 if let Some(x) = ext {
751 self.ty(x, &p);
752 }
753 }
754 TypeAst::Prim { .. }
755 | TypeAst::Lit { .. }
756 | TypeAst::Range { .. }
757 | TypeAst::Pattern { .. } => {}
758 }
759 }
760 fn member(&mut self, m: &'a MemberAst, parents: &[NodeRef<'a>]) {
761 let p = self.enter(NodeRef::Member(m), parents);
762 match m {
763 MemberAst::Value { ty, dflt, .. } => {
764 self.ty(ty, &p);
765 if let Some(d) = dflt {
766 self.expr(d, &p);
767 }
768 }
769 MemberAst::Derived { ty, expr, .. } => {
770 if let Some(t) = ty {
771 self.ty(t, &p);
772 }
773 self.expr(expr, &p);
774 }
775 MemberAst::Context { ty, .. } => self.ty(ty, &p),
776 MemberAst::Assert { cond, tail, .. } => {
777 self.expr(cond, &p);
778 if let Some(t) = tail {
779 self.tail(t, &p);
780 }
781 }
782 MemberAst::When { cond, body, .. } => {
783 self.expr(cond, &p);
784 for b in body {
785 self.member(b, &p);
786 }
787 }
788 }
789 }
790 fn expr(&mut self, e: &'a Rc<Expr>, parents: &[NodeRef<'a>]) {
791 let p = self.enter(NodeRef::Expr(e), parents);
792 match &**e {
793 Expr::Template(parts) => self.template(parts, &p),
794 Expr::Obj(entries) => {
795 for (_, v) in entries {
796 self.expr(v, &p);
797 }
798 }
799 Expr::Arr(items) => {
800 for (_, v) in items {
801 self.expr(v, &p);
802 }
803 }
804 Expr::Comp { head, clauses } => {
805 self.expr(head, &p);
806 self.clauses(clauses, &p);
807 }
808 Expr::MapComp { key, val, clauses } => {
809 self.expr(key, &p);
810 self.expr(val, &p);
811 self.clauses(clauses, &p);
812 }
813 Expr::Bin { l, r, .. } => {
814 self.expr(l, &p);
815 self.expr(r, &p);
816 }
817 Expr::Un { x, .. } | Expr::Paren(x) => self.expr(x, &p),
818 Expr::If { c, t, f } => {
819 self.expr(c, &p);
820 self.expr(t, &p);
821 self.expr(f, &p);
822 }
823 Expr::Lambda { body, .. } => self.expr(body, &p),
824 Expr::Call { fun, args } => {
825 self.expr(fun, &p);
826 for a in args {
827 self.expr(a, &p);
828 }
829 }
830 Expr::Member { x, .. } => self.expr(x, &p),
831 Expr::Index { x, i } => {
832 self.expr(x, &p);
833 self.expr(i, &p);
834 }
835 Expr::With { base, patch } => {
836 self.expr(base, &p);
837 self.expr(patch, &p);
838 }
839 Expr::Match { subject, arms } => {
840 self.expr(subject, &p);
841 for a in arms {
842 if let Some(t) = &a.ty {
843 self.ty(t, &p);
844 }
845 self.expr(&a.body, &p);
846 }
847 }
848 Expr::Lit(_)
849 | Expr::UnitLit { .. }
850 | Expr::Name(_)
851 | Expr::Ctx(_)
852 | Expr::Referrers { .. }
853 | Expr::Pattern(_) => {}
854 }
855 }
856 fn clauses(&mut self, clauses: &'a [ForClause], p: &Vec<NodeRef<'a>>) {
857 for c in clauses {
858 self.expr(&c.iter, p);
859 for f in &c.filters {
860 self.expr(f, p);
861 }
862 }
863 }
864}
865fn node_at<'a>(decls: &'a [Decl], pos: Pos) -> Option<Hit<'a>> {
866 let mut f = Finder { pos, best: None };
867 f.decls(decls);
868 f.best
869}
870
871fn decl_kind(d: &Decl) -> &'static str {
872 match &d.body {
873 DeclBody::Type { .. } => "type",
874 DeclBody::Const { .. } => "const",
875 DeclBody::Func { .. } => "func",
876 DeclBody::Output { .. } => "output",
877 DeclBody::Input { .. } => "input",
878 DeclBody::Diagnostic { .. } => "diagnostic",
879 DeclBody::Dimension { .. } => "dimension",
880 DeclBody::Unit { .. } => "unit",
881 DeclBody::Import { .. } => "import",
882 DeclBody::ReExport { .. } => "re_export",
883 }
884}
885
886fn name_range(text: &str, decl: &Decl, name: &str) -> Loc {
888 let loc = decl.loc.unwrap();
889 let lines: Vec<&str> = text.split('\n').collect();
890 let re = Regex::new(&format!(r"\b{}\b", regex::escape(name))).unwrap();
891 let mut i = loc.sl;
892 while i <= loc.el && i < lines.len() {
893 let from = if i == loc.sl {
894 byte_col(lines[i], loc.sc)
895 } else {
896 0
897 };
898 if let Some(m) = re.find_at(lines[i], from) {
899 let a = u16_col(lines[i], m.start());
900 return Loc {
901 sl: i,
902 sc: a,
903 el: i,
904 ec: a + u16len(name),
905 };
906 }
907 i += 1;
908 }
909 loc
910}
911fn member_range(text: &str, member: &MemberAst, name: &str) -> Loc {
912 let loc = member.loc().unwrap();
913 let line = text.split('\n').nth(loc.sl).unwrap_or("");
914 match find16(line, name, loc.sc) {
915 Some(i) => Loc {
916 sl: loc.sl,
917 sc: i,
918 el: loc.sl,
919 ec: i + u16len(name),
920 },
921 None => loc,
922 }
923}
924
925#[derive(Clone)]
927struct Site {
928 kind: String,
929 module: Rc<Module>,
930 decl: Option<usize>, decl_loc: Option<Loc>,
932 member_loc: Option<Loc>,
933 range: Loc,
934 name: String,
935}
936fn decl_id(d: &Decl) -> usize {
937 d as *const Decl as usize
938}
939
940fn site_of_target(st: &State, a: &Analysis, t: Option<&Target>) -> Option<Site> {
942 let t = t?;
943 let env = t.env.as_ref()?;
944 let m = a
945 .run
946 .modules
947 .iter()
948 .find(|x| Rc::ptr_eq(&x.env, env))?
949 .clone();
950 let text = text_of(st, &m);
951 let decl = m.decls.iter().find(|d| {
952 d.name() == Some(t.name.as_str())
953 && d.loc.is_some()
954 && !matches!(d.body, DeclBody::Import { .. })
955 })?;
956 Some(Site {
957 kind: decl_kind(decl).to_string(),
958 module: m.clone(),
959 decl: Some(decl_id(decl)),
960 decl_loc: decl.loc,
961 member_loc: None,
962 range: name_range(&text, decl, &t.name),
963 name: t.name.clone(),
964 })
965}
966fn rec_name(rt: Option<&RT>) -> Option<String> {
967 let rt = rt?;
968 match &rt.k {
969 RTk::Rec(_) => rt.name.borrow().clone(),
970 RTk::Pred { base, .. } => base.name.borrow().clone(),
971 _ => None,
972 }
973}
974fn record_members(ty: &TypeAst) -> &[MemberAst] {
975 match ty {
976 TypeAst::Record { members, .. } => members,
977 TypeAst::Named { ext: Some(x), .. } => match &**x {
978 TypeAst::Record { members, .. } => members,
979 _ => &[],
980 },
981 _ => &[],
982 }
983}
984fn member_site(
985 st: &State,
986 a: &Analysis,
987 m: &Rc<Module>,
988 rt: Option<&RT>,
989 member: &str,
990) -> Option<Site> {
991 let mut seen: Vec<String> = vec![];
993 let mut type_name = rec_name(rt);
994 while let Some(tn) = type_name.clone() {
995 if seen.contains(&tn) {
996 break;
997 }
998 seen.push(tn.clone());
999 let target = resolve_in(&m.env, &tn);
1000 let site = site_of_target(st, a, target.as_ref())?;
1001 let sm = site.module.clone();
1002 let decl = sm
1003 .decls
1004 .iter()
1005 .find(|d| decl_id(d) == site.decl.unwrap_or(0))?;
1006 let DeclBody::Type { ty, .. } = &decl.body else {
1007 return None;
1008 };
1009 let members = record_members(ty);
1010 if let Some(mem) = members.iter().find(|x| x.name() == Some(member)) {
1011 if mem.loc().is_some() {
1012 return Some(Site {
1013 kind: "member".into(),
1014 module: sm.clone(),
1015 decl: Some(decl_id(decl)),
1016 decl_loc: decl.loc,
1017 member_loc: mem.loc(),
1018 range: member_range(&text_of(st, &sm), mem, member),
1019 name: member.to_string(),
1020 });
1021 }
1022 }
1023 type_name = match ty {
1024 TypeAst::Named { name, .. } => Some(name.clone()),
1025 _ => None,
1026 };
1027 }
1028 None
1029}
1030
1031struct SiteAt<'a> {
1032 site: Option<Site>,
1033 ty: Option<Ty>,
1034 hit: Option<Hit<'a>>,
1035 module: Rc<Module>,
1036}
1037fn ns_export_site(st: &State, a: &Analysis, m: &Module, ns: &str, name: &str) -> Option<Site> {
1038 let nss = m.env.namespaces.borrow();
1039 let (_, exports) = nss.get(ns)?;
1040 let ex = exports.borrow().get(name).cloned()?;
1041 site_of_target(st, a, resolve_in(&ex.env, &ex.name).as_ref())
1042}
1043fn site_at<'a>(st: &State, a: &'a Analysis, uri: &str, pos: Pos) -> Option<SiteAt<'a>> {
1044 let m = module_of(a, &path_of(uri))?;
1045 let mi = a.run.modules.iter().position(|x| Rc::ptr_eq(x, &m))?;
1046 let decls: &'a [Decl] = &a.run.modules[mi].decls;
1047 let Some(hit) = node_at(decls, pos) else {
1048 return Some(SiteAt {
1049 site: None,
1050 ty: None,
1051 hit: None,
1052 module: m,
1053 });
1054 };
1055 let t = tables_of(a, &m);
1056 match &hit.node {
1057 NodeRef::Expr(e) => {
1058 let ty = t.types.get(&key_of(e)).cloned();
1059 match &***e {
1060 Expr::Name(n) => {
1061 let target = match t.res.get(&key_of(e)) {
1062 Some(r) => r.clone(),
1063 None => resolve_in(&m.env, n),
1064 };
1065 Some(SiteAt {
1066 site: site_of_target(st, a, target.as_ref()),
1067 ty,
1068 hit: Some(hit),
1069 module: m,
1070 })
1071 }
1072 Expr::Member { x, name, .. } => {
1073 if let Expr::Name(xn) = &**x {
1074 if m.env.namespaces.borrow().contains_key(xn) {
1075 let site = ns_export_site(st, a, &m, xn, name);
1076 return Some(SiteAt {
1077 site,
1078 ty,
1079 hit: Some(hit),
1080 module: m,
1081 });
1082 }
1083 }
1084 let xt = t.types.get(&key_of(x)).cloned();
1085 let site =
1086 member_site(st, a, &m, xt.as_ref().and_then(|t| t.rt.as_ref()), name);
1087 Some(SiteAt {
1088 site,
1089 ty,
1090 hit: Some(hit),
1091 module: m,
1092 })
1093 }
1094 _ => Some(SiteAt {
1095 site: None,
1096 ty,
1097 hit: Some(hit),
1098 module: m,
1099 }),
1100 }
1101 }
1102 NodeRef::Type(TypeAst::Named { name, .. }) => {
1103 let mut parts = name.splitn(2, '.');
1104 let head = parts.next().unwrap_or("");
1105 let tail = parts.next();
1106 let target = match tail {
1107 Some(tail) if m.env.namespaces.borrow().contains_key(head) => {
1108 let site = ns_export_site(st, a, &m, head, tail);
1109 return Some(SiteAt {
1110 site,
1111 ty: None,
1112 hit: Some(hit),
1113 module: m,
1114 });
1115 }
1116 _ => resolve_in(&m.env, head),
1117 };
1118 Some(SiteAt {
1119 site: site_of_target(st, a, target.as_ref()),
1120 ty: None,
1121 hit: Some(hit),
1122 module: m,
1123 })
1124 }
1125 NodeRef::Member(mem) if mem.name().is_some() => {
1126 let name = mem.name().unwrap().to_string();
1128 let decl = hit.parents.iter().find_map(|p| {
1129 if let NodeRef::Decl(d) = p {
1130 Some(*d)
1131 } else {
1132 None
1133 }
1134 });
1135 let site = decl.map(|d| Site {
1136 kind: "member".into(),
1137 module: m.clone(),
1138 decl: Some(decl_id(d)),
1139 decl_loc: d.loc,
1140 member_loc: mem.loc(),
1141 range: member_range(&text_of(st, &m), mem, &name),
1142 name,
1143 });
1144 Some(SiteAt {
1145 site,
1146 ty: None,
1147 hit: Some(hit),
1148 module: m,
1149 })
1150 }
1151 NodeRef::Decl(d) if d.name().is_some() => {
1152 let name = d.name().unwrap().to_string();
1153 let r = name_range(&text_of(st, &m), d, &name);
1154 if contains(r, pos) {
1155 let site = Site {
1156 kind: decl_kind(d).to_string(),
1157 module: m.clone(),
1158 decl: Some(decl_id(d)),
1159 decl_loc: d.loc,
1160 member_loc: None,
1161 range: r,
1162 name,
1163 };
1164 return Some(SiteAt {
1165 site: Some(site),
1166 ty: None,
1167 hit: Some(hit),
1168 module: m,
1169 });
1170 }
1171 Some(SiteAt {
1172 site: None,
1173 ty: None,
1174 hit: Some(hit),
1175 module: m,
1176 })
1177 }
1178 _ => Some(SiteAt {
1179 site: None,
1180 ty: None,
1181 hit: Some(hit),
1182 module: m,
1183 }),
1184 }
1185}
1186
1187fn decl_text(st: &State, site: &Site) -> Vec<String> {
1189 let text = text_of(st, &site.module);
1190 let lines: Vec<&str> = text.split('\n').collect();
1191 let doc_re = Regex::new(r"^\s*///").unwrap();
1192 let doc_above = |sl: usize| -> Vec<String> {
1193 let mut from = sl;
1194 let mut out: Vec<String> = vec![];
1195 while from > 0 && doc_re.is_match(lines[from - 1]) {
1196 from -= 1;
1197 out.insert(0, lines[from].trim().to_string());
1198 }
1199 out
1200 };
1201 if let Some(l) = site.member_loc {
1202 let mut out = doc_above(l.sl);
1203 let body: Vec<String> = if l.sl == l.el {
1204 vec![slice16(lines.get(l.sl).copied().unwrap_or(""), l.sc, l.ec).to_string()]
1205 } else {
1206 let mut b = vec![slice16(lines[l.sl], l.sc, u16len(lines[l.sl])).to_string()];
1207 b.extend(lines[l.sl + 1..l.el].iter().map(|x| x.to_string()));
1208 b.push(slice16(lines[l.el], 0, l.ec).to_string());
1209 b
1210 };
1211 out.extend(
1212 body.iter()
1213 .map(|x| x.trim().to_string())
1214 .filter(|x| !x.is_empty()),
1215 );
1216 return out;
1217 }
1218 let l = site.decl_loc.unwrap();
1219 let mut out = doc_above(l.sl);
1220 let body: Vec<String> = lines[l.sl..=l.el.min(lines.len() - 1)]
1221 .iter()
1222 .map(|x| x.to_string())
1223 .collect();
1224 if body.len() > 12 {
1225 out.extend(body[..11].iter().cloned());
1226 out.push(" …".into());
1227 out.push(body[body.len() - 1].clone());
1228 } else {
1229 out.extend(body);
1230 }
1231 out
1232}
1233fn hover(st: &mut State, uri: &str, pos: Pos) -> J {
1234 let Some(a) = st.analysis_of(uri) else {
1235 return J::Null;
1236 };
1237 let Some(s) = site_at(st, &a, uri, pos) else {
1238 return J::Null;
1239 };
1240 let mut parts: Vec<String> = vec![];
1241 if let Some(site) = &s.site {
1242 let lines = decl_text(st, site);
1243 let doc: Vec<String> = lines
1244 .iter()
1245 .filter(|l| l.starts_with("///"))
1246 .map(|l| {
1247 l.strip_prefix("///")
1248 .map(|r| r.strip_prefix(' ').unwrap_or(r))
1249 .unwrap_or(l)
1250 .to_string()
1251 })
1252 .collect();
1253 let code: Vec<&String> = lines.iter().filter(|l| !l.starts_with("///")).collect();
1254 if !doc.is_empty() {
1255 parts.push(doc.join("\n"));
1256 }
1257 parts.push(format!(
1258 "```decl\n{}\n```",
1259 code.iter()
1260 .map(|x| x.as_str())
1261 .collect::<Vec<_>>()
1262 .join("\n")
1263 ));
1264 }
1265 if let Some(ty) = &s.ty {
1266 parts.push(format!(
1267 "`{}{}`",
1268 type_text(ty.rt.as_ref()),
1269 if ty.abs { "?" } else { "" }
1270 ));
1271 }
1272 if parts.is_empty() {
1273 return J::Null;
1274 }
1275 let contents = J::obj(vec![
1276 ("kind", J::s("markdown")),
1277 ("value", J::s(parts.join("\n\n"))),
1278 ]);
1279 match s.hit.as_ref().map(|h| h.loc) {
1280 Some(l) => J::obj(vec![("contents", contents), ("range", range_json(l))]),
1281 None => J::obj(vec![("contents", contents)]),
1282 }
1283}
1284
1285fn location(m: &Module, loc: Loc) -> J {
1287 J::obj(vec![
1288 ("uri", J::s(uri_of(&m.path))),
1289 ("range", range_json(loc)),
1290 ])
1291}
1292fn definition(st: &mut State, uri: &str, pos: Pos) -> J {
1293 let Some(a) = st.analysis_of(uri) else {
1294 return J::Null;
1295 };
1296 match site_at(st, &a, uri, pos).and_then(|s| s.site) {
1297 Some(site) => location(&site.module, site.range),
1298 None => J::Null,
1299 }
1300}
1301fn named_types_of(ast: &TypeAst, out: &mut Vec<String>) {
1303 match ast {
1304 TypeAst::Named { name, args, .. } => {
1305 out.push(name.clone());
1306 for arg in args {
1307 named_types_of(arg, out);
1308 }
1309 }
1310 TypeAst::Array { elem, .. } => named_types_of(elem, out),
1311 TypeAst::Union { arms, .. } | TypeAst::Isect { arms, .. } => {
1312 for arm in arms {
1313 named_types_of(arm, out);
1314 }
1315 }
1316 TypeAst::Map { val, .. } => named_types_of(val, out),
1317 _ => {}
1318 }
1319}
1320fn named_types_of_rt(rt: Option<&RT>, out: &mut Vec<String>) {
1322 let Some(rt) = rt else { return };
1323 match &rt.k {
1324 RTk::Rec(_) => {
1325 if let Some(n) = rt.name.borrow().clone() {
1326 if !n.starts_with('{') {
1327 out.push(n);
1328 }
1329 }
1330 }
1331 RTk::Pred { base, .. } => named_types_of_rt(Some(base), out),
1332 RTk::Ref(target) => named_types_of_rt(Some(target), out),
1333 RTk::Arr { elem, .. } => named_types_of_rt(Some(elem), out),
1334 RTk::Map { val, .. } => named_types_of_rt(Some(val), out),
1335 RTk::Union(arms) => {
1336 for arm in arms {
1337 named_types_of_rt(Some(arm), out);
1338 }
1339 }
1340 _ => {}
1341 }
1342}
1343fn member_type_ast(m: &Module, site: &Site) -> Option<TypeAst> {
1345 if site.kind != "member" {
1346 return None;
1347 }
1348 let d = decl_by_id(m, site.decl)?;
1349 record_body_of(d).map(record_members).and_then(|members| {
1350 members
1351 .iter()
1352 .find(|x| x.name() == Some(site.name.as_str()))
1353 .and_then(|x| match x {
1354 MemberAst::Value { ty, .. } | MemberAst::Context { ty, .. } => Some(ty.clone()),
1355 MemberAst::Derived { ty, .. } => ty.clone(),
1356 _ => None,
1357 })
1358 })
1359}
1360fn type_definition(st: &mut State, uri: &str, pos: Pos) -> J {
1361 let Some(a) = st.analysis_of(uri) else {
1362 return J::Null;
1363 };
1364 let Some(s) = site_at(st, &a, uri, pos) else {
1365 return J::Null;
1366 };
1367 let mut names: Vec<String> = vec![];
1372 let mut env = s.module.env.clone();
1373 if let Some(site) = &s.site {
1374 let sm = site.module.clone();
1375 let d = decl_by_id(&sm, site.decl);
1376 let ast: Option<TypeAst> = member_type_ast(&sm, site).or_else(|| {
1377 d.and_then(|d| match &d.body {
1378 DeclBody::Output { ty, .. } | DeclBody::Input { ty, .. } => Some(ty.clone()),
1379 DeclBody::Const { ty: Some(ty), .. } => Some(ty.clone()),
1380 _ => None,
1381 })
1382 });
1383 if let Some(ast) = ast {
1384 named_types_of(&ast, &mut names);
1385 env = sm.env.clone();
1386 } else if let Some(DeclBody::Const { expr, .. }) = d.map(|d| &d.body) {
1387 let rt = tables_of(&a, &sm)
1388 .types
1389 .get(&key_of(expr))
1390 .and_then(|x| x.rt.clone());
1391 named_types_of_rt(rt.as_ref(), &mut names);
1392 }
1393 }
1394 if names.is_empty() {
1395 if let Some(NodeRef::Expr(e)) = s.hit.as_ref().map(|h| h.node.clone()) {
1396 if let Expr::Member { x, name, .. } = &**e {
1397 let xt = tables_of(&a, &s.module)
1399 .types
1400 .get(&key_of(x))
1401 .and_then(|t| t.rt.clone());
1402 if let Some(ms) = member_site(st, &a, &s.module, xt.as_ref(), name) {
1403 if let Some(ast) = member_type_ast(&ms.module, &ms) {
1404 named_types_of(&ast, &mut names);
1405 env = ms.module.env.clone();
1406 }
1407 }
1408 }
1409 }
1410 }
1411 if names.is_empty() {
1412 named_types_of_rt(s.ty.as_ref().and_then(|t| t.rt.as_ref()), &mut names);
1413 }
1414 let mut seen: Vec<(PathBuf, usize, usize)> = vec![];
1415 let mut locs: Vec<J> = vec![];
1416 for n in names {
1417 let (head, tail) = match n.split_once('.') {
1418 Some((h, t)) => (h.to_string(), Some(t.to_string())),
1419 None => (n.clone(), None),
1420 };
1421 let target = match &tail {
1422 Some(t) if env.namespaces.borrow().contains_key(&head) => {
1423 let ex = env
1424 .namespaces
1425 .borrow()
1426 .get(&head)
1427 .and_then(|(_, exports)| exports.borrow().get(t).cloned());
1428 ex.and_then(|ex| resolve_in(&ex.env, &ex.name))
1429 }
1430 _ => resolve_in(&env, &head),
1431 };
1432 let Some(site) = site_of_target(st, &a, target.as_ref()) else {
1433 continue;
1434 };
1435 let key = (site.module.path.clone(), site.range.sl, site.range.sc);
1436 if seen.contains(&key) {
1437 continue;
1438 }
1439 seen.push(key);
1440 locs.push(location(&site.module, site.range));
1441 }
1442 match locs.len() {
1443 0 => J::Null,
1444 1 => locs.pop().unwrap(),
1445 _ => J::Arr(locs),
1446 }
1447}
1448fn same(x: Option<&Site>, target: &Site) -> bool {
1449 match x {
1450 Some(x) => {
1451 Rc::ptr_eq(&x.module, &target.module)
1452 && x.name == target.name
1453 && x.kind == target.kind
1454 && (x.kind != "member" || x.decl == target.decl)
1455 }
1456 None => false,
1457 }
1458}
1459fn member_token_loc(text: &str, e: &Rc<Expr>, name: &str) -> Loc {
1460 let l = expr_loc(e).unwrap();
1461 let line = text.split('\n').nth(l.el).unwrap_or("");
1462 match rfind16(line, name, l.ec) {
1463 Some(i) => Loc {
1464 sl: l.el,
1465 sc: i,
1466 el: l.el,
1467 ec: i + u16len(name),
1468 },
1469 None => l,
1470 }
1471}
1472fn type_name_loc(l: Loc, offset: usize, name: &str) -> Loc {
1473 Loc {
1474 sl: l.sl,
1475 sc: l.sc + offset,
1476 el: l.sl,
1477 ec: l.sc + offset + u16len(name),
1478 }
1479}
1480fn import_item_loc(text: &str, d: &Decl, name: &str) -> Loc {
1481 let l = d.loc.unwrap();
1482 let line = text.split('\n').nth(l.sl).unwrap_or("");
1483 match find16(line, name, l.sc) {
1484 Some(i) => Loc {
1485 sl: l.sl,
1486 sc: i,
1487 el: l.sl,
1488 ec: i + u16len(name),
1489 },
1490 None => l,
1491 }
1492}
1493fn references(
1496 st: &mut State,
1497 uri: &str,
1498 pos: Pos,
1499 include_declaration: bool,
1500) -> Vec<(Rc<Module>, Loc)> {
1501 let Some(a) = st.analysis_of(uri) else {
1502 return vec![];
1503 };
1504 let Some(target) = site_at(st, &a, uri, pos).and_then(|s| s.site) else {
1505 return vec![];
1506 };
1507 let mut out: Vec<(Rc<Module>, Loc)> = vec![];
1508 for m in a.run.modules.clone() {
1509 let t = tables_of(&a, &m);
1510 let text = text_of(st, &m);
1511 let mut exprs: Vec<Rc<Expr>> = vec![];
1513 let mut types: Vec<&TypeAst> = vec![];
1514 for d in &m.decls {
1515 collect_decl(d, &mut exprs, &mut types);
1516 }
1517 for x in &exprs {
1518 if expr_loc(x).is_none() {
1519 continue;
1520 }
1521 match &**x {
1522 Expr::Name(n) => {
1523 let tg = match t.res.get(&key_of(x)) {
1524 Some(r) => r.clone(),
1525 None => resolve_in(&m.env, n),
1526 };
1527 if same(site_of_target(st, &a, tg.as_ref()).as_ref(), &target) {
1528 out.push((m.clone(), expr_loc(x).unwrap()));
1529 }
1530 }
1531 Expr::Member { x: xx, name, .. } => {
1532 let site = match &**xx {
1533 Expr::Name(xn) if m.env.namespaces.borrow().contains_key(xn) => {
1534 ns_export_site(st, &a, &m, xn, name)
1535 }
1536 _ => member_site(
1537 st,
1538 &a,
1539 &m,
1540 t.types.get(&key_of(xx)).and_then(|t| t.rt.as_ref()),
1541 name,
1542 ),
1543 };
1544 if same(site.as_ref(), &target) {
1545 out.push((m.clone(), member_token_loc(&text, x, name)));
1546 }
1547 }
1548 _ => {}
1549 }
1550 }
1551 for ty in types {
1552 let TypeAst::Named {
1553 name, loc: Some(l), ..
1554 } = ty
1555 else {
1556 continue;
1557 };
1558 let mut parts = name.splitn(2, '.');
1559 let head = parts.next().unwrap_or("");
1560 let tail = parts.next();
1561 let site = match tail {
1562 Some(tail) if m.env.namespaces.borrow().contains_key(head) => {
1563 ns_export_site(st, &a, &m, head, tail)
1564 }
1565 _ => site_of_target(st, &a, resolve_in(&m.env, head).as_ref()),
1566 };
1567 if same(site.as_ref(), &target) {
1568 out.push((
1569 m.clone(),
1570 type_name_loc(
1571 *l,
1572 if tail.is_some() { u16len(head) + 1 } else { 0 },
1573 tail.unwrap_or(head),
1574 ),
1575 ));
1576 }
1577 }
1578 for d in &m.decls {
1580 let (names, has_loc) = match &d.body {
1581 DeclBody::Import {
1582 names: Some(names), ..
1583 } => (names, d.loc.is_some()),
1584 DeclBody::ReExport { names, .. } => (names, d.loc.is_some()),
1585 _ => continue,
1586 };
1587 if !has_loc {
1588 continue;
1589 }
1590 for it in names {
1591 let local = it.alias.clone().unwrap_or_else(|| it.name.clone());
1592 let im = m.env.imports.borrow().get(&local).cloned();
1593 if let Some(im) = im {
1594 if same(
1595 site_of_target(st, &a, resolve_in(&im.env, &im.name).as_ref()).as_ref(),
1596 &target,
1597 ) {
1598 out.push((m.clone(), import_item_loc(&text, d, &it.name)));
1599 }
1600 }
1601 }
1602 }
1603 }
1604 if include_declaration {
1605 out.insert(0, (target.module.clone(), target.range));
1606 }
1607 let mut seen: Vec<(PathBuf, usize, usize)> = vec![];
1608 let mut kept: Vec<(Rc<Module>, Loc)> = vec![];
1609 for (m, l) in out {
1610 let k = (m.path.clone(), l.sl, l.sc);
1611 if seen.contains(&k) {
1612 continue;
1613 }
1614 seen.push(k);
1615 kept.push((m, l));
1616 }
1617 kept.sort_by(|p, q| {
1618 let (pp, qp) = (
1619 p.0.path.to_string_lossy().to_string(),
1620 q.0.path.to_string_lossy().to_string(),
1621 );
1622 pp.cmp(&qp)
1623 .then(p.1.sl.cmp(&q.1.sl))
1624 .then(p.1.sc.cmp(&q.1.sc))
1625 });
1626 kept
1627}
1628fn collect_decl<'a>(d: &'a Decl, exprs: &mut Vec<Rc<Expr>>, types: &mut Vec<&'a TypeAst>) {
1630 let mut ty = |t: &'a TypeAst| collect_type(t, exprs, types);
1631 match &d.body {
1632 DeclBody::Type {
1633 params,
1634 ty: t,
1635 tail,
1636 ..
1637 } => {
1638 for p in params {
1639 if let Some(pt) = &p.ty {
1640 collect_type(pt, exprs, types);
1641 }
1642 }
1643 collect_type(t, exprs, types);
1644 if let Some(tl) = tail {
1645 collect_tail(tl, exprs, types);
1646 }
1647 }
1648 DeclBody::Const { ty: t, expr, .. } => {
1649 if let Some(t) = t {
1650 ty(t);
1651 }
1652 collect_expr(expr, exprs, types);
1653 }
1654 DeclBody::Func {
1655 params, ret, body, ..
1656 } => {
1657 for p in params {
1658 if let Some(pt) = &p.ty {
1659 collect_type(pt, exprs, types);
1660 }
1661 }
1662 if let Some(r) = ret {
1663 collect_type(r, exprs, types);
1664 }
1665 collect_expr(body, exprs, types);
1666 }
1667 DeclBody::Output { ty: t, expr, .. } => {
1668 collect_type(t, exprs, types);
1669 collect_expr(expr, exprs, types);
1670 }
1671 DeclBody::Input {
1672 ty: t, fallback, ..
1673 } => {
1674 collect_type(t, exprs, types);
1675 if let Some(f) = fallback {
1676 collect_expr(f, exprs, types);
1677 }
1678 }
1679 DeclBody::Diagnostic {
1680 params, template, ..
1681 } => {
1682 for p in params {
1683 if let Some(pt) = &p.ty {
1684 collect_type(pt, exprs, types);
1685 }
1686 }
1687 collect_template(template, exprs, types);
1688 }
1689 DeclBody::Unit {
1690 factor: Some(f), ..
1691 } => {
1692 collect_expr(f, exprs, types);
1693 }
1694 _ => {}
1695 }
1696}
1697fn collect_tail<'a>(t: &'a Tail, exprs: &mut Vec<Rc<Expr>>, types: &mut Vec<&'a TypeAst>) {
1698 match t {
1699 Tail::Inline { template, .. } => collect_template(template, exprs, types),
1700 Tail::Ref { args, .. } => args.iter().for_each(|a| collect_expr(a, exprs, types)),
1701 }
1702}
1703fn collect_template<'a>(
1704 parts: &'a [TPart],
1705 exprs: &mut Vec<Rc<Expr>>,
1706 types: &mut Vec<&'a TypeAst>,
1707) {
1708 for p in parts {
1709 if let TPart::Expr(x) = p {
1710 collect_expr(x, exprs, types);
1711 }
1712 }
1713}
1714fn collect_type<'a>(t: &'a TypeAst, exprs: &mut Vec<Rc<Expr>>, types: &mut Vec<&'a TypeAst>) {
1715 types.push(t);
1716 match t {
1717 TypeAst::Record { members, .. } => {
1718 members.iter().for_each(|m| collect_member(m, exprs, types))
1719 }
1720 TypeAst::Map { key, val, .. } => {
1721 collect_type(key, exprs, types);
1722 collect_type(val, exprs, types);
1723 }
1724 TypeAst::Array { elem, .. } => collect_type(elem, exprs, types),
1725 TypeAst::Union { arms, .. } | TypeAst::Isect { arms, .. } => {
1726 arms.iter().for_each(|a| collect_type(a, exprs, types))
1727 }
1728 TypeAst::Func { params, ret, .. } => {
1729 params.iter().for_each(|a| collect_type(a, exprs, types));
1730 collect_type(ret, exprs, types);
1731 }
1732 TypeAst::Named {
1733 args, preds, ext, ..
1734 } => {
1735 args.iter().for_each(|a| collect_type(a, exprs, types));
1736 preds
1737 .iter()
1738 .flatten()
1739 .for_each(|x| collect_expr(x, exprs, types));
1740 if let Some(x) = ext {
1741 collect_type(x, exprs, types);
1742 }
1743 }
1744 _ => {}
1745 }
1746}
1747fn collect_member<'a>(m: &'a MemberAst, exprs: &mut Vec<Rc<Expr>>, types: &mut Vec<&'a TypeAst>) {
1748 match m {
1749 MemberAst::Value { ty, dflt, .. } => {
1750 collect_type(ty, exprs, types);
1751 if let Some(d) = dflt {
1752 collect_expr(d, exprs, types);
1753 }
1754 }
1755 MemberAst::Derived { ty, expr, .. } => {
1756 if let Some(t) = ty {
1757 collect_type(t, exprs, types);
1758 }
1759 collect_expr(expr, exprs, types);
1760 }
1761 MemberAst::Context { ty, .. } => collect_type(ty, exprs, types),
1762 MemberAst::Assert { cond, tail, .. } => {
1763 collect_expr(cond, exprs, types);
1764 if let Some(t) = tail {
1765 collect_tail(t, exprs, types);
1766 }
1767 }
1768 MemberAst::When { cond, body, .. } => {
1769 collect_expr(cond, exprs, types);
1770 body.iter().for_each(|b| collect_member(b, exprs, types));
1771 }
1772 }
1773}
1774fn collect_expr<'a>(e: &'a Rc<Expr>, exprs: &mut Vec<Rc<Expr>>, types: &mut Vec<&'a TypeAst>) {
1775 exprs.push(e.clone());
1776 let mut go = |x: &'a Rc<Expr>| collect_expr(x, exprs, types);
1777 match &**e {
1778 Expr::Template(parts) => {
1779 for p in parts {
1780 if let TPart::Expr(x) = p {
1781 go(x);
1782 }
1783 }
1784 }
1785 Expr::Obj(entries) => entries.iter().for_each(|(_, v)| go(v)),
1786 Expr::Arr(items) => items.iter().for_each(|(_, v)| go(v)),
1787 Expr::Comp { head, clauses } => {
1788 go(head);
1789 for c in clauses {
1790 go(&c.iter);
1791 c.filters.iter().for_each(&mut go);
1792 }
1793 }
1794 Expr::MapComp { key, val, clauses } => {
1795 go(key);
1796 go(val);
1797 for c in clauses {
1798 go(&c.iter);
1799 c.filters.iter().for_each(&mut go);
1800 }
1801 }
1802 Expr::Bin { l, r, .. } => {
1803 go(l);
1804 go(r);
1805 }
1806 Expr::Un { x, .. } | Expr::Paren(x) => go(x),
1807 Expr::If { c, t, f } => {
1808 go(c);
1809 go(t);
1810 go(f);
1811 }
1812 Expr::Lambda { body, .. } => go(body),
1813 Expr::Call { fun, args } => {
1814 go(fun);
1815 args.iter().for_each(&mut go);
1816 }
1817 Expr::Member { x, .. } => go(x),
1818 Expr::Index { x, i } => {
1819 go(x);
1820 go(i);
1821 }
1822 Expr::With { base, patch } => {
1823 go(base);
1824 go(patch);
1825 }
1826 Expr::Match { subject, arms } => {
1827 go(subject);
1828 for a in arms {
1829 if let Some(t) = &a.ty {
1830 collect_type(t, exprs, types);
1831 }
1832 collect_expr(&a.body, exprs, types);
1833 }
1834 }
1835 _ => {}
1836 }
1837}
1838
1839fn completion(st: &mut State, uri: &str, pos: Pos) -> J {
1841 let a = st.analysis_of(uri);
1842 let Some(text) = st.text(uri).cloned() else {
1843 return J::obj(vec![
1844 ("isIncomplete", J::Bool(false)),
1845 ("items", J::Arr(vec![])),
1846 ]);
1847 };
1848 let line = text.split('\n').nth(pos.line).unwrap_or("");
1849 let prefix = &line[..byte_col(line, pos.character)];
1850 let fresh;
1852 let session: &Session = match a.as_ref().or_else(|| st.last_good.get(uri)) {
1853 Some(x) => &x.session,
1854 None => {
1855 fresh = Session::with_overlay(Some(&path_of(uri).to_string_lossy()), Some(&st.overlay));
1856 &fresh
1857 }
1858 };
1859 let items: Vec<J> = session
1860 .complete(prefix, &[])
1861 .iter()
1862 .map(|c| {
1863 let (label, detail) = match c.find(" ") {
1864 Some(i) => (&c[..i], Some(&c[i + 2..])),
1865 None => (c.as_str(), None),
1866 };
1867 let kind = match detail {
1868 Some(d) => {
1869 if d.starts_with("derived")
1870 || d.starts_with("required")
1871 || d.starts_with("optional")
1872 || d.starts_with("defaulted")
1873 {
1874 5
1875 } else {
1876 6
1877 }
1878 }
1879 None => {
1880 if label
1881 .chars()
1882 .next()
1883 .map(|c| c.is_ascii_uppercase())
1884 .unwrap_or(false)
1885 {
1886 7
1887 } else if label.starts_with('$') {
1888 14
1889 } else {
1890 6
1891 }
1892 }
1893 };
1894 let mut item = vec![("label", J::s(label)), ("kind", J::Num(kind))];
1895 if let Some(d) = detail {
1896 item.push(("detail", J::s(d)));
1897 }
1898 J::obj(item)
1899 })
1900 .collect();
1901 J::obj(vec![
1902 ("isIncomplete", J::Bool(false)),
1903 ("items", J::Arr(items)),
1904 ])
1905}
1906
1907fn symbol_kind(d: &Decl) -> Option<i64> {
1909 Some(match &d.body {
1910 DeclBody::Type { .. } => 5,
1911 DeclBody::Const { .. } => 14,
1912 DeclBody::Func { .. } => 12,
1913 DeclBody::Output { .. }
1914 | DeclBody::Input { .. }
1915 | DeclBody::Dimension { .. }
1916 | DeclBody::Unit { .. } => 13,
1917 DeclBody::Diagnostic { .. } => 24,
1918 _ => return None,
1919 })
1920}
1921fn document_symbols(st: &State, uri: &str) -> J {
1922 let Some(text) = st.text(uri) else {
1923 return J::Arr(vec![]);
1924 };
1925 let parsed = parse_source(text);
1926 if !parsed.errors.is_empty() {
1927 return J::Arr(vec![]);
1928 }
1929 let mut out: Vec<J> = vec![];
1930 for d in &parsed.decls {
1931 let (Some(loc), Some(name), Some(kind)) = (d.loc, d.name(), symbol_kind(d)) else {
1932 continue;
1933 };
1934 let mut sym = vec![
1935 ("name", J::s(name)),
1936 ("kind", J::Num(kind)),
1937 ("range", range_json(loc)),
1938 ("selectionRange", range_json(name_range(text, d, name))),
1939 ];
1940 if let DeclBody::Type { ty, .. } = &d.body {
1941 let children: Vec<J> = record_members(ty)
1942 .iter()
1943 .filter(|m| m.loc().is_some() && m.name().is_some())
1944 .map(|m| {
1945 let n = m.name().unwrap();
1946 let (label, kind) = match m {
1947 MemberAst::Assert { .. } => (format!("assert {n}"), 24),
1948 MemberAst::Derived { hidden: true, .. } => (format!("{n}$"), 7),
1949 _ => (n.to_string(), 7),
1950 };
1951 J::obj(vec![
1952 ("name", J::s(label)),
1953 ("kind", J::Num(kind)),
1954 ("range", range_json(m.loc().unwrap())),
1955 ("selectionRange", range_json(member_range(text, m, n))),
1956 ])
1957 })
1958 .collect();
1959 if !children.is_empty() {
1960 sym.push(("children", J::Arr(children)));
1961 }
1962 }
1963 out.push(J::obj(sym));
1964 }
1965 J::Arr(out)
1966}
1967fn folding_ranges(st: &State, uri: &str) -> J {
1968 let Some(text) = st.text(uri) else {
1969 return J::Arr(vec![]);
1970 };
1971 let parsed = parse_source(text);
1972 if !parsed.errors.is_empty() {
1973 return J::Arr(vec![]);
1974 }
1975 let mut ranges: Vec<(usize, usize)> = vec![];
1976 for d in &parsed.decls {
1977 fold_decl(d, &mut ranges);
1978 }
1979 let mut seen: Vec<(usize, usize)> = vec![];
1980 let mut out: Vec<J> = vec![];
1981 for r in ranges {
1982 if seen.contains(&r) {
1983 continue;
1984 }
1985 seen.push(r);
1986 out.push(J::obj(vec![
1987 ("startLine", J::Num(r.0 as i64)),
1988 ("endLine", J::Num(r.1 as i64)),
1989 ("kind", J::s("region")),
1990 ]));
1991 }
1992 J::Arr(out)
1993}
1994fn fold_push(loc: Option<Loc>, out: &mut Vec<(usize, usize)>) {
1995 if let Some(l) = loc {
1996 if l.el > l.sl {
1997 out.push((l.sl, l.el));
1998 }
1999 }
2000}
2001fn fold_decl(d: &Decl, out: &mut Vec<(usize, usize)>) {
2002 fold_push(d.loc, out);
2003 let mut exprs: Vec<Rc<Expr>> = vec![];
2004 let mut types: Vec<&TypeAst> = vec![];
2005 fold_walk_decl(d, out);
2008 let _ = (&mut exprs, &mut types);
2009}
2010fn fold_walk_decl(d: &Decl, out: &mut Vec<(usize, usize)>) {
2011 match &d.body {
2012 DeclBody::Type {
2013 params, ty, tail, ..
2014 } => {
2015 for p in params {
2016 if let Some(t) = &p.ty {
2017 fold_type(t, out);
2018 }
2019 }
2020 fold_type(ty, out);
2021 if let Some(t) = tail {
2022 fold_tail(t, out);
2023 }
2024 }
2025 DeclBody::Const { ty, expr, .. } => {
2026 if let Some(t) = ty {
2027 fold_type(t, out);
2028 }
2029 fold_expr(expr, out);
2030 }
2031 DeclBody::Func {
2032 params, ret, body, ..
2033 } => {
2034 for p in params {
2035 if let Some(t) = &p.ty {
2036 fold_type(t, out);
2037 }
2038 }
2039 if let Some(t) = ret {
2040 fold_type(t, out);
2041 }
2042 fold_expr(body, out);
2043 }
2044 DeclBody::Output { ty, expr, .. } => {
2045 fold_type(ty, out);
2046 fold_expr(expr, out);
2047 }
2048 DeclBody::Input { ty, fallback, .. } => {
2049 fold_type(ty, out);
2050 if let Some(f) = fallback {
2051 fold_expr(f, out);
2052 }
2053 }
2054 DeclBody::Diagnostic {
2055 params, template, ..
2056 } => {
2057 for p in params {
2058 if let Some(t) = &p.ty {
2059 fold_type(t, out);
2060 }
2061 }
2062 fold_template(template, out);
2063 }
2064 DeclBody::Unit {
2065 factor: Some(f), ..
2066 } => {
2067 fold_expr(f, out);
2068 }
2069 _ => {}
2070 }
2071}
2072fn fold_tail(t: &Tail, out: &mut Vec<(usize, usize)>) {
2073 match t {
2074 Tail::Inline { template, .. } => fold_template(template, out),
2075 Tail::Ref { args, .. } => args.iter().for_each(|a| fold_expr(a, out)),
2076 }
2077}
2078fn fold_template(parts: &[TPart], out: &mut Vec<(usize, usize)>) {
2079 for p in parts {
2080 if let TPart::Expr(x) = p {
2081 fold_expr(x, out);
2082 }
2083 }
2084}
2085fn fold_type(t: &TypeAst, out: &mut Vec<(usize, usize)>) {
2086 if let TypeAst::Record { .. } = t {
2087 fold_push(t.loc(), out);
2088 }
2089 match t {
2090 TypeAst::Record { members, .. } => members.iter().for_each(|m| fold_member(m, out)),
2091 TypeAst::Map { key, val, .. } => {
2092 fold_type(key, out);
2093 fold_type(val, out);
2094 }
2095 TypeAst::Array { elem, .. } => fold_type(elem, out),
2096 TypeAst::Union { arms, .. } | TypeAst::Isect { arms, .. } => {
2097 arms.iter().for_each(|a| fold_type(a, out))
2098 }
2099 TypeAst::Func { params, ret, .. } => {
2100 params.iter().for_each(|a| fold_type(a, out));
2101 fold_type(ret, out);
2102 }
2103 TypeAst::Named {
2104 args, preds, ext, ..
2105 } => {
2106 args.iter().for_each(|a| fold_type(a, out));
2107 preds.iter().flatten().for_each(|x| fold_expr(x, out));
2108 if let Some(x) = ext {
2109 fold_type(x, out);
2110 }
2111 }
2112 _ => {}
2113 }
2114}
2115fn fold_member(m: &MemberAst, out: &mut Vec<(usize, usize)>) {
2116 if let MemberAst::When { .. } = m {
2117 fold_push(m.loc(), out);
2118 }
2119 match m {
2120 MemberAst::Value { ty, dflt, .. } => {
2121 fold_type(ty, out);
2122 if let Some(d) = dflt {
2123 fold_expr(d, out);
2124 }
2125 }
2126 MemberAst::Derived { ty, expr, .. } => {
2127 if let Some(t) = ty {
2128 fold_type(t, out);
2129 }
2130 fold_expr(expr, out);
2131 }
2132 MemberAst::Context { ty, .. } => fold_type(ty, out),
2133 MemberAst::Assert { cond, tail, .. } => {
2134 fold_expr(cond, out);
2135 if let Some(t) = tail {
2136 fold_tail(t, out);
2137 }
2138 }
2139 MemberAst::When { cond, body, .. } => {
2140 fold_expr(cond, out);
2141 body.iter().for_each(|b| fold_member(b, out));
2142 }
2143 }
2144}
2145fn fold_expr(e: &Rc<Expr>, out: &mut Vec<(usize, usize)>) {
2146 if matches!(&**e, Expr::Obj(_) | Expr::Arr(_) | Expr::Match { .. }) {
2147 fold_push(expr_loc(e), out);
2148 }
2149 match &**e {
2150 Expr::Template(parts) => fold_template(parts, out),
2151 Expr::Obj(entries) => entries.iter().for_each(|(_, v)| fold_expr(v, out)),
2152 Expr::Arr(items) => items.iter().for_each(|(_, v)| fold_expr(v, out)),
2153 Expr::Comp { head, clauses } => {
2154 fold_expr(head, out);
2155 for c in clauses {
2156 fold_expr(&c.iter, out);
2157 c.filters.iter().for_each(|f| fold_expr(f, out));
2158 }
2159 }
2160 Expr::MapComp { key, val, clauses } => {
2161 fold_expr(key, out);
2162 fold_expr(val, out);
2163 for c in clauses {
2164 fold_expr(&c.iter, out);
2165 c.filters.iter().for_each(|f| fold_expr(f, out));
2166 }
2167 }
2168 Expr::Bin { l, r, .. } => {
2169 fold_expr(l, out);
2170 fold_expr(r, out);
2171 }
2172 Expr::Un { x, .. } | Expr::Paren(x) => fold_expr(x, out),
2173 Expr::If { c, t, f } => {
2174 fold_expr(c, out);
2175 fold_expr(t, out);
2176 fold_expr(f, out);
2177 }
2178 Expr::Lambda { body, .. } => fold_expr(body, out),
2179 Expr::Call { fun, args } => {
2180 fold_expr(fun, out);
2181 args.iter().for_each(|a| fold_expr(a, out));
2182 }
2183 Expr::Member { x, .. } => fold_expr(x, out),
2184 Expr::Index { x, i } => {
2185 fold_expr(x, out);
2186 fold_expr(i, out);
2187 }
2188 Expr::With { base, patch } => {
2189 fold_expr(base, out);
2190 fold_expr(patch, out);
2191 }
2192 Expr::Match { subject, arms } => {
2193 fold_expr(subject, out);
2194 for a in arms {
2195 if let Some(t) = &a.ty {
2196 fold_type(t, out);
2197 }
2198 fold_expr(&a.body, out);
2199 }
2200 }
2201 _ => {}
2202 }
2203}
2204fn formatting(st: &State, uri: &str) -> J {
2205 let Some(text) = st.text(uri) else {
2206 return J::Arr(vec![]);
2207 };
2208 let Ok(out) = format(text) else {
2209 return J::Arr(vec![]);
2210 };
2211 if &out == text {
2212 return J::Arr(vec![]);
2213 }
2214 let lines: Vec<&str> = text.split('\n').collect();
2215 let last = lines.len() - 1;
2216 J::Arr(vec![J::obj(vec![
2217 (
2218 "range",
2219 range_json(Loc {
2220 sl: 0,
2221 sc: 0,
2222 el: last,
2223 ec: u16len(lines[last]),
2224 }),
2225 ),
2226 ("newText", J::s(out)),
2227 ])])
2228}
2229
2230fn prepare_rename(st: &mut State, uri: &str, pos: Pos) -> J {
2232 let a = st.analysis_of(uri);
2233 let s = a.as_ref().and_then(|a| site_at(st, a, uri, pos));
2234 if s.as_ref().map(|s| s.site.is_none()).unwrap_or(true) {
2235 let Some(locs) = local_ranges(st, uri, pos) else {
2237 return J::Null;
2238 };
2239 let Some(here) = locs.iter().find(|l| contains(**l, pos)) else {
2240 return J::Null;
2241 };
2242 let text = st.text(uri).cloned().unwrap_or_default();
2243 let line = text.split('\n').nth(here.sl).unwrap_or("");
2244 return J::obj(vec![
2245 ("range", range_json(*here)),
2246 ("placeholder", J::s(slice16(line, here.sc, here.ec))),
2247 ]);
2248 }
2249 let s = s.unwrap();
2250 let (Some(site), Some(hit)) = (&s.site, &s.hit) else {
2251 return J::Null;
2252 };
2253 let loc = match &hit.node {
2254 NodeRef::Expr(e) => match &***e {
2255 Expr::Member { name, .. } => member_token_loc(&text_of(st, &s.module), e, name),
2256 _ => hit.loc,
2257 },
2258 NodeRef::Type(t) => {
2259 let name = match t {
2260 TypeAst::Named { name, .. } => name.as_str(),
2261 _ => "",
2262 };
2263 let offset = name.find('.').map(|i| i + 1).unwrap_or(0);
2264 type_name_loc(hit.loc, offset, name.rsplit('.').next().unwrap_or(name))
2265 }
2266 NodeRef::Decl(_) | NodeRef::Member(_) => site.range,
2267 };
2268 J::obj(vec![
2269 ("range", range_json(loc)),
2270 ("placeholder", J::s(site.name.clone())),
2271 ])
2272}
2273fn rename(st: &mut State, uri: &str, pos: Pos, new_name: &str) -> J {
2274 let refs = references(st, uri, pos, true);
2275 if refs.is_empty() {
2276 let Some(locs) = local_ranges(st, uri, pos) else {
2277 return J::Null;
2278 };
2279 if locs.is_empty() {
2280 return J::Null;
2281 }
2282 let edits: Vec<J> = locs
2283 .iter()
2284 .map(|l| J::obj(vec![("range", range_json(*l)), ("newText", J::s(new_name))]))
2285 .collect();
2286 return J::obj(vec![(
2287 "changes",
2288 J::Obj(vec![(uri.to_string(), J::Arr(edits))]),
2289 )]);
2290 }
2291 let mut changes: Vec<(String, Vec<J>)> = vec![];
2292 for (m, l) in refs {
2293 let u = uri_of(&m.path);
2294 let edit = J::obj(vec![("range", range_json(l)), ("newText", J::s(new_name))]);
2295 match changes.iter_mut().find(|(k, _)| *k == u) {
2296 Some(e) => e.1.push(edit),
2297 None => changes.push((u, vec![edit])),
2298 }
2299 }
2300 J::obj(vec![(
2301 "changes",
2302 J::Obj(changes.into_iter().map(|(k, v)| (k, J::Arr(v))).collect()),
2303 )])
2304}
2305
2306fn code_lenses(st: &State, uri: &str) -> J {
2308 let Some(text) = st.text(uri) else {
2309 return J::Arr(vec![]);
2310 };
2311 let parsed = parse_source(text);
2312 if !parsed.errors.is_empty() {
2313 return J::Arr(vec![]);
2314 }
2315 let mut out: Vec<J> = vec![];
2316 for d in &parsed.decls {
2317 let Some(l) = d.loc else { continue };
2318 let (title, command, name) = match &d.body {
2319 DeclBody::Output { name, .. } => ("evaluate", "decl.evaluate", name),
2320 DeclBody::Input { name, .. } => ("validate", "decl.validate", name),
2321 _ => continue,
2322 };
2323 out.push(J::obj(vec![
2324 (
2325 "range",
2326 range_json(Loc {
2327 sl: l.sl,
2328 sc: l.sc,
2329 el: l.sl,
2330 ec: l.sc,
2331 }),
2332 ),
2333 (
2334 "command",
2335 J::obj(vec![
2336 ("title", J::s(title)),
2337 ("command", J::s(command)),
2338 ("arguments", J::Arr(vec![J::s(uri), J::s(name.clone())])),
2339 ]),
2340 ),
2341 ]));
2342 }
2343 J::Arr(out)
2344}
2345fn execute_command(st: &mut State, command: &str, args: Option<&Value>) -> J {
2346 let items: Vec<&Value> = match args {
2347 Some(Value::JArr(a)) => a.iter().collect(),
2348 _ => vec![],
2349 };
2350 let Some(uri) = as_str(items.first().copied()) else {
2351 return J::Null;
2352 };
2353 let root = as_str(items.get(1).copied()).map(|s| s.to_string());
2354 let path = path_of(uri);
2355 let mut session = Session::with_overlay(Some(&path.to_string_lossy()), Some(&st.overlay));
2356 let dir = path.parent().map(|p| p.to_path_buf()).unwrap_or_default();
2357 for (name, file) in st.inputs.clone() {
2358 let abs = std::path::absolute(dir.join(&file)).unwrap_or_else(|_| dir.join(&file));
2359 let _ = session.apply(Op::Bind {
2360 name,
2361 src: BindSource::File {
2362 file: file.clone(),
2363 text: read_text(&abs),
2364 },
2365 });
2366 }
2367 let names: Vec<String> = root.iter().cloned().collect();
2368 let diags_of = |run: &Run, extra: &[Diag]| -> J {
2369 let mut all: Vec<&Diag> = run.load_diags.iter().collect();
2370 all.extend(run.checks.iter().map(|(_, d)| d));
2371 all.extend(extra.iter());
2372 J::Arr(all.iter().map(|d| J::s(fmt_diag(d, None))).collect())
2373 };
2374 match command {
2375 "decl.evaluate" => {
2376 let Ok((run, ds, _exported)) = session.evaluate(&names) else {
2377 return J::Null;
2378 };
2379 let diagnostics = diags_of(&run, &run.diags);
2380 if let Some(r) = root {
2381 let doc = ds.first().and_then(|(_, j)| j.clone());
2382 return J::obj(vec![
2383 ("root", J::s(r)),
2384 ("document", doc.map(J::s).unwrap_or(J::Null)),
2385 ("diagnostics", diagnostics),
2386 ]);
2387 }
2388 let all = if run.eng.is_some() && ds.iter().all(|(_, j)| j.is_some()) {
2389 Some(format!(
2390 "{{{}}}",
2391 ds.iter()
2392 .map(|(n, j)| format!("{}:{}", json_str(n), j.clone().unwrap()))
2393 .collect::<Vec<_>>()
2394 .join(",")
2395 ))
2396 } else {
2397 None
2398 };
2399 J::obj(vec![
2400 ("root", J::Null),
2401 ("document", all.map(J::s).unwrap_or(J::Null)),
2402 ("diagnostics", diagnostics),
2403 ])
2404 }
2405 "decl.validate" => {
2406 let Ok((run, verdicts, diags)) = session.validate(&names) else {
2407 return J::Null;
2408 };
2409 let vs: Vec<J> = verdicts
2410 .iter()
2411 .map(|(n, e, w)| {
2412 J::obj(vec![
2413 ("name", J::s(n.clone())),
2414 ("errors", J::Num(*e as i64)),
2415 ("warnings", J::Num(*w as i64)),
2416 ])
2417 })
2418 .collect();
2419 J::obj(vec![
2420 ("verdicts", J::Arr(vs)),
2421 ("diagnostics", diags_of(&run, &diags)),
2422 ])
2423 }
2424 "decl.trace" => match root {
2425 Some(r) => match session.trace(&r) {
2426 Ok(lines) => J::obj(vec![(
2427 "lines",
2428 J::Arr(lines.into_iter().map(J::s).collect()),
2429 )]),
2430 Err(_) => J::Null,
2431 },
2432 None => J::Null,
2433 },
2434 "decl.showSyntaxTree" => syntax_tree(st, uri),
2435 "decl.reloadWorkspace" => {
2436 st.analyses.clear();
2437 let uris: Vec<String> = st.docs.iter().map(|(u, _)| u.clone()).collect();
2438 for u in uris {
2439 analyze(st, &u);
2440 }
2441 J::Null
2442 }
2443 _ => J::Null,
2444 }
2445}
2446
2447fn after(l: Loc, p: Pos) -> bool {
2449 p.line > l.el || (p.line == l.el && p.character >= l.ec)
2450}
2451fn src_of(text: &str, l: Loc) -> String {
2452 let lines: Vec<&str> = text.split('\n').collect();
2453 let line = |i: usize| lines.get(i).copied().unwrap_or("");
2454 if l.sl == l.el {
2455 return slice16(line(l.sl), l.sc, l.ec).to_string();
2456 }
2457 let mut parts = vec![slice16(line(l.sl), l.sc, u16len(line(l.sl))).to_string()];
2458 parts.extend(lines[l.sl + 1..l.el].iter().map(|x| x.to_string()));
2459 parts.push(slice16(line(l.el), 0, l.ec).to_string());
2460 parts.join("\n")
2461}
2462fn sig_json(label: String, params: Vec<String>, active: usize) -> J {
2463 J::obj(vec![
2464 (
2465 "signatures",
2466 J::Arr(vec![J::obj(vec![
2467 ("label", J::s(label)),
2468 (
2469 "parameters",
2470 J::Arr(
2471 params
2472 .into_iter()
2473 .map(|p| J::obj(vec![("label", J::s(p))]))
2474 .collect(),
2475 ),
2476 ),
2477 ])]),
2478 ),
2479 ("activeSignature", J::Num(0)),
2480 ("activeParameter", J::Num(active as i64)),
2481 ])
2482}
2483fn decl_by_id(m: &Module, id: Option<usize>) -> Option<&Decl> {
2484 m.decls.iter().find(|d| decl_id(d) == id.unwrap_or(0))
2485}
2486fn signature_help(st: &mut State, uri: &str, pos: Pos) -> J {
2487 let a = match st.analysis_of(uri) {
2488 Some(a) => a,
2489 None => match st.last_good.get(uri) {
2490 Some(a) => a.clone(),
2491 None => return J::Null,
2492 },
2493 };
2494 let Some(m) = module_of(&a, &path_of(uri)) else {
2495 return J::Null;
2496 };
2497 let Some(mi) = a.run.modules.iter().position(|x| Rc::ptr_eq(x, &m)) else {
2498 return J::Null;
2499 };
2500 let Some(hit) = node_at(&a.run.modules[mi].decls, pos) else {
2501 return J::Null;
2502 };
2503 let mut chain: Vec<NodeRef> = hit.parents.clone();
2504 chain.push(hit.node.clone());
2505 let calls: Vec<&Rc<Expr>> = chain
2506 .iter()
2507 .rev()
2508 .filter_map(|n| match n {
2509 NodeRef::Expr(e) if matches!(&***e, Expr::Call { .. }) => Some(*e),
2510 _ => None,
2511 })
2512 .collect();
2513 for c in calls {
2514 let Expr::Call { fun, args } = &**c else {
2515 continue;
2516 };
2517 let Some(fl) = expr_loc(fun) else { continue };
2518 if !after(fl, pos) {
2519 continue;
2520 }
2521 let mut active = 0usize;
2522 for (i, arg) in args.iter().enumerate() {
2523 if let Some(al) = expr_loc(arg) {
2524 if after(al, pos) {
2525 active = i + 1;
2526 } else if contains(al, pos) {
2527 active = i;
2528 }
2529 }
2530 }
2531 if let Expr::Name(n) = &**fun {
2532 let target = resolve_in(&m.env, n);
2533 let Some(site) = site_of_target(st, &a, target.as_ref()) else {
2534 return J::Null;
2535 };
2536 let sm = site.module.clone();
2537 let Some(decl) = decl_by_id(&sm, site.decl) else {
2538 return J::Null;
2539 };
2540 let DeclBody::Func {
2541 name, params, ret, ..
2542 } = &decl.body
2543 else {
2544 return J::Null;
2545 };
2546 let text = text_of(st, &sm);
2547 let ps: Vec<String> = params
2548 .iter()
2549 .map(|p| {
2550 format!(
2551 "{}: {}",
2552 p.name,
2553 p.ty.as_ref()
2554 .and_then(|t| t.loc())
2555 .map(|l| src_of(&text, l))
2556 .unwrap_or_else(|| "…".into())
2557 )
2558 })
2559 .collect();
2560 let r = ret
2561 .as_ref()
2562 .and_then(|t| t.loc())
2563 .map(|l| format!(": {}", src_of(&text, l)))
2564 .unwrap_or_default();
2565 let n = ps.len();
2566 return sig_json(
2567 format!("{name}({}){r}", ps.join(", ")),
2568 ps,
2569 active.min(n.saturating_sub(1)),
2570 );
2571 }
2572 if let Some(sp) = std_path(fun) {
2573 if let Some(e) = STD.iter().find(|e| e.0 == sp) {
2574 let ps: Vec<String> = (1..=e.1).map(|i| format!("a{i}")).collect();
2575 let n = ps.len();
2576 return sig_json(
2577 format!("std.{sp}({})", ps.join(", ")),
2578 ps,
2579 active.min(n.saturating_sub(1)),
2580 );
2581 }
2582 }
2583 return J::Null;
2584 }
2585 J::Null
2586}
2587
2588fn workspace_symbols(st: &State, query: &str) -> J {
2590 let q = query.to_lowercase();
2591 let mut out: Vec<(String, String, J)> = vec![];
2592 let mut seen: Vec<PathBuf> = vec![];
2593 for a in st.last_good.values() {
2594 for m in &a.run.modules {
2595 if seen.contains(&m.path) {
2596 continue;
2597 }
2598 seen.push(m.path.clone());
2599 let text = text_of(st, m);
2600 for d in &m.decls {
2601 let (Some(_), Some(name), Some(kind)) = (d.loc, d.name(), symbol_kind(d)) else {
2602 continue;
2603 };
2604 if !name.to_lowercase().contains(&q) {
2605 continue;
2606 }
2607 out.push((
2608 name.to_string(),
2609 uri_of(&m.path),
2610 J::obj(vec![
2611 ("name", J::s(name)),
2612 ("kind", J::Num(kind)),
2613 ("location", location(m, name_range(&text, d, name))),
2614 ]),
2615 ));
2616 }
2617 }
2618 }
2619 out.sort_by(|x, y| x.0.cmp(&y.0).then(x.1.cmp(&y.1)));
2620 J::Arr(out.into_iter().map(|x| x.2).collect())
2621}
2622fn pos_json(p: Pos) -> J {
2623 J::obj(vec![
2624 ("line", J::Num(p.line as i64)),
2625 ("character", J::Num(p.character as i64)),
2626 ])
2627}
2628fn selection_ranges(st: &State, uri: &str, positions: &[Pos]) -> J {
2629 let Some(text) = st.text(uri) else {
2630 return J::Arr(vec![]);
2631 };
2632 let parsed = parse_source(text);
2633 let point = |p: Pos| {
2634 J::obj(vec![(
2635 "range",
2636 J::obj(vec![("start", pos_json(p)), ("end", pos_json(p))]),
2637 )])
2638 };
2639 if !parsed.errors.is_empty() {
2640 return J::Arr(positions.iter().map(|p| point(*p)).collect());
2641 }
2642 J::Arr(
2643 positions
2644 .iter()
2645 .map(|p| {
2646 let Some(hit) = node_at(&parsed.decls, *p) else {
2647 return point(*p);
2648 };
2649 let mut chain: Vec<Loc> = vec![hit.loc];
2651 chain.extend(hit.parents.iter().rev().filter_map(|n| n.loc()));
2652 let mut sel: Option<J> = None;
2653 for l in chain.iter().rev() {
2654 sel = Some(match sel {
2655 Some(s) => J::obj(vec![("range", range_json(*l)), ("parent", s)]),
2656 None => J::obj(vec![("range", range_json(*l))]),
2657 });
2658 }
2659 sel.unwrap_or_else(|| point(*p))
2660 })
2661 .collect(),
2662 )
2663}
2664
2665const TOKEN_TYPES: [&str; 6] = [
2667 "type",
2668 "property",
2669 "function",
2670 "variable",
2671 "namespace",
2672 "parameter",
2673];
2674const TOKEN_MODS: [&str; 8] = [
2675 "declaration",
2676 "required",
2677 "optional",
2678 "defaulted",
2679 "derived",
2680 "hidden",
2681 "unresolved",
2682 "readonly",
2683];
2684const T_TYPE: i64 = 0;
2685const T_PROPERTY: i64 = 1;
2686const T_FUNCTION: i64 = 2;
2687const T_VARIABLE: i64 = 3;
2688const T_NAMESPACE: i64 = 4;
2689const T_PARAMETER: i64 = 5;
2690const M_DECLARATION: i64 = 1;
2691const M_REQUIRED: i64 = 2;
2692const M_OPTIONAL: i64 = 4;
2693const M_DEFAULTED: i64 = 8;
2694const M_DERIVED: i64 = 16;
2695const M_HIDDEN: i64 = 32;
2696const M_UNRESOLVED: i64 = 64;
2697const M_READONLY: i64 = 128;
2698fn member_mods(kind: MKind, hidden: bool) -> i64 {
2699 (match kind {
2700 MKind::Der => M_DERIVED,
2701 MKind::Dflt => M_DEFAULTED,
2702 MKind::Opt => M_OPTIONAL,
2703 MKind::Req => M_REQUIRED,
2704 }) | if hidden { M_HIDDEN } else { 0 }
2705}
2706fn member_kind_of(rt: Option<&RT>, name: &str) -> Option<(MKind, bool)> {
2707 let rt = rt?;
2708 let r: RT = match &rt.k {
2709 RTk::Pred { base, .. } => base.clone(),
2710 RTk::Ref(t) => t.clone(),
2711 _ => rt.clone(),
2712 };
2713 rec_members(&r)
2714 .into_iter()
2715 .find(|m| m.name == name)
2716 .map(|m| (m.kind, m.hidden))
2717}
2718fn param_loc(text: &str, decl: &Decl, name: &str) -> Option<Loc> {
2719 let l = decl.loc?;
2720 let line = text.split('\n').nth(l.sl).unwrap_or("");
2721 let open = find16(line, "(", l.sc)?;
2722 let re = Regex::new(&format!(r"\b{}\b", regex::escape(name))).unwrap();
2723 let m = re.find_at(line, byte_col(line, open))?;
2724 let a = u16_col(line, m.start());
2725 Some(Loc {
2726 sl: l.sl,
2727 sc: a,
2728 el: l.sl,
2729 ec: a + u16len(name),
2730 })
2731}
2732struct TokenWalk<'a> {
2733 st: &'a State,
2734 a: &'a Analysis,
2735 m: &'a Rc<Module>,
2736 text: String,
2737 t: Rc<Tables>,
2738 toks: Vec<(Loc, i64, i64)>,
2739}
2740impl<'a> TokenWalk<'a> {
2741 fn push(&mut self, l: Loc, ty: i64, mods: i64) {
2742 if l.sl == l.el && l.ec > l.sc {
2743 self.toks.push((l, ty, mods));
2744 }
2745 }
2746 fn decl(&mut self, d: &Decl) {
2747 let mut in_func: Vec<String> = vec![];
2748 if let (Some(_), Some(name)) = (d.loc, d.name()) {
2749 let r = name_range(&self.text, d, name);
2750 let ty = match &d.body {
2751 DeclBody::Type { .. } | DeclBody::Dimension { .. } | DeclBody::Unit { .. } => {
2752 T_TYPE
2753 }
2754 DeclBody::Func { .. } | DeclBody::Diagnostic { .. } => T_FUNCTION,
2755 _ => T_VARIABLE,
2756 };
2757 self.push(
2758 r,
2759 ty,
2760 M_DECLARATION
2761 | if matches!(d.body, DeclBody::Const { .. }) {
2762 M_READONLY
2763 } else {
2764 0
2765 },
2766 );
2767 if let DeclBody::Func { params, .. } = &d.body {
2768 in_func = params.iter().map(|p| p.name.clone()).collect();
2769 for p in params {
2770 if let Some(pl) = param_loc(&self.text, d, &p.name) {
2771 self.push(pl, T_PARAMETER, M_DECLARATION);
2772 }
2773 }
2774 }
2775 }
2776 match &d.body {
2777 DeclBody::Type {
2778 params, ty, tail, ..
2779 } => {
2780 for p in params {
2781 if let Some(t) = &p.ty {
2782 self.ty(t, &in_func);
2783 }
2784 }
2785 self.ty(ty, &in_func);
2786 if let Some(t) = tail {
2787 self.tail(t, &in_func);
2788 }
2789 }
2790 DeclBody::Const { ty, expr, .. } => {
2791 if let Some(t) = ty {
2792 self.ty(t, &in_func);
2793 }
2794 self.expr(expr, &in_func);
2795 }
2796 DeclBody::Func {
2797 params, ret, body, ..
2798 } => {
2799 for p in params {
2800 if let Some(t) = &p.ty {
2801 self.ty(t, &in_func);
2802 }
2803 }
2804 if let Some(t) = ret {
2805 self.ty(t, &in_func);
2806 }
2807 self.expr(body, &in_func);
2808 }
2809 DeclBody::Output { ty, expr, .. } => {
2810 self.ty(ty, &in_func);
2811 self.expr(expr, &in_func);
2812 }
2813 DeclBody::Input { ty, fallback, .. } => {
2814 self.ty(ty, &in_func);
2815 if let Some(f) = fallback {
2816 self.expr(f, &in_func);
2817 }
2818 }
2819 DeclBody::Diagnostic {
2820 params, template, ..
2821 } => {
2822 for p in params {
2823 if let Some(t) = &p.ty {
2824 self.ty(t, &in_func);
2825 }
2826 }
2827 self.template(template, &in_func);
2828 }
2829 DeclBody::Unit {
2830 factor: Some(f), ..
2831 } => {
2832 self.expr(f, &in_func);
2833 }
2834 _ => {}
2835 }
2836 }
2837 fn tail(&mut self, t: &Tail, in_func: &[String]) {
2838 match t {
2839 Tail::Inline { template, .. } => self.template(template, in_func),
2840 Tail::Ref { args, .. } => {
2841 for a in args {
2842 self.expr(a, in_func);
2843 }
2844 }
2845 }
2846 }
2847 fn template(&mut self, parts: &[TPart], in_func: &[String]) {
2848 for p in parts {
2849 if let TPart::Expr(x) = p {
2850 self.expr(x, in_func);
2851 }
2852 }
2853 }
2854 fn ty(&mut self, t: &TypeAst, in_func: &[String]) {
2855 if let TypeAst::Named {
2856 name, loc: Some(l), ..
2857 } = t
2858 {
2859 let mut parts = name.splitn(2, '.');
2860 let head = parts.next().unwrap_or("");
2861 match parts.next() {
2862 Some(tail) => {
2863 self.push(type_name_loc(*l, 0, head), T_NAMESPACE, 0);
2864 self.push(type_name_loc(*l, u16len(head) + 1, tail), T_TYPE, 0);
2865 }
2866 None => {
2867 if !["map", "ref", "quantity"].contains(&head) {
2868 let mods = if resolve_in(&self.m.env, head).is_some() {
2869 0
2870 } else {
2871 M_UNRESOLVED
2872 };
2873 self.push(type_name_loc(*l, 0, head), T_TYPE, mods);
2874 }
2875 }
2876 }
2877 }
2878 match t {
2879 TypeAst::Record { members, .. } => {
2880 for m in members {
2881 self.member(m, in_func);
2882 }
2883 }
2884 TypeAst::Map { key, val, .. } => {
2885 self.ty(key, in_func);
2886 self.ty(val, in_func);
2887 }
2888 TypeAst::Array { elem, .. } => self.ty(elem, in_func),
2889 TypeAst::Union { arms, .. } | TypeAst::Isect { arms, .. } => {
2890 for a in arms {
2891 self.ty(a, in_func);
2892 }
2893 }
2894 TypeAst::Func { params, ret, .. } => {
2895 for a in params {
2896 self.ty(a, in_func);
2897 }
2898 self.ty(ret, in_func);
2899 }
2900 TypeAst::Named {
2901 args, preds, ext, ..
2902 } => {
2903 for a in args {
2904 self.ty(a, in_func);
2905 }
2906 for x in preds.iter().flatten() {
2907 self.expr(x, in_func);
2908 }
2909 if let Some(x) = ext {
2910 self.ty(x, in_func);
2911 }
2912 }
2913 _ => {}
2914 }
2915 }
2916 fn member(&mut self, m: &MemberAst, in_func: &[String]) {
2917 match m {
2918 MemberAst::Value {
2919 name,
2920 opt,
2921 dflt,
2922 loc: Some(_),
2923 ..
2924 } => {
2925 let kind = if dflt.is_some() {
2926 MKind::Dflt
2927 } else if *opt {
2928 MKind::Opt
2929 } else {
2930 MKind::Req
2931 };
2932 let r = member_range(&self.text, m, name);
2933 self.push(r, T_PROPERTY, M_DECLARATION | member_mods(kind, false));
2934 }
2935 MemberAst::Derived {
2936 name,
2937 hidden,
2938 loc: Some(_),
2939 ..
2940 } => {
2941 let r = member_range(&self.text, m, name);
2942 self.push(
2943 r,
2944 T_PROPERTY,
2945 M_DECLARATION | member_mods(MKind::Der, *hidden),
2946 );
2947 }
2948 _ => {}
2949 }
2950 match m {
2951 MemberAst::Value { ty, dflt, .. } => {
2952 self.ty(ty, in_func);
2953 if let Some(d) = dflt {
2954 self.expr(d, in_func);
2955 }
2956 }
2957 MemberAst::Derived { ty, expr, .. } => {
2958 if let Some(t) = ty {
2959 self.ty(t, in_func);
2960 }
2961 self.expr(expr, in_func);
2962 }
2963 MemberAst::Context { ty, .. } => self.ty(ty, in_func),
2964 MemberAst::Assert { cond, tail, .. } => {
2965 self.expr(cond, in_func);
2966 if let Some(t) = tail {
2967 self.tail(t, in_func);
2968 }
2969 }
2970 MemberAst::When { cond, body, .. } => {
2971 self.expr(cond, in_func);
2972 for b in body {
2973 self.member(b, in_func);
2974 }
2975 }
2976 }
2977 }
2978 fn expr(&mut self, e: &Rc<Expr>, in_func: &[String]) {
2979 let mut scope: Vec<String> = in_func.to_vec();
2980 if let Some(l) = expr_loc(e) {
2981 match &**e {
2982 Expr::Name(n) => {
2983 let target = match self.t.res.get(&key_of(e)) {
2984 Some(r) => r.clone(),
2985 None => resolve_in(&self.m.env, n),
2986 };
2987 if n == "std" {
2988 self.push(l, T_NAMESPACE, 0);
2989 } else if in_func.contains(n)
2990 || target.as_ref().map(|t| t.kind == "var").unwrap_or(false)
2991 {
2992 self.push(l, T_PARAMETER, 0);
2993 } else {
2994 match target {
2995 None => self.push(l, T_VARIABLE, M_UNRESOLVED),
2996 Some(t) => {
2997 let ty = match t.kind {
2998 "func" => T_FUNCTION,
2999 "namespace" => T_NAMESPACE,
3000 "type" => T_TYPE,
3001 _ => T_VARIABLE,
3002 };
3003 self.push(l, ty, if t.kind == "const" { M_READONLY } else { 0 });
3004 }
3005 }
3006 }
3007 }
3008 Expr::Member { x, name, .. } => {
3009 let ml = member_token_loc(&self.text, e, name);
3010 if let Some(sp) = std_path(e) {
3011 self.push(
3012 ml,
3013 if STD.iter().any(|s| s.0 == sp) {
3014 T_FUNCTION
3015 } else {
3016 T_NAMESPACE
3017 },
3018 0,
3019 );
3020 } else if let Expr::Name(xn) = &**x {
3021 if self.m.env.namespaces.borrow().contains_key(xn) {
3022 let nss = self.m.env.namespaces.borrow();
3023 let ex = nss
3024 .get(xn)
3025 .and_then(|(_, exports)| exports.borrow().get(name).cloned());
3026 let tg = ex.and_then(|ex| resolve_in(&ex.env, &ex.name));
3027 let ty = match tg.as_ref().map(|t| t.kind) {
3028 Some("func") => T_FUNCTION,
3029 Some("type") => T_TYPE,
3030 _ => T_VARIABLE,
3031 };
3032 self.push(ml, ty, if tg.is_some() { 0 } else { M_UNRESOLVED });
3033 } else {
3034 let mk = member_kind_of(
3035 self.t.types.get(&key_of(x)).and_then(|t| t.rt.as_ref()),
3036 name,
3037 );
3038 self.push(
3039 ml,
3040 T_PROPERTY,
3041 mk.map(|(k, h)| member_mods(k, h)).unwrap_or(0),
3042 );
3043 }
3044 } else {
3045 let mk = member_kind_of(
3046 self.t.types.get(&key_of(x)).and_then(|t| t.rt.as_ref()),
3047 name,
3048 );
3049 self.push(
3050 ml,
3051 T_PROPERTY,
3052 mk.map(|(k, h)| member_mods(k, h)).unwrap_or(0),
3053 );
3054 }
3055 }
3056 Expr::Lambda { params, .. } => scope.extend(params.iter().cloned()),
3057 Expr::Comp { clauses, .. } | Expr::MapComp { clauses, .. } => {
3058 scope.extend(clauses.iter().map(|c| c.v.clone()))
3059 }
3060 _ => {}
3061 }
3062 }
3063 let s = &scope;
3064 match &**e {
3065 Expr::Template(parts) => self.template(parts, s),
3066 Expr::Obj(entries) => {
3067 for (_, v) in entries {
3068 self.expr(v, s);
3069 }
3070 }
3071 Expr::Arr(items) => {
3072 for (_, v) in items {
3073 self.expr(v, s);
3074 }
3075 }
3076 Expr::Comp { head, clauses } => {
3077 self.expr(head, s);
3078 for c in clauses {
3079 self.expr(&c.iter, s);
3080 for f in &c.filters {
3081 self.expr(f, s);
3082 }
3083 }
3084 }
3085 Expr::MapComp { key, val, clauses } => {
3086 self.expr(key, s);
3087 self.expr(val, s);
3088 for c in clauses {
3089 self.expr(&c.iter, s);
3090 for f in &c.filters {
3091 self.expr(f, s);
3092 }
3093 }
3094 }
3095 Expr::Bin { l, r, .. } => {
3096 self.expr(l, s);
3097 self.expr(r, s);
3098 }
3099 Expr::Un { x, .. } | Expr::Paren(x) => self.expr(x, s),
3100 Expr::If { c, t, f } => {
3101 self.expr(c, s);
3102 self.expr(t, s);
3103 self.expr(f, s);
3104 }
3105 Expr::Lambda { body, .. } => self.expr(body, s),
3106 Expr::Call { fun, args } => {
3107 self.expr(fun, s);
3108 for a in args {
3109 self.expr(a, s);
3110 }
3111 }
3112 Expr::Member { x, .. } => self.expr(x, s),
3113 Expr::Index { x, i } => {
3114 self.expr(x, s);
3115 self.expr(i, s);
3116 }
3117 Expr::With { base, patch } => {
3118 self.expr(base, s);
3119 self.expr(patch, s);
3120 }
3121 Expr::Match { subject, arms } => {
3122 self.expr(subject, s);
3123 for a in arms {
3124 if let Some(t) = &a.ty {
3125 self.ty(t, s);
3126 }
3127 self.expr(&a.body, s);
3128 }
3129 }
3130 _ => {}
3131 }
3132 }
3133}
3134fn semantic_tokens(st: &mut State, uri: &str) -> J {
3135 let empty = J::obj(vec![("data", J::Arr(vec![]))]);
3136 let Some(a) = st.analysis_of(uri) else {
3137 return empty;
3138 };
3139 let Some(m) = module_of(&a, &path_of(uri)) else {
3140 return empty;
3141 };
3142 let t = tables_of(&a, &m);
3143 let text = text_of(st, &m);
3144 let mut w = TokenWalk {
3145 st,
3146 a: &a,
3147 m: &m,
3148 text,
3149 t,
3150 toks: vec![],
3151 };
3152 let _ = w.st;
3153 let _ = w.a;
3154 for d in &m.decls {
3155 w.decl(d);
3156 }
3157 let mut toks = w.toks;
3158 toks.sort_by(|p, q| p.0.sl.cmp(&q.0.sl).then(p.0.sc.cmp(&q.0.sc)));
3159 let mut data: Vec<J> = vec![];
3160 let (mut pl, mut pc) = (0usize, 0usize);
3161 for (l, ty, mods) in toks {
3162 let dl = l.sl - pl;
3163 let dc: i64 = if dl == 0 {
3164 l.sc as i64 - pc as i64
3165 } else {
3166 l.sc as i64
3167 };
3168 if dl == 0 && dc < 0 {
3169 continue; }
3171 data.extend([
3172 J::Num(dl as i64),
3173 J::Num(dc),
3174 J::Num((l.ec - l.sc) as i64),
3175 J::Num(ty),
3176 J::Num(mods),
3177 ]);
3178 pl = l.sl;
3179 pc = l.sc;
3180 }
3181 J::obj(vec![("data", J::Arr(data))])
3182}
3183
3184struct HintWalk<'a> {
3186 st: &'a State,
3187 a: &'a Analysis,
3188 m: &'a Rc<Module>,
3189 text: String,
3190 t: Rc<Tables>,
3191 range: (Pos, Pos),
3192 hints: (bool, bool, bool, bool, bool),
3194 out: Vec<(Pos, J)>,
3195}
3196impl<'a> HintWalk<'a> {
3197 fn in_range(&self, p: Pos) -> bool {
3198 p.line >= self.range.0.line && p.line <= self.range.1.line
3199 }
3200 fn hint(&mut self, p: Pos, label: String, kind: Option<i64>, pad_left: bool, pad_right: bool) {
3201 let mut item = vec![("position", pos_json(p)), ("label", J::s(label))];
3202 if let Some(k) = kind {
3203 item.push(("kind", J::Num(k)));
3204 }
3205 if pad_right {
3206 item.push(("paddingRight", J::Bool(true)));
3207 }
3208 if pad_left {
3209 item.push(("paddingLeft", J::Bool(true)));
3210 }
3211 self.out.push((p, J::obj(item)));
3212 }
3213 fn decl(&mut self, d: &Decl) {
3214 if let DeclBody::Const {
3215 name,
3216 ty: None,
3217 expr,
3218 } = &d.body
3219 {
3220 if self.hints.0 && d.loc.is_some() {
3221 if let Some(rt) = self.t.types.get(&key_of(expr)).and_then(|t| t.rt.clone()) {
3222 let r = name_range(&self.text, d, name);
3223 let p = Pos {
3224 line: r.el,
3225 character: r.ec,
3226 };
3227 if self.in_range(p) {
3228 self.hint(
3229 p,
3230 format!(": {}", type_text(Some(&rt))),
3231 Some(1),
3232 false,
3233 false,
3234 );
3235 }
3236 }
3237 }
3238 }
3239 if let DeclBody::Output { name, expr, .. } = &d.body {
3240 if self.hints.3 && d.loc.is_some() {
3241 if let (Some(eng), Some(entry)) = (self.a.run.eng.clone(), self.a.run.entry.clone())
3242 {
3243 let root = entry
3244 .env
3245 .roots
3246 .borrow()
3247 .borrow()
3248 .iter()
3249 .find(|(n, _)| n == name)
3250 .map(|(_, v)| v.clone());
3251 if let Some(v) = root {
3252 self.values(&v, expr, name, &eng);
3253 }
3254 }
3255 }
3256 }
3257 match &d.body {
3258 DeclBody::Type {
3259 params, ty, tail, ..
3260 } => {
3261 for p in params {
3262 if let Some(t) = &p.ty {
3263 self.ty(t);
3264 }
3265 }
3266 self.ty(ty);
3267 if let Some(t) = tail {
3268 self.tail(t);
3269 }
3270 }
3271 DeclBody::Const { ty, expr, .. } => {
3272 if let Some(t) = ty {
3273 self.ty(t);
3274 }
3275 self.expr(expr);
3276 }
3277 DeclBody::Func {
3278 params, ret, body, ..
3279 } => {
3280 for p in params {
3281 if let Some(t) = &p.ty {
3282 self.ty(t);
3283 }
3284 }
3285 if let Some(t) = ret {
3286 self.ty(t);
3287 }
3288 self.expr(body);
3289 }
3290 DeclBody::Output { ty, expr, .. } => {
3291 self.ty(ty);
3292 self.expr(expr);
3293 }
3294 DeclBody::Input { ty, fallback, .. } => {
3295 self.ty(ty);
3296 if let Some(f) = fallback {
3297 self.expr(f);
3298 }
3299 }
3300 DeclBody::Diagnostic {
3301 params, template, ..
3302 } => {
3303 for p in params {
3304 if let Some(t) = &p.ty {
3305 self.ty(t);
3306 }
3307 }
3308 self.template(template);
3309 }
3310 DeclBody::Unit {
3311 factor: Some(f), ..
3312 } => {
3313 self.expr(f);
3314 }
3315 _ => {}
3316 }
3317 }
3318 fn values(&mut self, v: &Value, e: &Rc<Expr>, root: &str, eng: &crate::engine::Engine) {
3320 match (v, &**e) {
3321 (Value::Rec(inst), Expr::Obj(entries)) => {
3322 let inst = inst.borrow();
3323 let mut parts: Vec<String> = vec![];
3324 for mem in rec_members(&inst.rt) {
3325 let Some((_, s)) = inst.slots.iter().find(|(n, _)| *n == mem.name) else {
3326 continue;
3327 };
3328 if mem.kind != MKind::Der || s.hidden || s.state != SlotState::Ok {
3329 continue;
3330 }
3331 let txt = eng.serialize(&s.value, root, false);
3332 let shown = if u16len(&txt) > 40 {
3333 format!("{}…", slice16(&txt, 0, 37))
3334 } else {
3335 txt
3336 };
3337 parts.push(format!("{} = {}", mem.name, shown));
3338 }
3339 if let Some(l) = expr_loc(e) {
3340 let p = Pos {
3341 line: l.el,
3342 character: l.ec,
3343 };
3344 if !parts.is_empty() && self.in_range(p) {
3345 self.hint(p, format!("// {}", parts.join(", ")), None, true, false);
3346 }
3347 }
3348 let children: Vec<(Value, Rc<Expr>)> = entries
3349 .iter()
3350 .filter_map(|(k, val)| {
3351 inst.slots
3352 .iter()
3353 .find(|(n, _)| n == k)
3354 .filter(|(_, s)| s.state == SlotState::Ok)
3355 .map(|(_, s)| (s.value.clone(), val.clone()))
3356 })
3357 .collect();
3358 drop(inst);
3359 for (cv, ce) in children {
3360 self.values(&cv, &ce, root, eng);
3361 }
3362 }
3363 (Value::Arr(arr), Expr::Arr(items)) => {
3364 let vals: Vec<Value> = arr.borrow().items.clone();
3365 for (i, it) in vals.iter().enumerate() {
3366 if let Some((_, ce)) = items.get(i) {
3367 self.values(it, ce, root, eng);
3368 }
3369 }
3370 }
3371 (Value::Map(map), Expr::Obj(entries)) => {
3372 for (k, val) in entries {
3373 let cv = map.borrow().get(k).cloned();
3374 if let Some(cv) = cv {
3375 self.values(&cv, val, root, eng);
3376 }
3377 }
3378 }
3379 _ => {}
3380 }
3381 }
3382 fn tail(&mut self, t: &Tail) {
3383 match t {
3384 Tail::Inline { template, .. } => self.template(template),
3385 Tail::Ref { args, .. } => {
3386 for a in args {
3387 self.expr(a);
3388 }
3389 }
3390 }
3391 }
3392 fn template(&mut self, parts: &[TPart]) {
3393 for p in parts {
3394 if let TPart::Expr(x) = p {
3395 self.expr(x);
3396 }
3397 }
3398 }
3399 fn ty(&mut self, t: &TypeAst) {
3400 match t {
3401 TypeAst::Record { members, .. } => {
3402 for m in members {
3403 self.member(m);
3404 }
3405 }
3406 TypeAst::Map { key, val, .. } => {
3407 self.ty(key);
3408 self.ty(val);
3409 }
3410 TypeAst::Array { elem, .. } => self.ty(elem),
3411 TypeAst::Union { arms, .. } | TypeAst::Isect { arms, .. } => {
3412 for a in arms {
3413 self.ty(a);
3414 }
3415 }
3416 TypeAst::Func { params, ret, .. } => {
3417 for a in params {
3418 self.ty(a);
3419 }
3420 self.ty(ret);
3421 }
3422 TypeAst::Named {
3423 args, preds, ext, ..
3424 } => {
3425 for a in args {
3426 self.ty(a);
3427 }
3428 for x in preds.iter().flatten() {
3429 self.expr(x);
3430 }
3431 if let Some(x) = ext {
3432 self.ty(x);
3433 }
3434 }
3435 _ => {}
3436 }
3437 }
3438 fn member(&mut self, m: &MemberAst) {
3439 if let MemberAst::Derived {
3440 name,
3441 ty: None,
3442 expr,
3443 hidden,
3444 loc: Some(_),
3445 } = m
3446 {
3447 if self.hints.0 {
3448 if let Some(rt) = self.t.types.get(&key_of(expr)).and_then(|t| t.rt.clone()) {
3449 let r = member_range(&self.text, m, name);
3450 let p = Pos {
3451 line: r.el,
3452 character: r.ec + if *hidden { 1 } else { 0 },
3453 };
3454 if self.in_range(p) {
3455 self.hint(
3456 p,
3457 format!(": {}", type_text(Some(&rt))),
3458 Some(1),
3459 false,
3460 false,
3461 );
3462 }
3463 }
3464 }
3465 }
3466 match m {
3467 MemberAst::Value { ty, dflt, .. } => {
3468 self.ty(ty);
3469 if let Some(d) = dflt {
3470 self.expr(d);
3471 }
3472 }
3473 MemberAst::Derived { ty, expr, .. } => {
3474 if let Some(t) = ty {
3475 self.ty(t);
3476 }
3477 self.expr(expr);
3478 }
3479 MemberAst::Context { ty, .. } => self.ty(ty),
3480 MemberAst::Assert { cond, tail, .. } => {
3481 self.expr(cond);
3482 if let Some(t) = tail {
3483 self.tail(t);
3484 }
3485 }
3486 MemberAst::When { cond, body, .. } => {
3487 self.expr(cond);
3488 for b in body {
3489 self.member(b);
3490 }
3491 }
3492 }
3493 }
3494 fn expr(&mut self, e: &Rc<Expr>) {
3495 match &**e {
3496 Expr::Call { fun, args } if self.hints.1 => {
3497 if let Expr::Name(n) = &**fun {
3498 let target = match self.t.res.get(&key_of(fun)) {
3499 Some(r) => r.clone(),
3500 None => resolve_in(&self.m.env, n),
3501 };
3502 if let Some(site) = site_of_target(self.st, self.a, target.as_ref()) {
3503 let sm = site.module.clone();
3504 if let Some(DeclBody::Func { params, .. }) =
3505 decl_by_id(&sm, site.decl).map(|d| &d.body)
3506 {
3507 for (i, arg) in args.iter().enumerate() {
3508 if let (Some(p), Some(al)) = (params.get(i), expr_loc(arg)) {
3509 let pos = Pos {
3510 line: al.sl,
3511 character: al.sc,
3512 };
3513 if self.in_range(pos) {
3514 self.hint(
3515 pos,
3516 format!("{}:", p.name),
3517 Some(2),
3518 false,
3519 true,
3520 );
3521 }
3522 }
3523 }
3524 }
3525 }
3526 }
3527 }
3528 Expr::Ctx(name)
3529 if self.hints.4 && (name == "$parent" || name == "$root" || name == "$key") =>
3530 {
3531 if let Some(l) = expr_loc(e) {
3533 let decl = self.m.decls.iter().find(|d| {
3534 matches!(d.body, DeclBody::Type { .. })
3535 && d.loc
3536 .map(|dl| dl.sl <= l.sl && l.el <= dl.el)
3537 .unwrap_or(false)
3538 });
3539 let tl =
3540 decl.and_then(record_body_of)
3541 .map(record_members)
3542 .and_then(|members| {
3543 members.iter().find_map(|mm| match mm {
3544 MemberAst::Context { variable, ty, .. } if variable == name => {
3545 ty.loc()
3546 }
3547 _ => None,
3548 })
3549 });
3550 let p = Pos {
3551 line: l.el,
3552 character: l.ec,
3553 };
3554 if let Some(tl) = tl {
3555 if self.in_range(p) {
3556 self.hint(
3557 p,
3558 format!(": {}", src_of(&self.text, tl)),
3559 Some(1),
3560 false,
3561 false,
3562 );
3563 }
3564 }
3565 }
3566 }
3567 Expr::UnitLit { num, unit } if self.hints.2 => {
3568 if let (Ok((key, to_base)), Some(l)) = (self.m.env.unit_info(unit), expr_loc(e)) {
3569 let base = self
3570 .m
3571 .env
3572 .base_unit_of
3573 .borrow()
3574 .get(&key)
3575 .cloned()
3576 .unwrap_or(key);
3577 let p = Pos {
3578 line: l.el,
3579 character: l.ec,
3580 };
3581 if base != *unit && self.in_range(p) {
3582 self.hint(
3583 p,
3584 format!("= {} {}", crate::semantics::js_num_str(num * to_base), base),
3585 None,
3586 true,
3587 false,
3588 );
3589 }
3590 }
3591 }
3592 _ => {}
3593 }
3594 match &**e {
3595 Expr::Template(parts) => self.template(parts),
3596 Expr::Obj(entries) => {
3597 for (_, v) in entries {
3598 self.expr(v);
3599 }
3600 }
3601 Expr::Arr(items) => {
3602 for (_, v) in items {
3603 self.expr(v);
3604 }
3605 }
3606 Expr::Comp { head, clauses } => {
3607 self.expr(head);
3608 for c in clauses {
3609 self.expr(&c.iter);
3610 for f in &c.filters {
3611 self.expr(f);
3612 }
3613 }
3614 }
3615 Expr::MapComp { key, val, clauses } => {
3616 self.expr(key);
3617 self.expr(val);
3618 for c in clauses {
3619 self.expr(&c.iter);
3620 for f in &c.filters {
3621 self.expr(f);
3622 }
3623 }
3624 }
3625 Expr::Bin { l, r, .. } => {
3626 self.expr(l);
3627 self.expr(r);
3628 }
3629 Expr::Un { x, .. } | Expr::Paren(x) => self.expr(x),
3630 Expr::If { c, t, f } => {
3631 self.expr(c);
3632 self.expr(t);
3633 self.expr(f);
3634 }
3635 Expr::Lambda { body, .. } => self.expr(body),
3636 Expr::Call { fun, args } => {
3637 self.expr(fun);
3638 for a in args {
3639 self.expr(a);
3640 }
3641 }
3642 Expr::Member { x, .. } => self.expr(x),
3643 Expr::Index { x, i } => {
3644 self.expr(x);
3645 self.expr(i);
3646 }
3647 Expr::With { base, patch } => {
3648 self.expr(base);
3649 self.expr(patch);
3650 }
3651 Expr::Match { subject, arms } => {
3652 self.expr(subject);
3653 for a in arms {
3654 if let Some(t) = &a.ty {
3655 self.ty(t);
3656 }
3657 self.expr(&a.body);
3658 }
3659 }
3660 _ => {}
3661 }
3662 }
3663}
3664fn inlay_hints(st: &mut State, uri: &str, range: (Pos, Pos)) -> J {
3665 let Some(a) = st.analysis_of(uri) else {
3666 return J::Arr(vec![]);
3667 };
3668 let Some(m) = module_of(&a, &path_of(uri)) else {
3669 return J::Arr(vec![]);
3670 };
3671 let t = tables_of(&a, &m);
3672 let text = text_of(st, &m);
3673 let hints = (
3674 st.hint_types,
3675 st.hint_parameter_names,
3676 st.hint_units,
3677 st.hint_values,
3678 st.hint_context_variables,
3679 );
3680 let mut w = HintWalk {
3681 st,
3682 a: &a,
3683 m: &m,
3684 text,
3685 t,
3686 range,
3687 hints,
3688 out: vec![],
3689 };
3690 for d in &m.decls {
3691 w.decl(d);
3692 }
3693 let mut out = w.out;
3694 out.sort_by(|p, q| {
3695 p.0.line
3696 .cmp(&q.0.line)
3697 .then(p.0.character.cmp(&q.0.character))
3698 });
3699 J::Arr(out.into_iter().map(|x| x.1).collect())
3700}
3701
3702fn hierarchy_item(m: &Module, d: &Decl, text: &str) -> J {
3704 let name = d.name().unwrap_or("");
3705 J::obj(vec![
3706 ("name", J::s(name)),
3707 ("kind", J::Num(symbol_kind(d).unwrap_or(13))),
3708 ("uri", J::s(uri_of(&m.path))),
3709 ("range", range_json(d.loc.unwrap())),
3710 ("selectionRange", range_json(name_range(text, d, name))),
3711 ])
3712}
3713fn prepare_hierarchy(st: &mut State, uri: &str, pos: Pos, want: &str) -> J {
3714 let Some(a) = st.analysis_of(uri) else {
3715 return J::Null;
3716 };
3717 let Some(site) = site_at(st, &a, uri, pos).and_then(|s| s.site) else {
3718 return J::Null;
3719 };
3720 if site.decl.is_none() || site.kind != want {
3721 return J::Null;
3722 }
3723 let sm = site.module.clone();
3724 let Some(decl) = decl_by_id(&sm, site.decl) else {
3725 return J::Null;
3726 };
3727 J::Arr(vec![hierarchy_item(&sm, decl, &text_of(st, &sm))])
3728}
3729fn module_of_uri(st: &State, uri: &str) -> Option<(Rc<Analysis>, Rc<Module>)> {
3730 for a in st.last_good.values() {
3731 if let Some(m) = a.run.modules.iter().find(|x| uri_of(&x.path) == uri) {
3732 return Some((a.clone(), m.clone()));
3733 }
3734 }
3735 None
3736}
3737fn decl_containing(m: &Module, loc: Loc) -> Option<&Decl> {
3738 m.decls.iter().find(|d| {
3739 d.loc
3740 .map(|dl| dl.sl <= loc.sl && loc.el <= dl.el)
3741 .unwrap_or(false)
3742 && d.name().is_some()
3743 })
3744}
3745fn item_uri_line(item: Option<&Value>) -> Option<(String, usize)> {
3746 let item = item?;
3747 let uri = as_str(get(item, "uri"))?.to_string();
3748 let line = as_usize(
3749 get(item, "range")
3750 .and_then(|r| get(r, "start"))
3751 .and_then(|s| get(s, "line")),
3752 )?;
3753 Some((uri, line))
3754}
3755fn incoming_calls(st: &State, item: Option<&Value>) -> J {
3756 let Some((uri, line)) = item_uri_line(item) else {
3757 return J::Arr(vec![]);
3758 };
3759 let Some((a, _)) = module_of_uri(st, &uri) else {
3760 return J::Arr(vec![]);
3761 };
3762 let mut out: Vec<J> = vec![];
3763 for m in a.run.modules.clone() {
3764 let t = tables_of(&a, &m);
3765 let text = text_of(st, &m);
3766 let mut by_caller: Vec<(usize, Vec<Loc>)> = vec![];
3767 for d in &m.decls {
3768 let mut exprs: Vec<Rc<Expr>> = vec![];
3769 let mut types: Vec<&TypeAst> = vec![];
3770 collect_decl(d, &mut exprs, &mut types);
3771 for x in &exprs {
3772 let Expr::Call { fun, .. } = &**x else {
3773 continue;
3774 };
3775 let Expr::Name(n) = &**fun else { continue };
3776 let Some(fl) = expr_loc(fun) else { continue };
3777 let tg = match t.res.get(&key_of(fun)) {
3778 Some(r) => r.clone(),
3779 None => resolve_in(&m.env, n),
3780 };
3781 let Some(site) = site_of_target(st, &a, tg.as_ref()) else {
3782 continue;
3783 };
3784 if site.decl.is_none()
3785 || uri_of(&site.module.path) != uri
3786 || site.decl_loc.map(|l| l.sl) != Some(line)
3787 {
3788 continue;
3789 }
3790 if let Some(caller) = decl_containing(&m, fl) {
3791 let id = decl_id(caller);
3792 match by_caller.iter_mut().find(|(k, _)| *k == id) {
3793 Some(e) => e.1.push(fl),
3794 None => by_caller.push((id, vec![fl])),
3795 }
3796 }
3797 }
3798 }
3799 for (id, locs) in by_caller {
3800 let Some(caller) = decl_by_id(&m, Some(id)) else {
3801 continue;
3802 };
3803 out.push(J::obj(vec![
3804 ("from", hierarchy_item(&m, caller, &text)),
3805 (
3806 "fromRanges",
3807 J::Arr(locs.into_iter().map(range_json).collect()),
3808 ),
3809 ]));
3810 }
3811 }
3812 J::Arr(out)
3813}
3814fn outgoing_calls(st: &State, item: Option<&Value>) -> J {
3815 let Some((uri, line)) = item_uri_line(item) else {
3816 return J::Arr(vec![]);
3817 };
3818 let Some((a, m)) = module_of_uri(st, &uri) else {
3819 return J::Arr(vec![]);
3820 };
3821 let t = tables_of(&a, &m);
3822 let Some(decl) = m.decls.iter().find(|d| d.loc.map(|l| l.sl) == Some(line)) else {
3823 return J::Arr(vec![]);
3824 };
3825 let mut exprs: Vec<Rc<Expr>> = vec![];
3826 let mut types: Vec<&TypeAst> = vec![];
3827 collect_decl(decl, &mut exprs, &mut types);
3828 let mut by_callee: Vec<(String, J, Vec<Loc>)> = vec![];
3829 for x in &exprs {
3830 let Expr::Call { fun, .. } = &**x else {
3831 continue;
3832 };
3833 let Expr::Name(n) = &**fun else { continue };
3834 let Some(fl) = expr_loc(fun) else { continue };
3835 let tg = match t.res.get(&key_of(fun)) {
3836 Some(r) => r.clone(),
3837 None => resolve_in(&m.env, n),
3838 };
3839 let Some(site) = site_of_target(st, &a, tg.as_ref()) else {
3840 continue;
3841 };
3842 if site.kind != "func" {
3843 continue;
3844 }
3845 let sm = site.module.clone();
3846 let Some(callee) = decl_by_id(&sm, site.decl) else {
3847 continue;
3848 };
3849 let key = format!(
3850 "{}:{}",
3851 sm.path.to_string_lossy(),
3852 callee.loc.map(|l| l.sl).unwrap_or(0)
3853 );
3854 match by_callee.iter_mut().find(|(k, _, _)| *k == key) {
3855 Some(e) => e.2.push(fl),
3856 None => by_callee.push((
3857 key,
3858 hierarchy_item(&sm, callee, &text_of(st, &sm)),
3859 vec![fl],
3860 )),
3861 }
3862 }
3863 J::Arr(
3864 by_callee
3865 .into_iter()
3866 .map(|(_, to, locs)| {
3867 J::obj(vec![
3868 ("to", to),
3869 (
3870 "fromRanges",
3871 J::Arr(locs.into_iter().map(range_json).collect()),
3872 ),
3873 ])
3874 })
3875 .collect(),
3876 )
3877}
3878fn supertypes(st: &State, item: Option<&Value>) -> J {
3879 let Some((uri, line)) = item_uri_line(item) else {
3880 return J::Arr(vec![]);
3881 };
3882 let Some((a, m)) = module_of_uri(st, &uri) else {
3883 return J::Arr(vec![]);
3884 };
3885 let base = m.decls.iter().find_map(|d| match &d.body {
3886 DeclBody::Type {
3887 ty: TypeAst::Named {
3888 name, ext: Some(_), ..
3889 },
3890 ..
3891 } if d.loc.map(|l| l.sl) == Some(line) => Some(name.clone()),
3892 _ => None,
3893 });
3894 let Some(base) = base else {
3895 return J::Arr(vec![]);
3896 };
3897 let Some(site) = site_of_target(st, &a, resolve_in(&m.env, &base).as_ref()) else {
3898 return J::Arr(vec![]);
3899 };
3900 let sm = site.module.clone();
3901 match decl_by_id(&sm, site.decl) {
3902 Some(d) => J::Arr(vec![hierarchy_item(&sm, d, &text_of(st, &sm))]),
3903 None => J::Arr(vec![]),
3904 }
3905}
3906fn subtypes(st: &State, item: Option<&Value>) -> J {
3907 let Some((uri, line)) = item_uri_line(item) else {
3908 return J::Arr(vec![]);
3909 };
3910 let Some((a, _)) = module_of_uri(st, &uri) else {
3911 return J::Arr(vec![]);
3912 };
3913 let mut out: Vec<J> = vec![];
3914 for m in a.run.modules.clone() {
3915 for d in &m.decls {
3916 let DeclBody::Type {
3917 ty: TypeAst::Named {
3918 name, ext: Some(_), ..
3919 },
3920 ..
3921 } = &d.body
3922 else {
3923 continue;
3924 };
3925 if d.loc.is_none() {
3926 continue;
3927 }
3928 let Some(site) = site_of_target(st, &a, resolve_in(&m.env, name).as_ref()) else {
3929 continue;
3930 };
3931 if site.decl.is_some()
3932 && uri_of(&site.module.path) == uri
3933 && site.decl_loc.map(|l| l.sl) == Some(line)
3934 {
3935 out.push(hierarchy_item(&m, d, &text_of(st, &m)));
3936 }
3937 }
3938 }
3939 J::Arr(out)
3940}
3941
3942fn js_value_string(v: &Value) -> String {
3944 match v {
3945 Value::Str(s) => json_str(s),
3946 Value::Int(i) => i.to_string(),
3947 Value::Float(f) => crate::semantics::js_num_str(*f),
3948 Value::Bool(b) => b.to_string(),
3949 _ => "null".into(),
3950 }
3951}
3952trait ConstExprLoc {
3954 fn const_expr_loc(&self) -> Option<Loc>;
3955}
3956impl ConstExprLoc for DeclBody {
3957 fn const_expr_loc(&self) -> Option<Loc> {
3958 match self {
3959 DeclBody::Const { expr, .. } => expr_loc(expr),
3960 _ => None,
3961 }
3962 }
3963}
3964fn record_body_of(d: &Decl) -> Option<&TypeAst> {
3965 let DeclBody::Type { ty, .. } = &d.body else {
3966 return None;
3967 };
3968 match ty {
3969 TypeAst::Record { .. } => Some(ty),
3970 TypeAst::Named { ext: Some(x), .. } => match &**x {
3971 TypeAst::Record { .. } => Some(&**x),
3972 _ => None,
3973 },
3974 _ => None,
3975 }
3976}
3977fn record_parts(t: &TypeAst) -> Option<(&[MemberAst], Loc, bool)> {
3978 match t {
3979 TypeAst::Record {
3980 members,
3981 open,
3982 loc: Some(l),
3983 } => Some((members, *l, *open)),
3984 _ => None,
3985 }
3986}
3987fn safe_resolve(m: &Module, type_name: &str) -> Option<RT> {
3988 m.env
3989 .resolve(
3990 &TypeAst::Named {
3991 name: type_name.to_string(),
3992 args: vec![],
3993 preds: None,
3994 ext: None,
3995 loc: None,
3996 },
3997 None,
3998 )
3999 .ok()
4000}
4001fn placeholder_for(rt: Option<&RT>) -> String {
4002 let Some(rt) = rt else { return "null".into() };
4003 let r: RT = match &rt.k {
4004 RTk::Pred { base, .. } => base.clone(),
4005 _ => rt.clone(),
4006 };
4007 match &r.k {
4008 RTk::Prim(name) => match name.as_str() {
4009 "string" => "\"\"".into(),
4010 "int" => "0".into(),
4011 "float" => "0.0".into(),
4012 "bool" => "false".into(),
4013 _ => "null".into(),
4014 },
4015 RTk::Lit(v) => js_value_string(v),
4016 RTk::Range { lo, .. } => js_value_string(lo),
4017 RTk::Rec(_) => "{ }".into(),
4018 RTk::Arr { .. } => "[]".into(),
4019 RTk::Map { .. } => "{}".into(),
4020 RTk::Union(arms) => placeholder_for(arms.first()),
4021 _ => "null".into(),
4022 }
4023}
4024fn value_to_j(v: &Value) -> J {
4025 match v {
4026 Value::Null | Value::Undef | Value::Absent => J::Null,
4027 Value::Bool(b) => J::Bool(*b),
4028 Value::Int(i) => J::Num(i.to_string().parse().unwrap_or(0)),
4029 Value::Float(f) => J::Str(crate::semantics::js_num_str(*f)),
4030 Value::Str(s) => J::s(s.clone()),
4031 Value::JArr(items) => J::Arr(items.iter().map(value_to_j).collect()),
4032 Value::JObj(es) => J::Obj(es.iter().map(|(k, x)| (k.clone(), value_to_j(x))).collect()),
4033 other => J::s(format!("{other:?}")),
4034 }
4035}
4036fn resolve_lexical(dir: &Path, spec: &str) -> PathBuf {
4038 let joined = if spec.starts_with('/') {
4039 PathBuf::from(spec)
4040 } else {
4041 dir.join(spec)
4042 };
4043 let mut parts: Vec<String> = vec![];
4044 for c in joined.to_string_lossy().split('/') {
4045 match c {
4046 "" | "." => {}
4047 ".." => {
4048 parts.pop();
4049 }
4050 x => parts.push(x.to_string()),
4051 }
4052 }
4053 PathBuf::from(format!("/{}", parts.join("/")))
4054}
4055fn relative_path(from_dir: &Path, to: &Path) -> String {
4056 let f: Vec<&str> = from_dir
4057 .to_str()
4058 .unwrap_or("")
4059 .split('/')
4060 .filter(|x| !x.is_empty())
4061 .collect();
4062 let t: Vec<&str> = to
4063 .to_str()
4064 .unwrap_or("")
4065 .split('/')
4066 .filter(|x| !x.is_empty())
4067 .collect();
4068 let mut i = 0;
4069 while i < f.len() && i < t.len() && f[i] == t[i] {
4070 i += 1;
4071 }
4072 let mut parts: Vec<&str> = f[i..].iter().map(|_| "..").collect();
4073 parts.extend(t[i..].iter().copied());
4074 parts.join("/")
4075}
4076fn require_rel(from: &Path, to: &Path) -> String {
4077 let rel = relative_path(from.parent().unwrap_or(Path::new("/")), to);
4078 if rel.starts_with('.') {
4079 rel.strip_prefix("./").unwrap_or(&rel).to_string()
4080 } else {
4081 rel
4082 }
4083}
4084fn exporters_of(st: &State, a: &Analysis, m: &Module, name: &str) -> Vec<PathBuf> {
4087 let mut out: Vec<PathBuf> = vec![];
4088 let mut seen: Vec<PathBuf> = vec![m.path.clone()];
4089 let consider = |md: &Module, out: &mut Vec<PathBuf>, seen: &mut Vec<PathBuf>| {
4090 if !seen.contains(&md.path) && md.exports.borrow().contains_key(name) {
4091 seen.push(md.path.clone());
4092 out.push(md.path.clone());
4093 }
4094 };
4095 for md in &a.run.modules {
4096 consider(md, &mut out, &mut seen);
4097 }
4098 for other in st.last_good.values() {
4099 for md in &other.run.modules {
4100 consider(md, &mut out, &mut seen);
4101 }
4102 }
4103 let dir = m.path.parent().map(|p| p.to_path_buf()).unwrap_or_default();
4104 let mut names: Vec<String> = std::fs::read_dir(&dir)
4105 .map(|rd| {
4106 rd.flatten()
4107 .map(|e| e.file_name().to_string_lossy().to_string())
4108 .collect()
4109 })
4110 .unwrap_or_default();
4111 names.sort();
4112 for f in names {
4113 if !f.ends_with(".decl") {
4114 continue;
4115 }
4116 let p = resolve_lexical(&dir, &f);
4117 if seen.contains(&p) {
4118 continue;
4119 }
4120 let text = st.overlay.get(&p).cloned().unwrap_or_else(|| read_text(&p));
4121 let parsed = parse_source(&text);
4122 if !parsed.errors.is_empty() {
4123 continue;
4124 }
4125 if parsed.decls.iter().any(|d| {
4126 d.exported && d.name() == Some(name) && !matches!(d.body, DeclBody::Import { .. })
4127 }) {
4128 seen.push(p.clone());
4129 out.push(p);
4130 }
4131 }
4132 out
4133}
4134fn edit_json(l: Loc, new_text: &str) -> J {
4135 J::obj(vec![("range", range_json(l)), ("newText", J::s(new_text))])
4136}
4137fn insert_json(p: Pos, new_text: &str) -> J {
4138 edit_json(
4139 Loc {
4140 sl: p.line,
4141 sc: p.character,
4142 el: p.line,
4143 ec: p.character,
4144 },
4145 new_text,
4146 )
4147}
4148fn action_edits_json(
4149 title: String,
4150 kind: &str,
4151 diagnostic: Option<J>,
4152 preferred: bool,
4153 uri: &str,
4154 edits: Vec<J>,
4155) -> J {
4156 let mut item = vec![("title", J::s(title)), ("kind", J::s(kind))];
4157 if let Some(d) = diagnostic {
4158 item.push(("diagnostics", J::Arr(vec![d])));
4159 }
4160 if preferred {
4161 item.push(("isPreferred", J::Bool(true)));
4162 }
4163 item.push((
4164 "edit",
4165 J::obj(vec![(
4166 "changes",
4167 J::Obj(vec![(uri.to_string(), J::Arr(edits))]),
4168 )]),
4169 ));
4170 J::obj(item)
4171}
4172fn action_json(
4173 title: String,
4174 kind: &str,
4175 diagnostic: Option<J>,
4176 preferred: bool,
4177 uri: &str,
4178 edit: J,
4179) -> J {
4180 action_edits_json(title, kind, diagnostic, preferred, uri, vec![edit])
4181}
4182fn diag_pos(d: &Value, which: &str) -> Pos {
4183 let p = get(d, "range").and_then(|r| get(r, which));
4184 Pos {
4185 line: as_usize(p.and_then(|s| get(s, "line"))).unwrap_or(0),
4186 character: as_usize(p.and_then(|s| get(s, "character"))).unwrap_or(0),
4187 }
4188}
4189fn chain_of<'a>(hit: &Hit<'a>) -> Vec<NodeRef<'a>> {
4190 let mut chain: Vec<NodeRef> = vec![hit.node.clone()];
4191 chain.extend(hit.parents.iter().rev().cloned());
4192 chain
4193}
4194fn chain_expr<'a>(chain: &[NodeRef<'a>], pred: impl Fn(&Expr) -> bool) -> Option<Rc<Expr>> {
4195 chain.iter().find_map(|n| match n {
4196 NodeRef::Expr(e) if pred(e) => Some((*e).clone()),
4197 _ => None,
4198 })
4199}
4200fn exprs_under<'a>(n: &NodeRef<'a>) -> Vec<Rc<Expr>> {
4202 let mut exprs: Vec<Rc<Expr>> = vec![];
4203 let mut types: Vec<&TypeAst> = vec![];
4204 match n {
4205 NodeRef::Decl(d) => collect_decl(d, &mut exprs, &mut types),
4206 NodeRef::Member(m) => collect_member(m, &mut exprs, &mut types),
4207 NodeRef::Type(t) => collect_type(t, &mut exprs, &mut types),
4208 NodeRef::Expr(e) => collect_expr(e, &mut exprs, &mut types),
4209 }
4210 exprs
4211}
4212fn widen(rt: &RT) -> RT {
4214 match &rt.k {
4215 RTk::Lit(v) => {
4216 let name = match v {
4217 Value::Str(_) => "string",
4218 Value::Bool(_) => "bool",
4219 Value::Int(_) => "int",
4220 Value::Float(_) => "float",
4221 _ => "null",
4222 };
4223 crate::semantics::ty(RTk::Prim(name.to_string()))
4224 }
4225 _ => rt.clone(),
4226 }
4227}
4228fn is_logical(op: &str) -> bool {
4229 op == "&&" || op == "||"
4230}
4231fn mixed_in_expr(e: &Rc<Expr>, parent_op: Option<&str>) -> Option<Rc<Expr>> {
4234 if let Expr::Bin { op, .. } = &**e {
4235 if let (Some(pop), Some(_)) = (parent_op, expr_loc(e)) {
4236 if (op == "??" && is_logical(pop)) || (is_logical(op) && pop == "??") {
4237 return Some(e.clone());
4238 }
4239 }
4240 }
4241 let op: Option<&str> = match &**e {
4242 Expr::Bin { op, .. } => Some(op.as_str()),
4243 _ => None,
4244 };
4245 let kids: Vec<Rc<Expr>> = match &**e {
4246 Expr::Template(parts) => parts
4247 .iter()
4248 .filter_map(|p| {
4249 if let TPart::Expr(x) = p {
4250 Some(x.clone())
4251 } else {
4252 None
4253 }
4254 })
4255 .collect(),
4256 Expr::Obj(entries) => entries.iter().map(|(_, v)| v.clone()).collect(),
4257 Expr::Arr(items) => items.iter().map(|(_, v)| v.clone()).collect(),
4258 Expr::Comp { head, clauses } => std::iter::once(head.clone())
4259 .chain(
4260 clauses
4261 .iter()
4262 .flat_map(|c| std::iter::once(c.iter.clone()).chain(c.filters.iter().cloned())),
4263 )
4264 .collect(),
4265 Expr::MapComp { key, val, clauses } => vec![key.clone(), val.clone()]
4266 .into_iter()
4267 .chain(
4268 clauses
4269 .iter()
4270 .flat_map(|c| std::iter::once(c.iter.clone()).chain(c.filters.iter().cloned())),
4271 )
4272 .collect(),
4273 Expr::Bin { l, r, .. } => vec![l.clone(), r.clone()],
4274 Expr::Un { x, .. } | Expr::Paren(x) => vec![x.clone()],
4275 Expr::If { c, t, f } => vec![c.clone(), t.clone(), f.clone()],
4276 Expr::Lambda { body, .. } => vec![body.clone()],
4277 Expr::Call { fun, args } => std::iter::once(fun.clone())
4278 .chain(args.iter().cloned())
4279 .collect(),
4280 Expr::Member { x, .. } => vec![x.clone()],
4281 Expr::Index { x, i } => vec![x.clone(), i.clone()],
4282 Expr::With { base, patch } => vec![base.clone(), patch.clone()],
4283 Expr::Match { subject, arms } => std::iter::once(subject.clone())
4284 .chain(arms.iter().map(|a| a.body.clone()))
4285 .collect(),
4286 _ => vec![],
4287 };
4288 for k in kids {
4289 if let Some(t) = mixed_in_expr(&k, op) {
4290 return Some(t);
4291 }
4292 }
4293 None
4294}
4295fn mixed_in_node(n: &NodeRef) -> Option<Rc<Expr>> {
4296 match n {
4297 NodeRef::Expr(e) => mixed_in_expr(e, None),
4298 NodeRef::Decl(d) => {
4299 let tops: Vec<Rc<Expr>> = match &d.body {
4300 DeclBody::Const { expr, .. } | DeclBody::Output { expr, .. } => vec![expr.clone()],
4301 DeclBody::Input { fallback, .. } => fallback.iter().cloned().collect(),
4302 DeclBody::Func { body, .. } => vec![body.clone()],
4303 DeclBody::Unit { factor, .. } => factor.iter().cloned().collect(),
4304 _ => vec![],
4305 };
4306 tops.iter().find_map(|e| mixed_in_expr(e, None))
4307 }
4308 NodeRef::Member(m) => match m {
4309 MemberAst::Derived { expr, .. } => mixed_in_expr(expr, None),
4310 MemberAst::Value { dflt: Some(d), .. } => mixed_in_expr(d, None),
4311 MemberAst::Assert { cond, .. } | MemberAst::When { cond, .. } => {
4312 mixed_in_expr(cond, None)
4313 }
4314 _ => None,
4315 },
4316 NodeRef::Type(_) => None,
4317 }
4318}
4319fn mentions_name(e: &Rc<Expr>) -> bool {
4321 let mut exprs: Vec<Rc<Expr>> = vec![];
4322 let mut types: Vec<&TypeAst> = vec![];
4323 collect_expr(e, &mut exprs, &mut types);
4324 matches!(&**e, Expr::Name(_) | Expr::Ctx(_) | Expr::Referrers { .. })
4325 || exprs
4326 .iter()
4327 .any(|x| matches!(&**x, Expr::Name(_) | Expr::Ctx(_) | Expr::Referrers { .. }))
4328}
4329fn leading_spaces(line: &str) -> String {
4330 line.chars().take_while(|c| *c == ' ').collect()
4331}
4332fn on_type_formatting(st: &State, uri: &str, pos: Pos, ch: &str) -> J {
4337 let Some(text) = st.text(uri) else {
4338 return J::Arr(vec![]);
4339 };
4340 let lines: Vec<&str> = text.split('\n').collect();
4341 let line = |i: usize| lines.get(i).copied().unwrap_or("");
4342 let indent_of = |s: &str| s.chars().take_while(|c| *c == ' ').count();
4343 let edit = |l: usize, have: usize, want: usize| {
4344 if have == want {
4345 J::Arr(vec![])
4346 } else {
4347 J::Arr(vec![edit_json(
4348 Loc {
4349 sl: l,
4350 sc: 0,
4351 el: l,
4352 ec: have,
4353 },
4354 &" ".repeat(want),
4355 )])
4356 }
4357 };
4358 if ch == "\n" {
4359 let prev = if pos.line >= 1 {
4360 line(pos.line - 1)
4361 } else {
4362 ""
4363 };
4364 let cur = line(pos.line);
4365 let body = Regex::new(r"//.*$")
4366 .unwrap()
4367 .replace(prev, "")
4368 .trim_end()
4369 .to_string();
4370 let mut want = indent_of(prev);
4371 if Regex::new(r"[{\[(]$").unwrap().is_match(&body)
4372 || Regex::new(r"(?:[+\-*/%<>=!&|?:,]|\bthen|\belse|\bin|\bwith|=>)$")
4373 .unwrap()
4374 .is_match(&body)
4375 {
4376 want += 4;
4377 }
4378 if Regex::new(r"^\s*[}\])]").unwrap().is_match(cur) {
4379 want = want.saturating_sub(4);
4380 }
4381 return edit(pos.line, indent_of(cur), want);
4382 }
4383 if ch == "}" || ch == "]" || ch == ")" {
4384 let cur = line(pos.line);
4385 if cur.trim() != ch {
4386 return J::Arr(vec![]);
4387 }
4388 let close = ch.chars().next().unwrap();
4389 let open = match ch {
4390 "}" => '{',
4391 "]" => '[',
4392 _ => '(',
4393 };
4394 let mut depth = 0i64;
4396 for l in (0..=pos.line).rev() {
4397 let s = line(l);
4398 let chars: Vec<char> = s.chars().collect();
4399 let end = if l == pos.line {
4400 cur.find(ch).map(|b| cur[..b].chars().count()).unwrap_or(0)
4401 } else {
4402 chars.len()
4403 };
4404 for i in (0..end).rev() {
4405 let c = chars[i];
4406 if c == close {
4407 depth += 1;
4408 } else if c == open {
4409 if depth == 0 {
4410 return edit(pos.line, indent_of(cur), indent_of(s));
4411 }
4412 depth -= 1;
4413 }
4414 }
4415 }
4416 return J::Arr(vec![]);
4417 }
4418 J::Arr(vec![])
4419}
4420
4421fn code_actions(st: &mut State, uri: &str, range: (Pos, Pos), diagnostics: &[Value]) -> J {
4422 let Some(text) = st.text(uri).cloned() else {
4423 return J::Arr(vec![]);
4424 };
4425 let a = st.analysis_of(uri);
4426 let mut out: Vec<J> = vec![];
4427 let parsed = parse_source(&text);
4428 let lines: Vec<&str> = text.split('\n').collect();
4429 let (Some(a), true) = (a, parsed.errors.is_empty()) else {
4430 return J::Arr(out);
4431 };
4432 let Some(m) = module_of(&a, &path_of(uri)) else {
4433 return J::Arr(out);
4434 };
4435 let t = tables_of(&a, &m);
4436 let re_unknown = Regex::new(r"^unknown name ([A-Za-z_][A-Za-z0-9_]*)").unwrap();
4437 let re_undeclared = Regex::new(
4438 r"^member ([A-Za-z_][A-Za-z0-9_]*) is not declared on ([A-Za-z_][A-Za-z0-9_]*)$",
4439 )
4440 .unwrap();
4441 let re_missing = Regex::new(r"^required member ([A-Za-z_][A-Za-z0-9_]*) missing").unwrap();
4442 let re_ctx_undeclared =
4443 Regex::new(r"^(\$[a-z]+) used without a context declaration in ([A-Za-z_][A-Za-z0-9_]*)$")
4444 .unwrap();
4445 let re_ctx_ref =
4446 Regex::new(r"^(\$[a-z]+) declaration must be ref<\.\.\.> \(([A-Za-z_][A-Za-z0-9_]*)\)$")
4447 .unwrap();
4448 let re_override = Regex::new(r"^(?:illegal member-kind transition for|override widens inherited member) ([A-Za-z_][A-Za-z0-9_]*)[^(]*\(([A-Za-z_][A-Za-z0-9_]*)\)$").unwrap();
4449 let re_union =
4450 Regex::new(r"^record union arms not discriminable in ([A-Za-z_][A-Za-z0-9_]*)$").unwrap();
4451 let re_restated =
4452 Regex::new(r"^derived member ([A-Za-z_][A-Za-z0-9_]*) restated with a differing value")
4453 .unwrap();
4454 let trailing_comma = Regex::new(r",\s*$").unwrap();
4455 let touches = |d: &Value| {
4457 get(d, "range").is_some()
4458 && !(diag_pos(d, "end").line < range.0.line || diag_pos(d, "start").line > range.1.line)
4459 };
4460 for d in diagnostics.iter().filter(|d| touches(d)) {
4461 let message = as_str(get(d, "message")).unwrap_or("").to_string();
4462 let dpos = diag_pos(d, "start");
4463 if let Some(cap) = re_unknown.captures(&message) {
4464 let name = cap[1].to_string();
4465 for other in exporters_of(st, &a, &m, &name) {
4466 let mut spec = format!("./{}", require_rel(&m.path, &other));
4467 let dir = m.path.parent().map(|p| p.to_path_buf()).unwrap_or_default();
4468 let existing = parsed.decls.iter().find(|x| match &x.body {
4469 DeclBody::Import {
4470 from,
4471 names: Some(_),
4472 ..
4473 } => resolve_lexical(&dir, from) == other,
4474 _ => false,
4475 });
4476 let edit = match existing {
4477 Some(x) => {
4478 let l = x.loc.unwrap();
4479 let line = lines.get(l.sl).copied().unwrap_or("");
4480 let close = find16(line, "}", l.sc).map(|c| c as i64).unwrap_or(-1);
4481 if let DeclBody::Import { from, .. } = &x.body {
4482 spec = from.clone();
4483 }
4484 edit_json(
4485 Loc {
4486 sl: l.sl,
4487 sc: close.max(0) as usize,
4488 el: l.sl,
4489 ec: close.max(0) as usize,
4490 },
4491 &format!(", {name} "),
4492 )
4493 }
4494 None => {
4495 let last_import = parsed.decls.iter().rev().find(|x| {
4496 matches!(x.body, DeclBody::Import { .. } | DeclBody::ReExport { .. })
4497 });
4498 let at = last_import
4499 .and_then(|x| x.loc)
4500 .map(|l| l.el + 1)
4501 .unwrap_or(0);
4502 edit_json(
4503 Loc {
4504 sl: at,
4505 sc: 0,
4506 el: at,
4507 ec: 0,
4508 },
4509 &format!("import {{ {name} }} from \"{spec}\"\n"),
4510 )
4511 }
4512 };
4513 out.push(action_json(
4514 format!("import {name} from \"{spec}\""),
4515 "quickfix",
4516 Some(value_to_j(d)),
4517 true,
4518 uri,
4519 edit,
4520 ));
4521 }
4522 let mut nss: Vec<String> = vec![];
4524 for x in &parsed.decls {
4525 if let DeclBody::Import { ns: Some(ns), .. } = &x.body {
4526 if !nss.contains(ns) {
4527 nss.push(ns.clone());
4528 }
4529 }
4530 }
4531 for ns in nss {
4532 let exports = m.env.namespaces.borrow().get(&ns).map(|(_, ex)| ex.clone());
4533 let Some(exports) = exports else { continue };
4534 if !exports.borrow().contains_key(&name) {
4535 continue;
4536 }
4537 let Some(hit) = node_at(&parsed.decls, dpos) else {
4538 continue;
4539 };
4540 let chain = chain_of(&hit);
4541 let n = chain_expr(&chain, |x| matches!(x, Expr::Name(nm) if *nm == name));
4542 if let Some(l) = n.as_ref().and_then(expr_loc) {
4543 out.push(action_json(
4544 format!("qualify as {ns}.{name}"),
4545 "quickfix",
4546 Some(value_to_j(d)),
4547 false,
4548 uri,
4549 edit_json(l, &format!("{ns}.{name}")),
4550 ));
4551 }
4552 }
4553 }
4554 if let Some(cap) = re_undeclared.captures(&message) {
4555 let (name, type_name) = (cap[1].to_string(), cap[2].to_string());
4557 let site = site_of_target(st, &a, resolve_in(&m.env, &type_name).as_ref());
4558 if let Some(site) = site {
4559 let sm = site.module.clone();
4560 let body: Option<(&[MemberAst], Loc)> =
4561 decl_by_id(&sm, site.decl).and_then(|decl| match &decl.body {
4562 DeclBody::Type { ty, .. } => match ty {
4563 TypeAst::Record {
4564 members,
4565 loc: Some(l),
4566 ..
4567 } => Some((members.as_slice(), *l)),
4568 TypeAst::Named { ext: Some(ext), .. } => match &**ext {
4569 TypeAst::Record {
4570 members,
4571 loc: Some(l),
4572 ..
4573 } => Some((members.as_slice(), *l)),
4574 _ => None,
4575 },
4576 _ => None,
4577 },
4578 _ => None,
4579 });
4580 if let Some((members, body_loc)) = body {
4581 let hit = node_at(&parsed.decls, dpos);
4582 let mut entry: Option<Rc<Expr>> = None;
4583 if let Some(hit) = &hit {
4584 let chain = chain_of(hit);
4585 if let Some(obj) = chain_expr(&chain, |x| matches!(x, Expr::Obj(_))) {
4586 if let Expr::Obj(entries) = &*obj {
4587 entry = entries
4588 .iter()
4589 .find(|(k, _)| *k == name)
4590 .map(|(_, v)| v.clone());
4591 }
4592 }
4593 if entry.is_none() {
4594 for o in exprs_under(&hit.node) {
4595 if let Expr::Obj(entries) = &*o {
4596 if let Some((_, v)) = entries.iter().find(|(k, _)| *k == name) {
4597 entry = Some(v.clone());
4598 break;
4599 }
4600 }
4601 }
4602 }
4603 }
4604 let ty = entry
4605 .as_ref()
4606 .and_then(|v| t.types.get(&key_of(v)))
4607 .and_then(|x| x.rt.clone());
4608 let member_type = ty
4609 .map(|rt| type_text(Some(&widen(&rt))))
4610 .unwrap_or_else(|| "any".into());
4611 let last = members.last().and_then(|x| x.loc());
4612 let (at, new_text) = match last {
4613 Some(l) => (
4614 Pos {
4615 line: l.el,
4616 character: l.ec,
4617 },
4618 format!("\n {name}: {member_type}"),
4619 ),
4620 None => (
4621 Pos {
4622 line: body_loc.sl,
4623 character: body_loc.sc + 1,
4624 },
4625 format!(" {name}: {member_type}"),
4626 ),
4627 };
4628 out.push(action_json(
4629 format!("declare {name}: {member_type} on {type_name}"),
4630 "quickfix",
4631 Some(value_to_j(d)),
4632 true,
4633 &uri_of(&sm.path),
4634 insert_json(at, &new_text),
4635 ));
4636 }
4637 }
4638 }
4639 if message.starts_with("member access on a maybe-absent expression") {
4640 if let Some(hit) = node_at(&parsed.decls, dpos) {
4641 let chain = chain_of(&hit);
4642 if let Some(n) =
4643 chain_expr(&chain, |x| matches!(x, Expr::Member { safe: false, .. }))
4644 {
4645 if let (Expr::Member { name, .. }, Some(_)) = (&*n, expr_loc(&n)) {
4646 let tok = member_token_loc(&text, &n, name);
4647 out.push(action_json(
4648 "use ?.".into(),
4649 "quickfix",
4650 Some(value_to_j(d)),
4651 true,
4652 uri,
4653 edit_json(
4654 Loc {
4655 sl: tok.sl,
4656 sc: tok.sc - 1,
4657 el: tok.sl,
4658 ec: tok.sc,
4659 },
4660 "?.",
4661 ),
4662 ));
4663 }
4664 }
4665 }
4666 }
4667 if message.starts_with("maybe-absent expression consumed") {
4668 if let Some(hit) = node_at(&parsed.decls, dpos) {
4669 if let NodeRef::Expr(e) = &hit.node {
4670 if let Some(l) = expr_loc(e) {
4671 out.push(action_json(
4672 "supply a fallback with ??".into(),
4673 "quickfix",
4674 Some(value_to_j(d)),
4675 false,
4676 uri,
4677 insert_json(
4678 Pos {
4679 line: l.el,
4680 character: l.ec,
4681 },
4682 " ?? null",
4683 ),
4684 ));
4685 }
4686 }
4687 }
4688 }
4689 if message.starts_with("`??` mixed with") {
4690 if let Some(hit) = node_at(&parsed.decls, dpos) {
4692 if let Some(target) = mixed_in_node(&hit.node) {
4693 let l = expr_loc(&target).unwrap();
4694 out.push(action_edits_json(
4695 "parenthesize the ?? expression".into(),
4696 "quickfix",
4697 Some(value_to_j(d)),
4698 true,
4699 uri,
4700 vec![
4701 insert_json(
4702 Pos {
4703 line: l.sl,
4704 character: l.sc,
4705 },
4706 "(",
4707 ),
4708 insert_json(
4709 Pos {
4710 line: l.el,
4711 character: l.ec,
4712 },
4713 ")",
4714 ),
4715 ],
4716 ));
4717 }
4718 }
4719 }
4720 if message.starts_with("`match` is not exhaustive") {
4721 if let Some(hit) = node_at(&parsed.decls, dpos) {
4722 let chain = chain_of(&hit);
4723 if let Some(n) = chain_expr(&chain, |x| matches!(x, Expr::Match { .. })) {
4724 let Expr::Match {
4725 subject,
4726 arms: match_arms,
4727 } = &*n
4728 else {
4729 unreachable!()
4730 };
4731 let subject_rt = t.types.get(&key_of(subject)).and_then(|x| x.rt.clone());
4732 let arms: Vec<String> = match subject_rt.as_ref().map(|r| &r.k) {
4733 Some(RTk::Union(us)) => us
4734 .iter()
4735 .filter_map(|r| {
4736 if let RTk::Rec(_) = &r.k {
4737 rec_name(Some(r))
4738 } else {
4739 None
4740 }
4741 })
4742 .collect(),
4743 _ => vec![],
4744 };
4745 let covered: Vec<String> = match_arms
4746 .iter()
4747 .map(|arm| match &arm.ty {
4748 Some(TypeAst::Named { name, .. }) => name.clone(),
4749 _ => String::new(),
4750 })
4751 .collect();
4752 let missing: Vec<String> =
4753 arms.into_iter().filter(|x| !covered.contains(x)).collect();
4754 if let (Some(l), false) = (expr_loc(&n), missing.is_empty()) {
4755 let at = Pos {
4756 line: l.el,
4757 character: l.ec - 1,
4758 };
4759 let indent = format!(
4760 "{} ",
4761 leading_spaces(lines.get(l.sl).copied().unwrap_or(""))
4762 );
4763 let new_text = format!(
4764 "{}{}",
4765 missing
4766 .iter()
4767 .map(|x| format!("{indent}(v: {x}) => null\n"))
4768 .collect::<String>(),
4769 &indent[4..]
4770 );
4771 out.push(action_json(
4772 format!(
4773 "add the missing arm{}: {}",
4774 if missing.len() > 1 { "s" } else { "" },
4775 missing.join(", ")
4776 ),
4777 "quickfix",
4778 Some(value_to_j(d)),
4779 true,
4780 uri,
4781 insert_json(at, &new_text),
4782 ));
4783 }
4784 }
4785 }
4786 }
4787 if let Some(cap) = re_ctx_undeclared.captures(&message) {
4788 let (variable, type_name) = (cap[1].to_string(), cap[2].to_string());
4790 if let Some(site) = site_of_target(st, &a, resolve_in(&m.env, &type_name).as_ref()) {
4791 let sm = site.module.clone();
4792 if let Some((members, body_loc, _)) = decl_by_id(&sm, site.decl)
4793 .and_then(record_body_of)
4794 .and_then(record_parts)
4795 {
4796 let bound = if variable == "$key" {
4797 "string"
4798 } else {
4799 "ref<{ ... }>"
4800 };
4801 let first = members.first().and_then(|x| x.loc());
4802 let at = match first {
4803 Some(l) => Pos {
4804 line: l.sl,
4805 character: l.sc,
4806 },
4807 None => Pos {
4808 line: body_loc.sl,
4809 character: body_loc.sc + 1,
4810 },
4811 };
4812 let new_text = match first {
4813 Some(l) if l.sl > body_loc.sl => {
4814 format!("{variable}: {bound}\n{}", " ".repeat(l.sc))
4815 }
4816 Some(_) => format!("{variable}: {bound}, "),
4817 None => format!(" {variable}: {bound}, "),
4818 };
4819 out.push(action_json(
4820 format!("declare {variable}: {bound} on {type_name}"),
4821 "quickfix",
4822 Some(value_to_j(d)),
4823 true,
4824 &uri_of(&sm.path),
4825 insert_json(at, &new_text),
4826 ));
4827 }
4828 }
4829 }
4830 if let Some(cap) = re_ctx_ref.captures(&message) {
4831 let (variable, type_name) = (cap[1].to_string(), cap[2].to_string());
4833 if let Some(site) = site_of_target(st, &a, resolve_in(&m.env, &type_name).as_ref()) {
4834 let sm = site.module.clone();
4835 let tl = decl_by_id(&sm, site.decl)
4836 .and_then(record_body_of)
4837 .map(record_members)
4838 .and_then(|members| {
4839 members.iter().find_map(|x| match x {
4840 MemberAst::Context {
4841 variable: v, ty, ..
4842 } if *v == variable => ty.loc(),
4843 _ => None,
4844 })
4845 });
4846 if let Some(tl) = tl {
4847 let src = src_of(&text_of(st, &sm), tl);
4848 out.push(action_json(
4849 format!("declare {variable} as ref<{src}>"),
4850 "quickfix",
4851 Some(value_to_j(d)),
4852 true,
4853 &uri_of(&sm.path),
4854 edit_json(tl, &format!("ref<{src}>")),
4855 ));
4856 }
4857 }
4858 }
4859 if let Some(cap) = re_override.captures(&message) {
4860 let (member, type_name) = (cap[1].to_string(), cap[2].to_string());
4862 if let Some(site) = site_of_target(st, &a, resolve_in(&m.env, &type_name).as_ref()) {
4863 let sm = site.module.clone();
4864 if let Some(decl) = decl_by_id(&sm, site.decl) {
4865 let own = record_body_of(decl)
4866 .map(record_members)
4867 .and_then(|members| {
4868 members
4869 .iter()
4870 .find(|x| x.name() == Some(member.as_str()) && x.loc().is_some())
4871 })
4872 .and_then(|x| x.loc());
4873 let base = match &decl.body {
4874 DeclBody::Type {
4875 ty: TypeAst::Named { name, .. },
4876 ..
4877 } => Some(name.clone()),
4878 _ => None,
4879 };
4880 let parent = base.and_then(|b| {
4881 member_site(st, &a, &sm, safe_resolve(&sm, &b).as_ref(), &member)
4882 });
4883 if let (Some(own_loc), Some(parent)) = (own, parent) {
4884 if let Some(pl) = parent.member_loc {
4885 let parent_text = trailing_comma
4886 .replace(&src_of(&text_of(st, &parent.module), pl), "")
4887 .to_string();
4888 out.push(action_json(
4889 format!("use the parent's declaration: {parent_text}"),
4890 "quickfix",
4891 Some(value_to_j(d)),
4892 true,
4893 &uri_of(&sm.path),
4894 edit_json(own_loc, &parent_text),
4895 ));
4896 }
4897 }
4898 }
4899 }
4900 }
4901 if let Some(cap) = re_union.captures(&message) {
4902 let uname = cap[1].to_string();
4904 if let Some(site) = site_of_target(st, &a, resolve_in(&m.env, &uname).as_ref()) {
4905 let sm = site.module.clone();
4906 let arms: Vec<String> = match decl_by_id(&sm, site.decl).map(|d| &d.body) {
4907 Some(DeclBody::Type {
4908 ty: TypeAst::Union { arms, .. },
4909 ..
4910 }) => arms
4911 .iter()
4912 .filter_map(|t| match t {
4913 TypeAst::Named { name, .. } => Some(name.clone()),
4914 _ => None,
4915 })
4916 .collect(),
4917 _ => vec![],
4918 };
4919 let mut changes: Vec<(String, Vec<J>)> = vec![];
4920 for arm in arms {
4921 let Some(as_) = site_of_target(st, &a, resolve_in(&sm.env, &arm).as_ref())
4922 else {
4923 continue;
4924 };
4925 let am = as_.module.clone();
4926 let Some((members, body_loc, _)) = decl_by_id(&am, as_.decl)
4927 .and_then(record_body_of)
4928 .and_then(record_parts)
4929 else {
4930 continue;
4931 };
4932 if members.iter().any(|x| x.name() == Some("kind")) {
4933 continue;
4934 }
4935 let first = members.first().and_then(|x| x.loc());
4936 let at = match first {
4937 Some(l) => Pos {
4938 line: l.sl,
4939 character: l.sc,
4940 },
4941 None => Pos {
4942 line: body_loc.sl,
4943 character: body_loc.sc + 1,
4944 },
4945 };
4946 let new_text = match first {
4947 Some(l) if l.sl > body_loc.sl => {
4948 format!("kind: \"{arm}\"\n{}", " ".repeat(l.sc))
4949 }
4950 Some(_) => format!("kind: \"{arm}\", "),
4951 None => format!(" kind: \"{arm}\", "),
4952 };
4953 let u = uri_of(&am.path);
4954 match changes.iter_mut().find(|(k, _)| *k == u) {
4955 Some((_, v)) => v.push(insert_json(at, &new_text)),
4956 None => changes.push((u, vec![insert_json(at, &new_text)])),
4957 }
4958 }
4959 if !changes.is_empty() {
4960 out.push(J::obj(vec![
4961 (
4962 "title",
4963 J::s(format!("add a discriminant `kind` to the arms of {uname}")),
4964 ),
4965 ("kind", J::s("quickfix")),
4966 ("diagnostics", J::Arr(vec![value_to_j(d)])),
4967 ("isPreferred", J::Bool(true)),
4968 (
4969 "edit",
4970 J::obj(vec![(
4971 "changes",
4972 J::Obj(changes.into_iter().map(|(k, v)| (k, J::Arr(v))).collect()),
4973 )]),
4974 ),
4975 ]));
4976 }
4977 }
4978 }
4979 if let Some(cap) = re_restated.captures(&message) {
4980 let member = cap[1].to_string();
4982 for decl in &m.decls {
4983 let Some(members) = record_body_of(decl).map(record_members) else {
4984 continue;
4985 };
4986 let Some(own) = members.iter().find(|x| matches!(x, MemberAst::Derived { name, ty: Some(_), loc: Some(_), .. } if *name == member)) else { continue };
4987 let r = member_range(&text, own, &member);
4988 out.push(action_json(
4989 format!(
4990 "make {}.{member} defaulted (x?: T = e)",
4991 decl.name().unwrap_or("")
4992 ),
4993 "quickfix",
4994 Some(value_to_j(d)),
4995 false,
4996 uri,
4997 insert_json(
4998 Pos {
4999 line: r.el,
5000 character: r.ec,
5001 },
5002 "?",
5003 ),
5004 ));
5005 }
5006 }
5007 if let Some(cap) = re_missing.captures(&message) {
5008 let name = cap[1].to_string();
5009 let Some(hit) = node_at(&parsed.decls, dpos) else {
5011 continue;
5012 };
5013 let chain = chain_of(&hit);
5014 let mut obj: Option<Rc<Expr>> = chain_expr(&chain, |x| matches!(x, Expr::Obj(_)));
5015 if obj.is_none() {
5016 obj = chain
5017 .iter()
5018 .filter_map(|n| match n {
5019 NodeRef::Decl(d) => Some(*d),
5020 _ => None,
5021 })
5022 .filter_map(|d| match &d.body {
5023 DeclBody::Output { expr, .. } => Some(expr.clone()),
5024 DeclBody::Input { fallback, .. } => fallback.clone(),
5025 DeclBody::Const { expr, .. } => Some(expr.clone()),
5026 _ => None,
5027 })
5028 .find(|e| matches!(&**e, Expr::Obj(_)));
5029 }
5030 let Some(obj) = obj else { continue };
5031 let owner = chain.iter().find_map(|n| match n {
5033 NodeRef::Decl(d) => match &d.body {
5034 DeclBody::Output { ty, .. } | DeclBody::Input { ty, .. } => Some(ty.clone()),
5035 _ => None,
5036 },
5037 _ => None,
5038 });
5039 let rt: Option<RT> = match owner {
5040 Some(ty) => m.env.resolve(&ty, None).ok(),
5041 None => t.types.get(&key_of(&obj)).and_then(|x| x.rt.clone()),
5042 };
5043 let mem = rt
5044 .as_ref()
5045 .and_then(|r| rec_members(r).into_iter().find(|x| x.name == name));
5046 let value = placeholder_for(mem.as_ref().and_then(|x| x.ty.as_ref()));
5047 let Expr::Obj(entries) = &*obj else { continue };
5048 let edit = match entries.last().and_then(|(_, v)| expr_loc(v)) {
5049 Some(vl) => insert_json(
5050 Pos {
5051 line: vl.el,
5052 character: vl.ec,
5053 },
5054 &format!(", {name}: {value}"),
5055 ),
5056 None => {
5057 let ol = expr_loc(&obj).unwrap();
5058 insert_json(
5059 Pos {
5060 line: ol.sl,
5061 character: ol.sc + 1,
5062 },
5063 &format!(" {name}: {value}"),
5064 )
5065 }
5066 };
5067 out.push(action_json(
5068 format!("add {name}: {value}"),
5069 "quickfix",
5070 Some(value_to_j(d)),
5071 true,
5072 uri,
5073 edit,
5074 ));
5075 }
5076 }
5077 let Some(hit) = node_at(&parsed.decls, range.0) else {
5079 return J::Arr(out);
5080 };
5081 let chain = chain_of(&hit);
5082 let mut one = |title: String, kind: &str, edits: Vec<J>| {
5083 out.push(J::obj(vec![
5084 ("title", J::s(title)),
5085 ("kind", J::s(kind)),
5086 (
5087 "edit",
5088 J::obj(vec![(
5089 "changes",
5090 J::Obj(vec![(uri.to_string(), J::Arr(edits))]),
5091 )]),
5092 ),
5093 ]))
5094 };
5095 for n in &chain {
5097 let (expr, r, hidden) = match n {
5098 NodeRef::Member(
5099 mm @ MemberAst::Derived {
5100 name,
5101 ty: None,
5102 expr,
5103 hidden,
5104 ..
5105 },
5106 ) => (expr.clone(), member_range(&text, mm, name), *hidden),
5107 NodeRef::Decl(d) => match &d.body {
5108 DeclBody::Const {
5109 name,
5110 ty: None,
5111 expr,
5112 } => (expr.clone(), name_range(&text, d, name), false),
5113 _ => continue,
5114 },
5115 _ => continue,
5116 };
5117 if let Some(rt) = t.types.get(&key_of(&expr)).and_then(|x| x.rt.clone()) {
5118 let tt = type_text(Some(&rt));
5119 let at = Pos {
5120 line: r.el,
5121 character: r.ec + if hidden { 1 } else { 0 },
5122 };
5123 one(
5124 format!("annotate: {tt}"),
5125 "refactor.rewrite",
5126 vec![insert_json(at, &format!(": {tt}"))],
5127 );
5128 }
5129 break;
5130 }
5131 let member = chain.iter().find_map(|n| match n {
5133 NodeRef::Member(
5134 mm @ (MemberAst::Derived { loc: Some(_), .. } | MemberAst::Value { loc: Some(_), .. }),
5135 ) => Some(*mm),
5136 _ => None,
5137 });
5138 if let Some(mm) = member {
5139 let name = mm.name().unwrap_or("");
5140 let r = member_range(&text, mm, name);
5141 let after_name = Pos {
5142 line: r.el,
5143 character: r.ec,
5144 };
5145 let remove_next = edit_json(
5146 Loc {
5147 sl: r.el,
5148 sc: r.ec,
5149 el: r.el,
5150 ec: r.ec + 1,
5151 },
5152 "",
5153 );
5154 match mm {
5155 MemberAst::Derived { hidden, ty, .. } => {
5156 if *hidden {
5157 one(
5158 "make visible (derived)".into(),
5159 "refactor.rewrite",
5160 vec![remove_next.clone()],
5161 );
5162 } else {
5163 one(
5164 "make hidden (x$)".into(),
5165 "refactor.rewrite",
5166 vec![insert_json(after_name, "$")],
5167 );
5168 }
5169 if ty.is_some() {
5170 one(
5171 "make defaulted (x?: T = e)".into(),
5172 "refactor.rewrite",
5173 vec![insert_json(after_name, "?")],
5174 );
5175 }
5176 }
5177 MemberAst::Value { dflt: Some(_), .. } => one(
5178 "make derived (x: T = e)".into(),
5179 "refactor.rewrite",
5180 vec![remove_next],
5181 ),
5182 MemberAst::Value { opt: true, .. } => one(
5183 "make required".into(),
5184 "refactor.rewrite",
5185 vec![remove_next],
5186 ),
5187 _ => one(
5188 "make optional".into(),
5189 "refactor.rewrite",
5190 vec![insert_json(after_name, "?")],
5191 ),
5192 }
5193 }
5194 let decl = chain.iter().find_map(|n| match n {
5196 NodeRef::Decl(d) => Some(*d),
5197 _ => None,
5198 });
5199 if let Some(d) = decl {
5200 if let (Some(l), Some(name), false) = (d.loc, d.name(), d.exported) {
5201 if !matches!(d.body, DeclBody::Import { .. } | DeclBody::ReExport { .. }) {
5202 one(
5203 format!("export {name}"),
5204 "refactor.rewrite",
5205 vec![insert_json(
5206 Pos {
5207 line: l.sl,
5208 character: l.sc,
5209 },
5210 "export ",
5211 )],
5212 );
5213 }
5214 }
5215 if let DeclBody::Type { name, ty, .. } = &d.body {
5216 if let Ok(rt) = m.env.resolve(ty, None) {
5217 if is_rec(&rt) {
5218 let req: Vec<String> = rec_members(&rt)
5219 .iter()
5220 .filter(|x| x.kind == MKind::Req)
5221 .map(|x| format!("{}: {}", x.name, placeholder_for(x.ty.as_ref())))
5222 .collect();
5223 let last = lines.last().copied().unwrap_or("");
5224 let end = Pos {
5225 line: lines.len() - 1,
5226 character: u16len(last),
5227 };
5228 let lead = if last.is_empty() { "" } else { "\n" };
5229 let mut chars = name.chars();
5230 let lower = match chars.next() {
5231 Some(c) => format!("{}{}", c.to_lowercase(), chars.as_str()),
5232 None => String::new(),
5233 };
5234 one(
5235 format!("generate an output of {name}"),
5236 "refactor.rewrite",
5237 vec![insert_json(
5238 end,
5239 &format!(
5240 "{lead}output {lower}: {name} = {{ {}{}}}\n",
5241 req.join(", "),
5242 if req.is_empty() { "" } else { " " }
5243 ),
5244 )],
5245 );
5246 one(
5247 format!("generate an input of {name}"),
5248 "refactor.rewrite",
5249 vec![insert_json(end, &format!("{lead}input {lower}: {name}\n"))],
5250 );
5251 }
5252 }
5253 }
5254 }
5255 if !diagnostics.is_empty()
5256 && !lines
5257 .first()
5258 .copied()
5259 .unwrap_or("")
5260 .starts_with("// @expect-")
5261 {
5262 let first = diagnostics
5263 .iter()
5264 .find(|d| matches!(get(d, "severity"), Some(Value::Int(i)) if i.to_string() == "1"))
5265 .unwrap_or(&diagnostics[0]);
5266 let code = match get(first, "code") {
5267 Some(Value::Str(c)) => c.clone(),
5268 Some(Value::Int(i)) => i.to_string(),
5269 Some(Value::Float(f)) => crate::semantics::js_num_str(*f),
5270 _ => String::new(),
5271 };
5272 let assert_id = Regex::new(r"^[A-Z][A-Za-z0-9_]*\.").unwrap();
5273 let phase = if code.starts_with("E1") || code.starts_with("E2") {
5274 "parsing"
5275 } else if code.starts_with("E5") || code.starts_with("E6") || assert_id.is_match(&code) {
5276 "binding"
5277 } else {
5278 "checking"
5279 };
5280 one(
5281 "generate the fixture header (@expect-phase / @expect-error)".into(),
5282 "refactor.rewrite",
5283 vec![insert_json(
5284 Pos {
5285 line: 0,
5286 character: 0,
5287 },
5288 &format!("// @expect-phase: {phase}\n// @expect-error: {code}\n"),
5289 )],
5290 );
5291 }
5292 if let Some(obj) = chain_expr(&chain, |x| matches!(x, Expr::Obj(_))) {
5294 if let (Some(ol), Expr::Obj(entries)) = (expr_loc(&obj), &*obj) {
5295 let owner = chain.iter().find_map(|n| match n {
5296 NodeRef::Decl(d) => match &d.body {
5297 DeclBody::Output { ty, expr, .. } if Rc::ptr_eq(expr, &obj) => Some(ty.clone()),
5298 DeclBody::Input { ty, .. } => Some(ty.clone()),
5299 _ => None,
5300 },
5301 _ => None,
5302 });
5303 let owner = match (
5305 &owner,
5306 chain.iter().find_map(|n| match n {
5307 NodeRef::Decl(d) => Some(*d),
5308 _ => None,
5309 }),
5310 ) {
5311 (Some(_), Some(d)) if matches!(&d.body, DeclBody::Input { .. }) => None,
5312 _ => owner,
5313 };
5314 let rt: Option<RT> = match owner {
5315 Some(ty) => m.env.resolve(&ty, None).ok(),
5316 None => t.types.get(&key_of(&obj)).and_then(|x| x.rt.clone()),
5317 };
5318 if let Some(rt) = rt.filter(is_rec) {
5319 let have: Vec<&str> = entries.iter().map(|(k, _)| k.as_str()).collect();
5320 let missing: Vec<crate::semantics::Member> = rec_members(&rt)
5321 .into_iter()
5322 .filter(|x| x.kind == MKind::Req && !have.contains(&x.name.as_str()))
5323 .collect();
5324 if !missing.is_empty() {
5325 let fill = missing
5326 .iter()
5327 .map(|x| format!("{}: {}", x.name, placeholder_for(x.ty.as_ref())))
5328 .collect::<Vec<_>>()
5329 .join(", ");
5330 let edit = match entries.last().and_then(|(_, v)| expr_loc(v)) {
5331 Some(vl) => insert_json(
5332 Pos {
5333 line: vl.el,
5334 character: vl.ec,
5335 },
5336 &format!(", {fill}"),
5337 ),
5338 None => insert_json(
5339 Pos {
5340 line: ol.sl,
5341 character: ol.sc + 1,
5342 },
5343 &format!(" {fill}"),
5344 ),
5345 };
5346 one(
5347 format!(
5348 "fill the required members: {}",
5349 missing
5350 .iter()
5351 .map(|x| x.name.clone())
5352 .collect::<Vec<_>>()
5353 .join(", ")
5354 ),
5355 "refactor.rewrite",
5356 vec![edit],
5357 );
5358 }
5359 }
5360 }
5361 }
5362 if let Some(d) = decl {
5364 if let (DeclBody::Const { name, expr, .. }, Some(dl), Some(el)) =
5365 (&d.body, d.loc, d.body.const_expr_loc())
5366 {
5367 let nr = name_range(&text, d, name);
5368 let refs: Vec<Loc> = references(
5369 st,
5370 uri,
5371 Pos {
5372 line: nr.sl,
5373 character: nr.sc,
5374 },
5375 false,
5376 )
5377 .into_iter()
5378 .filter(|(rm, _)| rm.path == m.path)
5379 .map(|(_, l)| l)
5380 .collect();
5381 if !refs.is_empty() {
5382 let src = src_of(&text, el);
5383 let plain = matches!(
5384 &**expr,
5385 Expr::Name(_)
5386 | Expr::Lit(_)
5387 | Expr::UnitLit { .. }
5388 | Expr::Call { .. }
5389 | Expr::Member { .. }
5390 | Expr::Paren(_)
5391 );
5392 let new_text = if plain {
5393 src.clone()
5394 } else {
5395 format!("({src})")
5396 };
5397 let mut edits: Vec<J> = refs.iter().map(|l| edit_json(*l, &new_text)).collect();
5398 edits.push(edit_json(
5399 Loc {
5400 sl: dl.sl,
5401 sc: 0,
5402 el: dl.el + 1,
5403 ec: 0,
5404 },
5405 "",
5406 ));
5407 one(format!("inline {name}"), "refactor.inline", edits);
5408 }
5409 }
5410 }
5411 let own_body: Option<*const TypeAst> =
5413 decl.and_then(record_body_of).map(|t| t as *const TypeAst);
5414 let inline_record: Option<Loc> = chain
5415 .iter()
5416 .find_map(|n| match n {
5417 NodeRef::Type(t @ TypeAst::Record { loc: Some(l), .. })
5418 if own_body != Some(*t as *const TypeAst) =>
5419 {
5420 Some(*l)
5421 }
5422 _ => None,
5423 })
5424 .or_else(|| {
5425 chain.iter().find_map(|n| match n {
5426 NodeRef::Member(MemberAst::Value {
5427 ty: TypeAst::Record { loc: Some(l), .. },
5428 ..
5429 }) => Some(*l),
5430 _ => None,
5431 })
5432 });
5433 if let (Some(rl), Some(dl)) = (inline_record, decl.and_then(|d| d.loc)) {
5434 one(
5435 "extract to a named type".into(),
5436 "refactor.extract",
5437 vec![
5438 insert_json(
5439 Pos {
5440 line: dl.sl,
5441 character: 0,
5442 },
5443 &format!("type Extracted = {}\n", src_of(&text, rl)),
5444 ),
5445 edit_json(rl, "Extracted"),
5446 ],
5447 );
5448 }
5449 if let Some(ul) = chain_expr(&chain, |x| matches!(x, Expr::UnitLit { .. })) {
5451 if let (Expr::UnitLit { num, unit }, Some(l)) = (&*ul, expr_loc(&ul)) {
5452 if let Ok((key, to_base)) = m.env.unit_info(unit) {
5453 let base = m
5454 .env
5455 .base_unit_of
5456 .borrow()
5457 .get(&key)
5458 .cloned()
5459 .unwrap_or(key);
5460 if base != *unit {
5461 let converted =
5462 format!("{}{base}", crate::semantics::js_num_str(num * to_base));
5463 one(
5464 format!("convert to {converted}"),
5465 "refactor.rewrite",
5466 vec![edit_json(l, &converted)],
5467 );
5468 }
5469 }
5470 }
5471 }
5472 let type_decl = chain.iter().find_map(|n| match n {
5474 NodeRef::Decl(d) if matches!(d.body, DeclBody::Type { .. }) => Some(*d),
5475 _ => None,
5476 });
5477 if let Some((members, bl, open)) = type_decl.and_then(record_body_of).and_then(record_parts) {
5478 if members.len() > 1 && members.iter().all(|x| x.loc().is_some()) {
5479 let rank = |x: &MemberAst| match x {
5480 MemberAst::Context { .. } => 0,
5481 MemberAst::Value { dflt: Some(_), .. } => 3,
5482 MemberAst::Value { opt: true, .. } => 2,
5483 MemberAst::Value { .. } => 1,
5484 MemberAst::Derived { hidden: true, .. } => 5,
5485 MemberAst::Derived { .. } => 4,
5486 _ => 6,
5487 };
5488 let mut sorted: Vec<(usize, &MemberAst)> = members.iter().enumerate().collect();
5489 sorted.sort_by_key(|(i, x)| (rank(x), *i));
5490 if sorted.iter().enumerate().any(|(i, (j, _))| i != *j) {
5491 let lead = leading_spaces(lines.get(bl.sl).copied().unwrap_or(""));
5492 let indent = format!("{lead} ");
5493 let body: Vec<String> = sorted
5494 .iter()
5495 .map(|(_, x)| {
5496 format!(
5497 "{indent}{}",
5498 trailing_comma.replace(&src_of(&text, x.loc().unwrap()), "")
5499 )
5500 })
5501 .collect();
5502 let trailing = if open {
5503 format!("\n{indent}...")
5504 } else {
5505 String::new()
5506 };
5507 one(
5508 "reorder the members canonically".into(),
5509 "refactor.rewrite",
5510 vec![edit_json(
5511 bl,
5512 &format!("{{\n{}{trailing}\n{lead}}}", body.join("\n")),
5513 )],
5514 );
5515 }
5516 }
5517 }
5518 let assert_member = chain.iter().find_map(|n| match n {
5520 NodeRef::Member(mm @ MemberAst::Assert { loc: Some(_), .. }) => Some(*mm),
5521 _ => None,
5522 });
5523 if let (
5524 Some(MemberAst::Assert {
5525 name: aname,
5526 tail: Some(tail),
5527 loc: Some(al),
5528 ..
5529 }),
5530 Some(tdl),
5531 ) = (assert_member, type_decl.and_then(|d| d.loc))
5532 {
5533 let asrc = src_of(&text, *al);
5534 if let Some(em) = Regex::new(r"\belse\b").unwrap().find(&asrc) {
5535 let (mut line, mut col) = (al.sl, al.sc);
5536 for u in asrc[..em.start()].encode_utf16() {
5537 if u == '\n' as u16 {
5538 line += 1;
5539 col = 0;
5540 } else {
5541 col += 1;
5542 }
5543 }
5544 let tail_loc = Loc {
5545 sl: line,
5546 sc: col,
5547 el: al.el,
5548 ec: al.ec,
5549 };
5550 match tail {
5551 Tail::Inline { severity, template } => {
5552 let mut names: Vec<String> = vec![];
5554 let mut params: Vec<String> = vec![];
5555 for part in template {
5556 if let TPart::Expr(e) = part {
5557 for n in exprs_under(&NodeRef::Expr(e)) {
5558 if let Expr::Name(nm) = &*n {
5559 if !names.contains(nm) {
5560 names.push(nm.clone());
5561 let ty = t
5562 .types
5563 .get(&key_of(&n))
5564 .and_then(|x| x.rt.as_ref())
5565 .map(|rt| crate::infer::type_text(Some(rt)))
5566 .unwrap_or_else(|| "any".to_string());
5567 params.push(format!("{nm}: {ty}"));
5568 }
5569 }
5570 }
5571 }
5572 }
5573 let tmpl = template
5574 .iter()
5575 .map(|p| match p {
5576 TPart::Text(s) => s.clone(),
5577 TPart::Expr(e) => format!("${{{}}}", crate::session::expr_text(e)),
5578 })
5579 .collect::<String>();
5580 one(format!("declare a diagnostic for {aname}"), "refactor.extract", vec![
5581 insert_json(Pos { line: tdl.sl, character: 0 }, &format!("diagnostic {aname}({}) {{\n severity = {severity}\n message = `{tmpl}`\n}}\n", params.join(", "))),
5582 edit_json(tail_loc, &format!("else {aname}({})", names.join(", "))),
5583 ]);
5584 }
5585 Tail::Ref { name: dname, args } => {
5586 let dd = m.env.diags.borrow().get(dname).cloned();
5587 let declared = m.decls.iter().any(
5588 |d| matches!(&d.body, DeclBody::Diagnostic { name, .. } if name == dname),
5589 );
5590 if let (Some(dd), true) = (dd, declared) {
5591 let arg_text: Vec<String> = args
5592 .iter()
5593 .map(|a| {
5594 expr_loc(a)
5595 .map(|l| src_of(&text, l))
5596 .unwrap_or_else(|| crate::session::expr_text(a))
5597 })
5598 .collect();
5599 let mut message = String::from("`");
5600 for p in &dd.template {
5601 match p {
5602 TPart::Text(s) => message.push_str(s),
5603 TPart::Expr(e) => {
5604 let i = match &**e {
5605 Expr::Name(nm) => {
5606 dd.params.iter().position(|q| &q.name == nm)
5607 }
5608 _ => None,
5609 };
5610 message.push_str("${");
5611 message.push_str(
5612 &i.and_then(|i| arg_text.get(i).cloned())
5613 .unwrap_or_else(|| crate::session::expr_text(e)),
5614 );
5615 message.push('}');
5616 }
5617 }
5618 }
5619 message.push('`');
5620 one(
5621 format!("inline the diagnostic {dname}"),
5622 "refactor.inline",
5623 vec![edit_json(
5624 tail_loc,
5625 &format!("else {} {message}", dd.severity),
5626 )],
5627 );
5628 }
5629 }
5630 }
5631 }
5632 }
5633 if let Some(if_expr) = chain_expr(&chain, |x| matches!(x, Expr::If { .. })) {
5635 if let Some(il) = expr_loc(&if_expr) {
5636 let mut arms: Vec<(String, Rc<Expr>)> = vec![];
5638 let mut subject: Option<Rc<Expr>> = None;
5639 let mut member: Option<String> = None;
5640 let mut tail_expr: Option<Rc<Expr>> = None;
5641 let mut ok = true;
5642 let mut cur = if_expr.clone();
5643 loop {
5644 let (c, tt, f) = match &*cur {
5645 Expr::If { c, t, f } => (c.clone(), t.clone(), f.clone()),
5646 _ => {
5647 tail_expr = Some(cur.clone());
5648 break;
5649 }
5650 };
5651 let mut matched = false;
5652 if let Expr::Bin { op, l, r } = &*c {
5653 if op == "==" {
5654 if let (Expr::Member { x, name, .. }, Expr::Lit(Value::Str(lit))) =
5655 (&**l, &**r)
5656 {
5657 match &subject {
5658 None => {
5659 subject = Some(x.clone());
5660 member = Some(name.clone());
5661 }
5662 Some(s) => {
5663 if crate::session::expr_text(s) != crate::session::expr_text(x)
5664 || member.as_deref() != Some(name.as_str())
5665 {
5666 ok = false;
5667 break;
5668 }
5669 }
5670 }
5671 arms.push((lit.clone(), tt.clone()));
5672 matched = true;
5673 }
5674 }
5675 }
5676 if !matched {
5677 ok = false;
5678 break;
5679 }
5680 cur = f;
5681 }
5682 let srt = subject
5683 .as_ref()
5684 .and_then(|s| t.types.get(&key_of(s)))
5685 .and_then(|x| x.rt.clone());
5686 if let (true, false, Some(subj), Some(mem), Some(srt)) = (
5687 ok,
5688 arms.is_empty(),
5689 subject.as_ref(),
5690 member.as_ref(),
5691 srt.as_ref(),
5692 ) {
5693 if let RTk::Union(uarms) = &srt.k {
5694 let arm_name = |lit: &str| -> Option<String> {
5695 uarms.iter()
5696 .find(|r| matches!(r.k, RTk::Rec(_)) && rec_members(r).iter().any(|mm| &mm.name == mem && matches!(mm.ty.as_ref().map(|t| &t.k), Some(RTk::Lit(Value::Str(v))) if v == lit)))
5697 .and_then(|r| r.name.borrow().clone())
5698 };
5699 let names: Vec<Option<String>> =
5700 arms.iter().map(|(lit, _)| arm_name(lit)).collect();
5701 if names.iter().all(|n| n.is_some())
5702 && arms.iter().all(|(_, b)| expr_loc(b).is_some())
5703 {
5704 let lead = leading_spaces(lines.get(il.sl).copied().unwrap_or(""));
5705 let indent = format!("{lead} ");
5706 let subj_text = crate::session::expr_text(subj);
5707 let v: String = match &**subj {
5708 Expr::Name(n) => n
5709 .chars()
5710 .next()
5711 .map(|c| c.to_string())
5712 .unwrap_or_else(|| "v".into()),
5713 _ => "v".into(),
5714 };
5715 let subj_re =
5716 Regex::new(&format!(r"\b{}\b", regex::escape(&subj_text))).unwrap();
5717 let mut cases: Vec<String> = arms
5718 .iter()
5719 .zip(names.iter())
5720 .map(|((_, body), n)| {
5721 format!(
5722 "{indent}({v}: {}) => {}",
5723 n.as_ref().unwrap(),
5724 subj_re.replace_all(
5725 &src_of(&text, expr_loc(body).unwrap()),
5726 v.as_str()
5727 )
5728 )
5729 })
5730 .collect();
5731 if let Some(tl) = tail_expr.as_ref().and_then(expr_loc) {
5732 cases.push(format!("{indent}(other) => {}", src_of(&text, tl)));
5733 }
5734 one(
5735 "convert to match".into(),
5736 "refactor.rewrite",
5737 vec![edit_json(
5738 il,
5739 &format!("match {subj_text} {{\n{}\n{lead}}}", cases.join("\n")),
5740 )],
5741 );
5742 }
5743 }
5744 }
5745 }
5746 }
5747 if let Some(mx) = chain_expr(&chain, |x| matches!(x, Expr::Match { .. })) {
5748 if let (Expr::Match { subject, arms }, Some(ml)) = (&*mx, expr_loc(&mx)) {
5749 let srt = t.types.get(&key_of(subject)).and_then(|x| x.rt.clone());
5751 let recs: Vec<RT> = match srt.as_ref().map(|r| &r.k) {
5752 Some(RTk::Union(us)) => us
5753 .iter()
5754 .filter(|r| matches!(r.k, RTk::Rec(_)))
5755 .cloned()
5756 .collect(),
5757 _ => vec![],
5758 };
5759 let is_lit = |m: &crate::semantics::Member| {
5760 matches!(m.ty.as_ref().map(|t| &t.k), Some(RTk::Lit(_)))
5761 };
5762 let disc: Option<String> = recs.first().and_then(|r0| {
5763 rec_members(r0)
5764 .into_iter()
5765 .find(|mm| {
5766 is_lit(mm)
5767 && recs.iter().all(|r| {
5768 rec_members(r)
5769 .iter()
5770 .any(|x| x.name == mm.name && is_lit(x))
5771 })
5772 })
5773 .map(|mm| mm.name)
5774 });
5775 if let Some(disc) = disc {
5776 let subj_text = crate::session::expr_text(subject);
5777 let mut parts: Vec<String> = vec![];
5778 let mut fallback: Option<String> = None;
5779 let mut ok = true;
5780 for arm in arms {
5781 let Some(bl) = expr_loc(&arm.body) else {
5782 ok = false;
5783 break;
5784 };
5785 let var_re = Regex::new(&format!(r"\b{}\b", regex::escape(&arm.v))).unwrap();
5786 let body = var_re
5787 .replace_all(&src_of(&text, bl), subj_text.as_str())
5788 .to_string();
5789 let rec = match &arm.ty {
5790 Some(TypeAst::Named { name, .. }) => recs
5791 .iter()
5792 .find(|r| r.name.borrow().as_deref() == Some(name.as_str())),
5793 _ => None,
5794 };
5795 let lit = rec
5796 .and_then(|r| rec_members(r).into_iter().find(|x| x.name == disc))
5797 .and_then(|x| x.ty)
5798 .and_then(|ty| match &ty.k {
5799 RTk::Lit(Value::Str(s)) => Some(s.clone()),
5800 _ => None,
5801 });
5802 match lit {
5803 Some(l) => parts.push(format!(
5804 "if {subj_text}.{disc} == {} then {body}",
5805 json_str(&l)
5806 )),
5807 None => fallback = Some(body),
5808 }
5809 }
5810 if ok && !parts.is_empty() {
5811 one(
5812 "convert to if".into(),
5813 "refactor.rewrite",
5814 vec![edit_json(
5815 ml,
5816 &format!(
5817 "{} else {}",
5818 parts.join(" else "),
5819 fallback.unwrap_or_else(|| "null".into())
5820 ),
5821 )],
5822 );
5823 }
5824 }
5825 }
5826 }
5827 let derived = chain.iter().find_map(|n| match n {
5829 NodeRef::Member(
5830 mm @ MemberAst::Derived {
5831 loc: Some(_), expr, ..
5832 },
5833 ) if expr_loc(expr).is_some() => Some(*mm),
5834 _ => None,
5835 });
5836 if let (
5837 Some(
5838 dm @ MemberAst::Derived {
5839 name: dname,
5840 expr: dexpr,
5841 loc: Some(dl),
5842 ..
5843 },
5844 ),
5845 Some((members, _, _)),
5846 ) = (
5847 derived,
5848 type_decl.and_then(record_body_of).and_then(record_parts),
5849 ) {
5850 let mut uses: Vec<Loc> = vec![];
5851 for other in members {
5852 if std::ptr::eq(other, dm) {
5853 continue;
5854 }
5855 for e in exprs_under(&NodeRef::Member(other)) {
5856 if let Expr::Name(nm) = &*e {
5857 if nm == dname {
5858 if let Some(l) = expr_loc(&e) {
5859 uses.push(l);
5860 }
5861 }
5862 }
5863 }
5864 }
5865 if !uses.is_empty() {
5866 let src = src_of(&text, expr_loc(dexpr).unwrap());
5867 let plain = matches!(
5868 &**dexpr,
5869 Expr::Name(_)
5870 | Expr::Lit(_)
5871 | Expr::UnitLit { .. }
5872 | Expr::Call { .. }
5873 | Expr::Member { .. }
5874 | Expr::Paren(_)
5875 );
5876 let wrapped = if plain { src } else { format!("({src})") };
5877 let mut edits: Vec<J> = uses.iter().map(|l| edit_json(*l, &wrapped)).collect();
5878 edits.push(edit_json(
5879 Loc {
5880 sl: dl.sl,
5881 sc: 0,
5882 el: dl.el + 1,
5883 ec: 0,
5884 },
5885 "",
5886 ));
5887 one(format!("inline {dname}"), "refactor.inline", edits);
5888 }
5889 }
5890 let cmp = chain_expr(
5892 &chain,
5893 |x| matches!(x, Expr::Bin { op, l, r } if ["<", ">", "<=", ">=", "==", "!="].contains(&op.as_str()) && expr_loc(l).is_some() && expr_loc(r).is_some()),
5894 );
5895 if let Some(c) = cmp {
5896 if let (Expr::Bin { op, l, r }, Some(cl)) = (&*c, expr_loc(&c)) {
5897 let flipped = match op.as_str() {
5898 "<" => ">",
5899 ">" => "<",
5900 "<=" => ">=",
5901 ">=" => "<=",
5902 other => other,
5903 };
5904 one(
5905 "flip the comparison".into(),
5906 "refactor.rewrite",
5907 vec![edit_json(
5908 cl,
5909 &format!(
5910 "{} {} {}",
5911 src_of(&text, expr_loc(r).unwrap()),
5912 flipped,
5913 src_of(&text, expr_loc(l).unwrap())
5914 ),
5915 )],
5916 );
5917 }
5918 }
5919 let selected = if range.0.line != range.1.line || range.0.character != range.1.character {
5921 chain_expr(&chain, |_| true).and_then(|_| {
5922 chain.iter().find_map(|n| match n {
5923 NodeRef::Expr(e) => expr_loc(e)
5924 .filter(|l| {
5925 l.sl == range.0.line
5926 && l.sc == range.0.character
5927 && l.el == range.1.line
5928 && l.ec == range.1.character
5929 })
5930 .map(|_| (*e).clone()),
5931 _ => None,
5932 })
5933 })
5934 } else {
5935 None
5936 };
5937 if let Some(sel) = selected.filter(|e| !matches!(&**e, Expr::Name(_))) {
5938 let src = src_of(&text, expr_loc(&sel).unwrap());
5939 let enclosing_member = chain.iter().find_map(|n| match n {
5940 NodeRef::Member(mm) if mm.loc().is_some() => Some(*mm),
5941 _ => None,
5942 });
5943 let enclosing_decl = chain.iter().find_map(|n| match n {
5944 NodeRef::Decl(d) => Some(*d),
5945 _ => None,
5946 });
5947 if let (false, Some(dl)) = (mentions_name(&sel), enclosing_decl.and_then(|d| d.loc)) {
5948 one(
5949 "extract to a constant".into(),
5950 "refactor.extract",
5951 vec![
5952 insert_json(
5953 Pos {
5954 line: dl.sl,
5955 character: 0,
5956 },
5957 &format!("const extracted = {src}\n"),
5958 ),
5959 edit_json(expr_loc(&sel).unwrap(), "extracted"),
5960 ],
5961 );
5962 }
5963 if let Some(mm) = enclosing_member.filter(|mm| !matches!(mm, MemberAst::Context { .. })) {
5964 let ml = mm.loc().unwrap();
5965 let indent = leading_spaces(lines.get(ml.sl).copied().unwrap_or(""));
5966 one(
5967 "extract to a derived member".into(),
5968 "refactor.extract",
5969 vec![
5970 insert_json(
5971 Pos {
5972 line: ml.sl,
5973 character: 0,
5974 },
5975 &format!("{indent}extracted = {src}\n"),
5976 ),
5977 edit_json(expr_loc(&sel).unwrap(), "extracted"),
5978 ],
5979 );
5980 }
5981 }
5982 J::Arr(out)
5983}
5984
5985fn binds_name(n: &NodeRef, name: &str) -> bool {
5989 match n {
5990 NodeRef::Expr(e) => match &***e {
5991 Expr::Comp { clauses, .. } | Expr::MapComp { clauses, .. } => {
5992 clauses.iter().any(|c| c.v == name)
5993 }
5994 Expr::Lambda { params, .. } => params.iter().any(|p| p == name),
5995 Expr::Match { arms, .. } => arms.iter().any(|a| a.v == name),
5996 _ => false,
5997 },
5998 NodeRef::Decl(d) => {
5999 matches!(&d.body, DeclBody::Func { params, .. } if params.iter().any(|p| p.name == name))
6000 }
6001 _ => false,
6002 }
6003}
6004fn expr_binds_name(e: &Rc<Expr>, name: &str) -> bool {
6005 binds_name(&NodeRef::Expr(e), name)
6006}
6007fn loc_in(scope: Loc, src: &str, byte: usize, name: &str) -> Loc {
6009 let before = &src[..byte];
6010 let (line, col) = match before.rfind('\n') {
6011 Some(nl) => (
6012 scope.sl + before.matches('\n').count(),
6013 u16len(&before[nl + 1..]),
6014 ),
6015 None => (scope.sl, scope.sc + u16len(before)),
6016 };
6017 Loc {
6018 sl: line,
6019 sc: col,
6020 el: line,
6021 ec: col + u16len(name),
6022 }
6023}
6024fn binding_locs(text: &str, scope: &NodeRef, scope_loc: Loc, name: &str) -> Vec<Loc> {
6025 let src = src_of(text, scope_loc);
6026 let esc = regex::escape(name);
6027 let mut out: Vec<Loc> = vec![];
6028 match scope {
6029 NodeRef::Decl(_) => {
6030 let re = Regex::new(&format!(r"\(([^)]*)(?-u:\b)({esc})(?-u:\b)")).unwrap();
6031 if let Some(m) = re.find(&src) {
6032 out.push(loc_in(scope_loc, &src, m.end() - name.len(), name));
6034 }
6035 }
6036 NodeRef::Expr(e) if matches!(&***e, Expr::Lambda { .. }) => {
6037 let re = Regex::new(&format!(r"(?-u:\b)({esc})(?-u:\b)")).unwrap();
6039 for m in re.find_iter(&src) {
6040 let rest = &src[m.end()..];
6041 let ok = match rest.find('=') {
6042 Some(i) => rest[i..].starts_with("=>"),
6043 None => false,
6044 };
6045 if ok {
6046 out.push(loc_in(scope_loc, &src, m.start(), name));
6047 break;
6048 }
6049 }
6050 }
6051 NodeRef::Expr(e) if matches!(&***e, Expr::Match { .. }) => {
6052 let re = Regex::new(&format!(r"\(\s*({esc})(?-u:\b)")).unwrap();
6053 for m in re.find_iter(&src) {
6054 out.push(loc_in(scope_loc, &src, m.end() - name.len(), name));
6055 }
6056 }
6057 _ => {
6058 let re = Regex::new(&format!(r"(?-u:\b)for\s+({esc})(?-u:\b)")).unwrap();
6059 for m in re.find_iter(&src) {
6060 out.push(loc_in(scope_loc, &src, m.end() - name.len(), name));
6061 }
6062 }
6063 }
6064 out
6065}
6066fn uses_in_expr(e: &Rc<Expr>, name: &str, scope_key: usize, out: &mut Vec<Loc>) {
6068 if key_of(e) != scope_key && expr_binds_name(e, name) {
6069 return;
6070 }
6071 match &**e {
6072 Expr::Name(n) => {
6073 if n == name {
6074 if let Some(l) = expr_loc(e) {
6075 out.push(l);
6076 }
6077 }
6078 }
6079 Expr::Template(parts) => {
6080 for p in parts {
6081 if let TPart::Expr(x) = p {
6082 uses_in_expr(x, name, scope_key, out);
6083 }
6084 }
6085 }
6086 Expr::Obj(entries) => {
6087 for (_, v) in entries {
6088 uses_in_expr(v, name, scope_key, out);
6089 }
6090 }
6091 Expr::Arr(items) => {
6092 for (_, v) in items {
6093 uses_in_expr(v, name, scope_key, out);
6094 }
6095 }
6096 Expr::Comp { head, clauses } => {
6097 uses_in_expr(head, name, scope_key, out);
6098 for c in clauses {
6099 uses_in_expr(&c.iter, name, scope_key, out);
6100 for f in &c.filters {
6101 uses_in_expr(f, name, scope_key, out);
6102 }
6103 }
6104 }
6105 Expr::MapComp { key, val, clauses } => {
6106 uses_in_expr(key, name, scope_key, out);
6107 uses_in_expr(val, name, scope_key, out);
6108 for c in clauses {
6109 uses_in_expr(&c.iter, name, scope_key, out);
6110 for f in &c.filters {
6111 uses_in_expr(f, name, scope_key, out);
6112 }
6113 }
6114 }
6115 Expr::Bin { l, r, .. } => {
6116 uses_in_expr(l, name, scope_key, out);
6117 uses_in_expr(r, name, scope_key, out);
6118 }
6119 Expr::Un { x, .. } | Expr::Paren(x) => uses_in_expr(x, name, scope_key, out),
6120 Expr::If { c, t, f } => {
6121 uses_in_expr(c, name, scope_key, out);
6122 uses_in_expr(t, name, scope_key, out);
6123 uses_in_expr(f, name, scope_key, out);
6124 }
6125 Expr::Lambda { body, .. } => uses_in_expr(body, name, scope_key, out),
6126 Expr::Call { fun, args } => {
6127 uses_in_expr(fun, name, scope_key, out);
6128 for a in args {
6129 uses_in_expr(a, name, scope_key, out);
6130 }
6131 }
6132 Expr::Member { x, .. } => uses_in_expr(x, name, scope_key, out),
6133 Expr::Index { x, i } => {
6134 uses_in_expr(x, name, scope_key, out);
6135 uses_in_expr(i, name, scope_key, out);
6136 }
6137 Expr::With { base, patch } => {
6138 uses_in_expr(base, name, scope_key, out);
6139 uses_in_expr(patch, name, scope_key, out);
6140 }
6141 Expr::Match { subject, arms } => {
6142 uses_in_expr(subject, name, scope_key, out);
6143 for a in arms {
6144 if let Some(t) = &a.ty {
6145 uses_in_type(t, name, scope_key, out);
6146 }
6147 uses_in_expr(&a.body, name, scope_key, out);
6148 }
6149 }
6150 _ => {}
6151 }
6152}
6153fn uses_in_type(t: &TypeAst, name: &str, scope_key: usize, out: &mut Vec<Loc>) {
6154 match t {
6155 TypeAst::Record { members, .. } => {
6156 for m in members {
6157 uses_in_member(m, name, scope_key, out);
6158 }
6159 }
6160 TypeAst::Map { key, val, .. } => {
6161 uses_in_type(key, name, scope_key, out);
6162 uses_in_type(val, name, scope_key, out);
6163 }
6164 TypeAst::Array { elem, .. } => uses_in_type(elem, name, scope_key, out),
6165 TypeAst::Union { arms, .. } | TypeAst::Isect { arms, .. } => {
6166 for a in arms {
6167 uses_in_type(a, name, scope_key, out);
6168 }
6169 }
6170 TypeAst::Func { params, ret, .. } => {
6171 for a in params {
6172 uses_in_type(a, name, scope_key, out);
6173 }
6174 uses_in_type(ret, name, scope_key, out);
6175 }
6176 TypeAst::Named {
6177 args, preds, ext, ..
6178 } => {
6179 for a in args {
6180 uses_in_type(a, name, scope_key, out);
6181 }
6182 for x in preds.iter().flatten() {
6183 uses_in_expr(x, name, scope_key, out);
6184 }
6185 if let Some(x) = ext {
6186 uses_in_type(x, name, scope_key, out);
6187 }
6188 }
6189 _ => {}
6190 }
6191}
6192fn uses_in_member(m: &MemberAst, name: &str, scope_key: usize, out: &mut Vec<Loc>) {
6193 match m {
6194 MemberAst::Value { ty, dflt, .. } => {
6195 uses_in_type(ty, name, scope_key, out);
6196 if let Some(d) = dflt {
6197 uses_in_expr(d, name, scope_key, out);
6198 }
6199 }
6200 MemberAst::Derived { ty, expr, .. } => {
6201 if let Some(t) = ty {
6202 uses_in_type(t, name, scope_key, out);
6203 }
6204 uses_in_expr(expr, name, scope_key, out);
6205 }
6206 MemberAst::Context { ty, .. } => uses_in_type(ty, name, scope_key, out),
6207 MemberAst::Assert { cond, tail, .. } => {
6208 uses_in_expr(cond, name, scope_key, out);
6209 if let Some(t) = tail {
6210 uses_in_tail(t, name, scope_key, out);
6211 }
6212 }
6213 MemberAst::When { cond, body, .. } => {
6214 uses_in_expr(cond, name, scope_key, out);
6215 for b in body {
6216 uses_in_member(b, name, scope_key, out);
6217 }
6218 }
6219 }
6220}
6221fn uses_in_tail(t: &Tail, name: &str, scope_key: usize, out: &mut Vec<Loc>) {
6222 match t {
6223 Tail::Inline { template, .. } => {
6224 for p in template {
6225 if let TPart::Expr(x) = p {
6226 uses_in_expr(x, name, scope_key, out);
6227 }
6228 }
6229 }
6230 Tail::Ref { args, .. } => {
6231 for a in args {
6232 uses_in_expr(a, name, scope_key, out);
6233 }
6234 }
6235 }
6236}
6237fn local_ranges(st: &State, uri: &str, pos: Pos) -> Option<Vec<Loc>> {
6238 let text = st.text(uri)?.clone();
6239 let parsed = parse_source(&text);
6240 if !parsed.errors.is_empty() {
6241 return None;
6242 }
6243 let hit = node_at(&parsed.decls, pos)?;
6244 let mut chain: Vec<NodeRef> = vec![hit.node.clone()];
6245 chain.extend(hit.parents.iter().rev().cloned());
6246 let (name, scope): (String, Option<NodeRef>) = match &hit.node {
6247 NodeRef::Expr(e) if matches!(&***e, Expr::Name(_)) => {
6248 let Expr::Name(n) = &***e else { unreachable!() };
6249 let scope = hit.parents.iter().rev().find(|p| binds_name(p, n)).cloned();
6250 (n.clone(), scope)
6251 }
6252 _ => {
6253 let line = text.split('\n').nth(pos.line).unwrap_or("");
6255 let re = Regex::new(r"[A-Za-z_][A-Za-z0-9_]*").unwrap();
6256 let mut found: Option<String> = None;
6257 for m in re.find_iter(line) {
6258 let a = u16_col(line, m.start());
6259 let b = a + u16len(m.as_str());
6260 if a <= pos.character && pos.character <= b {
6261 found = Some(m.as_str().to_string());
6262 break;
6263 }
6264 }
6265 let n = found?;
6266 let scope = chain.iter().find(|p| binds_name(p, &n)).cloned();
6267 (n, scope)
6268 }
6269 };
6270 let scope = scope?;
6271 let scope_loc = scope.loc()?;
6272 let mut locs = binding_locs(&text, &scope, scope_loc, &name);
6273 match &scope {
6274 NodeRef::Expr(e) => {
6275 let key = key_of(e);
6277 match &***e {
6278 Expr::Comp { head, clauses } => {
6279 uses_in_expr(head, &name, key, &mut locs);
6280 for c in clauses {
6281 uses_in_expr(&c.iter, &name, key, &mut locs);
6282 for f in &c.filters {
6283 uses_in_expr(f, &name, key, &mut locs);
6284 }
6285 }
6286 }
6287 Expr::MapComp {
6288 key: k,
6289 val,
6290 clauses,
6291 } => {
6292 uses_in_expr(k, &name, key, &mut locs);
6293 uses_in_expr(val, &name, key, &mut locs);
6294 for c in clauses {
6295 uses_in_expr(&c.iter, &name, key, &mut locs);
6296 for f in &c.filters {
6297 uses_in_expr(f, &name, key, &mut locs);
6298 }
6299 }
6300 }
6301 Expr::Lambda { body, .. } => uses_in_expr(body, &name, key, &mut locs),
6302 Expr::Match { subject, arms } => {
6303 uses_in_expr(subject, &name, key, &mut locs);
6304 for a in arms {
6305 if let Some(t) = &a.ty {
6306 uses_in_type(t, &name, key, &mut locs);
6307 }
6308 uses_in_expr(&a.body, &name, key, &mut locs);
6309 }
6310 }
6311 _ => {}
6312 }
6313 }
6314 NodeRef::Decl(d) => {
6315 if let DeclBody::Func {
6316 params, ret, body, ..
6317 } = &d.body
6318 {
6319 for p in params {
6320 if let Some(t) = &p.ty {
6321 uses_in_type(t, &name, 0, &mut locs);
6322 }
6323 }
6324 if let Some(t) = ret {
6325 uses_in_type(t, &name, 0, &mut locs);
6326 }
6327 uses_in_expr(body, &name, 0, &mut locs);
6328 }
6329 }
6330 _ => {}
6331 }
6332 let mut seen: Vec<(usize, usize)> = vec![];
6333 let mut out: Vec<Loc> = vec![];
6334 for l in locs {
6335 if !seen.contains(&(l.sl, l.sc)) {
6336 seen.push((l.sl, l.sc));
6337 out.push(l);
6338 }
6339 }
6340 out.sort_by(|p, q| p.sl.cmp(&q.sl).then(p.sc.cmp(&q.sc)));
6341 Some(out)
6342}
6343fn linked_editing_range(st: &State, uri: &str, pos: Pos) -> J {
6344 match local_ranges(st, uri, pos) {
6345 Some(locs) if !locs.is_empty() => J::obj(vec![
6346 (
6347 "ranges",
6348 J::Arr(locs.iter().map(|l| range_json(*l)).collect()),
6349 ),
6350 ("wordPattern", J::s("[A-Za-z_][A-Za-z0-9_]*")),
6351 ]),
6352 _ => J::Null,
6353 }
6354}
6355
6356fn syntax_tree(st: &State, uri: &str) -> J {
6360 let Some(text) = st.text(uri) else {
6361 return J::Null;
6362 };
6363 let mut parser = tree_sitter::Parser::new();
6364 let lang: tree_sitter::Language = crate::parse::LANGUAGE.into();
6365 parser.set_language(&lang).expect("grammar");
6366 let tree = parser.parse(text, None).expect("parse");
6367 J::obj(vec![("tree", J::s(tree.root_node().to_sexp()))])
6368}
6369
6370fn handle(st: &mut State, msg: &Value) -> Option<i32> {
6373 get(msg, "method")?;
6376 let id = get(msg, "id");
6377 let method = as_str(get(msg, "method")).unwrap_or("");
6378 let params = get(msg, "params");
6379 let td_uri = || {
6380 as_str(
6381 params
6382 .and_then(|p| get(p, "textDocument"))
6383 .and_then(|t| get(t, "uri")),
6384 )
6385 .unwrap_or("")
6386 .to_string()
6387 };
6388 let position = || {
6389 let pos = params.and_then(|p| get(p, "position"));
6390 Pos {
6391 line: as_usize(pos.and_then(|p| get(p, "line"))).unwrap_or(0),
6392 character: as_usize(pos.and_then(|p| get(p, "character"))).unwrap_or(0),
6393 }
6394 };
6395 let reanalyze = |st: &mut State| {
6396 st.analyses.clear();
6397 let uris: Vec<String> = st.docs.iter().map(|(u, _)| u.clone()).collect();
6398 for u in uris {
6399 analyze(st, &u);
6400 }
6401 };
6402 match method {
6403 "initialize" => {
6404 st.progress_supported = as_bool(
6405 params
6406 .and_then(|p| get(p, "capabilities"))
6407 .and_then(|c| get(c, "window"))
6408 .and_then(|w| get(w, "workDoneProgress")),
6409 )
6410 .unwrap_or(false);
6411 let caps = J::obj(vec![
6412 ("textDocumentSync", J::Num(1)),
6413 ("hoverProvider", J::Bool(true)),
6414 ("definitionProvider", J::Bool(true)),
6415 ("typeDefinitionProvider", J::Bool(true)),
6416 ("referencesProvider", J::Bool(true)),
6417 ("documentHighlightProvider", J::Bool(true)),
6418 ("documentSymbolProvider", J::Bool(true)),
6419 ("foldingRangeProvider", J::Bool(true)),
6420 ("documentFormattingProvider", J::Bool(true)),
6421 (
6422 "renameProvider",
6423 J::obj(vec![("prepareProvider", J::Bool(true))]),
6424 ),
6425 (
6426 "completionProvider",
6427 J::obj(vec![(
6428 "triggerCharacters",
6429 J::Arr(vec![J::s("."), J::s("$"), J::s(":")]),
6430 )]),
6431 ),
6432 (
6433 "codeLensProvider",
6434 J::obj(vec![("resolveProvider", J::Bool(false))]),
6435 ),
6436 (
6437 "signatureHelpProvider",
6438 J::obj(vec![(
6439 "triggerCharacters",
6440 J::Arr(vec![J::s("("), J::s(",")]),
6441 )]),
6442 ),
6443 ("workspaceSymbolProvider", J::Bool(true)),
6444 ("selectionRangeProvider", J::Bool(true)),
6445 (
6446 "semanticTokensProvider",
6447 J::obj(vec![
6448 (
6449 "legend",
6450 J::obj(vec![
6451 (
6452 "tokenTypes",
6453 J::Arr(TOKEN_TYPES.iter().map(|t| J::s(*t)).collect()),
6454 ),
6455 (
6456 "tokenModifiers",
6457 J::Arr(TOKEN_MODS.iter().map(|t| J::s(*t)).collect()),
6458 ),
6459 ]),
6460 ),
6461 ("full", J::Bool(true)),
6462 ]),
6463 ),
6464 ("inlayHintProvider", J::Bool(true)),
6465 ("callHierarchyProvider", J::Bool(true)),
6466 ("typeHierarchyProvider", J::Bool(true)),
6467 (
6468 "codeActionProvider",
6469 J::obj(vec![(
6470 "codeActionKinds",
6471 J::Arr(vec![
6472 J::s("quickfix"),
6473 J::s("refactor.rewrite"),
6474 J::s("refactor.extract"),
6475 J::s("refactor.inline"),
6476 ]),
6477 )]),
6478 ),
6479 ("linkedEditingRangeProvider", J::Bool(true)),
6480 (
6481 "documentOnTypeFormattingProvider",
6482 J::obj(vec![
6483 ("firstTriggerCharacter", J::s("\n")),
6484 (
6485 "moreTriggerCharacter",
6486 J::Arr(vec![J::s("}"), J::s("]"), J::s(")")]),
6487 ),
6488 ]),
6489 ),
6490 (
6491 "executeCommandProvider",
6492 J::obj(vec![(
6493 "commands",
6494 J::Arr(
6495 [
6496 "decl.evaluate",
6497 "decl.validate",
6498 "decl.trace",
6499 "decl.showSyntaxTree",
6500 "decl.reloadWorkspace",
6501 ]
6502 .iter()
6503 .map(|c| J::s(*c))
6504 .collect(),
6505 ),
6506 )]),
6507 ),
6508 ]);
6509 reply(
6510 id,
6511 J::obj(vec![
6512 ("capabilities", caps),
6513 (
6514 "serverInfo",
6515 J::obj(vec![("name", J::s("decl-lsp")), ("version", J::s("0.3.0"))]),
6516 ),
6517 ]),
6518 );
6519 }
6520 "initialized" => {}
6521 "workspace/didChangeConfiguration" => {
6522 let inputs = params
6523 .and_then(|p| get(p, "settings"))
6524 .and_then(|s| get(s, "decl"))
6525 .and_then(|d| get(d, "inputs"));
6526 st.inputs = match inputs {
6527 Some(Value::JObj(es)) => es
6528 .iter()
6529 .filter_map(|(k, v)| as_str(Some(v)).map(|f| (k.clone(), f.to_string())))
6530 .collect(),
6531 _ => vec![],
6532 };
6533 let hints = params
6534 .and_then(|p| get(p, "settings"))
6535 .and_then(|s| get(s, "decl"))
6536 .and_then(|d| get(d, "inlayHints"));
6537 if let Some(b) = as_bool(hints.and_then(|h| get(h, "types"))) {
6538 st.hint_types = b;
6539 }
6540 if let Some(b) = as_bool(hints.and_then(|h| get(h, "parameterNames"))) {
6541 st.hint_parameter_names = b;
6542 }
6543 if let Some(b) = as_bool(hints.and_then(|h| get(h, "values"))) {
6544 st.hint_values = b;
6545 }
6546 if let Some(b) = as_bool(hints.and_then(|h| get(h, "units"))) {
6547 st.hint_units = b;
6548 }
6549 if let Some(b) = as_bool(hints.and_then(|h| get(h, "contextVariables"))) {
6550 st.hint_context_variables = b;
6551 }
6552 reanalyze(st);
6553 }
6554 "workspace/didChangeWatchedFiles" => reanalyze(st),
6555 "decl/files" => {
6556 if let Some(Value::JArr(files)) = params.and_then(|p| get(p, "files")) {
6559 for f in files.iter() {
6560 if let (Some(u), Some(text)) = (as_str(get(f, "uri")), as_str(get(f, "text"))) {
6561 st.overlay.insert(path_of(u), text.to_string());
6562 }
6563 }
6564 }
6565 if let Some(Value::JArr(removed)) = params.and_then(|p| get(p, "remove")) {
6566 for u in removed.iter() {
6567 if let Some(u) = as_str(Some(u)) {
6568 st.overlay.remove(&path_of(u));
6569 }
6570 }
6571 }
6572 reanalyze(st);
6573 }
6574 "textDocument/didOpen" => {
6575 let uri = td_uri();
6576 let text = as_str(
6577 params
6578 .and_then(|p| get(p, "textDocument"))
6579 .and_then(|t| get(t, "text")),
6580 )
6581 .unwrap_or("")
6582 .to_string();
6583 st.set(&uri, text);
6584 st.analyses.clear();
6585 analyze(st, &uri);
6586 }
6587 "textDocument/didChange" => {
6588 let uri = td_uri();
6589 let text = params
6590 .and_then(|p| get(p, "contentChanges"))
6591 .and_then(|c| {
6592 if let Value::JArr(items) = c {
6593 items.first()
6594 } else {
6595 None
6596 }
6597 })
6598 .and_then(|c| as_str(get(c, "text")))
6599 .unwrap_or("")
6600 .to_string();
6601 st.set(&uri, text);
6602 st.analyses.clear();
6603 analyze(st, &uri);
6604 }
6605 "textDocument/didSave" => {}
6606 "textDocument/didClose" => {
6607 let uri = td_uri();
6608 st.docs.retain(|(u, _)| *u != uri);
6609 st.overlay.remove(&path_of(&uri));
6610 st.analyses.remove(&uri);
6611 st.last_good.remove(&uri);
6612 notify(
6613 "textDocument/publishDiagnostics",
6614 J::obj(vec![
6615 ("uri", J::s(uri.clone())),
6616 ("diagnostics", J::Arr(vec![])),
6617 ]),
6618 );
6619 }
6620 "textDocument/hover" => {
6621 let r = hover(st, &td_uri(), position());
6622 reply(id, r);
6623 }
6624 "textDocument/definition" => {
6625 let r = definition(st, &td_uri(), position());
6626 reply(id, r);
6627 }
6628 "textDocument/typeDefinition" => {
6629 let r = type_definition(st, &td_uri(), position());
6630 reply(id, r);
6631 }
6632 "textDocument/references" => {
6633 let incl = as_bool(
6634 params
6635 .and_then(|p| get(p, "context"))
6636 .and_then(|c| get(c, "includeDeclaration")),
6637 )
6638 .unwrap_or(false);
6639 let refs = references(st, &td_uri(), position(), incl);
6640 reply(
6641 id,
6642 J::Arr(refs.iter().map(|(m, l)| location(m, *l)).collect()),
6643 );
6644 }
6645 "textDocument/documentHighlight" => {
6646 let uri = td_uri();
6647 let path = path_of(&uri);
6648 let refs = references(st, &uri, position(), true);
6649 reply(
6650 id,
6651 J::Arr(
6652 refs.iter()
6653 .filter(|(m, _)| m.path == path)
6654 .map(|(_, l)| J::obj(vec![("range", range_json(*l)), ("kind", J::Num(1))]))
6655 .collect(),
6656 ),
6657 );
6658 }
6659 "textDocument/completion" => {
6660 let r = completion(st, &td_uri(), position());
6661 reply(id, r);
6662 }
6663 "textDocument/documentSymbol" => reply(id, document_symbols(st, &td_uri())),
6664 "textDocument/foldingRange" => reply(id, folding_ranges(st, &td_uri())),
6665 "textDocument/formatting" => reply(id, formatting(st, &td_uri())),
6666 "textDocument/prepareRename" => {
6667 let r = prepare_rename(st, &td_uri(), position());
6668 reply(id, r);
6669 }
6670 "textDocument/rename" => {
6671 let new_name = as_str(params.and_then(|p| get(p, "newName")))
6672 .unwrap_or("")
6673 .to_string();
6674 let r = rename(st, &td_uri(), position(), &new_name);
6675 reply(id, r);
6676 }
6677 "textDocument/codeLens" => reply(id, code_lenses(st, &td_uri())),
6678 "textDocument/signatureHelp" => {
6679 let r = signature_help(st, &td_uri(), position());
6680 reply(id, r);
6681 }
6682 "workspace/symbol" => reply(
6683 id,
6684 workspace_symbols(
6685 st,
6686 as_str(params.and_then(|p| get(p, "query"))).unwrap_or(""),
6687 ),
6688 ),
6689 "textDocument/selectionRange" => {
6690 let positions: Vec<Pos> = match params.and_then(|p| get(p, "positions")) {
6691 Some(Value::JArr(items)) => items
6692 .iter()
6693 .map(|p| Pos {
6694 line: as_usize(get(p, "line")).unwrap_or(0),
6695 character: as_usize(get(p, "character")).unwrap_or(0),
6696 })
6697 .collect(),
6698 _ => vec![],
6699 };
6700 reply(id, selection_ranges(st, &td_uri(), &positions));
6701 }
6702 "textDocument/semanticTokens/full" => {
6703 let r = semantic_tokens(st, &td_uri());
6704 reply(id, r);
6705 }
6706 "textDocument/inlayHint" => {
6707 let rg = params.and_then(|p| get(p, "range"));
6708 let pt = |k: &str| {
6709 let p = rg.and_then(|r| get(r, k));
6710 Pos {
6711 line: as_usize(p.and_then(|x| get(x, "line"))).unwrap_or(0),
6712 character: as_usize(p.and_then(|x| get(x, "character"))).unwrap_or(0),
6713 }
6714 };
6715 let range = (pt("start"), pt("end"));
6716 let r = inlay_hints(st, &td_uri(), range);
6717 reply(id, r);
6718 }
6719 "textDocument/prepareCallHierarchy" => {
6720 let r = prepare_hierarchy(st, &td_uri(), position(), "func");
6721 reply(id, r);
6722 }
6723 "callHierarchy/incomingCalls" => {
6724 reply(id, incoming_calls(st, params.and_then(|p| get(p, "item"))))
6725 }
6726 "callHierarchy/outgoingCalls" => {
6727 reply(id, outgoing_calls(st, params.and_then(|p| get(p, "item"))))
6728 }
6729 "textDocument/prepareTypeHierarchy" => {
6730 let r = prepare_hierarchy(st, &td_uri(), position(), "type");
6731 reply(id, r);
6732 }
6733 "typeHierarchy/supertypes" => {
6734 reply(id, supertypes(st, params.and_then(|p| get(p, "item"))))
6735 }
6736 "typeHierarchy/subtypes" => reply(id, subtypes(st, params.and_then(|p| get(p, "item")))),
6737 "textDocument/codeAction" => {
6738 let rg = params.and_then(|p| get(p, "range"));
6739 let pt = |k: &str| {
6740 let p = rg.and_then(|r| get(r, k));
6741 Pos {
6742 line: as_usize(p.and_then(|x| get(x, "line"))).unwrap_or(0),
6743 character: as_usize(p.and_then(|x| get(x, "character"))).unwrap_or(0),
6744 }
6745 };
6746 let range = (pt("start"), pt("end"));
6747 let diags: Vec<Value> = match params
6748 .and_then(|p| get(p, "context"))
6749 .and_then(|c| get(c, "diagnostics"))
6750 {
6751 Some(Value::JArr(items)) => items.iter().cloned().collect(),
6752 _ => vec![],
6753 };
6754 let r = code_actions(st, &td_uri(), range, &diags);
6755 reply(id, r);
6756 }
6757 "textDocument/linkedEditingRange" => {
6758 reply(id, linked_editing_range(st, &td_uri(), position()))
6759 }
6760 "textDocument/onTypeFormatting" => {
6761 let ch = as_str(params.and_then(|p| get(p, "ch")))
6762 .unwrap_or("")
6763 .to_string();
6764 reply(id, on_type_formatting(st, &td_uri(), position(), &ch));
6765 }
6766 "workspace/executeCommand" => {
6767 let command = as_str(params.and_then(|p| get(p, "command")))
6768 .unwrap_or("")
6769 .to_string();
6770 let r = execute_command(st, &command, params.and_then(|p| get(p, "arguments")));
6771 reply(id, r);
6772 }
6773 "shutdown" => reply(id, J::Null),
6774 "exit" => return Some(0),
6775 _ => reply(id, J::Null),
6776 }
6777 None
6778}
6779
6780pub fn main() -> i32 {
6781 std::panic::set_hook(Box::new(|_| {})); let mut st = State::default();
6783 let mut stdin = std::io::stdin().lock();
6784 let mut buf: Vec<u8> = vec![];
6785 let mut chunk = [0u8; 65536];
6786 let cl = Regex::new(r"(?i)Content-Length: (\d+)").unwrap();
6787 loop {
6788 let n = match stdin.read(&mut chunk) {
6789 Ok(0) | Err(_) => return 0, Ok(n) => n,
6791 };
6792 buf.extend_from_slice(&chunk[..n]);
6793 while let Some(header_end) = buf.windows(4).position(|w| w == b"\r\n\r\n") {
6794 let header = String::from_utf8_lossy(&buf[..header_end]).to_string();
6795 let Some(len) = cl
6796 .captures(&header)
6797 .and_then(|c| c[1].parse::<usize>().ok())
6798 else {
6799 buf.drain(..header_end + 4);
6800 continue;
6801 };
6802 if buf.len() < header_end + 4 + len {
6803 break;
6804 }
6805 let body =
6806 String::from_utf8_lossy(&buf[header_end + 4..header_end + 4 + len]).to_string();
6807 buf.drain(..header_end + 4 + len);
6808 if let Ok(msg) = read_json(&body) {
6809 let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
6811 handle(&mut st, &msg)
6812 }));
6813 match outcome {
6814 Ok(Some(code)) => return code,
6815 Ok(None) => {}
6816 Err(payload) => {
6817 let message = payload
6818 .downcast_ref::<String>()
6819 .cloned()
6820 .or_else(|| payload.downcast_ref::<&str>().map(|x| x.to_string()))
6821 .unwrap_or_else(|| "internal error".into());
6822 notify(
6823 "window/logMessage",
6824 J::obj(vec![
6825 ("type", J::Num(1)),
6826 ("message", J::s(message.clone())),
6827 ]),
6828 );
6829 if let Some(id) = get(&msg, "id") {
6830 send(&format!("{{\"jsonrpc\":\"2.0\",\"id\":{},\"error\":{{\"code\":-32603,\"message\":{}}}}}", json_of(id), json_str(&message)));
6831 }
6832 }
6833 }
6834 }
6835 }
6836 }
6837}