difftreelog
feat(fmt) basic comment formatting in objects
in: master
21 files changed
.cargo/configdiffbeforeafterboth--- a/.cargo/config
+++ /dev/null
@@ -1,2 +0,0 @@
-[alias]
-xtask = "run --manifest-path ./xtask/Cargo.toml --"
.cargo/config.tomldiffbeforeafterboth--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,2 @@
+[alias]
+xtask = "run --package xtask --bin xtask --"
cmds/jrsonnet-fmt/Cargo.tomldiffbeforeafterboth--- a/cmds/jrsonnet-fmt/Cargo.toml
+++ b/cmds/jrsonnet-fmt/Cargo.toml
@@ -6,3 +6,5 @@
[dependencies]
dprint-core = "0.58.2"
jrsonnet-rowan-parser = { path = "../../crates/jrsonnet-rowan-parser" }
+insta = "1.15"
+indoc = "1.0"
cmds/jrsonnet-fmt/src/children.rsdiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-fmt/src/children.rs
@@ -0,0 +1,168 @@
+use std::{fmt::Debug, marker::PhantomData, mem};
+
+use jrsonnet_rowan_parser::{
+ nodes::{Trivia, TriviaKind},
+ AstNode, AstToken, SyntaxElement,
+ SyntaxKind::*,
+ SyntaxNode, TS,
+};
+
+pub type ChildTrivia = Vec<Trivia>;
+
+pub struct ChildIterator<I, T> {
+ inner: I,
+ _marker: PhantomData<T>,
+}
+
+pub fn children_between<T: AstNode + Debug>(
+ node: SyntaxNode,
+ start: Option<&SyntaxElement>,
+ end: Option<&SyntaxElement>,
+) -> (Vec<Child<T>>, ChildTrivia) {
+ let mut iter = node.children_with_tokens().peekable();
+ while iter.peek() == start {
+ iter.next();
+ }
+ children(
+ iter.take_while(|i| Some(i) != end),
+ start.is_none() || end.is_none(),
+ )
+}
+
+pub fn should_start_with_newline(tt: &ChildTrivia) -> bool {
+ // First for previous item end
+ count_newlines_before(&tt) >= 2
+}
+
+fn count_newlines_before(tt: &ChildTrivia) -> usize {
+ let mut nl_count = 0;
+ for t in tt {
+ match t.kind() {
+ TriviaKind::Whitespace => {
+ nl_count += t.text().bytes().filter(|b| *b == b'\n').count();
+ }
+ _ => break,
+ }
+ }
+ nl_count
+}
+fn count_newlines_after(tt: &ChildTrivia) -> usize {
+ let mut nl_count = 0;
+ for t in tt.iter().rev() {
+ match t.kind() {
+ TriviaKind::Whitespace => {
+ nl_count += t.text().bytes().filter(|b| *b == b'\n').count();
+ }
+ TriviaKind::SingleLineHashComment => {
+ nl_count += 1;
+ break;
+ }
+ TriviaKind::SingleLineSlashComment => {
+ nl_count += 1;
+ break;
+ }
+ _ => {}
+ }
+ }
+ nl_count
+}
+
+pub fn children<'a, T: AstNode + Debug>(
+ items: impl Iterator<Item = SyntaxElement>,
+ loose: bool,
+) -> (Vec<Child<T>>, ChildTrivia) {
+ let mut out = Vec::new();
+ let mut current_child = None::<Child<T>>;
+ let mut next = ChildTrivia::new();
+ // Previous element ended, do not add more inline comments
+ let mut started_next = false;
+ let mut had_some = false;
+
+ for item in items {
+ if let Some(value) = item.as_node().cloned().and_then(T::cast) {
+ let before_trivia = mem::take(&mut next);
+ let last_child = current_child.replace(Child {
+ newlines_above: if had_some {
+ count_newlines_before(&before_trivia)
+ + current_child
+ .as_ref()
+ .map(|c| count_newlines_after(&c.inline_trivia))
+ .unwrap_or_default()
+ } else {
+ 0
+ },
+ before_trivia,
+ value,
+ inline_trivia: Vec::new(),
+ });
+ if let Some(last_child) = last_child {
+ out.push(last_child)
+ }
+ had_some = true;
+ started_next = false;
+ } else if let Some(trivia) = item.as_token().cloned().and_then(Trivia::cast) {
+ let is_single_line_comment = trivia.kind() == TriviaKind::SingleLineHashComment
+ || trivia.kind() == TriviaKind::SingleLineSlashComment;
+ if started_next
+ || current_child.is_none()
+ || trivia.text().contains('\n') && !is_single_line_comment
+ {
+ next.push(trivia.clone());
+ started_next = true;
+ } else {
+ let cur = current_child.as_mut().expect("checked not none");
+ cur.inline_trivia.push(trivia);
+ if is_single_line_comment {
+ started_next = true;
+ }
+ }
+ had_some = true;
+ } else if loose {
+ if had_some {
+ break;
+ }
+ started_next = true;
+ } else {
+ assert!(
+ TS![, ;].contains(item.kind()) || item.kind() == ERROR,
+ "silently eaten token: {:?}",
+ item.kind()
+ )
+ }
+ }
+
+ if let Some(current_child) = current_child {
+ out.push(current_child);
+ }
+
+ (out, next)
+}
+
+#[derive(Debug)]
+pub struct Child<T> {
+ newlines_above: usize,
+ /// Comment before item, i.e
+ ///
+ /// ```ignore
+ /// // Comment
+ /// item
+ /// ```
+ pub before_trivia: ChildTrivia,
+ pub value: T,
+ /// Comment after line, but located at same line
+ ///
+ /// ```ignore
+ /// item1, // Inline comment
+ /// // Not inline comment
+ /// item2,
+ /// ```
+ pub inline_trivia: ChildTrivia,
+}
+
+impl<T> Child<T> {
+ /// If this child has two newlines above in source code, so it needs to have it in output
+ pub fn needs_newline_above(&self) -> bool {
+ // First line for end of previous item
+ self.newlines_above >= 2
+ }
+}
cmds/jrsonnet-fmt/src/comments.rsdiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-fmt/src/comments.rs
@@ -0,0 +1,159 @@
+use dprint_core::formatting::PrintItems;
+use jrsonnet_rowan_parser::{nodes::TriviaKind, AstToken};
+
+use crate::{children::ChildTrivia, p, pi};
+
+pub enum CommentLocation {
+ /// Above local, field, other things
+ AboveItem,
+ /// After item
+ ItemInline,
+ /// After all items in object
+ EndOfItems,
+}
+
+pub fn format_comments(comments: &ChildTrivia, loc: CommentLocation) -> PrintItems {
+ let mut pi = p!(new:);
+
+ for c in comments {
+ match c.kind() {
+ TriviaKind::Whitespace => {}
+ TriviaKind::MultiLineComment => {
+ let mut text = c
+ .text()
+ .strip_prefix("/*")
+ .expect("ml comment starts with /*")
+ .strip_suffix("*/")
+ .expect("ml comment ends with */");
+ // doc-style comment, /**
+ let doc = if text.starts_with('*') {
+ text = &text[1..];
+ true
+ } else {
+ false
+ };
+ // Is comment starts with text immediatly, i.e /*text
+ let mut immediate_start = true;
+ let mut lines = text
+ .split('\n')
+ .map(|l| l.trim_end())
+ .skip_while(|l| {
+ if l.is_empty() {
+ immediate_start = false;
+ true
+ } else {
+ false
+ }
+ })
+ .collect::<Vec<_>>();
+ while lines.last().map(|l| l.is_empty()).unwrap_or(false) {
+ lines.pop();
+ }
+ if lines.len() == 1 && !doc {
+ p!(pi: str("/* ") str(lines[0].trim()) str(" */") nl)
+ } else if !lines.is_empty() {
+ fn common_ws_prefix<'a>(a: &'a str, b: &str) -> &'a str {
+ let offset = a
+ .bytes()
+ .zip(b.bytes())
+ .take_while(|(a, b)| a == b && (a.is_ascii_whitespace() || *a == b'*'))
+ .count();
+ &a[..offset]
+ }
+ // First line is not empty, extract ws prefix of it
+ let mut common_ws_padding = if immediate_start && lines.len() > 1 {
+ common_ws_prefix(lines[1], lines[1])
+ } else {
+ common_ws_prefix(lines[0], lines[0])
+ };
+ for line in lines
+ .iter()
+ .skip(if immediate_start { 2 } else { 1 })
+ .filter(|l| !l.is_empty())
+ {
+ common_ws_padding = common_ws_prefix(common_ws_padding, line);
+ }
+ for line in lines
+ .iter_mut()
+ .skip(if immediate_start { 1 } else { 0 })
+ .filter(|l| !l.is_empty())
+ {
+ *line = line
+ .strip_prefix(common_ws_padding)
+ .expect("all non-empty lines start with this padding");
+ }
+
+ p!(pi: str("/*"));
+ if doc {
+ p!(pi: str("*"));
+ }
+ p!(pi: nl);
+ for mut line in lines {
+ if doc {
+ p!(pi: str(" *"));
+ }
+ if line.is_empty() {
+ p!(pi: nl);
+ } else {
+ if doc {
+ p!(pi: str(" "));
+ }
+ while let Some(new_line) = line.strip_prefix('\t') {
+ if doc {
+ p!(pi: str(" "));
+ } else {
+ p!(pi: tab);
+ }
+ line = new_line;
+ }
+ p!(pi: str(line) nl)
+ }
+ }
+ if doc {
+ p!(pi: str(" "));
+ }
+ p!(pi: str("*/") nl)
+ }
+ }
+ // TODO: Keep common padding for multiple continous lines of single-line comments
+ // I.e
+ // ```
+ // # Line1
+ // # Line2
+ // ```
+ // Should be reformatted as
+ // ```
+ // # Line1
+ // # Line2
+ // ```
+ // But currently comment formatter is not aware of continous comment lines, and reformats it as
+ // ```
+ // # Line1
+ // # Line2
+ // ```
+ TriviaKind::SingleLineHashComment => {
+ if matches!(loc, CommentLocation::ItemInline) {
+ p!(pi: str(" "))
+ }
+ p!(pi: str("# ") str(c.text().strip_prefix('#').expect("hash comment starts with #").trim()));
+ if !matches!(loc, CommentLocation::ItemInline) {
+ p!(pi: nl)
+ }
+ }
+ TriviaKind::SingleLineSlashComment => {
+ if matches!(loc, CommentLocation::ItemInline) {
+ p!(pi: str(" "))
+ }
+ p!(pi: str("// ") str(c.text().strip_prefix("//").expect("comment starts with //").trim()));
+ if !matches!(loc, CommentLocation::ItemInline) {
+ p!(pi: nl)
+ }
+ }
+ // Garbage in - garbage out
+ TriviaKind::ErrorCommentTooShort => p!(pi: str("/*/")),
+ TriviaKind::ErrorCommentUnterminated => p!(pi: str(c.text())),
+ }
+ }
+
+ pi
+}
cmds/jrsonnet-fmt/src/main.rsdiffbeforeafterboth1use std::any::type_name;23use dprint_core::formatting::{PrintItems, PrintOptions, Signal};4use jrsonnet_rowan_parser::{5 nodes::{6 ArgsDesc, Assertion, BinaryOperator, Bind, CompSpec, Destruct, DestructArrayPart,7 DestructRest, Expr, Field, FieldName, ForSpec, IfSpec, ImportKind, LhsExpr, Literal,8 Member, Name, Number, ObjBody, ObjLocal, ParamsDesc, SliceDesc, SourceFile, Text,9 UnaryOperator,10 },11 AstToken, SyntaxToken,12};1314pub trait Printable {15 fn print(&self) -> PrintItems;16}1718macro_rules! pi {19 (@i; $($t:tt)*) => {{20 #[allow(unused_mut)]21 let mut o = PrintItems::new();22 pi!(@s; o: $($t)*);23 o24 }};25 (@s; $o:ident: str($e:expr $(,)?) $($t:tt)*) => {{26 $o.push_str($e);27 pi!(@s; $o: $($t)*);28 }};29 (@s; $o:ident: nl $($t:tt)*) => {{30 $o.push_signal(Signal::NewLine);31 pi!(@s; $o: $($t)*);32 }};33 (@s; $o:ident: >i $($t:tt)*) => {{34 $o.push_signal(Signal::StartIndent);35 pi!(@s; $o: $($t)*);36 }};37 (@s; $o:ident: <i $($t:tt)*) => {{38 $o.push_signal(Signal::FinishIndent);39 pi!(@s; $o: $($t)*);40 }};41 (@s; $o:ident: {$expr:expr} $($t:tt)*) => {{42 $o.extend($expr.print());43 pi!(@s; $o: $($t)*);44 }};45 (@s; $o:ident: if ($e:expr)($($then:tt)*) $($t:tt)*) => {{46 if $e {47 pi!(@s; $o: $($then)*);48 }49 pi!(@s; $o: $($t)*);50 }};51 (@s; $o:ident: ifelse ($e:expr)($($then:tt)*)($($else:tt)*) $($t:tt)*) => {{52 if $e {53 pi!(@s; $o: $($then)*);54 } else {55 pi!(@s; $o: $($else)*);56 }57 pi!(@s; $o: $($t)*);58 }};59 (@s; $i:ident:) => {}60}61macro_rules! p {62 (new: $($t:tt)*) => {63 pi!(@i; $($t)*)64 };65 ($o:ident: $($t:tt)*) => {66 pi!(@s; $o: $($t)*)67 };68}6970impl<P> Printable for Option<P>71where72 P: Printable,73{74 fn print(&self) -> PrintItems {75 if let Some(v) = self {76 v.print()77 } else {78 p!(new: str(79 &format!(80 "/*missing {}*/",81 type_name::<P>().replace("jrsonnet_rowan_parser::generated::nodes::", "")82 ),83 ))84 }85 }86}8788impl Printable for SyntaxToken {89 fn print(&self) -> PrintItems {90 p!(new: str(&self.to_string()))91 }92}9394impl Printable for Text {95 fn print(&self) -> PrintItems {96 p!(new: str(&format!("{}", self)))97 }98}99impl Printable for Number {100 fn print(&self) -> PrintItems {101 p!(new: str(&format!("{}", self)))102 }103}104105impl Printable for Name {106 fn print(&self) -> PrintItems {107 p!(new: {self.ident_lit()})108 }109}110111impl Printable for DestructRest {112 fn print(&self) -> PrintItems {113 let mut pi = p!(new: str("..."));114 if let Some(name) = self.into() {115 p!(pi: {name});116 }117 pi118 }119}120121impl Printable for Destruct {122 fn print(&self) -> PrintItems {123 let mut pi = p!(new:);124 match self {125 Destruct::DestructFull(f) => {126 p!(pi: {f.name()})127 }128 Destruct::DestructSkip(_) => p!(pi: str("?")),129 Destruct::DestructArray(a) => {130 p!(pi: str("[") >i nl);131 for el in a.destruct_array_parts() {132 match el {133 DestructArrayPart::DestructArrayElement(e) => {134 p!(pi: {e.destruct()} str(",") nl)135 }136 DestructArrayPart::DestructRest(d) => {137 p!(pi: {d} str(",") nl)138 }139 }140 }141 p!(pi: <i str("]"));142 }143 Destruct::DestructObject(o) => {144 p!(pi: str("{") >i nl);145 for item in o.destruct_object_fields() {146 p!(pi: {item.field()});147 if let Some(des) = item.destruct() {148 p!(pi: str(": ") {des})149 }150 if let Some(def) = item.expr() {151 p!(pi: str(" = ") {def});152 }153 p!(pi: str(",") nl);154 }155 if let Some(rest) = o.destruct_rest() {156 p!(pi: {rest} nl)157 }158 p!(pi: <i str("}"));159 }160 }161 pi162 }163}164165impl Printable for FieldName {166 fn print(&self) -> PrintItems {167 match self {168 FieldName::FieldNameFixed(f) => {169 if let Some(id) = f.id() {170 p!(new: {id})171 } else if let Some(str) = f.text() {172 p!(new: {str})173 } else {174 p!(new: str("/*missing FieldName*/"))175 }176 }177 FieldName::FieldNameDynamic(d) => {178 p!(new: str("[") {d.expr()} str("]"))179 }180 }181 }182}183impl Printable for Field {184 fn print(&self) -> PrintItems {185 let mut pi = p!(new:);186 match self {187 Field::FieldNormal(n) => {188 p!(pi: {n.field_name()});189 if n.plus_token().is_some() {190 p!(pi: str("+"));191 }192 p!(pi: str(": ") {n.expr()});193 }194 Field::FieldMethod(m) => {195 p!(pi: {m.field_name()} {m.params_desc()} str(": ") {m.expr()});196 }197 }198 pi199 }200}201202impl Printable for ObjLocal {203 fn print(&self) -> PrintItems {204 p!(new: str("local ") {self.bind()})205 }206}207208impl Printable for Assertion {209 fn print(&self) -> PrintItems {210 let mut pi = p!(new: str("assert ") {self.condition()});211 if self.colon_token().is_some() || self.message().is_some() {212 p!(pi: str(": ") {self.message()})213 }214 pi215 }216}217218impl Printable for ParamsDesc {219 fn print(&self) -> PrintItems {220 let mut pi = p!(new: str("(") >i nl);221 for param in self.params() {222 p!(pi: {param.destruct()});223 if param.assign_token().is_some() || param.expr().is_some() {224 p!(pi: str(" = ") {param.expr()})225 }226 p!(pi: str(",") nl)227 }228 p!(pi: <i str(")"));229 pi230 }231}232impl Printable for ArgsDesc {233 fn print(&self) -> PrintItems {234 let mut pi = p!(new: str("(") >i nl);235 for arg in self.args() {236 if arg.name().is_some() || arg.assign_token().is_some() {237 p!(pi: {arg.name()} str(" = "));238 }239 p!(pi: {arg.expr()} str(",") nl)240 }241 p!(pi: <i str(")"));242 pi243 }244}245impl Printable for SliceDesc {246 fn print(&self) -> PrintItems {247 let mut pi = p!(new: str("["));248 if self.from().is_some() {249 p!(pi: {self.from()});250 }251 p!(pi: str(":"));252 if self.end().is_some() {253 p!(pi: {self.end().map(|e|e.expr())})254 }255 // Keep only one : in case if we don't need step256 if self.step().is_some() {257 p!(pi: str(":") {self.step().map(|e|e.expr())});258 }259 p!(pi: str("]"));260 pi261 }262}263264impl Printable for ObjBody {265 fn print(&self) -> PrintItems {266 match self {267 ObjBody::ObjBodyComp(_) => todo!(),268 ObjBody::ObjBodyMemberList(l) => {269 let mut pi = p!(new:);270 for mem in l.members() {271 match mem {272 Member::MemberBindStmt(b) => {273 p!(pi: {b.obj_local()})274 }275 Member::MemberAssertStmt(ass) => {276 p!(pi: {ass.assertion()})277 }278 Member::MemberField(f) => {279 p!(pi: {f.field()})280 }281 }282 p!(pi: str(",") nl)283 }284 pi285 }286 }287 }288}289impl Printable for UnaryOperator {290 fn print(&self) -> PrintItems {291 p!(new: str(self.text()))292 }293}294impl Printable for BinaryOperator {295 fn print(&self) -> PrintItems {296 p!(new: str(self.text()))297 }298}299impl Printable for Bind {300 fn print(&self) -> PrintItems {301 match self {302 Bind::BindDestruct(d) => {303 p!(new: {d.into()} str(" = ") {d.value()})304 }305 Bind::BindFunction(f) => {306 p!(new: str("function") {f.params()} str(" = ") {f.value()})307 }308 }309 }310}311impl Printable for Literal {312 fn print(&self) -> PrintItems {313 p!(new: str(&self.syntax().to_string()))314 }315}316impl Printable for ImportKind {317 fn print(&self) -> PrintItems {318 p!(new: str(&self.syntax().to_string()))319 }320}321impl Printable for LhsExpr {322 fn print(&self) -> PrintItems {323 p!(new: {self.expr()})324 }325}326impl Printable for ForSpec {327 fn print(&self) -> PrintItems {328 p!(new: str("for ") {self.bind()} str(" in ") {self.expr()})329 }330}331impl Printable for IfSpec {332 fn print(&self) -> PrintItems {333 p!(new: str("if ") {self.expr()})334 }335}336impl Printable for CompSpec {337 fn print(&self) -> PrintItems {338 match self {339 CompSpec::ForSpec(f) => f.print(),340 CompSpec::IfSpec(i) => i.print(),341 }342 }343}344impl Printable for Expr {345 fn print(&self) -> PrintItems {346 match self {347 Expr::ExprBinary(b) => {348 p!(new: {b.lhs()} str(" ") {b.binary_operator()} str(" ") {b.rhs()})349 }350 Expr::ExprUnary(u) => p!(new: {u.unary_operator()} {u.rhs()}),351 Expr::ExprSlice(s) => {352 p!(new: {s.expr()} {s.slice_desc()})353 }354 Expr::ExprIndex(i) => {355 p!(new: {i.expr()} str(".") {i.index()})356 }357 Expr::ExprIndexExpr(i) => p!(new: {i.base()} str("[") {i.index()} str("]")),358 Expr::ExprApply(a) => {359 let mut pi = p!(new: {a.expr()} {a.args_desc()});360 if a.tailstrict_kw_token().is_some() {361 p!(pi: str(" tailstrict"));362 }363 pi364 }365 Expr::ExprObjExtend(ex) => {366 p!(new: {ex.lhs_expr()} str(" ") {ex.expr()})367 }368 Expr::ExprParened(p) => {369 p!(new: str("(") {p.expr()} str(")"))370 }371 Expr::ExprIntrinsicThisFile(_) => p!(new: str("$intrinsicThisFile")),372 Expr::ExprIntrinsicId(_) => p!(new: str("$intrinsicId")),373 Expr::ExprIntrinsic(i) => p!(new: str("$intrinsic(") {i.name()} str(")")),374 Expr::ExprString(s) => p!(new: {s.text()}),375 Expr::ExprNumber(n) => p!(new: {n.number()}),376 Expr::ExprArray(a) => {377 let mut pi = p!(new: str("[") >i nl);378 for el in a.exprs() {379 p!(pi: {el} str(",") nl);380 }381 p!(pi: <i str("]"));382 pi383 }384 Expr::ExprObject(o) => {385 p!(new: str("{") >i nl {o.obj_body()} <i str("}"))386 }387 Expr::ExprArrayComp(arr) => {388 let mut pi = p!(new: str("[") {arr.expr()});389 for spec in arr.comp_specs() {390 p!(pi: str(" ") {spec});391 }392 p!(pi: str("]"));393 pi394 }395 Expr::ExprImport(v) => {396 p!(new: {v.import_kind()} str(" ") {v.text()})397 }398 Expr::ExprVar(n) => p!(new: {n.name()}),399 Expr::ExprLocal(l) => {400 let mut pi = p!(new: str("local") >i nl);401 for bind in l.binds() {402 p!(pi: {bind} str(",") nl);403 }404 p!(pi: <i str(";") nl {l.expr()});405 pi406 }407 Expr::ExprIfThenElse(ite) => {408 let mut pi =409 p!(new: str("if ") {ite.cond()} str(" then ") {ite.then().map(|t| t.expr())});410 if ite.else_kw_token().is_some() || ite.else_().is_some() {411 p!(pi: str(" else ") {ite.else_().map(|t| t.expr())})412 }413 pi414 }415 Expr::ExprFunction(f) => p!(new: str("function") {f.params_desc()} str(" ") {f.expr()}),416 Expr::ExprAssert(a) => p!(new: {a.assertion()} str("; ") {a.expr()}),417 Expr::ExprError(e) => p!(new: str("error ") {e.expr()}),418 Expr::ExprLiteral(l) => {419 p!(new: {l.literal()})420 }421 }422 }423}424425impl Printable for SourceFile {426 fn print(&self) -> PrintItems {427 assert!(self.expr().is_some());428 self.expr().print()429 }430}431432fn main() {433 let (parsed, _errors) = jrsonnet_rowan_parser::parse(434 r#"435436437 # Edit me!438 local b = import "b.libsonnet"; # comment439 local a = import "a.libsonnet";440441 local f(x,y)=x+y;442443 local {a: [b, ..., c], d, ...e} = null;444445 local ass = assert false : false; false;446447 local fn = function(a, b, c = 3) 4;448449 local comp = [a for b in c if d == e];450 local ocomp = {[k]: 1 for k in v};451452 local ? = skip;453454 local intr = $intrinsic(test);455 local intrId = $intrinsicId;456 local intrThisFile = $intrinsicThisFile;457458 local ie = a[expr];459460 local unary = !a;461462 local Template = {z: "foo"};463464 {465 local466467 h = 3,468 assert self.a == 1469470 : "error",471 "f": ((((((3)))))) ,472 "g g":473 f(4,2),474 arr: [[475 1, 2,476 ],477 3,478 {479 b: {480 c: {481 k: [16]482 }483 }484 }485 ],486 m: a[1::],487 m: b[::],488 k: if a == b then489490491 2492493 else Template {}494 } + Template495496497"#,498 );499500 // dbg!(errors);501 dbg!(&parsed);502503 let o = dprint_core::formatting::format(504 || parsed.print(),505 PrintOptions {506 indent_width: 2,507 max_width: 100,508 use_tabs: false,509 new_line_text: "\n",510 },511 );512 println!("{}", o);513}1use std::any::type_name;23use children::children_between;4use dprint_core::formatting::{PrintItems, PrintOptions};5use jrsonnet_rowan_parser::{6 nodes::{7 ArgsDesc, Assertion, BinaryOperator, Bind, CompSpec, Destruct, DestructArrayPart,8 DestructRest, Expr, Field, FieldName, ForSpec, IfSpec, ImportKind, LhsExpr, Literal,9 Member, Name, Number, ObjBody, ObjLocal, ParamsDesc, SliceDesc, SourceFile, Text,10 UnaryOperator,11 },12 AstNode, AstToken, SyntaxToken,13};1415use crate::{16 children::should_start_with_newline,17 comments::{format_comments, CommentLocation},18};1920mod children;21mod comments;22#[cfg(test)]23mod tests;2425pub trait Printable {26 fn print(&self) -> PrintItems;27}2829macro_rules! pi {30 (@i; $($t:tt)*) => {{31 #[allow(unused_mut)]32 let mut o = dprint_core::formatting::PrintItems::new();33 pi!(@s; o: $($t)*);34 o35 }};36 (@s; $o:ident: str($e:expr $(,)?) $($t:tt)*) => {{37 $o.push_str($e);38 pi!(@s; $o: $($t)*);39 }};40 (@s; $o:ident: nl $($t:tt)*) => {{41 $o.push_signal(dprint_core::formatting::Signal::NewLine);42 pi!(@s; $o: $($t)*);43 }};44 (@s; $o:ident: tab $($t:tt)*) => {{45 $o.push_signal(dprint_core::formatting::Signal::Tab);46 pi!(@s; $o: $($t)*);47 }};48 (@s; $o:ident: >i $($t:tt)*) => {{49 $o.push_signal(dprint_core::formatting::Signal::StartIndent);50 pi!(@s; $o: $($t)*);51 }};52 (@s; $o:ident: <i $($t:tt)*) => {{53 $o.push_signal(dprint_core::formatting::Signal::FinishIndent);54 pi!(@s; $o: $($t)*);55 }};56 (@s; $o:ident: {$expr:expr} $($t:tt)*) => {{57 $o.extend($expr.print());58 pi!(@s; $o: $($t)*);59 }};60 (@s; $o:ident: items($expr:expr) $($t:tt)*) => {{61 $o.extend($expr);62 pi!(@s; $o: $($t)*);63 }};64 (@s; $o:ident: if ($e:expr)($($then:tt)*) $($t:tt)*) => {{65 if $e {66 pi!(@s; $o: $($then)*);67 }68 pi!(@s; $o: $($t)*);69 }};70 (@s; $o:ident: ifelse ($e:expr)($($then:tt)*)($($else:tt)*) $($t:tt)*) => {{71 if $e {72 pi!(@s; $o: $($then)*);73 } else {74 pi!(@s; $o: $($else)*);75 }76 pi!(@s; $o: $($t)*);77 }};78 (@s; $i:ident:) => {}79}80macro_rules! p {81 (new: $($t:tt)*) => {82 pi!(@i; $($t)*)83 };84 ($o:ident: $($t:tt)*) => {85 pi!(@s; $o: $($t)*)86 };87}88pub(crate) use p;89pub(crate) use pi;9091impl<P> Printable for Option<P>92where93 P: Printable,94{95 fn print(&self) -> PrintItems {96 if let Some(v) = self {97 v.print()98 } else {99 p!(new: str(100 &format!(101 "/*missing {}*/",102 type_name::<P>().replace("jrsonnet_rowan_parser::generated::nodes::", "")103 ),104 ))105 }106 }107}108109impl Printable for SyntaxToken {110 fn print(&self) -> PrintItems {111 p!(new: str(&self.to_string()))112 }113}114115impl Printable for Text {116 fn print(&self) -> PrintItems {117 p!(new: str(&format!("{}", self)))118 }119}120impl Printable for Number {121 fn print(&self) -> PrintItems {122 p!(new: str(&format!("{}", self)))123 }124}125126impl Printable for Name {127 fn print(&self) -> PrintItems {128 p!(new: {self.ident_lit()})129 }130}131132impl Printable for DestructRest {133 fn print(&self) -> PrintItems {134 let mut pi = p!(new: str("..."));135 if let Some(name) = self.into() {136 p!(pi: {name});137 }138 pi139 }140}141142impl Printable for Destruct {143 fn print(&self) -> PrintItems {144 let mut pi = p!(new:);145 match self {146 Destruct::DestructFull(f) => {147 p!(pi: {f.name()})148 }149 Destruct::DestructSkip(_) => p!(pi: str("?")),150 Destruct::DestructArray(a) => {151 p!(pi: str("[") >i nl);152 for el in a.destruct_array_parts() {153 match el {154 DestructArrayPart::DestructArrayElement(e) => {155 p!(pi: {e.destruct()} str(",") nl)156 }157 DestructArrayPart::DestructRest(d) => {158 p!(pi: {d} str(",") nl)159 }160 }161 }162 p!(pi: <i str("]"));163 }164 Destruct::DestructObject(o) => {165 p!(pi: str("{") >i nl);166 for item in o.destruct_object_fields() {167 p!(pi: {item.field()});168 if let Some(des) = item.destruct() {169 p!(pi: str(": ") {des})170 }171 if let Some(def) = item.expr() {172 p!(pi: str(" = ") {def});173 }174 p!(pi: str(",") nl);175 }176 if let Some(rest) = o.destruct_rest() {177 p!(pi: {rest} nl)178 }179 p!(pi: <i str("}"));180 }181 }182 pi183 }184}185186impl Printable for FieldName {187 fn print(&self) -> PrintItems {188 match self {189 FieldName::FieldNameFixed(f) => {190 if let Some(id) = f.id() {191 p!(new: {id})192 } else if let Some(str) = f.text() {193 p!(new: {str})194 } else {195 p!(new: str("/*missing FieldName*/"))196 }197 }198 FieldName::FieldNameDynamic(d) => {199 p!(new: str("[") {d.expr()} str("]"))200 }201 }202 }203}204impl Printable for Field {205 fn print(&self) -> PrintItems {206 let mut pi = p!(new:);207 match self {208 Field::FieldNormal(n) => {209 p!(pi: {n.field_name()});210 if n.plus_token().is_some() {211 p!(pi: str("+"));212 }213 p!(pi: str(": ") {n.expr()});214 }215 Field::FieldMethod(m) => {216 p!(pi: {m.field_name()} {m.params_desc()} str(": ") {m.expr()});217 }218 }219 pi220 }221}222223impl Printable for ObjLocal {224 fn print(&self) -> PrintItems {225 p!(new: str("local ") {self.bind()})226 }227}228229impl Printable for Assertion {230 fn print(&self) -> PrintItems {231 let mut pi = p!(new: str("assert ") {self.condition()});232 if self.colon_token().is_some() || self.message().is_some() {233 p!(pi: str(": ") {self.message()})234 }235 pi236 }237}238239impl Printable for ParamsDesc {240 fn print(&self) -> PrintItems {241 let mut pi = p!(new: str("(") >i nl);242 for param in self.params() {243 p!(pi: {param.destruct()});244 if param.assign_token().is_some() || param.expr().is_some() {245 p!(pi: str(" = ") {param.expr()})246 }247 p!(pi: str(",") nl)248 }249 p!(pi: <i str(")"));250 pi251 }252}253impl Printable for ArgsDesc {254 fn print(&self) -> PrintItems {255 let mut pi = p!(new: str("(") >i nl);256 for arg in self.args() {257 if arg.name().is_some() || arg.assign_token().is_some() {258 p!(pi: {arg.name()} str(" = "));259 }260 p!(pi: {arg.expr()} str(",") nl)261 }262 p!(pi: <i str(")"));263 pi264 }265}266impl Printable for SliceDesc {267 fn print(&self) -> PrintItems {268 let mut pi = p!(new: str("["));269 if self.from().is_some() {270 p!(pi: {self.from()});271 }272 p!(pi: str(":"));273 if self.end().is_some() {274 p!(pi: {self.end().map(|e|e.expr())})275 }276 // Keep only one : in case if we don't need step277 if self.step().is_some() {278 p!(pi: str(":") {self.step().map(|e|e.expr())});279 }280 p!(pi: str("]"));281 pi282 }283}284285impl Printable for ObjBody {286 fn print(&self) -> PrintItems {287 match self {288 ObjBody::ObjBodyComp(_) => todo!(),289 ObjBody::ObjBodyMemberList(l) => {290 let mut pi = p!(new: str("{") >i nl);291 let (children, end_comments) = children_between::<Member>(292 l.syntax().clone(),293 l.l_brace_token().map(Into::into).as_ref(),294 l.r_brace_token().map(Into::into).as_ref(),295 );296 for mem in children.into_iter() {297 if mem.needs_newline_above() {298 p!(pi: nl);299 }300 p!(pi: items(format_comments(&mem.before_trivia, CommentLocation::AboveItem)));301 match mem.value {302 Member::MemberBindStmt(b) => {303 p!(pi: {b.obj_local()})304 }305 Member::MemberAssertStmt(ass) => {306 p!(pi: {ass.assertion()})307 }308 Member::MemberField(f) => {309 p!(pi: {f.field()})310 }311 }312 p!(pi: str(","));313 p!(pi: items(format_comments(&mem.inline_trivia, CommentLocation::ItemInline)));314 p!(pi: nl)315 }316317 // TODO: implement same thing as needs_newline_above, but for end comments318 if should_start_with_newline(&end_comments) {319 p!(pi: nl);320 }321 p!(pi: items(format_comments(&end_comments, CommentLocation::EndOfItems)));322 p!(pi: <i str("}"));323 pi324 }325 }326 }327}328impl Printable for UnaryOperator {329 fn print(&self) -> PrintItems {330 p!(new: str(self.text()))331 }332}333impl Printable for BinaryOperator {334 fn print(&self) -> PrintItems {335 p!(new: str(self.text()))336 }337}338impl Printable for Bind {339 fn print(&self) -> PrintItems {340 match self {341 Bind::BindDestruct(d) => {342 p!(new: {d.into()} str(" = ") {d.value()})343 }344 Bind::BindFunction(f) => {345 p!(new: str("function") {f.params()} str(" = ") {f.value()})346 }347 }348 }349}350impl Printable for Literal {351 fn print(&self) -> PrintItems {352 p!(new: str(&self.syntax().to_string()))353 }354}355impl Printable for ImportKind {356 fn print(&self) -> PrintItems {357 p!(new: str(&self.syntax().to_string()))358 }359}360impl Printable for LhsExpr {361 fn print(&self) -> PrintItems {362 p!(new: {self.expr()})363 }364}365impl Printable for ForSpec {366 fn print(&self) -> PrintItems {367 p!(new: str("for ") {self.bind()} str(" in ") {self.expr()})368 }369}370impl Printable for IfSpec {371 fn print(&self) -> PrintItems {372 p!(new: str("if ") {self.expr()})373 }374}375impl Printable for CompSpec {376 fn print(&self) -> PrintItems {377 match self {378 CompSpec::ForSpec(f) => f.print(),379 CompSpec::IfSpec(i) => i.print(),380 }381 }382}383impl Printable for Expr {384 fn print(&self) -> PrintItems {385 match self {386 Expr::ExprBinary(b) => {387 p!(new: {b.lhs()} str(" ") {b.binary_operator()} str(" ") {b.rhs()})388 }389 Expr::ExprUnary(u) => p!(new: {u.unary_operator()} {u.rhs()}),390 Expr::ExprSlice(s) => {391 p!(new: {s.expr()} {s.slice_desc()})392 }393 Expr::ExprIndex(i) => {394 p!(new: {i.expr()} str(".") {i.index()})395 }396 Expr::ExprIndexExpr(i) => p!(new: {i.base()} str("[") {i.index()} str("]")),397 Expr::ExprApply(a) => {398 let mut pi = p!(new: {a.expr()} {a.args_desc()});399 if a.tailstrict_kw_token().is_some() {400 p!(pi: str(" tailstrict"));401 }402 pi403 }404 Expr::ExprObjExtend(ex) => {405 p!(new: {ex.lhs_expr()} str(" ") {ex.expr()})406 }407 Expr::ExprParened(p) => {408 p!(new: str("(") {p.expr()} str(")"))409 }410 Expr::ExprIntrinsicThisFile(_) => p!(new: str("$intrinsicThisFile")),411 Expr::ExprIntrinsicId(_) => p!(new: str("$intrinsicId")),412 Expr::ExprIntrinsic(i) => p!(new: str("$intrinsic(") {i.name()} str(")")),413 Expr::ExprString(s) => p!(new: {s.text()}),414 Expr::ExprNumber(n) => p!(new: {n.number()}),415 Expr::ExprArray(a) => {416 let mut pi = p!(new: str("[") >i nl);417 for el in a.exprs() {418 p!(pi: {el} str(",") nl);419 }420 p!(pi: <i str("]"));421 pi422 }423 Expr::ExprObject(o) => {424 p!(new: {o.obj_body()})425 }426 Expr::ExprArrayComp(arr) => {427 let mut pi = p!(new: str("[") {arr.expr()});428 for spec in arr.comp_specs() {429 p!(pi: str(" ") {spec});430 }431 p!(pi: str("]"));432 pi433 }434 Expr::ExprImport(v) => {435 p!(new: {v.import_kind()} str(" ") {v.text()})436 }437 Expr::ExprVar(n) => p!(new: {n.name()}),438 Expr::ExprLocal(l) => {439 let mut pi = p!(new: str("local") >i nl);440 for bind in l.binds() {441 p!(pi: {bind} str(",") nl);442 }443 p!(pi: <i str(";") nl {l.expr()});444 pi445 }446 Expr::ExprIfThenElse(ite) => {447 let mut pi =448 p!(new: str("if ") {ite.cond()} str(" then ") {ite.then().map(|t| t.expr())});449 if ite.else_kw_token().is_some() || ite.else_().is_some() {450 p!(pi: str(" else ") {ite.else_().map(|t| t.expr())})451 }452 pi453 }454 Expr::ExprFunction(f) => p!(new: str("function") {f.params_desc()} str(" ") {f.expr()}),455 Expr::ExprAssert(a) => p!(new: {a.assertion()} str("; ") {a.expr()}),456 Expr::ExprError(e) => p!(new: str("error ") {e.expr()}),457 Expr::ExprLiteral(l) => {458 p!(new: {l.literal()})459 }460 }461 }462}463464impl Printable for SourceFile {465 fn print(&self) -> PrintItems {466 assert!(self.expr().is_some());467 self.expr().print()468 }469}470471fn main() {472 let (parsed, _errors) = jrsonnet_rowan_parser::parse(473 r#"474475476 # Edit me!477 local b = import "b.libsonnet"; # comment478 local a = import "a.libsonnet";479480 local f(x,y)=x+y;481482 local {a: [b, ..., c], d, ...e} = null;483484 local ass = assert false : false; false;485486 local fn = function(a, b, c = 3) 4;487488 local comp = [a for b in c if d == e];489 local ocomp = {[k]: 1 for k in v};490491 local ? = skip;492493 local intr = $intrinsic(test);494 local intrId = $intrinsicId;495 local intrThisFile = $intrinsicThisFile;496497 local ie = a[expr];498499 local unary = !a;500501 local Template = {z: "foo"};502503 {504 local505506 h = 3,507 assert self.a == 1508509 : "error",510 "f": ((((((3)))))) ,511 "g g":512 f(4,2),513 arr: [[514 1, 2,515 ],516 3,517 {518 b: {519 c: {520 k: [16]521 }522 }523 }524 ],525 m: a[1::],526 m: b[::],527528 comments: {529 _: '',530 // Plain comment531 a: '',532533 # Plain comment with empty line before534 b: '',535 /*Single-line multiline comment536537 */538 c: '',539540 /**Single-line multiline doc comment541542 */543 c: '',544545 /**multiline doc comment546 s547 */548 c: '',549550 /*551552 Multi-line553554 comment555 */556 d: '',557558 e: '', // Inline comment559560 k: '',561562 // Text after everything563 },564 comments2: {565 k: '',566 // Text after everything, but no newline above567 },568 k: if a == b then569570571 2572573 else Template {}574 } + Template575576577"#,578 );579580 // dbg!(errors);581 dbg!(&parsed);582583 let o = dprint_core::formatting::format(584 || parsed.print(),585 PrintOptions {586 indent_width: 2,587 max_width: 100,588 use_tabs: false,589 new_line_text: "\n",590 },591 );592 println!("{}", o);593}cmds/jrsonnet-fmt/src/snapshots/jrsonnet_fmt__tests__complex_comments_snapshot.snapdiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-fmt/src/snapshots/jrsonnet_fmt__tests__complex_comments_snapshot.snap
@@ -0,0 +1,53 @@
+---
+source: cmds/jrsonnet-fmt/src/tests.rs
+expression: "reformat(indoc!(\"{\n\t\t comments: {\n\t\t\t_: '',\n\t\t\t// Plain comment\n\t\t\ta: '',\n\n\t\t\t# Plain comment with empty line before\n\t\t\tb: '',\n\t\t\t/*Single-line multiline comment\n\n\t\t\t*/\n\t\t\tc: '',\n\n\t\t\t/**Single-line multiline doc comment\n\n\t\t\t*/\n\t\t\tc: '',\n\n\t\t\t/**Multiline doc\n\t\t\tComment\n\t\t\t*/\n\t\t\tc: '',\n\n\t\t\t/*\n\n\tMulti-line\n\n\tcomment\n\t\t\t*/\n\t\t\td: '',\n\n\t\t\te: '', // Inline comment\n\n\t\t\tk: '',\n\n\t\t\t// Text after everything\n\t\t },\n\t\t comments2: {\n\t\t\tk: '',\n\t\t\t// Text after everything, but no newline above\n\t\t },\n spacing: {\n a: '',\n\n b: '',\n },\n noSpacing: {\n a: '',\n b: '',\n },\n }\"))"
+---
+{
+ comments: {
+ _: '',
+ // Plain comment
+ a: '',
+
+ # Plain comment with empty line before
+ b: '',
+ /* Single-line multiline comment */
+ c: '',
+
+ /**
+ * Single-line multiline doc comment
+ */
+ c: '',
+
+ /**
+ * Multiline doc
+ * Comment
+ */
+ c: '',
+
+ /*
+ Multi-line
+
+ comment
+ */
+ d: '',
+
+ e: '', // Inline comment
+
+ k: '',
+
+ // Text after everything
+ },
+ comments2: {
+ k: '',
+ // Text after everything, but no newline above
+ },
+ spacing: {
+ a: '',
+
+ b: '',
+ },
+ noSpacing: {
+ a: '',
+ b: '',
+ },
+}
cmds/jrsonnet-fmt/src/tests.rsdiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-fmt/src/tests.rs
@@ -0,0 +1,124 @@
+use dprint_core::formatting::PrintOptions;
+use indoc::indoc;
+
+use crate::Printable;
+
+fn reformat(input: &str) -> String {
+ let (source, _) = jrsonnet_rowan_parser::parse(input);
+
+ dprint_core::formatting::format(
+ || source.print(),
+ PrintOptions {
+ indent_width: 2,
+ max_width: 100,
+ use_tabs: false,
+ new_line_text: "\n",
+ },
+ )
+}
+
+macro_rules! assert_formatted {
+ ($input:literal, $output:literal) => {
+ let formatted = reformat(indoc!($input));
+ let expected = indoc!($output);
+ if formatted != expected {
+ panic!(
+ "bad formatting, expected\n```\n{formatted}\n```\nto be equal to\n```\n{expected}\n```",
+ )
+ }
+ };
+}
+
+#[test]
+fn padding_stripped_for_multiline_comment() {
+ assert_formatted!(
+ "{
+ /*
+ Hello
+ World
+ */
+ _: null,
+ }",
+ "{
+ /*
+ Hello
+ World
+ */
+ _: null,
+ }"
+ );
+}
+
+// Fails
+#[test]
+fn last_comment_respects_spacing_with_inline_comment_above() {
+ assert_formatted!(
+ "{
+ a: '', // Inline
+
+ // Comment
+ }",
+ "{
+ a: '', // Inline
+
+ // Comment
+ }"
+ );
+}
+
+#[test]
+fn complex_comments_snapshot() {
+ insta::assert_display_snapshot!(reformat(indoc!(
+ "{
+ comments: {
+ _: '',
+ // Plain comment
+ a: '',
+
+ # Plain comment with empty line before
+ b: '',
+ /*Single-line multiline comment
+
+ */
+ c: '',
+
+ /**Single-line multiline doc comment
+
+ */
+ c: '',
+
+ /**Multiline doc
+ Comment
+ */
+ c: '',
+
+ /*
+
+ Multi-line
+
+ comment
+ */
+ d: '',
+
+ e: '', // Inline comment
+
+ k: '',
+
+ // Text after everything
+ },
+ comments2: {
+ k: '',
+ // Text after everything, but no newline above
+ },
+ spacing: {
+ a: '',
+
+ b: '',
+ },
+ noSpacing: {
+ a: '',
+ b: '',
+ },
+ }"
+ )))
+}
cmds/jrsonnet-lsp/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-lsp/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "jrsonnet-lsp"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+anyhow = "1.0.48"
+jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator" }
+jrsonnet-rowan-parser = { path = "../../crates/jrsonnet-rowan-parser" }
+lsp-server = "0.6.0"
+lsp-types = "0.93.0"
+serde = "1.0.130"
+serde_json = "1.0.71"
cmds/jrsonnet-lsp/src/main.rsdiffbeforeafterboth--- /dev/null
+++ b/cmds/jrsonnet-lsp/src/main.rs
@@ -0,0 +1,188 @@
+use std::{fs::File, io::Write, path::PathBuf, str::FromStr};
+
+use lsp_server::{Connection, ErrorCode, Message, Request, RequestId, Response};
+use lsp_types::{
+ notification::{DidChangeTextDocument, DidOpenTextDocument, Notification},
+ request::{DocumentLinkRequest, HoverRequest},
+ CompletionOptions, DidChangeTextDocumentParams, DidOpenTextDocumentParams, DocumentLink,
+ DocumentLinkOptions, ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind,
+ TextDocumentSyncOptions, Url, WorkDoneProgressOptions,
+};
+
+fn main() {
+ let mut log = File::create("test").unwrap();
+ writeln!(log, "start").unwrap();
+ let (connection, io_threads) = Connection::stdio();
+ let capabilities = serde_json::to_value(&ServerCapabilities {
+ completion_provider: Some(CompletionOptions::default()),
+ definition_provider: Some(lsp_types::OneOf::Left(true)),
+ document_link_provider: Some(DocumentLinkOptions {
+ resolve_provider: Some(false),
+ work_done_progress_options: WorkDoneProgressOptions::default(),
+ }),
+ hover_provider: Some(lsp_types::HoverProviderCapability::Simple(true)),
+ text_document_sync: Some(TextDocumentSyncCapability::Options(
+ TextDocumentSyncOptions {
+ change: Some(TextDocumentSyncKind::FULL),
+ open_close: Some(true),
+ ..TextDocumentSyncOptions::default()
+ },
+ )),
+ ..ServerCapabilities::default()
+ })
+ .expect("failed to convert capabilities to json");
+
+ connection
+ .initialize(capabilities)
+ .expect("failed to initialize connection");
+
+ writeln!(log, "initialized").unwrap();
+
+ main_loop(&mut log, &connection).expect("main loop failed");
+
+ io_threads.join().expect("failed to join io_threads");
+}
+fn main_loop(log: &mut File, connection: &Connection) -> anyhow::Result<()> {
+ // let mut es = EvaluationState::default();
+ // es.set_import_resolver(Box::new(FileImportResolver::default()));
+
+ let reply = |response: Response| {
+ connection
+ .sender
+ .send(Message::Response(response))
+ .expect("failed to respond");
+ };
+
+ for msg in &connection.receiver {
+ match msg {
+ Message::Response(_) => (),
+ Message::Request(req) => {
+ if connection.handle_shutdown(&req)? {
+ return Ok(());
+ }
+ if let Some((id, params)) = cast::<DocumentLinkRequest>(&req) {
+ reply(Response::new_ok(id, <Vec<DocumentLink>>::new()));
+ } else if let Some((id, params)) = cast::<HoverRequest>(&req) {
+ let pos = params
+ .text_document_position_params
+ .text_document
+ .uri
+ .path();
+ let buf = PathBuf::from_str(pos).unwrap();
+ // let pos = es
+ // .map_from_source_location(
+ // &buf,
+ // params.text_document_position_params.position.line as usize + 1,
+ // params.text_document_position_params.position.character as usize + 1,
+ // )
+ // .unwrap();
+ // let el = ExprLocation(buf.clone().into(), pos as usize, pos as usize);
+ // let es2 = es.clone();
+ // reply(Response::new_ok(
+ // id,
+ // Some(Hover {
+ // range: None,
+ // contents: HoverContents::Markup(MarkupContent {
+ // kind: MarkupKind::Markdown,
+ // value: es
+ // .run_in_state_with_breakpoint(el, move || {
+ // es2.reset_evaluation_state(&buf);
+ // es2.import_file(&PathBuf::new(), &buf)?
+ // .to_string()
+ // .map(|_| ())
+ // })
+ // .unwrap()
+ // .unwrap_or_else(|| Val::Null)
+ // .value_type()
+ // .to_string(),
+ // }),
+ // }),
+ // ));
+ } else {
+ reply(Response::new_err(
+ req.id,
+ ErrorCode::MethodNotFound as i32,
+ format!("unrecognized request {}", req.method),
+ ))
+ }
+ /*
+ if let Some((id, params)) = cast::<DocumentLinkRequest>(&req) {
+ let links = handle_links(&files, params).unwrap_or_default();
+ reply(Response::new_ok(id, links));
+ } else if let Some((id, params)) = cast::<GotoDefinition>(&req) {
+ if let Some(loc) = handle_goto(&files, params) {
+ reply(Response::new_ok(id, loc))
+ } else {
+ reply(Response::new_ok(id, ()))
+ }
+ } else if let Some((id, params)) = cast::<HoverRequest>(&req) {
+ match handle_hover(&files, params) {
+ Some((range, markdown)) => {
+ reply(Response::new_ok(
+ id,
+ Hover {
+ contents: HoverContents::Markup(MarkupContent {
+ kind: MarkupKind::Markdown,
+ value: markdown,
+ }),
+ range,
+ },
+ ));
+ }
+ None => {
+ reply(Response::new_ok(id, ()));
+ }
+ }
+ } else if let Some((id, params)) = cast::<Completion>(&req) {
+ let completions = handle_completion(&files, params.text_document_position)
+ .unwrap_or_default();
+ reply(Response::new_ok(id, completions));
+ } else
+ */
+ }
+ Message::Notification(req) => {
+ let mut handle = |text: String, uri: Url| {
+ writeln!(log, "updated file: {:?}", uri).unwrap();
+ let path = match PathBuf::from_str(uri.path()) {
+ Ok(x) => x,
+ Err(_) => return,
+ };
+ let (ast, errors) = jrsonnet_rowan_parser::parse(&text);
+ // es.add_parsed_file(path.into(), text.into(), parsed)
+ // .unwrap();
+ writeln!(log, "parsed: {:?}", uri).unwrap();
+ };
+
+ match &*req.method {
+ DidOpenTextDocument::METHOD => {
+ let params: DidOpenTextDocumentParams =
+ match serde_json::from_value(req.params) {
+ Ok(x) => x,
+ Err(_) => continue,
+ };
+ handle(params.text_document.text, params.text_document.uri);
+ }
+ DidChangeTextDocument::METHOD => {
+ let params: DidChangeTextDocumentParams =
+ match serde_json::from_value(req.params) {
+ Ok(x) => x,
+ Err(_) => continue,
+ };
+ for change in params.content_changes.into_iter() {
+ handle(change.text, params.text_document.uri.clone());
+ }
+ }
+ _ => continue,
+ }
+ }
+ }
+ }
+ Ok(())
+}
+fn cast<R>(req: &Request) -> Option<(RequestId, R::Params)>
+where
+ R: lsp_types::request::Request,
+ R::Params: serde::de::DeserializeOwned,
+{
+ req.clone().extract(R::METHOD).ok()
+}
crates/jrsonnet-rowan-parser/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-rowan-parser/Cargo.toml
+++ b/crates/jrsonnet-rowan-parser/Cargo.toml
@@ -4,19 +4,19 @@
edition = "2021"
[dependencies]
-anyhow = "1.0.51"
+anyhow = "1.0"
backtrace = "0.3.63"
drop_bomb = "0.1.5"
-indoc = "1.0.3"
-logos = "0.12.0"
-miette = { version = "4.2.1", features = ["fancy"] }
-rowan = "0.15.0"
-text-size = "1.1.0"
-thiserror = "1.0.30"
+indoc = "1.0"
+logos = "0.12"
+miette = { version = "4.2", features = ["fancy"] }
+rowan = "0.15"
+text-size = "1.1"
+thiserror = "1.0"
[dev-dependencies]
backtrace = "0.3.63"
-indoc = "1.0.3"
-insta = "1.10.0"
-anyhow = "1.0.57"
+indoc = "1.0"
+insta = "1.15"
+anyhow = "1.0"
jrsonnet-stdlib = { path = "../jrsonnet-stdlib" }
crates/jrsonnet-rowan-parser/jsonnet.ungramdiffbeforeafterboth--- a/crates/jrsonnet-rowan-parser/jsonnet.ungram
+++ b/crates/jrsonnet-rowan-parser/jsonnet.ungram
@@ -56,9 +56,7 @@
(Expr (',' Expr)* ','?)?
']'
ExprObject =
- '{'
ObjBody
- '}'
ExprArrayComp =
'['
Expr
@@ -168,6 +166,7 @@
(name:Name '=')? Expr
ObjBodyComp =
+ '{'
pre:ObjLocalPostComma*
'['
key:LhsExpr
@@ -177,8 +176,11 @@
value:Expr
post:ObjLocalPreComma*
CompSpec*
+ '}'
ObjBodyMemberList =
+ '{'
(Member (',' Member)* ','?)?
+ '}'
ObjBody =
ObjBodyComp
| ObjBodyMemberList
crates/jrsonnet-rowan-parser/src/event.rsdiffbeforeafterboth--- a/crates/jrsonnet-rowan-parser/src/event.rs
+++ b/crates/jrsonnet-rowan-parser/src/event.rs
@@ -24,6 +24,10 @@
Token {
kind: SyntaxKind,
},
+ /// Push token, but do not eat anything,
+ VirtualToken {
+ kind: SyntaxKind,
+ },
/// Position of finished node
Finish {
/// Same as forward_parent of Start, but for wrapping
@@ -105,6 +109,13 @@
self.token(kind);
eat_start_whitespace = true;
}
+ Event::VirtualToken { kind } => {
+ if eat_start_whitespace {
+ self.skip_whitespace();
+ }
+ self.virtual_token(kind);
+ eat_start_whitespace = false;
+ }
Event::Finish { wrapper } => {
self.builder.finish_node();
depth -= 1;
@@ -124,7 +135,7 @@
}
eat_start_whitespace = true;
}
- Event::Pending => panic!("placeholder should not end in events"),
+ Event::Pending => panic!("pending event should not appear in finished events"),
Event::Noop => {}
Event::Error(e) => {
self.errors.push(e);
@@ -137,6 +148,9 @@
errors: self.errors,
}
}
+ fn virtual_token(&mut self, kind: SyntaxKind) {
+ self.builder.token(JsonnetLanguage::kind_to_raw(kind), "")
+ }
fn token(&mut self, kind: SyntaxKind) {
let lexeme = self.lexemes[self.offset];
self.builder
crates/jrsonnet-rowan-parser/src/generated/nodes.rsdiffbeforeafterboth--- a/crates/jrsonnet-rowan-parser/src/generated/nodes.rs
+++ b/crates/jrsonnet-rowan-parser/src/generated/nodes.rs
@@ -291,15 +291,9 @@
pub(crate) syntax: SyntaxNode,
}
impl ExprObject {
- pub fn l_brace_token(&self) -> Option<SyntaxToken> {
- support::token(&self.syntax, T!['{'])
- }
pub fn obj_body(&self) -> Option<ObjBody> {
support::child(&self.syntax)
}
- pub fn r_brace_token(&self) -> Option<SyntaxToken> {
- support::token(&self.syntax, T!['}'])
- }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -538,6 +532,9 @@
pub(crate) syntax: SyntaxNode,
}
impl ObjBodyComp {
+ pub fn l_brace_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['{'])
+ }
pub fn pre(&self) -> AstChildren<ObjLocalPostComma> {
support::children(&self.syntax)
}
@@ -565,6 +562,9 @@
pub fn comp_specs(&self) -> AstChildren<CompSpec> {
support::children(&self.syntax)
}
+ pub fn r_brace_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['}'])
+ }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -598,9 +598,15 @@
pub(crate) syntax: SyntaxNode,
}
impl ObjBodyMemberList {
+ pub fn l_brace_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['{'])
+ }
pub fn members(&self) -> AstChildren<Member> {
support::children(&self.syntax)
}
+ pub fn r_brace_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T!['}'])
+ }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
crates/jrsonnet-rowan-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-rowan-parser/src/lib.rs
+++ b/crates/jrsonnet-rowan-parser/src/lib.rs
@@ -1,5 +1,11 @@
#![deny(unused_must_use)]
+use event::Sink;
+use generated::nodes::{SourceFile, Trivia};
+use lex::lex;
+use parser::{Parser, SyntaxError};
+pub use rowan;
+
mod ast;
mod event;
mod generated;
@@ -13,18 +19,18 @@
mod token_set;
pub use ast::{AstChildren, AstNode, AstToken};
-use event::Sink;
-use generated::nodes::SourceFile;
pub use generated::{nodes, syntax_kinds::SyntaxKind};
-pub use language::{
- JsonnetLanguage, PreorderWithTokens, SyntaxElement, SyntaxElementChildren, SyntaxNode,
- SyntaxNodeChildren, SyntaxToken,
-};
-use lex::lex;
-use parser::{Parser, SyntaxError};
+pub use language::*;
+pub use token_set::SyntaxKindSet;
+
pub fn parse(input: &str) -> (SourceFile, Vec<SyntaxError>) {
let lexemes = lex(input);
- let parser = Parser::new(&lexemes);
+ let kinds = lexemes
+ .iter()
+ .map(|l| l.kind)
+ .filter(|k| !Trivia::can_cast(*k))
+ .collect();
+ let parser = Parser::new(kinds);
let events = parser.parse();
let sink = Sink::new(events, &lexemes);
crates/jrsonnet-rowan-parser/src/marker.rsdiffbeforeafterboth--- a/crates/jrsonnet-rowan-parser/src/marker.rs
+++ b/crates/jrsonnet-rowan-parser/src/marker.rs
@@ -113,21 +113,3 @@
completed
}
}
-
-pub trait AsRange {
- fn as_range(&self, p: &Parser) -> TextRange;
- fn end_token(&self) -> usize;
-}
-
-impl AsRange for FinishedRanger {
- fn as_range(&self, p: &Parser) -> TextRange {
- TextRange::new(
- p.start_of_token(self.start_token),
- p.end_of_token(self.end_token),
- )
- }
-
- fn end_token(&self) -> usize {
- self.end_token
- }
-}
crates/jrsonnet-rowan-parser/src/parser.rsdiffbeforeafterboth--- a/crates/jrsonnet-rowan-parser/src/parser.rs
+++ b/crates/jrsonnet-rowan-parser/src/parser.rs
@@ -6,7 +6,7 @@
use crate::{
event::Event,
lex::Lexeme,
- marker::{AsRange, CompletedMarker, Marker, Ranger},
+ marker::{CompletedMarker, Marker, Ranger},
nodes::{BinaryOperatorKind, Literal, Number, Text, Trivia, UnaryOperatorKind},
token_set::SyntaxKindSet,
AstToken, SyntaxKind,
@@ -33,9 +33,9 @@
}
}
-pub struct Parser<'i> {
+pub struct Parser {
// TODO: remove all trivia before feeding to parser?
- lexemes: &'i [Lexeme<'i>],
+ kinds: Vec<SyntaxKind>,
pub offset: usize,
pub events: Vec<Event>,
pub entered: u32,
@@ -103,10 +103,10 @@
}
}
-impl<'i> Parser<'i> {
- pub fn new(lexemes: &'i [Lexeme<'i>]) -> Self {
+impl Parser {
+ pub fn new(kinds: Vec<SyntaxKind>) -> Self {
Self {
- lexemes,
+ kinds,
offset: 0,
events: vec![],
entered: 0,
@@ -134,14 +134,12 @@
.set(ExpectedSyntaxTrackingState::Unnamed);
}
pub fn start(&mut self) -> Marker {
- self.skip_trivia();
let start_event_idx = self.events.len();
self.events.push(Event::Pending);
self.entered += 1;
Marker::new(start_event_idx)
}
pub fn start_ranger(&mut self) -> Ranger {
- self.skip_trivia();
let pos = self.offset;
Ranger { pos }
}
@@ -178,45 +176,7 @@
} else {
self.error_with_no_skip();
}
- }
- fn current_token(&self) -> Lexeme<'i> {
- self.lexemes[self.offset]
- }
- fn previous_token(&mut self) -> Option<Lexeme<'i>> {
- if self.offset == 0 {
- return None;
- }
- let mut previous_token_idx = self.offset - 1;
- while self
- .lexemes
- .get(previous_token_idx)
- .map_or(false, |l| Trivia::can_cast(l.kind))
- && previous_token_idx != 0
- {
- previous_token_idx -= 1;
- }
-
- Some(self.lexemes[previous_token_idx])
- }
- pub fn start_of_token(&self, mut idx: usize) -> TextSize {
- while Trivia::can_cast(self.lexemes[idx].kind) {
- idx += 1;
- }
- self.lexemes[idx].range.start()
}
- pub fn end_of_token(&self, mut idx: usize) -> TextSize {
- while Trivia::can_cast(self.lexemes[idx].kind) {
- idx -= 1;
- }
- self.lexemes[idx].range.end()
- }
- pub(crate) fn custom_error(&mut self, marker: impl AsRange, error: impl AsRef<str>) {
- self.last_error_token = marker.end_token();
- self.events.push(Event::Error(SyntaxError::Custom {
- error: error.as_ref().to_string(),
- range: marker.as_range(self),
- }));
- }
pub(crate) fn error_with_recovery_set(
&mut self,
recovery_set: SyntaxKindSet,
@@ -238,27 +198,26 @@
self.expected_syntax_tracking_state
.set(ExpectedSyntaxTrackingState::Unnamed);
- self.skip_trivia();
if self.at_end() || self.at_ts(recovery_set) {
- let range = self
- .previous_token()
- .map(|t| t.range)
- .unwrap_or_else(|| TextRange::at(TextSize::from(0), TextSize::from(0)));
+ // let range = self
+ // .previous_token()
+ // .map(|t| t.range)
+ // .unwrap_or_else(|| TextRange::at(TextSize::from(0), TextSize::from(0)));
- self.events.push(Event::Error(SyntaxError::Missing {
- expected: expected_syntax,
- offset: range.end(),
- }));
+ // self.events.push(Event::Error(SyntaxError::Missing {
+ // expected: expected_syntax,
+ // offset: range.end(),
+ // }));
return None;
}
- let current_token = self.current_token();
+ let current_token = self.current();
- self.events.push(Event::Error(SyntaxError::Unexpected {
- expected: expected_syntax,
- found: current_token.kind,
- range: current_token.range,
- }));
+ // self.events.push(Event::Error(SyntaxError::Unexpected {
+ // expected: expected_syntax,
+ // found: current_token.kind,
+ // range: current_token.range,
+ // }));
self.clear_expected_syntaxes();
self.last_error_token = self.offset;
@@ -267,17 +226,14 @@
Some(m.complete(self, SyntaxKind::ERROR))
}
fn bump_assert(&mut self, kind: SyntaxKind) {
- self.skip_trivia();
assert!(self.at(kind), "expected {:?}", kind);
self.bump_remap(self.current());
}
fn bump(&mut self) {
- self.skip_trivia();
self.bump_remap(self.current());
}
fn bump_remap(&mut self, kind: SyntaxKind) {
- self.skip_trivia();
- assert_ne!(self.offset, self.lexemes.len(), "already at end");
+ assert_ne!(self.offset, self.kinds.len(), "already at end");
self.events.push(Event::Token { kind });
self.offset += 1;
self.clear_expected_syntaxes();
@@ -302,7 +258,7 @@
{
let next = 20;
write!(out, "\n\nNext {next} tokens:").unwrap();
- for (i, tok) in self.lexemes.iter().skip(self.offset).take(next).enumerate() {
+ for (i, tok) in self.kinds.iter().skip(self.offset).take(next).enumerate() {
write!(out, "\n{i}. {tok:?}").unwrap();
}
}
@@ -314,39 +270,12 @@
self.step();
let mut offset = self.offset;
for _ in 0..i {
- while self
- .lexemes
- .get(offset)
- .map(|l| Trivia::can_cast(l.kind))
- .unwrap_or(false)
- {
- offset += 1;
- }
offset += 1;
}
- while self
- .lexemes
- .get(offset)
- .map(|l| Trivia::can_cast(l.kind))
- .unwrap_or(false)
- {
- offset += 1;
- }
- self.lexemes.get(offset).map(|l| l.kind).unwrap_or(EOF)
+ self.kinds.get(offset).copied().unwrap_or(EOF)
}
fn current(&self) -> SyntaxKind {
self.nth(0)
- }
- fn skip_trivia(&mut self) {
- while Trivia::can_cast(self.peek_raw()) {
- self.offset += 1;
- }
- }
- fn peek_raw(&mut self) -> SyntaxKind {
- self.lexemes
- .get(self.offset)
- .map(|l| l.kind)
- .unwrap_or(SyntaxKind::EOF)
}
#[must_use]
pub(crate) fn expected_syntax_name(&mut self, name: &'static str) -> ExpectedSyntaxGuard {
@@ -507,15 +436,15 @@
None
};
let params = if p.at(T!['(']) {
- if let Some(plus) = plus {
- p.custom_error(plus, "can't extend with method");
- }
+ // if let Some(plus) = plus {
+ // p.custom_error(plus, "can't extend with method");
+ // }
params_desc(p);
- if p.at(T![+]) {
- let r = p.start_ranger();
- p.bump();
- p.custom_error(r.finish(p), "can't extend with method");
- }
+ // if p.at(T![+]) {
+ // let r = p.start_ranger();
+ // p.bump();
+ // p.custom_error(r.finish(p), "can't extend with method");
+ // }
true
} else {
false
@@ -669,10 +598,10 @@
if elems > 1 && !compspecs.is_empty() {
for spec in compspecs {
- p.custom_error(
- spec,
- "compspec may only be used if there is only one array element",
- )
+ // p.custom_error(
+ // spec,
+ // "compspec may only be used if there is only one array element",
+ // )
}
m.complete(p, EXPR_ARRAY)
@@ -797,9 +726,9 @@
} else if p.at(T![...]) {
let m_err = p.start_ranger();
destruct_rest(p);
- if had_rest {
- p.custom_error(m_err.finish(p), "only one rest can be present in array");
- }
+ // if had_rest {
+ // p.custom_error(m_err.finish(p), "only one rest can be present in array");
+ // }
had_rest = true;
} else {
destruct(p);
@@ -822,9 +751,9 @@
} else if p.at(T![...]) {
let m_err = p.start_ranger();
destruct_rest(p);
- if had_rest {
- p.custom_error(m_err.finish(p), "only one rest can be present in object");
- }
+ // if had_rest {
+ // p.custom_error(m_err.finish(p), "only one rest can be present in object");
+ // }
had_rest = true;
} else {
if had_rest {
crates/jrsonnet-rowan-parser/src/token_set.rsdiffbeforeafterboth--- a/crates/jrsonnet-rowan-parser/src/token_set.rs
+++ b/crates/jrsonnet-rowan-parser/src/token_set.rs
@@ -34,9 +34,9 @@
#[macro_export]
macro_rules! TS {
($($tt:tt)*) => {
- SyntaxKindSet::new(&[
+ $crate::SyntaxKindSet::new(&[
$(
- T![$tt]
+ $crate::T![$tt]
),*
])
};
jrsonnet-lsp/Cargo.tomldiffbeforeafterboth--- a/jrsonnet-lsp/Cargo.toml
+++ /dev/null
@@ -1,15 +0,0 @@
-[package]
-name = "jrsonnet-lsp"
-version = "0.1.0"
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-anyhow = "1.0.48"
-jrsonnet-evaluator = { path = "../jrsonnet-evaluator" }
-jrsonnet-parser = { path = "../jrsonnet-parser" }
-lsp-server = "0.5.2"
-lsp-types = "0.92.0"
-serde = "1.0.130"
-serde_json = "1.0.71"
jrsonnet-lsp/src/main.rsdiffbeforeafterboth--- a/jrsonnet-lsp/src/main.rs
+++ /dev/null
@@ -1,211 +0,0 @@
-use std::{
- collections::HashMap,
- fs::File,
- path::{Path, PathBuf},
- str::FromStr,
-};
-
-use jrsonnet_evaluator::{EvaluationState, FileImportResolver, Val};
-use jrsonnet_parser::{ExprLocation, ParserSettings};
-use lsp_server::{Connection, ErrorCode, Message, Request, RequestId, Response};
-use lsp_types::{
- notification::{DidChangeTextDocument, DidOpenTextDocument, Notification},
- request::{DocumentLinkRequest, HoverRequest},
- CompletionOptions, DidChangeTextDocumentParams, DidOpenTextDocumentParams, DocumentLink,
- DocumentLinkOptions, Hover, HoverContents, MarkupContent, MarkupKind, ServerCapabilities,
- TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, Url,
- WorkDoneProgressOptions,
-};
-
-use std::io::Write;
-
-fn main() {
- let mut log = File::create("test").unwrap();
- writeln!(log, "start").unwrap();
- let (connection, io_threads) = Connection::stdio();
- let capabilities = serde_json::to_value(&ServerCapabilities {
- completion_provider: Some(CompletionOptions::default()),
- definition_provider: Some(lsp_types::OneOf::Left(true)),
- document_link_provider: Some(DocumentLinkOptions {
- resolve_provider: Some(false),
- work_done_progress_options: WorkDoneProgressOptions::default(),
- }),
- hover_provider: Some(lsp_types::HoverProviderCapability::Simple(true)),
- text_document_sync: Some(TextDocumentSyncCapability::Options(
- TextDocumentSyncOptions {
- change: Some(TextDocumentSyncKind::FULL),
- open_close: Some(true),
- ..TextDocumentSyncOptions::default()
- },
- )),
- ..ServerCapabilities::default()
- })
- .expect("failed to convert capabilities to json");
-
- connection
- .initialize(capabilities)
- .expect("failed to initialize connection");
-
- writeln!(log, "initialized").unwrap();
-
- main_loop(&mut log, &connection).expect("main loop failed");
-
- io_threads.join().expect("failed to join io_threads");
-}
-fn main_loop(log: &mut File, connection: &Connection) -> anyhow::Result<()> {
- let mut es = EvaluationState::default();
- es.set_import_resolver(Box::new(FileImportResolver::default()));
-
- let reply = |response: Response| {
- connection
- .sender
- .send(Message::Response(response))
- .expect("failed to respond");
- };
-
- for msg in &connection.receiver {
- match msg {
- Message::Response(_) => (),
- Message::Request(req) => {
- if connection.handle_shutdown(&req)? {
- return Ok(());
- }
- if let Some((id, params)) = cast::<DocumentLinkRequest>(&req) {
- reply(Response::new_ok(id, <Vec<DocumentLink>>::new()));
- } else if let Some((id, params)) = cast::<HoverRequest>(&req) {
- let pos = params
- .text_document_position_params
- .text_document
- .uri
- .path();
- let buf = PathBuf::from_str(pos).unwrap();
- let pos = es
- .map_from_source_location(
- &buf,
- params.text_document_position_params.position.line as usize + 1,
- params.text_document_position_params.position.character as usize + 1,
- )
- .unwrap();
- let el = ExprLocation(buf.clone().into(), pos as usize, pos as usize);
- let es2 = es.clone();
- // reply(Response::new_ok(
- // id,
- // Some(Hover {
- // range: None,
- // contents: HoverContents::Markup(MarkupContent {
- // kind: MarkupKind::Markdown,
- // value: es
- // .run_in_state_with_breakpoint(el, move || {
- // es2.reset_evaluation_state(&buf);
- // es2.import_file(&PathBuf::new(), &buf)?
- // .to_string()
- // .map(|_| ())
- // })
- // .unwrap()
- // .unwrap_or_else(|| Val::Null)
- // .value_type()
- // .to_string(),
- // }),
- // }),
- // ));
- } else
- /*
- if let Some((id, params)) = cast::<DocumentLinkRequest>(&req) {
- let links = handle_links(&files, params).unwrap_or_default();
- reply(Response::new_ok(id, links));
- } else if let Some((id, params)) = cast::<GotoDefinition>(&req) {
- if let Some(loc) = handle_goto(&files, params) {
- reply(Response::new_ok(id, loc))
- } else {
- reply(Response::new_ok(id, ()))
- }
- } else if let Some((id, params)) = cast::<HoverRequest>(&req) {
- match handle_hover(&files, params) {
- Some((range, markdown)) => {
- reply(Response::new_ok(
- id,
- Hover {
- contents: HoverContents::Markup(MarkupContent {
- kind: MarkupKind::Markdown,
- value: markdown,
- }),
- range,
- },
- ));
- }
- None => {
- reply(Response::new_ok(id, ()));
- }
- }
- } else if let Some((id, params)) = cast::<Completion>(&req) {
- let completions = handle_completion(&files, params.text_document_position)
- .unwrap_or_default();
- reply(Response::new_ok(id, completions));
- } else
- */
- {
- reply(Response::new_err(
- req.id,
- ErrorCode::MethodNotFound as i32,
- format!("unrecognized request {}", req.method),
- ))
- }
- }
- Message::Notification(req) => {
- let mut handle = |text: String, uri: Url| {
- writeln!(log, "updated file: {:?}", uri).unwrap();
- let path = match PathBuf::from_str(uri.path()) {
- Ok(x) => x,
- Err(_) => return,
- };
- let parsed = match jrsonnet_parser::parse(
- &text,
- &ParserSettings {
- file_name: path.clone().into(),
- },
- ) {
- Ok(v) => v,
- Err(e) => {
- writeln!(log, "fuck D: {:?}", e).unwrap();
- return;
- // connection.sender.send(Message::Notification(Notification::new_err(req.id, ErrorCode::ParseError as i32, format!("Fuck D: {:?}", e))))
- }
- };
- es.add_parsed_file(path.into(), text.into(), parsed)
- .unwrap();
- writeln!(log, "parsed: {:?}", uri).unwrap();
- };
-
- match &*req.method {
- DidOpenTextDocument::METHOD => {
- let params: DidOpenTextDocumentParams =
- match serde_json::from_value(req.params) {
- Ok(x) => x,
- Err(_) => continue,
- };
- handle(params.text_document.text, params.text_document.uri);
- }
- DidChangeTextDocument::METHOD => {
- let params: DidChangeTextDocumentParams =
- match serde_json::from_value(req.params) {
- Ok(x) => x,
- Err(_) => continue,
- };
- for change in params.content_changes.into_iter() {
- handle(change.text, params.text_document.uri.clone());
- }
- }
- _ => continue,
- }
- }
- }
- }
- Ok(())
-}
-fn cast<R>(req: &Request) -> Option<(RequestId, R::Params)>
-where
- R: lsp_types::request::Request,
- R::Params: serde::de::DeserializeOwned,
-{
- req.clone().extract(R::METHOD).ok()
-}
xtask/src/sourcegen/ast.rsdiffbeforeafterboth--- a/xtask/src/sourcegen/ast.rs
+++ b/xtask/src/sourcegen/ast.rs
@@ -151,6 +151,19 @@
if let Some(old) = types.insert(field.ty(), field.method_name(kinds)) {
panic!("{name}.{} has same type as {name}.{}, resolve conflict by wrapping one field: {}", old, field.method_name(kinds), field.ty());
}
+ // TODO: check for assignable field types, i.e you can have
+ // ```
+ // SomeEnum =
+ // SomeItem
+ // | SomeOtherItem
+ // ```
+ // And check above will fail to detect conflict in
+ // ```
+ // SomeStruct =
+ // SomeEnum
+ // SomeItem
+ // ```
+ // Despite generating getters, which will both return SomeEnum
}
res.nodes.push(AstNodeSrc {
doc: Vec::new(),