blob: adf9c10bc78f1c85c36309bb7a074808905a374c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.javascript2.editor;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import org.netbeans.api.editor.mimelookup.MimePath;
import org.netbeans.api.editor.mimelookup.MimeRegistration;
import org.netbeans.api.lexer.Token;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.modules.javascript2.lexer.api.JsTokenId;
import org.netbeans.modules.javascript2.lexer.api.LexUtilities;
import org.netbeans.spi.editor.typinghooks.CamelCaseInterceptor;
/**
* Replaced old KeystrokeHandler and takes care about delete word actions.
*
* @author Martin Fousek <marfous@netbeans.org>
*/
public class JsCamelCaseInterceptor implements CamelCaseInterceptor {
@Override
public boolean beforeChange(MutableContext context) throws BadLocationException {
return false;
}
@Override
public void change(final MutableContext context) throws BadLocationException {
final Document doc = context.getDocument();
final int offset = context.getOffset();
final boolean reverse = context.isBackward();
doc.render(() -> {
int nextOffset = getWordOffset(doc, offset, reverse);
context.setNextWordOffset(nextOffset);
});
}
@Override
public void afterChange(MutableContext context) throws BadLocationException {
}
@Override
public void cancelled(MutableContext context) {
}
protected static int getWordOffset(Document doc, int offset, boolean reverse) {
TokenSequence<? extends JsTokenId> ts = LexUtilities.getJsTokenSequence(doc, offset);
if (ts == null) {
return -1;
}
ts.move(offset);
if (!ts.moveNext() && !ts.movePrevious()) {
return -1;
}
if (reverse && ts.offset() == offset && !ts.movePrevious()) {
return -1;
}
Token<? extends JsTokenId> token = ts.token();
if (token.id() == JsTokenId.WHITESPACE) {
// Eat up spaces
if ((reverse && ts.offset() < offset) || (!reverse && ts.offset() > offset)) {
return ts.offset();
}
}
if (token.id() == JsTokenId.IDENTIFIER || token.id() == JsTokenId.PRIVATE_IDENTIFIER) {
String image = token.text().toString();
int imageLength = image.length();
int offsetInImage = offset - ts.offset();
if (reverse) {
return getPreviousIdentifierWordOffset(ts, image, imageLength, offsetInImage);
} else {
return getNextIdentifierWordOffset(ts, image, imageLength, offsetInImage);
}
}
return -1;
}
private static int getPreviousIdentifierWordOffset(TokenSequence<? extends JsTokenId> ts, String image, int imageLength, int offsetInImage) {
offsetInImage = offsetInImage - 1;
if (offsetInImage < 0) {
return -1;
}
if (offsetInImage < imageLength && Character.isUpperCase(image.charAt(offsetInImage))) {
for (int i = offsetInImage - 1; i >= 0; i--) {
char charAtI = image.charAt(i);
if (!Character.isUpperCase(charAtI)) {
// return offset of previous uppercase char in the identifier
return ts.offset() + i + 1;
}
}
return ts.offset();
} else {
for (int i = offsetInImage - 1; i >= 0; i--) {
char charAtI = image.charAt(i);
if (Character.isUpperCase(charAtI)) {
// now skip over previous uppercase chars in the identifier
for (int j = i; j >= 0; j--) {
char charAtJ = image.charAt(j);
if (!Character.isUpperCase(charAtJ)) {
// return offset of previous uppercase char in the identifier
return ts.offset() + j + 1;
}
}
return ts.offset();
}
}
return ts.offset();
}
}
private static int getNextIdentifierWordOffset(TokenSequence<? extends JsTokenId> ts, String image, int imageLength, int offsetInImage) {
int start = offsetInImage + 1;
if (offsetInImage < 0 || offsetInImage >= image.length()) {
return -1;
}
if (Character.isUpperCase(image.charAt(offsetInImage))) {
// if starting from a Uppercase char, first skip over follwing upper case chars
for (int i = start; i < imageLength; i++) {
char charAtI = image.charAt(i);
if (!Character.isUpperCase(charAtI)) {
break;
}
start++;
}
}
for (int i = start; i < imageLength; i++) {
char charAtI = image.charAt(i);
if (Character.isUpperCase(charAtI)) {
return ts.offset() + i;
}
}
return ts.offset() + imageLength;
}
@MimeRegistration(mimeType = "text/javascript", service = CamelCaseInterceptor.Factory.class)
public static class Factory implements CamelCaseInterceptor.Factory {
@Override
public CamelCaseInterceptor createCamelCaseInterceptor(MimePath mimePath) {
return new JsCamelCaseInterceptor();
}
}
}