<commit_before>/*
 * SessionUserSettings.cpp
 *
 * 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.
 *
 */

#include <session/SessionUserSettings.hpp>

#include <iostream>

#include <boost/foreach.hpp>

#include <core/Error.hpp>
#include <core/FilePath.hpp>
#include <core/system/System.hpp>

#include <core/r_util/RSessionContext.hpp>

#include <session/SessionModuleContext.hpp>
#include <session/SessionOptions.hpp>
#include "modules/SessionErrors.hpp"
#include "modules/SessionShinyViewer.hpp"

#include <r/RExec.hpp>
#include <r/ROptions.hpp>
#include <r/session/RSession.hpp>
#include <r/session/RConsoleHistory.hpp>

using namespace rstudio::core ;

namespace rstudio {
namespace session {  
   
#define kAgreementPrefix "agreement."
   
namespace {
const char * const kContextId ="contextIdentifier";
const char * const kAgreementHash = kAgreementPrefix "agreedToHash";
const char * const kAutoCreatedProfile = "autoCreatedProfile";
const char * const kUiPrefs = "uiPrefs";
const char * const kRProfileOnResume = "rprofileOnResume";
const char * const kSaveAction = "saveAction";
const char * const kLoadRData = "loadRData";
const char * const kInitialWorkingDirectory = "initialWorkingDirectory";
const char * const kCRANMirrorName = "cranMirrorName";
const char * const kCRANMirrorHost = "cranMirrorHost";
const char * const kCRANMirrorUrl = "cranMirrorUrl";
const char * const kCRANMirrorCountry = "cranMirrorCountry";
const char * const kBioconductorMirrorName = "bioconductorMirrorName";
const char * const kBioconductorMirrorUrl = "bioconductorMirrorUrl";
const char * const kAlwaysSaveHistory = "alwaysSaveHistory";
const char * const kRemoveHistoryDuplicates = "removeHistoryDuplicates";

void setCRANReposOption(const std::string& url)
{
   if (!url.empty())
   {
      Error error = r::exec::RFunction(".rs.setCRANReposFromSettings",
                                       url).call();
      if (error)
         LOG_ERROR(error);
   }
}

void setBioconductorReposOption(const std::string& mirror)
{
   if (!mirror.empty())
   {
      Error error = r::options::setOption("BioC_mirror", mirror);
      if (error)
         LOG_ERROR(error);
   }
}



} // anonymous namespace
   
UserSettings& userSettings()
{
   static UserSettings instance ;
   return instance;
}
   
Error UserSettings::initialize()
{
   // calculate settings file path
   FilePath settingsDir = module_context::registerMonitoredUserScratchDir(
              "user-settings",
              boost::bind(&UserSettings::onSettingsFileChanged, this, _1));
   settingsFilePath_ = settingsDir.complete("user-settings");

   // if it doesn't exist see if we can migrate an old user settings
   if (!settingsFilePath_.exists())
   {
      FilePath oldSettingsPath =
            module_context::userScratchPath().complete("user-settings");
      if (oldSettingsPath.exists())
         oldSettingsPath.move(settingsFilePath_);
   }

   // read the settings
   Error error = settings_.initialize(settingsFilePath_);
   if (error)
      return error;

   // make sure we have a context id
   if (contextId().empty())
      setContextId(core::system::generateShortenedUuid());

   return Success();
}

void UserSettings::onSettingsFileChanged(
                     const core::system::FileChangeEvent& changeEvent)
{
   // ensure this is for our target file
   if (settingsFilePath_.absolutePath() !=
       changeEvent.fileInfo().absolutePath())
   {
      return;
   }

   // re-read the settings from disk
   Error error = settings_.initialize(settingsFilePath_);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   // update prefs cache
   updatePrefsCache(uiPrefs());

   // set underlying R repos options
   std::string cranMirrorURL = cranMirror().url;
   if (!cranMirrorURL.empty())
      setCRANReposOption(cranMirrorURL);
   std::string bioconductorMirrorURL = settings_.get(kBioconductorMirrorUrl);
   if (!bioconductorMirrorURL.empty())
      setBioconductorReposOption(bioconductorMirrorURL);

   // update remove dups in underlying R session
   using namespace rstudio::r::session;
   consoleHistory().setRemoveDuplicates(removeHistoryDuplicates());

   // fire event so others can react appropriately
   onChanged();
}


std::string UserSettings::contextId() const
{
   return settings_.get(kContextId);
}

std::string UserSettings::oldContextId() const
{
   return settings_.get("contextId");
}

void UserSettings::setContextId(const std::string& contextId)
{
   settings_.set(kContextId, contextId);
}

std::string UserSettings::agreementHash() const
{
   return settings_.get(kAgreementHash, "");
}
   
void UserSettings::setAgreementHash(const std::string& hash)
{
   settings_.set(kAgreementHash, hash);
}

  
bool UserSettings::autoCreatedProfile() const
{
   return settings_.getBool(kAutoCreatedProfile, false);
}
   
void UserSettings::setAutoCreatedProfile(bool autoCreated)
{
   settings_.set(kAutoCreatedProfile, autoCreated);
}

core::json::Object UserSettings::uiPrefs() const
{
   std::string value = settings_.get(kUiPrefs, "{}");
   json::Value jsonValue;
   bool success = core::json::parse(value, &jsonValue);
   if (success)
      return jsonValue.get_obj();
   else
      return json::Object();
}

void UserSettings::setUiPrefs(const core::json::Object& prefsObject)
{
   std::ostringstream output;
   json::writeFormatted(prefsObject, output);
   settings_.set(kUiPrefs, output.str());

   updatePrefsCache(prefsObject);
}

namespace {

template <typename T>
T readPref(const json::Object& prefs,
           const std::string& name,
           const T& defaultValue)
{
   T value = defaultValue;
   Error error = json::readObject(prefs,
                                  name,
                                  defaultValue,
                                  &value);
   if (error)
   {
      value = defaultValue;
      error.addProperty("pref", name);
      LOG_ERROR(error);
   }

   return value;
}

} // anonymous namespace

void UserSettings::updatePrefsCache(const json::Object& prefs) const
{ 
   bool useSpacesForTab = readPref<bool>(prefs, "use_spaces_for_tab", true);
   pUseSpacesForTab_.reset(new bool(useSpacesForTab));

   int numSpacesForTab = readPref<int>(prefs, "num_spaces_for_tab", 2);
   pNumSpacesForTab_.reset(new int(numSpacesForTab));

   bool autoAppendNewline = readPref<bool>(prefs, "auto_append_newline", false);
   pAutoAppendNewline_.reset(new bool(autoAppendNewline));

   bool stripTrailingWhitespace = readPref<bool>(prefs, "strip_trailing_whitespace", false);
   pStripTrailingWhitespace_.reset(new bool(stripTrailingWhitespace));

   std::string enc = readPref<std::string>(prefs, "default_encoding", "");
   pDefaultEncoding_.reset(new std::string(enc));

   std::string sweave = readPref<std::string>(prefs, "default_sweave_engine", "Sweave");
   pDefaultSweaveEngine_.reset(new std::string(sweave));

   std::string latex = readPref<std::string>(prefs, "default_latex_program", "pdfLaTeX");
   pDefaultLatexProgram_.reset(new std::string(latex));

   bool alwaysEnableRnwConcordance = readPref<bool>(prefs, "always_enable_concordance", true);
   pAlwaysEnableRnwConcordance_.reset(new bool(alwaysEnableRnwConcordance));

   std::string spellingLanguage = readPref<std::string>(prefs, "spelling_dictionary_language", "en_US");
   pSpellingLanguage_.reset(new std::string(spellingLanguage));

   json::Array spellingCustomDicts = readPref<core::json::Array>(prefs, "spelling_custom_dictionaries", core::json::Array());
   pSpellingCustomDicts_.reset(new json::Array(spellingCustomDicts));

   bool handleErrorsInUserCodeOnly = readPref<bool>(prefs, "handle_errors_in_user_code_only", true);
   pHandleErrorsInUserCodeOnly_.reset(new bool(handleErrorsInUserCodeOnly));

   int shinyViewerType = readPref<int>(prefs, "shiny_viewer_type", modules::shiny_viewer::SHINY_VIEWER_WINDOW);
   pShinyViewerType_.reset(new int(shinyViewerType));
   
   bool enableStyleDiagnostics = readPref<bool>(prefs, "enable_style_diagnostics", false);
   pEnableStyleDiagnostics_.reset(new bool(enableStyleDiagnostics));
   
   bool lintRFunctionCalls = readPref<bool>(prefs, "show_diagnostics_in_r_function_calls", false);
   pLintRFunctionCalls_.reset(new bool(lintRFunctionCalls));
   
}


// readers for ui prefs

bool UserSettings::useSpacesForTab() const
{
   return readUiPref<bool>(pUseSpacesForTab_);
}
<commit_msg>default to 'true' here as well<commit_after>/*
 * SessionUserSettings.cpp
 *
 * 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.
 *
 */

#include <session/SessionUserSettings.hpp>

#include <iostream>

#include <boost/foreach.hpp>

#include <core/Error.hpp>
#include <core/FilePath.hpp>
#include <core/system/System.hpp>

#include <core/r_util/RSessionContext.hpp>

#include <session/SessionModuleContext.hpp>
#include <session/SessionOptions.hpp>
#include "modules/SessionErrors.hpp"
#include "modules/SessionShinyViewer.hpp"

#include <r/RExec.hpp>
#include <r/ROptions.hpp>
#include <r/session/RSession.hpp>
#include <r/session/RConsoleHistory.hpp>

using namespace rstudio::core ;

namespace rstudio {
namespace session {  
   
#define kAgreementPrefix "agreement."
   
namespace {
const char * const kContextId ="contextIdentifier";
const char * const kAgreementHash = kAgreementPrefix "agreedToHash";
const char * const kAutoCreatedProfile = "autoCreatedProfile";
const char * const kUiPrefs = "uiPrefs";
const char * const kRProfileOnResume = "rprofileOnResume";
const char * const kSaveAction = "saveAction";
const char * const kLoadRData = "loadRData";
const char * const kInitialWorkingDirectory = "initialWorkingDirectory";
const char * const kCRANMirrorName = "cranMirrorName";
const char * const kCRANMirrorHost = "cranMirrorHost";
const char * const kCRANMirrorUrl = "cranMirrorUrl";
const char * const kCRANMirrorCountry = "cranMirrorCountry";
const char * const kBioconductorMirrorName = "bioconductorMirrorName";
const char * const kBioconductorMirrorUrl = "bioconductorMirrorUrl";
const char * const kAlwaysSaveHistory = "alwaysSaveHistory";
const char * const kRemoveHistoryDuplicates = "removeHistoryDuplicates";

void setCRANReposOption(const std::string& url)
{
   if (!url.empty())
   {
      Error error = r::exec::RFunction(".rs.setCRANReposFromSettings",
                                       url).call();
      if (error)
         LOG_ERROR(error);
   }
}

void setBioconductorReposOption(const std::string& mirror)
{
   if (!mirror.empty())
   {
      Error error = r::options::setOption("BioC_mirror", mirror);
      if (error)
         LOG_ERROR(error);
   }
}



} // anonymous namespace
   
UserSettings& userSettings()
{
   static UserSettings instance ;
   return instance;
}
   
Error UserSettings::initialize()
{
   // calculate settings file path
   FilePath settingsDir = module_context::registerMonitoredUserScratchDir(
              "user-settings",
              boost::bind(&UserSettings::onSettingsFileChanged, this, _1));
   settingsFilePath_ = settingsDir.complete("user-settings");

   // if it doesn't exist see if we can migrate an old user settings
   if (!settingsFilePath_.exists())
   {
      FilePath oldSettingsPath =
            module_context::userScratchPath().complete("user-settings");
      if (oldSettingsPath.exists())
         oldSettingsPath.move(settingsFilePath_);
   }

   // read the settings
   Error error = settings_.initialize(settingsFilePath_);
   if (error)
      return error;

   // make sure we have a context id
   if (contextId().empty())
      setContextId(core::system::generateShortenedUuid());

   return Success();
}

void UserSettings::onSettingsFileChanged(
                     const core::system::FileChangeEvent& changeEvent)
{
   // ensure this is for our target file
   if (settingsFilePath_.absolutePath() !=
       changeEvent.fileInfo().absolutePath())
   {
      return;
   }

   // re-read the settings from disk
   Error error = settings_.initialize(settingsFilePath_);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   // update prefs cache
   updatePrefsCache(uiPrefs());

   // set underlying R repos options
   std::string cranMirrorURL = cranMirror().url;
   if (!cranMirrorURL.empty())
      setCRANReposOption(cranMirrorURL);
   std::string bioconductorMirrorURL = settings_.get(kBioconductorMirrorUrl);
   if (!bioconductorMirrorURL.empty())
      setBioconductorReposOption(bioconductorMirrorURL);

   // update remove dups in underlying R session
   using namespace rstudio::r::session;
   consoleHistory().setRemoveDuplicates(removeHistoryDuplicates());

   // fire event so others can react appropriately
   onChanged();
}


std::string UserSettings::contextId() const
{
   return settings_.get(kContextId);
}

std::string UserSettings::oldContextId() const
{
   return settings_.get("contextId");
}

void UserSettings::setContextId(const std::string& contextId)
{
   settings_.set(kContextId, contextId);
}

std::string UserSettings::agreementHash() const
{
   return settings_.get(kAgreementHash, "");
}
   
void UserSettings::setAgreementHash(const std::string& hash)
{
   settings_.set(kAgreementHash, hash);
}

  
bool UserSettings::autoCreatedProfile() const
{
   return settings_.getBool(kAutoCreatedProfile, false);
}
   
void UserSettings::setAutoCreatedProfile(bool autoCreated)
{
   settings_.set(kAutoCreatedProfile, autoCreated);
}

core::json::Object UserSettings::uiPrefs() const
{
   std::string value = settings_.get(kUiPrefs, "{}");
   json::Value jsonValue;
   bool success = core::json::parse(value, &jsonValue);
   if (success)
      return jsonValue.get_obj();
   else
      return json::Object();
}

void UserSettings::setUiPrefs(const core::json::Object& prefsObject)
{
   std::ostringstream output;
   json::writeFormatted(prefsObject, output);
   settings_.set(kUiPrefs, output.str());

   updatePrefsCache(prefsObject);
}

namespace {

template <typename T>
T readPref(const json::Object& prefs,
           const std::string& name,
           const T& defaultValue)
{
   T value = defaultValue;
   Error error = json::readObject(prefs,
                                  name,
                                  defaultValue,
                                  &value);
   if (error)
   {
      value = defaultValue;
      error.addProperty("pref", name);
      LOG_ERROR(error);
   }

   return value;
}

} // anonymous namespace

void UserSettings::updatePrefsCache(const json::Object& prefs) const
{ 
   bool useSpacesForTab = readPref<bool>(prefs, "use_spaces_for_tab", true);
   pUseSpacesForTab_.reset(new bool(useSpacesForTab));

   int numSpacesForTab = readPref<int>(prefs, "num_spaces_for_tab", 2);
   pNumSpacesForTab_.reset(new int(numSpacesForTab));

   bool autoAppendNewline = readPref<bool>(prefs, "auto_append_newline", false);
   pAutoAppendNewline_.reset(new bool(autoAppendNewline));

   bool stripTrailingWhitespace = readPref<bool>(prefs, "strip_trailing_whitespace", false);
   pStripTrailingWhitespace_.reset(new bool(stripTrailingWhitespace));

   std::string enc = readPref<std::string>(prefs, "default_encoding", "");
   pDefaultEncoding_.reset(new std::string(enc));

   std::string sweave = readPref<std::string>(prefs, "default_sweave_engine", "Sweave");
   pDefaultSweaveEngine_.reset(new std::string(sweave));

   std::string latex = readPref<std::string>(prefs, "default_latex_program", "pdfLaTeX");
   pDefaultLatexProgram_.reset(new std::string(latex));

   bool alwaysEnableRnwConcordance = readPref<bool>(prefs, "always_enable_concordance", true);
   pAlwaysEnableRnwConcordance_.reset(new bool(alwaysEnableRnwConcordance));

   std::string spellingLanguage = readPref<std::string>(prefs, "spelling_dictionary_language", "en_US");
   pSpellingLanguage_.reset(new std::string(spellingLanguage));

   json::Array spellingCustomDicts = readPref<core::json::Array>(prefs, "spelling_custom_dictionaries", core::json::Array());
   pSpellingCustomDicts_.reset(new json::Array(spellingCustomDicts));

   bool handleErrorsInUserCodeOnly = readPref<bool>(prefs, "handle_errors_in_user_code_only", true);
   pHandleErrorsInUserCodeOnly_.reset(new bool(handleErrorsInUserCodeOnly));

   int shinyViewerType = readPref<int>(prefs, "shiny_viewer_type", modules::shiny_viewer::SHINY_VIEWER_WINDOW);
   pShinyViewerType_.reset(new int(shinyViewerType));
   
   bool enableStyleDiagnostics = readPref<bool>(prefs, "enable_style_diagnostics", false);
   pEnableStyleDiagnostics_.reset(new bool(enableStyleDiagnostics));
   
   bool lintRFunctionCalls = readPref<bool>(prefs, "show_diagnostics_in_r_function_calls", true);
   pLintRFunctionCalls_.reset(new bool(lintRFunctionCalls));
   
}


// readers for ui prefs

bool UserSettings::useSpacesForTab() const
{
   return readUiPref<bool>(pUseSpacesForTab_);
}

{'fraction_non_alphanumeric': 0.11331604464990416, 'fraction_numerical': 0.0012402751155710903, 'mean_word_length': 4.5434375, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4028128', 'n_tokens_mistral': 5504, 'n_tokens_neox': 5277, 'n_words': 1153}