blob: db1fc6faa4376538c9a0dc11571f5dfcd8eec5ae [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 javax.swing.text.JTextComponent;
import org.netbeans.api.editor.mimelookup.MimePath;
import org.netbeans.api.editor.mimelookup.MimeRegistration;
import org.netbeans.api.lexer.Language;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
import org.netbeans.modules.csl.api.EditorOptions;
import org.netbeans.modules.javascript2.lexer.api.JsTokenId;
import org.netbeans.modules.javascript2.lexer.api.LexUtilities;
import org.netbeans.modules.javascript2.editor.options.OptionsUtils;
import org.netbeans.spi.editor.typinghooks.DeletedTextInterceptor;
/**
*
* @author Petr Hejl
*/
public class JsDeletedTextInterceptor implements DeletedTextInterceptor {
private final Language<JsTokenId> language;
private final boolean singleQuote;
private final boolean comments;
public JsDeletedTextInterceptor(Language<JsTokenId> language, boolean singleQuote, boolean comments) {
this.language = language;
this.singleQuote = singleQuote;
this.comments = comments;
}
private boolean isInsertMatchingEnabled() {
EditorOptions options = EditorOptions.get(language.mimeType());
if (options != null) {
return options.getMatchBrackets();
}
return true;
}
private boolean isSmartQuotingEnabled() {
return OptionsUtils.forLanguage(language).autoCompletionSmartQuotes();
}
@Override
public void afterRemove(Context context) throws BadLocationException {
}
@Override
public boolean beforeRemove(Context context) throws BadLocationException {
return false;
}
@Override
public void cancelled(Context context) {
}
@Override
public void remove(Context context) throws BadLocationException {
// Can be replaced by LineDocument, except perhaps the getChars(), which is optimized.
Document doc = context.getDocument();
int dotPos = context.getOffset() - 1;
// FIXME
char ch = context.getText().charAt(0);
JTextComponent target = context.getComponent();
switch (ch) {
case ' ': {
if (comments) {
// Backspacing over "// " ? Delete the "//" too!
TokenSequence<? extends JsTokenId> ts = LexUtilities.getPositionedSequence(
doc, dotPos, language);
if (ts != null && ts.token().id() == JsTokenId.LINE_COMMENT) {
if (ts.offset() == dotPos - 2 && !doc.getText(dotPos, 1).equals(" ")) { //NOI18N
doc.remove(dotPos-2, 2);
target.getCaret().setDot(dotPos-2);
return;
}
}
}
break;
}
case '{':
case '(':
case '[': { // and '{' via fallthrough
if (isInsertMatchingEnabled()) {
char tokenAtDot = LexUtilities.getTokenChar(doc, dotPos, language);
if (((tokenAtDot == ']') &&
(LexUtilities.getTokenBalance(doc, JsTokenId.BRACKET_LEFT_BRACKET, JsTokenId.BRACKET_RIGHT_BRACKET, dotPos, language) != 0)) ||
((tokenAtDot == ')') &&
(LexUtilities.getTokenBalance(doc, JsTokenId.BRACKET_LEFT_PAREN, JsTokenId.BRACKET_RIGHT_PAREN, dotPos, language) != 0)) ||
((tokenAtDot == '}') &&
(LexUtilities.getTokenBalance(doc, JsTokenId.BRACKET_LEFT_CURLY, JsTokenId.BRACKET_RIGHT_CURLY, dotPos, language) != 0))) {
doc.remove(dotPos, 1);
}
}
break;
}
case '/': {
if (comments) {
// Backspacing over "//" ? Delete the whole "//"
TokenSequence<? extends JsTokenId> ts = LexUtilities.getPositionedSequence(
doc, dotPos, language);
if (ts != null && ts.token().id() == JsTokenId.REGEXP_BEGIN) {
if (ts.offset() == dotPos-1) {
doc.remove(dotPos-1, 1);
target.getCaret().setDot(dotPos-1);
return;
}
} else if (ts != null && ts.token().id() == JsTokenId.STRING) {
// ignore deleting of '/' inside a string
break;
}
}
// Fallthrough for match-deletion
}
case '\'':
if (!singleQuote) {
break;
}
case '`':
if (!singleQuote) {
break;
}
case '\"': {
if (isSmartQuotingEnabled()) {
CharSequence match = DocumentUtilities.getText(doc, dotPos, 1);
if ((match != null) && (match.length() > 0) && (match.charAt(0) == ch)) {
doc.remove(dotPos, 1);
}
}
break;
} // TODO: Test other auto-completion chars, like %q-foo-
default:
break;
}
}
@MimeRegistration(mimeType = JsTokenId.JAVASCRIPT_MIME_TYPE, service = DeletedTextInterceptor.Factory.class)
public static class JsFactory implements DeletedTextInterceptor.Factory {
@Override
public DeletedTextInterceptor createDeletedTextInterceptor(MimePath mimePath) {
return new JsDeletedTextInterceptor(JsTokenId.javascriptLanguage(), true, true);
}
}
@MimeRegistration(mimeType = JsTokenId.JSON_MIME_TYPE, service = DeletedTextInterceptor.Factory.class)
public static class JsonFactory implements DeletedTextInterceptor.Factory {
@Override
public DeletedTextInterceptor createDeletedTextInterceptor(MimePath mimePath) {
return new JsDeletedTextInterceptor(JsTokenId.jsonLanguage(), false, false);
}
}
}