Search in sources :

Example 1 with CodeModel

use of org.rstudio.studio.client.workbench.views.source.editors.text.ace.CodeModel in project rstudio by rstudio.

the class CompletionRequester method addFunctionArgumentCompletions.

private void addFunctionArgumentCompletions(String token, ArrayList<QualifiedName> completions) {
    AceEditor editor = (AceEditor) docDisplay_;
    if (editor != null) {
        Position cursorPosition = editor.getSession().getSelection().getCursor();
        CodeModel codeModel = editor.getSession().getMode().getRCodeModel();
        // Try to see if we can find a function name
        TokenCursor cursor = codeModel.getTokenCursor();
        // NOTE: This can fail if the document is empty
        if (!cursor.moveToPosition(cursorPosition))
            return;
        String tokenLower = token.toLowerCase();
        if (cursor.currentValue() == "(" || cursor.findOpeningBracket("(", false)) {
            if (cursor.moveToPreviousToken()) {
                // Check to see if this really is the name of a function
                JsArray<ScopeFunction> functionsInScope = codeModel.getAllFunctionScopes();
                String tokenName = cursor.currentValue();
                for (int i = 0; i < functionsInScope.length(); i++) {
                    ScopeFunction rFunction = functionsInScope.get(i);
                    String fnName = rFunction.getFunctionName();
                    if (tokenName == fnName) {
                        JsArrayString args = rFunction.getFunctionArgs();
                        for (int j = 0; j < args.length(); j++) {
                            String arg = args.get(j);
                            if (arg.toLowerCase().startsWith(tokenLower))
                                completions.add(new QualifiedName(args.get(j) + " = ", fnName, false, RCompletionType.CONTEXT));
                        }
                    }
                }
            }
        }
    }
}
Also used : TokenCursor(org.rstudio.studio.client.workbench.views.source.editors.text.ace.TokenCursor) Position(org.rstudio.studio.client.workbench.views.source.editors.text.ace.Position) CodeModel(org.rstudio.studio.client.workbench.views.source.editors.text.ace.CodeModel) AceEditor(org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor) JsArrayString(com.google.gwt.core.client.JsArrayString) JsArrayString(com.google.gwt.core.client.JsArrayString) ScopeFunction(org.rstudio.studio.client.workbench.views.source.editors.text.ScopeFunction)

Example 2 with CodeModel

use of org.rstudio.studio.client.workbench.views.source.editors.text.ace.CodeModel in project rstudio by rstudio.

the class CompletionRequester method addScopedCompletions.

private void addScopedCompletions(String token, ArrayList<QualifiedName> completions, String type) {
    AceEditor editor = (AceEditor) docDisplay_;
    // NOTE: this will be null in the console, so protect against that
    if (editor != null) {
        Position cursorPosition = editor.getSession().getSelection().getCursor();
        CodeModel codeModel = editor.getSession().getMode().getRCodeModel();
        JsArray<RScopeObject> scopeVariables = codeModel.getVariablesInScope(cursorPosition);
        String tokenLower = token.toLowerCase();
        for (int i = 0; i < scopeVariables.length(); i++) {
            RScopeObject variable = scopeVariables.get(i);
            if (variable.getType() == type && variable.getToken().toLowerCase().startsWith(tokenLower))
                completions.add(new QualifiedName(variable.getToken(), variable.getType(), false, RCompletionType.CONTEXT));
        }
    }
}
Also used : Position(org.rstudio.studio.client.workbench.views.source.editors.text.ace.Position) CodeModel(org.rstudio.studio.client.workbench.views.source.editors.text.ace.CodeModel) AceEditor(org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor) JsArrayString(com.google.gwt.core.client.JsArrayString) RScopeObject(org.rstudio.studio.client.workbench.views.source.editors.text.ace.RScopeObject)

Example 3 with CodeModel

use of org.rstudio.studio.client.workbench.views.source.editors.text.ace.CodeModel in project rstudio by rstudio.

