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

Add Haxe-specific double-click selection logic for strings and comments. #644

Merged
merged 2 commits into from Jun 9, 2017
Jump to file or symbol
Failed to load files and symbols.
+240 −0
Diff settings

Always

Just for now

View
@@ -727,6 +727,9 @@
<quoteHandler fileType="Haxe" className="com.intellij.plugins.haxe.ide.HaxeQuoteHandler"/>
<extendWordSelectionHandler implementation="com.intellij.plugins.haxe.editor.actions.wordSelection.HaxeStringSelectioner" />
<extendWordSelectionHandler implementation="com.intellij.plugins.haxe.editor.actions.wordSelection.HaxeCommentSelectioner" />
<completion.contributor language="Haxe" implementationClass="com.intellij.plugins.haxe.ide.HaxeControllingCompletionContributor" order="FIRST"/>
<completion.contributor language="any" implementationClass="com.intellij.plugins.haxe.ide.HaxeKeywordCompletionContributor"/>
<completion.contributor language="Haxe" implementationClass="com.intellij.plugins.haxe.ide.HaxeClassNameCompletionContributor"/>
@@ -0,0 +1,92 @@
/*
* Copyright 2017 Eric Bishton
*
* Licensed 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 com.intellij.plugins.haxe.editor.actions.wordSelection;
import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandlerBase;
import com.intellij.lang.Commenter;
import com.intellij.lang.LanguageCommenters;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.plugins.haxe.HaxeLanguage;
import com.intellij.plugins.haxe.ide.HaxeCommenter;
import com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypeSets;
import com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypes;
import com.intellij.plugins.haxe.lang.psi.impl.HaxePsiDocComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import java.util.ArrayList;
import java.util.List;
/**
* Created by ebishton on 6/6/17.
*/
public class HaxeCommentSelectioner extends ExtendWordSelectionHandlerBase {
@Override
public boolean canSelect(PsiElement e) {
return e.getLanguage().equals(HaxeLanguage.INSTANCE) && HaxeTokenTypeSets.ONLY_COMMENTS.contains(e.getNode().getElementType());
}
@Override
public List<TextRange> select(PsiElement e, CharSequence editorText, int cursorOffset, Editor editor) {
final TextRange originalRange = e.getTextRange();
// For the error condition, let the superclass log the standard error and throw an exception.
if (originalRange.getEndOffset() > editorText.length()) {
super.select(e, editorText, cursorOffset, editor);
}
final TextRange foundRange = SelectionUtil.selectToken(e, cursorOffset);
final CharSequence token = editorText.subSequence(foundRange.getStartOffset(), foundRange.getEndOffset());
final List<TextRange> ranges = new ArrayList<TextRange>(1);
if (isCommentToken(e, token)) {
ranges.addAll(expandToWholeLine(editorText, originalRange, true));
} else {
// Use the more limited defintion of a word when selecting inside of a comment.
ranges.add(SelectionUtil.selectWord(e, cursorOffset));
}
return ranges;
}
private boolean isCommentToken(PsiElement e, CharSequence token) {
final Commenter commenter = LanguageCommenters.INSTANCE.forLanguage(HaxeLanguage.INSTANCE);
assert(commenter instanceof HaxeCommenter);
final IElementType tokenType = e.getNode().getElementType();
if (tokenType == HaxeTokenTypeSets.DOC_COMMENT) {
// XXX: Should we be checking that the token is at the beginning or end of the element?
// Or, that the line prefix is actually the first thing on the line?
return ((HaxeCommenter)commenter).getDocumentationCommentLinePrefix().contentEquals(token)
|| ((HaxeCommenter)commenter).getDocumentationCommentPrefix().contentEquals(token)
|| ((HaxeCommenter)commenter).getDocumentationCommentSuffix().contentEquals(token)
// A lot of folks don't use the proper doc comment terminator "**/", and the compiler
// accepts a normal block comment terminator "*/".
|| commenter.getBlockCommentSuffix().contentEquals(token);
} else if (tokenType == HaxeTokenTypeSets.MML_COMMENT) {
return commenter.getBlockCommentPrefix().contentEquals(token)
|| commenter.getBlockCommentSuffix().contentEquals(token);
}
return commenter.getLineCommentPrefix().contentEquals(token);
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2017 Eric Bishton
*
* Licensed 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 com.intellij.plugins.haxe.editor.actions.wordSelection;
import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandlerBase;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.plugins.haxe.HaxeLanguage;
import com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypes;
import com.intellij.plugins.haxe.lang.psi.HaxePsiToken;
import com.intellij.psi.PsiElement;
import java.util.ArrayList;
import java.util.List;
/**
* Created by ebishton on 6/6/17.
*/
public class HaxeStringSelectioner extends ExtendWordSelectionHandlerBase {
@Override
public boolean canSelect(PsiElement e) {
return (e instanceof HaxePsiToken
&& e.getLanguage().equals(HaxeLanguage.INSTANCE)
&& HaxeTokenTypes.REGULAR_STRING_PART.equals(((HaxePsiToken)e).getTokenType()));
}
@Override
public List<TextRange> select(PsiElement e, CharSequence editorText, int cursorOffset, Editor editor) {
final TextRange originalRange = e.getTextRange();
// For the error condition, let the superclass log the standard error and throw an exception.
if (originalRange.getEndOffset() > editorText.length()) {
super.select(e, editorText, cursorOffset, editor);
}
// XXX: Check for the range actually being in the element? Shouldn't happen.
final List<TextRange> ranges = new ArrayList<TextRange>(1);
ranges.add(SelectionUtil.selectWord(e, cursorOffset));
return ranges;
}
}
@@ -0,0 +1,88 @@
/*
* Copyright 2017 Eric Bishton
*
* Licensed 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 com.intellij.plugins.haxe.editor.actions.wordSelection;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
/**
* Created by ebishton on 6/6/17.
*/
public class SelectionUtil {
private SelectionUtil() {}
static public boolean isWordDelimiter(char c) {
return !(Character.isAlphabetic(c) || Character.isDigit(c));
}
static public boolean isWhitespace(char c) {
return Character.isWhitespace(c);
}
/**
* Selects the word under the caret, using the definition of isWordDelimiter.
*
* @param e
* @param offset
* @return
*/
static public TextRange selectWord(PsiElement e, int offset) {
return findTokenLimits(e, offset, false);
}
/**
* Selects the token under the caret, using only whitespace as a delimiter.
*
* @param e
* @param offset
* @return
*/
static public TextRange selectToken(PsiElement e, int offset) {
return findTokenLimits(e, offset, true);
}
static private boolean isDelimiter(char c, boolean useWhitespace) {
return useWhitespace ? isWhitespace(c) : isWordDelimiter(c);
}
static private TextRange findTokenLimits(PsiElement e, int offset, boolean useWhitespace) {
final int tokenOffset = e.getTextOffset();
int startPos = offset - tokenOffset;
int endPos = startPos;
final String text = e.getText();
final int length = text.length();
// While scanning for the beginning and end of a word, we don't have to
// worry about escaped characters ("\n") because they are split apart into
// separate REGULAR_STRING_PART lexemes. Thus, they are automatically string
// separators.
// Scan backward for the start of the word. If the offset is a space, then
// we want the word before the startPos.
while (startPos > 0 && !isDelimiter(text.charAt(startPos - 1), useWhitespace)) {
--startPos;
}
// Scan forward to find the end of the word. If this offset is whitespace, then
// we are done.
while (endPos < length && !isDelimiter(text.charAt(endPos), useWhitespace)) {
++endPos;
}
return new TextRange(startPos + tokenOffset, endPos + tokenOffset);
}
}
ProTip! Use n and p to navigate between commits in a pull request.