diff --git a/src/com/millennialmedia/intellibot/psi/ref/RobotPythonFile.java b/src/com/millennialmedia/intellibot/psi/ref/RobotPythonFile.java index da609d2b..ed335bf1 100644 --- a/src/com/millennialmedia/intellibot/psi/ref/RobotPythonFile.java +++ b/src/com/millennialmedia/intellibot/psi/ref/RobotPythonFile.java @@ -11,6 +11,7 @@ import com.millennialmedia.intellibot.psi.element.DefinedKeyword; import com.millennialmedia.intellibot.psi.element.DefinedVariable; import com.millennialmedia.intellibot.psi.element.KeywordFile; +import com.millennialmedia.intellibot.psi.util.PythonParser; import com.millennialmedia.intellibot.psi.util.PerformanceCollector; import com.millennialmedia.intellibot.psi.util.PerformanceEntity; import com.millennialmedia.intellibot.psi.util.ReservedVariable; @@ -44,13 +45,13 @@ public Collection getDefinedKeywords() { PerformanceCollector debug = new PerformanceCollector(this, "get defined keywords"); Collection results = new HashSet(); for (PyFunction function : this.pythonFile.getTopLevelFunctions()) { - String keyword = functionToKeyword(function.getName()); + String keyword = PythonParser.keywordName(function); if (keyword != null) { - results.add(new KeywordDto(function, this.library, keyword, hasArguments(function.getParameterList().getParameters()))); + results.add(new KeywordDto(function, this.library, keyword, PythonParser.keywordHasArguments(function))); } } for (PyTargetExpression expression : this.pythonFile.getTopLevelAttributes()) { - String keyword = functionToKeyword(expression.getName()); + String keyword = PythonParser.keywordName(expression); if (keyword != null) { results.add(new KeywordDto(expression, this.library, keyword, false)); } diff --git a/src/com/millennialmedia/intellibot/psi/ref/RobotPythonWrapper.java b/src/com/millennialmedia/intellibot/psi/ref/RobotPythonWrapper.java index 6166a100..6cb06db6 100644 --- a/src/com/millennialmedia/intellibot/psi/ref/RobotPythonWrapper.java +++ b/src/com/millennialmedia/intellibot/psi/ref/RobotPythonWrapper.java @@ -6,9 +6,9 @@ import com.millennialmedia.intellibot.psi.dto.VariableDto; import com.millennialmedia.intellibot.psi.element.DefinedKeyword; import com.millennialmedia.intellibot.psi.element.DefinedVariable; +import com.millennialmedia.intellibot.psi.util.PythonParser; import com.millennialmedia.intellibot.psi.util.ReservedVariable; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -18,38 +18,13 @@ */ public abstract class RobotPythonWrapper { - private static final String UNDERSCORE = "_"; - private static final String SELF = "self"; - - protected static boolean hasArguments(@Nullable PyParameter[] parameters) { - if (parameters == null || parameters.length == 0) { - return false; - } - - for (PyParameter parameter : parameters) { - String name = parameter.getName(); - if (name != null && !SELF.equalsIgnoreCase(name)) { - return true; - } - } - return false; - } - - protected static String functionToKeyword(@Nullable String function) { - if (function == null || isPrivate(function)) { - return null; - } else { - return function; - } - } - protected static void addDefinedVariables(@NotNull PyClass pythonClass, @NotNull final Collection results) { pythonClass.visitClassAttributes( new Processor() { @Override public boolean process(PyTargetExpression expression) { - String keyword = expression.getName(); - if (keyword != null && !isPrivate(keyword)) { + String keyword = PythonParser.keywordName(expression); + if (keyword != null) { // not formatted ${X}, assume scalar results.add(new VariableDto(expression, ReservedVariable.wrapToScalar(keyword), null)); } @@ -61,10 +36,6 @@ public boolean process(PyTargetExpression expression) { ); } - private static boolean isPrivate(@NotNull String keyword) { - // these keeps out intended private functions - return keyword.startsWith(UNDERSCORE) || keyword.startsWith("ROBOT_LIBRARY_"); - } protected static void addDefinedKeywords(@NotNull PyClass pythonClass, @NotNull final String namespace, @NotNull final Collection results) { pythonClass.visitMethods( @@ -72,32 +43,9 @@ protected static void addDefinedKeywords(@NotNull PyClass pythonClass, @NotNull @Override public boolean process(PyFunction function) { - String keyword = functionToKeyword(function.getName()); + String keyword = PythonParser.keywordName(function); if (keyword != null) { - // Get info from @keyword - PyDecoratorList decorators = function.getDecoratorList(); - if (decorators != null) { - PyDecorator keyword_decorator = decorators.findDecorator("keyword"); - if (keyword_decorator != null) { - if (keyword_decorator.hasArgumentList()) { - // Get case 'name =' argument - PyExpression kwa = keyword_decorator.getKeywordArgument("name"); - if (kwa != null) { - keyword = kwa.getText().replaceAll("^\"|\"$", ""); - } - else { - // Otherwise, check if first argument is unnamed - PyExpression[] kda = keyword_decorator.getArguments(); - - // Argument exists and is unnamed - if (kda.length > 0 && kda[0].getName() == null) { - keyword = kda[0].getText().replaceAll("^\"|\"$", ""); - } - } - } - } - } - results.add(new KeywordDto(function, namespace, keyword, hasArguments(function.getParameterList().getParameters()))); + results.add(new KeywordDto(function, namespace, keyword, PythonParser.keywordHasArguments(function))); } return true; } @@ -110,7 +58,7 @@ public boolean process(PyFunction function) { @Override public boolean process(PyTargetExpression expression) { - String keyword = functionToKeyword(expression.getName()); + String keyword = PythonParser.keywordName(expression); if (keyword != null) { results.add(new KeywordDto(expression, namespace, keyword, false)); } diff --git a/src/com/millennialmedia/intellibot/psi/util/PythonParser.java b/src/com/millennialmedia/intellibot/psi/util/PythonParser.java new file mode 100644 index 00000000..4484f3f2 --- /dev/null +++ b/src/com/millennialmedia/intellibot/psi/util/PythonParser.java @@ -0,0 +1,91 @@ +package com.millennialmedia.intellibot.psi.util; + +import com.jetbrains.python.psi.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PythonParser { + + public static boolean keywordHasArguments(PyFunction keyword_function) { + PyParameter[] parameters = keyword_function.getParameterList().getParameters(); + + if (parameters == null || parameters.length == 0) { + return false; + } + + for (PyParameter parameter : parameters) { + String name = parameter.getName(); + if (name != null && !"self".equalsIgnoreCase(name)) { + return true; + } + } + return false; + } + + public static String keywordName(PyFunction function) { + String name = function.getName(); + String decoratorName = null; + + if (name == null || isPrivate(name)) { + return null; + } + + // Get info from @keyword + PyDecorator keywordDecorator = getKeywordDecorator(function); + if (keywordDecorator != null) { + decoratorName = keywordNameFromDecorator(keywordDecorator); + } + + return decoratorName != null ? decoratorName : name; + } + + public static String keywordName(PyExpression expression) { + String name = expression.getName(); + + if (name == null || isPrivate(name)) { + return null; + } + + return name; + } + + public static boolean isPrivate(@NotNull String keyword) { + // these keeps out intended private functions + return keyword.startsWith("_") || keyword.startsWith("ROBOT_LIBRARY_"); + } + + private static String keywordNameFromDecorator(PyDecorator decorator) { + if (!decorator.hasArgumentList()) { + return null; + } + + PyExpression keywordNameArgument = decorator.getKeywordArgument("name"); + + if (keywordNameArgument == null && decorator.getArguments()[0].getName() == null) { + keywordNameArgument = decorator.getArguments()[0]; + } + + if (keywordNameArgument != null) { + return keywordNameArgument.getText().replaceAll("^[\"|\']|[\"|\']$", ""); + } + + return null; + } + + private static PyDecorator getKeywordDecorator(@NotNull PyFunction function) { + PyDecoratorList decorators = function.getDecoratorList(); + + if (decorators == null) { + return null; + } + + for (PyDecorator decorator : decorators.getDecorators()) { + String decorator_name = decorator.getName(); + if (decorator_name != null && decorator_name.matches("^(robot.)?(api.)?(deco.)?keyword")) { + return decorator; + } + } + + return null; + } +}