-
Notifications
You must be signed in to change notification settings - Fork 408
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Completing /** should generate Javadoc with params
Signed-off-by: Snjezana Peco <[email protected]>
- Loading branch information
Showing
4 changed files
with
354 additions
and
1 deletion.
There are no files selected for viewing
268 changes: 268 additions & 0 deletions
268
...ls.core/src/org/eclipse/jdt/ls/core/internal/contentassist/JavadocCompletionProposal.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,268 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2000, 2016 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 | ||
* | ||
* Based on org.eclipse.jdt.internal.ui.text.javadoc.JavaDocAutoIndentStrategy | ||
* | ||
* Contributors: | ||
* IBM Corporation - initial API and implementation | ||
* Red Hat, Inc - decoupling from jdt.ui | ||
*******************************************************************************/ | ||
package org.eclipse.jdt.ls.core.internal.contentassist; | ||
|
||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import org.eclipse.core.runtime.CoreException; | ||
import org.eclipse.core.runtime.IProgressMonitor; | ||
import org.eclipse.jdt.core.ICompilationUnit; | ||
import org.eclipse.jdt.core.IJavaElement; | ||
import org.eclipse.jdt.core.IJavaProject; | ||
import org.eclipse.jdt.core.IMember; | ||
import org.eclipse.jdt.core.IMethod; | ||
import org.eclipse.jdt.core.ISourceRange; | ||
import org.eclipse.jdt.core.IType; | ||
import org.eclipse.jdt.core.JavaModelException; | ||
import org.eclipse.jdt.internal.corext.util.MethodOverrideTester; | ||
import org.eclipse.jdt.internal.corext.util.SuperTypeHierarchyCache; | ||
import org.eclipse.jdt.ls.core.internal.JDTUtils; | ||
import org.eclipse.jdt.ls.core.internal.corext.codemanipulation.CodeGeneration; | ||
import org.eclipse.jdt.ls.core.internal.corext.codemanipulation.StubUtility; | ||
import org.eclipse.jdt.ls.core.internal.handlers.CompletionResolveHandler; | ||
import org.eclipse.jdt.ls.core.internal.handlers.JsonRpcHelpers; | ||
import org.eclipse.jface.text.BadLocationException; | ||
import org.eclipse.jface.text.IDocument; | ||
import org.eclipse.jface.text.IRegion; | ||
import org.eclipse.jface.text.Region; | ||
import org.eclipse.jface.text.TextUtilities; | ||
import org.eclipse.lsp4j.CompletionItem; | ||
import org.eclipse.lsp4j.CompletionItemKind; | ||
import org.eclipse.lsp4j.InsertTextFormat; | ||
import org.eclipse.lsp4j.Range; | ||
import org.eclipse.lsp4j.TextEdit; | ||
|
||
public class JavadocCompletionProposal { | ||
|
||
public static final String JAVA_DOC_COMMENT = "Javadoc comment"; | ||
|
||
public List<CompletionItem> getProposals(ICompilationUnit cu, int offset, CompletionProposalRequestor collector, IProgressMonitor monitor) throws JavaModelException { | ||
if (cu == null) { | ||
throw new IllegalArgumentException("Compilation unit must not be null"); //$NON-NLS-1$ | ||
} | ||
List<CompletionItem> result = new ArrayList<>(); | ||
IDocument d = JsonRpcHelpers.toDocument(cu.getBuffer()); | ||
if (offset < 0 || d.getLength() == 0) { | ||
return result; | ||
} | ||
try { | ||
int p = (offset == d.getLength() ? offset - 1 : offset); | ||
IRegion line = d.getLineInformationOfOffset(p); | ||
String lineStr = d.get(line.getOffset(), line.getLength()).trim(); | ||
if (!lineStr.startsWith("/**")) { | ||
return result; | ||
} | ||
if (!hasEndJavadoc(d, offset)) { | ||
return result; | ||
} | ||
String text = collector.getContext().getToken() == null ? "" : new String(collector.getContext().getToken()); | ||
StringBuilder buf = new StringBuilder(text); | ||
IRegion prefix = findPrefixRange(d, line); | ||
String indentation = d.get(prefix.getOffset(), prefix.getLength()); | ||
int lengthToAdd = Math.min(offset - prefix.getOffset(), prefix.getLength()); | ||
buf.append(indentation.substring(0, lengthToAdd)); | ||
String lineDelimiter = TextUtilities.getDefaultLineDelimiter(d); | ||
ICompilationUnit unit = cu; | ||
try { | ||
unit.reconcile(ICompilationUnit.NO_AST, false, null, null); | ||
String string = createJavaDocTags(d, offset, indentation, lineDelimiter, unit); | ||
if (string != null && !string.trim().equals("*")) { | ||
buf.append(string); | ||
} else { | ||
return result; | ||
} | ||
int nextNonWS = findEndOfWhiteSpace(d, offset, d.getLength()); | ||
if (!Character.isWhitespace(d.getChar(nextNonWS))) { | ||
buf.append(lineDelimiter); | ||
} | ||
} catch (CoreException e) { | ||
// ignore | ||
} | ||
final CompletionItem ci = new CompletionItem(); | ||
Range range = JDTUtils.toRange(unit, offset, 0); | ||
ci.setTextEdit(new TextEdit(range, rtrim(buf.toString()))); | ||
ci.setFilterText(JAVA_DOC_COMMENT); | ||
ci.setLabel(JAVA_DOC_COMMENT); | ||
ci.setSortText(SortTextHelper.convertRelevance(0)); | ||
ci.setKind(CompletionItemKind.Snippet); | ||
ci.setInsertTextFormat(InsertTextFormat.PlainText); | ||
String documentation = prepareDocumentation(buf.toString(), lineDelimiter); | ||
ci.setDocumentation(documentation); | ||
Map<String, String> data = new HashMap<>(3); | ||
data.put(CompletionResolveHandler.DATA_FIELD_URI, JDTUtils.toURI(cu)); | ||
data.put(CompletionResolveHandler.DATA_FIELD_REQUEST_ID, "0"); | ||
data.put(CompletionResolveHandler.DATA_FIELD_PROPOSAL_ID, "0"); | ||
ci.setData(data); | ||
result.add(ci); | ||
} catch (BadLocationException excp) { | ||
// stop work | ||
} | ||
return result; | ||
} | ||
|
||
private String prepareDocumentation(String text, String lineDelimiter) { | ||
String[] lines = text.split(lineDelimiter); | ||
StringBuilder buf = new StringBuilder(); | ||
for (String line : lines) { | ||
buf.append(rtrim(line)); | ||
buf.append(lineDelimiter); | ||
} | ||
String result = buf.toString(); | ||
if (result.indexOf(lineDelimiter) == 0) { | ||
return result.replaceFirst(lineDelimiter, ""); | ||
} | ||
return result; | ||
} | ||
|
||
private String rtrim(String text) { | ||
int index = 0; | ||
int end = text.length(); | ||
while (index < end) { | ||
char c = text.charAt(index); | ||
if (c == ' ' || c == '\t') { | ||
index++; | ||
} else { | ||
break; | ||
} | ||
} | ||
return text.substring(index); | ||
} | ||
|
||
private IRegion findPrefixRange(IDocument document, IRegion line) throws BadLocationException { | ||
int lineOffset = line.getOffset(); | ||
int lineEnd = lineOffset + line.getLength(); | ||
int indentEnd = findEndOfWhiteSpace(document, lineOffset, lineEnd); | ||
if (indentEnd < lineEnd && document.getChar(indentEnd) == '*') { | ||
indentEnd++; | ||
while (indentEnd < lineEnd && document.getChar(indentEnd) == ' ') { | ||
indentEnd++; | ||
} | ||
} | ||
return new Region(lineOffset, indentEnd - lineOffset); | ||
} | ||
|
||
private int findEndOfWhiteSpace(IDocument document, int offset, int end) throws BadLocationException { | ||
while (offset < end) { | ||
char c = document.getChar(offset); | ||
if (c != ' ' && c != '\t') { | ||
return offset; | ||
} | ||
offset++; | ||
} | ||
return end; | ||
} | ||
|
||
private boolean hasEndJavadoc(IDocument document, int offset) throws BadLocationException { | ||
int pos = -1; | ||
while (offset < document.getLength()) { | ||
char c = document.getChar(offset); | ||
if (!Character.isWhitespace(c) && !(c == '*')) { | ||
pos = offset; | ||
break; | ||
} | ||
offset++; | ||
} | ||
if (document.getLength() >= pos + 2 && document.get(pos - 1, 2).equals("*/")) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
|
||
private String createJavaDocTags(IDocument document, int offset, String indentation, String lineDelimiter, ICompilationUnit unit) throws CoreException, BadLocationException { | ||
IJavaElement element = unit.getElementAt(offset); | ||
if (element == null) { | ||
return null; | ||
} | ||
switch (element.getElementType()) { | ||
case IJavaElement.TYPE: | ||
return createTypeTags(document, offset, indentation, lineDelimiter, (IType) element); | ||
|
||
case IJavaElement.METHOD: | ||
return createMethodTags(document, offset, indentation, lineDelimiter, (IMethod) element); | ||
|
||
default: | ||
return null; | ||
} | ||
} | ||
|
||
private String createTypeTags(IDocument document, int offset, String indentation, String lineDelimiter, IType type) throws CoreException, BadLocationException { | ||
if (!accept(offset, type)) { | ||
return null; | ||
} | ||
String[] typeParamNames = StubUtility.getTypeParameterNames(type.getTypeParameters()); | ||
String comment = CodeGeneration.getTypeComment(type.getCompilationUnit(), type.getTypeQualifiedName('.'), typeParamNames, lineDelimiter); | ||
if (comment != null) { | ||
return prepareTemplateComment(comment.trim(), indentation, type.getJavaProject(), lineDelimiter); | ||
} | ||
return null; | ||
} | ||
|
||
private boolean accept(int offset, IMember member) throws JavaModelException { | ||
ISourceRange nameRange = member.getNameRange(); | ||
if (nameRange == null) { | ||
return false; | ||
} | ||
int srcOffset = nameRange.getOffset(); | ||
return srcOffset > offset; | ||
} | ||
|
||
private String createMethodTags(IDocument document, int offset, String indentation, String lineDelimiter, IMethod method) throws CoreException, BadLocationException { | ||
if (!accept(offset, method)) { | ||
return null; | ||
} | ||
IMethod inheritedMethod = getInheritedMethod(method); | ||
String comment = CodeGeneration.getMethodComment(method, inheritedMethod, lineDelimiter); | ||
if (comment != null) { | ||
comment = comment.trim(); | ||
boolean javadocComment = comment.startsWith("/**"); //$NON-NLS-1$ | ||
if (javadocComment) { | ||
return prepareTemplateComment(comment, indentation, method.getJavaProject(), lineDelimiter); | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
private String prepareTemplateComment(String comment, String indentation, IJavaProject project, String lineDelimiter) { | ||
// trim comment start and end if any | ||
if (comment.endsWith("*/")) { | ||
comment = comment.substring(0, comment.length() - 2); | ||
} | ||
comment = comment.trim(); | ||
if (comment.startsWith("/*")) { //$NON-NLS-1$ | ||
if (comment.length() > 2 && comment.charAt(2) == '*') { | ||
comment = comment.substring(3); // remove '/**' | ||
} else { | ||
comment = comment.substring(2); // remove '/*' | ||
} | ||
} | ||
// trim leading spaces, but not new lines | ||
int nonSpace = 0; | ||
int len = comment.length(); | ||
while (nonSpace < len && Character.getType(comment.charAt(nonSpace)) == Character.SPACE_SEPARATOR) { | ||
nonSpace++; | ||
} | ||
comment = comment.substring(nonSpace); | ||
return comment; | ||
} | ||
|
||
private IMethod getInheritedMethod(IMethod method) throws JavaModelException { | ||
IType declaringType = method.getDeclaringType(); | ||
MethodOverrideTester tester = SuperTypeHierarchyCache.getMethodOverrideTester(declaringType); | ||
return tester.findOverriddenMethod(method, true); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters