Skip to content

Commit bddf2b1

Browse files
committed
JSDoc/ContentAssist: fix JSDocTypeHyperLinkDetector for params with optional flag
e.g. @param {String=} value refactored the code to use the same functions in completion and hyperlink detector
1 parent 1eaa6d4 commit bddf2b1

File tree

5 files changed

+146
-77
lines changed

5 files changed

+146
-77
lines changed

plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/internal/javascript/ti/JSDocSupport.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@
6868
*/
6969
public class JSDocSupport implements IModelBuilder {
7070

71-
private static final String DOTS = "...";
71+
public static final String PARAM_DOTS = "...";
72+
public static final String PARAM_OPTIONAL = "=";
7273

7374
public String getFeatureId() {
7475
return JSDocSupport.class.getName();
@@ -350,14 +351,16 @@ protected void parseParams(IMethod method, JSDocTags tags,
350351
final String token = st.peek();
351352
if (isBraced(token)) {
352353
String type = cutBraces(token);
353-
if (type.startsWith(DOTS)) {
354+
if (type.startsWith(PARAM_DOTS)) {
354355
pp.varargs = true;
355-
type = type.substring(DOTS.length());
356-
} else if (type.endsWith(DOTS)) {
356+
type = type.substring(PARAM_DOTS.length());
357+
} else if (type.endsWith(PARAM_DOTS)) {
357358
pp.varargs = true;
358-
type = type.substring(0, type.length() - DOTS.length());
359-
} else if (type.endsWith("=")) {
360-
type = type.substring(0, type.length() - 1);
359+
type = type.substring(0,
360+
type.length() - PARAM_DOTS.length());
361+
} else if (type.endsWith(PARAM_OPTIONAL)) {
362+
type = type.substring(0,
363+
type.length() - PARAM_OPTIONAL.length());
361364
pp.optional = true;
362365
}
363366
pp.type = type;
@@ -375,12 +378,12 @@ protected void parseParams(IMethod method, JSDocTags tags,
375378
defaultValueSeperatorIndex);
376379
}
377380
}
378-
if (!pp.varargs && paramName.endsWith(DOTS)) {
381+
if (!pp.varargs && paramName.endsWith(PARAM_DOTS)) {
379382
// just in case, so next condition doesn't use ".." as
380383
// property name.
381384
pp.varargs = true;
382385
paramName = paramName.substring(0, paramName.length()
383-
- DOTS.length());
386+
- PARAM_DOTS.length());
384387
}
385388
String propertyName = null;
386389
int propertiesObjectIndex = paramName.lastIndexOf('.');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2012 NumberFour AG
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/legal/epl-v10.html
8+
*
9+
* Contributors:
10+
* NumberFour AG - initial API and Implementation (Alex Panchenko)
11+
*******************************************************************************/
12+
package org.eclipse.dltk.javascript.internal.ui.text;
13+
14+
import org.eclipse.dltk.javascript.ui.text.IJavaScriptPartitions;
15+
import org.eclipse.jface.text.BadLocationException;
16+
import org.eclipse.jface.text.IDocument;
17+
import org.eclipse.jface.text.IRegion;
18+
import org.eclipse.jface.text.ITypedRegion;
19+
import org.eclipse.jface.text.Region;
20+
import org.eclipse.jface.text.TextUtilities;
21+
22+
public class JSDocTextUtils {
23+
24+
/**
25+
* Returns the region of the line containing the specified offset. If line
26+
* starts/ends in other partition, then only part of the line belonging to
27+
* the same partition is returned.
28+
*
29+
* @throws BadLocationException
30+
*/
31+
public static IRegion getLineRegion(IDocument document, int offset)
32+
throws BadLocationException {
33+
final IRegion region = document.getLineInformationOfOffset(offset);
34+
final ITypedRegion partition = TextUtilities.getPartition(document,
35+
IJavaScriptPartitions.JS_PARTITIONING, offset, false);
36+
if (partition.getOffset() > region.getOffset()
37+
|| partition.getOffset() + partition.getLength() < region
38+
.getOffset() + region.getLength()) {
39+
final int newOffset = Math.max(partition.getOffset(),
40+
region.getOffset());
41+
final int newEnd = Math.min(
42+
partition.getOffset() + partition.getLength(),
43+
region.getOffset() + region.getLength());
44+
return new Region(newOffset, newEnd - newOffset);
45+
}
46+
return region;
47+
}
48+
49+
/**
50+
* Detects tag name from the beginning of the specified line or
51+
* <code>null</code>.
52+
*/
53+
public static TypeNameNode getTag(char[] line, int begin, int end) {
54+
int index = begin;
55+
while (index < end && Character.isWhitespace(line[index])) {
56+
++index;
57+
}
58+
if (index < end && line[index] == '/') {
59+
++index;
60+
}
61+
while (index < end && line[index] == '*') {
62+
++index;
63+
}
64+
while (index < end && Character.isWhitespace(line[index])) {
65+
++index;
66+
}
67+
if (index < end && line[index] == '@') {
68+
final int tagStart = index;
69+
++index;
70+
if (index < end && Character.isJavaIdentifierStart(line[index])) {
71+
++index;
72+
while (index < end
73+
&& (Character.isJavaIdentifierPart(line[index])
74+
|| line[index] == '.' || line[index] == '-')) {
75+
++index;
76+
}
77+
}
78+
return new TypeNameNode(
79+
new String(line, tagStart, index - tagStart), tagStart,
80+
index);
81+
}
82+
return null;
83+
}
84+
85+
}

plugins/org.eclipse.dltk.javascript.ui/src/org/eclipse/dltk/javascript/internal/ui/text/TypeNameNode.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,8 @@ public TypeNameNode(String type, int start, int end) {
2121
this.start = start;
2222
this.end = end;
2323
}
24+
25+
public String value() {
26+
return type;
27+
}
2428
}

plugins/org.eclipse.dltk.javascript.ui/src/org/eclipse/dltk/javascript/internal/ui/text/completion/JSDocCompletionProposalComputer.java

Lines changed: 13 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@
2020
import org.eclipse.dltk.javascript.internal.core.codeassist.JSCompletionEngine;
2121
import org.eclipse.dltk.javascript.internal.ui.JavaScriptUI;
2222
import org.eclipse.dltk.javascript.internal.ui.templates.JSDocTemplateCompletionProcessor;
23+
import org.eclipse.dltk.javascript.internal.ui.text.JSDocTextUtils;
24+
import org.eclipse.dltk.javascript.internal.ui.text.TypeNameNode;
2325
import org.eclipse.dltk.javascript.parser.jsdoc.JSDocTag;
2426
import org.eclipse.dltk.javascript.typeinfo.TypeMode;
2527
import org.eclipse.dltk.javascript.typeinfo.model.Type;
2628
import org.eclipse.dltk.javascript.typeinfo.model.TypeKind;
27-
import org.eclipse.dltk.javascript.ui.text.IJavaScriptPartitions;
2829
import org.eclipse.dltk.ui.DLTKPluginImages;
2930
import org.eclipse.dltk.ui.templates.ScriptTemplateProposal;
3031
import org.eclipse.dltk.ui.text.completion.ContentAssistInvocationContext;
@@ -34,9 +35,6 @@
3435
import org.eclipse.jface.text.BadLocationException;
3536
import org.eclipse.jface.text.IDocument;
3637
import org.eclipse.jface.text.IRegion;
37-
import org.eclipse.jface.text.ITypedRegion;
38-
import org.eclipse.jface.text.Region;
39-
import org.eclipse.jface.text.TextUtilities;
4038
import org.eclipse.jface.text.contentassist.ICompletionProposal;
4139
import org.eclipse.jface.text.contentassist.IContextInformation;
4240

@@ -57,43 +55,25 @@ public List<ICompletionProposal> computeCompletionProposals(
5755
ContentAssistInvocationContext context, IProgressMonitor monitor) {
5856
IDocument document = context.getDocument();
5957
try {
60-
final IRegion region = getLineRegion(document,
58+
final IRegion region = JSDocTextUtils.getLineRegion(document,
6159
context.getInvocationOffset());
6260
final char[] line = document.get(region.getOffset(),
6361
region.getLength()).toCharArray();
6462
final int offsetInLine = context.getInvocationOffset()
6563
- region.getOffset();
66-
int index = 0;
67-
index = skipSpaces(line, index, offsetInLine);
68-
if (index < offsetInLine && line[index] == '/') {
69-
++index;
70-
}
71-
while (index < offsetInLine && line[index] == '*') {
72-
++index;
73-
}
74-
index = skipSpaces(line, index, offsetInLine);
75-
if (!(index < offsetInLine && line[index] == '@')) {
64+
final TypeNameNode tagName = JSDocTextUtils.getTag(line, 0,
65+
offsetInLine);
66+
if (tagName == null) {
7667
return Collections.emptyList();
7768
}
78-
final int tagStart = index;
79-
++index;
80-
if (index < offsetInLine
81-
&& Character.isJavaIdentifierStart(line[index])) {
82-
++index;
83-
while (index < offsetInLine
84-
&& (Character.isJavaIdentifierPart(line[index])
85-
|| line[index] == '.' || line[index] == '-')) {
86-
++index;
87-
}
69+
if (tagName.end == offsetInLine) {
70+
return completionOnTag(context, tagName.value());
8871
}
89-
if (index == offsetInLine) {
90-
return completionOnTag(context, new String(line, tagStart,
91-
index - tagStart));
72+
int index = tagName.end;
73+
while (index < offsetInLine && Character.isWhitespace(line[index])) {
74+
++index;
9275
}
93-
final int tagEnd = index;
94-
final String tagName = new String(line, tagStart, tagEnd - tagStart);
95-
index = skipSpaces(line, index, offsetInLine);
96-
if (JSDocTag.SEE.equals(tagName)) {
76+
if (JSDocTag.SEE.equals(tagName.value())) {
9777
int valueStart = index;
9878
while (index < offsetInLine) {
9979
if (Character.isWhitespace(line[index])) {
@@ -111,7 +91,7 @@ public List<ICompletionProposal> computeCompletionProposals(
11191
++index;
11292
depth = 1;
11393
nameStart = index;
114-
} else if (JSDocTag.TYPE.equals(tagName)) {
94+
} else if (JSDocTag.TYPE.equals(tagName.value())) {
11595
breakOnSpace = true;
11696
} else {
11797
return Collections.emptyList();
@@ -194,31 +174,6 @@ private List<ICompletionProposal> completionAfterSee(
194174
}
195175
}
196176

197-
private IRegion getLineRegion(IDocument document, int offset)
198-
throws BadLocationException {
199-
final IRegion region = document.getLineInformationOfOffset(offset);
200-
final ITypedRegion partition = TextUtilities.getPartition(document,
201-
IJavaScriptPartitions.JS_PARTITIONING, offset, false);
202-
if (partition.getOffset() > region.getOffset()
203-
|| partition.getOffset() + partition.getLength() < region
204-
.getOffset() + region.getLength()) {
205-
final int newOffset = Math.max(partition.getOffset(),
206-
region.getOffset());
207-
final int newEnd = Math.min(
208-
partition.getOffset() + partition.getLength(),
209-
region.getOffset() + region.getLength());
210-
return new Region(newOffset, newEnd - newOffset);
211-
}
212-
return region;
213-
}
214-
215-
private static int skipSpaces(final char[] line, int index, int offsetInLine) {
216-
while (index < offsetInLine && Character.isWhitespace(line[index])) {
217-
++index;
218-
}
219-
return index;
220-
}
221-
222177
private List<ICompletionProposal> completionOnTag(
223178
ContentAssistInvocationContext context, String tag) {
224179
final List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();

plugins/org.eclipse.dltk.javascript.ui/src/org/eclipse/dltk/javascript/internal/ui/text/hyperlink/JSDocTypeHyperlinkDetector.java

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@
1818
import org.eclipse.dltk.core.ISourceModule;
1919
import org.eclipse.dltk.core.ModelException;
2020
import org.eclipse.dltk.core.ScriptModelUtil;
21+
import org.eclipse.dltk.internal.javascript.ti.JSDocSupport;
2122
import org.eclipse.dltk.internal.javascript.ti.TypeInferencer2;
2223
import org.eclipse.dltk.internal.ui.actions.SelectionConverter;
2324
import org.eclipse.dltk.internal.ui.editor.EditorUtility;
2425
import org.eclipse.dltk.internal.ui.editor.ModelElementHyperlink;
2526
import org.eclipse.dltk.javascript.internal.ui.JavaScriptUI;
27+
import org.eclipse.dltk.javascript.internal.ui.text.JSDocTextUtils;
2628
import org.eclipse.dltk.javascript.internal.ui.text.JSDocTypeUtil;
2729
import org.eclipse.dltk.javascript.internal.ui.text.TypeNameNode;
30+
import org.eclipse.dltk.javascript.parser.jsdoc.JSDocTag;
2831
import org.eclipse.dltk.javascript.typeinfo.IElementConverter;
2932
import org.eclipse.dltk.javascript.typeinfo.TypeInfoManager;
3033
import org.eclipse.dltk.javascript.typeinfo.TypeMode;
@@ -61,19 +64,39 @@ public IHyperlink[] detectHyperlinks(ITextViewer textViewer,
6164
.equals(contentType)) {
6265
return null;
6366
}
64-
final int lineNumber = doc.getLineOfOffset(offset);
65-
final IRegion lineRegion = doc.getLineInformation(lineNumber);
67+
final IRegion lineRegion = JSDocTextUtils
68+
.getLineRegion(doc, offset);
6669
final String line = doc.get(lineRegion.getOffset(),
6770
lineRegion.getLength());
68-
final int start = line.lastIndexOf('{',
69-
offset - lineRegion.getOffset());
71+
final int offsetInLine = offset - lineRegion.getOffset();
72+
int start = line.lastIndexOf('{', offsetInLine);
7073
if (start < 0) {
7174
return null;
7275
}
73-
final int end = line.indexOf('}', offset - lineRegion.getOffset());
76+
++start;
77+
int end = line.indexOf('}', offsetInLine);
7478
if (end < 0) {
7579
return null;
7680
}
81+
final TypeNameNode tagName = JSDocTextUtils.getTag(
82+
line.toCharArray(), 0, start);
83+
if (tagName != null && JSDocTag.PARAM.equals(tagName.value())) {
84+
if (line.regionMatches(
85+
end - JSDocSupport.PARAM_OPTIONAL.length(),
86+
JSDocSupport.PARAM_OPTIONAL, 0,
87+
JSDocSupport.PARAM_OPTIONAL.length())) {
88+
end -= JSDocSupport.PARAM_OPTIONAL.length();
89+
}
90+
if (line.regionMatches(end - JSDocSupport.PARAM_DOTS.length(),
91+
JSDocSupport.PARAM_DOTS, 0,
92+
JSDocSupport.PARAM_DOTS.length())) {
93+
end -= JSDocSupport.PARAM_DOTS.length();
94+
}
95+
if (line.regionMatches(start, JSDocSupport.PARAM_DOTS, 0,
96+
JSDocSupport.PARAM_DOTS.length())) {
97+
start += JSDocSupport.PARAM_DOTS.length();
98+
}
99+
}
77100
final ITextEditor editor = (ITextEditor) getAdapter(ITextEditor.class);
78101
if (editor == null) {
79102
return null;
@@ -89,10 +112,9 @@ public IHyperlink[] detectHyperlinks(ITextViewer textViewer,
89112
}
90113
TypeInferencer2 inferencer2 = new TypeInferencer2();
91114
inferencer2.setModelElement(input);
92-
final int typeExpressionOffset = lineRegion.getOffset() + start + 1;
93-
final TypeNameNode selection = JSDocTypeUtil.findName(
94-
inferencer2, line.substring(start + 1, end), offset
95-
- typeExpressionOffset);
115+
final int typeExpressionOffset = lineRegion.getOffset() + start;
116+
final TypeNameNode selection = JSDocTypeUtil.findName(inferencer2,
117+
line.substring(start, end), offset - typeExpressionOffset);
96118
if (selection == null) {
97119
return null;
98120
}
@@ -165,7 +187,7 @@ public boolean visit(IModelElement element) {
165187

166188
@SuppressWarnings("serial")
167189
private static class ModelElementFound extends RuntimeException {
168-
private final IModelElement element;
190+
final IModelElement element;
169191

170192
public ModelElementFound(IModelElement element) {
171193
this.element = element;

0 commit comments

Comments
 (0)