Search in sources :

Example 1 with TokenCursor

use of org.rstudio.studio.client.workbench.views.source.editors.text.ace.TokenCursor 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 TokenCursor

use of org.rstudio.studio.client.workbench.views.source.editors.text.ace.TokenCursor 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 3 with TokenCursor

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

the class RCompletionManager method goToFunctionDefinition.

public void goToFunctionDefinition() {
    // check for a file-local definition (intra-file navigation -- using
    // the active scope tree)
    AceEditor editor = (AceEditor) docDisplay_;
    if (editor != null) {
        TokenCursor cursor = editor.getSession().getMode().getRCodeModel().getTokenCursor();
        if (cursor.moveToPosition(editor.getCursorPosition(), true)) {
            // token (obstensibly a funciton name)
            if (cursor.isLeftBracket())
                cursor.moveToPreviousToken();
            // navigate (as this isn't the 'full' function name)
            if (cursor.moveToPreviousToken()) {
                if (cursor.isExtractionOperator())
                    return;
                cursor.moveToNextToken();
            }
            // if this is a string, try resolving that string as a file name
            if (cursor.hasType("string")) {
                String tokenValue = cursor.currentValue();
                String path = tokenValue.substring(1, tokenValue.length() - 1);
                FileSystemItem filePath = FileSystemItem.createFile(path);
                // This will show a dialog error if no such file exists; this
                // seems the most appropriate action in such a case.
                fileTypeRegistry_.editFile(filePath);
            }
            String functionName = cursor.currentValue();
            JsArray<ScopeFunction> scopes = editor.getAllFunctionScopes();
            for (int i = 0; i < scopes.length(); i++) {
                ScopeFunction scope = scopes.get(i);
                if (scope.getFunctionName().equals(functionName)) {
                    navigableSourceEditor_.navigateToPosition(SourcePosition.create(scope.getPreamble().getRow(), scope.getPreamble().getColumn()), true);
                    return;
                }
            }
        }
    }
    // intra-file navigation failed -- hit the server and find a definition
    // in the project index
    // determine current line and cursor position
    InputEditorLineWithCursorPosition lineWithPos = InputEditorUtil.getLineWithCursorPosition(input_);
    // delayed progress indicator
    final GlobalProgressDelayer progress = new GlobalProgressDelayer(globalDisplay_, 1000, "Searching for function definition...");
    server_.getObjectDefinition(lineWithPos.getLine(), lineWithPos.getPosition(), new ServerRequestCallback<ObjectDefinition>() {

        @Override
        public void onResponseReceived(ObjectDefinition def) {
            // dismiss progress
            progress.dismiss();
            // if we got a hit
            if (def.getObjectName() != null) {
                // search locally if a function navigator was provided
                if (navigableSourceEditor_ != null) {
                    // try to search for the function locally
                    SourcePosition position = navigableSourceEditor_.findFunctionPositionFromCursor(def.getObjectName());
                    if (position != null) {
                        navigableSourceEditor_.navigateToPosition(position, true);
                        // we're done
                        return;
                    }
                }
                // navigate to the file/loc
                if (def.getObjectType() == FileFunctionDefinition.OBJECT_TYPE) {
                    FileFunctionDefinition fileDef = def.getObjectData().cast();
                    fileTypeRegistry_.editFile(fileDef.getFile(), fileDef.getPosition());
                } else // search path definition
                if (def.getObjectType() == SearchPathFunctionDefinition.OBJECT_TYPE) {
                    SearchPathFunctionDefinition searchDef = def.getObjectData().cast();
                    eventBus_.fireEvent(new CodeBrowserNavigationEvent(searchDef));
                } else // finally, check to see if it's a data frame
                if (def.getObjectType() == DataDefinition.OBJECT_TYPE) {
                    eventBus_.fireEvent(new SendToConsoleEvent("View(" + def.getObjectName() + ")", true, false));
                }
            }
        }

        @Override
        public void onError(ServerError error) {
            progress.dismiss();
            globalDisplay_.showErrorMessage("Error Searching for Function", error.getUserMessage());
        }
    });
}
Also used : SearchPathFunctionDefinition(org.rstudio.studio.client.workbench.codesearch.model.SearchPathFunctionDefinition) InputEditorLineWithCursorPosition(org.rstudio.studio.client.workbench.views.console.shell.editor.InputEditorLineWithCursorPosition) ServerError(org.rstudio.studio.client.server.ServerError) GlobalProgressDelayer(org.rstudio.studio.client.common.GlobalProgressDelayer) AceEditor(org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor) ObjectDefinition(org.rstudio.studio.client.workbench.codesearch.model.ObjectDefinition) ScopeFunction(org.rstudio.studio.client.workbench.views.source.editors.text.ScopeFunction) TokenCursor(org.rstudio.studio.client.workbench.views.source.editors.text.ace.TokenCursor) FileSystemItem(org.rstudio.core.client.files.FileSystemItem) CodeBrowserNavigationEvent(org.rstudio.studio.client.workbench.views.source.events.CodeBrowserNavigationEvent) SourcePosition(org.rstudio.studio.client.workbench.views.source.model.SourcePosition) SendToConsoleEvent(org.rstudio.studio.client.workbench.views.console.events.SendToConsoleEvent) FileFunctionDefinition(org.rstudio.studio.client.workbench.codesearch.model.FileFunctionDefinition)

