/*
* Copyright 2011-2017 Kay Stenschke
*
* 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.kstenschke.shifter.models;
import com.intellij.openapi.editor.Editor;
import com.kstenschke.shifter.utils.UtilsFile;
import com.kstenschke.shifter.utils.UtilsTextual;
import org.jetbrains.annotations.Nullable;
/**
* Manager of "shiftable" word shiftableTypes - detects word type to evoke resp. shifting
*/
public class ShiftableTypesManager {
public static final int TYPE_UNKNOWN = 0;
// Dictionary (list of strings) based shiftableTypes
private static final int TYPE_ACCESSIBILITY = 1;
private static final int TYPE_DICTIONARY_WORD_EXT_SPECIFIC = 2;
private static final int TYPE_DICTIONARY_WORD_GLOBAL = 3;
public static final int TYPE_NUMERIC_VALUE = 10;
private static final int TYPE_NUMERIC_POSTFIXED_STRING = 11;
public static final int TYPE_ROMAN_NUMERAL = 12;
// Generic shiftableTypes
public static final int TYPE_QUOTED_STRING = 20;
private static final int TYPE_HTML_ENCODABLE_STRING = 21;
private static final int TYPE_CAMEL_CASE_STRING = 22;
// <, >, +, -
private static final int TYPE_OPERATOR_SIGN = 30;
public static final int TYPE_LOGICAL_OPERATOR = 31;
private static final int TYPE_MONO_CHARACTER_STRING = 32;
private static final int TYPE_RGB_COLOR = 40;
// %, cm, em, in, pt, px, rem, vw, vh, vmin, vmax
public static final int TYPE_CSS_UNIT = 41;
private static final int TYPE_DOC_COMMENT_TAG = 50;
private static final int TYPE_DOC_COMMENT_DATATYPE = 51;
public static final int TYPE_PHP_VARIABLE = 60;
public static final int TYPE_PHP_DOC_PARAM_LINE = 61;
public static final int TYPE_JS_VARIABLES_DECLARATIONS = 62;
public static final int TYPE_SIZZLE_SELECTOR = 63;
// @see trailing comment shifting is implemented in ActionsPerformer.shiftSelection()
public static final int TYPE_TRAILING_COMMENT = 64;
private static final int TYPE_TERNARY_EXPRESSION = 70;
private static final int TYPE_WORDS_TUPEL = 71;
// Word type objects
private com.kstenschke.shifter.models.shiftableTypes.StaticWordType wordTypeAccessibilities;
private com.kstenschke.shifter.models.shiftableTypes.DictionaryTerm typeDictionaryTerm;
// Generic shiftableTypes (calculated when shifted)
private com.kstenschke.shifter.models.shiftableTypes.CssUnit typePixelValue;
private com.kstenschke.shifter.models.shiftableTypes.DocCommentTag typeTagInDocComment;
private com.kstenschke.shifter.models.shiftableTypes.DocCommentType typeDataTypeInDocComment;
private com.kstenschke.shifter.models.shiftableTypes.NumericValue typeNumericValue;
private com.kstenschke.shifter.models.shiftableTypes.OperatorSign typeOperatorSign;
private com.kstenschke.shifter.models.shiftableTypes.PhpVariable typePhpVariable;
private com.kstenschke.shifter.models.shiftableTypes.RbgColor typeRgbColor;
private com.kstenschke.shifter.models.shiftableTypes.RomanNumber typeRomanNumber;
private com.kstenschke.shifter.models.shiftableTypes.StringMonoCharacter typeMonoCharacterString;
private com.kstenschke.shifter.models.shiftableTypes.Tupel wordsTupel;
private com.kstenschke.shifter.models.shiftableTypes.QuotedString typeQuotedString;
/**
* Detect word type (get the one w/ highest priority to be shifted) of given string
*
* @param word Word whose type shall be identified
* @param prefixChar Prefix character
* @param postfixChar Postfix character
* @param isLastLineInDocument
* @param line Whole line the caret is in
* @param filename Name of edited file
* @return int
*/
public int getWordType(String word, String prefixChar, String postfixChar, boolean isLastLineInDocument, String line, String filename) {
// Selected code line w/ trailing //-comment: moves the comment into a new line before the code
if (com.kstenschke.shifter.models.shiftableTypes.TrailingComment.isTrailingComment(word, postfixChar, isLastLineInDocument)) {
return TYPE_TRAILING_COMMENT;
}
if (com.kstenschke.shifter.models.shiftableTypes.PhpDocParam.isPhpDocParamLine(line) && !com.kstenschke.shifter.models.shiftableTypes.PhpDocParam.containsDataType(line)) {
// return TYPE_PHP_DOC_PARAM_LINE;
// PHP doc param line is handled in line-shifting fallback
return TYPE_UNKNOWN;
}
// PHP variable (must be prefixed w/ "$")
this.typePhpVariable = new com.kstenschke.shifter.models.shiftableTypes.PhpVariable();
if (this.typePhpVariable.isPhpVariable(word)) {
return TYPE_PHP_VARIABLE;
}
if (com.kstenschke.shifter.models.shiftableTypes.JsVariablesDeclarations.isJsVariables(word)) {
return TYPE_JS_VARIABLES_DECLARATIONS;
}
if (com.kstenschke.shifter.models.shiftableTypes.SizzleSelector.isSelector(word)) {
return TYPE_SIZZLE_SELECTOR;
}
// DocComment shiftableTypes (must be prefixed w/ "@")
this.typeDataTypeInDocComment = new com.kstenschke.shifter.models.shiftableTypes.DocCommentType();
if (this.typeDataTypeInDocComment.isDocCommentTypeLineContext(line)) {
this.typeTagInDocComment = new com.kstenschke.shifter.models.shiftableTypes.DocCommentTag();
if (prefixChar.matches("@") && this.typeTagInDocComment.isDocCommentTag(prefixChar, line)) {
return TYPE_DOC_COMMENT_TAG;
}
if (this.typeDataTypeInDocComment.isDocCommentType(prefixChar, line)) {
return TYPE_DOC_COMMENT_DATATYPE;
}
}
// Object visibility
if (!"@".equals(prefixChar) && this.isKeywordAccessType(word)) {
return TYPE_ACCESSIBILITY;
}
// File extension specific term in dictionary
this.typeDictionaryTerm = new com.kstenschke.shifter.models.shiftableTypes.DictionaryTerm();
String fileExtension = UtilsFile.extractFileExtension(filename);
if (fileExtension != null && this.typeDictionaryTerm.isTermInDictionary(word, fileExtension)) {
return TYPE_DICTIONARY_WORD_EXT_SPECIFIC;
}
// Ternary Expression - swap IF and ELSE
if (com.kstenschke.shifter.models.shiftableTypes.TernaryExpression.isTernaryExpression(word, prefixChar)) {
return TYPE_TERNARY_EXPRESSION;
}
// Quoted (must be wrapped in single or double quotes or backticks)
this.typeQuotedString = new com.kstenschke.shifter.models.shiftableTypes.QuotedString();
if (this.typeQuotedString.isQuotedString(prefixChar, postfixChar)) {
return TYPE_QUOTED_STRING;
}
// RGB (must be prefixed w/ "#")
if (com.kstenschke.shifter.models.shiftableTypes.RbgColor.isRgbColorString(word, prefixChar)) {
this.typeRgbColor = new com.kstenschke.shifter.models.shiftableTypes.RbgColor();
return TYPE_RGB_COLOR;
}
// Pixel value (must consist of numeric value followed by "px")
if (com.kstenschke.shifter.models.shiftableTypes.CssUnit.isCssUnitValue(word)) {
this.typePixelValue = new com.kstenschke.shifter.models.shiftableTypes.CssUnit();
return TYPE_CSS_UNIT;
}
if (com.kstenschke.shifter.models.shiftableTypes.NumericValue.isNumericValue(word)) {
this.typeNumericValue = new com.kstenschke.shifter.models.shiftableTypes.NumericValue();
return TYPE_NUMERIC_VALUE;
}
// Operator sign (<, >, +, -)
if (com.kstenschke.shifter.models.shiftableTypes.OperatorSign.isOperatorSign(word)) {
this.typeOperatorSign = new com.kstenschke.shifter.models.shiftableTypes.OperatorSign();
return TYPE_OPERATOR_SIGN;
}
// Roman Numeral
if (com.kstenschke.shifter.models.shiftableTypes.RomanNumber.isRomanNumber(word)) {
this.typeRomanNumber = new com.kstenschke.shifter.models.shiftableTypes.RomanNumber();
return TYPE_ROMAN_NUMERAL;
}
if (com.kstenschke.shifter.models.shiftableTypes.LogicalOperator.isLogicalOperator(word)) {
// Logical operators "&&" and "||" must be detected before MonoCharStrings to avoid confusing
return TYPE_LOGICAL_OPERATOR;
}
// MonoCharString (= consisting from any amount of the same character)
if (com.kstenschke.shifter.models.shiftableTypes.StringMonoCharacter.isMonoCharacterString(word)) {
this.typeMonoCharacterString = new com.kstenschke.shifter.models.shiftableTypes.StringMonoCharacter();
return TYPE_MONO_CHARACTER_STRING;
}
// Term in dictionary (anywhere, that is w/o limiting to the current file extension)
if (this.typeDictionaryTerm.isTermInDictionary(word, false)) {
return TYPE_DICTIONARY_WORD_GLOBAL;
}
if (com.kstenschke.shifter.models.shiftableTypes.StringNumericPostfix.isNumericPostfix(word)) {
return TYPE_NUMERIC_POSTFIXED_STRING;
}
wordsTupel = new com.kstenschke.shifter.models.shiftableTypes.Tupel();
if (wordsTupel.isWordsTupel(word)) {
return TYPE_WORDS_TUPEL;
}
if (com.kstenschke.shifter.models.shiftableTypes.StringHtmlEncodable.isHtmlEncodable(word)) {
return TYPE_HTML_ENCODABLE_STRING;
}
return TYPE_UNKNOWN;
}
public int getWordType(String word, CharSequence editorText, int caretOffset, String filename) {
String line = UtilsTextual.getLineAtOffset(editorText.toString(), caretOffset);
int editorTextLength = editorText.length();
int offsetPostfixChar = caretOffset + word.length();
String postfixChar = editorTextLength > offsetPostfixChar
? String.valueOf(editorText.charAt(offsetPostfixChar))
: "";
boolean isLastLineInDocument = offsetPostfixChar == editorTextLength;
return this.getWordType(word, "", postfixChar, isLastLineInDocument, line, filename);
}
/**
* @param word
* @return boolean
*/
private boolean isKeywordAccessType(String word) {
String[] keywordsAccessType = {"public", "private", "protected"};
this.wordTypeAccessibilities = new com.kstenschke.shifter.models.shiftableTypes.StaticWordType(keywordsAccessType);
return this.wordTypeAccessibilities.hasWord(word);
}
/**
* Shift given word
* ShifterTypesManager: get next/previous keyword of given word group
* Generic: calculate shifted value
*
* @param word Word to be shifted
* @param idWordType Word type ID
* @param isUp Shift up or down?
* @param editorText Full text of currently edited document
* @param caretOffset Caret offset in document
* @param filename Filename of currently edited file
* @param editor Editor instance
* @param moreCount Current "more" count, starting w/ 1. If non-more shift: null
* @return The shifted word
*/
public String getShiftedWord(String word, int idWordType, boolean isUp, CharSequence editorText, int caretOffset, Integer moreCount, String filename, @Nullable Editor editor) {
switch (idWordType) {
// String based word shiftableTypes
case TYPE_ACCESSIBILITY:
return this.wordTypeAccessibilities.getShifted(word, isUp);
case TYPE_DICTIONARY_WORD_GLOBAL:
case TYPE_DICTIONARY_WORD_EXT_SPECIFIC:
// The dictionary stored the matching terms-line, we don't need to differ global/ext-specific anymore
return this.typeDictionaryTerm.getShifted(word, isUp);
// Generic shiftableTypes (shifting is calculated)
case TYPE_SIZZLE_SELECTOR:
return com.kstenschke.shifter.models.shiftableTypes.SizzleSelector.getShifted(word);
case TYPE_RGB_COLOR:
return this.typeRgbColor.getShifted(word, isUp);
case TYPE_NUMERIC_VALUE:
// Numeric values including UNIX and millisecond timestamps
return this.typeNumericValue.getShifted(word, isUp, editor, filename);
case TYPE_CSS_UNIT:
return this.typePixelValue.getShifted(word, isUp);
case TYPE_PHP_VARIABLE:
return this.typePhpVariable.getShifted(word, editorText, isUp, moreCount);
case TYPE_TERNARY_EXPRESSION:
return com.kstenschke.shifter.models.shiftableTypes.TernaryExpression.getShifted(word);
case TYPE_QUOTED_STRING:
return this.typeQuotedString.getShifted(word, editorText, isUp);
case TYPE_OPERATOR_SIGN:
return this.typeOperatorSign.getShifted(word);
case TYPE_ROMAN_NUMERAL:
return this.typeRomanNumber.getShifted(word, isUp);
case TYPE_LOGICAL_OPERATOR:
return com.kstenschke.shifter.models.shiftableTypes.LogicalOperator.getShifted(word);
case TYPE_MONO_CHARACTER_STRING:
return this.typeMonoCharacterString.getShifted(word, isUp);
case TYPE_DOC_COMMENT_TAG:
String textAfterCaret = editorText.toString().substring(caretOffset);
return this.typeTagInDocComment.getShifted(word, isUp, filename, textAfterCaret);
case TYPE_DOC_COMMENT_DATATYPE:
return this.typeDataTypeInDocComment.getShifted(word, isUp, filename);
case TYPE_HTML_ENCODABLE_STRING:
return com.kstenschke.shifter.models.shiftableTypes.StringHtmlEncodable.getShifted(word);
case TYPE_NUMERIC_POSTFIXED_STRING:
return com.kstenschke.shifter.models.shiftableTypes.StringNumericPostfix.getShifted(word, isUp);
case TYPE_WORDS_TUPEL:
return wordsTupel.getShifted(word);
default:
return word;
}
}
/**
* @param word
* @param isUp
* @param editorText
* @param caretOffset
* @param moreCount
* @param filename
* @param editor
* @return String
*/
public String getShiftedWord(String word, boolean isUp, CharSequence editorText, int caretOffset, @Nullable Integer moreCount, String filename, Editor editor) {
String line = UtilsTextual.getLineAtOffset(editorText.toString(), caretOffset);
int idWordType = this.getWordType(word, "", "", false, line, filename);
return this.getShiftedWord(word, idWordType, isUp, editorText, caretOffset, moreCount, filename, editor);
}
}