/* * ChunkContextUi.java * * Copyright (C) 2009-16 by RStudio, Inc. * * Unless you have received this program directly from RStudio pursuant * to the terms of a commercial license agreement with RStudio, then * this program is licensed to you under the terms of version 3 of the * GNU Affero General Public License. This program is distributed WITHOUT * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. * */ package org.rstudio.studio.client.workbench.views.source.editors.text.rmd; import java.util.Map; import org.rstudio.core.client.StringUtil; import org.rstudio.core.client.widget.Operation; import org.rstudio.studio.client.RStudioGinjector; import org.rstudio.studio.client.common.GlobalDisplay; import org.rstudio.studio.client.common.r.knitr.RMarkdownChunkHeaderParser; import org.rstudio.studio.client.workbench.views.console.shell.assist.PopupPositioner; import org.rstudio.studio.client.workbench.views.source.editors.text.DocDisplay; import org.rstudio.studio.client.workbench.views.source.editors.text.PinnedLineWidget; import org.rstudio.studio.client.workbench.views.source.editors.text.Scope; import org.rstudio.studio.client.workbench.views.source.editors.text.TextEditingTarget; import org.rstudio.studio.client.workbench.views.source.editors.text.TextEditingTargetScopeHelper; import org.rstudio.studio.client.workbench.views.source.editors.text.ace.LineWidget; import org.rstudio.studio.client.workbench.views.source.editors.text.ace.Position; import org.rstudio.studio.client.workbench.views.source.editors.text.ace.Range; import org.rstudio.studio.client.workbench.views.source.editors.text.rmd.display.ChunkOptionsPopupPanel; import org.rstudio.studio.client.workbench.views.source.editors.text.rmd.display.CustomEngineChunkOptionsPopupPanel; import org.rstudio.studio.client.workbench.views.source.editors.text.rmd.display.DefaultChunkOptionsPopupPanel; import org.rstudio.studio.client.workbench.views.source.editors.text.rmd.display.SetupChunkOptionsPopupPanel; import org.rstudio.studio.client.workbench.views.source.editors.text.rmd.events.InterruptChunkEvent; import com.google.gwt.core.client.JsArrayString; public class ChunkContextUi implements ChunkContextToolbar.Host { public ChunkContextUi(TextEditingTarget target, int renderPass, boolean dark, Scope chunk, PinnedLineWidget.Host lineWidgetHost) { target_ = target; chunk_ = chunk; int preambleRow = chunk.getPreamble().getRow(); preambleRow_ = preambleRow; isSetup_ = isSetupChunk(preambleRow); host_ = lineWidgetHost; dark_ = dark; renderPass_ = renderPass; engine_ = getEngine(preambleRow); createToolbar(preambleRow); } // Public methods ---------------------------------------------------------- public int getPreambleRow() { return lineWidget_.getRow(); } public void setState(int state) { toolbar_.setState(state); } public LineWidget getLineWidget() { return lineWidget_.getLineWidget(); } public void detach() { lineWidget_.detach(); } public void syncToChunk() { int row = lineWidget_.getRow(); boolean isSetup = isSetupChunk(row); if (isSetup_ != isSetup) { isSetup_ = isSetup; toolbar_.setRunPrevious(!isSetup_); } String engine = getEngine(row); if (engine != engine_) { engine_ = engine; toolbar_.setEngine(engine); } } public void setRenderPass(int pass) { renderPass_ = pass; } public int getRenderPass() { return renderPass_; } // ChunkContextToolbar.Host implementation --------------------------------- @Override public void runPreviousChunks() { target_.executeChunks(chunkPosition(), TextEditingTargetScopeHelper.PREVIOUS_CHUNKS); target_.focus(); } @Override public void runChunk() { target_.executeChunk(chunkPosition()); target_.focus(); } @Override public void showOptions(int x, int y) { ChunkOptionsPopupPanel panel = createPopupPanel(); panel.init(target_.getDocDisplay(), chunkPosition()); panel.show(); panel.focus(); PopupPositioner.setPopupPosition(panel, x, y, 10); } @Override public void interruptChunk() { target_.fireEvent(new InterruptChunkEvent(preambleRow_)); } @Override public void dequeueChunk() { RStudioGinjector.INSTANCE.getGlobalDisplay().showYesNoMessage( GlobalDisplay.MSG_QUESTION, "Chunk Pending Execution", "The code in this chunk is scheduled to run later, when other " + "chunks have finished executing.", false, // include cancel null, // yes operation, new Operation() { @Override public void execute() { target_.dequeueChunk(lineWidget_.getRow()); } }, null, // cancel operation "OK", "Don't Run", true); } @Override public void switchChunk(String chunkType) { if (chunk_ != null) { DocDisplay docDisplay = target_.getDocDisplay(); Position start = chunk_.getPreamble(); Position end = chunk_.getEnd(); String chunkText = docDisplay.getTextForRange(Range.fromPoints(start, end)); JsArrayString chunkLines = StringUtil.split(chunkText, "\n"); if (chunkLines.length() > 0) { String firstLine = chunkLines.get(0); Position linedEnd = Position.create(start.getRow(),firstLine.length()); String newFirstLine = firstLine.replaceFirst("[, ]*engine='[a-zA-Z]+'", ""); newFirstLine = newFirstLine.replaceFirst("{[a-zA-Z]+", "{" + chunkType); docDisplay.replaceRange(Range.fromPoints(start, linedEnd), newFirstLine); target_.getNotebook().clearChunkOutput(chunk_); } } } // Private methods --------------------------------------------------------- private Position chunkPosition() { return Position.create(lineWidget_.getRow(), 0); } private boolean isSetupChunk(int row) { String line = target_.getDocDisplay().getLine(row); return line.contains("r setup"); } private void createToolbar(int row) { toolbar_ = new ChunkContextToolbar(this, dark_, !isSetup_, engine_); toolbar_.setHeight("0px"); lineWidget_ = new PinnedLineWidget( ChunkContextToolbar.LINE_WIDGET_TYPE, target_.getDocDisplay(), toolbar_, row, null, host_); } private String getEngine(int row) { String line = target_.getDocDisplay().getLine(row); Map<String, String> options = RMarkdownChunkHeaderParser.parse(line); String engine = StringUtil.stringValue(options.get("engine")); return engine; } private ChunkOptionsPopupPanel createPopupPanel() { int row = lineWidget_.getRow(); if (isSetupChunk(row)) return new SetupChunkOptionsPopupPanel(); String engine = getEngine(row); if (!engine.toLowerCase().equals("r")) return new CustomEngineChunkOptionsPopupPanel(); return new DefaultChunkOptionsPopupPanel(); } private final TextEditingTarget target_; private final PinnedLineWidget.Host host_; private final int preambleRow_; private final boolean dark_; private final Scope chunk_; private ChunkContextToolbar toolbar_; private PinnedLineWidget lineWidget_; private int renderPass_; private boolean isSetup_; private String engine_; }