Skip to content

Commit 51f34eb

Browse files
committed
Ruby: diagnostics: add support for markdown messages
1 parent 006ee5a commit 51f34eb

File tree

3 files changed

+120
-58
lines changed

3 files changed

+120
-58
lines changed

ruby/extractor/src/diagnostics.rs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ pub struct LogWriter {
8888
}
8989

9090
impl LogWriter {
91-
pub fn message(&self, id: &str, name: &str) -> DiagnosticMessage {
91+
pub fn new_entry(&self, id: &str, name: &str) -> DiagnosticMessage {
9292
DiagnosticMessage {
9393
timestamp: chrono::Utc::now(),
9494
source: Source {
@@ -199,6 +199,17 @@ impl DiagnosticLoggers {
199199
}
200200
}
201201

202+
fn longest_backtick_sequence_length(text: &str) -> usize {
203+
let mut count = 0;
204+
for c in text.chars() {
205+
if c == '`' {
206+
count += 1;
207+
} else {
208+
count = 0;
209+
}
210+
}
211+
count
212+
}
202213
impl DiagnosticMessage {
203214
pub fn full_error_message(&self) -> String {
204215
match &self.location {
@@ -216,12 +227,38 @@ impl DiagnosticMessage {
216227
}
217228
}
218229

219-
pub fn text(&mut self, text: &str) -> &mut Self {
230+
fn text(&mut self, text: &str) -> &mut Self {
220231
self.plaintext_message = text.to_owned();
221232
self
222233
}
223234

224-
#[allow(unused)]
235+
pub fn message(&mut self, text: &str, args: &[&str]) -> &mut Self {
236+
let parts = text.split("{}");
237+
let args = args.iter().chain(std::iter::repeat(&""));
238+
let mut plain = String::with_capacity(2 * text.len());
239+
let mut markdown = String::with_capacity(2 * text.len());
240+
for (p, a) in parts.zip(args) {
241+
plain.push_str(p);
242+
plain.push_str(a);
243+
markdown.push_str(p);
244+
if a.len() > 0 {
245+
let count = longest_backtick_sequence_length(a) + 1;
246+
markdown.push_str(&"`".repeat(count));
247+
if count > 1 {
248+
markdown.push_str(" ");
249+
}
250+
markdown.push_str(a);
251+
if count > 1 {
252+
markdown.push_str(" ");
253+
}
254+
markdown.push_str(&"`".repeat(count));
255+
}
256+
}
257+
self.text(&plain);
258+
self.markdown(&markdown);
259+
self
260+
}
261+
225262
pub fn markdown(&mut self, text: &str) -> &mut Self {
226263
self.markdown_message = text.to_owned();
227264
self
@@ -276,3 +313,20 @@ impl DiagnosticMessage {
276313
self
277314
}
278315
}
316+
317+
#[test]
318+
fn test_message() {
319+
let mut m = DiagnosticLoggers::new("foo")
320+
.logger()
321+
.new_entry("id", "name");
322+
m.message("hello: {}", &["hello"]);
323+
assert_eq!("hello: hello", m.plaintext_message);
324+
assert_eq!("hello: `hello`", m.markdown_message);
325+
326+
let mut m = DiagnosticLoggers::new("foo")
327+
.logger()
328+
.new_entry("id", "name");
329+
m.message("hello with backticks: {}", &["`hello`"]);
330+
assert_eq!("hello with backticks: `hello`", m.plaintext_message);
331+
assert_eq!("hello with backticks: `` `hello` ``", m.markdown_message);
332+
}

ruby/extractor/src/extractor.rs

Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ impl<'a> Visitor<'a> {
276276

277277
fn record_parse_error_for_node(
278278
&mut self,
279-
error_message: String,
279+
message: &str,
280+
args: &[&str],
280281
node: Node,
281282
status_page: bool,
282283
) {
@@ -291,26 +292,31 @@ impl<'a> Visitor<'a> {
291292
);
292293
let mut mesg = self
293294
.diagnostics_writer
294-
.message("parse-error", "Parse error");
295-
&mesg.severity(diagnostics::Severity::Error)
295+
.new_entry("parse-error", "Parse error");
296+
&mesg
297+
.severity(diagnostics::Severity::Error)
296298
.location(self.path, start_line, start_column, end_line, end_column)
297-
.text(&error_message);
299+
.message(message, args);
298300
if status_page {
299301
&mesg.status_page();
300302
}
301303
self.record_parse_error(loc, &mesg);
302304
}
303305

304306
fn enter_node(&mut self, node: Node) -> bool {
305-
if node.is_error() || node.is_missing() {
306-
let error_message = if node.is_missing() {
307-
format!("parse error: expecting '{}'", node.kind())
308-
} else {
309-
"parse error".to_string()
310-
};
311-
self.record_parse_error_for_node(error_message, node, true);
307+
if node.is_missing() {
308+
self.record_parse_error_for_node(
309+
"parse error: expecting {}",
310+
&[node.kind()],
311+
node,
312+
true,
313+
);
312314
return false;
313315
}
316+
if node.is_error() {
317+
self.record_parse_error_for_node("parse error", &[], node, true);
318+
return false;
319+
};
314320

315321
let id = self.trap_writer.fresh_id();
316322

@@ -390,14 +396,13 @@ impl<'a> Visitor<'a> {
390396
}
391397
}
392398
_ => {
393-
let error_message = format!("unknown table type: '{}'", node.kind());
394399
self.record_parse_error(
395400
loc,
396401
self.diagnostics_writer
397-
.message("parse-error", "Parse error")
402+
.new_entry("parse-error", "Parse error")
398403
.severity(diagnostics::Severity::Error)
399404
.location(self.path, start_line, start_column, end_line, end_column)
400-
.text(&error_message),
405+
.message("unknown table type: {}", &[node.kind()]),
401406
);
402407

403408
valid = false;
@@ -445,23 +450,29 @@ impl<'a> Visitor<'a> {
445450
values.push(trap::Arg::Label(child_node.label));
446451
}
447452
} else if field.name.is_some() {
448-
let error_message = format!(
449-
"type mismatch for field {}::{} with type {:?} != {:?}",
450-
node.kind(),
451-
child_node.field_name.unwrap_or("child"),
452-
child_node.type_name,
453-
field.type_info
453+
self.record_parse_error_for_node(
454+
"type mismatch for field {}::{} with type {} != {}",
455+
&[
456+
node.kind(),
457+
child_node.field_name.unwrap_or("child"),
458+
&format!("{:?}", child_node.type_name),
459+
&format!("{:?}", field.type_info),
460+
],
461+
*node,
462+
false,
454463
);
455-
self.record_parse_error_for_node(error_message, *node,false);
456464
}
457465
} else if child_node.field_name.is_some() || child_node.type_name.named {
458-
let error_message = format!(
459-
"value for unknown field: {}::{} and type {:?}",
460-
node.kind(),
461-
&child_node.field_name.unwrap_or("child"),
462-
&child_node.type_name
466+
self.record_parse_error_for_node(
467+
"value for unknown field: {}::{} and type {}",
468+
&[
469+
node.kind(),
470+
&child_node.field_name.unwrap_or("child"),
471+
&format!("{:?}", child_node.type_name),
472+
],
473+
*node,
474+
false,
463475
);
464-
self.record_parse_error_for_node(error_message, *node, false);
465476
}
466477
}
467478
let mut args = Vec::new();
@@ -484,7 +495,7 @@ impl<'a> Visitor<'a> {
484495
node.kind(),
485496
column_name
486497
);
487-
self.record_parse_error_for_node(error_message, *node, false);
498+
self.record_parse_error_for_node(&error_message, &[], *node, false);
488499
}
489500
}
490501
Storage::Table {
@@ -494,13 +505,12 @@ impl<'a> Visitor<'a> {
494505
} => {
495506
for (index, child_value) in child_values.iter().enumerate() {
496507
if !*has_index && index > 0 {
497-
let error_message = format!(
508+
self.record_parse_error_for_node(
498509
"too many values for field: {}::{}",
499-
node.kind(),
500-
table_name,
510+
&[node.kind(), table_name],
511+
*node,
512+
false,
501513
);
502-
503-
self.record_parse_error_for_node(error_message, *node, false);
504514
break;
505515
}
506516
let mut args = vec![trap::Arg::Label(parent_id)];
@@ -597,8 +607,8 @@ fn location_for(visitor: &mut Visitor, n: Node) -> (usize, usize, usize, usize)
597607
visitor.diagnostics_writer.write(
598608
visitor
599609
.diagnostics_writer
600-
.message("internal-error", "Internal error")
601-
.text("expecting a line break symbol, but none found while correcting end column value")
610+
.new_entry("internal-error", "Internal error")
611+
.message("expecting a line break symbol, but none found while correcting end column value", &[])
602612
.severity(diagnostics::Severity::Error),
603613
);
604614
}
@@ -612,12 +622,11 @@ fn location_for(visitor: &mut Visitor, n: Node) -> (usize, usize, usize, usize)
612622
visitor.diagnostics_writer.write(
613623
visitor
614624
.diagnostics_writer
615-
.message("internal-error", "Internal error")
616-
.text(&format!(
625+
.new_entry("internal-error", "Internal error")
626+
.message(
617627
"cannot correct end column value: end_byte index {} is not in range [1,{}]",
618-
index,
619-
source.len()
620-
))
628+
&[&index.to_string(), &source.len().to_string()],
629+
)
621630
.severity(diagnostics::Severity::Error),
622631
);
623632
}

ruby/extractor/src/main.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ fn main() -> std::io::Result<()> {
7373
Err(e) => {
7474
main_thread_logger.write(
7575
main_thread_logger
76-
.message("configuration-error", "Configuration error")
77-
.text(&format!("{}; defaulting to 1 thread.", e))
76+
.new_entry("configuration-error", "Configuration error")
77+
.message("{}; defaulting to 1 thread.", &[&e])
7878
.severity(diagnostics::Severity::Warning),
7979
);
8080
1
@@ -94,8 +94,8 @@ fn main() -> std::io::Result<()> {
9494
Err(e) => {
9595
main_thread_logger.write(
9696
main_thread_logger
97-
.message("configuration-error", "Configuration error")
98-
.text(&format!("{}; using gzip.", e))
97+
.new_entry("configuration-error", "Configuration error")
98+
.message("{}; using gzip.", &[&e])
9999
.severity(diagnostics::Severity::Warning),
100100
);
101101
trap::Compression::Gzip
@@ -197,16 +197,15 @@ fn main() -> std::io::Result<()> {
197197
needs_conversion = false;
198198
diagnostics_writer.write(
199199
diagnostics_writer
200-
.message(
200+
.new_entry(
201201
"character-decoding-error",
202202
"Character decoding error",
203203
)
204204
.file(&path.to_string_lossy())
205-
.text(&format!(
206-
"could not decode the file contents as '{}': {}",
207-
&encoding_name,
208-
msg,
209-
))
205+
.message(
206+
"could not decode the file contents as {}: {}",
207+
&[&encoding_name, &msg],
208+
)
210209
.status_page()
211210
.severity(diagnostics::Severity::Warning),
212211
);
@@ -216,12 +215,12 @@ fn main() -> std::io::Result<()> {
216215
} else {
217216
diagnostics_writer.write(
218217
diagnostics_writer
219-
.message("unknown-character-encoding", "Unknown character encoding")
218+
.new_entry("unknown-character-encoding", "Unknown character encoding")
220219
.file(&path.to_string_lossy())
221-
.text(&format!(
222-
"unknown character encoding '{}' in '#encoding:' directive.",
223-
&encoding_name
224-
))
220+
.message(
221+
"unknown character encoding {} in {} directive.",
222+
&[&encoding_name, "#encoding:"],
223+
)
225224
.status_page()
226225
.help_link("https://docs.ruby-lang.org/en/3.2/syntax/comments_rdoc.html#label-encoding+Directive")
227226
.severity(diagnostics::Severity::Warning),

0 commit comments

Comments
 (0)