blob: 4fefbbae30e828ef0e929c7b66391b2748e066ea [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 David Green.
* 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:
* David Green - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.internal.wikitext.commonmark.inlines;
import com.google.common.base.CharMatcher;
import com.google.common.base.Optional;
public class PotentialEmphasisSpan extends SourceSpan {
@Override
public Optional<? extends Inline> createInline(Cursor cursor) {
char c = cursor.getChar();
if ((c == '_' || c == '*') && !currentPositionIsEscaped(cursor)) {
int length = lengthMatching(cursor, c);
boolean leftFlanking = isLeftFlanking(cursor, length);
boolean rightFlanking = isRightFlanking(cursor, length);
boolean canOpen = leftFlanking;
boolean canClose = rightFlanking;
if (c == '_') {
canOpen = leftFlanking && (!rightFlanking || isPunctuation(charBefore(cursor)));
canClose = rightFlanking && (!leftFlanking || isPunctuation(charAfter(cursor, length)));
}
return Optional.of(new PotentialEmphasisDelimiter(cursor.getLineAtOffset(), cursor.getOffset(), length,
cursor.getTextAtOffset(length), canOpen, canClose));
}
return Optional.absent();
}
boolean isLeftFlanking(Cursor cursor, int length) {
char charBefore = charBefore(cursor);
char charAfter = charAfter(cursor, length);
return !isWhitespace(charAfter)
&& !(isPunctuation(charAfter) && !isWhitespace(charBefore) && !isPunctuation(charBefore));
}
private char charAfter(Cursor cursor, int length) {
return cursor.hasNext(length) ? cursor.getNext(length) : '\n';
}
private char charBefore(Cursor cursor) {
return cursor.hasPrevious() ? cursor.getPrevious() : '\n';
}
boolean isRightFlanking(Cursor cursor, int length) {
char charBefore = charBefore(cursor);
char charAfter = charAfter(cursor, length);
return !isWhitespace(charBefore)
&& !(isPunctuation(charBefore) && !isWhitespace(charAfter) && !isPunctuation(charAfter));
}
private boolean isWhitespace(char c) {
return CharMatcher.WHITESPACE.matches(c);
}
private boolean isPunctuation(char c) {
String punctuation = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`,{|}~";
return punctuation.indexOf(c) >= 0;
}
private boolean currentPositionIsEscaped(Cursor cursor) {
int backslashCount = 0;
for (int x = 1; cursor.hasPrevious(x) && cursor.getPrevious(x) == '\\'; ++x) {
++backslashCount;
}
return backslashCount % 2 == 1;
}
private int lengthMatching(Cursor cursor, char c) {
int x = 1;
while (cursor.hasNext(x) && cursor.getNext(x) == c) {
++x;
}
return x;
}
static boolean isLetterOrDigit(char previous) {
return (previous >= '0' && previous <= '9') || (previous >= 'A' && previous <= 'Z')
|| (previous >= 'a' && previous <= 'z');
}
}