Example 4 with TokenCursor

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

the class RoxygenHelper method extractCall.

private String extractCall(TokenCursor cursor) {
    // Force document tokenization
    editor_.getSession().getMode().getCodeModel().tokenizeUpToRow(editor_.getSession().getDocument().getLength());
    TokenCursor clone = cursor.cloneCursor();
    final Position startPos = clone.currentPosition();
    if (!clone.moveToNextToken())
        return null;
    if (!clone.currentValue().equals("("))
        return null;
    if (!clone.fwdToMatchingToken())
        return null;
    Position endPos = clone.currentPosition();
    endPos.setColumn(endPos.getColumn() + 1);
    return editor_.getSession().getTextRange(Range.fromPoints(startPos, endPos));
}
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)

Example 5 with TokenCursor

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

the class TextEditingTargetRenameHelper method renameFunctionArgument.

private int renameFunctionArgument(String functionName, String argName) {
    TokenCursor cursor = editor_.getSession().getMode().getCodeModel().getTokenCursor();
    Stack<String> functionNames = new Stack<String>();
    boolean renaming = false;
    do {
        if (cursor.isLeftBracket()) {
            if (cursor.valueEquals("(") && cursor.peekBwd(1).isValidForFunctionCall()) {
                String currentFunctionName = cursor.peekBwd(1).getValue();
                renaming = currentFunctionName.equals(functionName);
                functionNames.push(functionName);
                pushState(STATE_FUNCTION_CALL);
            } else {
                pushState(STATE_DEFAULT);
            }
        }
        if (cursor.isRightBracket()) {
            popState();
            if (cursor.valueEquals(")") && !functionNames.empty()) {
                functionNames.pop();
                renaming = !functionNames.empty() && functionNames.peek().equals(functionName);
            }
        }
        if (renaming && peekState() == STATE_FUNCTION_CALL && cursor.valueEquals(argName) && cursor.peekFwd(1).valueEquals("=")) {
            ranges_.add(getTokenRange(cursor));
        }
    } while (cursor.moveToNextToken());
    return applyRanges();
}
Also used : TokenCursor(org.rstudio.studio.client.workbench.views.source.editors.text.ace.TokenCursor) JsArrayString(com.google.gwt.core.client.JsArrayString) Stack(java.util.Stack)

Aggregations

TokenCursor (org.rstudio.studio.client.workbench.views.source.editors.text.ace.TokenCursor)12 Position (org.rstudio.studio.client.workbench.views.source.editors.text.ace.Position)7 AceEditor (org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor)6 JsArrayString (com.google.gwt.core.client.JsArrayString)5 CodeModel (org.rstudio.studio.client.workbench.views.source.editors.text.ace.CodeModel)4 ServerError (org.rstudio.studio.client.server.ServerError)2 InputEditorLineWithCursorPosition (org.rstudio.studio.client.workbench.views.console.shell.editor.InputEditorLineWithCursorPosition)2 ScopeFunction (org.rstudio.studio.client.workbench.views.source.editors.text.ScopeFunction)2 Token (org.rstudio.studio.client.workbench.views.source.editors.text.ace.Token)2 SourcePosition (org.rstudio.studio.client.workbench.views.source.model.SourcePosition)2 Element (com.google.gwt.dom.client.Element)1 Stack (java.util.Stack)1 FileSystemItem (org.rstudio.core.client.files.FileSystemItem)1 GlobalProgressDelayer (org.rstudio.studio.client.common.GlobalProgressDelayer)1 FileFunctionDefinition (org.rstudio.studio.client.workbench.codesearch.model.FileFunctionDefinition)1 ObjectDefinition (org.rstudio.studio.client.workbench.codesearch.model.ObjectDefinition)1 SearchPathFunctionDefinition (org.rstudio.studio.client.workbench.codesearch.model.SearchPathFunctionDefinition)1 SendToConsoleEvent (org.rstudio.studio.client.workbench.views.console.events.SendToConsoleEvent)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