8
8
use std:: env:: current_dir;
9
9
use std:: path:: PathBuf ;
10
10
11
- use harp:: object:: RObject ;
12
- use harp:: string:: r_string_decode;
11
+ use harp:: utils:: r_is_string;
13
12
use harp:: utils:: r_normalize_path;
14
13
use stdext:: unwrap;
15
- use stdext:: IntoResult ;
16
14
use tower_lsp:: lsp_types:: CompletionItem ;
17
15
use tree_sitter:: Node ;
18
16
@@ -33,13 +31,24 @@ pub(super) fn completions_from_string_file_path(
33
31
//
34
32
// NOTE: This includes the quotation characters on the string, and so
35
33
// also includes any internal escapes! We need to decode the R string
36
- // before searching the path entries.
34
+ // by parsing it before searching the path entries.
37
35
let token = context. document . contents . node_slice ( & node) ?. to_string ( ) ;
38
- let contents = unsafe { r_string_decode ( token. as_str ( ) ) . into_result ( ) ? } ;
39
- log:: trace!( "String value (decoded): {}" , contents) ;
36
+
37
+ // It's entirely possible that we can fail to parse the string, `R_ParseVector()`
38
+ // can fail in various ways. We silently swallow these because they are unlikely
39
+ // to report to real file paths and just bail (posit-dev/positron#6584).
40
+ let Ok ( contents) = harp:: parse_expr ( & token) else {
41
+ return Ok ( completions) ;
42
+ } ;
43
+
44
+ // Double check that parsing gave a string. It should, because `node` points to
45
+ // a tree-sitter string node.
46
+ if !r_is_string ( contents. sexp ) {
47
+ return Ok ( completions) ;
48
+ }
40
49
41
50
// Use R to normalize the path.
42
- let path = r_normalize_path ( RObject :: from ( contents) ) ?;
51
+ let path = r_normalize_path ( contents) ?;
43
52
44
53
// parse the file path and get the directory component
45
54
let mut path = PathBuf :: from ( path. as_str ( ) ) ;
@@ -82,3 +91,28 @@ pub(super) fn completions_from_string_file_path(
82
91
83
92
Ok ( completions)
84
93
}
94
+
95
+ #[ cfg( test) ]
96
+ mod tests {
97
+ use crate :: fixtures:: point_from_cursor;
98
+ use crate :: lsp:: completions:: sources:: unique:: file_path:: completions_from_string_file_path;
99
+ use crate :: lsp:: document_context:: DocumentContext ;
100
+ use crate :: lsp:: documents:: Document ;
101
+ use crate :: r_task;
102
+ use crate :: treesitter:: node_find_string;
103
+
104
+ #[ test]
105
+ fn test_unparseable_string ( ) {
106
+ // https://github.com/posit-dev/positron/issues/6584
107
+ r_task ( || {
108
+ // "\R" is an unrecognized escape character and `R_ParseVector()` errors on it
109
+ let ( text, point) = point_from_cursor ( r#" ".\R\utils.R@" "# ) ;
110
+ let document = Document :: new ( text. as_str ( ) , None ) ;
111
+ let context = DocumentContext :: new ( & document, point, None ) ;
112
+ let node = node_find_string ( & context. node ) . unwrap ( ) ;
113
+
114
+ let completions = completions_from_string_file_path ( & node, & context) . unwrap ( ) ;
115
+ assert_eq ! ( completions. len( ) , 0 ) ;
116
+ } )
117
+ }
118
+ }
0 commit comments