Merge remote branch 'origin/option-ref'
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/grammar/CommonKeyword_hasValue_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/grammar/CommonKeyword_hasValue_Test.java
index 0e0da1f..4062a60 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/grammar/CommonKeyword_hasValue_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/grammar/CommonKeyword_hasValue_Test.java
@@ -28,6 +28,8 @@
     assertThat(BYTES.hasValue("bytes"), equalTo(true));
     assertThat(OPENING_BRACKET.hasValue("["), equalTo(true));
     assertThat(CLOSING_BRACKET.hasValue("]"), equalTo(true));
+    assertThat(OPENING_CURLY_BRACKET.hasValue("{"), equalTo(true));
+    assertThat(CLOSING_CURLY_BRACKET.hasValue("}"), equalTo(true));
     assertThat(DEFAULT.hasValue("default"), equalTo(true));
     assertThat(EQUAL.hasValue("="), equalTo(true));
     assertThat(SEMICOLON.hasValue(";"), equalTo(true));
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/util/Finder.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/util/Finder.java
index b530ec6..d33172c 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/util/Finder.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/util/Finder.java
@@ -34,10 +34,17 @@
 
   public static Option findOption(String name, Protobuf root) {
     for (Option option : getAllContentsOfType(root, Option.class))
-      if (name.equals(option.getName())) return option;
+      if (name.equals(nameOf(option))) return option;
     return null;
   }
 
