<commit_before>/*
 * SessionErrors.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 <algorithm>

#include <core/Error.hpp>
#include <core/Exec.hpp>
#include <core/json/JsonRpc.hpp>

#include <r/RExec.hpp>
#include <r/ROptions.hpp>

#include <boost/bind.hpp>
#include <session/SessionModuleContext.hpp>
#include <session/SessionUserSettings.hpp>
#include "SessionErrors.hpp"

using namespace core;

namespace session {
namespace modules {
namespace errors {
namespace {

void enqueErrorHandlerChanged(int type)
{
   json::Object errorHandlerType;
   errorHandlerType["type"] = type;
   ClientEvent errorHandlerChanged(
            client_events::kErrorHandlerChanged, errorHandlerType);
   module_context::enqueClientEvent(errorHandlerChanged);
}

Error setErrHandler(int type, bool inMyCode,
                    boost::shared_ptr<SEXP> pErrorHandler)
{
   // clear the previous error handler; if we don't do this, the error handler
   // we set will be unset by DisableErrorHandlerScope during call evaluation
   r::options::setErrorOption(R_NilValue);

   Error error = r::exec::RFunction(
            ".rs.setErrorManagementType", type, inMyCode).call();
   if (error)
      return error;

   *pErrorHandler = r::options::getOption("error");
   return Success();
}

Error setErrHandlerType(int type,
                        boost::shared_ptr<SEXP> pErrorHandler)
{
   Error error = setErrHandler(type,
                               userSettings().handleErrorsInUserCodeOnly(),
                               pErrorHandler);
   if (error)
      return error;

   userSettings().setErrorHandlerType(type);
   enqueErrorHandlerChanged(type);
   return Success();
}

Error setErrHandlerType(boost::shared_ptr<SEXP> pErrorHandler,
                        const json::JsonRpcRequest& request,
                        json::JsonRpcResponse* pResponse)
{
   int type = 0;
   Error error = json::readParams(request.params, &type);
   if (error)
      return error;

   return setErrHandlerType(type, pErrorHandler);
}

// Initialize the error handler to the one that the user specified. Note that
// this initialization routine runs *before* .Rprofile is sourced, so any error
// handler set in .Rprofile will trump this one.
Error initializeErrManagement(boost::shared_ptr<SEXP> pErrorHandler,
                              boost::shared_ptr<bool> pHandleUserErrorsOnly)
{
   SEXP currentHandler = r::options::getOption("error");
   *pHandleUserErrorsOnly = userSettings().handleErrorsInUserCodeOnly();
   if (currentHandler == R_NilValue)
      setErrHandlerType(userSettings().errorHandlerType(), pErrorHandler);
   return Success();
}

void detectHandlerChange(boost::shared_ptr<SEXP> pErrorHandler)
{
   // check to see if the error option has been changed from beneath us; if
   // it has, emit a client event so the client doesn't show an incorrect
   // error handler
   SEXP currentHandler = r::options::getOption("error");
   if (currentHandler != *pErrorHandler)
   {
      *pErrorHandler = currentHandler;
      enqueErrorHandlerChanged(currentHandler == R_NilValue ?
                                  ERRORS_MESSAGE:
                                  ERRORS_CUSTOM);
   }
}

void onUserSettingsChanged(boost::shared_ptr<SEXP> pErrorHandler,
                           boost::shared_ptr<bool> pHandleUserErrorsOnly)
{
   // check to see if the setting for 'handle errors in user code only'
   // has been changed since we last looked at it; if it has, switch the
   // error handler in a corresponding way
   bool handleUserErrorsOnly = userSettings().handleErrorsInUserCodeOnly();
   if (handleUserErrorsOnly != *pHandleUserErrorsOnly)
   {
      Error error = setErrHandler(userSettings().errorHandlerType(),
                                  handleUserErrorsOnly,
                                  pErrorHandler);
      if (error)
         LOG_ERROR(error);

      *pHandleUserErrorsOnly = handleUserErrorsOnly;
   }
}

} // anonymous namespace

json::Value errorStateAsJson()
{<commit_msg>improve the experience when custom error handlers are set in ~/.Rprofile or at the command line<commit_after>/*
 * SessionErrors.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 <algorithm>

#include <core/Error.hpp>
#include <core/Exec.hpp>
#include <core/json/JsonRpc.hpp>

#include <r/RExec.hpp>
#include <r/ROptions.hpp>

#include <boost/bind.hpp>
#include <session/SessionModuleContext.hpp>
#include <session/SessionUserSettings.hpp>
#include "SessionErrors.hpp"

using namespace core;

namespace session {
namespace modules {
namespace errors {
namespace {

void enqueErrorHandlerChanged(int type)
{
   json::Object errorHandlerType;
   errorHandlerType["type"] = type;
   ClientEvent errorHandlerChanged(
            client_events::kErrorHandlerChanged, errorHandlerType);
   module_context::enqueClientEvent(errorHandlerChanged);
}

Error setErrHandler(int type, bool inMyCode,
                    boost::shared_ptr<SEXP> pErrorHandler)
{
   // when setting the error handler to "custom", just leave it as it was
   if (type == ERRORS_CUSTOM)
      return Success();

   // clear the previous error handler; if we don't do this, the error handler
   // we set will be unset by DisableErrorHandlerScope during call evaluation
   r::options::setErrorOption(R_NilValue);

   Error error = r::exec::RFunction(
            ".rs.setErrorManagementType", type, inMyCode).call();
   if (error)
      return error;

   *pErrorHandler = r::options::getOption("error");
   return Success();
}

Error setErrHandlerType(int type,
                        boost::shared_ptr<SEXP> pErrorHandler)
{
   Error error = setErrHandler(type,
                               userSettings().handleErrorsInUserCodeOnly(),
                               pErrorHandler);
   if (error)
      return error;

   userSettings().setErrorHandlerType(type);
   enqueErrorHandlerChanged(type);
   return Success();
}

Error setErrHandlerType(boost::shared_ptr<SEXP> pErrorHandler,
                        const json::JsonRpcRequest& request,
                        json::JsonRpcResponse* pResponse)
{
   int type = 0;
   Error error = json::readParams(request.params, &type);
   if (error)
      return error;

   return setErrHandlerType(type, pErrorHandler);
}

Error initializeErrManagement(boost::shared_ptr<SEXP> pErrorHandler,
                              boost::shared_ptr<bool> pHandleUserErrorsOnly)
{
   SEXP currentHandler = r::options::getOption("error");
   *pHandleUserErrorsOnly = userSettings().handleErrorsInUserCodeOnly();
   // This runs after ~/.RProfile, so don't change the error handler if
   // there's already one assigned, or if we're aware of a custom error
   // handler.
   if (currentHandler == R_NilValue &&
       userSettings().errorHandlerType() != ERRORS_CUSTOM)
      setErrHandlerType(userSettings().errorHandlerType(), pErrorHandler);
   else
      userSettings().setErrorHandlerType(ERRORS_CUSTOM);
   return Success();
}

void detectHandlerChange(boost::shared_ptr<SEXP> pErrorHandler)
{
   // check to see if the error option has been changed from beneath us; if
   // it has, emit a client event so the client doesn't show an incorrect
   // error handler
   SEXP currentHandler = r::options::getOption("error");
   if (currentHandler != *pErrorHandler)
   {
      *pErrorHandler = currentHandler;
      int handlerType = (currentHandler == R_NilValue) ?
                        ERRORS_MESSAGE :
                        ERRORS_CUSTOM;
      userSettings().setErrorHandlerType(handlerType);
      enqueErrorHandlerChanged(handlerType);
   }
}

void onUserSettingsChanged(boost::shared_ptr<SEXP> pErrorHandler,
                           boost::shared_ptr<bool> pHandleUserErrorsOnly)
{
   // check to see if the setting for 'handle errors in user code only'
   // has been changed since we last looked at it; if it has, switch the
   // error handler in a corresponding way
   bool handleUserErrorsOnly = userSettings().handleErrorsInUserCodeOnly();
   if (handleUserErrorsOnly != *pHandleUserErrorsOnly)
   {
      Error error = setErrHandler(userSettings().errorHandlerType(),
                                  handleUserErrorsOnly,
                                  pErrorHandler);
      if (error)
         LOG_ERROR(error);

      *pHandleUserErrorsOnly = handleUserErrorsOnly;
   }
}

} // anonymous namespace

json::Value errorStateAsJson()
{
{'fraction_non_alphanumeric': 0.09287991498405951, 'fraction_numerical': 0.0021253985122210413, 'mean_word_length': 3.2201793721973093, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4428830', 'n_tokens_mistral': 2615, 'n_tokens_neox': 2534, 'n_words': 745}