Skip to content

Commit

Permalink
Jump to next/prev diagnostic in workspace (#3116)
Browse files Browse the repository at this point in the history
Implements functions and keybindings to move to next/previous/first/last diagnostic in the workspace
  • Loading branch information
dariooddenino committed Oct 11, 2022
1 parent 0813276 commit 262ab16
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 28 deletions.
111 changes: 84 additions & 27 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,12 @@ use crate::{

use crate::job::{self, Job, Jobs};
use futures_util::{FutureExt, StreamExt};
use std::{collections::HashMap, fmt, future::Future};
use std::{collections::HashSet, num::NonZeroUsize};
use std::{
collections::{BTreeMap, HashMap, HashSet},
fmt,
future::Future,
num::NonZeroUsize,
};

use std::{
borrow::Cow,
Expand Down Expand Up @@ -304,6 +308,10 @@ impl MappableCommand {
goto_last_diag, "Goto last diagnostic",
goto_next_diag, "Goto next diagnostic",
goto_prev_diag, "Goto previous diagnostic",
goto_first_diag_workspace, "Goto first diagnostic in workspace",
goto_last_diag_workspace, "Goto last diagnostic in workspace",
goto_next_diag_workspace, "Goto next diagnostic in workspace",
goto_prev_diag_workspace, "Goto previous diagnostic in workspace",
goto_line_start, "Goto line start",
goto_line_end, "Goto line end",
goto_next_buffer, "Goto next buffer",
Expand Down Expand Up @@ -2800,7 +2808,7 @@ fn exit_select_mode(cx: &mut Context) {
}
}

fn goto_pos(editor: &mut Editor, pos: usize) {
pub fn goto_pos(editor: &mut Editor, pos: usize) {
let (view, doc) = current!(editor);

push_jump(view, doc);
Expand All @@ -2826,23 +2834,81 @@ fn goto_last_diag(cx: &mut Context) {
goto_pos(cx.editor, pos);
}

fn goto_next_diag(cx: &mut Context) {
let editor = &mut cx.editor;
let (view, doc) = current!(editor);

/// Finds the position of the next/previous diagnostic.
pub fn get_next_diag_pos(
view: &mut View,
doc: &mut Document,
direction: Direction,
) -> Option<usize> {
let cursor_pos = doc
.selection(view.id)
.primary()
.cursor(doc.text().slice(..));

let diag = doc
.diagnostics()
.iter()
.find(|diag| diag.range.start > cursor_pos)
.or_else(|| doc.diagnostics().first());
let diag = match direction {
Direction::Forward => doc
.diagnostics()
.iter()
.find(|diag| diag.range.start > cursor_pos),
Direction::Backward => doc
.diagnostics()
.iter()
.find(|diag| diag.range.start < cursor_pos),
};

let pos = match diag {
Some(diag) => diag.range.start,
return diag.map(|d| d.range.start);
}

/// Finds the next/previous document with diagnostics in it.
pub fn get_next_diag_doc(
doc: &mut Document,
editor_diagnostics: BTreeMap<helix_lsp::lsp::Url, Vec<helix_lsp::lsp::Diagnostic>>,
direction: Direction,
) -> Option<PathBuf> {
let current_url = doc.url();
let diagnostics = editor_diagnostics.iter();
let next_diags = match direction {
Direction::Forward => {
let mut iter = diagnostics
.filter(|(_, diags)| !diags.is_empty())
.skip_while(|(url, _)| Some(*url) != current_url.as_ref());
iter.next();
iter.next().or_else(|| {
editor_diagnostics
.iter()
.filter(|(_, diags)| !diags.is_empty())
.next()
})
}
Direction::Backward => {
let mut iter = diagnostics
.rev()
.filter(|(_, diags)| !diags.is_empty())
.skip_while(|(url, _)| Some(*url) != current_url.as_ref());
iter.next(); // skip current
iter.next().or_else(|| {
editor_diagnostics
.iter()
.filter(|(_, diags)| !diags.is_empty())
.last()
})
}
};
match next_diags {
Some((url, _)) => url.to_file_path().ok(),
None => None,
}
}

fn goto_next_diag(cx: &mut Context) {
let editor = &mut cx.editor;
let (view, doc) = current!(editor);

let diag_pos = get_next_diag_pos(view, doc, Direction::Forward)
.or_else(|| doc.diagnostics().first().map(|d| d.range.start));

let pos = match diag_pos {
Some(pos) => pos,
None => return,
};

Expand All @@ -2853,20 +2919,11 @@ fn goto_prev_diag(cx: &mut Context) {
let editor = &mut cx.editor;
let (view, doc) = current!(editor);

let cursor_pos = doc
.selection(view.id)
.primary()
.cursor(doc.text().slice(..));

let diag = doc
.diagnostics()
.iter()
.rev()
.find(|diag| diag.range.start < cursor_pos)
.or_else(|| doc.diagnostics().last());
let diag_pos = get_next_diag_pos(view, doc, Direction::Backward)
.or_else(|| doc.diagnostics().last().map(|d| d.range.start));

let pos = match diag {
Some(diag) => diag.range.start,
let pos = match diag_pos {
Some(pos) => pos,
None => return,
};

Expand Down
119 changes: 118 additions & 1 deletion helix-term/src/commands/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ use tui::text::{Span, Spans};

use super::{align_view, push_jump, Align, Context, Editor, Open};

use helix_core::{path, Selection};
use helix_core::{movement::Direction, path, Selection};
use helix_view::{apply_transaction, editor::Action, theme::Style};

use crate::{
commands::{get_next_diag_doc, get_next_diag_pos, goto_pos},
compositor::{self, Compositor},
ui::{
self, lsp::SignatureHelp, overlay::overlayed, FileLocation, FilePicker, Popup, PromptEvent,
Expand Down Expand Up @@ -411,6 +412,122 @@ pub fn workspace_diagnostics_picker(cx: &mut Context) {
cx.push_layer(Box::new(overlayed(picker)));
}

pub fn goto_first_diag_workspace(cx: &mut Context) {
let editor = &mut cx.editor;

let mut diagnostics = editor
.diagnostics
.iter()
.filter(|(_, diags)| !diags.is_empty())
.map(|(url, _)| url);

let diag = diagnostics.next();
match diag {
Some(url) => {
let path = url.to_file_path().unwrap();
editor
.open(&path, Action::Replace)
.expect("editor.open failed");
let doc = doc!(editor);
let pos = match doc.diagnostics().first() {
Some(diag) => diag.range.start,
None => return,
};
goto_pos(editor, pos);
}
None => return,
}
}

pub fn goto_last_diag_workspace(cx: &mut Context) {
let editor = &mut cx.editor;

let diagnostics = editor
.diagnostics
.iter()
.filter(|(_, diags)| !diags.is_empty())
.map(|(url, _)| url);

let diag = diagnostics.last();
match diag {
Some(url) => {
let path = url.to_file_path().unwrap();
editor
.open(&path, Action::Replace)
.expect("editor.open failed");
let doc = doc!(editor);
let pos = match doc.diagnostics().last() {
Some(diag) => diag.range.start,
None => return,
};
goto_pos(editor, pos);
}
None => return,
}
}

pub fn goto_next_diag_workspace(cx: &mut Context) {
let editor = &mut cx.editor;
let (view, doc) = current!(editor);

let doc_next_diag_pos = get_next_diag_pos(view, doc, Direction::Forward);

match doc_next_diag_pos {
Some(pos) => goto_pos(editor, pos),
None => {
let diagnostics = editor.diagnostics.clone();
let next_doc = get_next_diag_doc(doc, diagnostics, Direction::Forward);
match next_doc {
Some(path) => {
editor
.open(&path, Action::Replace)
.expect("editor.open failed");
let doc = doc!(editor);
match doc.diagnostics().get(0) {
Some(diag) => {
let pos = diag.range.start;
goto_pos(editor, pos)
}
None => return,
}
}
None => return,
}
}
}
}

pub fn goto_prev_diag_workspace(cx: &mut Context) {
let editor = &mut cx.editor;
let (view, doc) = current!(editor);

let doc_prev_diag_pos = get_next_diag_pos(view, doc, Direction::Backward);

match doc_prev_diag_pos {
Some(pos) => goto_pos(editor, pos),
None => {
let diagnostics = editor.diagnostics.clone();
let next_doc = get_next_diag_doc(doc, diagnostics, Direction::Backward);
match next_doc {
Some(path) => {
editor
.open(&path, Action::Replace)
.expect("editor.open failed");
let doc = doc!(editor);
match doc.diagnostics().last() {
Some(diag) => {
let pos = diag.range.start;
goto_pos(editor, pos)
}
None => return,
}
}
None => return,
}
}
}
}

impl ui::menu::Item for lsp::CodeActionOrCommand {
type Data = ();
fn label(&self, _data: &Self::Data) -> Spans {
Expand Down
4 changes: 4 additions & 0 deletions helix-term/src/keymap/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ pub fn default() -> HashMap<Mode, Keymap> {
"[" => { "Left bracket"
"d" => goto_prev_diag,
"D" => goto_first_diag,
"w" => goto_prev_diag_workspace,
"W" => goto_first_diag_workspace,
"f" => goto_prev_function,
"c" => goto_prev_class,
"a" => goto_prev_parameter,
Expand All @@ -111,6 +113,8 @@ pub fn default() -> HashMap<Mode, Keymap> {
"]" => { "Right bracket"
"d" => goto_next_diag,
"D" => goto_last_diag,
"w" => goto_next_diag_workspace,
"W" => goto_last_diag_workspace,
"f" => goto_next_function,
"c" => goto_next_class,
"a" => goto_next_parameter,
Expand Down

0 comments on commit 262ab16

Please sign in to comment.