+  private static String nameOf(Option option) {
+    PropertyRef ref = option.getProperty();
+    if (ref == null) return null;
+    Property property = ref.getProperty();
+    return (property == null) ? null : property.getName();
+  }
+
   public static Property findProperty(String name, Protobuf root) {
     for (Property property : allProperties(root))
       if (name.equals(property.getName())) return property;
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/QualifiedNames_addLeadingDot_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/QualifiedNames_addLeadingDot_Test.java
index ff1cea0..eb44a88 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/QualifiedNames_addLeadingDot_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/QualifiedNames_addLeadingDot_Test.java
@@ -12,7 +12,9 @@
 import static org.junit.Assert.assertThat;
 
 import org.eclipse.xtext.naming.QualifiedName;
-import org.junit.Test;
+import org.junit.*;
+
+import com.google.eclipse.protobuf.junit.core.XtextRule;
 
 /**
  * Tests for <code>{@link QualifiedNames#addLeadingDot(QualifiedName)}</code>.
@@ -21,15 +23,23 @@
  */
 public class QualifiedNames_addLeadingDot_Test {
 
+  @Rule public XtextRule xtext = new XtextRule();
+
+  private QualifiedNames qualifiedNames;
+
+  @Before public void setUp() {
+    qualifiedNames = xtext.getInstanceOf(QualifiedNames.class);
+  }
+
   @Test public void should_add_leading_dot() {
     QualifiedName name = QualifiedName.create("jedis", "Luke");
-    QualifiedName withLeadingDot = QualifiedNames.addLeadingDot(name);
+    QualifiedName withLeadingDot = qualifiedNames.addLeadingDot(name);
     assertThat(withLeadingDot.toString(), equalTo(".jedis.Luke"));
   }
 
   @Test public void should_not_add_leading_dot_if_qualified_name_already_has_it() {
     QualifiedName name = QualifiedName.create("", "jedis", "Luke");
-    QualifiedName withLeadingDot = QualifiedNames.addLeadingDot(name);
+    QualifiedName withLeadingDot = qualifiedNames.addLeadingDot(name);
     assertThat(withLeadingDot.toString(), equalTo(".jedis.Luke"));
   }
 }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java
index d874af1..853b1c0 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java
@@ -53,6 +53,7 @@
   @Inject private Fields fields;
   @Inject private Images images;
   @Inject private Literals literals;
+  @Inject private Options options;
   @Inject private Properties properties;
 
   @Override public void completeProtobuf_Syntax(EObject model, Assignment assignment, ContentAssistContext context,
@@ -69,67 +70,35 @@
     proposeAndAccept(proposal, imageHelper.getImage(images.imageFor(Syntax.class)), context, acceptor);
   }
 
-  @Override public void completeBuiltInOption_Name(EObject model, Assignment assignment, ContentAssistContext context,
-      ICompletionProposalAcceptor acceptor) {
-    if (proposeOptions(model, context, acceptor)) return;
-    if (model instanceof Option) {
-      EObject container = model.eContainer();
-      proposeOptions(container, context, acceptor);
-    }
-  }
-
-  private boolean proposeOptions(EObject model, ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
-    if (model instanceof Protobuf) {
-      proposeCommonFileOptions(context, acceptor);
-      return true;
-    }
-    if (model instanceof Message) {
-      proposeCommonMessageOptions(context, acceptor);
-      return true;
-    }
-    if (model instanceof Enum) {
-      proposeCommonEnumOptions(context, acceptor);
-      return true;
-    }
-    return false;
-  }
-
-  private void proposeCommonFileOptions(ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
-    proposeOptions(descriptorProvider.get().fileOptions(), context, acceptor);
-  }
-
-  private void proposeCommonMessageOptions(ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
-    proposeOptions(descriptorProvider.get().messageOptions(), context, acceptor);
-  }
-
-  private void proposeCommonEnumOptions(ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
-    proposeOptions(descriptorProvider.get().enumOptions(), context, acceptor);
-  }
-
-  private void proposeOptions(Collection<Property> options, ContentAssistContext context,
-      ICompletionProposalAcceptor acceptor) {
-    for (Property option : options)
-      proposeOption(option, context, acceptor);
-  }
-
-  @Override public void completeBuiltInOption_Value(EObject model, Assignment assignment, ContentAssistContext context,
-      ICompletionProposalAcceptor acceptor) {
-    if (!(model instanceof BuiltInOption)) return;
-    BuiltInOption option = (BuiltInOption) model;
+  @Override public void completeBuiltInOption_Property(EObject model, Assignment assignment,
+      ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
     ProtoDescriptor descriptor = descriptorProvider.get();
-    Enum enumType = descriptor.enumTypeOf(option);
+    Collection<Property> optionProperties = descriptor.availableOptionPropertiesFor(model);
+    if (!optionProperties.isEmpty()) proposeOptions(optionProperties, context, acceptor);
+  }
+
+  private void proposeOptions(Collection<Property> optionProperties, ContentAssistContext context,
+      ICompletionProposalAcceptor acceptor) {
+    for (Property p : optionProperties) proposeOption(p, context, acceptor);
+  }
+
+  @Override public void completeBuiltInOption_Value(EObject model, Assignment assignment,
+      ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
+    BuiltInOption option = (BuiltInOption) model;
+    Property property = options.propertyFrom(option);
+    if (property == null) return;
+    ProtoDescriptor descriptor = descriptorProvider.get();
+    Enum enumType = descriptor.enumTypeOf(property);
     if (enumType != null) {
       proposeAndAccept(enumType, context, acceptor);
       return;
     }
-    Property fileOption = descriptor.lookupOption(option.getName());
-    if (fileOption == null) return;
-    if (properties.isString(fileOption)) {
-      proposeEmptyString(context, acceptor);
+    if (properties.isBool(property)) {
+      proposeBooleanValues(context, acceptor);
       return;
     }
-    if (properties.isBool(fileOption)) {
-      proposeBooleanValues(context, acceptor);
+    if (properties.isString(property)) {
+      proposeEmptyString(context, acceptor);
       return;
     }
   }
@@ -157,7 +126,10 @@
       }
       return true;
     }
-    if (OPENING_BRACKET.hasValue(keyword)) { return proposeOpenBracket(context, acceptor); }
+    if (OPENING_BRACKET.hasValue(keyword)) return proposeOpeningBracket(context, acceptor);
+    if (OPENING_CURLY_BRACKET.hasValue(keyword)) {
+      return context.getCurrentModel() instanceof Option;
+    }
     if (TRUE.hasValue(keyword) || FALSE.hasValue(keyword)) {
       if (isBoolProposalValid(context)) {
         proposeBooleanValues(context, acceptor);
@@ -187,8 +159,8 @@
     EObject model = context.getCurrentModel();
     if (model instanceof Property) return properties.isBool((Property) model);
     if (model instanceof Option) {
-      Property fileOption = descriptorProvider.get().lookupOption(((Option) model).getName());
-      return fileOption != null && properties.isBool(fileOption);
+      Property option = options.propertyFrom((Option) model);
+      return option != null && properties.isBool(option);
     }
     if (model instanceof FieldOption) {
       Property fileOption = descriptorProvider.get().lookupOption(((FieldOption) model).getName());
@@ -201,8 +173,8 @@
     EObject model = context.getCurrentModel();
     if (model instanceof Property) return properties.mayBeNan((Property) model);
     if (model instanceof Option) {
-      Property fileOption = descriptorProvider.get().lookupOption(((Option) model).getName());
-      return fileOption != null && properties.mayBeNan(fileOption);
+      Property option = options.propertyFrom((Option) model);
+      return option != null && properties.mayBeNan(option);
     }
     if (model instanceof FieldOption) {
       Property fileOption = descriptorProvider.get().lookupOption(((FieldOption) model).getName());
@@ -211,7 +183,7 @@
     return false;
   }
 
-  private boolean proposeOpenBracket(ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
+  private boolean proposeOpeningBracket(ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
     EObject model = context.getCurrentModel();
     if (!(model instanceof Property)) return false;
     Property p = (Property) model;
@@ -318,20 +290,20 @@
   }
 
   private void proposeCommonFieldOptions(Field field, ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
-    List<String> options = existingFieldOptionNames(field);
-    proposeDefaultKeyword(field, options, context, acceptor);
+    List<String> optionNames = existingFieldOptionNames(field);
+    proposeDefaultKeyword(field, optionNames, context, acceptor);
     for (Property option : descriptorProvider.get().fieldOptions()) {
       String optionName = option.getName();
-      if (options.contains(optionName) || ("packed".equals(optionName) && !canBePacked(field))) continue;
+      if (optionNames.contains(optionName) || ("packed".equals(optionName) && !canBePacked(field))) continue;
       proposeOption(option, context, acceptor);
     }
   }
 
   private List<String> existingFieldOptionNames(Field field) {
-    List<FieldOption> options = field.getFieldOptions();
-    if (options.isEmpty()) return emptyList();
+    List<FieldOption> allFieldOptions = field.getFieldOptions();
+    if (allFieldOptions.isEmpty()) return emptyList();
     List<String> optionNames = new ArrayList<String>();
-    for (FieldOption option : options)
+    for (FieldOption option : allFieldOptions)
       optionNames.add(option.getName());
     return optionNames;
   }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/documentation/SingleLineCommentDocumentationProvider.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/documentation/SingleLineCommentDocumentationProvider.java
index efda1a0..c042793 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/documentation/SingleLineCommentDocumentationProvider.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/documentation/SingleLineCommentDocumentationProvider.java
@@ -11,15 +11,15 @@
 import static com.google.eclipse.protobuf.util.CommonWords.space;
 import static org.eclipse.xtext.nodemodel.util.NodeModelUtils.getNode;
 
-import com.google.eclipse.protobuf.protobuf.*;
-import com.google.eclipse.protobuf.scoping.*;
-import com.google.eclipse.protobuf.util.ModelNodes;
-import com.google.inject.Inject;
-
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.xtext.documentation.IEObjectDocumentationProvider;
 import org.eclipse.xtext.nodemodel.*;
 
+import com.google.eclipse.protobuf.protobuf.*;
+import com.google.eclipse.protobuf.scoping.*;
+import com.google.eclipse.protobuf.util.*;
+import com.google.inject.Inject;
+
 /**
  * Provides single line comments of a protobuf element as its documentation when hovered.
  *
@@ -33,6 +33,7 @@
 
   @Inject private ProtoDescriptorProvider descriptorProvider;
   @Inject private ModelNodes nodes;
+  @Inject private Options options;
 
   public String getDocumentation(EObject o) {
     String comment = findComment(o);
@@ -56,8 +57,7 @@
 
   private EObject findRealTarget(EObject o) {
     if (o instanceof Option) {
-      ProtoDescriptor descriptor = descriptorProvider.get();
-      Property p = descriptor.lookupOption(((Option) o).getName());
+      Property p = options.propertyFrom((Option) o);
       return p != null ? p : o;
     }
     if (o instanceof FieldOption) {
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/syntaxcoloring/ProtobufSemanticHighlightingCalculator.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/syntaxcoloring/ProtobufSemanticHighlightingCalculator.java
index f256ca4..355f92d 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/syntaxcoloring/ProtobufSemanticHighlightingCalculator.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/syntaxcoloring/ProtobufSemanticHighlightingCalculator.java
@@ -30,6 +30,7 @@
 public class ProtobufSemanticHighlightingCalculator implements ISemanticHighlightingCalculator {
 
   @Inject private ModelNodes nodes;
+  @Inject private Options options;
   @Inject private FieldOptions fieldOptions;
 
   public void provideHighlightingFor(XtextResource resource, IHighlightedPositionAcceptor acceptor) {
@@ -199,7 +200,8 @@
   }
 
   private void highlight(Option option, IHighlightedPositionAcceptor acceptor) {
-    highlightName(option, acceptor, DEFAULT_ID);
+    Property property = options.propertyFrom(option);
+    if (property != null) highlightName(property, acceptor, DEFAULT_ID);
     ValueRef ref = option.getValue();
     if (ref instanceof LiteralRef) {
       highlightFirstFeature(option, OPTION__VALUE, acceptor, ENUM_LITERAL_ID);
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Labels.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Labels.java
index e486599..df19786 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Labels.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Labels.java
@@ -1,10 +1,10 @@
 /*
  * Copyright (c) 2011 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.ui.labeling;
@@ -12,26 +12,30 @@
 import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.IMPORT__IMPORT_URI;
 import static org.eclipse.jface.viewers.StyledString.DECORATIONS_STYLER;
 
-import com.google.eclipse.protobuf.protobuf.*;
-import com.google.eclipse.protobuf.util.ModelNodes;
-import com.google.eclipse.protobuf.util.Properties;
-import com.google.inject.*;
-
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.jface.viewers.StyledString;
 import org.eclipse.xtext.nodemodel.INode;
 
+import com.google.eclipse.protobuf.protobuf.*;
+import com.google.eclipse.protobuf.util.*;
+import com.google.inject.*;
+
 /**
  * Registry of commonly used text in the 'Protocol Buffer' editor.
- * 
+ *
  * @author alruiz@google.com (Alex Ruiz)
  */
 @Singleton public class Labels {
 
   @Inject private ModelNodes nodes;
+  @Inject private Options options;
   @Inject private Properties properties;
 
   public Object labelFor(Object o) {
+    if (o instanceof Option) {
+      Option option = (Option) o;
+      return labelFor(option);
+    }
     if (o instanceof ExtendMessage) {
       ExtendMessage extend = (ExtendMessage) o;
       return labelFor(extend);
@@ -59,13 +63,18 @@
     return null;
   }
 
-  private Object labelFor(ExtendMessage extend) {
-    return messageName(extend.getMessage());
+  private Object labelFor(Option o) {
+    Property p = options.propertyFrom(o);
+    return p == null ? null : p.getName();
   }
 
-  private Object labelFor(Extensions extensions) {
+  private Object labelFor(ExtendMessage e) {
+    return messageName(e.getMessage());
+  }
+
+  private Object labelFor(Extensions e) {
     StringBuilder builder = new StringBuilder();
-    EList<Range> ranges = extensions.getRanges();
+    EList<Range> ranges = e.getRanges();
     int rangeCount = ranges.size();
     for (int i = 0; i < rangeCount; i++) {
       if (i > 0) builder.append(", ");
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/Messages.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/Messages.java
index 1b729fe..2996518 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/Messages.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/Messages.java
@@ -14,10 +14,9 @@
  * @author alruiz@google.com (Alex Ruiz)
  */
 public class Messages extends NLS {
-  
+
   public static String importDeclarations;
-  public static String optionDeclarations;
-  
+
   static {
     Class<Messages> targetType = Messages.class;
     NLS.initializeMessages(targetType.getName(), targetType);
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/Messages.properties b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/Messages.properties
index fbd7d57..9d709ca 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/Messages.properties
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/Messages.properties
@@ -1,2 +1 @@
 importDeclarations=import declarations
-optionDeclarations=option declarations
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/OutlineViewModel.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/OutlineViewModel.java
index 7bfb540..231fab9 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/OutlineViewModel.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/OutlineViewModel.java
@@ -24,7 +24,7 @@
 @SuppressWarnings("restriction")
 class OutlineViewModel {
 
-  private static final Class<?>[] GROUP_TYPES = { Package.class, Import.class, Option.class, ProtobufElement.class };
+  private static final Class<?>[] GROUP_TYPES = { Package.class, Import.class, ProtobufElement.class };
 
   private final Map<Class<?>, List<EObject>> elements = newLinkedHashMap();
 
@@ -51,10 +51,6 @@
     return elementsOfType(Import.class);
   }
 
-  List<EObject> options() {
-    return elementsOfType(Option.class);
-  }
-
   List<EObject> remainingElements() {
     return elementsOfType(ProtobufElement.class);
   }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/ProtobufOutlineTreeProvider.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/ProtobufOutlineTreeProvider.java
index ec63990..5dd7d5d 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/ProtobufOutlineTreeProvider.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/outline/ProtobufOutlineTreeProvider.java
@@ -8,7 +8,7 @@
  */
 package com.google.eclipse.protobuf.ui.outline;
 
-import static com.google.eclipse.protobuf.ui.outline.Messages.*;
+import static com.google.eclipse.protobuf.ui.outline.Messages.importDeclarations;
 
 import java.util.*;
 
@@ -32,7 +32,7 @@
     IGNORED_ELEMENT_TYPES.add(FieldOption.class);
     IGNORED_ELEMENT_TYPES.add(MessageRef.class);
   }
-  
+
   boolean _isLeaf(Extensions e) {
     return true;
   }
@@ -49,7 +49,6 @@
     OutlineViewModel model = new OutlineViewModel(protobuf);
     for (EObject aPackage : model.packages()) createNode(parent, aPackage);
     addGroup(parent, protobuf, model.imports(), "imports", importDeclarations);
-    addGroup(parent, protobuf, model.options(), "options", optionDeclarations);
     for (EObject e : model.remainingElements()) {
       createNode(parent, e);
     }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext
index d524c02..84ba83c 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext
@@ -38,15 +38,6 @@
 QualifiedName:
   '.'? Name ('.' Name)*;
 
-Option:
-  BuiltInOption | CustomOption;
-
-BuiltInOption:
-  'option' name=Name '=' value=ValueRef ';';
-
-CustomOption:
-  'option' '(' name=QualifiedName ')' '=' value=ValueRef ';';
-
 Type:
   Message | Enum;
 
@@ -56,7 +47,7 @@
   '}' (';')?;
 
 MessageElement:
-  Extensions | Type | Field | ExtendMessage | Option;
+  Option | Extensions | Type | Field | ExtendMessage;
 
 Extensions:
   'extensions' ranges+=Range (',' ranges+=Range)* ';';
@@ -77,7 +68,7 @@
   '}' (';')?;
 
 GroupElement:
-  Field | Option | Enum;
+  Option | Field | Enum;
 
 Property:
   modifier=Modifier type=AbstractTypeRef name=Name '=' index=(LONG | HEX)
@@ -209,4 +200,16 @@
 
 MessageRef:
   type=[Message|QualifiedName];
+
+Option:
+  BuiltInOption | CustomOption;
+
+BuiltInOption:
+  'option' property=PropertyRef '=' value=ValueRef ';';
+
+CustomOption:
+  'option' '(' property=PropertyRef ')' '=' value=ValueRef ';';
+
+PropertyRef:
+  property=[Property|QualifiedName];
   
\ No newline at end of file
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/formatting/ProtobufFormatter.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/formatting/ProtobufFormatter.java
index f22f18f..932a909 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/formatting/ProtobufFormatter.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/formatting/ProtobufFormatter.java
@@ -13,8 +13,7 @@
 import static com.google.eclipse.protobuf.util.CommonWords.space;
 
 import org.eclipse.xtext.Keyword;
-import org.eclipse.xtext.formatting.impl.AbstractDeclarativeFormatter;
-import org.eclipse.xtext.formatting.impl.FormattingConfig;
+import org.eclipse.xtext.formatting.impl.*;
 
 import com.google.eclipse.protobuf.services.ProtobufGrammarAccess;
 
@@ -43,11 +42,11 @@
     for (Keyword k : g.findKeywords(SEMICOLON.toString())) {
       c.setNoSpace().before(k);
     }
-    for (Keyword k : g.findKeywords("{")) {
+    for (Keyword k : g.findKeywords(OPENING_CURLY_BRACKET.toString())) {
       c.setIndentationIncrement().after(k);
       c.setLinewrap(1).after(k);
     }
-    for (Keyword k : g.findKeywords("}")) {
+    for (Keyword k : g.findKeywords(CLOSING_CURLY_BRACKET.toString())) {
       c.setIndentationDecrement().before(k);
       c.setLinewrap(2).after(k);
     }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/grammar/CommonKeyword.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/grammar/CommonKeyword.java
index 4d6fb11..921af4f 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/grammar/CommonKeyword.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/grammar/CommonKeyword.java
@@ -15,13 +15,13 @@
  */
 public enum CommonKeyword {
 
-  // we used to get keywords from IGrammarAccess. The problem was that we still had to hardcode the keyword we were
+  // we used to get keywords from IGrammarAccess. The problem was that we still had to hard-code the keyword we were
   // looking for. The code was too complicated and if the grammar changed for some reason, we had to change our
   // implementation anyway.
 
-  BOOL("bool"), TRUE("true"), FALSE("false"), BYTES("bytes"), OPENING_BRACKET("["), CLOSING_BRACKET("]"), 
-    DEFAULT("default"), EQUAL("="), SEMICOLON(";"), STRING("string"), SYNTAX("syntax"), NAN("nan"), FLOAT("float"), 
-    DOUBLE("double");
+  BOOL("bool"), TRUE("true"), FALSE("false"), BYTES("bytes"), OPENING_BRACKET("["), CLOSING_BRACKET("]"),
+      OPENING_CURLY_BRACKET("{"), CLOSING_CURLY_BRACKET("}"), DEFAULT("default"), EQUAL("="), SEMICOLON(";"),
+      STRING("string"), SYNTAX("syntax"), NAN("nan"), FLOAT("float"), DOUBLE("double");
 
   private final String value;
 
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ImportedNamesProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ImportedNamesProvider.java
index a45374c..d0eea21 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ImportedNamesProvider.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ImportedNamesProvider.java
@@ -8,21 +8,20 @@
  */
 package com.google.eclipse.protobuf.scoping;
 
-import static com.google.eclipse.protobuf.scoping.QualifiedNames.addPackageNameSegments;
 import static java.util.Collections.*;
 import static org.eclipse.xtext.util.SimpleAttributeResolver.newResolver;
 import static org.eclipse.xtext.util.Strings.isEmpty;
 import static org.eclipse.xtext.util.Tuples.pair;
 
-import com.google.common.base.Function;
-import com.google.eclipse.protobuf.util.ProtobufElementFinder;
-import com.google.inject.*;
+import java.util.*;
 
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.xtext.naming.*;
 import org.eclipse.xtext.util.*;
 
-import java.util.*;
+import com.google.common.base.Function;
+import com.google.eclipse.protobuf.util.ProtobufElementFinder;
+import com.google.inject.*;
 
 /**
  * Provides alternative qualified names for imported protobuf elements.
@@ -35,6 +34,7 @@
   @Inject private final IQualifiedNameConverter converter = new IQualifiedNameConverter.DefaultImpl();
 
   @Inject private ProtobufElementFinder finder;
+  @Inject private QualifiedNames qualifiedNames;
 
   private final Function<EObject, String> resolver = newResolver(String.class, "name");
 
@@ -48,7 +48,7 @@
         if (isEmpty(name)) return emptyList();
         QualifiedName qualifiedName = converter.toQualifiedName(name);
         allNames.add(qualifiedName);
-        allNames.addAll(addPackageNameSegments(qualifiedName, finder.packageOf(obj), converter));
+        allNames.addAll(qualifiedNames.addPackageNameSegments(qualifiedName, finder.packageOf(obj)));
         return unmodifiableList(allNames);
       }
     });
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/LiteralDescriptions.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/LiteralDescriptions.java
new file mode 100644
index 0000000..0b847f3
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/LiteralDescriptions.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2011 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 java.util.Collections.emptyList;
+import static org.eclipse.xtext.EcoreUtil2.getAllContentsOfType;
+import static org.eclipse.xtext.resource.EObjectDescription.create;
+
+import java.util.*;
+
+import org.eclipse.xtext.resource.IEObjectDescription;
+
+import com.google.eclipse.protobuf.protobuf.*;
+import com.google.eclipse.protobuf.protobuf.Enum;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class LiteralDescriptions {
+
+  Collection<IEObjectDescription> literalsOf(Enum anEnum) {
+    if (anEnum == null) return emptyList();
+    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
+    for (Literal literal : getAllContentsOfType(anEnum, Literal.class))
+      descriptions.add(create(literal.getName(), literal));
+    return descriptions;
+  }
+}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/LocalNamesProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/LocalNamesProvider.java
index e24a2e8..8c19341 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/LocalNamesProvider.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/LocalNamesProvider.java
@@ -8,21 +8,20 @@
  */
 package com.google.eclipse.protobuf.scoping;
 
-import static com.google.eclipse.protobuf.scoping.QualifiedNames.addPackageNameSegments;
 import static java.util.Collections.*;
 import static org.eclipse.xtext.util.SimpleAttributeResolver.newResolver;
 import static org.eclipse.xtext.util.Strings.isEmpty;
 import static org.eclipse.xtext.util.Tuples.pair;
 
-import com.google.common.base.Function;
-import com.google.eclipse.protobuf.util.ProtobufElementFinder;
-import com.google.inject.*;
+import java.util.*;
 
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.xtext.naming.*;
 import org.eclipse.xtext.util.*;
 
-import java.util.*;
+import com.google.common.base.Function;
+import com.google.eclipse.protobuf.util.ProtobufElementFinder;
+import com.google.inject.*;
 
 /**
  * Provides alternative qualified names for protobuf elements.
@@ -61,6 +60,7 @@
   @Inject private final IQualifiedNameConverter converter = new IQualifiedNameConverter.DefaultImpl();
 
   @Inject private ProtobufElementFinder finder;
+  @Inject private QualifiedNames qualifiedNames;
 
   private final Function<EObject, String> resolver = newResolver(String.class, "name");
 
@@ -81,7 +81,7 @@
           qualifiedName = converter.toQualifiedName(containerName).append(qualifiedName);
           allNames.add(qualifiedName);
         }
-        allNames.addAll(addPackageNameSegments(qualifiedName, finder.packageOf(obj), converter));
+        allNames.addAll(qualifiedNames.addPackageNameSegments(qualifiedName, finder.packageOf(obj)));
         return unmodifiableList(allNames);
       }
     });
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/OptionDescriptions.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/OptionDescriptions.java
new file mode 100644
index 0000000..716232e
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/OptionDescriptions.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2011 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.scoping.OptionType.*;
+import static java.util.Collections.emptyList;
+import static org.eclipse.xtext.resource.EObjectDescription.create;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.xtext.resource.IEObjectDescription;
+
+import com.google.eclipse.protobuf.protobuf.*;
+import com.google.eclipse.protobuf.protobuf.Enum;
+import com.google.inject.Inject;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class OptionDescriptions {
+
+  private static final Map<Class<?>, OptionType> OPTION_TYPES_BY_CONTAINER = new HashMap<Class<?>, OptionType>();
+
+  static {
+    OPTION_TYPES_BY_CONTAINER.put(Protobuf.class, FILE);
+    OPTION_TYPES_BY_CONTAINER.put(Enum.class, ENUM);
+    OPTION_TYPES_BY_CONTAINER.put(Message.class, MESSAGE);
+    OPTION_TYPES_BY_CONTAINER.put(Service.class, SERVICE);
+    OPTION_TYPES_BY_CONTAINER.put(Rpc.class, RPC);
+  }
+
+  @Inject private ProtoDescriptorProvider descriptorProvider;
+  @Inject private LocalNamesProvider localNamesProvider;
+  @Inject private QualifiedNameDescriptions qualifiedNamesDescriptions;
+
+  Collection <IEObjectDescription> builtInOptionProperties(BuiltInOption option) {
+    ProtoDescriptor descriptor = descriptorProvider.get();
+    Collection<Property> properties = descriptor.availableOptionPropertiesFor(option.eContainer());
+    if (!properties.isEmpty()) return describe(properties);
+    return emptyList();
+  }
+
+  Collection <IEObjectDescription> localCustomOptionProperties(EObject root, CustomOption option) {
+    return localCustomOptionProperties(root, option, 0);
+  }
+
+  private  Collection <IEObjectDescription> localCustomOptionProperties(EObject root, CustomOption option, int level) {
+    OptionType optionType = optionType(option);
+    if (optionType == null) return emptyList();
+    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
+    for (EObject element : root.eContents()) {
+      if (isExtendingOptionMessage(element, optionType)) {
+        ExtendMessage extend = (ExtendMessage) element;
+        for (MessageElement e : extend.getElements()) {
+          if (!(e instanceof Property)) continue;
+          List<QualifiedName> names = localNamesProvider.namesOf(e);
+          int nameCount = names.size();
+          for (int i = level; i < nameCount; i++) {
+            descriptions.add(create(names.get(i), e));
+          }
+          descriptions.addAll(qualifiedNamesDescriptions.qualifiedNames(e));
+        }
+        continue;
+      }
+      if (element instanceof Message) {
+        descriptions.addAll(localCustomOptionProperties(element, option, level + 1));
+      }
+    }
+    return descriptions;
+  }
+
+  private OptionType optionType(CustomOption option) {
+    EObject container = option.eContainer();
+    for (Entry<Class<?>, OptionType> optionTypeByContainer : OPTION_TYPES_BY_CONTAINER.entrySet()) {
+      if (optionTypeByContainer.getKey().isInstance(container)) {
+        return optionTypeByContainer.getValue();
+      }
+    }
+    return null;
+  }
+
+  private boolean isExtendingOptionMessage(EObject o, OptionType optionType) {
+    if (!(o instanceof ExtendMessage)) return false;
+    Message message = messageFrom((ExtendMessage) o);
+    if (message == null) return false;
+    return optionType.messageName.equals(message.getName());
+  }
+
+  private Message messageFrom(ExtendMessage extend) {
+    MessageRef ref = extend.getMessage();
+    return ref == null ? null : ref.getType();
+  }
+
+  private Collection<IEObjectDescription> describe(Collection<Property> properties) {
+    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
+    for (Property p : properties) {
+      descriptions.add(create(p.getName(), p));
+    }
+    return descriptions;
+  }
+}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/OptionType.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/OptionType.java
index d7592c6..5bfa135 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/OptionType.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/OptionType.java
@@ -8,6 +8,16 @@
  */
 package com.google.eclipse.protobuf.scoping;
 
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
 enum OptionType {
-  FILE, MESSAGE, FIELD, ENUM, METHOD;
+  FILE("FileOptions"), MESSAGE("MessageOptions"), FIELD("FieldOptions"), ENUM("EnumOptions"),
+      ENUM_LITERAL("EnumValueOptions"), SERVICE("ServiceOptions"), RPC("MethodOptions");
+
+  final String messageName;
+
+  private OptionType(String messageName) {
+    this.messageName = messageName;
+  }
 }
\ No newline at end of file
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor.java
index a09197c..fc0ca3e 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor.java
@@ -22,9 +22,9 @@
 import java.util.*;
 
 import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
 import org.eclipse.xtext.nodemodel.INode;
-import org.eclipse.xtext.parser.IParseResult;
-import org.eclipse.xtext.parser.IParser;
+import org.eclipse.xtext.parser.*;
 import org.eclipse.xtext.resource.XtextResource;
 
 import com.google.eclipse.protobuf.protobuf.*;
@@ -46,11 +46,13 @@
   private static final Map<String, OptionType> OPTION_DEFINITION_BY_NAME = new HashMap<String, OptionType>();
 
   static {
-    OPTION_DEFINITION_BY_NAME.put("FileOptions", FILE);
-    OPTION_DEFINITION_BY_NAME.put("MessageOptions", MESSAGE);
-    OPTION_DEFINITION_BY_NAME.put("FieldOptions", FIELD);
-    OPTION_DEFINITION_BY_NAME.put("EnumOptions", ENUM);
-    OPTION_DEFINITION_BY_NAME.put("MethodOptions", METHOD);
+    addOptionTypes(FILE, MESSAGE, FIELD, ENUM);
+  }
+
+  private static void addOptionTypes(OptionType...types) {
+    for (OptionType type : types) {
+      OPTION_DEFINITION_BY_NAME.put(type.messageName, type);
+    }
   }
 
   private final List<Type> allTypes = new ArrayList<Type>();
@@ -131,6 +133,20 @@
   }
 
   /**
+   * Returns the options available for the given option container. For example, if the given object is an
+   * <code>{@link Enum}</code>, this method will return <code>{@link #enumOptions()}</code>.
+   * @param optionContainer the given container of an option.
+   * @return the options available for the given option container, or an empty collection if the are not any
+   * options available for the given option container.
+   */
+  public Collection<Property> availableOptionPropertiesFor(EObject optionContainer) {
+    if (optionContainer instanceof Protobuf) return fileOptions();
+    if (optionContainer instanceof Enum) return enumOptions();
+    if (optionContainer instanceof Message) return messageOptions();
+    return emptyList();
+  }
+
+  /**
    * Returns all the file-level options available. These are the options defined in
    * {@code google/protobuf/descriptor.proto} (more details can be found
    * <a href=http://code.google.com/apis/protocolbuffers/docs/proto.html#options" target="_blank">here</a>.)
@@ -148,7 +164,7 @@
    * @return the option whose name matches the given one or {@code null} if a matching option is not found.
    */
   public Property lookupOption(String name) {
-    return lookupOption(name, FILE, MESSAGE, ENUM, METHOD);
+    return lookupOption(name, FILE, MESSAGE, ENUM);
   }
 
   private Property lookupOption(String name, OptionType...types) {
@@ -215,24 +231,19 @@
    * @param option the given option.
    * @return the enum type of the given option or {@code null} if the type of the given option is not enum.
    */
-  public Enum enumTypeOf(BuiltInOption option) {
-    String name = option.getName();
-    return enumTypeOf(lookupOption(name));
-  }
-
-  /**
-   * Returns the enum type of the given option, only if the given option is defined in
-   * {@code google/protobuf/descriptor.proto} and its type is enum (more details can be found <a
-   * href=http://code.google.com/apis/protocolbuffers/docs/proto.html#options" target="_blank">here</a>.)
-   * @param option the given option.
-   * @return the enum type of the given option or {@code null} if the type of the given option is not enum.
-   */
   public Enum enumTypeOf(BuiltInFieldOption option) {
     String name = option.getName();
     return enumTypeOf(lookupFieldOption(name));
   }
 
-  private Enum enumTypeOf(Property p) {
+  /**
+   * Returns the enum type of the given property, only if the given property is defined in
+   * {@code google/protobuf/descriptor.proto} and its type is enum (more details can be found <a
+   * href=http://code.google.com/apis/protocolbuffers/docs/proto.html#options" target="_blank">here</a>.)
+   * @param p the given property.
+   * @return the enum type of the given property or {@code null} if the type of the given property is not enum.
+   */
+  public Enum enumTypeOf(Property p) {
     if (p == null) return null;
     INode node = nodes.firstNodeForFeature(p, PROPERTY__TYPE);
     if (node == null) return null;
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 67accec..be4bcc9 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,33 +8,16 @@
  */
 package com.google.eclipse.protobuf.scoping;
 
-import static com.google.eclipse.protobuf.scoping.QualifiedNames.addLeadingDot;
-import static java.util.Collections.emptyList;
-import static org.eclipse.emf.common.util.URI.createURI;
-import static org.eclipse.emf.ecore.util.EcoreUtil.getAllContents;
-import static org.eclipse.xtext.EcoreUtil2.getAllContentsOfType;
-import static org.eclipse.xtext.resource.EObjectDescription.create;
-
 import java.util.*;
 
-import org.eclipse.emf.common.util.TreeIterator;
-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.xtext.naming.IQualifiedNameProvider;
-import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.emf.ecore.*;
 import org.eclipse.xtext.resource.IEObjectDescription;
-import org.eclipse.xtext.resource.XtextResource;
 import org.eclipse.xtext.scoping.IScope;
 import org.eclipse.xtext.scoping.impl.*;
 
 import com.google.eclipse.protobuf.protobuf.*;
 import com.google.eclipse.protobuf.protobuf.Enum;
-import com.google.eclipse.protobuf.protobuf.Package;
-import com.google.eclipse.protobuf.util.FieldOptions;
-import com.google.eclipse.protobuf.util.ProtobufElementFinder;
+import com.google.eclipse.protobuf.util.*;
 import com.google.inject.Inject;
 
 /**
@@ -48,208 +31,77 @@
 
   private static final boolean DO_NOT_IGNORE_CASE = false;
 
+  @Inject private ProtoDescriptorProvider descriptorProvider;
   @Inject private FieldOptions fieldOptions;
   @Inject private ProtobufElementFinder finder;
-  @Inject private ProtoDescriptorProvider descriptorProvider;
-  @Inject private IQualifiedNameProvider nameProvider;
-  @Inject private ImportUriResolver uriResolver;
-  @Inject private LocalNamesProvider localNamesProvider;
-  @Inject private ImportedNamesProvider importedNamesProvider;
-  @Inject private PackageResolver packageResolver;
+  @Inject private LiteralDescriptions literalDescriptions;
+  @Inject private OptionDescriptions optionDescriptions;
+  @Inject private Options options;
+  @Inject private TypeDescriptions typeDescriptions;
 
   @SuppressWarnings("unused")
   IScope scope_TypeRef_type(TypeRef typeRef, EReference reference) {
     Protobuf root = finder.rootOf(typeRef);
     Set<IEObjectDescription> descriptions = new HashSet<IEObjectDescription>();
     EObject current = typeRef.eContainer().eContainer(); // get message of the property containing the TypeReference
+    Class<Type> targetType = Type.class;
     while (current != null) {
-      descriptions.addAll(typesIn(current));
+      descriptions.addAll(typeDescriptions.localTypes(current, targetType));
       current = current.eContainer();
     }
-    descriptions.addAll(importedTypes(root, Type.class));
+    descriptions.addAll(typeDescriptions.importedTypes(root, targetType));
     return createScope(descriptions);
   }
 
-  private Collection<IEObjectDescription> typesIn(EObject root) {
-    return children(root, Type.class);
-  }
-
   @SuppressWarnings("unused")
-  IScope scope_MessageRef_type(MessageRef msgRef, EReference reference) {
-    Protobuf root = finder.rootOf(msgRef);
+  IScope scope_MessageRef_type(MessageRef messageRef, EReference reference) {
+    Protobuf root = finder.rootOf(messageRef);
     Set<IEObjectDescription> descriptions = new HashSet<IEObjectDescription>();
-    descriptions.addAll(messagesIn(root));
-    descriptions.addAll(importedTypes(root, Message.class));
+    Class<Message> targetType = Message.class;
+    descriptions.addAll(typeDescriptions.localTypes(root, targetType));
+    descriptions.addAll(typeDescriptions.importedTypes(root, targetType));
     return createScope(descriptions);
   }
 
-  private Collection<IEObjectDescription> messagesIn(Protobuf root) {
-    return children(root, Message.class);
-  }
-
-  private <T extends Type> Collection<IEObjectDescription> children(EObject root, Class<T> targetType) {
-    return children(root, targetType, 0);
-  }
-
-  private <T extends Type> Collection<IEObjectDescription> children(EObject root, Class<T> targetType, int level) {
-    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
-    for (EObject element : root.eContents()) {
-      if (!targetType.isInstance(element)) continue;
-      List<QualifiedName> names = localNamesProvider.namesOf(element);
-      int nameCount = names.size();
-      for (int i = level; i < nameCount; i++) {
-        descriptions.add(create(names.get(i), element));
-      }
-      descriptions.addAll(fullyQualifiedNamesOf(element));
-      // TODO investigate if groups can have messages, and if so, add those messages to the scope.
-      if (element instanceof Message) {
-        descriptions.addAll(children(element, targetType, level + 1));
-      }
-    }
-    return descriptions;
-  }
-
-  private <T extends Type> Collection<IEObjectDescription> importedTypes(Protobuf root, Class<T> targetType) {
-    List<Import> allImports = finder.importsIn(root);
-    if (allImports.isEmpty()) return emptyList();
-    return importedTypes(allImports, finder.packageOf(root), targetType);
-  }
-
-  private <T extends Type> Collection<IEObjectDescription> importedTypes(List<Import> allImports, Package aPackage,
-      Class<T> targetType) {
-    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
-    for (Import anImport : allImports) {
-      if (isImportingDescriptor(anImport)) {
-        descriptions.addAll(allBuiltInTypes(targetType));
-        continue;
-      }
-      Resource importedResource = importedResourceFrom(anImport);
-      Protobuf importedRoot = rootElementOf(importedResource);
-      if (importedRoot != null) {
-        descriptions.addAll(publicImportedTypes(importedRoot, targetType));
-        if (arePackagesRelated(aPackage, importedRoot)) {
-          descriptions.addAll(typesIn(importedRoot));
-          continue;
-        }
-      }
-      descriptions.addAll(children(importedResource, targetType));
-    }
-    return descriptions;
-  }
-
-  private boolean isImportingDescriptor(Import anImport) {
-    String descriptorLocation = descriptorProvider.descriptorLocation().toString();
-    return descriptorLocation.equals(anImport.getImportURI());
-  }
-
-  private <T extends Type> Collection<IEObjectDescription> allBuiltInTypes(Class<T> targetType) {
-    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
-    ProtoDescriptor descriptor = descriptorProvider.get();
-    for (Type t : descriptor.allTypes()) {
-      if (!targetType.isInstance(t)) continue;
-      T type = targetType.cast(t);
-      descriptions.addAll(fullyQualifiedNamesOf(type));
-    }
-    return descriptions;
-  }
-
-  private <T extends Type> Collection<IEObjectDescription> publicImportedTypes(Protobuf root, Class<T> targetType) {
-    List<Import> allImports = finder.publicImportsIn(root);
-    if (allImports.isEmpty()) return emptyList();
-    return importedTypes(allImports, finder.packageOf(root), targetType);
-  }
-
-  private Resource importedResourceFrom(Import anImport) {
-    ResourceSet resourceSet = finder.rootOf(anImport).eResource().getResourceSet();
-    URI importUri = createURI(uriResolver.apply(anImport));
-    try {
-      return resourceSet.getResource(importUri, true);
-    } catch (Throwable t) {
-      return null;
-    }
-  }
-
-  private Protobuf rootElementOf(Resource resource) {
-    if (resource instanceof XtextResource) {
-      EObject root = ((XtextResource) resource).getParseResult().getRootASTElement();
-      return (Protobuf) root;
-    }
-    TreeIterator<Object> contents = getAllContents(resource, true);
-    if (contents.hasNext()) {
-      Object next = contents.next();
-      if (next instanceof Protobuf) return (Protobuf) next;
-    }
-    return null;
-  }
-
-  private boolean arePackagesRelated(Package aPackage, EObject root) {
-    Package p = finder.packageOf(root);
-    return packageResolver.areRelated(aPackage, p);
-  }
-
-  private <T extends Type> Collection<IEObjectDescription> children(Resource resource, Class<T> targetType) {
-    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
-    TreeIterator<Object> contents = getAllContents(resource, true);
-    while (contents.hasNext()) {
-      Object next = contents.next();
-      if (!targetType.isInstance(next)) continue;
-      T type = targetType.cast(next);
-      descriptions.addAll(fullyQualifiedNamesOf(type));
-      for (QualifiedName name : importedNamesProvider.namesOf(type)) {
-        descriptions.add(create(name, type));
-      }
-    }
-    return descriptions;
-  }
-
-  private Collection<IEObjectDescription> fullyQualifiedNamesOf(EObject obj) {
-    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
-    QualifiedName fqn = nameProvider.getFullyQualifiedName(obj);
-    descriptions.add(create(fqn, obj));
-    descriptions.add(create(addLeadingDot(fqn), obj));
-    return descriptions;
-  }
-
   @SuppressWarnings("unused")
   IScope scope_LiteralRef_literal(LiteralRef literalRef, EReference reference) {
     EObject container = literalRef.eContainer();
+    Enum anEnum = null;
+    if (container instanceof BuiltInOption) {
+      ProtoDescriptor descriptor = descriptorProvider.get();
+      Property p = options.propertyFrom((Option) container);
+      anEnum = descriptor.enumTypeOf(p);
+    }
     if (container instanceof Property) {
-      Enum anEnum = finder.enumTypeOf((Property) container);
-      if (anEnum != null) return scopeForLiterals(anEnum);
+      anEnum = finder.enumTypeOf((Property) container);
     }
-    Enum anEnum = enumTypeOfOption(container);
-    if (anEnum != null) return scopeForLiterals(anEnum);
-    return null;
-  }
-
-  private Enum enumTypeOfOption(EObject mayBeOption) {
-    ProtoDescriptor descriptor = descriptorProvider.get();
-    if (mayBeOption instanceof BuiltInOption) {
-      return descriptor.enumTypeOf((BuiltInOption) mayBeOption);
-    }
-    if (mayBeOption instanceof BuiltInFieldOption) {
-      BuiltInFieldOption option = (BuiltInFieldOption) mayBeOption;
+    if (container instanceof BuiltInFieldOption) {
+      BuiltInFieldOption option = (BuiltInFieldOption) container;
       if (fieldOptions.isDefaultValueOption(option)) {
         Property property = (Property) option.eContainer();
-        return finder.enumTypeOf(property);
+        anEnum = finder.enumTypeOf(property);
+      } else {
+        ProtoDescriptor descriptor = descriptorProvider.get();
+        anEnum = descriptor.enumTypeOf(option);
       }
-      return descriptor.enumTypeOf(option);
     }
-    return null;
+    return createScope(literalDescriptions.literalsOf(anEnum));
   }
 
-  private static IScope scopeForLiterals(Enum anEnum) {
-    Collection<IEObjectDescription> descriptions = describeLiterals(anEnum);
+  @SuppressWarnings("unused")
+  IScope scope_PropertyRef_property(PropertyRef propertyRef, EReference reference) {
+    Set<IEObjectDescription> descriptions = new HashSet<IEObjectDescription>();
+    EObject mayBeOption = propertyRef.eContainer();
+    if (mayBeOption instanceof BuiltInOption) {
+      descriptions.addAll(optionDescriptions.builtInOptionProperties((BuiltInOption) mayBeOption));
+    }
+    if (mayBeOption instanceof CustomOption) {
+      Protobuf root = finder.rootOf(propertyRef);
+      descriptions.addAll(optionDescriptions.localCustomOptionProperties(root, (CustomOption) mayBeOption));
+    }
     return createScope(descriptions);
   }
 
-  private static Collection<IEObjectDescription> describeLiterals(Enum anEnum) {
-    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
-    for (Literal literal : getAllContentsOfType(anEnum, Literal.class))
-      descriptions.add(create(literal.getName(), literal));
-    return descriptions;
-  }
-
   private static IScope createScope(Iterable<IEObjectDescription> descriptions) {
     return new SimpleScope(descriptions, DO_NOT_IGNORE_CASE);
   }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/QualifiedNameDescriptions.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/QualifiedNameDescriptions.java
new file mode 100644
index 0000000..2a22297
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/QualifiedNameDescriptions.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2011 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 org.eclipse.xtext.resource.EObjectDescription.create;
+
+import java.util.*;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.xtext.naming.*;
+import org.eclipse.xtext.resource.IEObjectDescription;
+
+import com.google.inject.Inject;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class QualifiedNameDescriptions {
+
+  @Inject private IQualifiedNameProvider nameProvider;
+  @Inject private QualifiedNames qualifiedNames;
+
+  Collection<IEObjectDescription> qualifiedNames(EObject obj) {
+    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
+    QualifiedName fqn = nameProvider.getFullyQualifiedName(obj);
+    descriptions.add(create(fqn, obj));
+    descriptions.add(create(qualifiedNames.addLeadingDot(fqn), obj));
+    return descriptions;
+  }
+}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/QualifiedNames.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/QualifiedNames.java
index 23c73cb..eb5bdc0 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/QualifiedNames.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/QualifiedNames.java
@@ -16,13 +16,16 @@
 import org.eclipse.xtext.naming.*;
 
 import com.google.eclipse.protobuf.protobuf.Package;
+import com.google.inject.Inject;
 
 /**
  * @author alruiz@google.com (Alex Ruiz)
  */
-final class QualifiedNames {
+class QualifiedNames {
 
-  static QualifiedName addLeadingDot(QualifiedName name) {
+  @Inject private final IQualifiedNameConverter converter = new IQualifiedNameConverter.DefaultImpl();
+
+  QualifiedName addLeadingDot(QualifiedName name) {
     if (name.getFirstSegment().equals("")) return name;
     List<String> segments = new ArrayList<String>();
     segments.addAll(name.getSegments());
@@ -30,9 +33,9 @@
     return QualifiedName.create(segments.toArray(new String[segments.size()]));
   }
 
-  static List<QualifiedName> addPackageNameSegments(QualifiedName name, Package p, IQualifiedNameConverter converter) {
+  List<QualifiedName> addPackageNameSegments(QualifiedName name, Package p) {
     QualifiedName current = name;
-    List<String> segments = fqnSegments(p, converter);
+    List<String> segments = fqnSegments(p);
     int segmentCount = segments.size();
     if (segmentCount <= 1) return emptyList();
     List<QualifiedName> allNames = new ArrayList<QualifiedName>();
@@ -43,12 +46,10 @@
     return unmodifiableList(allNames);
   }
 
-  static private List<String> fqnSegments(Package p, IQualifiedNameConverter converter) {
+  private List<String> fqnSegments(Package p) {
     if (p == null) return emptyList();
     String packageName = p.getName();
     if (isEmpty(packageName)) return emptyList();
     return converter.toQualifiedName(packageName).getSegments();
   }
-
-  private QualifiedNames() {}
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/TypeDescriptions.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/TypeDescriptions.java
new file mode 100644
index 0000000..5082138
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/TypeDescriptions.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2011 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 java.util.Collections.emptyList;
+import static org.eclipse.emf.common.util.URI.createURI;
+import static org.eclipse.emf.ecore.util.EcoreUtil.getAllContents;
+import static org.eclipse.xtext.resource.EObjectDescription.create;
+
+import java.util.*;
+
+import org.eclipse.emf.common.util.*;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.*;
+import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.xtext.resource.*;
+import org.eclipse.xtext.scoping.impl.ImportUriResolver;
+
+import com.google.eclipse.protobuf.protobuf.*;
+import com.google.eclipse.protobuf.protobuf.Package;
+import com.google.eclipse.protobuf.util.ProtobufElementFinder;
+import com.google.inject.Inject;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class TypeDescriptions {
+
+  @Inject private ProtoDescriptorProvider descriptorProvider;
+  @Inject private ProtobufElementFinder finder;
+  @Inject private ImportedNamesProvider importedNamesProvider;
+  @Inject private LocalNamesProvider localNamesProvider;
+  @Inject private PackageResolver packageResolver;
+  @Inject private QualifiedNameDescriptions qualifiedNamesDescriptions;
+  @Inject private ImportUriResolver uriResolver;
+
+  <T extends Type> Collection<IEObjectDescription> localTypes(EObject root, Class<T> targetType) {
+    return localTypes(root, targetType, 0);
+  }
+
+  private <T extends Type> Collection<IEObjectDescription> localTypes(EObject root, Class<T> targetType, int level) {
+    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
+    for (EObject element : root.eContents()) {
+      if (!targetType.isInstance(element)) continue;
+      List<QualifiedName> names = localNamesProvider.namesOf(element);
+      int nameCount = names.size();
+      for (int i = level; i < nameCount; i++) {
+        descriptions.add(create(names.get(i), element));
+      }
+      descriptions.addAll(qualifiedNamesDescriptions.qualifiedNames(element));
+      // TODO investigate if groups can have messages, and if so, add those messages to the scope.
+      if (element instanceof Message) {
+        descriptions.addAll(localTypes(element, targetType, level + 1));
+      }
+    }
+    return descriptions;
+  }
+
+  <T extends Type> Collection<IEObjectDescription> importedTypes(Protobuf root, Class<T> targetType) {
+    List<Import> allImports = finder.importsIn(root);
+    if (allImports.isEmpty()) return emptyList();
+    return importedTypes(allImports, finder.packageOf(root), targetType);
+  }
+
+  private <T extends Type> Collection<IEObjectDescription> importedTypes(List<Import> allImports, Package aPackage,
+      Class<T> targetType) {
+    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
+    for (Import anImport : allImports) {
+      if (isImportingDescriptor(anImport)) {
+        descriptions.addAll(allBuiltInTypes(targetType));
+        continue;
+      }
+      Resource importedResource = importedResourceFrom(anImport);
+      Protobuf importedRoot = rootElementOf(importedResource);
+      if (importedRoot != null) {
+        descriptions.addAll(publicImportedTypes(importedRoot, targetType));
+        if (arePackagesRelated(aPackage, importedRoot)) {
+          descriptions.addAll(localTypes(importedRoot, targetType));
+          continue;
+        }
+      }
+      descriptions.addAll(children(importedResource, targetType));
+    }
+    return descriptions;
+  }
+
+  private boolean isImportingDescriptor(Import anImport) {
+    String descriptorLocation = descriptorProvider.descriptorLocation().toString();
+    return descriptorLocation.equals(anImport.getImportURI());
+  }
+
+  private <T extends Type> Collection<IEObjectDescription> allBuiltInTypes(Class<T> targetType) {
+    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
+    ProtoDescriptor descriptor = descriptorProvider.get();
+    for (Type t : descriptor.allTypes()) {
+      if (!targetType.isInstance(t)) continue;
+      T type = targetType.cast(t);
+      descriptions.addAll(qualifiedNamesDescriptions.qualifiedNames(type));
+    }
+    return descriptions;
+  }
+
+  private <T extends Type> Collection<IEObjectDescription> publicImportedTypes(Protobuf root, Class<T> targetType) {
+    List<Import> allImports = finder.publicImportsIn(root);
+    if (allImports.isEmpty()) return emptyList();
+    return importedTypes(allImports, finder.packageOf(root), targetType);
+  }
+
+  private Resource importedResourceFrom(Import anImport) {
+    ResourceSet resourceSet = finder.rootOf(anImport).eResource().getResourceSet();
+    URI importUri = createURI(uriResolver.apply(anImport));
+    try {
+      return resourceSet.getResource(importUri, true);
+    } catch (Throwable t) {
+      return null;
+    }
+  }
+
+  private Protobuf rootElementOf(Resource resource) {
+    if (resource instanceof XtextResource) {
+      EObject root = ((XtextResource) resource).getParseResult().getRootASTElement();
+      return (Protobuf) root;
+    }
+    TreeIterator<Object> contents = getAllContents(resource, true);
+    if (contents.hasNext()) {
+      Object next = contents.next();
+      if (next instanceof Protobuf) return (Protobuf) next;
+    }
+    return null;
+  }
+
+  private boolean arePackagesRelated(Package aPackage, EObject root) {
+    Package p = finder.packageOf(root);
+    return packageResolver.areRelated(aPackage, p);
+  }
+
+  private <T extends Type> Collection<IEObjectDescription> children(Resource resource, Class<T> targetType) {
+    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
+    TreeIterator<Object> contents = getAllContents(resource, true);
+    while (contents.hasNext()) {
+      Object next = contents.next();
+      if (!targetType.isInstance(next)) continue;
+      T type = targetType.cast(next);
+      descriptions.addAll(qualifiedNamesDescriptions.qualifiedNames(type));
+      for (QualifiedName name : importedNamesProvider.namesOf(type)) {
+        descriptions.add(create(name, type));
+      }
+    }
+    return descriptions;
+  }
+}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/Options.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/Options.java
new file mode 100644
index 0000000..db78ce5
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/Options.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2011 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.util;
+
+import org.eclipse.emf.ecore.EObject;
+
+import com.google.eclipse.protobuf.protobuf.*;
+import com.google.inject.Singleton;
+
+/**
+ * Utility methods related to <code>{@link Option}</code>.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+@Singleton
+public class Options {
+
+  /**
+   * Indicates whether the given option is a file-level option.
+   * @param option the option to verify.
+   * @return {@code true} if the given option is a file-level option, {@code false} otherwise.
+   */
+  public boolean isFileOption(Option option) {
+    return isOptionContainerInstanceOf(option, Protobuf.class);
+  }
+
+  private boolean isOptionContainerInstanceOf(Option option, Class<? extends EObject> type) {
+    return type.isInstance(option.eContainer());
+  }
+
+  /**
+   * Returns the <code>{@link Property}</code> the given <code>{@link Option}</code> is referring to.
+   * @param option the given {@code BuiltInFileOption}.
+   * @return the {@code Property} the given {code BuiltInFileOption} is referring to, or {@code null} if it cannot be
+   * found.
+   */
+  public Property propertyFrom(Option option) {
+    PropertyRef ref = option.getProperty();
+    return (ref == null) ? null : ref.getProperty();
+  }
+}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/Properties.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/Properties.java
index f556355..625ca65 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/Properties.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/Properties.java
@@ -16,7 +16,7 @@
 import com.google.inject.Singleton;
 
 /**
- * Utility methods re <code>{@link Property}</code>.
+ * Utility methods related to <code>{@link Property}</code>.
  *
  * @author alruiz@google.com (Alex Ruiz)
  */