the class CompletionRequester method addScopedArgumentCompletions.

private void addScopedArgumentCompletions(String token, ArrayList<QualifiedName> completions) {
    AceEditor editor = (AceEditor) docDisplay_;
    // NOTE: this will be null in the console, so protect against that
    if (editor != null) {
        Position cursorPosition = editor.getSession().getSelection().getCursor();
        CodeModel codeModel = editor.getSession().getMode().getRCodeModel();
        JsArray<RFunction> scopedFunctions = codeModel.getFunctionsInScope(cursorPosition);
        if (scopedFunctions.length() == 0)
            return;
        String tokenLower = token.toLowerCase();
        for (int i = 0; i < scopedFunctions.length(); i++) {
            RFunction scopedFunction = scopedFunctions.get(i);
            String functionName = scopedFunction.getFunctionName();
            JsArrayString argNames = scopedFunction.getFunctionArgs();
            for (int j = 0; j < argNames.length(); j++) {
                String argName = argNames.get(j);
                if (argName.toLowerCase().startsWith(tokenLower)) {
                    if (functionName == null || functionName == "") {
                        completions.add(new QualifiedName(argName, "<anonymous function>", false, RCompletionType.CONTEXT));
                    } else {
                        completions.add(new QualifiedName(argName, functionName, false, RCompletionType.CONTEXT));
                    }
                }
            }
        }
    }
}
Also used : Position(org.rstudio.studio.client.workbench.views.source.editors.text.ace.Position) CodeModel(org.rstudio.studio.client.workbench.views.source.editors.text.ace.CodeModel) AceEditor(org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor) JsArrayString(com.google.gwt.core.client.JsArrayString) JsArrayString(com.google.gwt.core.client.JsArrayString) RFunction(org.rstudio.studio.client.workbench.views.source.editors.text.RFunction)

Example 4 with CodeModel

use of org.rstudio.studio.client.workbench.views.source.editors.text.ace.CodeModel in project rstudio by rstudio.

the class RCompletionManager method getAutocompletionContext.

