Repository: racer-rust/racer (language: rust)

Issue:
Racer outputs multiple-line results
* Racer version: master. Also present in release 2.0.8

When completing a `const` that occurs any number of lines directly above another `const`, racer matches on not only the identifier of the const, but everything until the end of the line the second const is on as well.

# Reproducing the bug

Given a small file `example.rs`:

```rust
pub const SE



pub const A_CONST: u8 = 2
```

Get completions for the first line:

```bash
racer complete 1 12 example.rs
```

The output is:

```
PREFIX 10,12,SE
MATCH SE



pub const A_CONST,1,10,example.rs,Const,pub const SE
END
```

&nbsp;

&nbsp;


Interestingly, `Debug` output of the `Message` enum containing the match was

```
 Match("SE\n\n\n\npub const A_CONST", 1, 10, "example.rs", Const, "pub const SE")
```

So the problem seems to be caused in the completion machinery itself, which is why https://github.com/racer-rust/racer/pull/585 didn't fix it.


Discussion / hints:

Fix patch:
diff --git a/src/racer/matchers.rs b/src/racer/matchers.rs
index 17ae2566..681d7b1a 100644
--- a/src/racer/matchers.rs
+++ b/src/racer/matchers.rs
@@ -156,7 +156,7 @@ fn find_keyword_impl(
 fn is_const_fn(src: &str, blob_range: ByteRange) -> bool {
     if let Some(b) = strip_word(&src[blob_range.to_range()], "const") {
         let s = src[(blob_range.start + b).0..].trim_left();
-        !s.starts_with("fn") && !s.starts_with("unsafe")
+        s.starts_with("fn") || s.starts_with("unsafe")
     } else {
         false
     }
@@ -174,20 +174,22 @@ fn match_pattern_start(
 
     let blob = &src[context.range.to_range()];
     if let Some(start) = find_keyword(blob, pattern, ignore, context) {
-        if let Some(end) = blob[start.0..].find(':') {
-            let s = blob[start.0..start.0 + end].trim_right();
-            return Some(Match {
-                matchstr: s.to_owned(),
-                filepath: context.filepath.to_path_buf(),
-                point: context.range.start + start,
-                coords: None,
-                local: context.is_local,
-                mtype: mtype,
-                contextstr: first_line(blob),
-                generic_args: Vec::new(),
-                generic_types: Vec::new(),
-                docs: String::new(),
-            })
+        if let Some(end) = blob[start.0..].find(|c: char| c == ':' || c.is_whitespace()) {
+            if blob[start.0 + end..].trim_left().chars().next() == Some(':') {
+                let s = &blob[start.0..start.0 + end];
+                return Some(Match {
+                    matchstr: s.to_owned(),
+                    filepath: context.filepath.to_path_buf(),
+                    point: context.range.start + start,
+                    coords: None,
+                    local: context.is_local,
+                    mtype: mtype,
+                    contextstr: first_line(blob),
+                    generic_args: Vec::new(),
+                    generic_types: Vec::new(),
+                    docs: String::new(),
+                })
+            }
         }
     }
     None


Test patch: