Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NETBEANS-791] Highlights for the top line of JavaDoc are done on every line. #550

Merged
merged 4 commits into from Jul 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion javadoc/nbproject/project.properties
Expand Up @@ -16,7 +16,7 @@
# under the License.

javac.compilerargs=-Xlint:unchecked
javac.source=1.7
javac.source=1.8

# requires nb.javac for compiling of tests on Mac
requires.nb.javac=true
Expand Down
Expand Up @@ -20,6 +20,7 @@
package org.netbeans.modules.javadoc.highlighting;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Enumeration;
Expand All @@ -45,6 +46,7 @@
import org.netbeans.api.lexer.TokenHierarchyListener;
import org.netbeans.api.lexer.TokenId;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.api.lexer.TokenUtilities;
import org.netbeans.spi.editor.highlighting.HighlightsSequence;
import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer;
import org.openide.util.WeakListeners;
Expand All @@ -55,21 +57,26 @@
public class Highlighting extends AbstractHighlightsContainer implements TokenHierarchyListener {

private static final Logger LOG = Logger.getLogger(Highlighting.class.getName());

private static final String WS = " \t\n"; // NOI18N
private static final String JAPANESE_PERIOD = "\u3002"; // 。 NOI18N
private static final List<String> PERIODS = Arrays.asList(
JAPANESE_PERIOD
);

public static final String LAYER_ID = "org.netbeans.modules.javadoc.highlighting"; //NOI18N

private final AttributeSet fontColor;

private final Document document;
private TokenHierarchy<? extends Document> hierarchy = null;
private final AtomicLong version = new AtomicLong();

/** Creates a new instance of Highlighting */
public Highlighting(Document doc) {
AttributeSet firstLineFontColor = MimeLookup.getLookup(MimePath.get("text/x-java")).lookup(FontColorSettings.class).getTokenFontColors("javadoc-first-sentence"); //NOI18N
AttributeSet commentFontColor = MimeLookup.getLookup(MimePath.get("text/x-java")).lookup(FontColorSettings.class).getTokenFontColors("comment"); //NOI18N
if(firstLineFontColor != null && commentFontColor != null) {
Collection<Object> attrs = new LinkedList<Object>();
Collection<Object> attrs = new LinkedList<>();
for (Enumeration<?> e = firstLineFontColor.getAttributeNames(); e.hasMoreElements(); ) {
Object key = e.nextElement();
Object value = firstLineFontColor.getAttribute(key);
Expand Down Expand Up @@ -110,7 +117,7 @@ public HighlightsSequence getHighlights(int startOffset, int endOffset) {
public void tokenHierarchyChanged(TokenHierarchyEvent evt) {
TokenChange<?> tc = evt.tokenChange();
int affectedArea [] = null;

TokenSequence<? extends TokenId> seq = tc.currentTokenSequence();
if (seq.language().equals(JavadocTokenId.language())) {
// Change inside javadoc
Expand All @@ -131,7 +138,7 @@ public void tokenHierarchyChanged(TokenHierarchyEvent evt) {
// find out whether it really involves javadoc or not.
affectedArea = new int [] { tc.offset(), evt.affectedEndOffset() };
}

if (affectedArea != null) {
version.incrementAndGet();
fireHighlightsChange(affectedArea[0], affectedArea[1]);
Expand All @@ -147,13 +154,34 @@ public void tokenHierarchyChanged(TokenHierarchyEvent evt) {
if (seq.moveNext()) {
int start = seq.offset();
do {
String period = null;
int indexOfPeriod = -1;
for (String p : PERIODS) {
int index = TokenUtilities.indexOf(seq.token().text(), p);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be more appropriate if Javadoc lexer returned some specific tokenID japanese period char (and ordinary period char), since the period after the 1st sentence is a lexical element. But I fear that a new token ID could break existing lexer clients.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fear that a new token ID could break existing lexer clients.

Me, too...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sdedic So, I would like to avoid changing the JavadocLexer(java.lexer) because a new tokenID(e.g. JavadocTokenId.JAPANESE_DOT) is needed only for this feature at the moment. Even if someone adds the new tokenID, probably, we can notice that because I added unit tests to this feature.

However, if you prefer adding the tokenID, I'll try it :)

if (index != -1) {
if (indexOfPeriod == -1 || index < indexOfPeriod) {
indexOfPeriod = index;
period = p;
}
}
}

if (seq.token().id() == JavadocTokenId.DOT) {
if (seq.moveNext()) {
if (isWhiteSpace(seq.token())) {
return new int [] { start, seq.offset()};
}
seq.movePrevious();
}
} else if (period != null && indexOfPeriod != -1) {
// NETBEANS-791
int offset = indexOfPeriod + 1;
while (offset < seq.token().length()
&& isPeriod(seq.token().text().subSequence(offset, offset + 1))) {
// e.g. 。。。
offset++;
}
return new int[]{start, seq.offset() + offset};
} else if (seq.token().id() == JavadocTokenId.TAG) {
if (seq.movePrevious()) {
if (!seq.token().text().toString().trim().endsWith("{")) {
Expand All @@ -172,21 +200,24 @@ private static boolean isWhiteSpace(Token<? extends TokenId> token) {
if (token == null || token.id() != JavadocTokenId.OTHER_TEXT) {
return false;
}
String ws = " \t\n";
return ws.indexOf(token.text().charAt(0)) >= 0;
return WS.indexOf(token.text().charAt(0)) >= 0;
}

private static boolean isPeriod(CharSequence cs) {
return PERIODS.stream().anyMatch(period -> TokenUtilities.equals(cs, period));
}

private final class HSImpl implements HighlightsSequence {
private long version;
private TokenHierarchy<? extends Document> scanner;

private final long version;
private final TokenHierarchy<? extends Document> scanner;
private List<TokenSequence<? extends TokenId>> sequences;
private int startOffset;
private int endOffset;
private final int startOffset;
private final int endOffset;

private List<Integer> lines = null;
private int linesIdx = -1;

public HSImpl(long version, TokenHierarchy<? extends Document> scanner, int startOffset, int endOffset) {
this.version = version;
this.scanner = scanner;
Expand All @@ -195,10 +226,11 @@ public HSImpl(long version, TokenHierarchy<? extends Document> scanner, int star
this.sequences = null;
}

@Override
public boolean moveNext() {
synchronized (Highlighting.this) {
checkVersion();

if (sequences == null) {
// initialize
TokenSequence<?> tokenSequence = scanner.tokenSequence();
Expand All @@ -208,7 +240,7 @@ public boolean moveNext() {
return false;
}
TokenSequence<?> seq = tokenSequence.subSequence(startOffset, endOffset);
sequences = new ArrayList<TokenSequence<? extends TokenId>>();
sequences = new ArrayList<>();
sequences.add(seq);
}

Expand All @@ -217,11 +249,11 @@ public boolean moveNext() {
linesIdx += 2;
return true;
}

lines = null;
linesIdx = -1;
}

while (!sequences.isEmpty()) {
TokenSequence<? extends TokenId> seq = sequences.get(sequences.size() - 1);

Expand Down Expand Up @@ -257,10 +289,11 @@ public boolean moveNext() {
}
}

@Override
public int getStartOffset() {
synchronized (Highlighting.this) {
checkVersion();

if (sequences == null) {
throw new NoSuchElementException("Call moveNext() first."); //NOI18N
}
Expand All @@ -273,10 +306,11 @@ public int getStartOffset() {
}
}

@Override
public int getEndOffset() {
synchronized (Highlighting.this) {
checkVersion();

if (sequences == null) {
throw new NoSuchElementException("Call moveNext() first."); //NOI18N
}
Expand All @@ -289,10 +323,11 @@ public int getEndOffset() {
}
}

@Override
public AttributeSet getAttributes() {
synchronized (Highlighting.this) {
checkVersion();

if (sequences == null) {
throw new NoSuchElementException("Call moveNext() first."); //NOI18N
}
Expand All @@ -304,17 +339,17 @@ public AttributeSet getAttributes() {
}
}
}

private void checkVersion() {
if (this.version != Highlighting.this.version.get()) {
throw new ConcurrentModificationException();
}
}

private List<Integer> splitByLines(int sentenceStart, int sentenceEnd) {
ArrayList<Integer> lines = new ArrayList<Integer>();
ArrayList<Integer> lines = new ArrayList<>();
int offset = sentenceStart;

try {
while (offset < sentenceEnd) {
Element lineElement = document.getDefaultRootElement().getElement(
Expand All @@ -325,9 +360,9 @@ private List<Integer> splitByLines(int sentenceStart, int sentenceEnd) {

String line = document.getText(rowStart, rowEnd - rowStart);
int idx = 0;
while (idx < line.length() &&
(line.charAt(idx) == ' ' ||
line.charAt(idx) == '\t' ||
while (idx < line.length() &&
(line.charAt(idx) == ' ' ||
line.charAt(idx) == '\t' ||
line.charAt(idx) == '*'))
{
idx++;
Expand All @@ -343,7 +378,7 @@ private List<Integer> splitByLines(int sentenceStart, int sentenceEnd) {
} catch (BadLocationException e) {
LOG.log(Level.WARNING, "Can't determine javadoc first sentence", e);
}

return lines.isEmpty() ? null : lines;
}
} // End of HSImpl class
Expand Down