From 989f7d5b076a553d64275c0da417c6e13f065fac Mon Sep 17 00:00:00 2001 From: kngwyu Date: Tue, 30 Jan 2018 11:30:49 +0900 Subject: [PATCH 1/7] add test for issue815 --- tests/system.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/system.rs b/tests/system.rs index f4edcaea..95c63b41 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -4180,3 +4180,17 @@ fn completes_for_let_destracted_var_over_comment() { "; assert_eq!(get_only_completion(src, None).matchstr, "variable"); } + +// For Issue #815 +#[test] +fn complete_let_after_raw_string() { + let _lock = sync!(); + let src = r##" + fn main() { + let s = r#"""#; + let v = Vec::::new(); + v.l~ + } + "##; + assert_eq!(get_one_completion(src, None).matchstr, "len"); +} From 768c9c9a1e7f547be893f76b54a1ab8ddcb93efb Mon Sep 17 00:00:00 2001 From: kngwyu Date: Tue, 30 Jan 2018 14:02:46 +0900 Subject: [PATCH 2/7] add support for raw string with #s to CodeIndicesIter --- src/racer/codecleaner.rs | 106 ++++++++++++++++++++++++++++++++------- 1 file changed, 87 insertions(+), 19 deletions(-) diff --git a/src/racer/codecleaner.rs b/src/racer/codecleaner.rs index 71dace68..19e5dd7a 100644 --- a/src/racer/codecleaner.rs +++ b/src/racer/codecleaner.rs @@ -1,11 +1,18 @@ use core::{Point, SourceByteRange}; +/// Type of the string +#[derive(Clone, Copy, Debug)] +enum StringType { + Raw(usize), // raw string started with n #s + NotRaw, // normal string +} + #[derive(Clone,Copy)] enum State { Code, Comment, CommentBlock, - String, + String(StringType), Char, Finished } @@ -26,7 +33,7 @@ impl<'a> Iterator for CodeIndicesIter<'a> { State::Code => Some(self.code()), State::Comment => Some(self.comment()), State::CommentBlock => Some(self.comment_block()), - State::String => Some(self.string()), + State::String(level) => Some(self.string(level)), State::Char => Some(self.char()), State::Finished => None } @@ -37,7 +44,7 @@ impl<'a> CodeIndicesIter<'a> { fn code(&mut self) -> SourceByteRange { let mut pos = self.pos; let start = match self.state { - State::String | + State::String(_) | State::Char => { pos-1 }, // include quote _ => { pos } }; @@ -59,7 +66,8 @@ impl<'a> CodeIndicesIter<'a> { _ => {} }, b'"' => { // " - self.state = State::String; + let raw_level = self.detect_raw_level(pos); + self.state = State::String(raw_level); self.pos = pos; return (start, pos); // include dblquotes }, @@ -123,26 +131,59 @@ impl<'a> CodeIndicesIter<'a> { self.code() } - fn string(&mut self) -> SourceByteRange { + fn string(&mut self, str_type: StringType) -> SourceByteRange { let src_bytes = self.src.as_bytes(); let mut pos = self.pos; - if pos > 1 && src_bytes[pos-2] == b'r' { - // raw string (eg br"\"): no escape - match src_bytes[pos..].iter().position(|&b| b == b'"') { - Some(p) => pos += p+1, - None => pos = src_bytes.len() + match str_type { + StringType::Raw(level) => { + // raw string (eg br#"\"#) + // detect corresponding end(if start is r##", ##") greedy + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + enum SharpState { + Sharp((usize, usize)), // (Num of #, Pos of end ") + None, + } + let mut cur_state = SharpState::None; + let mut end_was_find = false; + for (i, &b) in src_bytes[self.pos..].iter().enumerate() { + match cur_state { + SharpState::Sharp((n_sharp, pos_quote)) => { + cur_state = match b { + b'#' => SharpState::Sharp((n_sharp + 1, pos_quote)), + b'"' => SharpState::Sharp((0, i)), + _ => SharpState::None, + } + } + SharpState::None => { + if b == b'"' { + cur_state = SharpState::Sharp((0, i)); + } + } + } + if let SharpState::Sharp((n_sharp, pos_quote)) = cur_state { + if n_sharp == level { + end_was_find = true; + pos += pos_quote + 1; + break; + } + } + } + if !end_was_find { + pos = src_bytes.len(); + } } - } else { - let mut is_not_escaped = true; - for &b in &src_bytes[pos..] { - pos += 1; - match b { - b'"' if is_not_escaped => { break; }, // " - b'\\' => { is_not_escaped = !is_not_escaped; }, - _ => { is_not_escaped = true; } + StringType::NotRaw => { + let mut is_not_escaped = true; + for &b in &src_bytes[pos..] { + pos += 1; + match b { + b'"' if is_not_escaped => { break; }, // " + b'\\' => { is_not_escaped = !is_not_escaped; }, + _ => { is_not_escaped = true; } + } } } - } + }; self.pos = pos; self.code() } @@ -161,6 +202,22 @@ impl<'a> CodeIndicesIter<'a> { self.pos = pos; self.code() } + + fn detect_raw_level(&self, pos: usize) -> StringType { + let src_bytes = self.src.as_bytes(); + let mut sharp = 0; + if pos == 0 { + return StringType::NotRaw; + } + for &b in src_bytes[..pos - 1].iter().rev() { + match b { + b'#' => sharp += 1, + b'r' => return StringType::Raw(sharp), + _ => return StringType::NotRaw, + } + } + StringType::NotRaw + } } /// Returns indices of chunks of code (minus comments and string contents) @@ -431,6 +488,17 @@ mod code_indices_iter_test { } } + #[test] + fn removes_nested_rawstr() { + let src = &rejustify(r####" + this is some code br###""" r##""##"### more code + "####); + + let mut it = code_chunks(src); + assert_eq!("this is some code br###\"", slice(src, it.next().unwrap())); + assert_eq!("\"### more code", slice(src, it.next().unwrap())); + } + } #[cfg(test)] From ca77d7fd6873aec328bdcb6c7a1d41c8a408b349 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Tue, 30 Jan 2018 14:13:52 +0900 Subject: [PATCH 3/7] refactored --- src/racer/codecleaner.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/racer/codecleaner.rs b/src/racer/codecleaner.rs index 19e5dd7a..7291c6c4 100644 --- a/src/racer/codecleaner.rs +++ b/src/racer/codecleaner.rs @@ -140,11 +140,11 @@ impl<'a> CodeIndicesIter<'a> { // detect corresponding end(if start is r##", ##") greedy #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum SharpState { - Sharp((usize, usize)), // (Num of #, Pos of end ") + Sharp((usize, usize)), // (Num of preceeding #s, Pos of end ") None, } let mut cur_state = SharpState::None; - let mut end_was_find = false; + let mut end_was_found = false; for (i, &b) in src_bytes[self.pos..].iter().enumerate() { match cur_state { SharpState::Sharp((n_sharp, pos_quote)) => { @@ -162,13 +162,13 @@ impl<'a> CodeIndicesIter<'a> { } if let SharpState::Sharp((n_sharp, pos_quote)) = cur_state { if n_sharp == level { - end_was_find = true; + end_was_found = true; pos += pos_quote + 1; break; } } } - if !end_was_find { + if !end_was_found { pos = src_bytes.len(); } } @@ -209,6 +209,7 @@ impl<'a> CodeIndicesIter<'a> { if pos == 0 { return StringType::NotRaw; } + // now pos is at one byte after ", so we have to start at pos - 2 for &b in src_bytes[..pos - 1].iter().rev() { match b { b'#' => sharp += 1, From dd8c4e2649262f4b93bd008d60d8d4e1abbe7725 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Wed, 31 Jan 2018 14:02:49 +0900 Subject: [PATCH 4/7] refactored and modified comments --- src/racer/codecleaner.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/racer/codecleaner.rs b/src/racer/codecleaner.rs index 7291c6c4..3fbe7ef1 100644 --- a/src/racer/codecleaner.rs +++ b/src/racer/codecleaner.rs @@ -3,7 +3,7 @@ use core::{Point, SourceByteRange}; /// Type of the string #[derive(Clone, Copy, Debug)] enum StringType { - Raw(usize), // raw string started with n #s + Raw(usize), // Raw(n) => raw string started with n #s NotRaw, // normal string } @@ -66,8 +66,8 @@ impl<'a> CodeIndicesIter<'a> { _ => {} }, b'"' => { // " - let raw_level = self.detect_raw_level(pos); - self.state = State::String(raw_level); + let str_type = self.detect_str_type(pos); + self.state = State::String(str_type); self.pos = pos; return (start, pos); // include dblquotes }, @@ -137,11 +137,11 @@ impl<'a> CodeIndicesIter<'a> { match str_type { StringType::Raw(level) => { // raw string (eg br#"\"#) - // detect corresponding end(if start is r##", ##") greedy + // detect corresponding end(if start is r##", ##") greedily #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum SharpState { Sharp((usize, usize)), // (Num of preceeding #s, Pos of end ") - None, + None, // No preceeding "##... } let mut cur_state = SharpState::None; let mut end_was_found = false; @@ -203,7 +203,7 @@ impl<'a> CodeIndicesIter<'a> { self.code() } - fn detect_raw_level(&self, pos: usize) -> StringType { + fn detect_str_type(&self, pos: usize) -> StringType { let src_bytes = self.src.as_bytes(); let mut sharp = 0; if pos == 0 { From 5bafbca624ca2cbaa9a88dffc5733e5e8670d64d Mon Sep 17 00:00:00 2001 From: kngwyu Date: Thu, 8 Feb 2018 11:40:52 +0900 Subject: [PATCH 5/7] modified test name and assertion type --- tests/system.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/system.rs b/tests/system.rs index 95c63b41..337c63bc 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -4183,7 +4183,7 @@ fn completes_for_let_destracted_var_over_comment() { // For Issue #815 #[test] -fn complete_let_after_raw_string() { +fn completes_methods_after_raw_string() { let _lock = sync!(); let src = r##" fn main() { @@ -4192,5 +4192,5 @@ fn complete_let_after_raw_string() { v.l~ } "##; - assert_eq!(get_one_completion(src, None).matchstr, "len"); + assert!(get_all_completions(src, None).iter().any(|ma| ma.matchstr == "len")); } From c830adeec84e6dfcb204f2cded0ef36264f9db06 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sun, 18 Feb 2018 22:44:00 +0900 Subject: [PATCH 6/7] use rustdoc comment for StringType --- src/racer/codecleaner.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/racer/codecleaner.rs b/src/racer/codecleaner.rs index 3fbe7ef1..bb82bdb3 100644 --- a/src/racer/codecleaner.rs +++ b/src/racer/codecleaner.rs @@ -3,8 +3,10 @@ use core::{Point, SourceByteRange}; /// Type of the string #[derive(Clone, Copy, Debug)] enum StringType { - Raw(usize), // Raw(n) => raw string started with n #s - NotRaw, // normal string + /// Raw(n) => raw string started with n #s + Raw(usize), + /// normal string starts with " + NotRaw, } #[derive(Clone,Copy)] @@ -138,7 +140,6 @@ impl<'a> CodeIndicesIter<'a> { StringType::Raw(level) => { // raw string (eg br#"\"#) // detect corresponding end(if start is r##", ##") greedily - #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum SharpState { Sharp((usize, usize)), // (Num of preceeding #s, Pos of end ") None, // No preceeding "##... From a9d354b94351d3b3e4495022d3eb33e360ade0cb Mon Sep 17 00:00:00 2001 From: kngwyu Date: Wed, 21 Feb 2018 21:35:39 +0900 Subject: [PATCH 7/7] renamed enum StringType and fixed typo --- src/racer/codecleaner.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/racer/codecleaner.rs b/src/racer/codecleaner.rs index bb82bdb3..a31351c4 100644 --- a/src/racer/codecleaner.rs +++ b/src/racer/codecleaner.rs @@ -2,11 +2,11 @@ use core::{Point, SourceByteRange}; /// Type of the string #[derive(Clone, Copy, Debug)] -enum StringType { +enum StrStyle { + /// normal string starts with " + Cooked, /// Raw(n) => raw string started with n #s Raw(usize), - /// normal string starts with " - NotRaw, } #[derive(Clone,Copy)] @@ -14,7 +14,7 @@ enum State { Code, Comment, CommentBlock, - String(StringType), + String(StrStyle), Char, Finished } @@ -133,16 +133,16 @@ impl<'a> CodeIndicesIter<'a> { self.code() } - fn string(&mut self, str_type: StringType) -> SourceByteRange { + fn string(&mut self, str_type: StrStyle) -> SourceByteRange { let src_bytes = self.src.as_bytes(); let mut pos = self.pos; match str_type { - StringType::Raw(level) => { + StrStyle::Raw(level) => { // raw string (eg br#"\"#) - // detect corresponding end(if start is r##", ##") greedily + // detect corresponding end(if start is r##", "##) greedily enum SharpState { - Sharp((usize, usize)), // (Num of preceeding #s, Pos of end ") - None, // No preceeding "##... + Sharp((usize, usize)), // (Num of preceding #s, Pos of end ") + None, // No preceding "##... } let mut cur_state = SharpState::None; let mut end_was_found = false; @@ -173,7 +173,7 @@ impl<'a> CodeIndicesIter<'a> { pos = src_bytes.len(); } } - StringType::NotRaw => { + StrStyle::Cooked => { let mut is_not_escaped = true; for &b in &src_bytes[pos..] { pos += 1; @@ -204,21 +204,21 @@ impl<'a> CodeIndicesIter<'a> { self.code() } - fn detect_str_type(&self, pos: usize) -> StringType { + fn detect_str_type(&self, pos: usize) -> StrStyle { let src_bytes = self.src.as_bytes(); let mut sharp = 0; if pos == 0 { - return StringType::NotRaw; + return StrStyle::Cooked; } // now pos is at one byte after ", so we have to start at pos - 2 for &b in src_bytes[..pos - 1].iter().rev() { match b { b'#' => sharp += 1, - b'r' => return StringType::Raw(sharp), - _ => return StringType::NotRaw, + b'r' => return StrStyle::Raw(sharp), + _ => return StrStyle::Cooked, } } - StringType::NotRaw + StrStyle::Cooked } }