blob: 3d67cff687f6c5382f95eae44e8585098e1e4ae7 [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 com.oracle.js.parser.ir.Block;
import com.oracle.js.parser.ir.CallNode;
import com.oracle.js.parser.ir.FunctionNode;
import com.oracle.js.parser.ir.IdentNode;
import com.oracle.js.parser.ir.LexicalContext;
import com.oracle.js.parser.ir.LiteralNode;
import com.oracle.js.parser.ir.Node;
import com.oracle.js.parser.ir.VarNode;
import com.oracle.js.parser.ir.visitor.NodeVisitor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import org.netbeans.api.lexer.Token;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.modules.csl.api.KeystrokeHandler;
import org.netbeans.modules.csl.api.OffsetRange;
import org.netbeans.modules.csl.spi.ParserResult;
import org.netbeans.modules.javascript2.lexer.api.JsTokenId;
import org.netbeans.modules.javascript2.lexer.api.LexUtilities;
import org.netbeans.modules.javascript2.editor.parser.JsParserResult;
/**
*
* @author Petr Pisl
*/
class JsKeyStrokeHandler implements KeystrokeHandler {
public JsKeyStrokeHandler() {
}
@Override
public boolean beforeCharInserted(Document doc, int caretOffset, JTextComponent target, char ch) throws BadLocationException {
return false;
}
@Override
public boolean afterCharInserted(Document doc, int caretOffset, JTextComponent target, char ch) throws BadLocationException {
return false;
}
@Override
public boolean charBackspaced(Document doc, int caretOffset, JTextComponent target, char ch) throws BadLocationException {
return false;
}
@Override
public int beforeBreak(Document doc, int caretOffset, JTextComponent target) throws BadLocationException {
return -1;
}
@Override
public OffsetRange findMatching(Document doc, int caretOffset) {
return OffsetRange.NONE;
}
@Override
public List<OffsetRange> findLogicalRanges(final ParserResult info, final int caretOffset) {
final Set<OffsetRange> ranges = new LinkedHashSet();
if (info instanceof JsParserResult) {
final JsParserResult jsParserResult = (JsParserResult) info;
FunctionNode root = jsParserResult.getRoot();
final TokenSequence<? extends JsTokenId> ts = LexUtilities.getJsTokenSequence(jsParserResult.getSnapshot(), caretOffset);
if (root != null && ts != null) {
root.accept(new NodeVisitor(new LexicalContext()) {
final HashSet<String> referencedFunction = new HashSet();
private OffsetRange getOffsetRange(IdentNode node) {
// because the truffle parser doesn't set correctly the finish offset, when there are comments after the indent node
return new OffsetRange(node.getStart(), node.getStart() + node.getName().length());
}
private OffsetRange getOffsetRange(Node node) {
if (node instanceof FunctionNode) {
return getOffsetRange((FunctionNode) node);
}
if (node instanceof IdentNode) {
return getOffsetRange((IdentNode)node);
}
return new OffsetRange(node.getStart(), node.getFinish());
}
private OffsetRange getOffsetRange(FunctionNode node) {
return new OffsetRange(com.oracle.js.parser.Token.descPosition(node.getFirstToken()),
com.oracle.js.parser.Token.descPosition(node.getLastToken()) + com.oracle.js.parser.Token.descLength(node.getLastToken()));
}
@Override
protected boolean enterDefault(Node node) {
OffsetRange range = getOffsetRange(node);
if (node != null && range.getStart() <= caretOffset && caretOffset <= range.getEnd()) {
ranges.add(new OffsetRange(range.getStart(),range.getEnd()));
return super.enterDefault(node);
}
return false;
}
@Override
public boolean enterFunctionNode(FunctionNode node) {
OffsetRange fnRange = getOffsetRange(node);
if (node.isProgram()) {
ranges.add(new OffsetRange(0, jsParserResult.getSnapshot().getText().length()));
if (fnRange.getStart() <= caretOffset && caretOffset <= fnRange.getEnd()) {
ranges.add(new OffsetRange(fnRange.getStart(), fnRange.getEnd()));
}
processFunction(node);
return false;
}
if (fnRange.getStart() <= caretOffset && caretOffset <= fnRange.getEnd()) {
ranges.add(new OffsetRange(fnRange.getStart(), fnRange.getEnd()));
int firstParamOffset = fnRange.getEnd();
int lastParamOffset = -1;
for (Node param : node.getParameters()) {
OffsetRange paramRange = getOffsetRange(param);
if (param.getStart() < firstParamOffset) {
firstParamOffset = paramRange.getStart();
}
if (param.getFinish() > lastParamOffset) {
lastParamOffset = paramRange.getEnd();
}
}
if (node.getParameters().size() > 1 && firstParamOffset < lastParamOffset && firstParamOffset <= caretOffset && caretOffset <= lastParamOffset) {
ranges.add(new OffsetRange(firstParamOffset, lastParamOffset));
for (Node param : node.getParameters()) {
param.accept(this);
}
}
if (fnRange.getStart() <= caretOffset && caretOffset <= fnRange.getEnd()) {
ranges.add(new OffsetRange(fnRange.getStart(), fnRange.getEnd()));
processFunction(node);
}
}
return false;
}
private void processFunction(FunctionNode node) {
Block body = node.getBody();
for (Node statement : body.getStatements()) {
OffsetRange stRange = getOffsetRange(statement);
if (stRange.getStart() <= caretOffset && caretOffset <= stRange.getEnd()) {
statement.accept(this);
}
}
}
@Override
public boolean enterVarNode(VarNode node) {
OffsetRange range = getOffsetRange(node);
ts.move(range.getStart());
Token<? extends JsTokenId> token = LexUtilities.findPreviousIncluding(ts, Arrays.asList(JsTokenId.KEYWORD_VAR));
if (token != null && ts.offset() <= caretOffset && caretOffset <= range.getEnd()) {
ranges.add(new OffsetRange(ts.offset(), range.getEnd()));
return enterDefault(node);
}
return false;
}
@Override
public boolean enterLiteralNode(LiteralNode node) {
if (node.isString() && node.getStart() <= caretOffset && caretOffset <= node.getFinish()) {
// include the " or '
ranges.add(new OffsetRange(node.getStart() - 1, node.getFinish() + 1));
}
return super.enterLiteralNode(node);
}
@Override
public boolean enterCallNode(CallNode node) {
if (node.getArgs().size() > 1) {
OffsetRange range = getOffsetRange(node);
if (range.getStart() <= caretOffset && caretOffset <= range.getEnd()) {
ranges.add(range);
int firstArgOffset = node.getFinish();
int lastArgOffset = -1;
for (Node arg : node.getArgs()) {
OffsetRange argRange = getOffsetRange(arg);
if (argRange.getStart() < firstArgOffset) {
firstArgOffset = argRange.getStart();
if (arg instanceof LiteralNode && ((LiteralNode)arg).isString()) {
firstArgOffset--;
}
}
if (argRange.getEnd() > lastArgOffset) {
lastArgOffset = argRange.getEnd();
if (arg instanceof LiteralNode && ((LiteralNode)arg).isString()) {
lastArgOffset++;
}
}
}
if (firstArgOffset <= caretOffset && caretOffset <= lastArgOffset) {
ranges.add(new OffsetRange(firstArgOffset, lastArgOffset));
}
for (Node arg : node.getArgs()) {
OffsetRange argRange = getOffsetRange(arg);
if (argRange.getStart() <= caretOffset && caretOffset <= argRange.getEnd()) {
arg.accept(this);
}
}
}
return false;
} else {
return super.enterCallNode(node);
}
}
});
}
}
final ArrayList<OffsetRange> retval = new ArrayList(ranges);
Collections.reverse(retval);
return retval;
}
@Override
public int getNextWordOffset(Document doc, int caretOffset, boolean reverse) {
return -1;
}
}