Implements new scoping pipeline

Removes scoping methods dependent on ModelElementFinder and replaces
them with methods dependent on ProtobufImportedNamespaceAwareLocalScopeProvider.

Change-Id: I50e8691a61bbb85f277fef6748ed4724c85fdd41
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue131_AddOptionsForService_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue131_AddOptionsForService_Test.java
index 401bb86..d924229 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue131_AddOptionsForService_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue131_AddOptionsForService_Test.java
@@ -13,13 +13,12 @@
 import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
 import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
 import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-import static com.google.eclipse.protobuf.junit.matchers.ContainAllNames.containAll;
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_SOURCE__TARGET;
 
-import org.eclipse.emf.ecore.EReference;
 import org.eclipse.xtext.scoping.IScope;
 import org.junit.Rule;
 import org.junit.Test;
-
 import com.google.eclipse.protobuf.junit.core.XtextRule;
 import com.google.eclipse.protobuf.protobuf.Option;
 import com.google.eclipse.protobuf.scoping.ProtobufScopeProvider;
@@ -33,7 +32,6 @@
 public class Issue131_AddOptionsForService_Test {
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
 
-  @Inject private EReference reference;
   @Inject private ProtobufScopeProvider scopeProvider;
 
   // syntax = "proto2";
@@ -52,10 +50,9 @@
   // }
   @Test public void should_support_Service_options() {
     Option option = xtext.find("code", ")", Option.class);
-    IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
-    assertThat(descriptionsIn(scope), containAll("code", "proto.code", "google.proto.code", "com.google.proto.code",
-                                                 ".com.google.proto.code",
-                                                 "info", "proto.info", "google.proto.info", "com.google.proto.info",
-                                                 ".com.google.proto.info"));
+    IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET);
+    assertThat(descriptionsIn(scope), contain("code", "proto.code", "google.proto.code",
+        "com.google.proto.code", "info", "proto.info", "google.proto.info",
+        "com.google.proto.info"));
   }
 }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue147_AddSupportForGroupOptions_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue147_AddSupportForGroupOptions_Test.java
index 62a5015..8abafc0 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue147_AddSupportForGroupOptions_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue147_AddSupportForGroupOptions_Test.java
@@ -12,25 +12,17 @@
 import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
 import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
 import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-import static com.google.eclipse.protobuf.junit.matchers.ContainAllFields.containAll;
-import static com.google.eclipse.protobuf.junit.matchers.ContainAllNames.containAll;
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_SOURCE__TARGET;
 
-import java.util.Collection;
-
-import org.eclipse.emf.ecore.EReference;
 import org.eclipse.xtext.scoping.IScope;
 import org.junit.Rule;
 import org.junit.Test;
 
 import com.google.eclipse.protobuf.junit.core.XtextRule;
 import com.google.eclipse.protobuf.protobuf.CustomFieldOption;
-import com.google.eclipse.protobuf.protobuf.Group;
-import com.google.eclipse.protobuf.protobuf.MessageField;
 import com.google.eclipse.protobuf.protobuf.NativeFieldOption;
-import com.google.eclipse.protobuf.scoping.ProtoDescriptor;
-import com.google.eclipse.protobuf.scoping.ProtoDescriptorProvider;
 import com.google.eclipse.protobuf.scoping.ProtobufScopeProvider;
-import com.google.eclipse.protobuf.util.EResources;
 import com.google.inject.Inject;
 
 /**
@@ -41,8 +33,6 @@
 public class Issue147_AddSupportForGroupOptions_Test {
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
 
-  @Inject private EReference reference;
-  @Inject private ProtoDescriptorProvider descriptorProvider;
   @Inject private ProtobufScopeProvider scopeProvider;
 
   // syntax = "proto2";
@@ -54,12 +44,10 @@
   // }
   @Test public void should_provide_fields_for_native_option() {
     NativeFieldOption option = xtext.find("deprecated", NativeFieldOption.class);
-    IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
-    Group group = xtext.find("membership", Group.class);
-    ProtoDescriptor descriptor =
-        descriptorProvider.primaryDescriptor(EResources.getProjectOf(option.eResource()));
-    Collection<MessageField> optionSources = descriptor.availableOptionsFor(group);
-    assertThat(descriptionsIn(scope), containAll(optionSources));
+    IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(),
+        OPTION_SOURCE__TARGET);
+    assertThat(descriptionsIn(scope), contain("ctype", "packed", "deprecated",
+        "experimental_map_key"));
   }
 
   // syntax = "proto2";
@@ -79,10 +67,10 @@
   // }
   @Test public void should_provide_fields_for_custom_option() {
     CustomFieldOption option = xtext.find("code", ")", CustomFieldOption.class);
-    IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
-    assertThat(descriptionsIn(scope), containAll("code", "proto.code", "google.proto.code", "com.google.proto.code",
-                                                 ".com.google.proto.code",
-                                                 "info", "proto.info", "google.proto.info", "com.google.proto.info",
-                                                 ".com.google.proto.info"));
+    IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(),
+        OPTION_SOURCE__TARGET);
+    assertThat(descriptionsIn(scope), contain("code", "proto.code", "google.proto.code",
+        "com.google.proto.code", "info", "proto.info", "google.proto.info",
+        "com.google.proto.info"));
   }
 }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue156_AddSupportForEnumValueOptions_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue156_AddSupportForEnumValueOptions_Test.java
index 9afbe58..e10ba9f 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue156_AddSupportForEnumValueOptions_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue156_AddSupportForEnumValueOptions_Test.java
@@ -13,13 +13,12 @@
 import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
 import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
 import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-import static com.google.eclipse.protobuf.junit.matchers.ContainAllNames.containAll;
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_SOURCE__TARGET;
 
-import org.eclipse.emf.ecore.EReference;
 import org.eclipse.xtext.scoping.IScope;
 import org.junit.Rule;
 import org.junit.Test;
-
 import com.google.eclipse.protobuf.junit.core.XtextRule;
 import com.google.eclipse.protobuf.protobuf.CustomFieldOption;
 import com.google.eclipse.protobuf.scoping.ProtobufScopeProvider;
@@ -33,7 +32,6 @@
 public class Issue156_AddSupportForEnumValueOptions_Test {
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
 
-  @Inject private EReference reference;
   @Inject private ProtobufScopeProvider scopeProvider;
 
   // syntax = "proto2";
@@ -49,7 +47,7 @@
   // }
   @Test public void should_provide_fields_for_custom_field_option() {
     CustomFieldOption option = xtext.find("active", ")", CustomFieldOption.class);
-    IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
-    assertThat(descriptionsIn(scope), containAll("active", ".active"));
+    IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET);
+    assertThat(descriptionsIn(scope), contain("active"));
   }
 }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java
index f66915f..4668d91 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014 Google Inc.
+ * Copyright (c) 2014 Google Inc.
  *
  * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
  * Public License v1.0 which accompanies this distribution, and is available at
@@ -12,12 +12,17 @@
 import static org.eclipse.xtext.util.Strings.isEmpty;
 
 import com.google.eclipse.protobuf.model.util.Imports;
+import com.google.eclipse.protobuf.preferences.general.PreferenceNames;
 import com.google.eclipse.protobuf.protobuf.Import;
 import com.google.eclipse.protobuf.scoping.IImportResolver;
+import com.google.eclipse.protobuf.scoping.IUriResolver;
 import com.google.inject.Inject;
 
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.EReference;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
+import org.mockito.Mockito;
 
 import java.io.File;
 
@@ -31,16 +36,32 @@
     return new IntegrationTestModule();
   }
 
-  private IntegrationTestModule( ) {}
+  private IntegrationTestModule() {}
 
-  @Override protected void configure() {
+  @Override
+  protected void configure() {
     binder().bind(IImportResolver.class).to(ImportResolver.class);
     mockAndBind(EReference.class);
+    IPreferenceStoreAccess mockStoreAccess = Mockito.mock(IPreferenceStoreAccess.class);
+    IUriResolver mockUriResolver = Mockito.mock(IUriResolver.class);
+    binder().bind(IPreferenceStoreAccess.class).toInstance(mockStoreAccess);
+    // TODO (atrookey) Get rid of the excessive mocking.
+    binder().bind(IUriResolver.class).toInstance(mockUriResolver);
+    IPreferenceStore mockPreferenceStore = Mockito.mock(IPreferenceStore.class);
+    Mockito.when(mockStoreAccess.getWritablePreferenceStore(Mockito.anyObject()))
+        .thenReturn(mockPreferenceStore);
+    Mockito.when(mockPreferenceStore.getString(PreferenceNames.DESCRIPTOR_PROTO_PATH))
+        .thenReturn(PreferenceNames.DEFAULT_DESCRIPTOR_PATH);
+    Mockito.when(
+            mockUriResolver.resolveUri(
+                Mockito.eq(PreferenceNames.DEFAULT_DESCRIPTOR_PATH),
+                Mockito.anyObject(),
+                Mockito.anyObject()))
+        .thenReturn("platform:/plugin/com.google.eclipse.protobuf/descriptor.proto");
   }
 
   private static class ImportResolver implements IImportResolver {
-    @Inject
-    private Imports imports;
+    @Inject private Imports imports;
 
     @Override
     public String resolve(Import anImport) {
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_ComplexTypeLink_target_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_ComplexTypeLink_target_Test.java
index 47e5fb1..80bde8e 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_ComplexTypeLink_target_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_ComplexTypeLink_target_Test.java
@@ -8,6 +8,7 @@
  */
 package com.google.eclipse.protobuf.scoping;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.when;
 
@@ -17,14 +18,17 @@
 import static com.google.eclipse.protobuf.junit.matchers.ContainAllNames.containAll;
 import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.COMPLEX_TYPE;
 import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.COMPLEX_TYPE_LINK;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.COMPLEX_TYPE_LINK__TARGET;
 
 import org.eclipse.emf.ecore.EReference;
+import org.eclipse.xtext.naming.QualifiedName;
 import org.eclipse.xtext.scoping.IScope;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
 import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.protobuf.ComplexType;
 import com.google.eclipse.protobuf.protobuf.ComplexTypeLink;
 import com.google.eclipse.protobuf.protobuf.MessageField;
 import com.google.inject.Inject;
@@ -67,12 +71,10 @@
   @Test public void should_provide_Types() {
     MessageField field = xtext.find("type", MessageField.class);
     IScope scope = scopeProvider.getScope(typeOf(field), reference);
-    assertThat(descriptionsIn(scope), containAll("Type", "proto.Type", "google.proto.Type", "com.google.proto.Type",
-                                                 ".com.google.proto.Type",
-                                                 "Address", "proto.Address", "google.proto.Address",
-                                                 "com.google.proto.Address", ".com.google.proto.Address",
-                                                 "Contact", "proto.Contact", "google.proto.Contact",
-                                                 "com.google.proto.Contact", ".com.google.proto.Contact"));
+    assertThat(descriptionsIn(scope), containAll("Type", "proto.Type", "google.proto.Type",
+        "com.google.proto.Type", "Address", "proto.Address", "google.proto.Address",
+        "com.google.proto.Address", "Contact", "proto.Contact", "google.proto.Contact",
+        "com.google.proto.Contact"));
   }
 
   // // Create file types.proto
@@ -103,10 +105,8 @@
   @Test public void should_provide_imported_Types() {
     MessageField field = xtext.find("type", " =", MessageField.class);
     IScope scope = scopeProvider.getScope(typeOf(field), reference);
-    assertThat(descriptionsIn(scope), containAll("test.proto.Type", ".test.proto.Type",
-                                                 "test.proto.Address", ".test.proto.Address",
-                                                 "Contact", "proto.Contact", "google.proto.Contact",
-                                                 "com.google.proto.Contact", ".com.google.proto.Contact"));
+    assertThat(descriptionsIn(scope), containAll("test.proto.Type", "test.proto.Address",
+        "Contact", "proto.Contact", "google.proto.Contact", "com.google.proto.Contact"));
   }
 
   // // Create file types.proto
