diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs
index a11e98ac0be..2d61a3aef53 100644
--- a/crates/ra_analysis/src/completion.rs
+++ b/crates/ra_analysis/src/completion.rs
@@ -1,114 +1,52 @@
 mod completion_item;
-mod reference_completion;
+mod completion_context;
+
+mod complete_fn_param;
+mod complete_keyword;
+mod complete_snippet;
+mod complete_path;
+mod complete_scope;
 
-use ra_editor::find_node_at_offset;
-use ra_text_edit::AtomTextEdit;
-use ra_syntax::{
-    algo::visit::{visitor_ctx, VisitorCtx},
-    ast,
-    AstNode,
-    SyntaxNodeRef,
-};
 use ra_db::SyntaxDatabase;
-use rustc_hash::{FxHashMap};
-use hir::source_binder;
 
 use crate::{
     db,
     Cancelable, FilePosition,
-    completion::completion_item::{Completions, CompletionKind},
+    completion::{
+        completion_item::{Completions, CompletionKind},
+        completion_context::CompletionContext,
+    },
 };
 
 pub use crate::completion::completion_item::{CompletionItem, InsertText};
 
+/// Main entry point for copmletion. We run comletion as a two-phase process.
+///
+/// First, we look at the position and collect a so-called `CompletionContext.
+/// This is a somewhat messy process, because, during completion, syntax tree is
+/// incomplete and can look readlly weired.
+///
+/// Once the context is collected, we run a series of completion routines whihc
+/// look at the context and produce completion items.
 pub(crate) fn completions(
     db: &db::RootDatabase,
     position: FilePosition,
 ) -> Cancelable<Option<Completions>> {
     let original_file = db.source_file(position.file_id);
-    // Insert a fake ident to get a valid parse tree
-    let file = {
-        let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string());
-        original_file.reparse(&edit)
-    };
-
-    let module = ctry!(source_binder::module_from_position(db, position)?);
+    let ctx = ctry!(CompletionContext::new(db, &original_file, position)?);
 
     let mut acc = Completions::default();
-    let mut has_completions = false;
-    // First, let's try to complete a reference to some declaration.
-    if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
-        has_completions = true;
-        reference_completion::completions(&mut acc, db, &module, &file, name_ref)?;
-        // special case, `trait T { fn foo(i_am_a_name_ref) {} }`
-        if is_node::<ast::Param>(name_ref.syntax()) {
-            param_completions(&mut acc, name_ref.syntax());
-        }
-    }
 
-    // Otherwise, if this is a declaration, use heuristics to suggest a name.
-    if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) {
-        if is_node::<ast::Param>(name.syntax()) {
-            has_completions = true;
-            param_completions(&mut acc, name.syntax());
-        }
-    }
-    if !has_completions {
-        return Ok(None);
-    }
+    complete_fn_param::complete_fn_param(&mut acc, &ctx);
+    complete_keyword::complete_expr_keyword(&mut acc, &ctx);
+    complete_snippet::complete_expr_snippet(&mut acc, &ctx);
+    complete_snippet::complete_item_snippet(&mut acc, &ctx);
+    complete_path::complete_path(&mut acc, &ctx)?;
+    complete_scope::complete_scope(&mut acc, &ctx)?;
+
     Ok(Some(acc))
 }
 
