use of org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor 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));
}
}
}
}
}
}
}
use of org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor 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));
}
}
}
use of org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor 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));
}
}
}
}
}
}
use of org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor 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;
}
use of org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor 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());
}
});
}
Aggregations