/* * SetupChunkOptionsPopupPanel.java * * Copyright (C) 2009-12 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.display; import com.google.gwt.core.client.JsArrayString; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.ui.TextBox; import com.google.inject.Inject; import org.rstudio.core.client.Debug; import org.rstudio.core.client.StringUtil; import org.rstudio.core.client.js.JsObject; import org.rstudio.core.client.js.JsUtil; import org.rstudio.core.client.widget.TriStateCheckBox; import org.rstudio.studio.client.RStudioGinjector; import org.rstudio.studio.client.common.codetools.CodeToolsServerOperations; import org.rstudio.studio.client.server.ServerError; import org.rstudio.studio.client.server.ServerRequestCallback; 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.ace.Token; import org.rstudio.studio.client.workbench.views.source.editors.text.ace.TokenIterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; public class SetupChunkOptionsPopupPanel extends ChunkOptionsPopupPanel { @Inject public void initialize(CodeToolsServerOperations server) { server_ = server; } public SetupChunkOptionsPopupPanel() { super(false); RStudioGinjector.INSTANCE.injectMembers(this); figureDimensionsPanel_.setVisible(false); useCustomFigureCheckbox_.setVisible(false); revertButton_.setVisible(false); enginePanel_.setVisible(false); setHeader("Default Chunk Options", true); } String indented(String text) { // Ace will automatically translate tabs into appropriate // indent on insertion return "\t" + text; } private int findEndOfChunk() { int row = position_.getRow(); int max = display_.getRowCount(); for (int i = row; i < max; i++) { String line = display_.getLine(i); if (line.equals("```")) return i; } return -1; } private String trueString(boolean value) { return value ? "TRUE" : "FALSE"; } private void addParam(Map<String, String> options, String name) { if (has(name)) options.put(name, get(name)); } private void addCheckboxParam(Map<String, String> options, TriStateCheckBox checkBox, String name) { if (!checkBox.isIndeterminate()) options.put(name, trueString(checkBox.isChecked())); } private void addTextBoxParam(Map<String, String> options, TextBox textBox, String name) { String value = textBox.getValue(); if (!StringUtil.isNullOrEmpty(value)) options.put(name, value); } private Position findKnitrPrefix(TokenIterator iterator) { Token token = iterator.stepBackward(); if (!(token.valueEquals("::") || token.valueEquals(":::"))) return null; token = iterator.stepBackward(); return token.valueEquals("knitr") ? iterator.getCurrentTokenPosition() : null; } private Range findOptsChunk() { TokenIterator iterator = display_.createTokenIterator(position_); while (true) { Token token = iterator.stepForward(); if (token == null) break; if (token.hasType("codeend")) break; Position startPos = iterator.getCurrentTokenPosition(); if (!token.getValue().equals("opts_chunk")) continue; Position knitrPrefixPos = findKnitrPrefix(iterator.clone()); if (knitrPrefixPos != null) startPos = knitrPrefixPos; token = iterator.stepForward(); if (!token.getValue().equals("$")) continue; token = iterator.stepForward(); if (!token.getValue().equals("set")) continue; token = iterator.stepForward(); if (!token.getValue().equals("(")) continue; if (!iterator.fwdToMatchingToken()) continue; token = iterator.stepForward(); Position endPos = iterator.getCurrentTokenPosition(); return Range.fromPoints(startPos, endPos); } return null; } private Range syncSelection() { Range range = findOptsChunk(); if (range == null) { display_.clearSelection(); display_.setCursorPosition( Position.create(position_.getRow() + 1, 0)); } else { display_.setSelectionRange(range); } return display_.getSelectionRange(); } private String joinOptions(Map<String, String> options) { return StringUtil.collapse(options, " = ", ",\n\t"); } private String getChunkText() { int chunkStart = position_.getRow(); int chunkEnd = findEndOfChunk(); JsArrayString chunkText = display_.getLines(chunkStart + 1, chunkEnd - 1); return chunkText.join("\n"); } @Override protected void initOptions(final Command afterInit) { String chunkText = getChunkText(); server_.extractChunkOptions( chunkText, new ServerRequestCallback<JsObject>() { @Override public void onError(ServerError error) { Debug.logError(error); } @Override public void onResponseReceived(JsObject object) { JsArrayString keys = object.keys(); for (String key : JsUtil.asIterable(keys)) chunkOptions_.put(key, object.getAsString(key)); afterInit.execute(); } }); } @Override protected void synchronize() { syncSelection(); Map<String, String> options = new LinkedHashMap<String, String>(); Set<String> keys = chunkOptions_.keySet(); for (String key : keys) options.put(key, chunkOptions_.get(key)); addParam(options, "echo"); addParam(options, "eval"); addParam(options, "include"); addCheckboxParam(options, showMessagesInOutputCb_, "message"); addCheckboxParam(options, showWarningsInOutputCb_, "warning"); addTextBoxParam(options, figHeightBox_, "fig.height"); addTextBoxParam(options, figWidthBox_, "fig.width"); if (options.isEmpty()) { display_.insertCode(""); return; } // For 2 or fewer options, display all on one line if (options.size() <= 2) { String joined = StringUtil.collapse(options, " = ", ", "); String code = "knitr::opts_chunk$set(" + joined + ")\n"; display_.insertCode(code); return; } Map<String, String> sorted = sortedOptions(options); String code = "knitr::opts_chunk$set(\n\t" + joinOptions(sorted) + "\n)\n"; display_.insertCode(code); } @Override protected void revert() { } private CodeToolsServerOperations server_; }