* Merge with HEAD
diff --git a/plugins/org.eclipse.dltk.ruby.ui/plugin.properties b/plugins/org.eclipse.dltk.ruby.ui/plugin.properties
index f6f7d0e..c328787 100644
--- a/plugins/org.eclipse.dltk.ruby.ui/plugin.properties
+++ b/plugins/org.eclipse.dltk.ruby.ui/plugin.properties
@@ -76,6 +76,8 @@
 RubyEditorFoldingPreferencePage.name = Folding
 RubyCodeTemplatesPreferencePage.name = Templates
 
+RubyTaskTagsPreferencePage.name = Task Tags
+
 # Decorators
 RubyProjectDecorator.name = Ruby Project Decorator
 
diff --git a/plugins/org.eclipse.dltk.ruby.ui/plugin.xml b/plugins/org.eclipse.dltk.ruby.ui/plugin.xml
index f889310..4f39f7f 100644
--- a/plugins/org.eclipse.dltk.ruby.ui/plugin.xml
+++ b/plugins/org.eclipse.dltk.ruby.ui/plugin.xml
@@ -153,6 +153,11 @@
               id="org.eclipse.dltk.ruby.preferences.templates"
               name="%RubyCodeTemplatesPreferencePage.name">
      </page>	          
+      <page
+              category="org.eclipse.dltk.ruby.preferences"
+              class="org.eclipse.dltk.ruby.internal.ui.preferences.RubyTodoTaskPreferencePage"
+              id="org.eclipse.dltk.ruby.preferences.todo"
+              name="%RubyTaskTagsPreferencePage.name"/>
     </extension>
     
     <extension point="org.eclipse.ui.views">  
diff --git a/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/documentation/RubyDocumentationProvider.java b/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/documentation/RubyDocumentationProvider.java
index 20533dc..8475e17 100644
--- a/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/documentation/RubyDocumentationProvider.java
+++ b/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/documentation/RubyDocumentationProvider.java
@@ -5,7 +5,9 @@
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
- 
+ * Contributors:
+ *     xored software, Inc. - initial API and Implementation (Andrei Sobolev)
+ *     xored software, Inc. - RubyDocumentation display improvements (Alex Panchenko <alex@xored.com>)
  *******************************************************************************/
 package org.eclipse.dltk.ruby.internal.ui.documentation;
 
@@ -40,6 +42,12 @@
 
 public class RubyDocumentationProvider implements IScriptDocumentationProvider {
 
+	private static final String PUBLIC = "public"; //$NON-NLS-1$
+
+	private static final String PROTECTED = "protected"; //$NON-NLS-1$
+
+	private static final String PRIVATE = "private"; //$NON-NLS-1$
+
 	protected String getLine(Document d, int line) throws BadLocationException {
 		return d.get(d.getLineOffset(line), d.getLineLength(line));
 	}
@@ -73,30 +81,51 @@
 				.setDocumentPartitioner(IRubyPartitions.RUBY_PARTITIONING, null);
 	}
 
