blob: 1ba52f123608ad89631ef1bb0214974b55bdbb05 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2007 IBM Corporation and others.
* 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 org.eclipse.dltk.ruby.internal.ui.documentation;
import java.io.Reader;
import java.io.StringReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.dltk.core.IBuffer;
import org.eclipse.dltk.core.IField;
import org.eclipse.dltk.core.IMember;
import org.eclipse.dltk.core.IMethod;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.ISourceRange;
import org.eclipse.dltk.core.IType;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.internal.core.BuiltinProjectFragment;
import org.eclipse.dltk.ruby.core.PredefinedVariables;
import org.eclipse.dltk.ruby.core.model.FakeField;
import org.eclipse.dltk.ruby.internal.ui.docs.RiHelper;
import org.eclipse.dltk.ruby.internal.ui.text.RubyPartitionScanner;
import org.eclipse.dltk.ruby.internal.ui.text.RubyPartitions;
import org.eclipse.dltk.ui.documentation.IScriptDocumentationProvider;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.rules.FastPartitioner;
public class RubyDocumentationProvider implements IScriptDocumentationProvider {
protected String getLine(Document d, int line) throws BadLocationException {
return d.get(d.getLineOffset(line), d.getLineLength(line));
}
/**
* Installs a partitioner with <code>document</code>.
*
* @param document
* the document
*/
private static void installStuff(Document document) {
String[] types = new String[] { RubyPartitions.RUBY_STRING,
RubyPartitions.RUBY_COMMENT, IDocument.DEFAULT_CONTENT_TYPE };
FastPartitioner partitioner = new FastPartitioner(
new RubyPartitionScanner(), types);
partitioner.connect(document);
document.setDocumentPartitioner(RubyPartitions.RUBY_PARTITIONING,
partitioner);
}
/**
* Removes partitioner with <code>document</code>.
*
* @param document
* the document
*/
private static void removeStuff(Document document) {
document.setDocumentPartitioner(RubyPartitions.RUBY_PARTITIONING, null);
}
public static String getHeaderComment(String contents, int offset) {
int start = offset;
int end = start;
String result = "";
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 {
while (pos >= 0 && pos <= doc.getLength()) {
ITypedRegion region = TextUtilities.getPartition(doc,
RubyPartitions.RUBY_PARTITIONING, pos, true);
if (region.getType().equals(RubyPartitions.RUBY_DOC)
|| region.getType().equals(RubyPartitions.RUBY_COMMENT)) {
start = region.getOffset();
}
if (region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE)) {
String content = doc.get(region.getOffset(),
region.getLength()).trim();
if (content.length() > 0)
break;
}
pos = region.getOffset() - 1;
}
pos = start + 1;
while (pos <= doc.getLength()) {
ITypedRegion region = TextUtilities.getPartition(doc,
RubyPartitions.RUBY_PARTITIONING, pos, true);
if (region.getType().equals(RubyPartitions.RUBY_DOC)
|| region.getType().equals(RubyPartitions.RUBY_COMMENT)) {
end = region.getOffset() + region.getLength();
}
if (region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE)) {
String content = doc.get(region.getOffset(),
region.getLength()).trim();
if (content.length() > 0)
break;
}
pos = region.getOffset() + region.getLength() + 1;
}
if (end >= doc.getLength())
end = doc.getLength() - 1;
result = doc.get(start, end - start);
} catch (BadLocationException e1) {
return null;
} finally {
removeStuff(doc);
}
return result;
}
protected String getHeaderComment(IMember member) {
if (member instanceof IField) {
return null;
}
try {
ISourceRange range = member.getSourceRange();
if (range == null)
return null;
int offset = range.getOffset();
IBuffer buf = null;
ISourceModule sourceModule = member.getSourceModule();
if (!sourceModule.isConsistent()) {
return null;
}
buf = sourceModule.getBuffer();
String contents = buf.getContents();
return RubyDocumentationProvider.getHeaderComment(contents, offset);
} catch (ModelException e) {
}
return null;
}
private Reader proccessBuiltinType(IType type) {
String keyword = type.getElementName();
RiHelper helper = RiHelper.getInstance();
String doc = helper.getDocFor(keyword);
if (doc != null)
return new StringReader(doc);
return new StringReader("Built-in method");
}
private Reader proccessBuiltinMethod(IMethod method) {
final String divider = "#";
IModelElement pp = method.getAncestor(IModelElement.TYPE);
if (pp.getElementName().startsWith("<<"))
pp = pp.getAncestor(IModelElement.TYPE);
String keyword = pp.getElementName() + divider
+ method.getElementName();
RiHelper helper = RiHelper.getInstance();
String doc = helper.getDocFor(keyword);
if (doc != null && (doc.indexOf("Nothing known about") != -1)
|| doc.trim().length() == 0) {
// XXX megafix: some Kernel methods are documented in Object
if (pp.getElementName().equals("Kernel")) {
keyword = "Object" + divider + method.getElementName();
doc = helper.getDocFor(keyword);
}
}
if (doc != null)
return new StringReader(doc);
return new StringReader("Built-in method");
}
public Reader getInfo(IMember member, boolean lookIntoParents,
boolean lookIntoExternal) {
boolean isBuiltin = member.getAncestor(IModelElement.PROJECT_FRAGMENT) instanceof BuiltinProjectFragment;
if (isBuiltin && member instanceof IMethod) {
IMethod method = (IMethod) member;
return proccessBuiltinMethod(method);
} else if (isBuiltin && member instanceof IType) {
IType type = (IType) member;
return proccessBuiltinType(type);
} else if (member instanceof FakeField) {
FakeField field = (FakeField) member;
String doc = PredefinedVariables.getDocOf(field.getElementName());
if (doc != null)
return new StringReader(doc);
}
String header = getHeaderComment(member);
if (header == null || header.length() == 0)
return null;
return new StringReader(convertToHTML(header));
}
private static String replaceSpecTag(String original, String sc, String tag) {
String filtered = original;
if (sc.equals("*") || sc.equals("+"))
sc = "\\" + sc;
Pattern bold = Pattern.compile(sc + "[_a-zA-Z0-9]+" + sc);
while (true) {
Matcher matcher = bold.matcher(filtered);
if (matcher.find()) {
String startStr = filtered.substring(0, matcher.start());
String endStr = filtered.substring(matcher.end());
String grp = matcher.group();
filtered = startStr + "<" + tag + ">"
+ grp.substring(1, grp.length() - 1) + "</" + tag + ">"
+ endStr;
} else
break;
}
return filtered;
}
protected String convertToHTML(String header) {
if (header == null)
return "";
StringBuffer result = new StringBuffer();
Document d = new Document(header);
boolean enabled = true;
for (int line = 0;; line++) {
try {
String str = getLine(d, line).trim();
if (str == null)
break;
if (str.startsWith("#--")) {
enabled = false;
} else if (str.startsWith("#++")) {
enabled = true;
continue;
}
if (!enabled)
continue;
if (str.startsWith("=begin"))
continue;
if (str.startsWith("=end"))
continue;
while (str.length() > 0 && str.startsWith("#"))
str = str.substring(1);
str = replaceSpecTag(str, "*", "b");
str = replaceSpecTag(str, "+", "tt");
str = replaceSpecTag(str, "_", "em");
str.replaceAll("\\*[_a-zA-Z0-9]+\\*", "");
if (str.length() == 0)
result.append("<p>");
else {
if (str.trim().startsWith("== ")) {
result.append("<h2>");
result.append(str.substring(3));
result.append("</h2>");
} else if (str.trim().startsWith("= ")) {
result.append("<h1>");
result.append(str.substring(2));
result.append("</h1>");
} else if (str.trim().startsWith("---")) {
result.append("<hr>");
} else {
result.append(str + "<br>");
}
}
} catch (BadLocationException e) {
break;
}
}
// result.append("</p>\n");
return result.toString();
}
public Reader getInfo(String content) {
return null;
}
}