1 /* 2 * TextFileType.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.filetypes; 16 17 import com.google.gwt.resources.client.ImageResource; 18 19 import org.rstudio.core.client.FilePosition; 20 import org.rstudio.core.client.UnicodeLetters; 21 import org.rstudio.core.client.command.AppCommand; 22 import org.rstudio.core.client.files.FileSystemItem; 23 import org.rstudio.core.client.regex.Pattern; 24 import org.rstudio.studio.client.application.events.EventBus; 25 import org.rstudio.studio.client.common.filetypes.events.OpenSourceFileEvent; 26 import org.rstudio.studio.client.common.filetypes.model.NavigationMethods; 27 import org.rstudio.studio.client.common.reditor.EditorLanguage; 28 import org.rstudio.studio.client.workbench.commands.Commands; 29 import org.rstudio.studio.client.workbench.views.source.SourceWindowManager; 30 import org.rstudio.studio.client.workbench.views.source.editors.text.ace.spelling.CharClassifier; 31 import org.rstudio.studio.client.workbench.views.source.editors.text.ace.spelling.TokenPredicate; 32 33 import java.util.HashSet; 34 35 public class TextFileType extends EditableFileType 36 { TextFileType(String id, String label, EditorLanguage editorLanguage, String defaultExtension, ImageResource defaultIcon, boolean wordWrap, boolean canSourceOnSave, boolean canExecuteCode, boolean canExecuteAllCode, boolean canExecuteToCurrentLine, boolean canPreviewHTML, boolean canKnitToHTML, boolean canCompilePDF, boolean canExecuteChunks, boolean canAutoIndent, boolean canCheckSpelling, boolean canShowScopeTree, boolean canPreviewFromR)37 TextFileType(String id, 38 String label, 39 EditorLanguage editorLanguage, 40 String defaultExtension, 41 ImageResource defaultIcon, 42 boolean wordWrap, 43 boolean canSourceOnSave, 44 boolean canExecuteCode, 45 boolean canExecuteAllCode, 46 boolean canExecuteToCurrentLine, 47 boolean canPreviewHTML, 48 boolean canKnitToHTML, 49 boolean canCompilePDF, 50 boolean canExecuteChunks, 51 boolean canAutoIndent, 52 boolean canCheckSpelling, 53 boolean canShowScopeTree, 54 boolean canPreviewFromR) 55 { 56 super(id, label, defaultIcon); 57 editorLanguage_ = editorLanguage; 58 defaultExtension_ = defaultExtension; 59 wordWrap_ = wordWrap; 60 canSourceOnSave_ = canSourceOnSave; 61 canExecuteCode_ = canExecuteCode; 62 canExecuteAllCode_ = canExecuteAllCode; 63 canExecuteToCurrentLine_ = canExecuteToCurrentLine; 64 canKnitToHTML_ = canKnitToHTML; 65 canPreviewHTML_ = canPreviewHTML; 66 canCompilePDF_ = canCompilePDF; 67 canExecuteChunks_ = canExecuteChunks; 68 canAutoIndent_ = canAutoIndent; 69 canCheckSpelling_ = canCheckSpelling; 70 canShowScopeTree_ = canShowScopeTree; 71 canPreviewFromR_ = canPreviewFromR; 72 } 73 74 @Override openFile(FileSystemItem file, FilePosition position, int navMethod, EventBus eventBus)75 public void openFile(FileSystemItem file, 76 FilePosition position, 77 int navMethod, 78 EventBus eventBus) 79 { 80 eventBus.fireEvent(new OpenSourceFileEvent(file, 81 position, 82 this, 83 navMethod)); 84 } 85 86 @Override openFile(FileSystemItem file, EventBus eventBus)87 public void openFile(FileSystemItem file, EventBus eventBus) 88 { 89 openFile(file, null, NavigationMethods.DEFAULT, eventBus); 90 } 91 getEditorLanguage()92 public EditorLanguage getEditorLanguage() 93 { 94 return editorLanguage_; 95 } 96 getWordWrap()97 public boolean getWordWrap() 98 { 99 return wordWrap_; 100 } 101 canSource()102 public boolean canSource() 103 { 104 return canExecuteCode_ && !canExecuteChunks_; 105 } 106 canSourceWithEcho()107 public boolean canSourceWithEcho() 108 { 109 return canSource(); 110 } 111 canSourceOnSave()112 public boolean canSourceOnSave() 113 { 114 return canSourceOnSave_; 115 } 116 canExecuteCode()117 public boolean canExecuteCode() 118 { 119 return canExecuteCode_; 120 } 121 canExecuteAllCode()122 public boolean canExecuteAllCode() 123 { 124 return canExecuteAllCode_; 125 } 126 canExecuteToCurrentLine()127 public boolean canExecuteToCurrentLine() 128 { 129 return canExecuteToCurrentLine_; 130 } 131 canKnitToHTML()132 public boolean canKnitToHTML() 133 { 134 return canKnitToHTML_; 135 } 136 canPreviewHTML()137 public boolean canPreviewHTML() 138 { 139 return canPreviewHTML_; 140 } 141 canCompilePDF()142 public boolean canCompilePDF() 143 { 144 return canCompilePDF_; 145 } 146 canCompileNotebook()147 public boolean canCompileNotebook() 148 { 149 return false; 150 } 151 canAuthorContent()152 public boolean canAuthorContent() 153 { 154 return canKnitToHTML() || canPreviewHTML() || canCompilePDF(); 155 } 156 canExecuteChunks()157 public boolean canExecuteChunks() 158 { 159 return canExecuteChunks_; 160 } 161 canAutoIndent()162 public boolean canAutoIndent() 163 { 164 return canAutoIndent_; 165 } 166 canCheckSpelling()167 public boolean canCheckSpelling() 168 { 169 return canCheckSpelling_; 170 } 171 canShowScopeTree()172 public boolean canShowScopeTree() 173 { 174 return canShowScopeTree_; 175 } 176 canGoNextPrevSection()177 public boolean canGoNextPrevSection() 178 { 179 return isRmd() || isRpres(); 180 } 181 canPreviewFromR()182 public boolean canPreviewFromR() 183 { 184 return canPreviewFromR_; 185 } 186 isText()187 public boolean isText() 188 { 189 return FileTypeRegistry.TEXT.getTypeId().equals(getTypeId()); 190 } 191 isR()192 public boolean isR() 193 { 194 return FileTypeRegistry.R.getTypeId().equals(getTypeId()); 195 } 196 isRnw()197 public boolean isRnw() 198 { 199 return FileTypeRegistry.SWEAVE.getTypeId().equals(getTypeId()); 200 } 201 isRd()202 public boolean isRd() 203 { 204 return FileTypeRegistry.RD.getTypeId().equals(getTypeId()); 205 } 206 isJS()207 public boolean isJS() 208 { 209 return FileTypeRegistry.JS.getTypeId().equals(getTypeId()); 210 } 211 isRmd()212 public boolean isRmd() 213 { 214 return FileTypeRegistry.RMARKDOWN.getTypeId().equals(getTypeId()) || 215 isQuartoMarkdown(); 216 } 217 isRhtml()218 public boolean isRhtml() 219 { 220 return FileTypeRegistry.RHTML.getTypeId().equals(getTypeId()); 221 } 222 isRpres()223 public boolean isRpres() 224 { 225 return FileTypeRegistry.RPRESENTATION.getTypeId().equals(getTypeId()); 226 } 227 isSql()228 public boolean isSql() 229 { 230 return FileTypeRegistry.SQL.getTypeId().equals(getTypeId()); 231 } 232 isYaml()233 public boolean isYaml() 234 { 235 return FileTypeRegistry.YAML.getTypeId().equals(getTypeId()); 236 } 237 requiresKnit()238 public boolean requiresKnit() 239 { 240 return FileTypeRegistry.RMARKDOWN.getTypeId().equals(getTypeId()) || 241 FileTypeRegistry.RHTML.getTypeId().equals(getTypeId()) || 242 FileTypeRegistry.RPRESENTATION.getTypeId().equals(getTypeId()); 243 } 244 isMarkdown()245 public boolean isMarkdown() 246 { 247 return FileTypeRegistry.RMARKDOWN.getTypeId().equals(getTypeId()) || 248 isQuartoMarkdown() || 249 FileTypeRegistry.MARKDOWN.getTypeId().equals(getTypeId()); 250 } 251 isQuartoMarkdown()252 public boolean isQuartoMarkdown() 253 { 254 return FileTypeRegistry.QUARTO.getTypeId().equals(getTypeId()); 255 } 256 isPlainMarkdown()257 public boolean isPlainMarkdown() 258 { 259 return FileTypeRegistry.MARKDOWN.getTypeId().equals(getTypeId()); 260 } 261 isRNotebook()262 public boolean isRNotebook() 263 { 264 return FileTypeRegistry.RNOTEBOOK.getTypeId().equals(getTypeId()); 265 } 266 isC()267 public boolean isC() 268 { 269 return EditorLanguage.LANG_CPP.equals(getEditorLanguage()); 270 } 271 isPython()272 public boolean isPython() 273 { 274 return EditorLanguage.LANG_PYTHON.equals(getEditorLanguage()); 275 } 276 isCpp()277 public boolean isCpp() 278 { 279 return false; 280 } 281 isStan()282 public boolean isStan() 283 { 284 return EditorLanguage.LANG_STAN.equals(getEditorLanguage()); 285 } 286 getPreviewButtonText()287 public String getPreviewButtonText() 288 { 289 return "Preview"; 290 } 291 createPreviewCommand(String file)292 public String createPreviewCommand(String file) 293 { 294 return null; 295 } 296 isScript()297 public boolean isScript() 298 { 299 return false; 300 } 301 getScriptInterpreter()302 public String getScriptInterpreter() 303 { 304 return null; 305 } 306 getSupportedCommands(Commands commands)307 public HashSet<AppCommand> getSupportedCommands(Commands commands) 308 { 309 HashSet<AppCommand> results = new HashSet<>(); 310 results.add(commands.saveSourceDoc()); 311 results.add(commands.reopenSourceDocWithEncoding()); 312 results.add(commands.saveSourceDocAs()); 313 results.add(commands.saveSourceDocWithEncoding()); 314 results.add(commands.printSourceDoc()); 315 results.add(commands.vcsFileLog()); 316 results.add(commands.vcsFileDiff()); 317 results.add(commands.vcsFileRevert()); 318 results.add(commands.vcsViewOnGitHub()); 319 results.add(commands.vcsBlameOnGitHub()); 320 results.add(commands.goToLine()); 321 results.add(commands.expandSelection()); 322 results.add(commands.shrinkSelection()); 323 324 if (isJS()) 325 { 326 results.add(commands.previewJS()); 327 } 328 329 if (isSql()) 330 { 331 results.add(commands.previewSql()); 332 } 333 334 if (isYaml()) 335 { 336 results.add(commands.commentUncomment()); 337 } 338 339 if ((canExecuteCode() && !isScript()) || isC()) 340 { 341 results.add(commands.reindent()); 342 results.add(commands.showDiagnosticsActiveDocument()); 343 } 344 345 if (canExecuteCode() && !isC() && !isScript()) 346 { 347 results.add(commands.executeCurrentFunction()); 348 } 349 350 if (canExecuteCode()) 351 { 352 results.add(commands.executeCode()); 353 results.add(commands.executeCodeWithoutFocus()); 354 355 if (!isScript()) 356 { 357 results.add(commands.executeLastCode()); 358 results.add(commands.extractFunction()); 359 results.add(commands.extractLocalVariable()); 360 results.add(commands.commentUncomment()); 361 results.add(commands.reflowComment()); 362 results.add(commands.reformatCode()); 363 results.add(commands.renameInScope()); 364 results.add(commands.profileCode()); 365 results.add(commands.profileCodeWithoutFocus()); 366 } 367 } 368 369 if (canExecuteAllCode()) 370 { 371 results.add(commands.executeAllCode()); 372 results.add(commands.sourceActiveDocument()); 373 // file types with chunks take the Cmd+Shift+Enter shortcut 374 // for run current chunk 375 if (!canExecuteChunks()) 376 results.add(commands.sourceActiveDocumentWithEcho()); 377 } 378 379 if (canExecuteToCurrentLine()) 380 { 381 results.add(commands.executeToCurrentLine()); 382 results.add(commands.executeFromCurrentLine()); 383 results.add(commands.executeCurrentSection()); 384 results.add(commands.profileCode()); 385 } 386 if (canKnitToHTML()) 387 { 388 results.add(commands.editRmdFormatOptions()); 389 results.add(commands.knitWithParameters()); 390 results.add(commands.clearKnitrCache()); 391 results.add(commands.restartRClearOutput()); 392 results.add(commands.restartRRunAllChunks()); 393 results.add(commands.notebookCollapseAllOutput()); 394 results.add(commands.notebookExpandAllOutput()); 395 results.add(commands.executeSetupChunk()); 396 } 397 398 if (canKnitToHTML() || canCompileNotebook()) 399 { 400 results.add(commands.knitDocument()); 401 } 402 if (canPreviewHTML()) 403 { 404 results.add(commands.previewHTML()); 405 } 406 if (canKnitToHTML() || canPreviewHTML()) 407 { 408 results.add(commands.quartoRenderDocument()); 409 } 410 if (canCompilePDF()) 411 { 412 results.add(commands.compilePDF()); 413 results.add(commands.synctexSearch()); 414 } 415 if (canCompileNotebook()) 416 { 417 results.add(commands.compileNotebook()); 418 } 419 if (canExecuteChunks()) 420 { 421 results.add(commands.insertChunk()); 422 results.add(commands.executePreviousChunks()); 423 results.add(commands.executeSubsequentChunks()); 424 results.add(commands.executeCurrentChunk()); 425 results.add(commands.executeNextChunk()); 426 results.add(commands.runSelectionAsJob()); 427 results.add(commands.runSelectionAsLauncherJob()); 428 } 429 if (isMarkdown()) 430 { 431 results.add(commands.toggleRmdVisualMode()); 432 results.add(commands.enableProsemirrorDevTools()); 433 } 434 if (canCheckSpelling()) 435 { 436 results.add(commands.checkSpelling()); 437 } 438 if (canShowScopeTree()) 439 { 440 results.add(commands.toggleDocumentOutline()); 441 } 442 443 results.add(commands.wordCount()); 444 results.add(commands.goToNextSection()); 445 results.add(commands.goToPrevSection()); 446 results.add(commands.goToNextChunk()); 447 results.add(commands.goToPrevChunk()); 448 results.add(commands.findReplace()); 449 results.add(commands.findNext()); 450 results.add(commands.findPrevious()); 451 results.add(commands.findFromSelection()); 452 results.add(commands.replaceAndFind()); 453 results.add(commands.setWorkingDirToActiveDoc()); 454 results.add(commands.debugDumpContents()); 455 results.add(commands.debugImportDump()); 456 results.add(commands.popoutDoc()); 457 if (!SourceWindowManager.isMainSourceWindow()) 458 results.add(commands.returnDocToMain()); 459 460 if (isR()) 461 { 462 results.add(commands.sourceAsLauncherJob()); 463 results.add(commands.sourceAsJob()); 464 results.add(commands.runSelectionAsJob()); 465 results.add(commands.runSelectionAsLauncherJob()); 466 results.add(commands.runDocumentFromServerDotR()); 467 } 468 469 results.add(commands.sendToTerminal()); 470 results.add(commands.sendFilenameToTerminal()); 471 results.add(commands.openNewTerminalAtEditorLocation()); 472 results.add(commands.toggleSoftWrapMode()); 473 474 return results; 475 } 476 getDefaultExtension()477 public String getDefaultExtension() 478 { 479 return defaultExtension_; 480 } 481 getTokenPredicate()482 public TokenPredicate getTokenPredicate() 483 { 484 return (token, row, column) -> 485 { 486 if (reNospellType_.match(token.getType(), 0) != null) { 487 return false; 488 } 489 490 return reTextType_.match(token.getType(), 0) != null || 491 reStringType_.match(token.getType(), 0) != null || 492 reHeaderType_.match(token.getType(), 0) != null || 493 reCommentType_.match(token.getType(), 0) != null; 494 }; 495 } 496 497 // default to only returning comments and text, override in subclasses 498 // for more or less specificity 499 public TokenPredicate getSpellCheckTokenPredicate() 500 { 501 return (token, row, column) -> 502 { 503 if (reNospellType_.match(token.getType(), 0) != null) { 504 return false; 505 } 506 507 return (reCommentType_.match(token.getType(), 0) != null || 508 reTextType_.match(token.getType(), 0) != null) && 509 reKeywordType_.match(token.getType(), 0) == null && 510 reIdentifierType_.match(token.getType(), 0) == null; 511 }; 512 } 513 514 public CharClassifier getCharPredicate() 515 { 516 return new CharClassifier() 517 { 518 @Override 519 public CharClass classify(char c) 520 { 521 if (UnicodeLetters.isLetter(c)) 522 return CharClass.Word; 523 else if (c == '\'' || c == '’') 524 return CharClass.Boundary; 525 else 526 return CharClass.NonWord; 527 } 528 }; 529 } 530 531 /** 532 * Returns a regex pattern that will match against the beginning of 533 * Rnw lines, right up to the index where options begin. 534 */ 535 public Pattern getRnwStartPatternBegin() 536 { 537 return null; 538 } 539 540 public Pattern getRnwStartPatternEnd() 541 { 542 return null; 543 } 544 545 private final EditorLanguage editorLanguage_; 546 private final boolean wordWrap_; 547 private final boolean canSourceOnSave_; 548 private final boolean canExecuteCode_; 549 private final boolean canExecuteAllCode_; 550 private final boolean canExecuteToCurrentLine_; 551 private final boolean canPreviewHTML_; 552 private final boolean canKnitToHTML_; 553 private final boolean canCompilePDF_; 554 private final boolean canExecuteChunks_; 555 private final boolean canAutoIndent_; 556 private final boolean canCheckSpelling_; 557 private final boolean canShowScopeTree_; 558 private final boolean canPreviewFromR_; 559 private final String defaultExtension_; 560 561 protected static Pattern reTextType_ = Pattern.create("\\btext\\b"); 562 protected static Pattern reStringType_ = Pattern.create("\\bstring\\b"); 563 protected static Pattern reHeaderType_ = Pattern.create("\\bheading\\b"); 564 protected static Pattern reNospellType_ = Pattern.create("\\bnospell\\b"); 565 protected static Pattern reCommentType_ = Pattern.create("\\bcomment\\b"); 566 protected static Pattern reKeywordType_ = Pattern.create("\\bkeyword\\b"); 567 protected static Pattern reIdentifierType_ = Pattern.create("\\bidentifier\\b"); 568 } 569