private AutocompletionContext getAutocompletionContext() {
    AutocompletionContext context = new AutocompletionContext();
    String firstLine = input_.getText();
    int row = input_.getCursorPosition().getRow();
    // trim to cursor position
    firstLine = firstLine.substring(0, input_.getCursorPosition().getColumn());
    // file completions
    if (DocumentMode.isCursorInMarkdownMode(docDisplay_) && firstLine.matches(".*\\[.*\\]\\(.*"))
        return getAutocompletionContextForFileMarkdownLink(firstLine);
    // Get the token at the cursor position.
    String tokenRegex = ".*[^" + RegexUtil.wordCharacter() + "._:$@'\"`-]";
    String token = firstLine.replaceAll(tokenRegex, "");
    // If we're completing an object within a string, assume it's a
    // file-system completion. Note that we may need other contextual information
    // to decide if e.g. we only want directories.
    String firstLineStripped = StringUtil.stripBalancedQuotes(StringUtil.stripRComment(firstLine));
    boolean isFileCompletion = false;
    if (firstLineStripped.indexOf('\'') != -1 || firstLineStripped.indexOf('"') != -1) {
        isFileCompletion = true;
        addAutocompletionContextForFile(context, firstLine);
    }
    // pass the whole line as a token
    if (firstLine.startsWith("```{") || firstLine.startsWith("<<"))
        return new AutocompletionContext(firstLine, AutocompletionContext.TYPE_CHUNK);
    // If this line starts with a '?', assume it's a help query
    if (firstLine.matches("^\\s*[?].*"))
        return new AutocompletionContext(token, AutocompletionContext.TYPE_HELP);
    // escape early for roxygen
    if (firstLine.matches("\\s*#+'.*"))
        return new AutocompletionContext(token, AutocompletionContext.TYPE_ROXYGEN);
    // about the appropriate completion
    if (token.contains("$") || token.contains("@"))
        addAutocompletionContextForDollar(context);
    // 'debug(stats::rnorm)' we know not to auto-insert parens)
    if (token.contains("::"))
        addAutocompletionContextForNamespace(token, context);
    // If this is not a file completion, we need to further strip and
    // then set the token. Note that the token will have already been
    // set if this is a file completion.
    token = token.replaceAll(".*[$@:]", "");
    if (!isFileCompletion)
        context.setToken(token);
    // access to the R Code model
    AceEditor editor = (AceEditor) docDisplay_;
    if (editor == null)
        return context;
    CodeModel codeModel = editor.getSession().getMode().getRCodeModel();
    // We might need to grab content from further up in the document than
    // the current cursor position -- so tokenize ahead.
    codeModel.tokenizeUpToRow(row + 100);
    // Make a token cursor and place it at the first token previous
    // to the cursor.
    TokenCursor tokenCursor = codeModel.getTokenCursor();
    if (!tokenCursor.moveToPosition(input_.getCursorPosition()))
        return context;
    // names.
    if (tokenCursor.moveToNextToken()) {
        if (tokenCursor.currentValue() == ":" || tokenCursor.currentValue() == "::" || tokenCursor.currentValue() == ":::") {
            return new AutocompletionContext(token, AutocompletionContext.TYPE_PACKAGE);
        }
        tokenCursor.moveToPreviousToken();
    }
    TokenCursor startCursor = tokenCursor.cloneCursor();
    // Find an opening '(' or '[' -- this provides the function or object
    // for completion.
    int initialNumCommas = 0;
    if (tokenCursor.currentValue() != "(" && tokenCursor.currentValue() != "[") {
        int commaCount = tokenCursor.findOpeningBracketCountCommas(new String[] { "[", "(" }, true);
        // commaCount == -1 implies we failed to find an opening bracket
        if (commaCount == -1) {
            commaCount = tokenCursor.findOpeningBracketCountCommas("[", false);
            if (commaCount == -1)
                return context;
            else
                initialNumCommas = commaCount;
        } else {
            initialNumCommas = commaCount;
        }
    }
    // Figure out whether we're looking at '(', '[', or '[[',
    // and place the token cursor on the first token preceding.
    TokenCursor endOfDecl = tokenCursor.cloneCursor();
    int initialDataType = AutocompletionContext.TYPE_UNKNOWN;
    if (tokenCursor.currentValue() == "(") {
        initialDataType = AutocompletionContext.TYPE_FUNCTION;
        if (!tokenCursor.moveToPreviousToken())
            return context;
    } else if (tokenCursor.currentValue() == "[") {
        if (!tokenCursor.moveToPreviousToken())
            return context;
        if (tokenCursor.currentValue() == "[") {
            if (!endOfDecl.moveToPreviousToken())
                return context;
            initialDataType = AutocompletionContext.TYPE_DOUBLE_BRACKET;
            if (!tokenCursor.moveToPreviousToken())
                return context;
        } else {
            initialDataType = AutocompletionContext.TYPE_SINGLE_BRACKET;
        }
    }
    // Get the string marking the function or data
    if (!tokenCursor.findStartOfEvaluationContext())
        return context;
    // Try to get the function call string -- either there's
    // an associated closing paren we can use, or we should just go up
    // to the current cursor position.
    // First, attempt to determine where the closing paren is located. If
    // this fails, we'll just use the start cursor's position (and later
    // attempt to finish the expression to make it parsable)
    Position endPos = startCursor.currentPosition();
    endPos.setColumn(endPos.getColumn() + startCursor.currentValue().length());
    // try to look forward for closing paren
    if (endOfDecl.currentValue() == "(") {
        TokenCursor closingParenCursor = endOfDecl.cloneCursor();
        if (closingParenCursor.fwdToMatchingToken()) {
            endPos = closingParenCursor.currentPosition();
            endPos.setColumn(endPos.getColumn() + 1);
        }
    }
    // We can now set the function call string.
    //
    // We strip out the current statement under the cursor, so that
    // match.call() can later properly resolve the current argument.
    //
    // Attempt to find the start of the current statement.
    TokenCursor clone = startCursor.cloneCursor();
    do {
        String value = clone.currentValue();
        if (value.indexOf(",") != -1 || value.equals("("))
            break;
        if (clone.bwdToMatchingToken())
            continue;
    } while (clone.moveToPreviousToken());
    Position startPosition = clone.currentPosition();
    // Include the opening paren if that's what we found
    if (clone.currentValue().equals("("))
        startPosition.setColumn(startPosition.getColumn() + 1);
    String beforeText = editor.getTextForRange(Range.fromPoints(tokenCursor.currentPosition(), startPosition));
    // Now, attempt to find the end of the current statement.
    // Look for the ',' or ')' that ends the statement for the 
    // currently active argument.
    boolean lookupSucceeded = false;
    while (clone.moveToNextToken()) {
        String value = clone.currentValue();
        if (value.indexOf(",") != -1 || value.equals(")")) {
            lookupSucceeded = true;
            break;
        }
        // pairs properly, so finding one implies that we have a parse error).
        if (value.equals("]") || value.equals("}"))
            break;
        if (clone.fwdToMatchingToken())
            continue;
    }
    String afterText = "";
    if (lookupSucceeded) {
        afterText = editor.getTextForRange(Range.fromPoints(clone.currentPosition(), endPos));
    }
    context.setFunctionCallString((beforeText + afterText).trim());
    // Try to identify whether we're producing autocompletions for
    // a _named_ function argument; if so, produce completions tuned to
    // that argument.
    TokenCursor argsCursor = startCursor.cloneCursor();
    do {
        String argsValue = argsCursor.currentValue();
        // within a named argument, although this isn't perfect.
        if (argsValue.equals(",") || argsValue.equals("(") || argsValue.equals("$") || argsValue.equals("@") || argsValue.equals("::") || argsValue.equals(":::") || argsValue.equals("]") || argsValue.equals(")") || argsValue.equals("}")) {
            break;
        }
        // a function argument.
        if (argsValue.equals("=") && argsCursor.moveToPreviousToken()) {
            if (!isFileCompletion)
                context.setToken(token);
            context.add(argsCursor.currentValue(), AutocompletionContext.TYPE_ARGUMENT, 0);
            return context;
        }
    } while (argsCursor.moveToPreviousToken());
    String initialData = docDisplay_.getTextForRange(Range.fromPoints(tokenCursor.currentPosition(), endOfDecl.currentPosition())).trim();
    // And the first context
    context.add(initialData, initialDataType, initialNumCommas);
    // Get the rest of the single-bracket contexts for completions as well
    String assocData;
    int dataType;
    int numCommas;
    while (true) {
        int commaCount = tokenCursor.findOpeningBracketCountCommas("[", false);
        if (commaCount == -1)
            break;
        numCommas = commaCount;
        TokenCursor declEnd = tokenCursor.cloneCursor();
        if (!tokenCursor.moveToPreviousToken())
            return context;
        if (tokenCursor.currentValue() == "[") {
            if (!declEnd.moveToPreviousToken())
                return context;
            dataType = AutocompletionContext.TYPE_DOUBLE_BRACKET;
            if (!tokenCursor.moveToPreviousToken())
                return context;
        } else {
            dataType = AutocompletionContext.TYPE_SINGLE_BRACKET;
        }
        tokenCursor.findStartOfEvaluationContext();
        assocData = docDisplay_.getTextForRange(Range.fromPoints(tokenCursor.currentPosition(), declEnd.currentPosition())).trim();
        context.add(assocData, dataType, numCommas);
    }
    return context;
}
Also used : TokenCursor(org.rstudio.studio.client.workbench.views.source.editors.text.ace.TokenCursor) InputEditorPosition(org.rstudio.studio.client.workbench.views.console.shell.editor.InputEditorPosition) Position(org.rstudio.studio.client.workbench.views.source.editors.text.ace.Position) SourcePosition(org.rstudio.studio.client.workbench.views.source.model.SourcePosition) InputEditorLineWithCursorPosition(org.rstudio.studio.client.workbench.views.console.shell.editor.InputEditorLineWithCursorPosition) CodeModel(org.rstudio.studio.client.workbench.views.source.editors.text.ace.CodeModel) AceEditor(org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor)

