@@ -18,7 +18,7 @@ pub mod theme;
1818pub mod tree;
1919pub mod view;
2020
21- use std:: num:: NonZeroUsize ;
21+ use std:: { borrow :: Cow , num:: NonZeroUsize , path :: Path } ;
2222
2323// uses NonZeroUsize so Option<DocumentId> use a byte rather than two
2424#[ derive( Clone , Copy , PartialEq , Eq , PartialOrd , Ord , Hash , Debug ) ]
@@ -72,8 +72,152 @@ pub fn align_view(doc: &mut Document, view: &View, align: Align) {
7272 doc. set_view_offset ( view. id , view_offset) ;
7373}
7474
75+ /// Returns the left-side position of the primary selection.
76+ pub fn primary_cursor ( view : & View , doc : & Document ) -> usize {
77+ doc. selection ( view. id )
78+ . primary ( )
79+ . cursor ( doc. text ( ) . slice ( ..) )
80+ }
81+
82+ /// Returns the next diagnostic in the document if any.
83+ ///
84+ /// This does not wrap-around.
85+ pub fn next_diagnostic_in_doc < ' d > (
86+ view : & View ,
87+ doc : & ' d Document ,
88+ severity_filter : Option < helix_core:: diagnostic:: Severity > ,
89+ ) -> Option < & ' d Diagnostic > {
90+ let cursor = primary_cursor ( view, doc) ;
91+ doc. diagnostics ( )
92+ . iter ( )
93+ . filter ( |diagnostic| diagnostic. severity >= severity_filter)
94+ . find ( |diag| diag. range . start > cursor)
95+ }
96+
97+ /// Returns the previous diagnostic in the document if any.
98+ ///
99+ /// This does not wrap-around.
100+ pub fn prev_diagnostic_in_doc < ' d > (
101+ view : & View ,
102+ doc : & ' d Document ,
103+ severity_filter : Option < helix_core:: diagnostic:: Severity > ,
104+ ) -> Option < & ' d Diagnostic > {
105+ let cursor = primary_cursor ( view, doc) ;
106+ doc. diagnostics ( )
107+ . iter ( )
108+ . rev ( )
109+ . filter ( |diagnostic| diagnostic. severity >= severity_filter)
110+ . find ( |diag| diag. range . start < cursor)
111+ }
112+
113+ pub struct WorkspaceDiagnostic < ' e > {
114+ pub path : Cow < ' e , Path > ,
115+ pub diagnostic : Cow < ' e , helix_lsp:: lsp:: Diagnostic > ,
116+ pub offset_encoding : OffsetEncoding ,
117+ }
118+ impl < ' e > WorkspaceDiagnostic < ' e > {
119+ pub fn into_owned ( self ) -> WorkspaceDiagnostic < ' static > {
120+ WorkspaceDiagnostic {
121+ path : Cow :: Owned ( self . path . into_owned ( ) ) ,
122+ diagnostic : Cow :: Owned ( self . diagnostic . into_owned ( ) ) ,
123+ offset_encoding : self . offset_encoding ,
124+ }
125+ }
126+ }
127+
128+ fn workspace_diagnostics < ' e > (
129+ editor : & ' e Editor ,
130+ severity_filter : Option < helix_core:: diagnostic:: Severity > ,
131+ ) -> impl Iterator < Item = WorkspaceDiagnostic < ' e > > {
132+ editor
133+ . diagnostics
134+ . iter ( )
135+ . filter_map ( |( uri, diagnostics) | {
136+ // Extract Path from diagnostic Uri, skipping diagnostics that don't have a path.
137+ uri. as_path ( ) . map ( |p| ( p, diagnostics) )
138+ } )
139+ . flat_map ( |( path, diagnostics) | {
140+ diagnostics
141+ . iter ( )
142+ . map ( move |( diagnostic, language_server_id) | ( path, diagnostic, language_server_id) )
143+ } )
144+ . filter ( move |( _, diagnostic, _) | {
145+ // Filter by severity
146+ let severity = diagnostic
147+ . severity
148+ . and_then ( Document :: lsp_severity_to_severity) ;
149+ severity >= severity_filter
150+ } )
151+ . map ( |( path, diag, language_server_id) | {
152+ // Map language server ID to offset encoding
153+ let offset_encoding = editor
154+ . language_server_by_id ( * language_server_id)
155+ . map ( |client| client. offset_encoding ( ) )
156+ . unwrap_or_default ( ) ;
157+ ( path, diag, offset_encoding)
158+ } )
159+ . map ( |( path, diagnostic, offset_encoding) | WorkspaceDiagnostic {
160+ path : Cow :: Borrowed ( path) ,
161+ diagnostic : Cow :: Borrowed ( diagnostic) ,
162+ offset_encoding,
163+ } )
164+ }
165+
166+ pub fn first_diagnostic_in_workspace (
167+ editor : & Editor ,
168+ severity_filter : Option < helix_core:: diagnostic:: Severity > ,
169+ ) -> Option < WorkspaceDiagnostic > {
170+ workspace_diagnostics ( editor, severity_filter) . next ( )
171+ }
172+
173+ pub fn next_diagnostic_in_workspace (
174+ editor : & Editor ,
175+ severity_filter : Option < helix_core:: diagnostic:: Severity > ,
176+ ) -> Option < WorkspaceDiagnostic > {
177+ let ( view, doc) = current_ref ! ( editor) ;
178+
179+ let Some ( current_doc_path) = doc. path ( ) else {
180+ return first_diagnostic_in_workspace ( editor, severity_filter) ;
181+ } ;
182+
183+ let cursor = primary_cursor ( view, doc) ;
184+
185+ workspace_diagnostics ( editor, severity_filter)
186+ . filter ( |d| {
187+ // Skip diagnostics before the current document
188+ d. path >= current_doc_path. as_path ( )
189+ } )
190+ . filter ( |d| {
191+ // Skip diagnostics before the primary cursor in the current document
192+ if d. path == current_doc_path. as_path ( ) {
193+ let Some ( start) = helix_lsp:: util:: lsp_pos_to_pos (
194+ doc. text ( ) ,
195+ d. diagnostic . range . start ,
196+ d. offset_encoding ,
197+ ) else {
198+ return false ;
199+ } ;
200+ if start <= cursor {
201+ return false ;
202+ }
203+ }
204+ true
205+ } )
206+ . next ( )
207+ }
208+
209+ pub fn ensure_selections_forward ( view : & View , doc : & mut Document ) {
210+ let selection = doc
211+ . selection ( view. id )
212+ . clone ( )
213+ . transform ( |r| r. with_direction ( Direction :: Forward ) ) ;
214+
215+ doc. set_selection ( view. id , selection) ;
216+ }
217+
75218pub use document:: Document ;
76219pub use editor:: Editor ;
77- use helix_core:: char_idx_at_visual_offset;
220+ use helix_core:: { char_idx_at_visual_offset, movement:: Direction , Diagnostic } ;
221+ use helix_lsp:: OffsetEncoding ;
78222pub use theme:: Theme ;
79223pub use view:: View ;
0 commit comments