+	private static int findOffsetBeforeMethod(Document doc, int start)
+			throws BadLocationException {
+		int line = doc.getLineOfOffset(start);
+		for (;;) {
+			if (--line < 0) {
+				throw new BadLocationException();
+			}
+			final IRegion r = doc.getLineInformation(line);
+			if (r.getLength() == 0) {
+				continue;
+			}
+			String s = doc.get(r.getOffset(), r.getLength());
+			if (isBlank(s)) {
+				continue;
+			}
+			s = s.trim();
+			if (PUBLIC.equals(s) || PROTECTED.equals(s) || PRIVATE.equals(s)) {
+				/**
+				 * skip access modifiers between method and comment, e.g.
+				 * 
+				 * <code>
+				 * # foo-method	documentation 
+				 * public 
+				 * def foo 
+				 * end
+				 * </code>
+				 */
+				continue;
+			}
+			return r.getOffset() + r.getLength() - 1;
+		}
+	}
+
 	public static String getHeaderComment(String contents, int offset) {
 		int start = offset;
 		int end = start;
 
-		String result = ""; //$NON-NLS-1$
-
 		Document doc = new Document(contents);
 		installStuff(doc);
 
-		int pos = 0;
-
-		if (start > 0) {
-			try {
-				int line = doc.getLineOfOffset(start);
-				if (line == 0)
-					return null;
-				IRegion inf = doc.getLineInformation(line - 1);
-				pos = inf.getOffset() + inf.getLength() - 1;
-			} catch (BadLocationException e) {
-				return null;
-			}
-		}
-
 		try {
+			int pos = 0;
+			if (start > 0) {
+				pos = findOffsetBeforeMethod(doc, start);
+			}
 			while (pos >= 0 && pos <= doc.getLength()) {
 				ITypedRegion region = TextUtilities.getPartition(doc,
 						IRubyPartitions.RUBY_PARTITIONING, pos, true);
@@ -136,15 +165,13 @@
 			if (end >= doc.getLength())
 				end = doc.getLength() - 1;
 
-			result = doc.get(start, end - start);
+			return doc.get(start, end - start);
 
 		} catch (BadLocationException e1) {
 			return null;
 		} finally {
 			removeStuff(doc);
 		}
-
-		return result;
 	}
 
 	protected String getHeaderComment(IMember member) {
@@ -181,6 +208,8 @@
 		return null;
 	}
 
+	private static final String NOTHING_KNOWN_ABOUT = "Nothing known about"; //$NON-NLS-1$
+
 	private Reader proccessBuiltinMethod(IMethod method) {
 		final String divider = "#"; //$NON-NLS-1$
 		IModelElement pp = method.getAncestor(IModelElement.TYPE);
@@ -190,13 +219,13 @@
 				+ method.getElementName();
 		RiHelper helper = RiHelper.getInstance();
 		String doc = helper.getDocFor(keyword);
-		if (doc != null && (doc.indexOf("Nothing known about") != -1) //$NON-NLS-1$
-				|| doc.trim().length() == 0) {
+		if (doc != null
+				&& (doc.indexOf(NOTHING_KNOWN_ABOUT) >= 0 || isBlank(doc))) {
 			// XXX megafix: some Kernel methods are documented in Object
 			if (pp.getElementName().equals("Kernel")) { //$NON-NLS-1$
 				keyword = "Object" + divider + method.getElementName(); //$NON-NLS-1$
 				doc = helper.getDocFor(keyword);
-				if (doc == null || doc.indexOf("Nothing known about") >= 0) { //$NON-NLS-1$
+				if (doc != null && doc.indexOf(NOTHING_KNOWN_ABOUT) >= 0) {
 					doc = null;
 				}
 			} else {
@@ -315,4 +344,26 @@
 	public Reader getInfo(String content) {
 		return null;
 	}
+
+	/**
+	 * Checks if a String is whitespace, empty ("") or null.
+	 * 
+	 * @param str
+	 *            the String to check, may be null
+	 * @return <code>true</code> if the String is null, empty or whitespace
+	 */
+	private static boolean isBlank(String s) {
+		if (s != null) {
+			final int len = s.length();
+			if (len != 0) {
+				for (int i = 0; i < len; i++) {
+					if (!Character.isWhitespace(s.charAt(i))) {
+						return false;
+					}
+				}
+			}
+		}
+		return true;
+	}
+
 }
diff --git a/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/preferences/RubyPreferencesMessages.java b/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/preferences/RubyPreferencesMessages.java
index 310964a..7962f29 100644
--- a/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/preferences/RubyPreferencesMessages.java
+++ b/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/preferences/RubyPreferencesMessages.java
@@ -29,4 +29,7 @@
 	public static String EditorFoldingPreferencePageDescription;
 
 	public static String GlobalPreferencePageDescription;
+
+	public static String TodoTaskDescription;
+
 }
diff --git a/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/preferences/RubyPreferencesMessages.properties b/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/preferences/RubyPreferencesMessages.properties
index 954ce64..4e61f8c 100644
--- a/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/preferences/RubyPreferencesMessages.properties
+++ b/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/preferences/RubyPreferencesMessages.properties
@@ -16,3 +16,4 @@
 
 GlobalPreferencePageDescription = General Ruby preferences
 
+TodoTaskDescription = Strings indicating tasks in Ruby comments.
diff --git a/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/preferences/RubyTodoTaskPreferencePage.java b/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/preferences/RubyTodoTaskPreferencePage.java
new file mode 100644
index 0000000..0c6bb3a
--- /dev/null
+++ b/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/preferences/RubyTodoTaskPreferencePage.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008 xored software, 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
+ *
+ * Contributors:
+ *     xored software, Inc. - initial API and Implementation (Alex Panchenko)
+ *******************************************************************************/
+package org.eclipse.dltk.ruby.internal.ui.preferences;
+
+import org.eclipse.core.runtime.Preferences;
+import org.eclipse.dltk.ruby.core.RubyPlugin;
+import org.eclipse.dltk.ui.preferences.TodoTaskAbstractPreferencePage;
+
+public class RubyTodoTaskPreferencePage extends TodoTaskAbstractPreferencePage {
+
+	protected String getHelpId() {
+		return null;
+	}
+
+	protected void setDescription() {
+		setDescription(RubyPreferencesMessages.TodoTaskDescription);
+	}
+
+	protected Preferences getPluginPreferences() {
+		return RubyPlugin.getDefault().getPluginPreferences();
+	}
+
+}
diff --git a/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/text/completion/RubyCompletionProposalLabelProvider.java b/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/text/completion/RubyCompletionProposalLabelProvider.java
index 311b404..2b40b92 100644
--- a/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/text/completion/RubyCompletionProposalLabelProvider.java
+++ b/plugins/org.eclipse.dltk.ruby.ui/src/org/eclipse/dltk/ruby/internal/ui/text/completion/RubyCompletionProposalLabelProvider.java
@@ -10,14 +10,25 @@
 package org.eclipse.dltk.ruby.internal.ui.text.completion;
 
 import org.eclipse.dltk.core.CompletionProposal;
+import org.eclipse.dltk.core.IBuildpathEntry;
 import org.eclipse.dltk.core.IMethod;
 import org.eclipse.dltk.core.IModelElement;
+import org.eclipse.dltk.core.IProjectFragment;
+import org.eclipse.dltk.core.IScriptFolder;
+import org.eclipse.dltk.core.ISourceModule;
 import org.eclipse.dltk.core.IType;
+import org.eclipse.dltk.core.environment.EnvironmentPathUtils;
 import org.eclipse.dltk.ruby.core.model.FakeMethod;
+import org.eclipse.dltk.ui.ScriptElementLabels;
 import org.eclipse.dltk.ui.text.completion.CompletionProposalLabelProvider;
 
 public class RubyCompletionProposalLabelProvider extends
 		CompletionProposalLabelProvider {
+
+	private static final String SEPARATOR = " - "; //$NON-NLS-1$
+	private static final String PACKAGE_SEPARATOR = "::"; //$NON-NLS-1$
+	private static final String FOLDER_SEPARATOR = "/"; //$NON-NLS-1$
+
 	protected String createMethodProposalLabel(CompletionProposal methodProposal) {
 		StringBuffer nameBuffer = new StringBuffer();
 
@@ -30,7 +41,7 @@
 		nameBuffer.append(')');
 
 		IMethod method = (IMethod) methodProposal.getModelElement();
-		nameBuffer.append(" - "); //$NON-NLS-1$
+		nameBuffer.append(SEPARATOR);
 		if (method instanceof FakeMethod
 				&& ((FakeMethod) method).getReceiver() != null) {
 			nameBuffer.append(((FakeMethod) method).getReceiver());
@@ -38,7 +49,7 @@
 			IModelElement parent = method.getParent();
 			if (parent instanceof IType) {
 				IType type = (IType) parent;
-				nameBuffer.append(type.getTypeQualifiedName("::")); //$NON-NLS-1$
+				nameBuffer.append(type.getTypeQualifiedName(PACKAGE_SEPARATOR));
 			} else {
 				nameBuffer.append(parent.getElementName());
 			}
@@ -57,10 +68,10 @@
 		// parameters
 		nameBuffer.append('(');
 		appendUnboundedParameterList(nameBuffer, methodProposal);
-		nameBuffer.append(')'); //$NON-NLS-1$
+		nameBuffer.append(')');
 
 		IMethod method = (IMethod) methodProposal.getModelElement();
-		nameBuffer.append(" - "); //$NON-NLS-1$
+		nameBuffer.append(SEPARATOR);
 		if (method instanceof FakeMethod
 				&& ((FakeMethod) method).getReceiver() != null) {
 			String receiver = ((FakeMethod) method).getReceiver();
@@ -69,7 +80,7 @@
 			IModelElement parent = method.getParent();
 			if (parent instanceof IType) {
 				IType type = (IType) parent;
-				nameBuffer.append(type.getTypeQualifiedName("::")); //$NON-NLS-1$
+				nameBuffer.append(type.getTypeQualifiedName(PACKAGE_SEPARATOR));
 			} else {
 				nameBuffer.append(parent.getElementName());
 			}
@@ -79,22 +90,83 @@
 	}
 
 	protected String createTypeProposalLabel(CompletionProposal typeProposal) {
-		StringBuffer nameBuffer = new StringBuffer();
-
+		final StringBuffer nameBuffer = new StringBuffer();
 		nameBuffer.append(typeProposal.getName());
-
-		IType type = (IType) typeProposal.getModelElement();
-		nameBuffer.append(" - "); //$NON-NLS-1$
-		IModelElement parent = type.getParent();
+		final IType type = (IType) typeProposal.getModelElement();
+		final IModelElement parent = type.getParent();
+		final ISourceModule parentModule;
 		if (parent instanceof IType) {
-			IType type2 = (IType) parent;
-			nameBuffer.append(type2.getElementName()); // XXX, fqn may be
-			// better idea
+			nameBuffer.append(SEPARATOR);
+			parentModule = appendType((IType) parent, nameBuffer);
+		} else if (parent instanceof ISourceModule) {
+			parentModule = (ISourceModule) parent;
 		} else {
-			nameBuffer.append(parent.getElementName());
+			parentModule = null;
 		}
-
+		if (parentModule != null) {
+			nameBuffer.append(SEPARATOR);
+			appendSourceModule(parentModule, nameBuffer);
+		}
 		return nameBuffer.toString();
 	}
 
+	private ISourceModule appendType(IType type, StringBuffer sb) {
+		final IModelElement parent = type.getParent();
+		if (parent instanceof IType) {
+			final ISourceModule module = appendType((IType) parent, sb);
+			sb.append(PACKAGE_SEPARATOR);
+			sb.append(type.getElementName());
+			return module;
+		} else {
+			sb.append(type.getElementName());
+			if (parent instanceof ISourceModule) {
+				return (ISourceModule) parent;
+			} else {
+				return null;
+			}
+		}
+	}
+
+	private void appendSourceModule(ISourceModule module, StringBuffer sb) {
+		final IModelElement parent = module.getParent();
+		final IProjectFragment fragment;
+		if (parent instanceof IScriptFolder) {
+			fragment = appendScriptFolder((IScriptFolder) parent, sb);
+		} else {
+			fragment = null;
+		}
+		sb.append(module.getElementName());
+		if (fragment != null) {
+			if (!fragment.isArchive()) {
+				if (fragment.getPath().toString().startsWith(
+						IBuildpathEntry.BUILTIN_EXTERNAL_ENTRY_STR)) {
+					sb.append(' ');
+					sb.append(ScriptElementLabels.BUILTINS_FRAGMENT);
+				} else if (fragment.isExternal()) {
+					sb.append(SEPARATOR);
+					sb.append(EnvironmentPathUtils.getLocalPath(
+							fragment.getPath()).toPortableString());
+				}
+			}
+		}
+	}
+
+	private IProjectFragment appendScriptFolder(IScriptFolder folder,
+			StringBuffer sb) {
+		final IModelElement parent = folder.getParent();
+		final IProjectFragment fragment;
+		if (parent instanceof IScriptFolder) {
+			fragment = appendScriptFolder((IScriptFolder) parent, sb);
+		} else if (parent instanceof IProjectFragment) {
+			fragment = (IProjectFragment) parent;
+		} else {
+			fragment = null;
+		}
+		if (!folder.isRootFolder()) {
+			sb.append(folder.getElementName());
+			sb.append(FOLDER_SEPARATOR);
+		}
+		return fragment;
+	}
+
 }