Example 5 with CodeModel

use of org.rstudio.studio.client.workbench.views.source.editors.text.ace.CodeModel in project rstudio by rstudio.

the class RCompletionManager method addAutocompletionContextForDollar.

private boolean addAutocompletionContextForDollar(AutocompletionContext context) {
    // Establish an evaluation context by looking backwards
    AceEditor editor = (AceEditor) docDisplay_;
    if (editor == null)
        return false;
    CodeModel codeModel = editor.getSession().getMode().getRCodeModel();
    codeModel.tokenizeUpToRow(input_.getCursorPosition().getRow());
    TokenCursor cursor = codeModel.getTokenCursor();
    if (!cursor.moveToPosition(input_.getCursorPosition()))
        return false;
    // Move back to the '$'
    while (cursor.currentValue() != "$" && cursor.currentValue() != "@") if (!cursor.moveToPreviousToken())
        return false;
    int type = cursor.currentValue() == "$" ? AutocompletionContext.TYPE_DOLLAR : AutocompletionContext.TYPE_AT;
    // Put a cursor here
    TokenCursor contextEndCursor = cursor.cloneCursor();
    // We allow for arbitrary elements previous, so we want to get e.g.
    //
    //     env::foo()$bar()[1]$baz
    // Get the string forming the context
    //
    //
    // If this fails, we still want to report an empty evaluation context
    // (the completion is still occurring in a '$' context, so we do want
    // to exclude completions from other scopes)
    String data = "";
    if (cursor.moveToPreviousToken() && cursor.findStartOfEvaluationContext()) {
        data = editor.getTextForRange(Range.fromPoints(cursor.currentPosition(), contextEndCursor.currentPosition()));
    }
    context.add(data, type);
    return true;
}
Also used : TokenCursor(org.rstudio.studio.client.workbench.views.source.editors.text.ace.TokenCursor) CodeModel(org.rstudio.studio.client.workbench.views.source.editors.text.ace.CodeModel) AceEditor(org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor)