@@ -137,12 +137,10 @@
   @Test public void should_provide_imported_Types_with_equal_package() {
     MessageField field = xtext.find("type", " =", MessageField.class);
     IScope scope = scopeProvider.getScope(typeOf(field), reference);
-    assertThat(descriptionsIn(scope), containAll("Type", "proto.Type", "google.proto.Type", "com.google.proto.Type",
-                                                 ".com.google.proto.Type",
-                                                 "Address", "proto.Address", "google.proto.Address",
-                                                 "com.google.proto.Address", ".com.google.proto.Address",
-                                                 "Contact", "proto.Contact", "google.proto.Contact",
-                                                 "com.google.proto.Contact", ".com.google.proto.Contact"));
+    assertThat(descriptionsIn(scope), containAll("Type", "proto.Type", "google.proto.Type",
+        "com.google.proto.Type", "Address", "proto.Address", "google.proto.Address",
+        "com.google.proto.Address", "Contact", "proto.Contact", "google.proto.Contact",
+        "com.google.proto.Contact"));
   }
 
   // // Create file types.proto
@@ -173,10 +171,8 @@
   @Test public void should_provide_public_imported_Types() {
     MessageField field = xtext.find("type", " =", MessageField.class);
     IScope scope = scopeProvider.getScope(typeOf(field), reference);
-    assertThat(descriptionsIn(scope), containAll("test.proto.Type", ".test.proto.Type",
-                                                 "test.proto.Address", ".test.proto.Address",
-                                                 "Contact", "proto.Contact", "google.proto.Contact",
-                                                 "com.google.proto.Contact", ".com.google.proto.Contact"));
+    assertThat(descriptionsIn(scope), containAll("test.proto.Type", "test.proto.Address",
+        "Contact", "proto.Contact", "google.proto.Contact", "com.google.proto.Contact"));
   }
 
   // // Create file public-types.proto
