@@ -158,6 +158,31 @@ pub fn rewrite_string<'a>(orig: &str, fmt: &StringFormat<'a>) -> Option<String>
158
158
wrap_str ( result, fmt. config . max_width ( ) , fmt. shape )
159
159
}
160
160
161
+ /// Returns the index to the end of the url if the given string includes an
162
+ /// URL or alike. Otherwise, returns None;
163
+ fn detect_url ( s : & [ & str ] , index : usize ) -> Option < usize > {
164
+ let start = match s[ ..=index] . iter ( ) . rposition ( |g| is_whitespace ( g) ) {
165
+ Some ( pos) => pos + 1 ,
166
+ None => 0 ,
167
+ } ;
168
+ if s. len ( ) < start + 8 {
169
+ return None ;
170
+ }
171
+ let prefix = s[ start..start + 8 ] . join ( "" ) ;
172
+ if prefix. starts_with ( "https://" )
173
+ || prefix. starts_with ( "http://" )
174
+ || prefix. starts_with ( "ftp://" )
175
+ || prefix. starts_with ( "file://" )
176
+ {
177
+ match s[ index..] . iter ( ) . position ( |g| is_whitespace ( g) ) {
178
+ Some ( pos) => Some ( index + pos - 1 ) ,
179
+ None => Some ( s. len ( ) - 1 ) ,
180
+ }
181
+ } else {
182
+ None
183
+ }
184
+ }
185
+
161
186
/// Trims whitespaces to the right except for the line feed character.
162
187
fn trim_right_but_line_feed ( trim_end : bool , result : String ) -> String {
163
188
let whitespace_except_line_feed = |c : char | c. is_whitespace ( ) && c != '\n' ;
@@ -193,13 +218,16 @@ enum SnippetState {
193
218
EndWithLineFeed ( String , usize ) ,
194
219
}
195
220
221
+ fn not_whitespace_except_line_feed ( g : & str ) -> bool {
222
+ is_line_feed ( g) || !is_whitespace ( g)
223
+ }
224
+
196
225
/// Break the input string at a boundary character around the offset `max_chars`. A boundary
197
226
/// character is either a punctuation or a whitespace.
198
227
fn break_string ( max_chars : usize , trim_end : bool , line_end : & str , input : & [ & str ] ) -> SnippetState {
199
228
let break_at = |index /* grapheme at index is included */ | {
200
229
// Take in any whitespaces to the left/right of `input[index]` while
201
230
// preserving line feeds
202
- let not_whitespace_except_line_feed = |g| is_line_feed ( g) || !is_whitespace ( g) ;
203
231
let index_minus_ws = input[ 0 ..=index]
204
232
. iter ( )
205
233
. rposition ( |grapheme| not_whitespace_except_line_feed ( grapheme) )
@@ -258,6 +286,24 @@ fn break_string(max_chars: usize, trim_end: bool, line_end: &str, input: &[&str]
258
286
// - extra whitespaces to the right can be trimmed
259
287
return break_at ( max_chars - 1 ) ;
260
288
}
289
+ if let Some ( url_index_end) = detect_url ( input, max_chars) {
290
+ let index_plus_ws = url_index_end + input[ url_index_end..]
291
+ . iter ( )
292
+ . skip ( 1 )
293
+ . position ( |grapheme| not_whitespace_except_line_feed ( grapheme) )
294
+ . unwrap_or ( 0 ) ;
295
+ return if trim_end {
296
+ SnippetState :: LineEnd (
297
+ input[ ..=url_index_end] . join ( "" ) . to_string ( ) ,
298
+ index_plus_ws + 1 ,
299
+ )
300
+ } else {
301
+ return SnippetState :: LineEnd (
302
+ input[ ..=index_plus_ws] . join ( "" ) . to_string ( ) ,
303
+ index_plus_ws + 1 ,
304
+ ) ;
305
+ } ;
306
+ }
261
307
match input[ 0 ..max_chars]
262
308
. iter ( )
263
309
. rposition ( |grapheme| is_whitespace ( grapheme) )
@@ -303,7 +349,7 @@ fn is_punctuation(grapheme: &str) -> bool {
303
349
304
350
#[ cfg( test) ]
305
351
mod test {
306
- use super :: { break_string, rewrite_string, SnippetState , StringFormat } ;
352
+ use super :: { break_string, detect_url , rewrite_string, SnippetState , StringFormat } ;
307
353
use config:: Config ;
308
354
use shape:: { Indent , Shape } ;
309
355
use unicode_segmentation:: UnicodeSegmentation ;
@@ -610,4 +656,31 @@ mod test {
610
656
Some ( "Vestibulum\\ \n // ac lacus." . to_string( ) )
611
657
) ;
612
658
}
659
+
660
+ #[ test]
661
+ fn detect_urls ( ) {
662
+ let string = "aaa http://example.org something" ;
663
+ let graphemes = UnicodeSegmentation :: graphemes ( & * string, false ) . collect :: < Vec < & str > > ( ) ;
664
+ assert_eq ! ( detect_url( & graphemes, 8 ) , Some ( 21 ) ) ;
665
+
666
+ let string = "https://example.org something" ;
667
+ let graphemes = UnicodeSegmentation :: graphemes ( & * string, false ) . collect :: < Vec < & str > > ( ) ;
668
+ assert_eq ! ( detect_url( & graphemes, 0 ) , Some ( 18 ) ) ;
669
+
670
+ let string = "aaa ftp://example.org something" ;
671
+ let graphemes = UnicodeSegmentation :: graphemes ( & * string, false ) . collect :: < Vec < & str > > ( ) ;
672
+ assert_eq ! ( detect_url( & graphemes, 8 ) , Some ( 20 ) ) ;
673
+
674
+ let string = "aaa file://example.org something" ;
675
+ let graphemes = UnicodeSegmentation :: graphemes ( & * string, false ) . collect :: < Vec < & str > > ( ) ;
676
+ assert_eq ! ( detect_url( & graphemes, 8 ) , Some ( 21 ) ) ;
677
+
678
+ let string = "aaa http not an url" ;
679
+ let graphemes = UnicodeSegmentation :: graphemes ( & * string, false ) . collect :: < Vec < & str > > ( ) ;
680
+ assert_eq ! ( detect_url( & graphemes, 6 ) , None ) ;
681
+
682
+ let string = "aaa file://example.org" ;
683
+ let graphemes = UnicodeSegmentation :: graphemes ( & * string, false ) . collect :: < Vec < & str > > ( ) ;
684
+ assert_eq ! ( detect_url( & graphemes, 8 ) , Some ( 21 ) ) ;
685
+ }
613
686
}
0 commit comments