@@ -29,10 +29,10 @@ use tui::{
29 backend::CrosstermBackend,
29 backend::CrosstermBackend,
30 buffer::Buffer as Surface,
30 buffer::Buffer as Surface,
31 layout::Rect,
31 layout::Rect,
32− style::{Color, Style},
32+ style::{Color, Modifier, Style},
35−const OFFSET: u16 = 6; // 5 linenr + 1 gutter
35+const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
37type Terminal = tui::Terminal<CrosstermBackend<std::io::Stdout>>;
37type Terminal = tui::Terminal<CrosstermBackend<std::io::Stdout>>;
@@ -205,6 +205,16 @@ impl Renderer {
208+ // ugh, improve with a traverse method
209+ // or interleave highlight spans with selection and diagnostic spans
210+ let style = if view.state.diagnostics.iter().any(|diagnostic| {
211+ diagnostic.range.0 <= char_index && diagnostic.range.1 > char_index
213+ style.clone().add_modifier(Modifier::UNDERLINED)
208 // TODO: paint cursor heads except primary
218 // TODO: paint cursor heads except primary
210 self.surface
220 self.surface
@@ -212,18 +222,23 @@ impl Renderer {
213 visual_x += width;
223 visual_x += width;
215− // if grapheme == "\t"
217 char_index += 1;
226 char_index += 1;
222 let style: Style = theme.get("ui.linenr");
232 let style: Style = theme.get("ui.linenr");
233+ let warning: Style = theme.get("warning");
223 let last_line = view.last_line();
234 let last_line = view.last_line();
224 for (i, line) in (view.first_line..last_line).enumerate() {
235 for (i, line) in (view.first_line..last_line).enumerate() {
236+ if view.state.diagnostics.iter().any(|d| d.line == line) {
237+ self.surface.set_stringn(0, i as u16, "●", 1, warning);
225 self.surface
240 self.surface
226− .set_stringn(0, i as u16, format!("{:>5}", line + 1), 5, style);
241+ .set_stringn(1, i as u16, format!("{:>5}", line + 1), 5, style);
@@ -240,6 +255,13 @@ impl Renderer {
241 self.surface
256 self.surface
242 .set_string(1, self.size.1 - 2, mode, self.text_color);
257 .set_string(1, self.size.1 - 2, mode, self.text_color);
259+ self.surface.set_string(
262+ format!("{}", view.state.diagnostics.len()),
245 pub fn render_prompt(&mut self, view: &View, prompt: &Prompt, theme: &Theme) {
267 pub fn render_prompt(&mut self, view: &View, prompt: &Prompt, theme: &Theme) {
@@ -545,7 +567,59 @@ impl<'a> Application<'a> {
545 pub async fn handle_lsp_notification(&mut self, notification: Option<helix_lsp::Notification>) {
567 pub async fn handle_lsp_notification(&mut self, notification: Option<helix_lsp::Notification>) {
546 use helix_lsp::Notification;
568 use helix_lsp::Notification;
547 match notification {
569 match notification {
548− Some(Notification::PublishDiagnostics(params)) => unimplemented!("{:?}", params),
570+ Some(Notification::PublishDiagnostics(params)) => {
571+ let view = self.editor.views.iter_mut().find(|view| {
576+ .map(|path| helix_lsp::Url::from_file_path(path).unwrap());
578+ eprintln!("{:?} {} {}", path, params.uri, params.diagnostics.len());
580+ path == Some(params.uri.clone())
583+ fn lsp_pos_to_pos(doc: &helix_core::RopeSlice, pos: helix_lsp::Position) -> usize {
584+ let line = doc.line_to_char(pos.line as usize);
585+ let line_start = doc.char_to_utf16_cu(line);
586+ doc.utf16_cu_to_char(pos.character as usize + line_start)
589+ if let Some(view) = view {
590+ let doc = view.state.doc().slice(..);
591+ let diagnostics = params
595+ let start = lsp_pos_to_pos(&doc, diagnostic.range.start);
596+ let end = lsp_pos_to_pos(&doc, diagnostic.range.end);
599+ // "{:?}-{:?} {}-{} {}",
600+ // diagnostic.range.start,
601+ // diagnostic.range.end,
604+ // diagnostic.message
607+ helix_core::Diagnostic {
608+ range: (start, end),
609+ line: diagnostic.range.start.line as usize,
610+ message: diagnostic.message,
618+ view.state.diagnostics = diagnostics;
549 _ => unreachable!(),
623 _ => unreachable!(),