1 /* 2 * RoxygenHelper.java 3 * 4 * Copyright (C) 2021 by RStudio, PBC 5 * 6 * Unless you have received this program directly from RStudio pursuant 7 * to the terms of a commercial license agreement with RStudio, then 8 * this program is licensed to you under the terms of version 3 of the 9 * GNU Affero General Public License. This program is distributed WITHOUT 10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, 11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the 12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. 13 * 14 */ 15 package org.rstudio.studio.client.common.r.roxygen; 16 17 import com.google.gwt.core.client.JavaScriptObject; 18 import com.google.gwt.core.client.JsArray; 19 import com.google.gwt.core.client.JsArrayInteger; 20 import com.google.gwt.core.client.JsArrayString; 21 import com.google.inject.Inject; 22 23 import org.rstudio.core.client.Debug; 24 import org.rstudio.core.client.StringUtil; 25 import org.rstudio.core.client.regex.Match; 26 import org.rstudio.core.client.regex.Pattern; 27 import org.rstudio.studio.client.RStudioGinjector; 28 import org.rstudio.studio.client.common.GlobalDisplay; 29 import org.rstudio.studio.client.common.filetypes.DocumentMode; 30 import org.rstudio.studio.client.server.ServerError; 31 import org.rstudio.studio.client.server.ServerRequestCallback; 32 import org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor; 33 import org.rstudio.studio.client.workbench.views.source.editors.text.DocDisplay; 34 import org.rstudio.studio.client.workbench.views.source.editors.text.Scope; 35 import org.rstudio.studio.client.workbench.views.source.editors.text.WarningBarDisplay; 36 import org.rstudio.studio.client.workbench.views.source.editors.text.ace.AceEditorNative; 37 import org.rstudio.studio.client.workbench.views.source.editors.text.ace.Position; 38 import org.rstudio.studio.client.workbench.views.source.editors.text.ace.Range; 39 import org.rstudio.studio.client.workbench.views.source.editors.text.ace.TokenCursor; 40 41 import java.util.ArrayList; 42 import java.util.Arrays; 43 44 public class RoxygenHelper 45 { RoxygenHelper(DocDisplay docDisplay, WarningBarDisplay view)46 public RoxygenHelper(DocDisplay docDisplay, 47 WarningBarDisplay view) 48 { 49 editor_ = (AceEditor) docDisplay; 50 view_ = view; 51 RStudioGinjector.INSTANCE.injectMembers(this); 52 } 53 54 @Inject initialize(GlobalDisplay globalDisplay, RoxygenServerOperations server)55 void initialize(GlobalDisplay globalDisplay, 56 RoxygenServerOperations server) 57 { 58 server_ = server; 59 globalDisplay_ = globalDisplay; 60 } 61 getFunctionName(Scope scope)62 private static native final String getFunctionName(Scope scope) 63 /*-{ 64 return scope.attributes.name; 65 }-*/; 66 getFunctionArgs(Scope scope)67 private static native final JsArrayString getFunctionArgs(Scope scope) 68 /*-{ 69 return scope.attributes.args; 70 }-*/; 71 insertRoxygenSkeleton()72 public void insertRoxygenSkeleton() 73 { 74 if (!DocumentMode.isCursorInRMode(editor_)) 75 return; 76 77 // We check these first because we might lie within an 78 // anonymous function scope, whereas what we first want 79 // to check is for an enclosing `setGeneric` etc. 80 TokenCursor cursor = getTokenCursor(); 81 if (cursor.moveToPosition(editor_.getCursorPosition(), true)) 82 { 83 String enclosingScope = findEnclosingScope(cursor); 84 85 if (enclosingScope == "setClass") 86 insertRoxygenSkeletonS4Class(cursor); 87 else if (enclosingScope == "setGeneric") 88 insertRoxygenSkeletonSetGeneric(cursor); 89 else if (enclosingScope == "setMethod") 90 insertRoxygenSkeletonSetMethod(cursor); 91 else if (enclosingScope == "setRefClass") 92 insertRoxygenSkeletonSetRefClass(cursor); 93 94 if (enclosingScope != null) 95 return; 96 } 97 98 // If the above checks failed, we'll want to insert a 99 // roxygen skeleton for a 'regular' function call. 100 Scope scope = editor_.getCurrentScope(); 101 if (scope != null && scope.isFunction()) 102 { 103 insertRoxygenSkeletonFunction(scope); 104 } 105 else 106 { 107 globalDisplay_.showErrorMessage( 108 "Insert Roxygen Skeleton", 109 "Unable to insert skeleton (the cursor is not currently " + 110 "inside an R function definition)."); 111 } 112 } 113 getTokenCursor()114 private TokenCursor getTokenCursor() 115 { 116 return editor_.getSession().getMode().getCodeModel().getTokenCursor(); 117 } 118 extractCall(TokenCursor cursor)119 private String extractCall(TokenCursor cursor) 120 { 121 // Force document tokenization 122 editor_.getSession().getMode().getCodeModel().tokenizeUpToRow( 123 editor_.getSession().getDocument().getLength()); 124 125 TokenCursor clone = cursor.cloneCursor(); 126 final Position startPos = clone.currentPosition(); 127 128 if (!clone.moveToNextToken()) 129 return null; 130 131 if (clone.currentValue() != "(") 132 return null; 133 134 if (!clone.fwdToMatchingToken()) 135 return null; 136 137 Position endPos = clone.currentPosition(); 138 endPos.setColumn(endPos.getColumn() + 1); 139 140 return editor_.getSession().getTextRange( 141 Range.fromPoints(startPos, endPos)); 142 } 143 insertRoxygenSkeletonSetRefClass(TokenCursor cursor)144 private void insertRoxygenSkeletonSetRefClass(TokenCursor cursor) 145 { 146 final Position startPos = cursor.currentPosition(); 147 String call = extractCall(cursor); 148 if (call == null) 149 return; 150 151 server_.getSetRefClassCall( 152 call, 153 new ServerRequestCallback<SetRefClassCall>() 154 { 155 @Override 156 public void onResponseReceived(SetRefClassCall response) 157 { 158 if (hasRoxygenBlock(startPos)) 159 { 160 amendExistingRoxygenBlock( 161 startPos.getRow() - 1, 162 response.getClassName(), 163 response.getFieldNames(), 164 response.getFieldTypes(), 165 "field", 166 RE_ROXYGEN_FIELD); 167 } 168 else 169 { 170 insertRoxygenTemplate( 171 response.getClassName(), 172 response.getFieldNames(), 173 response.getFieldTypes(), 174 "field", 175 "reference class", 176 startPos); 177 } 178 } 179 180 @Override 181 public void onError(ServerError error) 182 { 183 Debug.logError(error); 184 } 185 186 }); 187 } 188 insertRoxygenSkeletonSetGeneric(TokenCursor cursor)189 private void insertRoxygenSkeletonSetGeneric(TokenCursor cursor) 190 { 191 final Position startPos = cursor.currentPosition(); 192 String call = extractCall(cursor); 193 if (call == null) 194 return; 195 196 server_.getSetGenericCall( 197 call, 198 new ServerRequestCallback<SetGenericCall>() 199 { 200 @Override 201 public void onResponseReceived(SetGenericCall response) 202 { 203 if (hasRoxygenBlock(startPos)) 204 { 205 amendExistingRoxygenBlock( 206 startPos.getRow() - 1, 207 response.getGeneric(), 208 response.getParameters(), 209 null, 210 "param", 211 RE_ROXYGEN_PARAM); 212 } 213 else 214 { 215 insertRoxygenTemplate( 216 response.getGeneric(), 217 response.getParameters(), 218 null, 219 "param", 220 "generic function", 221 startPos); 222 } 223 } 224 225 @Override 226 public void onError(ServerError error) 227 { 228 Debug.logError(error); 229 } 230 231 }); 232 } 233 insertRoxygenSkeletonSetMethod(TokenCursor cursor)234 private void insertRoxygenSkeletonSetMethod(TokenCursor cursor) 235 { 236 final Position startPos = cursor.currentPosition(); 237 String call = extractCall(cursor); 238 if (call == null) 239 return; 240 241 server_.getSetMethodCall( 242 call, 243 new ServerRequestCallback<SetMethodCall>() 244 { 245 @Override 246 public void onResponseReceived(SetMethodCall response) 247 { 248 if (hasRoxygenBlock(startPos)) 249 { 250 amendExistingRoxygenBlock( 251 startPos.getRow() - 1, 252 response.getGeneric(), 253 response.getParameterNames(), 254 response.getParameterTypes(), 255 "param", 256 RE_ROXYGEN_PARAM); 257 } 258 else 259 { 260 insertRoxygenTemplate( 261 response.getGeneric(), 262 response.getParameterNames(), 263 response.getParameterTypes(), 264 "param", 265 "method", 266 startPos); 267 } 268 } 269 270 @Override 271 public void onError(ServerError error) 272 { 273 Debug.logError(error); 274 } 275 276 }); 277 } 278 279 insertRoxygenSkeletonS4Class(TokenCursor cursor)280 private void insertRoxygenSkeletonS4Class(TokenCursor cursor) 281 { 282 final Position startPos = cursor.currentPosition(); 283 String setClassCall = extractCall(cursor); 284 if (setClassCall == null) 285 return; 286 287 server_.getSetClassCall( 288 setClassCall, 289 new ServerRequestCallback<SetClassCall>() 290 { 291 @Override 292 public void onResponseReceived(SetClassCall response) 293 { 294 if (hasRoxygenBlock(startPos)) 295 { 296 amendExistingRoxygenBlock( 297 startPos.getRow() - 1, 298 response.getClassName(), 299 response.getSlots(), 300 null, 301 "slot", 302 RE_ROXYGEN_SLOT); 303 } 304 else 305 { 306 insertRoxygenTemplate( 307 response.getClassName(), 308 response.getSlots(), 309 response.getTypes(), 310 "slot", 311 "S4 class", 312 startPos); 313 } 314 } 315 316 @Override 317 public void onError(ServerError error) 318 { 319 Debug.logError(error); 320 } 321 }); 322 } 323 findEnclosingScope(TokenCursor cursor)324 private String findEnclosingScope(TokenCursor cursor) 325 { 326 if (ROXYGEN_ANNOTATABLE_CALLS.contains(cursor.currentValue())) 327 return cursor.currentValue(); 328 329 // Check to see if we're on e.g. `x <- setRefClass(...)`. 330 if (cursor.isLeftAssign() && 331 ROXYGEN_ANNOTATABLE_CALLS.contains(cursor.nextValue())) 332 { 333 cursor.moveToNextToken(); 334 return cursor.currentValue(); 335 } 336 337 if (ROXYGEN_ANNOTATABLE_CALLS.contains(cursor.nextValue(2))) 338 { 339 cursor.moveToNextToken(); 340 cursor.moveToNextToken(); 341 return cursor.currentValue(); 342 } 343 344 while (cursor.currentValue() ==")") 345 if (!cursor.moveToPreviousToken()) 346 return null; 347 348 while (cursor.findOpeningBracket("(", false)) 349 { 350 if (!cursor.moveToPreviousToken()) 351 return null; 352 353 if (ROXYGEN_ANNOTATABLE_CALLS.contains(cursor.currentValue())) 354 return cursor.currentValue(); 355 } 356 357 return null; 358 } 359 insertRoxygenSkeletonFunction(Scope scope)360 public void insertRoxygenSkeletonFunction(Scope scope) 361 { 362 // Attempt to find the bounds for the roxygen block 363 // associated with this function, if it exists 364 if (hasRoxygenBlock(scope.getPreamble())) 365 { 366 amendExistingRoxygenBlock( 367 scope.getPreamble().getRow() - 1, 368 getFunctionName(scope), 369 getFunctionArgs(scope), 370 null, 371 "param", 372 RE_ROXYGEN_PARAM); 373 } 374 else 375 { 376 insertRoxygenTemplate( 377 getFunctionName(scope), 378 getFunctionArgs(scope), 379 null, 380 "param", 381 "function", 382 scope.getPreamble()); 383 } 384 } 385 amendExistingRoxygenBlock( int row, String objectName, JsArrayString argNames, JsArrayString argTypes, String tagName, Pattern pattern)386 private void amendExistingRoxygenBlock( 387 int row, 388 String objectName, 389 JsArrayString argNames, 390 JsArrayString argTypes, 391 String tagName, 392 Pattern pattern) 393 { 394 // Get the range encompassing this Roxygen block. 395 Range range = getRoxygenBlockRange(row); 396 397 // Extract that block (as an array of strings) 398 JsArrayString block = extractRoxygenBlock( 399 editor_.getWidget().getEditor(), 400 range); 401 402 // If the block contains roxygen parameters that require 403 // non-local information (e.g. @inheritParams), then 404 // bail. 405 for (int i = 0; i < block.length(); i++) 406 { 407 if (RE_ROXYGEN_NONLOCAL.test(block.get(i))) 408 { 409 view_.showWarningBar( 410 "Cannot automatically update roxygen blocks " + 411 "that are not self-contained."); 412 return; 413 } 414 } 415 416 String roxygenDelim = RE_ROXYGEN.match(block.get(0), 0).getGroup(1); 417 418 // The replacement block (we build by munging parts of 419 // the old block 420 JsArrayString replacement = JsArray.createArray().cast(); 421 422 // Scan through the block to get the names of 423 // pre-existing parameters. 424 JsArrayString params = listParametersInRoxygenBlock( 425 block, 426 pattern); 427 428 // Figure out what parameters need to be removed, and remove them. 429 // Any parameter not mentioned in the current function's argument list 430 // should be stripped out. 431 JsArrayString paramsToRemove = setdiff(params, argNames); 432 433 int blockLength = block.length(); 434 for (int i = 0; i < blockLength; i++) 435 { 436 // If we encounter a param we don't want to extract, then 437 // move over it. 438 Match match = pattern.match(block.get(i), 0); 439 if (match != null && contains(paramsToRemove, match.getGroup(1))) 440 { 441 i++; 442 while (i < blockLength && !RE_ROXYGEN_WITH_TAG.test(block.get(i))) 443 i++; 444 445 i--; 446 continue; 447 } 448 449 replacement.push(block.get(i)); 450 } 451 452 // Now, add example roxygen for any parameters that are 453 // present in the function prototype, but not present 454 // within the roxygen block. 455 int insertionPosition = findParamsInsertionPosition(replacement, pattern); 456 JsArrayInteger indices = setdiffIndices(argNames, params); 457 458 // NOTE: modifies replacement 459 insertNewTags( 460 replacement, 461 argNames, 462 argTypes, 463 indices, 464 roxygenDelim, 465 tagName, 466 insertionPosition); 467 468 // Ensure space between final param and next tag 469 ensureSpaceBetweenFirstParamAndPreviousEntry(replacement, roxygenDelim, pattern); 470 ensureSpaceBetweenFinalParamAndNextTag(replacement, roxygenDelim, pattern); 471 472 // Apply the replacement. 473 editor_.getSession().replace(range, replacement.join("\n") + "\n"); 474 } 475 ensureSpaceBetweenFirstParamAndPreviousEntry( JsArrayString replacement, String roxygenDelim, Pattern pattern)476 private void ensureSpaceBetweenFirstParamAndPreviousEntry( 477 JsArrayString replacement, 478 String roxygenDelim, 479 Pattern pattern) 480 { 481 int n = replacement.length(); 482 for (int i = 1; i < n; i++) 483 { 484 if (pattern.test(replacement.get(i))) 485 { 486 if (!RE_ROXYGEN_EMPTY.test(replacement.get(i - 1))) 487 spliceIntoArray(replacement, roxygenDelim, i); 488 return; 489 } 490 } 491 } 492 ensureSpaceBetweenFinalParamAndNextTag( JsArrayString replacement, String roxygenDelim, Pattern pattern)493 private void ensureSpaceBetweenFinalParamAndNextTag( 494 JsArrayString replacement, 495 String roxygenDelim, 496 Pattern pattern) 497 { 498 int n = replacement.length(); 499 for (int i = n - 1; i >= 0; i--) 500 { 501 if (pattern.test(replacement.get(i))) 502 { 503 i++; 504 if (i < n && RE_ROXYGEN_WITH_TAG.test(replacement.get(i))) 505 { 506 spliceIntoArray(replacement, roxygenDelim, i); 507 } 508 return; 509 } 510 } 511 } 512 spliceIntoArray( JsArrayString array, String string, int pos)513 private static final native void spliceIntoArray( 514 JsArrayString array, 515 String string, 516 int pos) 517 /*-{ 518 array.splice(pos, 0, string); 519 }-*/; 520 insertNewTags( JsArrayString array, JsArrayString argNames, JsArrayString argTypes, JsArrayInteger indices, String roxygenDelim, String tagName, int position)521 private static final native void insertNewTags( 522 JsArrayString array, 523 JsArrayString argNames, 524 JsArrayString argTypes, 525 JsArrayInteger indices, 526 String roxygenDelim, 527 String tagName, 528 int position) 529 /*-{ 530 531 var newRoxygenEntries = []; 532 for (var i = 0; i < indices.length; i++) { 533 534 var idx = indices[i]; 535 var arg = argNames[idx]; 536 var type = argTypes == null ? null : argTypes[idx]; 537 538 var entry = roxygenDelim + " @" + tagName + " " + arg + " "; 539 540 if (type != null) 541 entry += type = ". "; 542 543 newRoxygenEntries.push(entry); 544 } 545 546 Array.prototype.splice.apply( 547 array, 548 [position, 0].concat(newRoxygenEntries) 549 ); 550 551 }-*/; 552 findParamsInsertionPosition( JsArrayString block, Pattern pattern)553 private int findParamsInsertionPosition( 554 JsArrayString block, 555 Pattern pattern) 556 { 557 // Try to find the last '@param' block, and insert after that. 558 int n = block.length(); 559 for (int i = n - 1; i >= 0; i--) 560 { 561 if (pattern.test(block.get(i))) 562 { 563 i++; 564 565 // Move up to the next tag (or end) 566 while (i < n && !RE_ROXYGEN_WITH_TAG.test(block.get(i))) 567 i++; 568 569 return i - 1; 570 } 571 } 572 573 // Try to find the first tag, and insert before that. 574 for (int i = 0; i < n; i++) 575 if (RE_ROXYGEN_WITH_TAG.test(block.get(i))) 576 return i; 577 578 // Just insert at the end 579 return block.length(); 580 } 581 setdiff( JsArrayString self, JsArrayString other)582 private static JsArrayString setdiff( 583 JsArrayString self, 584 JsArrayString other) 585 { 586 JsArrayString result = JsArray.createArray().cast(); 587 for (int i = 0; i < self.length(); i++) 588 if (!contains(other, self.get(i))) 589 result.push(self.get(i)); 590 return result; 591 } 592 setdiffIndices( JsArrayString self, JsArrayString other)593 private static JsArrayInteger setdiffIndices( 594 JsArrayString self, 595 JsArrayString other) 596 { 597 JsArrayInteger result = JsArray.createArray().cast(); 598 for (int i = 0; i < self.length(); i++) 599 if (!contains(other, self.get(i))) 600 result.push(i); 601 return result; 602 } 603 contains( JsArrayString array, String object)604 private static native final boolean contains( 605 JsArrayString array, 606 String object) 607 /*-{ 608 for (var i = 0, n = array.length; i < n; i++) 609 if (array[i] === object) 610 return true; 611 return false; 612 }-*/; 613 614 extractRoxygenBlock( AceEditorNative editor, Range range)615 private static native final JsArrayString extractRoxygenBlock( 616 AceEditorNative editor, 617 Range range) 618 /*-{ 619 var lines = editor.getSession().getDocument().$lines; 620 return lines.slice(range.start.row, range.end.row); 621 }-*/; 622 listParametersInRoxygenBlock( JsArrayString block, Pattern pattern)623 private JsArrayString listParametersInRoxygenBlock( 624 JsArrayString block, 625 Pattern pattern) 626 { 627 JsArrayString roxygenParams = JsArrayString.createArray().cast(); 628 for (int i = 0; i < block.length(); i++) 629 { 630 String line = block.get(i); 631 Match match = pattern.match(line, 0); 632 if (match != null) 633 roxygenParams.push(match.getGroup(1)); 634 } 635 636 return roxygenParams; 637 } 638 getRoxygenBlockRange(int row)639 private Range getRoxygenBlockRange(int row) 640 { 641 while (row >= 0 && StringUtil.isWhitespace(editor_.getLine(row))) 642 row--; 643 644 int blockEnd = row + 1; 645 646 while (row >= 0 && RE_ROXYGEN.test(editor_.getLine(row))) 647 row--; 648 649 if (row == 0 && !RE_ROXYGEN.test(editor_.getLine(row))) 650 row++; 651 652 int blockStart = row + 1; 653 654 return Range.fromPoints( 655 Position.create(blockStart, 0), 656 Position.create(blockEnd, 0)); 657 } 658 hasRoxygenBlock(Position position)659 private boolean hasRoxygenBlock(Position position) 660 { 661 int row = position.getRow() - 1; 662 if (row < 0) return false; 663 664 // Skip whitespace. 665 while (row >= 0 && StringUtil.isWhitespace(editor_.getLine(row))) 666 row--; 667 668 // Check if we landed on an Roxygen block. 669 return RE_ROXYGEN.test(editor_.getLine(row)); 670 } 671 insertRoxygenTemplate( String name, JsArrayString argNames, JsArrayString argTypes, String argTagName, String type, Position position)672 private void insertRoxygenTemplate( 673 String name, 674 JsArrayString argNames, 675 JsArrayString argTypes, 676 String argTagName, 677 String type, 678 Position position) 679 { 680 String roxygenParams = argsToExampleRoxygen( 681 argNames, 682 argTypes, 683 argTagName); 684 685 // Add some spacing between params and the next tags, 686 // if there were one or more arguments. 687 if (argNames.length() != 0) 688 roxygenParams += "\n#'\n"; 689 690 String block = 691 "#' Title\n" + 692 "#'\n" + 693 roxygenParams + 694 "#' @return\n" + 695 "#' @export\n" + 696 "#'\n" + 697 "#' @examples\n"; 698 699 Position insertionPosition = Position.create( 700 position.getRow(), 701 0); 702 703 editor_.insertCode(insertionPosition, block); 704 } 705 argsToExampleRoxygen( JsArrayString argNames, JsArrayString argTypes, String tagName)706 private String argsToExampleRoxygen( 707 JsArrayString argNames, 708 JsArrayString argTypes, 709 String tagName) 710 { 711 String roxygen = ""; 712 if (argNames.length() == 0) return ""; 713 714 if (argTypes == null) 715 { 716 roxygen += argToExampleRoxygen(argNames.get(0), null, tagName); 717 for (int i = 1; i < argNames.length(); i++) 718 roxygen += "\n" + argToExampleRoxygen(argNames.get(i), null, tagName); 719 } 720 else 721 { 722 roxygen += argToExampleRoxygen(argNames.get(0), argTypes.get(0), tagName); 723 for (int i = 1; i < argNames.length(); i++) 724 roxygen += "\n" + argToExampleRoxygen(argNames.get(i), argTypes.get(i), tagName); 725 } 726 727 return roxygen; 728 } 729 argToExampleRoxygen(String argName, String argType, String tagName)730 private String argToExampleRoxygen(String argName, String argType, String tagName) 731 { 732 String output = "#' @" + tagName + " " + argName + " "; 733 if (argType != null) 734 output += argType + ". "; 735 return output; 736 } 737 738 public static class SetClassCall extends JavaScriptObject 739 { SetClassCall()740 protected SetClassCall() {} 741 getClassName()742 public final native String getClassName() /*-{ return this["Class"]; }-*/; getSlots()743 public final native JsArrayString getSlots() /*-{ return this["slots"]; }-*/; getTypes()744 public final native JsArrayString getTypes() /*-{ return this["types"]; }-*/; getNumSlots()745 public final native int getNumSlots() /*-{ return this["slots"].length; }-*/; 746 } 747 748 public static class SetGenericCall extends JavaScriptObject 749 { SetGenericCall()750 protected SetGenericCall() {} 751 getGeneric()752 public final native String getGeneric() /*-{ return this["generic"]; }-*/; getParameters()753 public final native JsArrayString getParameters() /*-{ return this["parameters"]; }-*/; 754 } 755 756 public static class SetMethodCall extends JavaScriptObject 757 { SetMethodCall()758 protected SetMethodCall() {} 759 getGeneric()760 public final native String getGeneric() /*-{ return this["generic"]; }-*/; getParameterNames()761 public final native JsArrayString getParameterNames() /*-{ return this["parameter.names"]; }-*/; getParameterTypes()762 public final native JsArrayString getParameterTypes() /*-{ return this["parameter.types"]; }-*/; 763 } 764 765 public static class SetRefClassCall extends JavaScriptObject 766 { SetRefClassCall()767 protected SetRefClassCall() {} 768 getClassName()769 public final native String getClassName() /*-{ return this["Class"]; }-*/; getFieldNames()770 public final native JsArrayString getFieldNames() /*-{ return this["field.names"]; }-*/; getFieldTypes()771 public final native JsArrayString getFieldTypes() /*-{ return this["field.types"]; }-*/; 772 } 773 774 private final AceEditor editor_; 775 private final WarningBarDisplay view_; 776 private GlobalDisplay globalDisplay_; 777 778 private RoxygenServerOperations server_; 779 780 private static final Pattern RE_ROXYGEN = 781 Pattern.create("^(\\s*#+')", ""); 782 783 private static final Pattern RE_ROXYGEN_EMPTY = 784 Pattern.create("^\\s*#+'\\s*$", ""); 785 786 private static final Pattern RE_ROXYGEN_PARAM = 787 Pattern.create("^\\s*#+'\\s*@param\\s+([^\\s]+)", ""); 788 789 private static final Pattern RE_ROXYGEN_FIELD = 790 Pattern.create("^\\s*#+'\\s*@field\\s+([^\\s]+)", ""); 791 792 private static final Pattern RE_ROXYGEN_SLOT = 793 Pattern.create("^\\s*#+'\\s*@slot\\s+([^\\s]+)", ""); 794 795 private static final Pattern RE_ROXYGEN_WITH_TAG = 796 Pattern.create("^\\s*#+'\\s*@[^@]", ""); 797 798 private static final ArrayList<String> ROXYGEN_ANNOTATABLE_CALLS = 799 new ArrayList<>( 800 Arrays.asList(new String[] { 801 "setClass", 802 "setRefClass", 803 "setMethod", 804 "setGeneric" 805 })); 806 807 private static final Pattern RE_ROXYGEN_NONLOCAL = 808 Pattern.create("^\\s*#+'\\s*@(?:inheritParams|template)", ""); 809 } 810