Aggregations

AceEditor (org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor)6 CodeModel (org.rstudio.studio.client.workbench.views.source.editors.text.ace.CodeModel)6 Position (org.rstudio.studio.client.workbench.views.source.editors.text.ace.Position)4 TokenCursor (org.rstudio.studio.client.workbench.views.source.editors.text.ace.TokenCursor)4 JsArrayString (com.google.gwt.core.client.JsArrayString)3 InputEditorLineWithCursorPosition (org.rstudio.studio.client.workbench.views.console.shell.editor.InputEditorLineWithCursorPosition)1 InputEditorPosition (org.rstudio.studio.client.workbench.views.console.shell.editor.InputEditorPosition)1 InputEditorSelection (org.rstudio.studio.client.workbench.views.console.shell.editor.InputEditorSelection)1 RFunction (org.rstudio.studio.client.workbench.views.source.editors.text.RFunction)1 ScopeFunction (org.rstudio.studio.client.workbench.views.source.editors.text.ScopeFunction)1 DplyrJoinContext (org.rstudio.studio.client.workbench.views.source.editors.text.ace.DplyrJoinContext)1 RInfixData (org.rstudio.studio.client.workbench.views.source.editors.text.ace.RInfixData)1 RScopeObject (org.rstudio.studio.client.workbench.views.source.editors.text.ace.RScopeObject)1 SourcePosition (org.rstudio.studio.client.workbench.views.source.model.SourcePosition)1