diff --git a/cli/src/diagnostics.rs b/cli/src/diagnostics.rs index b8b7609f..3761214a 100644 --- a/cli/src/diagnostics.rs +++ b/cli/src/diagnostics.rs @@ -387,12 +387,28 @@ impl<'a> DiagnosticRenderer<'a> { .map(|r| serde_json::json!({ "severity": r.severity.label(), "message": r.message })) .collect(); + let suggestions_json: Vec<_> = diag + .suggestions + .iter() + .map(|s| { + serde_json::json!({ + "message": s.message, + "replacement": s.replacement, + "span": { + "start": s.span.start, + "end": s.span.end, + } + }) + }) + .collect(); + let obj = serde_json::json!({ "severity": diag.severity.label(), "code": diag.code.map(|c| format!("{:?}", c)), "message": diag.message, "span": span_json, "related": related_json, + "suggestions": suggestions_json, }); obj.to_string() @@ -480,3 +496,41 @@ impl DiagnosticStats { .to_string() } } + +#[cfg(test)] +mod tests { + use super::*; + use techscript_common::Span; + + #[test] + fn test_fix_suggestion_rendering() { + let sm = SourceManager::new(); + let diag = + RichDiagnostic::error("undefined variable `foo`").with_suggestion(FixSuggestion { + message: "change to `bar`".into(), + span: Span::new(0, 3), + replacement: "bar".into(), + }); + + // Plain output rendering + let renderer_plain = DiagnosticRenderer::new(DiagnosticOutput::Plain, &sm); + let rendered_plain = renderer_plain.render(&diag); + assert!(rendered_plain.contains("suggestion: change to `bar` → `bar`")); + + // Colored output rendering + let renderer_colored = DiagnosticRenderer::new(DiagnosticOutput::Colored, &sm); + let rendered_colored = renderer_colored.render(&diag); + assert!(rendered_colored.contains("suggestion")); + assert!(rendered_colored.contains("change to `bar`")); + assert!(rendered_colored.contains("bar")); + + // JSON output rendering + let renderer_json = DiagnosticRenderer::new(DiagnosticOutput::Json, &sm); + let json_str = renderer_json.render(&diag); + let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap(); + assert_eq!(parsed["suggestions"][0]["message"], "change to `bar`"); + assert_eq!(parsed["suggestions"][0]["replacement"], "bar"); + assert_eq!(parsed["suggestions"][0]["span"]["start"], 0); + assert_eq!(parsed["suggestions"][0]["span"]["end"], 3); + } +} diff --git a/examples/threads/threads.txs b/examples/threads/threads.txs index 7396bb40..34286a5d 100644 --- a/examples/threads/threads.txs +++ b/examples/threads/threads.txs @@ -6,12 +6,12 @@ spawn_fn = thread["spawn"] join_fn = thread["join"] do task_a() - time.sleep(100) + time.sleep(200) say "Task A completed on worker thread." end do task_b() - time.sleep(50) + time.sleep(10) say "Task B completed on worker thread." end diff --git a/stdlib/tests/stdlib_tests.rs b/stdlib/tests/stdlib_tests.rs index 05c13a8c..51fdb1dc 100644 --- a/stdlib/tests/stdlib_tests.rs +++ b/stdlib/tests/stdlib_tests.rs @@ -519,6 +519,7 @@ fn test_http_module() { let response = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHello Server!"; let _ = stream.write_all(response.as_bytes()); let _ = stream.flush(); + let _ = stream.shutdown(std::net::Shutdown::Both); } });