-/// Complete repeated parametes, both name and type. For example, if all
-/// functions in a file have a `spam: &mut Spam` parameter, a completion with
-/// `spam: &mut Spam` insert text/label and `spam` lookup string will be
-/// suggested.
-fn param_completions(acc: &mut Completions, ctx: SyntaxNodeRef) {
-    let mut params = FxHashMap::default();
-    for node in ctx.ancestors() {
-        let _ = visitor_ctx(&mut params)
-            .visit::<ast::SourceFile, _>(process)
-            .visit::<ast::ItemList, _>(process)
-            .accept(node);
-    }
-    params
-        .into_iter()
-        .filter_map(|(label, (count, param))| {
-            let lookup = param.pat()?.syntax().text().to_string();
-            if count < 2 {
-                None
-            } else {
-                Some((label, lookup))
-            }
-        })
-        .for_each(|(label, lookup)| {
-            CompletionItem::new(label)
-                .lookup_by(lookup)
-                .kind(CompletionKind::Magic)
-                .add_to(acc)
-        });
-
-    fn process<'a, N: ast::FnDefOwner<'a>>(
-        node: N,
-        params: &mut FxHashMap<String, (u32, ast::Param<'a>)>,
-    ) {
-        node.functions()
-            .filter_map(|it| it.param_list())
-            .flat_map(|it| it.params())
-            .for_each(|param| {
-                let text = param.syntax().text().to_string();
-                params.entry(text).or_insert((0, param)).0 += 1;
-            })
-    }
-}
-
-fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool {
-    match node.ancestors().filter_map(N::cast).next() {
-        None => false,
-        Some(n) => n.syntax().range() == node.range(),
-    }
-}
-
 #[cfg(test)]
 fn check_completion(code: &str, expected_completions: &str, kind: CompletionKind) {
     use crate::mock_analysis::{single_file_with_position, analysis_and_position};
@@ -120,51 +58,3 @@ fn check_completion(code: &str, expected_completions: &str, kind: CompletionKind
     let completions = completions(&analysis.imp.db, position).unwrap().unwrap();
     completions.assert_match(expected_completions, kind);
 }
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    fn check_magic_completion(code: &str, expected_completions: &str) {
-        check_completion(code, expected_completions, CompletionKind::Magic);
-    }
-
-    #[test]
-    fn test_param_completion_last_param() {
-        check_magic_completion(
-            r"
-            fn foo(file_id: FileId) {}
-            fn bar(file_id: FileId) {}
-            fn baz(file<|>) {}
-            ",
-            r#"file_id "file_id: FileId""#,
-        );
-    }
-
-    #[test]
-    fn test_param_completion_nth_param() {
-        check_magic_completion(
-            r"
-            fn foo(file_id: FileId) {}
-            fn bar(file_id: FileId) {}
-            fn baz(file<|>, x: i32) {}
-            ",
-            r#"file_id "file_id: FileId""#,
-        );
-    }
-
-    #[test]
-    fn test_param_completion_trait_param() {
-        check_magic_completion(
-            r"
-            pub(crate) trait SourceRoot {
-                pub fn contains(&self, file_id: FileId) -> bool;
-                pub fn module_map(&self) -> &ModuleMap;
-                pub fn lines(&self, file_id: FileId) -> &LineIndex;
-                pub fn syntax(&self, file<|>)
-            }
-            ",
-            r#"file_id "file_id: FileId""#,
-        );
-    }
-}
diff --git a/crates/ra_analysis/src/completion/complete_fn_param.rs b/crates/ra_analysis/src/completion/complete_fn_param.rs
new file mode 100644
index 00000000000..6a6213e67bf
--- /dev/null
+++ b/crates/ra_analysis/src/completion/complete_fn_param.rs
@@ -0,0 +1,103 @@
+use ra_syntax::{
+    algo::visit::{visitor_ctx, VisitorCtx},
+    ast,
+    AstNode,
+};
+use rustc_hash::FxHashMap;
+
+use crate::completion::{CompletionContext, Completions, CompletionKind, CompletionItem};
+
+/// Complete repeated parametes, both name and type. For example, if all
+/// functions in a file have a `spam: &mut Spam` parameter, a completion with
+/// `spam: &mut Spam` insert text/label and `spam` lookup string will be
+/// suggested.
+pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) {
+    if !ctx.is_param {
+        return;
+    }
+
+    let mut params = FxHashMap::default();
+    for node in ctx.leaf.ancestors() {
+        let _ = visitor_ctx(&mut params)
+            .visit::<ast::SourceFile, _>(process)
+            .visit::<ast::ItemList, _>(process)
+            .accept(node);
+    }
+    params
+        .into_iter()
+        .filter_map(|(label, (count, param))| {
+            let lookup = param.pat()?.syntax().text().to_string();
+            if count < 2 {
+                None
+            } else {
+                Some((label, lookup))
+            }
+        })
+        .for_each(|(label, lookup)| {
+            CompletionItem::new(label)
+                .lookup_by(lookup)
+                .kind(CompletionKind::Magic)
+                .add_to(acc)
+        });
+
+    fn process<'a, N: ast::FnDefOwner<'a>>(
+        node: N,
+        params: &mut FxHashMap<String, (u32, ast::Param<'a>)>,
+    ) {
+        node.functions()
+            .filter_map(|it| it.param_list())
+            .flat_map(|it| it.params())
+            .for_each(|param| {
+                let text = param.syntax().text().to_string();
+                params.entry(text).or_insert((0, param)).0 += 1;
+            })
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::completion::*;
+
+    fn check_magic_completion(code: &str, expected_completions: &str) {
+        check_completion(code, expected_completions, CompletionKind::Magic);
+    }
+
+    #[test]
+    fn test_param_completion_last_param() {
+        check_magic_completion(
+            r"
+            fn foo(file_id: FileId) {}
+            fn bar(file_id: FileId) {}
+            fn baz(file<|>) {}
+            ",
+            r#"file_id "file_id: FileId""#,
+        );
+    }
+
+    #[test]
+    fn test_param_completion_nth_param() {
+        check_magic_completion(
+            r"
+            fn foo(file_id: FileId) {}
+            fn bar(file_id: FileId) {}
+            fn baz(file<|>, x: i32) {}
+            ",
+            r#"file_id "file_id: FileId""#,
+        );
+    }
+
+    #[test]
+    fn test_param_completion_trait_param() {
+        check_magic_completion(
+            r"
+            pub(crate) trait SourceRoot {
+                pub fn contains(&self, file_id: FileId) -> bool;
+                pub fn module_map(&self) -> &ModuleMap;
+                pub fn lines(&self, file_id: FileId) -> &LineIndex;
+                pub fn syntax(&self, file<|>)
+            }
+            ",
+            r#"file_id "file_id: FileId""#,
+        );
+    }
+}
diff --git a/crates/ra_analysis/src/completion/complete_keyword.rs b/crates/ra_analysis/src/completion/complete_keyword.rs
new file mode 100644
index 00000000000..dead15bb6f0
--- /dev/null
+++ b/crates/ra_analysis/src/completion/complete_keyword.rs
@@ -0,0 +1,204 @@
+use ra_syntax::{
+    algo::visit::{visitor, Visitor},
+    AstNode,
+    ast::{self, LoopBodyOwner},
+    SyntaxKind::*, SyntaxNodeRef,
+};
+
+use crate::completion::{CompletionContext, CompletionItem, Completions, CompletionKind::*};
+
+pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
+    if !ctx.is_trivial_path {
+        return;
+    }
+    let fn_def = match ctx.enclosing_fn {
+        Some(it) => it,
+        None => return,
+    };
+    acc.add(keyword("if", "if $0 {}"));
+    acc.add(keyword("match", "match $0 {}"));
+    acc.add(keyword("while", "while $0 {}"));
+    acc.add(keyword("loop", "loop {$0}"));
+
+    if ctx.after_if {
+        acc.add(keyword("else", "else {$0}"));
+        acc.add(keyword("else if", "else if $0 {}"));
+    }
+    if is_in_loop_body(ctx.leaf) {
+        acc.add(keyword("continue", "continue"));
+        acc.add(keyword("break", "break"));
+    }
+    acc.add_all(complete_return(fn_def, ctx.is_stmt));
+}
+
+fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool {
+    for node in leaf.ancestors() {
+        if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
+            break;
+        }
+        let loop_body = visitor()
+            .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
+            .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
+            .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
+            .accept(node);
+        if let Some(Some(body)) = loop_body {
+            if leaf.range().is_subrange(&body.syntax().range()) {
+                return true;
+            }
+        }
+    }
+    false
+}
+
+fn complete_return(fn_def: ast::FnDef, is_stmt: bool) -> Option<CompletionItem> {
+    let snip = match (is_stmt, fn_def.ret_type().is_some()) {
+        (true, true) => "return $0;",
+        (true, false) => "return;",
+        (false, true) => "return $0",
+        (false, false) => "return",
+    };
+    Some(keyword("return", snip))
+}
+
+fn keyword(kw: &str, snippet: &str) -> CompletionItem {
+    CompletionItem::new(kw)
+        .kind(Keyword)
+        .snippet(snippet)
+        .build()
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::completion::{CompletionKind, check_completion};
+    fn check_keyword_completion(code: &str, expected_completions: &str) {
+        check_completion(code, expected_completions, CompletionKind::Keyword);
+    }
+
+    #[test]
+    fn test_completion_kewords() {
+        check_keyword_completion(
+            r"
+            fn quux() {
+                <|>
+            }
+            ",
+            r#"
+            if "if $0 {}"
+            match "match $0 {}"
+            while "while $0 {}"
+            loop "loop {$0}"
+            return "return"
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_completion_else() {
+        check_keyword_completion(
+            r"
+            fn quux() {
+                if true {
+                    ()
+                } <|>
+            }
+            ",
+            r#"
+            if "if $0 {}"
+            match "match $0 {}"
+            while "while $0 {}"
+            loop "loop {$0}"
+            else "else {$0}"
+            else if "else if $0 {}"
+            return "return"
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_completion_return_value() {
+        check_keyword_completion(
+            r"
+            fn quux() -> i32 {
+                <|>
+                92
+            }
+            ",
+            r#"
+            if "if $0 {}"
+            match "match $0 {}"
+            while "while $0 {}"
+            loop "loop {$0}"
+            return "return $0;"
+            "#,
+        );
+        check_keyword_completion(
+            r"
+            fn quux() {
+                <|>
+                92
+            }
+            ",
+            r#"
+            if "if $0 {}"
+            match "match $0 {}"
+            while "while $0 {}"
+            loop "loop {$0}"
+            return "return;"
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_completion_return_no_stmt() {
+        check_keyword_completion(
+            r"
+            fn quux() -> i32 {
+                match () {
+                    () => <|>
+                }
+            }
+            ",
+            r#"
+            if "if $0 {}"
+            match "match $0 {}"
+            while "while $0 {}"
+            loop "loop {$0}"
+            return "return $0"
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_continue_break_completion() {
+        check_keyword_completion(
+            r"
+            fn quux() -> i32 {
+                loop { <|> }
+            }
+            ",
+            r#"
+            if "if $0 {}"
+            match "match $0 {}"
+            while "while $0 {}"
+            loop "loop {$0}"
+            continue "continue"
+            break "break"
+            return "return $0"
+            "#,
+        );
+        check_keyword_completion(
+            r"
+            fn quux() -> i32 {
+                loop { || { <|> } }
+            }
+            ",
+            r#"
+            if "if $0 {}"
+            match "match $0 {}"
+            while "while $0 {}"
+            loop "loop {$0}"
+            return "return $0"
+            "#,
+        );
+    }
+}
diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs
new file mode 100644
index 00000000000..5fc24af7209
--- /dev/null
+++ b/crates/ra_analysis/src/completion/complete_path.rs
@@ -0,0 +1,95 @@
+use crate::{
+    Cancelable,
+    completion::{CompletionItem, Completions, CompletionKind::*, CompletionContext},
+};
+
+pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> {
+    let (path, module) = match (&ctx.path_prefix, &ctx.module) {
+        (Some(path), Some(module)) => (path.clone(), module),
+        _ => return Ok(()),
+    };
+    let def_id = match module.resolve_path(ctx.db, path)? {
+        Some(it) => it,
+        None => return Ok(()),
+    };
+    let target_module = match def_id.resolve(ctx.db)? {
+        hir::Def::Module(it) => it,
+        _ => return Ok(()),
+    };
+    let module_scope = target_module.scope(ctx.db)?;
+    module_scope.entries().for_each(|(name, _res)| {
+        CompletionItem::new(name.to_string())
+            .kind(Reference)
+            .add_to(acc)
+    });
+    Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::completion::{CompletionKind, check_completion};
+
+    fn check_reference_completion(code: &str, expected_completions: &str) {
+        check_completion(code, expected_completions, CompletionKind::Reference);
+    }
+
+    #[test]
+    fn completes_use_item_starting_with_self() {
+        check_reference_completion(
+            r"
+            use self::m::<|>;
+
+            mod m {
+                struct Bar;
+            }
+            ",
+            "Bar",
+        );
+    }
+
+    #[test]
+    fn completes_use_item_starting_with_crate() {
+        check_reference_completion(
+            "
+            //- /lib.rs
+            mod foo;
+            struct Spam;
+            //- /foo.rs
+            use crate::Sp<|>
+            ",
+            "Spam;foo",
+        );
+    }
+
+    #[test]
+    fn completes_nested_use_tree() {
+        check_reference_completion(
+            "
+            //- /lib.rs
+            mod foo;
+            struct Spam;
+            //- /foo.rs
+            use crate::{Sp<|>};
+            ",
+            "Spam;foo",
+        );
+    }
+
+    #[test]
+    fn completes_deeply_nested_use_tree() {
+        check_reference_completion(
+            "
+            //- /lib.rs
+            mod foo;
+            pub mod bar {
+                pub mod baz {
+                    pub struct Spam;
+                }
+            }
+            //- /foo.rs
+            use crate::{bar::{baz::Sp<|>}};
+            ",
+            "Spam",
+        );
+    }
+}
diff --git a/crates/ra_analysis/src/completion/complete_scope.rs b/crates/ra_analysis/src/completion/complete_scope.rs
new file mode 100644
index 00000000000..d07c0e46d97
--- /dev/null
+++ b/crates/ra_analysis/src/completion/complete_scope.rs
@@ -0,0 +1,171 @@
+use rustc_hash::FxHashSet;
+use ra_syntax::TextUnit;
+
+use crate::{
+    Cancelable,
+    completion::{CompletionItem, Completions, CompletionKind::*, CompletionContext},
+};
+
+pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> {
+    if !ctx.is_trivial_path {
+        return Ok(());
+    }
+    if let Some(fn_def) = ctx.enclosing_fn {
+        let scopes = hir::FnScopes::new(fn_def);
+        complete_fn(acc, &scopes, ctx.offset);
+    }
+
+    if let Some(module) = &ctx.module {
+        let module_scope = module.scope(ctx.db)?;
+        module_scope
+            .entries()
+            .filter(|(_name, res)| {
+                // Don't expose this item
+                match res.import {
+                    None => true,
+                    Some(import) => {
+                        let range = import.range(ctx.db, module.source().file_id());
+                        !range.is_subrange(&ctx.leaf.range())
+                    }
+                }
+            })
+            .for_each(|(name, _res)| {
+                CompletionItem::new(name.to_string())
+                    .kind(Reference)
+                    .add_to(acc)
+            });
+    }
+
+    Ok(())
+}
+
+fn complete_fn(acc: &mut Completions, scopes: &hir::FnScopes, offset: TextUnit) {
+    let mut shadowed = FxHashSet::default();
+    scopes
+        .scope_chain_for_offset(offset)
+        .flat_map(|scope| scopes.entries(scope).iter())
+        .filter(|entry| shadowed.insert(entry.name()))
+        .for_each(|entry| {
+            CompletionItem::new(entry.name().to_string())
+                .kind(Reference)
+                .add_to(acc)
+        });
+    if scopes.self_param.is_some() {
+        CompletionItem::new("self").kind(Reference).add_to(acc);
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::completion::{CompletionKind, check_completion};
+
+    fn check_reference_completion(code: &str, expected_completions: &str) {
+        check_completion(code, expected_completions, CompletionKind::Reference);
+    }
+
+    #[test]
+    fn completes_bindings_from_let() {
+        check_reference_completion(
+            r"
+            fn quux(x: i32) {
+                let y = 92;
+                1 + <|>;
+                let z = ();
+            }
+            ",
+            "y;x;quux",
+        );
+    }
+
+    #[test]
+    fn completes_bindings_from_if_let() {
+        check_reference_completion(
+            r"
+            fn quux() {
+                if let Some(x) = foo() {
+                    let y = 92;
+                };
+                if let Some(a) = bar() {
+                    let b = 62;
+                    1 + <|>
+                }
+            }
+            ",
+            "b;a;quux",
+        );
+    }
+
+    #[test]
+    fn completes_bindings_from_for() {
+        check_reference_completion(
+            r"
+            fn quux() {
+                for x in &[1, 2, 3] {
+                    <|>
+                }
+            }
+            ",
+            "x;quux",
+        );
+    }
+
+    #[test]
+    fn completes_module_items() {
+        check_reference_completion(
+            r"
+            struct Foo;
+            enum Baz {}
+            fn quux() {
+                <|>
+            }
+            ",
+            "quux;Foo;Baz",
+        );
+    }
+
+    #[test]
+    fn completes_module_items_in_nested_modules() {
+        check_reference_completion(
+            r"
+            struct Foo;
+            mod m {
+                struct Bar;
+                fn quux() { <|> }
+            }
+            ",
+            "quux;Bar",
+        );
+    }
+
+    #[test]
+    fn completes_return_type() {
+        check_reference_completion(
+            r"
+            struct Foo;
+            fn x() -> <|>
+            ",
+            "Foo;x",
+        )
+    }
+
+    #[test]
+    fn dont_show_to_completions_for_shadowing() {
+        check_reference_completion(
+            r"
+            fn foo() -> {
+                let bar = 92;
+                {
+                    let bar = 62;
+                    <|>
+                }
+            }
+            ",
+            "bar;foo",
+        )
+    }
+
+    #[test]
+    fn completes_self_in_methods() {
+        check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self")
+    }
+}
diff --git a/crates/ra_analysis/src/completion/complete_snippet.rs b/crates/ra_analysis/src/completion/complete_snippet.rs
new file mode 100644
index 00000000000..ccd68832b4a
--- /dev/null
+++ b/crates/ra_analysis/src/completion/complete_snippet.rs
@@ -0,0 +1,76 @@
+use crate::completion::{CompletionItem, Completions, CompletionKind::*, CompletionContext};
+
+pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
+    if !(ctx.is_trivial_path && ctx.enclosing_fn.is_some()) {
+        return;
+    }
+    CompletionItem::new("pd")
+        .snippet("eprintln!(\"$0 = {:?}\", $0);")
+        .kind(Snippet)
+        .add_to(acc);
+    CompletionItem::new("ppd")
+        .snippet("eprintln!(\"$0 = {:#?}\", $0);")
+        .kind(Snippet)
+        .add_to(acc);
+}
+
+pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
+    if !ctx.is_new_item {
+        return;
+    }
+    CompletionItem::new("Test function")
+        .lookup_by("tfn")
+        .snippet(
+            "\
+#[test]
+fn ${1:feature}() {
+    $0
+}",
+        )
+        .kind(Snippet)
+        .add_to(acc);
+    CompletionItem::new("pub(crate)")
+        .snippet("pub(crate) $0")
+        .kind(Snippet)
+        .add_to(acc);
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::completion::{CompletionKind, check_completion};
+    fn check_snippet_completion(code: &str, expected_completions: &str) {
+        check_completion(code, expected_completions, CompletionKind::Snippet);
+    }
+
+    #[test]
+    fn completes_snippets_in_expressions() {
+        check_snippet_completion(
+            r"fn foo(x: i32) { <|> }",
+            r##"
+            pd "eprintln!(\"$0 = {:?}\", $0);"
+            ppd "eprintln!(\"$0 = {:#?}\", $0);"
+            "##,
+        );
+    }
+
+    #[test]
+    fn completes_snippets_in_items() {
+        // check_snippet_completion(r"
+        //     <|>
+        //     ",
+        //     r##"[CompletionItem { label: "Test function", lookup: None, snippet: Some("#[test]\nfn test_${1:feature}() {\n$0\n}"##,
+        // );
+        check_snippet_completion(
+            r"
+            #[cfg(test)]
+            mod tests {
+                <|>
+            }
+            ",
+            r##"
+            tfn "Test function" "#[test]\nfn ${1:feature}() {\n    $0\n}"
+            pub(crate) "pub(crate) $0"
+            "##,
+        );
+    }
+}
diff --git a/crates/ra_analysis/src/completion/completion_context.rs b/crates/ra_analysis/src/completion/completion_context.rs
new file mode 100644
index 00000000000..064fbc6f7be
--- /dev/null
+++ b/crates/ra_analysis/src/completion/completion_context.rs
@@ -0,0 +1,156 @@
+use ra_editor::find_node_at_offset;
+use ra_text_edit::AtomTextEdit;
+use ra_syntax::{
+    algo::find_leaf_at_offset,
+    ast,
+    AstNode,
+    SyntaxNodeRef,
+    SourceFileNode,
+    TextUnit,
+    SyntaxKind::*,
+};
+use hir::source_binder;
+
+use crate::{db, FilePosition, Cancelable};
+
+/// `CompletionContext` is created early during completion to figure out, where
+/// exactly is the cursor, syntax-wise.
+#[derive(Debug)]
+pub(super) struct CompletionContext<'a> {
+    pub(super) db: &'a db::RootDatabase,
+    pub(super) offset: TextUnit,
+    pub(super) leaf: SyntaxNodeRef<'a>,
+    pub(super) module: Option<hir::Module>,
+    pub(super) enclosing_fn: Option<ast::FnDef<'a>>,
+    pub(super) is_param: bool,
+    /// A single-indent path, like `foo`.
+    pub(super) is_trivial_path: bool,
+    /// If not a trivial, path, the prefix (qualifier).
+    pub(super) path_prefix: Option<hir::Path>,
+    pub(super) after_if: bool,
+    pub(super) is_stmt: bool,
+    /// Something is typed at the "top" level, in module or impl/trait.
+    pub(super) is_new_item: bool,
+}
+
+impl<'a> CompletionContext<'a> {
+    pub(super) fn new(
+        db: &'a db::RootDatabase,
+        original_file: &'a SourceFileNode,
+        position: FilePosition,
+    ) -> Cancelable<Option<CompletionContext<'a>>> {
+        let module = source_binder::module_from_position(db, position)?;
+        let leaf =
+            ctry!(find_leaf_at_offset(original_file.syntax(), position.offset).left_biased());
+        let mut ctx = CompletionContext {
+            db,
+            leaf,
+            offset: position.offset,
+            module,
+            enclosing_fn: None,
+            is_param: false,
+            is_trivial_path: false,
+            path_prefix: None,
+            after_if: false,
+            is_stmt: false,
+            is_new_item: false,
+        };
+        ctx.fill(original_file, position.offset);
+        Ok(Some(ctx))
+    }
+
+    fn fill(&mut self, original_file: &SourceFileNode, offset: TextUnit) {
+        // Insert a fake ident to get a valid parse tree. We will use this file
+        // to determine context, though the original_file will be used for
+        // actual completion.
+        let file = {
+            let edit = AtomTextEdit::insert(offset, "intellijRulezz".to_string());
+            original_file.reparse(&edit)
+        };
+
+        // First, let's try to complete a reference to some declaration.
+        if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), offset) {
+            // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
+            // See RFC#1685.
+            if is_node::<ast::Param>(name_ref.syntax()) {
+                self.is_param = true;
+                return;
+            }
+            self.classify_name_ref(&file, name_ref);
+        }
+
+        // Otherwise, see if this is a declaration. We can use heuristics to
+        // suggest declaration names, see `CompletionKind::Magic`.
+        if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), offset) {
+            if is_node::<ast::Param>(name.syntax()) {
+                self.is_param = true;
+                return;
+            }
+        }
+    }
+    fn classify_name_ref(&mut self, file: &SourceFileNode, name_ref: ast::NameRef) {
+        let name_range = name_ref.syntax().range();
+        let top_node = name_ref
+            .syntax()
+            .ancestors()
+            .take_while(|it| it.range() == name_range)
+            .last()
+            .unwrap();
+
+        match top_node.parent().map(|it| it.kind()) {
+            Some(SOURCE_FILE) | Some(ITEM_LIST) => {
+                self.is_new_item = true;
+                return;
+            }
+            _ => (),
+        }
+
+        let parent = match name_ref.syntax().parent() {
+            Some(it) => it,
+            None => return,
+        };
+        if let Some(segment) = ast::PathSegment::cast(parent) {
+            let path = segment.parent_path();
+            if let Some(mut path) = hir::Path::from_ast(path) {
+                if !path.is_ident() {
+                    path.segments.pop().unwrap();
+                    self.path_prefix = Some(path);
+                    return;
+                }
+            }
+            if path.qualifier().is_none() {
+                self.is_trivial_path = true;
+                self.enclosing_fn = self
+                    .leaf
+                    .ancestors()
+                    .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
+                    .find_map(ast::FnDef::cast);
+
+                self.is_stmt = match name_ref
+                    .syntax()
+                    .ancestors()
+                    .filter_map(ast::ExprStmt::cast)
+                    .next()
+                {
+                    None => false,
+                    Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(),
+                };
+
+                if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
+                    if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) {
+                        if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
+                            self.after_if = true;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool {
+    match node.ancestors().filter_map(N::cast).next() {
+        None => false,
+        Some(n) => n.syntax().range() == node.range(),
+    }
+}
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs
deleted file mode 100644
index c2ac9545376..00000000000
--- a/crates/ra_analysis/src/completion/reference_completion.rs
+++ /dev/null
@@ -1,633 +0,0 @@
-use rustc_hash::{FxHashSet};
-use ra_editor::find_node_at_offset;
-use ra_syntax::{
-    algo::visit::{visitor, Visitor},
-    SourceFileNode, AstNode,
-    ast::{self, LoopBodyOwner},
-    SyntaxKind::*,
-};
-use hir::{
-    self,
-    FnScopes, Def, Path
-};
-
-use crate::{
-    db::RootDatabase,
-    completion::{CompletionItem, Completions, CompletionKind::*},
-    Cancelable
-};
-
-pub(super) fn completions(
-    acc: &mut Completions,
-    db: &RootDatabase,
-    module: &hir::Module,
-    file: &SourceFileNode,
-    name_ref: ast::NameRef,
-) -> Cancelable<()> {
-    let kind = match classify_name_ref(name_ref) {
-        Some(it) => it,
-        None => return Ok(()),
-    };
-
-    match kind {
-        NameRefKind::LocalRef { enclosing_fn } => {
-            if let Some(fn_def) = enclosing_fn {
-                let scopes = FnScopes::new(fn_def);
-                complete_fn(name_ref, &scopes, acc);
-                complete_expr_keywords(&file, fn_def, name_ref, acc);
-                complete_expr_snippets(acc);
-            }
-
-            let module_scope = module.scope(db)?;
-            module_scope
-                .entries()
-                .filter(|(_name, res)| {
-                    // Don't expose this item
-                    match res.import {
-                        None => true,
-                        Some(import) => {
-                            let range = import.range(db, module.source().file_id());
-                            !range.is_subrange(&name_ref.syntax().range())
-                        }
-                    }
-                })
-                .for_each(|(name, _res)| {
-                    CompletionItem::new(name.to_string())
-                        .kind(Reference)
-                        .add_to(acc)
-                });
-        }
-        NameRefKind::Path(path) => complete_path(acc, db, module, path)?,
-        NameRefKind::BareIdentInMod => {
-            let name_range = name_ref.syntax().range();
-            let top_node = name_ref
-                .syntax()
-                .ancestors()
-                .take_while(|it| it.range() == name_range)
-                .last()
-                .unwrap();
-            match top_node.parent().map(|it| it.kind()) {
-                Some(SOURCE_FILE) | Some(ITEM_LIST) => complete_mod_item_snippets(acc),
-                _ => (),
-            }
-        }
-    }
-    Ok(())
-}
-
-enum NameRefKind<'a> {
-    /// NameRef is a part of single-segment path, for example, a refernece to a
-    /// local variable.
-    LocalRef {
-        enclosing_fn: Option<ast::FnDef<'a>>,
-    },
-    /// NameRef is the last segment in some path
-    Path(Path),
-    /// NameRef is bare identifier at the module's root.
-    /// Used for keyword completion
-    BareIdentInMod,
-}
-
-fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
-    let name_range = name_ref.syntax().range();
-    let top_node = name_ref
-        .syntax()
-        .ancestors()
-        .take_while(|it| it.range() == name_range)
-        .last()
-        .unwrap();
-    match top_node.parent().map(|it| it.kind()) {
-        Some(SOURCE_FILE) | Some(ITEM_LIST) => return Some(NameRefKind::BareIdentInMod),
-        _ => (),
-    }
-
-    let parent = name_ref.syntax().parent()?;
-    if let Some(segment) = ast::PathSegment::cast(parent) {
-        let path = segment.parent_path();
-        if let Some(path) = Path::from_ast(path) {
-            if !path.is_ident() {
-                return Some(NameRefKind::Path(path));
-            }
-        }
-        if path.qualifier().is_none() {
-            let enclosing_fn = name_ref
-                .syntax()
-                .ancestors()
-                .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
-                .find_map(ast::FnDef::cast);
-            return Some(NameRefKind::LocalRef { enclosing_fn });
-        }
-    }
-    None
-}
-
-fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Completions) {
-    let mut shadowed = FxHashSet::default();
-    scopes
-        .scope_chain(name_ref.syntax())
-        .flat_map(|scope| scopes.entries(scope).iter())
-        .filter(|entry| shadowed.insert(entry.name()))
-        .for_each(|entry| {
-            CompletionItem::new(entry.name().to_string())
-                .kind(Reference)
-                .add_to(acc)
-        });
-    if scopes.self_param.is_some() {
-        CompletionItem::new("self").kind(Reference).add_to(acc);
-    }
-}
-
-fn complete_path(
-    acc: &mut Completions,
-    db: &RootDatabase,
-    module: &hir::Module,
-    mut path: Path,
-) -> Cancelable<()> {
-    if path.segments.is_empty() {
-        return Ok(());
-    }
-    path.segments.pop();
-    let def_id = match module.resolve_path(db, path)? {
-        None => return Ok(()),
-        Some(it) => it,
-    };
-    let target_module = match def_id.resolve(db)? {
-        Def::Module(it) => it,
-        _ => return Ok(()),
-    };
-    let module_scope = target_module.scope(db)?;
-    module_scope.entries().for_each(|(name, _res)| {
-        CompletionItem::new(name.to_string())
-            .kind(Reference)
-            .add_to(acc)
-    });
-    Ok(())
-}
-
-fn complete_mod_item_snippets(acc: &mut Completions) {
-    CompletionItem::new("Test function")
-        .lookup_by("tfn")
-        .snippet(
-            "\
-#[test]
-fn ${1:feature}() {
-    $0
-}",
-        )
-        .kind(Snippet)
-        .add_to(acc);
-    CompletionItem::new("pub(crate)")
-        .snippet("pub(crate) $0")
-        .kind(Snippet)
-        .add_to(acc);
-}
-
-fn complete_expr_keywords(
-    file: &SourceFileNode,
-    fn_def: ast::FnDef,
-    name_ref: ast::NameRef,
-    acc: &mut Completions,
-) {
-    acc.add(keyword("if", "if $0 {}"));
-    acc.add(keyword("match", "match $0 {}"));
-    acc.add(keyword("while", "while $0 {}"));
-    acc.add(keyword("loop", "loop {$0}"));
-
-    if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
-        if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) {
-            if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
-                acc.add(keyword("else", "else {$0}"));
-                acc.add(keyword("else if", "else if $0 {}"));
-            }
-        }
-    }
-    if is_in_loop_body(name_ref) {
-        acc.add(keyword("continue", "continue"));
-        acc.add(keyword("break", "break"));
-    }
-    acc.add_all(complete_return(fn_def, name_ref));
-}
-
-fn is_in_loop_body(name_ref: ast::NameRef) -> bool {
-    for node in name_ref.syntax().ancestors() {
-        if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
-            break;
-        }
-        let loop_body = visitor()
-            .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
-            .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
-            .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
-            .accept(node);
-        if let Some(Some(body)) = loop_body {
-            if name_ref
-                .syntax()
-                .range()
-                .is_subrange(&body.syntax().range())
-            {
-                return true;
-            }
-        }
-    }
-    false
-}
-
-fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<CompletionItem> {
-    // let is_last_in_block = name_ref.syntax().ancestors().filter_map(ast::Expr::cast)
-    //     .next()
-    //     .and_then(|it| it.syntax().parent())
-    //     .and_then(ast::Block::cast)
-    //     .is_some();
-
-    // if is_last_in_block {
-    //     return None;
-    // }
-
-    let is_stmt = match name_ref
-        .syntax()
-        .ancestors()
-        .filter_map(ast::ExprStmt::cast)
-        .next()
-    {
-        None => false,
-        Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(),
-    };
-    let snip = match (is_stmt, fn_def.ret_type().is_some()) {
-        (true, true) => "return $0;",
-        (true, false) => "return;",
-        (false, true) => "return $0",
-        (false, false) => "return",
-    };
-    Some(keyword("return", snip))
-}
-
-fn keyword(kw: &str, snippet: &str) -> CompletionItem {
-    CompletionItem::new(kw)
-        .kind(Keyword)
-        .snippet(snippet)
-        .build()
-}
-
-fn complete_expr_snippets(acc: &mut Completions) {
-    CompletionItem::new("pd")
-        .snippet("eprintln!(\"$0 = {:?}\", $0);")
-        .kind(Snippet)
-        .add_to(acc);
-    CompletionItem::new("ppd")
-        .snippet("eprintln!(\"$0 = {:#?}\", $0);")
-        .kind(Snippet)
-        .add_to(acc);
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::completion::{CompletionKind, check_completion};
-
-    fn check_reference_completion(code: &str, expected_completions: &str) {
-        check_completion(code, expected_completions, CompletionKind::Reference);
-    }
-
-    fn check_keyword_completion(code: &str, expected_completions: &str) {
-        check_completion(code, expected_completions, CompletionKind::Keyword);
-    }
-
-    fn check_snippet_completion(code: &str, expected_completions: &str) {
-        check_completion(code, expected_completions, CompletionKind::Snippet);
-    }
-
-    #[test]
-    fn test_completion_let_scope() {
-        check_reference_completion(
-            r"
-            fn quux(x: i32) {
-                let y = 92;
-                1 + <|>;
-                let z = ();
-            }
-            ",
-            "y;x;quux",
-        );
-    }
-
-    #[test]
-    fn test_completion_if_let_scope() {
-        check_reference_completion(
-            r"
-            fn quux() {
-                if let Some(x) = foo() {
-                    let y = 92;
-                };
-                if let Some(a) = bar() {
-                    let b = 62;
-                    1 + <|>
-                }
-            }
-            ",
-            "b;a;quux",
-        );
-    }
-
-    #[test]
-    fn test_completion_for_scope() {
-        check_reference_completion(
-            r"
-            fn quux() {
-                for x in &[1, 2, 3] {
-                    <|>
-                }
-            }
-            ",
-            "x;quux",
-        );
-    }
-
-    #[test]
-    fn test_completion_mod_scope() {
-        check_reference_completion(
-            r"
-            struct Foo;
-            enum Baz {}
-            fn quux() {
-                <|>
-            }
-            ",
-            "quux;Foo;Baz",
-        );
-    }
-
-    #[test]
-    fn test_completion_mod_scope_no_self_use() {
-        check_reference_completion(
-            r"
-            use foo<|>;
-            ",
-            "",
-        );
-    }
-
-    #[test]
-    fn test_completion_self_path() {
-        check_reference_completion(
-            r"
-            use self::m::<|>;
-
-            mod m {
-                struct Bar;
-            }
-            ",
-            "Bar",
-        );
-    }
-
-    #[test]
-    fn test_completion_mod_scope_nested() {
-        check_reference_completion(
-            r"
-            struct Foo;
-            mod m {
-                struct Bar;
-                fn quux() { <|> }
-            }
-            ",
-            "quux;Bar",
-        );
-    }
-
-    #[test]
-    fn test_complete_type() {
-        check_reference_completion(
-            r"
-            struct Foo;
-            fn x() -> <|>
-            ",
-            "Foo;x",
-        )
-    }
-
-    #[test]
-    fn test_complete_shadowing() {
-        check_reference_completion(
-            r"
-            fn foo() -> {
-                let bar = 92;
-                {
-                    let bar = 62;
-                    <|>
-                }
-            }
-            ",
-            "bar;foo",
-        )
-    }
-
-    #[test]
-    fn test_complete_self() {
-        check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self")
-    }
-
-    #[test]
-    fn test_complete_crate_path() {
-        check_reference_completion(
-            "
-            //- /lib.rs
-            mod foo;
-            struct Spam;
-            //- /foo.rs
-            use crate::Sp<|>
-            ",
-            "Spam;foo",
-        );
-    }
-
-    #[test]
-    fn test_complete_crate_path_with_braces() {
-        check_reference_completion(
-            "
-            //- /lib.rs
-            mod foo;
-            struct Spam;
-            //- /foo.rs
-            use crate::{Sp<|>};
-            ",
-            "Spam;foo",
-        );
-    }
-
-    #[test]
-    fn test_complete_crate_path_in_nested_tree() {
-        check_reference_completion(
-            "
-            //- /lib.rs
-            mod foo;
-            pub mod bar {
-                pub mod baz {
-                    pub struct Spam;
-                }
-            }
-            //- /foo.rs
-            use crate::{bar::{baz::Sp<|>}};
-            ",
-            "Spam",
-        );
-    }
-
-    #[test]
-    fn test_completion_kewords() {
-        check_keyword_completion(
-            r"
-            fn quux() {
-                <|>
-            }
-            ",
-            r#"
-            if "if $0 {}"
-            match "match $0 {}"
-            while "while $0 {}"
-            loop "loop {$0}"
-            return "return"
-            "#,
-        );
-    }
-
-    #[test]
-    fn test_completion_else() {
-        check_keyword_completion(
-            r"
-            fn quux() {
-                if true {
-                    ()
-                } <|>
-            }
-            ",
-            r#"
-            if "if $0 {}"
-            match "match $0 {}"
-            while "while $0 {}"
-            loop "loop {$0}"
-            else "else {$0}"
-            else if "else if $0 {}"
-            return "return"
-            "#,
-        );
-    }
-
-    #[test]
-    fn test_completion_return_value() {
-        check_keyword_completion(
-            r"
-            fn quux() -> i32 {
-                <|>
-                92
-            }
-            ",
-            r#"
-            if "if $0 {}"
-            match "match $0 {}"
-            while "while $0 {}"
-            loop "loop {$0}"
-            return "return $0;"
-            "#,
-        );
-        check_keyword_completion(
-            r"
-            fn quux() {
-                <|>
-                92
-            }
-            ",
-            r#"
-            if "if $0 {}"
-            match "match $0 {}"
-            while "while $0 {}"
-            loop "loop {$0}"
-            return "return;"
-            "#,
-        );
-    }
-
-    #[test]
-    fn test_completion_return_no_stmt() {
-        check_keyword_completion(
-            r"
-            fn quux() -> i32 {
-                match () {
-                    () => <|>
-                }
-            }
-            ",
-            r#"
-            if "if $0 {}"
-            match "match $0 {}"
-            while "while $0 {}"
-            loop "loop {$0}"
-            return "return $0"
-            "#,
-        );
-    }
-
-    #[test]
-    fn test_continue_break_completion() {
-        check_keyword_completion(
-            r"
-            fn quux() -> i32 {
-                loop { <|> }
-            }
-            ",
-            r#"
-            if "if $0 {}"
-            match "match $0 {}"
-            while "while $0 {}"
-            loop "loop {$0}"
-            continue "continue"
-            break "break"
-            return "return $0"
-            "#,
-        );
-        check_keyword_completion(
-            r"
-            fn quux() -> i32 {
-                loop { || { <|> } }
-            }
-            ",
-            r#"
-            if "if $0 {}"
-            match "match $0 {}"
-            while "while $0 {}"
-            loop "loop {$0}"
-            return "return $0"
-            "#,
-        );
-    }
-
-    #[test]
-    fn completes_snippets_in_expressions() {
-        check_snippet_completion(
-            r"fn foo(x: i32) { <|> }",
-            r##"
-            pd "eprintln!(\"$0 = {:?}\", $0);"
-            ppd "eprintln!(\"$0 = {:#?}\", $0);"
-            "##,
-        );
-    }
-
-    #[test]
-    fn completes_snippets_in_items() {
-        // check_snippet_completion(r"
-        //     <|>
-        //     ",
-        //     r##"[CompletionItem { label: "Test function", lookup: None, snippet: Some("#[test]\nfn test_${1:feature}() {\n$0\n}"##,
-        // );
-        check_snippet_completion(
-            r"
-            #[cfg(test)]
-            mod tests {
-                <|>
-            }
-            ",
-            r##"
-            tfn "Test function" "#[test]\nfn ${1:feature}() {\n    $0\n}"
-            pub(crate) "pub(crate) $0"
-            "##,
-        );
-    }
-
-}
diff --git a/crates/ra_hir/src/function/scope.rs b/crates/ra_hir/src/function/scope.rs
index 86345329166..9f1aa1ef2d8 100644
--- a/crates/ra_hir/src/function/scope.rs
+++ b/crates/ra_hir/src/function/scope.rs
@@ -1,7 +1,7 @@
 use rustc_hash::{FxHashMap, FxHashSet};
 
 use ra_syntax::{
-    AstNode, SmolStr, SyntaxNodeRef, TextRange,
+    AstNode, SmolStr, SyntaxNodeRef, TextUnit, TextRange,
     algo::generate,
     ast::{self, ArgListOwner, LoopBodyOwner, NameOwner},
 };
