| 30 | 30 | compile 'com.googlecode.juniversalchardet:juniversalchardet:1.0.3' |
| 31 | 31 | compile 'org.apache.commons:commons-configuration2:2.1' |
| 32 | compile files('libs/renjin-script-engine-0.8.2320-jar-with-dependencies.jar') | |
| 32 | compile files('libs/renjin-script-engine-0.8.2324-jar-with-dependencies.jar') | |
| 33 | 33 | } |
| 34 | 34 | |
| 35 | version = '1.2.5' | |
| 35 | version = '1.2.6' | |
| 36 | 36 | applicationName = 'scrivenvar' |
| 37 | 37 | mainClassName = 'com.scrivenvar.Main' |
| 519 | 519 | // This will allow dynamic filters to be added and removed just by |
| 520 | 520 | // updating the properties file. |
| 521 | list.add( createExtensionFilter( MARKDOWN ) ); | |
| 521 | list.add( createExtensionFilter( SOURCE ) ); | |
| 522 | 522 | list.add( createExtensionFilter( DEFINITION ) ); |
| 523 | 523 | list.add( createExtensionFilter( XML ) ); |
| 40 | 40 | RMARKDOWN( "rmarkdown" ), |
| 41 | 41 | RXML( "rxml" ), |
| 42 | MARKDOWN( "markdown" ), | |
| 42 | SOURCE( "source" ), | |
| 43 | 43 | DEFINITION( "definition" ), |
| 44 | 44 | XML( "xml" ), |
| 36 | 36 | import java.util.List; |
| 37 | 37 | import javafx.collections.ObservableList; |
| 38 | import javafx.event.EventHandler; | |
| 38 | 39 | import javafx.scene.Node; |
| 39 | 40 | import javafx.scene.control.MultipleSelectionModel; |
| 40 | 41 | import javafx.scene.control.SelectionMode; |
| 41 | 42 | import javafx.scene.control.TreeItem; |
| 42 | 43 | import javafx.scene.control.TreeView; |
| 44 | import javafx.scene.input.MouseButton; | |
| 45 | import javafx.scene.input.MouseEvent; | |
| 43 | 46 | |
| 44 | 47 | /** |
| ... | ||
| 65 | 68 | setTreeView( root ); |
| 66 | 69 | initTreeView(); |
| 70 | } | |
| 71 | ||
| 72 | /** | |
| 73 | * Allows observers to receive double-click events on the tree view. | |
| 74 | * | |
| 75 | * @param handler The handler that | |
| 76 | */ | |
| 77 | public void addBranchSelectedListener( | |
| 78 | final EventHandler<? super MouseEvent> handler ) { | |
| 79 | ||
| 80 | getTreeView().addEventHandler( MouseEvent.ANY, event -> { | |
| 81 | if( event.getButton().equals( MouseButton.PRIMARY ) && event.getClickCount() == 2 ) { | |
| 82 | if( event.getEventType().equals( MouseEvent.MOUSE_CLICKED ) ) { | |
| 83 | handler.handle( event ); | |
| 84 | } | |
| 85 | ||
| 86 | event.consume(); | |
| 87 | } | |
| 88 | } ); | |
| 67 | 89 | } |
| 68 | 90 | |
| 47 | 47 | import javafx.scene.control.IndexRange; |
| 48 | 48 | import javafx.scene.control.TreeItem; |
| 49 | import javafx.scene.control.TreeView; | |
| 49 | 50 | import javafx.scene.input.InputEvent; |
| 50 | 51 | import javafx.scene.input.KeyCode; |
| ... | ||
| 57 | 58 | import static javafx.scene.input.KeyCombination.SHIFT_DOWN; |
| 58 | 59 | import javafx.scene.input.KeyEvent; |
| 60 | import javafx.scene.input.MouseEvent; | |
| 59 | 61 | import org.fxmisc.richtext.StyledTextArea; |
| 60 | 62 | import org.fxmisc.wellbehaved.event.EventPattern; |
| ... | ||
| 91 | 93 | private int initialCaretPosition; |
| 92 | 94 | |
| 95 | /** | |
| 96 | * Empty constructor. | |
| 97 | */ | |
| 93 | 98 | private VariableNameInjector() { |
| 94 | 99 | } |
| 95 | 100 | |
| 96 | 101 | public static void listen( final FileEditorTab tab, final DefinitionPane pane ) { |
| 97 | VariableNameInjector vni = new VariableNameInjector(); | |
| 102 | final VariableNameInjector vni = new VariableNameInjector(); | |
| 98 | 103 | |
| 99 | 104 | vni.setFileEditorTab( tab ); |
| 100 | 105 | vni.setDefinitionPane( pane ); |
| 101 | ||
| 106 | vni.initBranchSelectedListener(); | |
| 102 | 107 | vni.initKeyboardEventListeners(); |
| 108 | } | |
| 109 | ||
| 110 | /** | |
| 111 | * Traps double-click events on the definition pane. | |
| 112 | */ | |
| 113 | private void initBranchSelectedListener() { | |
| 114 | getDefinitionPane().addBranchSelectedListener( (final MouseEvent event) -> { | |
| 115 | final Object source = event.getSource(); | |
| 116 | ||
| 117 | if( source instanceof TreeView ) { | |
| 118 | final TreeView tree = (TreeView)source; | |
| 119 | final TreeItem item = (TreeItem)tree.getSelectionModel().getSelectedItem(); | |
| 120 | ||
| 121 | if( item instanceof VariableTreeItem ) { | |
| 122 | final VariableTreeItem var = (VariableTreeItem)item; | |
| 123 | final String text = decorate( var.toPath() ); | |
| 124 | ||
| 125 | replaceSelection( text ); | |
| 126 | } | |
| 127 | } | |
| 128 | } ); | |
| 103 | 129 | } |
| 104 | 130 | |
| ... | ||
| 161 | 187 | |
| 162 | 188 | // Decorate the variable upon exiting vMode. |
| 163 | decorateVariable(); | |
| 189 | decorate(); | |
| 164 | 190 | } |
| 165 | 191 | break; |
| ... | ||
| 274 | 300 | if( leaf != null ) { |
| 275 | 301 | replaceText( boundaries[ 0 ], boundaries[ 1 ], leaf.toPath() ); |
| 276 | decorateVariable(); | |
| 302 | decorate(); | |
| 277 | 303 | expand( leaf ); |
| 278 | 304 | } |
| 279 | 305 | } |
| 280 | 306 | |
| 281 | 307 | /** |
| 282 | 308 | * Called when autocomplete finishes on a valid leaf or when the user presses |
| 283 | 309 | * Enter to finish manual autocomplete. |
| 284 | 310 | */ |
| 285 | private void decorateVariable() { | |
| 311 | private void decorate() { | |
| 286 | 312 | // A little bit of duplication... |
| 287 | 313 | final String paragraph = getCaretParagraph(); |
| 288 | 314 | final int[] boundaries = getWordBoundaries( paragraph ); |
| 289 | 315 | final String old = paragraph.substring( boundaries[ 0 ], boundaries[ 1 ] ); |
| 290 | 316 | |
| 291 | final String newVariable = getVariableDecorator().decorate( old ); | |
| 317 | final String newVariable = decorate( old ); | |
| 292 | 318 | |
| 293 | 319 | final int posEnded = getCurrentCaretPosition(); |
| 294 | 320 | final int posBegan = posEnded - old.length(); |
| 295 | 321 | |
| 296 | 322 | getEditor().replaceText( posBegan, posEnded, newVariable ); |
| 323 | } | |
| 324 | ||
| 325 | /** | |
| 326 | * Called when user double-clicks on a tree view item. | |
| 327 | * | |
| 328 | * @param variable The variable to decorate. | |
| 329 | */ | |
| 330 | private String decorate( final String variable ) { | |
| 331 | return getVariableDecorator().decorate( variable ); | |
| 332 | } | |
| 333 | ||
| 334 | /** | |
| 335 | * Inserts the given string at the current caret position, or replaces | |
| 336 | * selected text (if any). | |
| 337 | * | |
| 338 | * @param s The string to inject. | |
| 339 | */ | |
| 340 | private void replaceSelection( final String s ) { | |
| 341 | getEditor().replaceSelection( s ); | |
| 297 | 342 | } |
| 298 | 343 | |
| 41 | 41 | public class HTMLPreviewProcessor extends AbstractProcessor<String> { |
| 42 | 42 | |
| 43 | private HTMLPreviewPane htmlPreviewPane; | |
| 43 | // There is only one preview panel. | |
| 44 | private static HTMLPreviewPane htmlPreviewPane; | |
| 44 | 45 | |
| 45 | 46 | /** |
| 53 | 53 | public final class InlineRProcessor extends DefaultVariableProcessor { |
| 54 | 54 | |
| 55 | private final Notifier notifier = Services.load( Notifier.class ); | |
| 56 | private final Options options = Services.load( Options.class ); | |
| 55 | private static final Notifier NOTIFIER = Services.load( Notifier.class ); | |
| 56 | private static final Options OPTIONS = Services.load( Options.class ); | |
| 57 | 57 | |
| 58 | private ScriptEngine engine; | |
| 58 | // Only one editor is open at a time. | |
| 59 | private static final ScriptEngine ENGINE = | |
| 60 | (new ScriptEngineManager()).getEngineByName( "Renjin" ); | |
| 59 | 61 | |
| 60 | 62 | /** |
| ... | ||
| 174 | 176 | |
| 175 | 177 | private synchronized ScriptEngine getScriptEngine() { |
| 176 | if( this.engine == null ) { | |
| 177 | this.engine = (new ScriptEngineManager()).getEngineByName( "Renjin" ); | |
| 178 | } | |
| 179 | ||
| 180 | return this.engine; | |
| 178 | return ENGINE; | |
| 181 | 179 | } |
| 182 | 180 | |
| 183 | 181 | private Notifier getNotifier() { |
| 184 | return this.notifier; | |
| 182 | return NOTIFIER; | |
| 185 | 183 | } |
| 186 | 184 | |
| 187 | 185 | private Options getOptions() { |
| 188 | return this.options; | |
| 186 | return OPTIONS; | |
| 189 | 187 | } |
| 190 | 188 | |
| 36 | 36 | import com.vladsch.flexmark.superscript.SuperscriptExtension; |
| 37 | 37 | import java.util.ArrayList; |
| 38 | import java.util.List; | |
| 39 | ||
| 38 | import java.util.Collection; | |
| 40 | 39 | |
| 41 | 40 | /** |
| 42 | 41 | * Responsible for parsing a Markdown document and rendering it as HTML. |
| 43 | 42 | * |
| 44 | 43 | * @author White Magic Software, Ltd. |
| 45 | 44 | */ |
| 46 | 45 | public class MarkdownProcessor extends AbstractProcessor<String> { |
| 47 | 46 | |
| 48 | private List<Extension> extensions; | |
| 47 | private final static HtmlRenderer RENDERER; | |
| 48 | private final static Parser PARSER; | |
| 49 | ||
| 50 | static { | |
| 51 | final Collection<Extension> extensions = new ArrayList<>(); | |
| 52 | extensions.add( TablesExtension.create() ); | |
| 53 | extensions.add( SuperscriptExtension.create() ); | |
| 54 | extensions.add( StrikethroughSubscriptExtension.create() ); | |
| 55 | ||
| 56 | RENDERER = HtmlRenderer.builder().extensions( extensions ).build(); | |
| 57 | PARSER = Parser.builder().extensions( extensions ).build(); | |
| 58 | } | |
| 49 | 59 | |
| 50 | 60 | /** |
| ... | ||
| 91 | 101 | */ |
| 92 | 102 | private Node parse( final String markdown ) { |
| 93 | return createParser().parse( markdown ); | |
| 103 | return getParser().parse( markdown ); | |
| 94 | 104 | } |
| 95 | 105 | |
| ... | ||
| 102 | 112 | */ |
| 103 | 113 | private String toHtml( final String markdown ) { |
| 104 | return createRenderer().render( parse( markdown ) ); | |
| 105 | } | |
| 106 | ||
| 107 | /** | |
| 108 | * Returns the list of extensions to use when parsing and rendering Markdown | |
| 109 | * into HTML. | |
| 110 | * | |
| 111 | * @return A non-null list of Markdown extensions. | |
| 112 | */ | |
| 113 | private synchronized List<Extension> getExtensions() { | |
| 114 | if( this.extensions == null ) { | |
| 115 | this.extensions = createExtensions(); | |
| 116 | } | |
| 117 | ||
| 118 | return this.extensions; | |
| 119 | } | |
| 120 | ||
| 121 | /** | |
| 122 | * Creates a list that includes a TablesExtension. Subclasses may override | |
| 123 | * this method to insert more extensions, or remove the table extension. | |
| 124 | * | |
| 125 | * @return A list with an extension for parsing and rendering tables. | |
| 126 | */ | |
| 127 | protected List<Extension> createExtensions() { | |
| 128 | final List<Extension> result = new ArrayList<>(); | |
| 129 | result.add( TablesExtension.create() ); | |
| 130 | result.add( SuperscriptExtension.create() ); | |
| 131 | result.add( StrikethroughSubscriptExtension.create() ); | |
| 132 | return result; | |
| 114 | return getRenderer().render( parse( markdown ) ); | |
| 133 | 115 | } |
| 134 | 116 | |
| 135 | 117 | /** |
| 136 | 118 | * Creates the Markdown document processor. |
| 137 | 119 | * |
| 138 | 120 | * @return A Parser that can build an abstract syntax tree. |
| 139 | 121 | */ |
| 140 | private Parser createParser() { | |
| 141 | return Parser.builder().extensions( getExtensions() ).build(); | |
| 122 | private Parser getParser() { | |
| 123 | return PARSER; | |
| 142 | 124 | } |
| 143 | 125 | |
| 144 | /** | |
| 145 | * Creates the HTML document renderer. | |
| 146 | * | |
| 147 | * @return A renderer that can convert a Markdown AST to HTML. | |
| 148 | */ | |
| 149 | private HtmlRenderer createRenderer() { | |
| 150 | return HtmlRenderer.builder().extensions( getExtensions() ).build(); | |
| 126 | private HtmlRenderer getRenderer() { | |
| 127 | return RENDERER; | |
| 151 | 128 | } |
| 152 | 129 | } |
| 79 | 79 | break; |
| 80 | 80 | |
| 81 | case MARKDOWN: | |
| 81 | case SOURCE: | |
| 82 | 82 | processor = createMarkdownProcessor( tab ); |
| 83 | 83 | break; |
| 38 | 38 | * @author White Magic Software, Ltd. |
| 39 | 39 | */ |
| 40 | class RVariableProcessor extends DefaultVariableProcessor { | |
| 40 | public class RVariableProcessor extends DefaultVariableProcessor { | |
| 41 | 41 | |
| 42 | 42 | public RVariableProcessor( |
| 42 | 42 | public class XMLCaretInsertionProcessor extends CaretInsertionProcessor { |
| 43 | 43 | |
| 44 | private VTDGen parser; | |
| 44 | private final static VTDGen PARSER = new VTDGen(); | |
| 45 | 45 | |
| 46 | 46 | /** |
| ... | ||
| 139 | 139 | |
| 140 | 140 | private synchronized VTDGen getParser() { |
| 141 | if( this.parser == null ) { | |
| 142 | this.parser = createParser(); | |
| 143 | } | |
| 144 | ||
| 145 | return this.parser; | |
| 146 | } | |
| 147 | ||
| 148 | /** | |
| 149 | * Creates a high-performance XML document parser. | |
| 150 | * | |
| 151 | * @return A new XML parser. | |
| 152 | */ | |
| 153 | protected VTDGen createParser() { | |
| 154 | return new VTDGen(); | |
| 141 | return PARSER; | |
| 155 | 142 | } |
| 156 | 143 | } |
| 131 | 131 | Dialog.file.choose.save.title=Save File |
| 132 | 132 | |
| 133 | Dialog.file.choose.filter.title.markdown=Markdown Files | |
| 133 | Dialog.file.choose.filter.title.source=Source Files | |
| 134 | 134 | Dialog.file.choose.filter.title.definition=Definition Files |
| 135 | 135 | Dialog.file.choose.filter.title.xml=XML Files |
| 68 | 68 | file.ext.rmarkdown=*.Rmd |
| 69 | 69 | file.ext.rxml=*.Rxml |
| 70 | file.ext.markdown=*.md,*.markdown,*.mkdown,*.mdown,*.mkdn,*.mkd,*.mdwn,*.mdtxt,*.mdtext,*.text,*.txt,${file.ext.rmarkdown} | |
| 70 | file.ext.source=*.md,*.markdown,*.mkdown,*.mdown,*.mkdn,*.mkd,*.mdwn,*.mdtxt,*.mdtext,*.text,*.txt,${file.ext.rmarkdown},${file.ext.rxml} | |
| 71 | 71 | file.ext.definition=${definition.file.ext.yaml} |
| 72 | 72 | file.ext.xml=*.xml,${file.ext.rxml} |