@@ -214,10 +210,38 @@
   @Test public void should_provide_public_imported_Types_with_more_than_one_level() {
     MessageField field = xtext.find("type", " =", MessageField.class);
     IScope scope = scopeProvider.getScope(typeOf(field), reference);
-    assertThat(descriptionsIn(scope), containAll("test.proto.Type", ".test.proto.Type",
-                                                 "test.proto.Address", ".test.proto.Address",
-                                                 "Contact", "proto.Contact", "google.proto.Contact",
-                                                 "com.google.proto.Contact", ".com.google.proto.Contact"));
+    assertThat(descriptionsIn(scope), containAll("test.proto.Type", "test.proto.Address",
+        "Contact", "proto.Contact", "google.proto.Contact", "com.google.proto.Contact"));
+  }
+
+  // // Create file sample_proto.proto
+  // syntax = "proto2";
+  // package sample.proto;
+  //
+  //  enum Fruit {
+  //    GRAPE =  1;
+  //  }
+
+  //  syntax = "proto2";
+  //  package sample.proto.foo;
+  //  import "sample_proto.proto";
+  //
+  //  enum Fruit {
+  //     GRAPE =  1;
+  //  }
+  //
+  //  message Fruits {
+  //   optional Fruit grape = 1;
+  // }
+  @Test public void should_provide_nearest_ComplexType() {
+    MessageField field = xtext.find("grape", " =", MessageField.class);
+    ComplexTypeLink link = (ComplexTypeLink) field.getType();
+    ComplexType scopedEnum = link.getTarget();  // Enum inherits from ComplexType
+    IScope scope = scopeProvider.getScope(field, COMPLEX_TYPE_LINK__TARGET);
+    Object expectedEnum =
+        scope.getSingleElement(
+            QualifiedName.create("sample", "proto", "foo", "Fruit")).getEObjectOrProxy();
+    assertEquals(expectedEnum, scopedEnum);
   }
 
   private static ComplexTypeLink typeOf(MessageField field) {
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_ExtensionFieldName_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_ExtensionFieldName_Test.java
index 58c3c53..f3fcaa3 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_ExtensionFieldName_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_ExtensionFieldName_Test.java
@@ -13,7 +13,8 @@
 import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
 import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
 import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-import static com.google.eclipse.protobuf.junit.matchers.ContainAllNames.containAll;
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.FIELD_NAME__TARGET;
 
 import org.eclipse.emf.ecore.EReference;
 import org.eclipse.xtext.scoping.IScope;
@@ -21,6 +22,8 @@
 import org.junit.Test;
 
 import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.protobuf.ComplexValue;
+import com.google.eclipse.protobuf.protobuf.ComplexValueField;
 import com.google.eclipse.protobuf.protobuf.ExtensionFieldName;
 import com.google.eclipse.protobuf.protobuf.FieldName;
 import com.google.eclipse.protobuf.protobuf.ValueField;
@@ -34,7 +37,6 @@
 public class ProtobufScopeProvider_scope_FieldName_target_with_ExtensionFieldName_Test {
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
 
-  @Inject private EReference reference;
   @Inject private ProtobufScopeProvider scopeProvider;
 
   // syntax = "proto2";
@@ -60,9 +62,11 @@
   //   }
   // };
   @Test public void should_provide_sources_for_aggregate_field() {
-    ValueField field = xtext.find("google.proto.test.fileopt", "]", ValueField.class);
+    ComplexValueField valueField = xtext.find("file", " {", ComplexValueField.class);
+    ComplexValue value = valueField.getValues().get(0);
+    ValueField field = value.getFields().get(0);
     ExtensionFieldName name = (ExtensionFieldName) field.getName();
-    IScope scope = scopeProvider.scope_FieldName_target(name, reference);
-    assertThat(descriptionsIn(scope), containAll("google.proto.test.fileopt", ".google.proto.test.fileopt"));
+    IScope scope = scopeProvider.getScope(name, FIELD_NAME__TARGET);
+    assertThat(descriptionsIn(scope), contain("google.proto.test.fileopt"));
   }
 }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_NormalFieldName_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_NormalFieldName_Test.java
index f52b4c1..538d71a 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_NormalFieldName_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_NormalFieldName_Test.java
@@ -9,11 +9,12 @@
 package com.google.eclipse.protobuf.scoping;
 
 import static org.junit.Assert.assertThat;
-
 import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
 import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
 import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
 import static com.google.eclipse.protobuf.junit.matchers.ContainAllFieldsInMessage.containAllFieldsIn;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.FIELD_NAME__TARGET;
 
 import org.eclipse.emf.ecore.EReference;
 import org.eclipse.xtext.scoping.IScope;
@@ -28,7 +29,7 @@
 import com.google.inject.Inject;
 
 /**
- * Tests for <code>{@link ProtobufScopeProvider#scope_FieldName_target(FieldName, EReference)}</code>
+ * Tests for <code>{@link ProtobufScopeProvider#getScope(FieldName, EReference)}</code>
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
@@ -56,9 +57,8 @@
   @Test public void should_provide_sources_for_aggregate_field() {
     ValueField field = xtext.find("code", ":", ValueField.class);
     NormalFieldName name = (NormalFieldName) field.getName();
-    IScope scope = scopeProvider.scope_FieldName_target(name, reference);
-    Message message = xtext.find("Type", " {", Message.class);
-    assertThat(descriptionsIn(scope), containAllFieldsIn(message));
+    IScope scope = scopeProvider.getScope(name, FIELD_NAME__TARGET);
+    assertThat(descriptionsIn(scope), contain("code"));
   }
 
   // syntax = "proto2";
@@ -84,9 +84,9 @@
   @Test public void should_provide_sources_for_nested_field_notation_in_option() {
     ValueField field = xtext.find("value", ":", ValueField.class);
     NormalFieldName name = (NormalFieldName) field.getName();
-    IScope scope = scopeProvider.scope_FieldName_target(name, reference);
+    IScope scope = scopeProvider.getScope(name, FIELD_NAME__TARGET);
     Message message = xtext.find("Names", " {", Message.class);
-    assertThat(descriptionsIn(scope), containAllFieldsIn(message));
+    assertThat(descriptionsIn(scope), contain("value"));
   }
 
   // syntax = "proto2";
@@ -107,9 +107,8 @@
   @Test public void should_provide_sources_for_field_notation_in_field_option() {
     ValueField field = xtext.find("code", ":", ValueField.class);
     NormalFieldName name = (NormalFieldName) field.getName();
-    IScope scope = scopeProvider.scope_FieldName_target(name, reference);
-    Message message = xtext.find("Type", " {", Message.class);
-    assertThat(descriptionsIn(scope), containAllFieldsIn(message));
+    IScope scope = scopeProvider.getScope(name, FIELD_NAME__TARGET);
+    assertThat(descriptionsIn(scope), contain("code"));
   }
 
   // syntax = "proto2";
@@ -135,8 +134,8 @@
   @Test public void should_provide_sources_for_nested_field_notation_in_field_option() {
     ValueField field = xtext.find("value", ":", ValueField.class);
     NormalFieldName name = (NormalFieldName) field.getName();
-    IScope scope = scopeProvider.scope_FieldName_target(name, reference);
+    IScope scope = scopeProvider.getScope(name, FIELD_NAME__TARGET);
     Message message = xtext.find("Names", " {", Message.class);
-    assertThat(descriptionsIn(scope), containAllFieldsIn(message));
+    assertThat(descriptionsIn(scope), contain("value"));
   }
 }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_LiteralLink_target_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_LiteralLink_target_Test.java
index 1c13b6f..aad3151 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_LiteralLink_target_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_LiteralLink_target_Test.java
@@ -9,10 +9,12 @@
 package com.google.eclipse.protobuf.scoping;
 
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertEquals;
 import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
 import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
 import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-import static com.google.eclipse.protobuf.junit.matchers.ContainAllLiteralsInEnum.containAllLiteralsIn;
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.LITERAL_LINK__TARGET;
 
 import org.eclipse.emf.ecore.EReference;
 import org.eclipse.xtext.scoping.IScope;
@@ -20,23 +22,23 @@
 import org.junit.Test;
 
 import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.protobuf.DefaultValueFieldOption;
 import com.google.eclipse.protobuf.protobuf.Enum;
 import com.google.eclipse.protobuf.protobuf.FieldOption;
+import com.google.eclipse.protobuf.protobuf.Literal;
 import com.google.eclipse.protobuf.protobuf.LiteralLink;
+import com.google.eclipse.protobuf.protobuf.MessageField;
 import com.google.eclipse.protobuf.protobuf.Option;
-import com.google.eclipse.protobuf.util.EResources;
 import com.google.inject.Inject;
 
 /**
- * Tests for <code>{@link ProtobufScopeProvider#scope_LiteralLink_target(LiteralLink, EReference)}</code>.
+ * Tests for <code>{@link ProtobufScopeProvider#getScope(LiteralLink, EReference)}</code>.
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
 public class ProtobufScopeProvider_scope_LiteralLink_target_Test {
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
 
-  @Inject private EReference reference;
-  @Inject private ProtoDescriptorProvider descriptorProvider;
   @Inject private ProtobufScopeProvider scopeProvider;
 
   // syntax = "proto2";
@@ -50,10 +52,10 @@
   //   optional Type type = 1 [default = ONE];
   // }
   @Test public void should_provide_Literals_for_default_value() {
-    FieldOption option = xtext.find("default", FieldOption.class);
-    IScope scope = scopeProvider.scope_LiteralLink_target(valueOf(option), reference);
-    Enum typeEnum = xtext.find("Type", " {", Enum.class);
-    assertThat(descriptionsIn(scope), containAllLiteralsIn(typeEnum));
+    MessageField field = xtext.find("type", " =", MessageField.class);
+    FieldOption option = field.getFieldOptions().get(0);
+    IScope scope = scopeProvider.getScope(valueOf(option), LITERAL_LINK__TARGET);
+    assertThat(descriptionsIn(scope), contain("ONE", "TWO"));
   }
 
   // syntax = "proto2";
@@ -61,10 +63,8 @@
   // option optimize_for = SPEED;
   @Test public void should_provide_Literals_for_native_option() {
     Option option = xtext.find("optimize_for", Option.class);
-    IScope scope = scopeProvider.scope_LiteralLink_target(valueOf(option), reference);
-    Enum optimizeModeEnum = descriptorProvider.primaryDescriptor(
-        EResources.getProjectOf(xtext.root().eResource())).enumByName("OptimizeMode");
-    assertThat(descriptionsIn(scope), containAllLiteralsIn(optimizeModeEnum));
+    IScope scope = scopeProvider.getScope(valueOf(option), LITERAL_LINK__TARGET);
+    assertThat(descriptionsIn(scope), contain("SPEED", "CODE_SIZE", "LITE_RUNTIME"));
   }
 
   // syntax = "proto2";
@@ -83,9 +83,8 @@
   // option (type) = ONE;
   @Test public void should_provide_Literals_for_source_of_custom_option() {
     Option option = xtext.find("type", ")", Option.class);
-    IScope scope = scopeProvider.scope_LiteralLink_target(valueOf(option), reference);
-    Enum typeEnum = xtext.find("Type", " {", Enum.class);
-    assertThat(descriptionsIn(scope), containAllLiteralsIn(typeEnum));
+    IScope scope = scopeProvider.getScope(valueOf(option), LITERAL_LINK__TARGET);
+    assertThat(descriptionsIn(scope), contain("ONE", "TWO"));
   }
 
   // syntax = "proto2";
@@ -108,9 +107,9 @@
   // option (info).type = ONE;
   @Test public void should_provide_Literals_for_source_of_field_of_custom_option() {
     Option option = xtext.find("info", ")", Option.class);
-    IScope scope = scopeProvider.scope_LiteralLink_target(valueOf(option), reference);
-    Enum typeEnum = xtext.find("Type", " {", Enum.class);
-    assertThat(descriptionsIn(scope), containAllLiteralsIn(typeEnum));
+    IScope scope = scopeProvider.getScope(valueOf(option), LITERAL_LINK__TARGET);
+    xtext.find("Type", " {", Enum.class);
+    assertThat(descriptionsIn(scope), contain("ONE", "TWO"));
   }
 
   private static LiteralLink valueOf(Option option) {
@@ -124,11 +123,8 @@
   //  }
   @Test public void should_provide_Literals_for_source_of_native_field_option() {
     FieldOption option = xtext.find("ctype", FieldOption.class);
-    IScope scope = scopeProvider.scope_LiteralLink_target(valueOf(option), reference);
-    ProtoDescriptor descriptor =
-        descriptorProvider.primaryDescriptor(EResources.getProjectOf(xtext.resource()));
-    Enum cTypeEnum = descriptor.enumByName("CType");
-    assertThat(descriptionsIn(scope), containAllLiteralsIn(cTypeEnum));
+    IScope scope = scopeProvider.getScope(valueOf(option), LITERAL_LINK__TARGET);
+    assertThat(descriptionsIn(scope), contain("STRING", "CORD", "STRING_PIECE"));
   }
 
   // syntax = "proto2";
@@ -149,9 +145,9 @@
   // }
   @Test public void should_provide_Literals_for_source_of_custom_field_option() {
     FieldOption option = xtext.find("type", ")", FieldOption.class);
-    IScope scope = scopeProvider.scope_LiteralLink_target(valueOf(option), reference);
-    Enum typeEnum = xtext.find("Type", " {", Enum.class);
-    assertThat(descriptionsIn(scope), containAllLiteralsIn(typeEnum));
+    IScope scope = scopeProvider.getScope(valueOf(option), LITERAL_LINK__TARGET);
+    xtext.find("Type", " {", Enum.class);
+    assertThat(descriptionsIn(scope), contain("ONE", "TWO"));
   }
 
   // syntax = "proto2";
@@ -176,9 +172,43 @@
   // }
   @Test public void should_provide_Literals_for_source_of_field_in_custom_field_option() {
     FieldOption option = xtext.find("info", ")", FieldOption.class);
-    IScope scope = scopeProvider.scope_LiteralLink_target(valueOf(option), reference);
-    Enum typeEnum = xtext.find("Type", " {", Enum.class);
-    assertThat(descriptionsIn(scope), containAllLiteralsIn(typeEnum));
+    IScope scope = scopeProvider.getScope(valueOf(option), LITERAL_LINK__TARGET);
+    xtext.find("Type", " {", Enum.class);
+    assertThat(descriptionsIn(scope), contain("ONE", "TWO"));
+  }
+
+  // // Create file sample_proto.proto
+  // syntax = "proto2";
+  // package sample.proto;
+  //
+  // message Example {
+  //   enum Alpha {
+  //     BAR =  1;
+  //   }
+  // }
+
+  //  syntax = "proto2";
+  //  package example.proto;
+  //  import "sample_proto.proto";
+  //
+  // message Fruits {
+  //  enum Beta {
+  //     BAR =  1;
+  //  }
+  //   optional Grape grape = 4 [default = BAR];
+  // }
+  @Test public void should_provide_Literal_nearest_to_option() {
+    // Beta
+    MessageField field = xtext.find("grape", " =", MessageField.class);
+    DefaultValueFieldOption option = (DefaultValueFieldOption) field.getFieldOptions().get(0);
+    LiteralLink link = (LiteralLink) option.getValue();
+    Enum enumBeta = (Enum)link.getTarget().eContainer();
+
+    // Alpha
+    Enum beta = xtext.find("Beta", Enum.class);
+    Literal literal = (Literal) beta.getElements().get(0);
+    Enum expectedEnum = (Enum) literal.eContainer();
+    assertEquals(expectedEnum, enumBeta);
   }
 
   private static LiteralLink valueOf(FieldOption option) {
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_ExtensionOptionField_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_ExtensionOptionField_Test.java
index d883338..4dc1113 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_ExtensionOptionField_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_ExtensionOptionField_Test.java
@@ -9,11 +9,11 @@
 package com.google.eclipse.protobuf.scoping;
 
 import static org.junit.Assert.assertThat;
-
 import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
 import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
 import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-import static com.google.eclipse.protobuf.junit.matchers.ContainAllNames.containAll;
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_SOURCE__TARGET;
 
 import org.eclipse.emf.ecore.EReference;
 import org.eclipse.xtext.scoping.IScope;
@@ -28,14 +28,13 @@
 import com.google.inject.Inject;
 
 /**
- * Tests for <code>{@link ProtobufScopeProvider#scope_OptionField_target(OptionField, EReference)}</code>.
+ * Tests for <code>{@link ProtobufScopeProvider#getScope(OptionField, EReference)}</code>.
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
 public class ProtobufScopeProvider_scope_OptionField_target_with_ExtensionOptionField_Test {
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
 
-  @Inject private EReference reference;
   @Inject private ProtobufScopeProvider scopeProvider;
 
   // syntax = "proto2";
@@ -62,8 +61,8 @@
   @Test public void should_provide_extend_message_fields_for_first_field_in_custom_option() {
     CustomOption option = xtext.find("type", ")", CustomOption.class);
     ExtensionOptionField codeOptionField = (ExtensionOptionField) option.getFields().get(0);
-    IScope scope = scopeProvider.scope_OptionField_target(codeOptionField, reference);
-    assertThat(descriptionsIn(scope), containAll("active", "com.google.proto.active", ".com.google.proto.active"));
+    IScope scope = scopeProvider.getScope(codeOptionField, OPTION_SOURCE__TARGET);
+    assertThat(descriptionsIn(scope), contain("active", "com.google.proto.active"));
   }
 
   // syntax = "proto2";
@@ -92,7 +91,7 @@
   @Test public void should_provide_message_fields_for_first_field_in_field_custom_option() {
     CustomFieldOption option = xtext.find("type", ")", CustomFieldOption.class);
     ExtensionOptionField codeOptionField = (ExtensionOptionField) option.getFields().get(0);
-    IScope scope = scopeProvider.scope_OptionField_target(codeOptionField, reference);
-    assertThat(descriptionsIn(scope), containAll("active", "com.google.proto.active", ".com.google.proto.active"));
+    IScope scope = scopeProvider.getScope(codeOptionField, OPTION_SOURCE__TARGET);
+    assertThat(descriptionsIn(scope), contain("active", "com.google.proto.active"));
   }
 }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_MessageOptionField_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_MessageOptionField_Test.java
index f46338f..ee5ad96 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_MessageOptionField_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_MessageOptionField_Test.java
@@ -9,14 +9,14 @@
 package com.google.eclipse.protobuf.scoping;
 
 import static org.junit.Assert.assertThat;
-
 import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
 import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
 import static com.google.eclipse.protobuf.junit.core.SearchOption.IGNORE_CASE;
 import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-import static com.google.eclipse.protobuf.junit.matchers.ContainAllFieldsInMessage.containAllFieldsIn;
-import static com.google.eclipse.protobuf.junit.matchers.ContainAllNames.containAll;
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_FIELD__TARGET;
 
+import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.ecore.EReference;
 import org.eclipse.xtext.scoping.IScope;
 import org.junit.Rule;
@@ -25,21 +25,20 @@
 import com.google.eclipse.protobuf.junit.core.XtextRule;
 import com.google.eclipse.protobuf.protobuf.CustomFieldOption;
 import com.google.eclipse.protobuf.protobuf.CustomOption;
-import com.google.eclipse.protobuf.protobuf.Group;
-import com.google.eclipse.protobuf.protobuf.Message;
+import com.google.eclipse.protobuf.protobuf.FieldOption;
+import com.google.eclipse.protobuf.protobuf.MessageField;
 import com.google.eclipse.protobuf.protobuf.MessageOptionField;
 import com.google.eclipse.protobuf.protobuf.OptionField;
 import com.google.inject.Inject;
 
 /**
- * Tests for <code>{@link ProtobufScopeProvider#scope_OptionField_target(OptionField, EReference)}</code>.
+ * Tests for <code>{@link ProtobufScopeProvider#getScope(OptionField, EReference)}</code>.
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
 public class ProtobufScopeProvider_scope_OptionField_target_with_MessageOptionField_Test {
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
 
-  @Inject private EReference reference;
   @Inject private ProtobufScopeProvider scopeProvider;
 
   // syntax = "proto2";
@@ -59,9 +58,8 @@
   @Test public void should_provide_message_fields_for_first_field_in_custom_option() {
     CustomOption option = xtext.find("type", ")", CustomOption.class);
     MessageOptionField codeOptionField = (MessageOptionField) option.getFields().get(0);
-    IScope scope = scopeProvider.scope_OptionField_target(codeOptionField, reference);
-    Message typeMessage = xtext.find("Type", " {", Message.class);
-    assertThat(descriptionsIn(scope), containAllFieldsIn(typeMessage));
+    IScope scope = scopeProvider.getScope(codeOptionField, OPTION_FIELD__TARGET);
+    assertThat(descriptionsIn(scope), contain("code", "name"));
   }
 
   // syntax = "proto2";
@@ -84,10 +82,9 @@
   // option (type).code.number = 68;
   @Test public void should_provide_message_fields_for_field_in_custom_option() {
     CustomOption option = xtext.find("type", ")", CustomOption.class);
-    MessageOptionField numberOptionField = (MessageOptionField) option.getFields().get(1);
-    IScope scope = scopeProvider.scope_OptionField_target(numberOptionField, reference);
-    Message codeMessage = xtext.find("Code", " {", Message.class);
-    assertThat(descriptionsIn(scope), containAllFieldsIn(codeMessage));
+    OptionField numberOptionField = (MessageOptionField) option.getFields().get(1);
+    IScope scope = scopeProvider.getScope(numberOptionField, OPTION_FIELD__TARGET);
+    assertThat(descriptionsIn(scope), contain("number"));
   }
 
   // syntax = "proto2";
@@ -105,9 +102,8 @@
   @Test public void should_provide_group_fields_for_first_field_in_custom_option() {
     CustomOption option = xtext.find("type", ")", CustomOption.class, IGNORE_CASE);
     MessageOptionField codeOptionField = (MessageOptionField) option.getFields().get(0);
-    IScope scope = scopeProvider.scope_OptionField_target(codeOptionField, reference);
-    Group groupMessage = xtext.find("Type", " =", Group.class);
-    assertThat(descriptionsIn(scope), containAllFieldsIn(groupMessage));
+    IScope scope = scopeProvider.getScope(codeOptionField, OPTION_FIELD__TARGET);
+    assertThat(descriptionsIn(scope), contain("code", "name"));
   }
 
   // syntax = "proto2";
@@ -129,9 +125,8 @@
   @Test public void should_provide_message_fields_for_first_field_in_field_custom_option() {
     CustomFieldOption option = xtext.find("type", ")", CustomFieldOption.class);
     MessageOptionField codeOptionField = (MessageOptionField) option.getFields().get(0);
-    IScope scope = scopeProvider.scope_OptionField_target(codeOptionField, reference);
-    Message typeMessage = xtext.find("Type", " {", Message.class);
-    assertThat(descriptionsIn(scope), containAllFieldsIn(typeMessage));
+    IScope scope = scopeProvider.getScope(codeOptionField, OPTION_FIELD__TARGET);
+    assertThat(descriptionsIn(scope), contain("code", "name"));
   }
 
   // syntax = "proto2";
@@ -157,9 +152,8 @@
   @Test public void should_provide_message_fields_for_field_in_field_custom_option() {
     CustomFieldOption option = xtext.find("type", ")", CustomFieldOption.class);
     MessageOptionField numberOptionField = (MessageOptionField) option.getFields().get(1);
-    IScope scope = scopeProvider.scope_OptionField_target(numberOptionField, reference);
-    Message codeMessage = xtext.find("Code", " {", Message.class);
-    assertThat(descriptionsIn(scope), containAllFieldsIn(codeMessage));
+    IScope scope = scopeProvider.getScope(numberOptionField, OPTION_FIELD__TARGET);
+    assertThat(descriptionsIn(scope), contain("number"));
   }
 
   // syntax = "proto2";
@@ -177,11 +171,12 @@
   //   optional boolean active = 1 [(type).code = 68];
   // }
   @Test public void should_provide_group_fields_for_first_field_in_field_custom_option() {
-    CustomFieldOption option = xtext.find("type", ")", CustomFieldOption.class, IGNORE_CASE);
-    MessageOptionField codeOptionField = (MessageOptionField) option.getFields().get(0);
-    IScope scope = scopeProvider.scope_OptionField_target(codeOptionField, reference);
-    Group groupMessage = xtext.find("Type", " =", Group.class);
-    assertThat(descriptionsIn(scope), containAllFieldsIn(groupMessage));
+    MessageField messageField = xtext.find("active", MessageField.class);
+    EList<FieldOption> fieldOptions = messageField.getFieldOptions();
+    CustomFieldOption customFieldOption = (CustomFieldOption) fieldOptions.get(0);
+    OptionField codeOptionField = customFieldOption.getFields().get(0);
+    IScope scope = scopeProvider.getScope(codeOptionField, OPTION_FIELD__TARGET);
+    assertThat(descriptionsIn(scope), contain("code", "name"));
   }
 
   // syntax = "proto2";
@@ -208,7 +203,39 @@
   @Test public void should_provide_field_option_from_oneof() {
     CustomFieldOption option = xtext.find("type", ")", CustomFieldOption.class);
     MessageOptionField codeOptionField = (MessageOptionField) option.getFields().get(0);
-    IScope scope = scopeProvider.scope_OptionField_target(codeOptionField, reference);
-    assertThat(descriptionsIn(scope), containAll("foo", "bar", "baz"));
+    IScope scope = scopeProvider.getScope(codeOptionField, OPTION_FIELD__TARGET);
+    assertThat(descriptionsIn(scope), contain("foo", "bar", "baz"));
+  }
+
+  // syntax = "proto2";
+  //
+  // package com.google.proto;
+  //
+  // import 'google/protobuf/descriptor.proto';
+  //
+  // message Type {
+  //   bool foo = 1;
+  //   oneof choose_one {
+  //     bool bar = 2;
+  //     bool baz = 3;
+  //   }
+  //   oneof choose_another {
+  //     bool qux = 4;
+  //     bool quux = 5;
+  //   }
+  // }
+  //
+  // extend google.protobuf.FieldOptions {
+  //   optional Type type = 1000;
+  // }
+  //
+  // message Person {
+  //   optional bool active = 1 [(type).bar = true];
+  // }
+  @Test public void should_provide_field_option_from_multiple_oneof() {
+    CustomFieldOption option = xtext.find("type", ")", CustomFieldOption.class);
+    MessageOptionField codeOptionField = (MessageOptionField) option.getFields().get(0);
+    IScope scope = scopeProvider.getScope(codeOptionField, OPTION_FIELD__TARGET);
+    assertThat(descriptionsIn(scope), contain("foo", "bar", "baz", "qux", "quux"));
   }
 }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionSource_target_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionSource_target_Test.java
index 8b30f48..175a43a 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionSource_target_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionSource_target_Test.java
@@ -12,37 +12,27 @@
 import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
 import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
 import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-import static com.google.eclipse.protobuf.junit.matchers.ContainAllFields.containAll;
-import static com.google.eclipse.protobuf.junit.matchers.ContainAllNames.containAll;
-import static com.google.eclipse.protobuf.scoping.OptionType.FIELD;
-import static com.google.eclipse.protobuf.scoping.OptionType.FILE;
-
-import java.util.Collection;
-
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_SOURCE__TARGET;
 import org.eclipse.emf.ecore.EReference;
 import org.eclipse.xtext.scoping.IScope;
 import org.junit.Rule;
 import org.junit.Test;
-
 import com.google.eclipse.protobuf.junit.core.XtextRule;
 import com.google.eclipse.protobuf.protobuf.CustomFieldOption;
-import com.google.eclipse.protobuf.protobuf.MessageField;
 import com.google.eclipse.protobuf.protobuf.NativeFieldOption;
 import com.google.eclipse.protobuf.protobuf.Option;
 import com.google.eclipse.protobuf.protobuf.OptionSource;
-import com.google.eclipse.protobuf.util.EResources;
 import com.google.inject.Inject;
 
 /**
- * Tests for <code>{@link ProtobufScopeProvider#scope_OptionSource_target(OptionSource, EReference)}</code>
+ * Tests for <code>{@link ProtobufScopeProvider#getScope(OptionSource, EReference)}</code>
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
 public class ProtobufScopeProvider_scope_OptionSource_target_Test {
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
 
-  @Inject private EReference reference;
-  @Inject private ProtoDescriptorProvider descriptorProvider;
   @Inject private ProtobufScopeProvider scopeProvider;
 
   // syntax = "proto2";
@@ -50,11 +40,10 @@
   // option optimize_for = SPEED;
   @Test public void should_provide_sources_for_native_option() {
     Option option = xtext.find("optimize_for", Option.class);
-    IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
-    ProtoDescriptor descriptor =
-        descriptorProvider.primaryDescriptor(EResources.getProjectOf(xtext.resource()));
-    Collection<MessageField> optionSources = descriptor.optionsOfType(FILE);
-    assertThat(descriptionsIn(scope), containAll(optionSources));
+    IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET);
+    assertThat(descriptionsIn(scope), contain("java_package", "java_outer_classname",
+        "java_multiple_files", "java_generate_equals_and_hash", "optimize_for",
+        "cc_generic_services", "java_generic_services", "py_generic_services"));
   }
 
   // syntax = "proto2";
@@ -64,11 +53,9 @@
   // }
   @Test public void should_provide_sources_for_native_field_option() {
     NativeFieldOption option = xtext.find("ctype", NativeFieldOption.class);
-    IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
-    ProtoDescriptor descriptor =
-        descriptorProvider.primaryDescriptor(EResources.getProjectOf(xtext.resource()));
-    Collection<MessageField> optionSources = descriptor.optionsOfType(FIELD);
-    assertThat(descriptionsIn(scope), containAll(optionSources));
+    IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET);
+    assertThat(descriptionsIn(scope), contain("ctype", "packed", "deprecated",
+        "experimental_map_key"));
   }
 
   // syntax = "proto2";
@@ -84,11 +71,10 @@
   // option (code) = 68;
   @Test public void should_provide_sources_for_custom_option() {
     Option option = xtext.find("code", ")", Option.class);
-    IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
-    assertThat(descriptionsIn(scope), containAll("code", "proto.code", "google.proto.code", "com.google.proto.code",
-                                                 ".com.google.proto.code",
-                                                 "info", "proto.info", "google.proto.info", "com.google.proto.info",
-                                                 ".com.google.proto.info"));
+    IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET);
+    assertThat(descriptionsIn(scope), contain("code", "proto.code", "google.proto.code",
+        "com.google.proto.code", "info", "proto.info", "google.proto.info",
+        "com.google.proto.info"));
   }
 
   // // Create file custom-options.proto
@@ -110,9 +96,8 @@
   // option (test.proto.code) = 68;
   @Test public void should_provide_imported_sources_for_custom_option() {
     Option option = xtext.find("code", ")", Option.class);
-    IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
-    assertThat(descriptionsIn(scope), containAll("test.proto.code", ".test.proto.code",
-                                                 "test.proto.info", ".test.proto.info"));
+    IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET);
+    assertThat(descriptionsIn(scope), contain("test.proto.code", "test.proto.info"));
   }
 
   // // Create file custom-options.proto
@@ -134,11 +119,10 @@
   // option (code) = 68;
   @Test public void should_provide_imported_sources_for_custom_option_with_equal_package() {
     Option option = xtext.find("code", ")", Option.class);
-    IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
-    assertThat(descriptionsIn(scope), containAll("code", "proto.code", "google.proto.code", "com.google.proto.code",
-                                                 ".com.google.proto.code",
-                                                 "info", "proto.info", "google.proto.info", "com.google.proto.info",
-                                                 ".com.google.proto.info"));
+    IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET);
+    assertThat(descriptionsIn(scope), contain("code", "proto.code", "google.proto.code",
+        "com.google.proto.code", "info", "proto.info", "google.proto.info",
+        "com.google.proto.info"));
   }
 
   // syntax = "proto2";
@@ -155,10 +139,9 @@
   // }
   @Test public void should_provide_sources_for_custom_field_option() {
     CustomFieldOption option = xtext.find("code", ")", CustomFieldOption.class);
-    IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
-    assertThat(descriptionsIn(scope), containAll("code", "proto.code", "google.proto.code", "com.google.proto.code",
-                                                 ".com.google.proto.code",
-                                                 "info", "proto.info", "google.proto.info", "com.google.proto.info",
-                                                 ".com.google.proto.info"));
+    IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET);
+    assertThat(descriptionsIn(scope), contain("code", "proto.code", "google.proto.code",
+        "com.google.proto.code", "info", "proto.info", "google.proto.info",
+        "com.google.proto.info"));
   }
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java
index 378fe86..d793075 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java
@@ -15,6 +15,7 @@
 import com.google.eclipse.protobuf.resource.FastXtextResourceSet;
 import com.google.eclipse.protobuf.resource.GlobalResourceServiceProvider;
 import com.google.eclipse.protobuf.scoping.ExtensionRegistryProvider;
+import com.google.eclipse.protobuf.scoping.ProtobufCaseInsensitivityHelper;
 import com.google.eclipse.protobuf.validation.ProtobufResourceValidator;
 import com.google.eclipse.protobuf.validation.ProtobufSyntaxErrorMessageProvider;
 import com.google.inject.Binder;
@@ -27,6 +28,7 @@
 import org.eclipse.xtext.resource.IGlobalServiceProvider;
 import org.eclipse.xtext.resource.XtextResource;
 import org.eclipse.xtext.resource.XtextResourceSet;
+import org.eclipse.xtext.scoping.ICaseInsensitivityHelper;
 import org.eclipse.xtext.validation.IResourceValidator;
 
 /**
@@ -42,6 +44,10 @@
     return ProtobufQualifiedNameConverter.class;
   }
 
+  public Class<? extends ICaseInsensitivityHelper> bindICaseInsensitivityHelper() {
+    return ProtobufCaseInsensitivityHelper.class;
+  }
+
   @Override
   public Class<? extends IQualifiedNameProvider> bindIQualifiedNameProvider() {
     return ProtobufQualifiedNameProvider.class;
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/QualifiedNames.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/QualifiedNames.java
index a7402ad..b7708de 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/QualifiedNames.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/QualifiedNames.java
@@ -8,8 +8,7 @@
  */
 package com.google.eclipse.protobuf.model.util;
 
-import static com.google.common.collect.Lists.newArrayList;
-
+import java.util.ArrayList;
 import java.util.List;
 
 import org.eclipse.xtext.naming.QualifiedName;
@@ -23,14 +22,23 @@
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
-@Singleton public class QualifiedNames {
+@Singleton
+public class QualifiedNames {
   @Inject private StringLists stringLists;
 
+  public static QualifiedName removeLeadingDot(QualifiedName name) {
+    String firstSegment = name.getFirstSegment();
+    if (firstSegment != null && firstSegment.isEmpty()) {
+      return name.skipFirst(1);
+    }
+    return name;
+  }
+
   public QualifiedName addLeadingDot(QualifiedName name) {
-    if (name.getFirstSegment().equals("")) {
+    if (name.getFirstSegment().isEmpty()) {
       return name;
     }
-    List<String> segments = newArrayList();
+    List<String> segments = new ArrayList<>();
     segments.addAll(name.getSegments());
     segments.add(0, "");
     return createQualifiedName(segments);
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufCaseInsensitivityHelper.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufCaseInsensitivityHelper.java
new file mode 100644
index 0000000..34027fd
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufCaseInsensitivityHelper.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.scoping;
+
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.xtext.scoping.impl.CaseInsensitivityHelper;
+
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_SOURCE__TARGET;
+
+/**
+ * Specify cases where scoping is case insensitive.
+ *
+ * @author atrookey@google.com (Alexander Rookey)
+ */
+public class ProtobufCaseInsensitivityHelper extends CaseInsensitivityHelper {
+  @Override
+  public boolean isIgnoreCase(EReference reference) {
+    if (reference.equals(OPTION_SOURCE__TARGET)) {
+      return true;
+    }
+    return super.isIgnoreCase(reference);
+  }
+}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportScope.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportScope.java
index fcc0476..15f9a2c 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportScope.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportScope.java
@@ -8,7 +8,7 @@
  */
 package com.google.eclipse.protobuf.scoping;
 
-import static java.util.Collections.emptyList;
+import static com.google.eclipse.protobuf.model.util.QualifiedNames.removeLeadingDot;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -22,10 +22,18 @@
 import org.eclipse.xtext.scoping.impl.ImportNormalizer;
 import org.eclipse.xtext.scoping.impl.ImportScope;
 
-import com.google.common.collect.Lists;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
 
+/**
+ * {@link ImportScope} that allows additional ImportNormalizers to be added after initialization.
+ *
+ * @author (atrookey@google.com) Alexander Rookey
+ */
 public class ProtobufImportScope extends ImportScope {
   private final EClass type;
+  private List<ImportNormalizer> normalizers;
 
   public ProtobufImportScope(
       List<ImportNormalizer> namespaceResolvers,
@@ -35,46 +43,74 @@
       boolean ignoreCase) {
     super(namespaceResolvers, parent, importFrom, type, ignoreCase);
     this.type = type;
+    this.normalizers = removeDuplicates(namespaceResolvers);
   }
 
   @Override
   protected Iterable<IEObjectDescription> getAliasedElements(
       Iterable<IEObjectDescription> candidates) {
-    ArrayList<IEObjectDescription> descriptions =
-        Lists.newArrayList(super.getAliasedElements(candidates));
+    Multimap<QualifiedName, IEObjectDescription> keyToDescription = LinkedHashMultimap.create();
+    Multimap<QualifiedName, ImportNormalizer> keyToNormalizer = HashMultimap.create();
+
     for (IEObjectDescription imported : candidates) {
-      descriptions.add(new AliasedEObjectDescription(addLeadingDot(imported.getName()), imported));
+      QualifiedName fullyQualifiedName = imported.getName();
+      for (ImportNormalizer normalizer : normalizers) {
+        QualifiedName alias = normalizer.deresolve(fullyQualifiedName);
+        if (alias != null) {
+          QualifiedName key = alias;
+          if (isIgnoreCase()) {
+            key = key.toLowerCase();
+          }
+          keyToDescription.put(key, new AliasedEObjectDescription(alias, imported));
+          keyToNormalizer.put(key, normalizer);
+        }
+      }
     }
-    return descriptions;
+    for (QualifiedName name : keyToNormalizer.keySet()) {
+      if (keyToNormalizer.get(name).size() > 1) keyToDescription.removeAll(name);
+    }
+    return keyToDescription.values();
   }
 
+  // TODO (atrookey) Refactor this method for clarity
   @Override
   protected Iterable<IEObjectDescription> getLocalElementsByName(QualifiedName name) {
-    List<IEObjectDescription> result =
-        (List<IEObjectDescription>) super.getLocalElementsByName(name);
+    List<IEObjectDescription> result = new ArrayList<>();
     QualifiedName resolvedQualifiedName = null;
-    final QualifiedName resolvedName = name.skipFirst(1);
     ISelectable importFrom = getImportFrom();
-    if (resolvedName != null) {
-      Iterable<IEObjectDescription> resolvedElements =
-          importFrom.getExportedObjects(type, resolvedName, isIgnoreCase());
-      for (IEObjectDescription resolvedElement : resolvedElements) {
-        if (resolvedQualifiedName == null) resolvedQualifiedName = resolvedName;
-        else if (!resolvedQualifiedName.equals(resolvedName)) {
-          if (result.get(0).getEObjectOrProxy() != resolvedElement.getEObjectOrProxy()) {
-            return emptyList();
+    for (ImportNormalizer normalizer : normalizers) {
+      final QualifiedName resolvedName = normalizer.resolve(name);
+      if (resolvedName != null) {
+        Iterable<IEObjectDescription> resolvedElements =
+            importFrom.getExportedObjects(type, resolvedName, isIgnoreCase());
+        for (IEObjectDescription resolvedElement : resolvedElements) {
+          if (resolvedQualifiedName == null) {
+            resolvedQualifiedName = resolvedName;
+          } else if (!resolvedQualifiedName.equals(resolvedName)) {
+            if (result.get(0).getEObjectOrProxy() != resolvedElement.getEObjectOrProxy()) {
+              continue;
+            }
           }
+          QualifiedName alias = normalizer.deresolve(resolvedElement.getName());
+          if (alias == null)
+            throw new IllegalStateException(
+                "Couldn't deresolve " + resolvedElement.getName() + " with import " + normalizer);
+          final AliasedEObjectDescription aliasedEObjectDescription =
+              new AliasedEObjectDescription(alias, resolvedElement);
+          result.add(aliasedEObjectDescription);
         }
-        QualifiedName alias = addLeadingDot(resolvedElement.getName());
-        final AliasedEObjectDescription aliasedEObjectDescription =
-            new AliasedEObjectDescription(alias, resolvedElement);
-        result.add(aliasedEObjectDescription);
       }
     }
     return result;
   }
 
-  private QualifiedName addLeadingDot(QualifiedName qualifiedName) {
-    return QualifiedName.create("").append(qualifiedName);
+  /** Before getting element, check for and remove leading dot. */
+  @Override
+  public IEObjectDescription getSingleElement(QualifiedName name) {
+    return super.getSingleElement(removeLeadingDot(name));
+  }
+
+  public void addNormalizer(ImportNormalizer normalizer) {
+    normalizers.add(normalizer);
   }
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportUriGlobalScopeProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportUriGlobalScopeProvider.java
index df27228..0edb1a1 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportUriGlobalScopeProvider.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportUriGlobalScopeProvider.java
@@ -8,6 +8,7 @@
  */
 package com.google.eclipse.protobuf.scoping;
 
+import com.google.common.base.Predicate;
 import com.google.eclipse.protobuf.model.util.Imports;
 import com.google.eclipse.protobuf.model.util.Protobufs;
 import com.google.eclipse.protobuf.model.util.Resources;
@@ -19,7 +20,12 @@
 
 import org.eclipse.core.resources.IProject;
 import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EClass;
 import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.xtext.resource.IEObjectDescription;
+import org.eclipse.xtext.resource.IResourceDescription;
+import org.eclipse.xtext.resource.IResourceDescriptions;
+import org.eclipse.xtext.scoping.IScope;
 import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider;
 import org.eclipse.xtext.util.IResourceScopeCache;
 
@@ -30,6 +36,18 @@
  * caches the result.
  */
 public class ProtobufImportUriGlobalScopeProvider extends ImportUriGlobalScopeProvider {
+  @Override
+  protected IScope createLazyResourceScope(
+      IScope parent,
+      URI uri,
+      IResourceDescriptions descriptions,
+      EClass type,
+      Predicate<IEObjectDescription> filter,
+      boolean ignoreCase) {
+    IResourceDescription description = descriptions.getResourceDescription(uri);
+    return ProtobufSelectableBasedScope.createScope(parent, description, filter, type, ignoreCase);
+  }
+
   @Inject private Protobufs protobufs;
   @Inject private Resources resources;
   @Inject private Imports imports;
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportedNamespaceAwareLocalScopeProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportedNamespaceAwareLocalScopeProvider.java
index 76cd4ab..9767067 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportedNamespaceAwareLocalScopeProvider.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportedNamespaceAwareLocalScopeProvider.java
@@ -8,6 +8,8 @@
  */
 package com.google.eclipse.protobuf.scoping;
 
+import static java.util.Collections.singletonList;
+
 import java.lang.reflect.Array;
 import java.util.ArrayList;
 import java.util.List;
@@ -15,6 +17,8 @@
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.ecore.EClass;
 import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.resource.Resource;
 import org.eclipse.xtext.naming.QualifiedName;
 import org.eclipse.xtext.resource.ISelectable;
 import org.eclipse.xtext.scoping.IScope;
@@ -26,10 +30,18 @@
 import com.google.eclipse.protobuf.naming.ProtobufQualifiedNameConverter;
 import com.google.inject.Inject;
 
+/**
+ * A local scope provider for the Protobuf language that
+ * understands namespace imports.
+ *
+ * @author (atrookey@google.com) Alexander Rookey
+ */
 public class ProtobufImportedNamespaceAwareLocalScopeProvider
     extends ImportedNamespaceAwareLocalScopeProvider {
   @Inject private ProtobufQualifiedNameConverter qualifiedNameConverter;
 
+  private static final boolean WILDCARD = true;
+
   @Override
   protected ImportScope createImportScope(
       IScope parent,
@@ -46,22 +58,22 @@
     List<ImportNormalizer> importedNamespaceResolvers = new ArrayList<>();
     EList<EObject> eContents = context.eContents();
     for (EObject child : eContents) {
-      String namespace = getImportedNamespace(child);
-      if (namespace != null) {
-        ImportNormalizer resolver = createImportedNamespaceResolver(namespace, ignoreCase);
+      String name = getImportedNamespace(child);
+      if (name != null && !name.isEmpty()) {
+        ImportNormalizer resolver = createImportedNamespaceResolver(name, ignoreCase);
         if (resolver != null) {
           importedNamespaceResolvers.add(resolver);
         }
-        importedNamespaceResolvers.addAll(createResolversForInnerNamespaces(namespace, ignoreCase));
+        importedNamespaceResolvers.addAll(createResolversForInnerNamespaces(name, ignoreCase));
       }
     }
     return importedNamespaceResolvers;
   }
 
   /**
-   * Creates resolvers required for scoping to handle intersecting packages. The
-   * imported namespace {@code com.google.proto.foo} requires the following
-   * resolvers:
+   * Creates resolvers required for scoping to handle intersecting packages. The imported namespace
+   * {@code com.google.proto.foo} requires the following resolvers:
+   *
    * <ul>
    * <li>{@code com.*}
    * <li>{@code com.google.*}
@@ -88,14 +100,7 @@
     return importedNamespaceResolvers;
   }
 
-  /**
-   * Creates a new {@link ImportNormalizer} for the given namespace.
-   *
-   * @param namespace the namespace.
-   * @param ignoreCase {@code true} if the resolver should be case insensitive.
-   * @return a new {@link ImportNormalizer} or {@code null} if the namespace
-   *         cannot be converted to a valid qualified name.
-   */
+  /** Creates an {@link ImportNormalizer} with wildcards. */
   @Override
   protected ImportNormalizer createImportedNamespaceResolver(String namespace, boolean ignoreCase) {
     if (Strings.isEmpty(namespace)) {
@@ -105,6 +110,84 @@
     if (importedNamespace == null || importedNamespace.isEmpty()) {
       return null;
     }
-    return doCreateImportNormalizer(importedNamespace, true, ignoreCase);
+    return doCreateImportNormalizer(importedNamespace, WILDCARD, ignoreCase);
+  }
+
+  /**
+   * Creates a {@link ProtobufImportScope} regardless of whether or not
+   * {@code namespaceResolvers} is empty.
+   */
+  @Override
+  protected IScope getLocalElementsScope(
+      IScope parent, final EObject context, final EReference reference) {
+    IScope result = parent;
+    ISelectable allDescriptions = getAllDescriptions(context.eResource());
+    QualifiedName name = getQualifiedNameOfLocalElement(context);
+    boolean ignoreCase = isIgnoreCase(reference);
+    final List<ImportNormalizer> namespaceResolvers =
+        getImportedNamespaceResolvers(context, ignoreCase);
+    if (isRelativeImport() && name != null && !name.isEmpty()) {
+      ImportNormalizer localNormalizer = doCreateImportNormalizer(name, true, ignoreCase);
+      result =
+          createImportScope(
+              result,
+              singletonList(localNormalizer),
+              allDescriptions,
+              reference.getEReferenceType(),
+              isIgnoreCase(reference));
+    }
+    result =
+        createImportScope(
+            result,
+            namespaceResolvers,
+            null,
+            reference.getEReferenceType(),
+            isIgnoreCase(reference));
+    if (name != null) {
+      ImportNormalizer localNormalizer = doCreateImportNormalizer(name, true, ignoreCase);
+      result =
+          createImportScope(
+              result,
+              singletonList(localNormalizer),
+              allDescriptions,
+              reference.getEReferenceType(),
+              isIgnoreCase(reference));
+    }
+    return result;
+  }
+
+  /** 
+   * Makes {@code getAllDescriptions()} visible to {@link ProtobufScopeProvider}
+   */
+  @Override
+  protected ISelectable getAllDescriptions(Resource resource) {
+    return super.getAllDescriptions(resource);
+  }
+  /** 
+   * Makes {@code getImportedNamespaceResolvers()} visible to
+   * {@link ProtobufScopeProvider}
+   */
+  @Override
+  protected List<ImportNormalizer> getImportedNamespaceResolvers(
+      EObject context, boolean ignoreCase) {
+    return super.getImportedNamespaceResolvers(context, ignoreCase);
+  }
+  /** 
+   * Makes {@code getResourceScope()} visible to {@link ProtobufScopeProvider}
+   */
+  @Override
+  protected IScope getResourceScope(Resource res, EReference reference) {
+    return super.getResourceScope(res, reference);
+  }
+
+  /** Returns a {@link ProtobufSelectableBasedScope} instead of {@link SelectableBasedScope} */
+  @Override
+  protected IScope getResourceScope(IScope parent, EObject context, EReference reference) {
+    if (context.eResource() == null) {
+      return parent;
+    }
+    ISelectable allDescriptions = getAllDescriptions(context.eResource());
+    return ProtobufSelectableBasedScope.createScope(
+        parent, allDescriptions, reference.getEReferenceType(), isIgnoreCase(reference));
   }
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java
index f34a85a..d541014 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java
@@ -8,80 +8,209 @@
  */
 package com.google.eclipse.protobuf.scoping;
 
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_SOURCE__TARGET;
 import static com.google.eclipse.protobuf.scoping.OptionType.typeOf;
+import static com.google.eclipse.protobuf.util.Encodings.UTF_8;
 import static com.google.eclipse.protobuf.util.Tracer.DEBUG_SCOPING;
 import static com.google.eclipse.protobuf.validation.ProtobufResourceValidator.getScopeProviderTimingCollector;
 import static java.util.Collections.emptySet;
+import static java.util.Collections.singletonMap;
+import static org.eclipse.emf.ecore.resource.ContentHandler.UNSPECIFIED_CONTENT_TYPE;
+import static org.eclipse.xtext.EcoreUtil2.resolveLazyCrossReferences;
+import static org.eclipse.xtext.resource.XtextResource.OPTION_ENCODING;
+import static org.eclipse.xtext.util.CancelIndicator.NullImpl;
 
-import com.google.eclipse.protobuf.model.util.MessageFields;
 import com.google.eclipse.protobuf.model.util.ModelObjects;
-import com.google.eclipse.protobuf.model.util.Options;
+import com.google.eclipse.protobuf.naming.NameResolver;
+import com.google.eclipse.protobuf.naming.ProtobufQualifiedNameConverter;
+import com.google.eclipse.protobuf.preferences.general.PreferenceNames;
 import com.google.eclipse.protobuf.protobuf.AbstractCustomOption;
 import com.google.eclipse.protobuf.protobuf.AbstractOption;
 import com.google.eclipse.protobuf.protobuf.ComplexType;
+import com.google.eclipse.protobuf.protobuf.ComplexTypeLink;
 import com.google.eclipse.protobuf.protobuf.ComplexValue;
-import com.google.eclipse.protobuf.protobuf.DefaultValueFieldOption;
+import com.google.eclipse.protobuf.protobuf.ComplexValueField;
+import com.google.eclipse.protobuf.protobuf.CustomFieldOption;
+import com.google.eclipse.protobuf.protobuf.CustomOption;
 import com.google.eclipse.protobuf.protobuf.Enum;
 import com.google.eclipse.protobuf.protobuf.ExtensibleType;
 import com.google.eclipse.protobuf.protobuf.FieldName;
+import com.google.eclipse.protobuf.protobuf.FieldOption;
 import com.google.eclipse.protobuf.protobuf.Group;
-import com.google.eclipse.protobuf.protobuf.GroupElement;
-import com.google.eclipse.protobuf.protobuf.IndexedElement;
+import com.google.eclipse.protobuf.protobuf.Literal;
 import com.google.eclipse.protobuf.protobuf.LiteralLink;
 import com.google.eclipse.protobuf.protobuf.Message;
 import com.google.eclipse.protobuf.protobuf.MessageField;
-import com.google.eclipse.protobuf.protobuf.MessageLink;
-import com.google.eclipse.protobuf.protobuf.MessageOptionField;
-import com.google.eclipse.protobuf.protobuf.NormalFieldName;
+import com.google.eclipse.protobuf.protobuf.NativeFieldOption;
+import com.google.eclipse.protobuf.protobuf.NativeOption;
+import com.google.eclipse.protobuf.protobuf.OneOf;
 import com.google.eclipse.protobuf.protobuf.OptionField;
 import com.google.eclipse.protobuf.protobuf.OptionSource;
 import com.google.eclipse.protobuf.protobuf.Protobuf;
 import com.google.eclipse.protobuf.protobuf.Rpc;
-import com.google.eclipse.protobuf.protobuf.SimpleValueField;
 import com.google.eclipse.protobuf.protobuf.Stream;
 import com.google.eclipse.protobuf.protobuf.TypeExtension;
+import com.google.eclipse.protobuf.protobuf.TypeLink;
+import com.google.eclipse.protobuf.protobuf.ValueField;
 import com.google.eclipse.protobuf.util.EResources;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 
+import org.apache.log4j.Level;
 import org.eclipse.core.resources.IProject;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.xtext.linking.impl.LinkingHelper;
+import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.xtext.nodemodel.ICompositeNode;
+import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
 import org.eclipse.xtext.resource.IEObjectDescription;
+import org.eclipse.xtext.resource.ISelectable;
 import org.eclipse.xtext.scoping.IScope;
 import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
-import org.eclipse.xtext.scoping.impl.SimpleScope;
+import org.eclipse.xtext.scoping.impl.ImportNormalizer;
+import org.eclipse.xtext.scoping.impl.SelectableBasedScope;
+import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
+import org.eclipse.xtext.util.IResourceScopeCache;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
+import java.util.List;
 
 /**
- * Custom scoping description.
+ * A scope provider for the Protobuf language.
  *
- * @author alruiz@google.com (Alex Ruiz)
- *
- * @see <a href="http://www.eclipse.org/Xtext/documentation/latest/xtext.html#scoping">Xtext
- * Scoping</a>
+ * @author atrookey@google.com (Alexander Rookey)
  */
 public class ProtobufScopeProvider extends AbstractDeclarativeScopeProvider
     implements ScopeProvider {
-  private static final boolean DO_NOT_IGNORE_CASE = false;
-
   @Inject private ComplexTypeFinderStrategy complexTypeFinderDelegate;
   @Inject private CustomOptionFieldFinder customOptionFieldFinder;
   @Inject private CustomOptionFieldNameFinder customOptionFieldNameFinder;
   @Inject private CustomOptionFinderStrategy customOptionFinderDelegate;
-  @Inject private ExtensionFieldNameFinderStrategy extensionFieldNameFinderDelegate;
   @Inject private ExtensionFieldFinderStrategy extensionFieldFinderDelegate;
-  @Inject private ProtoDescriptorProvider descriptorProvider;
+  @Inject private ExtensionFieldNameFinderStrategy extensionFieldNameFinderDelegate;
   @Inject private MessageFieldFinderStrategy messageFieldFinderDelegate;
-  @Inject private MessageFields messageFields;
   @Inject private ModelElementFinder modelElementFinder;
   @Inject private ModelObjects modelObjects;
   @Inject private NormalFieldNameFinderStrategy normalFieldNameFinderDelegate;
-  @Inject private LiteralDescriptions literalDescriptions;
-  @Inject private NativeOptionDescriptions nativeOptionDescriptions;
-  @Inject private Options options;
+  @Inject private LinkingHelper linkingHelper;
+  @Inject private IPreferenceStoreAccess storeAccess;
+  @Inject private IUriResolver uriResolver;
+  @Inject private IResourceScopeCache cache;
+  @Inject private NameResolver nameResolver;
+  @Inject private ProtobufQualifiedNameConverter nameConverter;
+
+  private static final String CACHEKEY = "IMPORT_NORMALIZER_CACHE_KEY";
+
+  /**
+   * Returns the name of the descriptor.proto message declaring default options. The options must
+   * match the option type of {@code context}. For example, if {@code context} is a file option,
+   * {@code FileOptions} should be returned because it contains the declarations of the default file
+   * options.
+   */
+  // TODO (atrookey) Create utility for getting the type of an Option.
+  private static String getOptionType(EObject context) {
+    if (context == null) {
+      return "FileOptions";
+    }
+    if (context instanceof Message) {
+      return "MessageOptions";
+    }
+    if (context instanceof Enum) {
+      return "EnumOptions";
+    }
+    if (context instanceof FieldOption) {
+      return "FieldOptions";
+    }
+    if (context instanceof Rpc) {
+      return "MethodOptions";
+    }
+    if (context instanceof Stream) {
+      return "StreamOptions";
+    }
+    if (context instanceof Literal) {
+      return "EnumValueOptions";
+    }
+    return getOptionType(context.eContainer());
+  }
+
+  private Collection<IEObjectDescription> allMessages(Protobuf root) {
+    return modelElementFinder.find(root, complexTypeFinderDelegate, Message.class);
+  }
+
+  /**
+   * Returns the InputStream associated with the resource at location {@code descriptorLocation}.
+   */
+  private InputStream openFile(URI fileLocation) throws IOException {
+    URL url = new URL(fileLocation.toString());
+    return url.openConnection().getInputStream();
+  }
+
+  // TODO (atrookey) Create utility for getting package.
+  private String getPackageOfResource(Resource resource) {
+    Protobuf object;
+    if (resource != null && (object = (Protobuf) resource.getContents().get(0)) != null) {
+      for (EObject content : object.getElements()) {
+        if (content instanceof com.google.eclipse.protobuf.protobuf.Package) {
+          return ((com.google.eclipse.protobuf.protobuf.Package) content).getImportedNamespace();
+        }
+      }
+    }
+    return "";
+  }
+
+  /**
+   * Returns descriptor associated with the current project.
+   */
+  private  @Nullable Resource getDescriptorResource(EObject context) {
+    URI descriptorLocation;
+    IProject project = EResources.getProjectOf(context.eResource());
+    IPreferenceStore store = storeAccess.getWritablePreferenceStore(project);
+    String rawDescriptorLocation = store.getString(PreferenceNames.DESCRIPTOR_PROTO_PATH);
+    descriptorLocation =
+        URI.createURI(uriResolver.resolveUri(rawDescriptorLocation, null, project));
+    ResourceSet resourceSet = context.eResource().getResourceSet();
+    Resource resource = resourceSet.getResource(descriptorLocation, true);
+    if (resource != null) {
+      return resource;
+    }
+    try {
+      InputStream contents = openFile(descriptorLocation);
+      resource = resourceSet.createResource(descriptorLocation, UNSPECIFIED_CONTENT_TYPE);
+      resource.load(contents, singletonMap(OPTION_ENCODING, UTF_8));
+      resolveLazyCrossReferences(resource, NullImpl);
+      return resource;
+    } catch (IOException e) {
+      logger.log(Level.ERROR, e);
+    }
+    return null;
+  }
+
+  /**
+   * Returns name of an object as a QualifiedName.
+   */
+  private QualifiedName getEObjectName(EObject object) {
+    ICompositeNode node = NodeModelUtils.getNode(object);
+    String name = linkingHelper.getCrossRefNodeAsString(node, true);
+    return QualifiedName.create(name);
+  }
+
+  /**
+   * Returns the local scope provider.
+   */
+  private ProtobufImportedNamespaceAwareLocalScopeProvider getLocalScopeProvider() {
+    return (ProtobufImportedNamespaceAwareLocalScopeProvider) super.getDelegate();
+  }
 
   @Override
   public IScope getScope(EObject context, EReference reference) {
@@ -95,6 +224,86 @@
     return scope;
   }
 
+  /**
+   * Recursively scope {@code FieldName} starting with an {@code OptionSource}.
+   */
+  private IScope getScopeOfFieldName(
+      IScope delegatedScope, OptionSource optionSource, EReference reference) {
+    IScope retval = IScope.NULLSCOPE;
+    IScope parentScope = super.getScope(optionSource, OPTION_SOURCE__TARGET);
+    QualifiedName optionSourceName = getEObjectName(optionSource);
+    IEObjectDescription indexedElementDescription = parentScope.getSingleElement(optionSourceName);
+    if (indexedElementDescription != null) {
+      EObject indexedElement = indexedElementDescription.getEObjectOrProxy();
+      retval = getLocalScopeOfMessageFieldOrGroup(delegatedScope, indexedElement, reference);
+    }
+    return retval;
+  }
+
+  /**
+   * Locally scope any children of {@code context} that are of type {@code OneOf}.
+   */
+  private IScope getScopeOfOneOf(IScope scope, EObject context, EReference reference) {
+    IScope result = scope;
+    for (EObject element : context.eContents()) {
+      if (element instanceof OneOf) {
+        result = getLocalScopeProvider().getLocalElementsScope(result, element, reference);
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Recursively scope {@code OptionField} starting with an {@code OptionSource}.
+   */
+  private IScope getScopeOfOptionField(
+      OptionSource optionSource, EReference reference, int fieldIndex, EList<OptionField> fields) {
+    if (fieldIndex < 0 || fields.size() <= fieldIndex) {
+      throw new IllegalArgumentException();
+    }
+    IScope parentScope, retval = IScope.NULLSCOPE;
+    QualifiedName name = QualifiedName.EMPTY;
+    if (fieldIndex == 0) {
+      parentScope = super.getScope(optionSource, OPTION_SOURCE__TARGET);
+      name = getEObjectName(optionSource);
+    } else {
+      OptionField parentOptionField = fields.get(fieldIndex - 1);
+      parentScope = getScopeOfOptionField(optionSource, reference, fieldIndex - 1, fields);
+      name = getEObjectName(parentOptionField);
+    }
+    IEObjectDescription indexedElementDescription = parentScope.getSingleElement(name);
+    if (indexedElementDescription != null) {
+      EObject indexedElement = indexedElementDescription.getEObjectOrProxy();
+      retval = getLocalScopeOfMessageFieldOrGroup(retval, indexedElement, reference);
+    }
+    return retval;
+  }
+
+  /**
+   * Recursively scope nested OptionFields and FieldNames.
+   *
+   * <p>If {@code IndexedElement} refers to an {@code MessageField}, scope the {@code Message} of
+   * that field. If it refers to a {@code Group}, return the scope of that {@code Group}.
+   */
+  private IScope getLocalScopeOfMessageFieldOrGroup(
+      IScope parentScope, EObject indexedElement, EReference reference) {
+    IScope retval = IScope.NULLSCOPE;
+    if (indexedElement instanceof MessageField) {
+      TypeLink typeLink = ((MessageField) indexedElement).getType();
+      if (typeLink instanceof ComplexTypeLink) {
+        ComplexType complexType = ((ComplexTypeLink) typeLink).getTarget();
+        IScope result =
+            getLocalScopeProvider().getLocalElementsScope(parentScope, complexType, reference);
+        retval = getScopeOfOneOf(result, complexType, reference);
+      }
+    }
+    if (indexedElement instanceof Group) {
+      retval =
+          getLocalScopeProvider().getLocalElementsScope(parentScope, indexedElement, reference);
+    }
+    return retval;
+  }
+
   @Override
   public Collection<IEObjectDescription> potentialComplexTypesFor(MessageField field) {
     return modelElementFinder.find(field, complexTypeFinderDelegate, ComplexType.class);
@@ -106,11 +315,20 @@
     return modelElementFinder.find(root, complexTypeFinderDelegate, ExtensibleType.class);
   }
 
-  @SuppressWarnings("unused")
-  public IScope scope_MessageLink_target(MessageLink link, EReference r) {
-    Protobuf root = modelObjects.rootOf(link);
-    Collection<IEObjectDescription> messages = allMessages(root);
-    return createScope(messages);
+  @Override
+  public Collection<IEObjectDescription> potentialExtensionFieldNames(ComplexValue value) {
+    return customOptionFieldNameFinder.findFieldNamesSources(
+        value, extensionFieldNameFinderDelegate);
+  }
+
+  @Override
+  public Collection<IEObjectDescription> potentialExtensionFieldsFor(AbstractCustomOption option) {
+    return customOptionFieldFinder.findOptionFields(option, extensionFieldFinderDelegate);
+  }
+
+  @Override
+  public Collection<IEObjectDescription> potentialMessageFieldsFor(AbstractCustomOption option) {
+    return customOptionFieldFinder.findOptionFields(option, messageFieldFinderDelegate);
   }
 
   @Override
@@ -125,67 +343,9 @@
     return allMessages(root);
   }
 
-  private Collection<IEObjectDescription> allMessages(Protobuf root) {
-    return modelElementFinder.find(root, complexTypeFinderDelegate, Message.class);
-  }
-
-  @SuppressWarnings("unused")
-  public IScope scope_LiteralLink_target(LiteralLink link, EReference r) {
-    EObject container = link.eContainer();
-    Enum anEnum = null;
-    if (container instanceof DefaultValueFieldOption) {
-      container = container.eContainer();
-    }
-    if (container instanceof AbstractOption) {
-      AbstractOption option = (AbstractOption) container;
-      if (options.isNative(option)) {
-        IProject project = EResources.getProjectOf(option.eResource());
-        ProtoDescriptor descriptor = descriptorProvider.primaryDescriptor(project);
-        IndexedElement e = options.rootSourceOf(option);
-        anEnum = descriptor.enumTypeOf((MessageField) e);
-      }
-    }
-    if (container instanceof AbstractCustomOption) {
-      AbstractCustomOption option = (AbstractCustomOption) container;
-      container = options.sourceOf(option);
-    }
-    if (container instanceof SimpleValueField) {
-      SimpleValueField field = (SimpleValueField) container;
-      container = field.getName().getTarget();
-    }
-    if (container instanceof MessageField) {
-      anEnum = messageFields.enumTypeOf((MessageField) container);
-    }
-    return createScope(literalDescriptions.literalsOf(anEnum));
-  }
-
-  @SuppressWarnings("unused")
-  public IScope scope_OptionSource_target(OptionSource source, EReference r) {
-    EObject c = source.eContainer();
-    if (c instanceof AbstractOption) {
-      AbstractOption option = (AbstractOption) c;
-      if (options.isNative(option)) {
-        return createScope(nativeOptionDescriptions.sources(option));
-      }
-    }
-    if (c instanceof AbstractCustomOption) {
-      AbstractCustomOption option = (AbstractCustomOption) c;
-
-      if (c instanceof GroupElement) {
-        EObject container = c.eContainer();
-        if (container instanceof Group) {
-          OptionType optionType = OptionType.findOptionTypeForLevelOf(container.eContainer());
-          return createScope(
-              optionType != null
-                  ? modelElementFinder.find(option, customOptionFinderDelegate, optionType)
-                  : Collections.<IEObjectDescription>emptySet());
-        }
-      }
-
-      return createScope(potentialSourcesFor(option));
-    }
-    Set<IEObjectDescription> descriptions = emptySet();
-    return createScope(descriptions);
+  @Override
+  public Collection<IEObjectDescription> potentialNormalFieldNames(ComplexValue value) {
+    return customOptionFieldNameFinder.findFieldNamesSources(value, normalFieldNameFinderDelegate);
   }
 
   @Override
@@ -198,75 +358,234 @@
     return descriptions;
   }
 
-  @SuppressWarnings("unused")
-  public IScope scope_OptionField_target(OptionField field, EReference r) {
-    return createScope(allPossibleSourcesOf(field));
-  }
-
-  private Collection<IEObjectDescription> allPossibleSourcesOf(OptionField field) {
-    if (field == null) {
-      return emptySet();
-    }
-    EObject container = field.eContainer();
-    if (container instanceof AbstractCustomOption) {
-      AbstractCustomOption option = (AbstractCustomOption) container;
-      if (field instanceof MessageOptionField) {
-        return customOptionFieldFinder.findOptionFields(option, messageFieldFinderDelegate, field);
+  /**
+   * Recursively scopes the {@code FieldName} starting with the {@code OptionSource}.
+   *
+   * For example:
+   *
+   * <pre>
+   * message FooOptions {
+   *   optional int32 opt1 = 1;
+   * }
+   * extend google.protobuf.FieldOptions {
+   *   optional FooOptions foo_options = 1234;
+   * }
+   * message Bar {
+   *   optional int32 b = 1 [(foo_options) = { opt1: 123 }];
+   * }
+   * </pre>
+   *
+   * The {@code NormalFieldName} {@code opt1} contains a cross-reference to {@code FooOptions.opt1}.
+   */
+  public IScope scope_FieldName_target(FieldName fieldName, EReference reference) {
+    OptionSource optionSource = null;
+    EObject valueField = fieldName.eContainer();
+    if (valueField != null && valueField instanceof ValueField) {
+      EObject complexValue = valueField.eContainer();
+      if (complexValue != null && complexValue instanceof ComplexValue) {
+        EObject unknownOption = complexValue.eContainer();
+        IScope delegatedScope = super.delegateGetScope(fieldName, reference);
+        if (unknownOption != null && unknownOption instanceof ComplexValueField) {
+          ComplexValueField complexValueField = (ComplexValueField) unknownOption;
+          IScope parentScope = scope_FieldName_target(complexValueField.getName(), reference);
+          IEObjectDescription indexedElementDescription =
+              parentScope.getSingleElement(this.getEObjectName(complexValueField.getName()));
+          // TODO (atrookey) This is only necessary because of ExtensionFieldName.
+          // Could be made more specific.
+          return getLocalScopeOfMessageFieldOrGroup(
+              delegatedScope, indexedElementDescription.getEObjectOrProxy(), reference);
+        }
+        if (unknownOption != null && unknownOption instanceof NativeFieldOption) {
+          NativeFieldOption nativeFieldOption = (NativeFieldOption) unknownOption;
+          optionSource = nativeFieldOption.getSource();
+        }
+        if (unknownOption != null && unknownOption instanceof CustomFieldOption) {
+          CustomFieldOption customFieldOption = (CustomFieldOption) unknownOption;
+          optionSource = customFieldOption.getSource();
+        }
+        if (unknownOption != null && unknownOption instanceof NativeOption) {
+          NativeOption option = (NativeOption) unknownOption;
+          optionSource = option.getSource();
+        }
+        if (unknownOption != null && unknownOption instanceof CustomOption) {
+          CustomOption option = (CustomOption) unknownOption;
+          optionSource = option.getSource();
+        }
+        return getScopeOfFieldName(delegatedScope, optionSource, reference);
       }
-      return customOptionFieldFinder.findOptionFields(option, extensionFieldFinderDelegate, field);
-    }
-    return emptySet();
-  }
-
-  @Override
-  public Collection<IEObjectDescription> potentialMessageFieldsFor(AbstractCustomOption option) {
-    return customOptionFieldFinder.findOptionFields(option, messageFieldFinderDelegate);
-  }
-
-  @Override
-  public Collection<IEObjectDescription> potentialExtensionFieldsFor(AbstractCustomOption option) {
-    return customOptionFieldFinder.findOptionFields(option, extensionFieldFinderDelegate);
-  }
-
-  @SuppressWarnings("unused")
-  public IScope scope_FieldName_target(FieldName name, EReference r) {
-    return createScope(findSources(name));
-  }
-
-  private Collection<IEObjectDescription> findSources(FieldName name) {
-    ComplexValue value = container(name);
-    if (value == null) {
-      return emptySet();
-    }
-    if (name instanceof NormalFieldName) {
-      return potentialNormalFieldNames(value);
-    }
-    return potentialExtensionFieldNames(value);
-  }
-
-  private ComplexValue container(FieldName name) {
-    EObject container = name;
-    while (container != null) {
-      if (container instanceof ComplexValue) {
-        return (ComplexValue) container;
-      }
-      container = container.eContainer();
     }
     return null;
   }
 
-  @Override
-  public Collection<IEObjectDescription> potentialNormalFieldNames(ComplexValue value) {
-    return customOptionFieldNameFinder.findFieldNamesSources(value, normalFieldNameFinderDelegate);
+  /**
+   * Creates a scope containing elements of type {@code Literal} that can be
+   * referenced with their local name only.
+   *
+   * For example:
+   *
+   * <pre>
+   * enum MyEnum {
+   *   FOO = 1;
+   * }
+   * extend google.protobuf.ServiceOptions {
+   *   optional MyEnum my_service_option = 50005;
+   * }
+   * service MyService {
+   *   option (my_service_option) = FOO;
+   * }
+   * </pre>
+   *
+   * The {@code LiteralLink} {@code FOO} contains a cross-reference to {@code MyEnum.FOO}.
+   */
+  public IScope scope_LiteralLink_target(LiteralLink literalLink, EReference reference) {
+    IScope scope = IScope.NULLSCOPE;
+    scope =
+        createLiteralLinkResolvedScopeForResource(
+            reference, scope, getDescriptorResource(literalLink));
+    scope = createLiteralLinkResolvedScopeForResource(reference, scope, literalLink.eResource());
+    return scope;
   }
 
-  @Override
-  public Collection<IEObjectDescription> potentialExtensionFieldNames(ComplexValue value) {
-    return customOptionFieldNameFinder.findFieldNamesSources(
-        value, extensionFieldNameFinderDelegate);
+  // TODO (atrookey) Create resolvers for ProtobufImportScopes at all scope levels,
+  // not just top level.
+  private IScope createLiteralLinkResolvedScopeForResource(
+      EReference reference, IScope parent, Resource resource) {
+    IScope scope = parent;
+    scope = getProtobufImportScope(scope, resource, reference);
+    List<ImportNormalizer> protoNormalizers =
+        cache.get(
+            CACHEKEY,
+            resource,
+            new Provider<List<ImportNormalizer>>() {
+              @Override
+              public List<ImportNormalizer> get() {
+                return createEnumElementResolvers(
+                    resource.getContents().get(0), getPackageOfResource(resource), false);
+              }
+            });
+    if (scope instanceof ProtobufImportScope) {
+      for (ImportNormalizer normalizer : protoNormalizers) {
+        ((ProtobufImportScope) scope).addNormalizer(normalizer);
+      }
+    } else if (scope instanceof SelectableBasedScope) {
+      ISelectable allDescriptions = getLocalScopeProvider().getAllDescriptions(resource);
+      scope =
+          getLocalScopeProvider()
+              .createImportScope(
+                  scope, protoNormalizers, allDescriptions, reference.getEReferenceType(), false);
+    }
+    return scope;
   }
 
-  private static IScope createScope(Iterable<IEObjectDescription> descriptions) {
-    return new SimpleScope(descriptions, DO_NOT_IGNORE_CASE);
+  /**
+   * Creates an {@code ImportNormalizer} for every {@code Enum} that is a descendant of {@code context}.
+   */
+  private List<ImportNormalizer> createEnumElementResolvers(
+      EObject context, String qualifiedName, boolean ignoreCase) {
+    List<ImportNormalizer> importedNamespaceResolvers = new ArrayList<>();
+    for (EObject child : context.eContents()) {
+      if (child instanceof Enum) {
+        String name = appendNameOfEObject(qualifiedName, child);
+        if (!name.isEmpty()) {
+          ImportNormalizer resolver =
+              getLocalScopeProvider().createImportedNamespaceResolver(name, ignoreCase);
+          if (resolver != null) {
+            importedNamespaceResolvers.add(resolver);
+          }
+        }
+      }
+      if (child instanceof Message) {
+        String name = appendNameOfEObject(qualifiedName, child);
+        importedNamespaceResolvers.addAll(createEnumElementResolvers(child, name, ignoreCase));
+      }
+    }
+    return importedNamespaceResolvers;
+  }
+
+  private String appendNameOfEObject(String qualifiedName, EObject child) {
+    String childName = nameResolver.nameOf(child);
+    if (qualifiedName.isEmpty()) {
+      return childName;
+    }
+    return qualifiedName + nameConverter.getDelimiter() + childName;
+  }
+
+  /**
+   * Recursively scopes the {@code OptionField} starting with the {@code OptionSource}.
+   *
+   * For example:
+   *
+   * <pre>
+   * message Code {
+   *   optional double number = 1;
+   * }
+   * message Type {
+   *   optional Code code = 1;
+   * }
+   * extend proto2.FieldOptions {
+   *   optional Type type = 1000;
+   * }
+   * message Person {
+   *   optional bool active = 1 [(type).code.number = 68];
+   * }
+   * </pre>
+   *
+   * The {@code OptionField} {@code number} contains a cross-reference to {@code Code.number}.
+   */
+  public @Nullable IScope scope_OptionField_target(OptionField optionField, EReference reference) {
+    EObject customOption = optionField.eContainer();
+    if (customOption != null) {
+      OptionSource optionSource = null;
+      EList<OptionField> fields = null;
+      if (customOption instanceof CustomFieldOption) {
+        optionSource = ((CustomFieldOption) customOption).getSource();
+        fields = ((CustomFieldOption) customOption).getFields();
+      }
+      if (customOption instanceof CustomOption) {
+        optionSource = ((CustomOption) customOption).getSource();
+        fields = ((CustomOption) customOption).getFields();
+      }
+      if (optionSource != null && fields != null) {
+        int index = fields.indexOf(optionField);
+        return getScopeOfOptionField(optionSource, reference, index, fields);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Creates a scope containing the default options defined in descriptor.proto.
+   *
+   * For example:
+   *
+   * <pre>
+   * option java_package = "com.example.foo";
+   * </pre>
+   *
+   * The {@code OptionSource} {@code java_package} contains a cross-reference to {@code
+   * google.protobuf.FileOptions.java_package} defined in descriptor.proto.
+   */
+  public IScope scope_OptionSource_target(OptionSource optionSource, EReference reference) {
+    Resource descriptorResource = getDescriptorResource(optionSource);
+    String descriptorMessage =
+        getPackageOfResource(descriptorResource)
+            + nameConverter.getDelimiter()
+            + getOptionType(optionSource);
+    ImportNormalizer normalizer =
+        getLocalScopeProvider().createImportedNamespaceResolver(descriptorMessage, false);
+    IScope scope = delegateGetScope(optionSource, reference);
+    scope = getProtobufImportScope(scope, getDescriptorResource(optionSource), reference);
+    ((ProtobufImportScope) scope).addNormalizer(normalizer);
+    return scope;
+  }
+
+  /** Returns the top level scope of the {@code Resource}. */
+  private ProtobufImportScope getProtobufImportScope(
+      IScope parent, Resource resource, EReference reference) {
+    EObject protobuf = resource.getContents().get(0);
+    IScope descriptorResourceScope =
+        getLocalScopeProvider().getResourceScope(parent, protobuf, reference);
+    return (ProtobufImportScope)
+        getLocalScopeProvider().getLocalElementsScope(descriptorResourceScope, protobuf, reference);
   }
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufSelectableBasedScope.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufSelectableBasedScope.java
new file mode 100644
index 0000000..40f6e00
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufSelectableBasedScope.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.scoping;
+
+import static com.google.eclipse.protobuf.model.util.QualifiedNames.removeLeadingDot;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.xtext.resource.IEObjectDescription;
+import org.eclipse.xtext.resource.ISelectable;
+import org.eclipse.xtext.scoping.IScope;
+import org.eclipse.xtext.scoping.impl.SelectableBasedScope;
+
+import com.google.common.base.Predicate;
+
+/**
+ * {@link SelectableBasedScope} that handles qualified names with a leading dot.
+ *
+ * @author (atrookey@google.com) Alexander Rookey
+ */
+public class ProtobufSelectableBasedScope extends SelectableBasedScope {
+  public static IScope createScope(
+      IScope outer, ISelectable selectable, EClass type, boolean ignoreCase) {
+    return createScope(outer, selectable, null, type, ignoreCase);
+  }
+
+  public static IScope createScope(
+      IScope outer,
+      ISelectable selectable,
+      Predicate<IEObjectDescription> filter,
+      EClass type,
+      boolean ignoreCase) {
+    if (selectable == null || selectable.isEmpty()) {
+      return outer;
+    }
+    return new ProtobufSelectableBasedScope(outer, selectable, filter, type, ignoreCase);
+  }
+
+  protected ProtobufSelectableBasedScope(
+      IScope outer,
+      ISelectable selectable,
+      Predicate<IEObjectDescription> filter,
+      EClass type,
+      boolean ignoreCase) {
+    super(outer, selectable, filter, type, ignoreCase);
+  }
+
+  /** Before getting element, removes leading dot. */
+  @Override
+  public IEObjectDescription getSingleElement(QualifiedName name) {
+    return super.getSingleElement(removeLeadingDot(name));
+  }
+}