@@ -57,6 +57,48 @@ impl FnScopes {
             self.scopes[scope].parent
         })
     }
+    pub fn scope_chain_for_offset<'a>(
+        &'a self,
+        offset: TextUnit,
+    ) -> impl Iterator<Item = ScopeId> + 'a {
+        let scope = self
+            .scope_for
+            .iter()
+            // find containin scope
+            .min_by_key(|(ptr, _scope)| {
+                (
+                    !(ptr.range().start() <= offset && offset <= ptr.range().end()),
+                    ptr.range().len(),
+                )
+            })
+            .map(|(ptr, scope)| self.adjust(*ptr, *scope, offset));
+
+        generate(scope, move |&scope| self.scopes[scope].parent)
+    }
+    // XXX: during completion, cursor might be outside of any particular
+    // expression. Try to figure out the correct scope...
+    fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId {
+        let r = ptr.range();
+        let child_scopes = self
+            .scope_for
+            .iter()
+            .map(|(ptr, scope)| (ptr.range(), scope))
+            .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);
+
+        child_scopes
+            .max_by(|(r1, _), (r2, _)| {
+                if r2.is_subrange(&r1) {
+                    std::cmp::Ordering::Greater
+                } else if r1.is_subrange(&r2) {
+                    std::cmp::Ordering::Less
+                } else {
+                    r1.start().cmp(&r2.start())
+                }
+            })
+            .map(|(ptr, scope)| *scope)
+            .unwrap_or(original_scope)
+    }
+
     pub fn resolve_local_name<'a>(&'a self, name_ref: ast::NameRef) -> Option<&'a ScopeEntry> {
         let mut shadowed = FxHashSet::default();
         let ret = self
@@ -144,6 +186,8 @@ impl ScopeEntry {
 }
 
 fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
+    // A hack for completion :(
+    scopes.set_scope(block.syntax(), scope);
     for stmt in block.statements() {
         match stmt {
             ast::Stmt::LetStmt(stmt) => {
@@ -165,6 +209,7 @@ fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: Sco
         }
     }
     if let Some(expr) = block.expr() {
+        eprintln!("{:?}", expr);
         scopes.set_scope(expr.syntax(), scope);
         compute_expr_scopes(expr, scopes, scope);
     }