1 /*
2  * SessionModuleContext.hpp
3  *
4  * Copyright (C) 2021 by RStudio, PBC
5  *
6  *
7  * Unless you have received this program directly from RStudio pursuant
8  * to the terms of a commercial license agreement with RStudio, then
9  * this program is licensed to you under the terms of version 3 of the
10  * GNU Affero General Public License. This program is distributed WITHOUT
11  * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
12  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
13  * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
14  *
15  */
16 
17 #ifndef SESSION_MODULE_CONTEXT_HPP
18 #define SESSION_MODULE_CONTEXT_HPP
19 
20 #include <string>
21 
22 #include <boost/utility.hpp>
23 #include <boost/function.hpp>
24 #include <boost/shared_ptr.hpp>
25 
26 #include <core/BoostSignals.hpp>
27 #include <core/HtmlUtils.hpp>
28 #include <core/Version.hpp>
29 #include <core/system/System.hpp>
30 #include <core/system/ShellUtils.hpp>
31 #include <core/system/FileChangeEvent.hpp>
32 #include <core/http/UriHandler.hpp>
33 #include <core/json/JsonRpc.hpp>
34 #include <core/r_util/RToolsInfo.hpp>
35 #include <core/r_util/RActiveSessions.hpp>
36 #include <core/Thread.hpp>
37 
38 #include <session/SessionOptions.hpp>
39 #include <session/SessionClientEvent.hpp>
40 #include <session/SessionSourceDatabase.hpp>
41 
42 namespace rstudio {
43 namespace core {
44    class DistributedEvent;
45    class Error;
46    class Success;
47    class FilePath;
48    class FileInfo;
49    class Settings;
50    namespace system {
51       class ProcessSupervisor;
52       struct ProcessResult;
53    }
54    namespace shell_utils {
55       class ShellCommand;
56    }
57 }
58 }
59 
60 namespace rstudio {
61 namespace r {
62 namespace session {
63    struct RSuspendOptions;
64 }
65 }
66 }
67 
68 namespace rstudio {
69 namespace session {
70 
71 namespace module_context {
72 
73 enum PackageCompatStatus
74 {
75    COMPAT_OK      = 0,
76    COMPAT_MISSING = 1,
77    COMPAT_TOO_OLD = 2,
78    COMPAT_TOO_NEW = 3,
79    COMPAT_UNKNOWN = 4
80 };
81 
82 // paths
83 core::FilePath userHomePath();
84 std::string createAliasedPath(const core::FileInfo& fileInfo);
85 std::string createAliasedPath(const core::FilePath& path);
86 std::string createFileUrl(const core::FilePath& path);
87 core::FilePath resolveAliasedPath(const std::string& aliasedPath);
88 core::FilePath userScratchPath();
89 core::FilePath userUploadedFilesScratchPath();
90 core::FilePath scopedScratchPath();
91 core::FilePath sharedScratchPath();
92 core::FilePath sharedProjectScratchPath();
93 core::FilePath sessionScratchPath();
94 core::FilePath oldScopedScratchPath();
95 bool isVisibleUserFile(const core::FilePath& filePath);
96 
97 core::FilePath safeCurrentPath();
98 
99 core::json::Object createFileSystemItem(const core::FileInfo& fileInfo);
100 core::json::Object createFileSystemItem(const core::FilePath& filePath);
101 
102 // r session info
103 std::string rVersion();
104 std::string rVersionLabel();
105 std::string rHomeDir();
106 
107 // active sessions
108 core::r_util::ActiveSession& activeSession();
109 core::r_util::ActiveSessions& activeSessions();
110 
111 // get a temp file
112 core::FilePath tempFile(const std::string& prefix,
113                         const std::string& extension);
114 
115 core::FilePath tempDir();
116 
117 std::string rLibsUser();
118 
119 // find out the location of a binary
120 core::FilePath findProgram(const std::string& name);
121 
122 bool addTinytexToPathIfNecessary();
123 bool isPdfLatexInstalled();
124 
125 // is the file a text file
126 bool isTextFile(const core::FilePath& targetPath);
127 
128 // edit a file
129 void editFile(const core::FilePath& targetPath, int lineNumber = -1);
130 
131 // find the location of the R script
132 core::Error rBinDir(core::FilePath* pRBinDirPath);
133 core::Error rScriptPath(core::FilePath* pRScriptPath);
134 core::shell_utils::ShellCommand rCmd(const core::FilePath& rBinDir);
135 
136 // get the R local help port
137 std::string rLocalHelpPort();
138 
139 // get current libpaths
140 std::vector<core::FilePath> getLibPaths();
141 
142 // is the packages pane disabled
143 bool disablePackages();
144 
145 // check if a package is installed
146 bool isPackageInstalled(const std::string& packageName);
147 
148 // check if a package is installed with a specific version
149 bool isPackageVersionInstalled(const std::string& packageName,
150                                const std::string& version);
151 
152 // check if the required versions of various packages are installed
153 bool isMinimumDevtoolsInstalled();
154 bool isMinimumRoxygenInstalled();
155 
156 std::string packageVersion(const std::string& packageName);
157 core::Error packageVersion(const std::string& packageName,
158                            core::Version* pVersion);
159 
160 bool hasMinimumRVersion(const std::string& version);
161 
162 // check if a package is installed with a specific version and RStudio protocol
163 // version (used to allow packages to disable compatibility with older RStudio
164 // releases)
165 PackageCompatStatus getPackageCompatStatus(
166       const std::string& packageName,
167       const std::string& packageVersion,
168       int protocolVersion);
169 
170 core::Error installPackage(const std::string& pkgPath,
171                            const std::string& libPath = std::string());
172 
173 core::Error installEmbeddedPackage(const std::string& name);
174 
175 // find the package name for a source file
176 std::string packageNameForSourceFile(const core::FilePath& sourceFilePath);
177 
178 // is this R or C++ source file part of another (unmonitored) package?
179 bool isUnmonitoredPackageSourceFile(const core::FilePath& filePath);
180 
181 // register a handler for rBrowseUrl
182 typedef boost::function<bool(const std::string&)> RBrowseUrlHandler;
183 core::Error registerRBrowseUrlHandler(const RBrowseUrlHandler& handler);
184 
185 // register a handler for rBrowseFile
186 typedef boost::function<bool(const core::FilePath&)> RBrowseFileHandler;
187 core::Error registerRBrowseFileHandler(const RBrowseFileHandler& handler);
188 
189 // register an inbound uri handler (include a leading slash)
190 core::Error registerAsyncUriHandler(
191                    const std::string& name,
192                    const core::http::UriAsyncHandlerFunction& handlerFunction);
193 
194 // register an inbound uri handler (include a leading slash)
195 core::Error registerUriHandler(
196                         const std::string& name,
197                         const core::http::UriHandlerFunction& handlerFunction);
198 
199 // register an inbound upload handler (include a leading slash)
200 core::Error registerUploadHandler(const std::string& name,
201                                   const core::http::UriAsyncUploadHandlerFunction& handlerFunction);
202 
203 // register a local uri handler (scoped by a special prefix which indicates
204 // a local scope)
205 core::Error registerAsyncLocalUriHandler(
206                    const std::string& name,
207                    const core::http::UriAsyncHandlerFunction& handlerFunction);
208 
209 // register a local uri handler (scoped by a special prefix which indicates
210 // a local scope)
211 core::Error registerLocalUriHandler(
212                         const std::string& name,
213                         const core::http::UriHandlerFunction& handlerFunction);
214 
215 typedef boost::function<void(int, const std::string&)> PostbackHandlerContinuation;
216 
217 // register a postback handler. see docs in SessionPostback.cpp for
218 // details on the requirements of postback handlers
219 typedef boost::function<void(const std::string&, const PostbackHandlerContinuation&)>
220                                                       PostbackHandlerFunction;
221 core::Error registerPostbackHandler(
222                               const std::string& name,
223                               const PostbackHandlerFunction& handlerFunction,
224                               std::string* pShellCommand);
225 
226 // register an async rpc method
227 core::Error registerAsyncRpcMethod(
228                               const std::string& name,
229                               const core::json::JsonRpcAsyncFunction& function);
230 
231 // register an idle-only async rpc method
232 core::Error registerIdleOnlyAsyncRpcMethod(
233                               const std::string& name,
234                               const core::json::JsonRpcAsyncFunction& function);
235 
236 // register an rpc method
237 core::Error registerRpcMethod(const std::string& name,
238                               const core::json::JsonRpcFunction& function);
239 
240 void registerRpcMethod(const core::json::JsonRpcAsyncMethod& method);
241 
242 core::Error executeAsync(const core::json::JsonRpcFunction& function,
243                          const core::json::JsonRpcRequest& request,
244                          core::json::JsonRpcResponse* pResponse);
245 
246 
247 // create a waitForMethod function -- when called this function will:
248 //
249 //   (a) enque the passed event
250 //   (b) wait for the specified methodName to be returned from the client
251 //   (c) automatically re-issue the event after a client-init
252 //
253 typedef boost::function<bool(core::json::JsonRpcRequest*, const ClientEvent&)> WaitForMethodFunction;
254 WaitForMethodFunction registerWaitForMethod(const std::string& methodName);
255 
256 namespace {
257 
258 template <typename T>
rpcAsyncCoupleRunner(boost::function<core::Error (const core::json::JsonRpcRequest &,T *)> initFunc,boost::function<core::Error (const core::json::JsonRpcRequest &,const T &,core::json::JsonRpcResponse *)> workerFunc,const core::json::JsonRpcRequest & request,core::json::JsonRpcResponse * pResponse)259 core::Error rpcAsyncCoupleRunner(
260       boost::function<core::Error(const core::json::JsonRpcRequest&, T*)> initFunc,
261       boost::function<core::Error(const core::json::JsonRpcRequest&, const T&, core::json::JsonRpcResponse*)> workerFunc,
262       const core::json::JsonRpcRequest& request,
263       core::json::JsonRpcResponse* pResponse)
264 {
265    T state;
266    core::Error error = initFunc(request, &state);
267    if (error)
268       return error;
269 
270    return executeAsync(boost::bind(workerFunc, _1, state, _2),
271                        request,
272                        pResponse);
273 }
274 
275 } // anonymous namespace
276 
277 // Registers a two-part request handler, where "initFunc" runs on the main
278 // thread (and has access to everything a normal handler does, like R) and
279 // "workerFunc" runs on a background thread (and must not touch anything
280 // that isn't threadsafe). It is a Good Idea to only use workerFunc functions
281 // that are declared in the "workers" sub-project.
282 //
283 // The T type parameter represents the type of a value that initFunc produces
284 // and workerFunc consumes. This can be used to pass context between the two.
285 template <typename T>
registerRpcAsyncCoupleMethod(const std::string & name,boost::function<core::Error (const core::json::JsonRpcRequest &,T *)> initFunc,boost::function<core::Error (const core::json::JsonRpcRequest &,const T &,core::json::JsonRpcResponse *)> workerFunc)286 core::Error registerRpcAsyncCoupleMethod(
287       const std::string& name,
288       boost::function<core::Error(const core::json::JsonRpcRequest&, T*)> initFunc,
289       boost::function<core::Error(const core::json::JsonRpcRequest&, const T&, core::json::JsonRpcResponse*)> workerFunc)
290 {
291    return registerRpcMethod(name, boost::bind(rpcAsyncCoupleRunner<T>,
292                                               initFunc,
293                                               workerFunc,
294                                               _1,
295                                               _2));
296 }
297 
298 enum ConsoleOutputType
299 {
300    ConsoleOutputNormal,
301    ConsoleOutputError
302 };
303 
304 enum ChangeSource
305 {
306    ChangeSourceREPL,
307    ChangeSourceRPC,
308    ChangeSourceURI
309 };
310 
311 
312 // custom slot combiner that takes the first non empty value
313 template<typename T>
314 struct firstNonEmpty
315 {
316   typedef T result_type;
317 
318   template<typename InputIterator>
operator ()rstudio::session::module_context::firstNonEmpty319   T operator()(InputIterator first, InputIterator last) const
320   {
321      for (InputIterator it = first; it != last; ++it)
322      {
323         if (!it->empty())
324            return *it;
325      }
326      return T();
327   }
328 };
329 
330 
331 // session events
332 struct Events : boost::noncopyable
333 {
334    RSTUDIO_BOOST_SIGNAL<void(core::json::Object*)>                    onSessionInfo;
335    RSTUDIO_BOOST_SIGNAL<void()>                                       onClientInit;
336    RSTUDIO_BOOST_SIGNAL<void()>                                       onInitComplete;
337    RSTUDIO_BOOST_SIGNAL<void(bool)>                                   onDeferredInit;
338    RSTUDIO_BOOST_SIGNAL<void(bool)>                                   afterSessionInitHook;
339    RSTUDIO_BOOST_SIGNAL<void()>                                       onBeforeExecute;
340    RSTUDIO_BOOST_SIGNAL<void(const std::string&)>                     onConsolePrompt;
341    RSTUDIO_BOOST_SIGNAL<void(const std::string&)>                     onConsoleInput;
342    RSTUDIO_BOOST_SIGNAL<void(const std::string&, const std::string&)> onActiveConsoleChanged;
343    RSTUDIO_BOOST_SIGNAL<void(ConsoleOutputType, const std::string&)>  onConsoleOutput;
344    RSTUDIO_BOOST_SIGNAL<void()>                                       onUserInterrupt;
345    RSTUDIO_BOOST_SIGNAL<void(ChangeSource)>                           onDetectChanges;
346    RSTUDIO_BOOST_SIGNAL<void(core::FilePath)>                         onSourceEditorFileSaved;
347    RSTUDIO_BOOST_SIGNAL<void(bool)>                                   onBackgroundProcessing;
348    RSTUDIO_BOOST_SIGNAL<void(const std::vector<std::string>&)>        onLibPathsChanged;
349    RSTUDIO_BOOST_SIGNAL<void(const std::string&)>                     onPackageLoaded;
350    RSTUDIO_BOOST_SIGNAL<void()>                                       onPackageLibraryMutated;
351    RSTUDIO_BOOST_SIGNAL<void()>                                       onPreferencesSaved;
352    RSTUDIO_BOOST_SIGNAL<void(const core::DistributedEvent&)>          onDistributedEvent;
353    RSTUDIO_BOOST_SIGNAL<void(core::FilePath)>                         onPermissionsChanged;
354    RSTUDIO_BOOST_SIGNAL<void(bool)>                                   onShutdown;
355    RSTUDIO_BOOST_SIGNAL<void()>                                       onQuit;
356    RSTUDIO_BOOST_SIGNAL<void()>                                       onDestroyed;
357 
358    // signal for detecting extended type of documents
359    RSTUDIO_BOOST_SIGNAL<std::string(boost::shared_ptr<source_database::SourceDocument>),
360                  firstNonEmpty<std::string> > onDetectSourceExtendedType;
361 };
362 
363 Events& events();
364 
365 // ProcessSupervisor
366 core::system::ProcessSupervisor& processSupervisor();
367 
368 // schedule incremental work. execute will be called back periodically
369 // (up to every 25ms if the process is completely idle). if execute
370 // returns true then it will be called back again, if it returns false
371 // then it won't ever be called again. in a given period of work the
372 // execute method will be called multiple times (consecutively) for up
373 // to the specified incrementalDuration. if you want to implement a
374 // stateful worker simply create a shared_ptr to your worker object
375 // and then bind one of its members as the execute parameter. passing
376 // true as the idleOnly parameter (the default) means that the execute
377 // function will only be called back during idle time (when the session
378 // is waiting for user input)
379 void scheduleIncrementalWork(
380          const boost::posix_time::time_duration& incrementalDuration,
381          const boost::function<bool()>& execute,
382          bool idleOnly = true);
383 
384 // variation of scheduleIncrementalWork which performs a configurable
385 // amount of work immediately. this work occurs synchronously with the
386 // call and will consist of execute being called back repeatedly for
387 // up to the specified initialDuration
388 void scheduleIncrementalWork(
389          const boost::posix_time::time_duration& initialDuration,
390          const boost::posix_time::time_duration& incrementalDuration,
391          const boost::function<bool()>& execute,
392          bool idleOnly = true);
393 
394 
395 // schedule work to done every time the specified period elapses.
396 // if the execute function returns true then the worker will be called
397 // again after the specified period. pass idleOnly = true to restrict
398 // periodic work to idle time.
399 void schedulePeriodicWork(const boost::posix_time::time_duration& period,
400                           const boost::function<bool()> &execute,
401                           bool idleOnly = true,
402                           bool immediate = true);
403 
404 
405 // schedule work to be done after a fixed delay
406 void scheduleDelayedWork(const boost::posix_time::time_duration& period,
407                          const boost::function<void()> &execute,
408                          bool idleOnly = true);
409 
410 
411 core::string_utils::LineEnding lineEndings(const core::FilePath& filePath);
412 
413 core::Error readAndDecodeFile(const core::FilePath& filePath,
414                               const std::string& encoding,
415                               bool allowSubstChars,
416                               std::string* pContents);
417 
418 core::Error convertToUtf8(const std::string& encodedContent,
419                           const std::string& encoding,
420                           bool allowSubstChars,
421                           std::string* pDecodedContent);
422 
423 // source R files
424 core::Error sourceModuleRFile(const std::string& rSourceFile);
425 core::Error sourceModuleRFileWithResult(const std::string& rSourceFile,
426                                         const core::FilePath& workingDir,
427                                         core::system::ProcessResult* pResult);
428 
429 // enque client events (note R methods can do this via .rs.enqueClientEvent)
430 void enqueClientEvent(const ClientEvent& event);
431 
432 // check whether a directory is currently being monitored by one of our subsystems
433 bool isDirectoryMonitored(const core::FilePath& directory);
434 
435 // check whether an R source file belongs to the package under development
436 bool isRScriptInPackageBuildTarget(const core::FilePath& filePath);
437 
438 // convenience method for filtering out file listing and changes
439 bool fileListingFilter(const core::FileInfo& fileInfo, bool hideObjectFiles);
440 
441 // enque file changed events
442 void enqueFileChangedEvent(const core::system::FileChangeEvent& event);
443 void enqueFileChangedEvents(const core::FilePath& vcsStatusRoot,
444                             const std::vector<core::system::FileChangeEvent>& events);
445 
446 
447 // register a scratch path which is monitored.
448 typedef boost::function<void(const core::system::FileChangeEvent&)> OnFileChange;
449 core::FilePath registerMonitoredUserScratchDir(const std::string& dirName,
450                                                const OnFileChange& onFileChange);
451 
452 // enqueue new console input
453 core::Error enqueueConsoleInput(const std::string& input);
454 
455 // write output to the console (convenience wrapper for enquing a
456 // kConsoleWriteOutput event)
457 void consoleWriteOutput(const std::string& output);
458 
459 // write an error to the console (convenience wrapper for enquing a
460 // kConsoleWriteOutput event)
461 void consoleWriteError(const std::string& message);
462 
463 // show an error dialog (convenience wrapper for enquing kShowErrorMessage)
464 void showErrorMessage(const std::string& title, const std::string& message);
465 
466 void showFile(const core::FilePath& filePath,
467               const std::string& window = "_blank");
468 
469 
470 void showContent(const std::string& title, const core::FilePath& filePath);
471 
472 std::string resourceFileAsString(const std::string& fileName);
473 
474 std::string pathRelativeTo(const core::FilePath& sourcePath,
475                            const core::FilePath& targetPath);
476 
477 void activatePane(const std::string& pane);
478 
479 int saveWorkspaceAction();
480 void syncRSaveAction();
481 
482 std::string libPathsString();
483 bool canBuildCpp();
484 bool installRBuildTools(const std::string& action);
485 bool haveRcppAttributes();
486 bool isRtoolsCompatible(const core::r_util::RToolsInfo& rTools);
487 bool addRtoolsToPathIfNecessary(std::string* pPath,
488                                 std::string* pWarningMessage);
489 bool addRtoolsToPathIfNecessary(core::system::Options* pEnvironment,
490                                 std::string* pWarningMessage);
491 
492 bool isMacOS();
493 bool hasMacOSDeveloperTools();
494 bool hasMacOSCommandLineTools();
495 void checkXcodeLicense();
496 
497 #ifdef __APPLE__
498 core::Error copyImageToCocoaPasteboard(const core::FilePath& filePath);
499 #else
copyImageToCocoaPasteboard(const core::FilePath & filePath)500 inline core::Error copyImageToCocoaPasteboard(const core::FilePath& filePath)
501 {
502    return core::systemError(boost::system::errc::not_supported, ERROR_LOCATION);
503 }
504 #endif
505 
506 struct VcsContext
507 {
508    std::string detectedVcs;
509    std::vector<std::string> applicableVcs;
510    std::string svnRepositoryRoot;
511    std::string gitRemoteOriginUrl;
512 };
513 VcsContext vcsContext(const core::FilePath& workingDir);
514 
515 std::string normalizeVcsOverride(const std::string& vcsOverride);
516 
517 core::FilePath shellWorkingDirectory();
518 
519 // persist state accross suspend and resume
520 
521 typedef boost::function<void (const r::session::RSuspendOptions&,
522                               core::Settings*)> SuspendFunction;
523 typedef boost::function<void(const core::Settings&)> ResumeFunction;
524 
525 class SuspendHandler
526 {
527 public:
SuspendHandler(const SuspendFunction & suspend,const ResumeFunction & resume)528    SuspendHandler(const SuspendFunction& suspend,
529                   const ResumeFunction& resume)
530       : suspend_(suspend), resume_(resume)
531    {
532    }
533 
534    // COPYING: via compiler
535 
suspend() const536    const SuspendFunction& suspend() const { return suspend_; }
resume() const537    const ResumeFunction& resume() const { return resume_; }
538 
539 private:
540    SuspendFunction suspend_;
541    ResumeFunction resume_;
542 };
543 
544 void addSuspendHandler(const SuspendHandler& handler);
545 
546 bool rSessionResumed();
547 
548 const int kCompileOutputCommand = 0;
549 const int kCompileOutputNormal = 1;
550 const int kCompileOutputError = 2;
551 
552 struct CompileOutput
553 {
CompileOutputrstudio::session::module_context::CompileOutput554    CompileOutput(int type, const std::string& output)
555       : type(type), output(output)
556    {
557    }
558 
559    int type;
560    std::string output;
561 };
562 
563 core::json::Object compileOutputAsJson(const CompileOutput& compileOutput);
564 
565 
566 std::string previousRpubsUploadId(const core::FilePath& filePath);
567 
568 std::string CRANReposURL();
569 
570 std::string rstudioCRANReposURL();
571 
572 std::string downloadFileMethod(const std::string& defaultMethod = "");
573 
574 std::string CRANDownloadOptions();
575 
576 bool haveSecureDownloadFileMethod();
577 
578 void reconcileSecureDownloadConfiguration();
579 
580 struct UserPrompt
581 {
582    enum Type { Info = 0, Warning = 1, Error = 2, Question = 3 };
583    enum Response { ResponseYes = 0, ResponseNo = 1, ResponseCancel = 2 };
584 
UserPromptrstudio::session::module_context::UserPrompt585    UserPrompt(int type,
586               const std::string& caption,
587               const std::string& message,
588               bool includeCancel = false)
589    {
590       commonInit(type, caption, message, "", "", includeCancel, true);
591    }
592 
UserPromptrstudio::session::module_context::UserPrompt593    UserPrompt(int type,
594               const std::string& caption,
595               const std::string& message,
596               bool includeCancel,
597               bool yesIsDefault)
598    {
599       commonInit(type, caption, message, "", "", includeCancel, yesIsDefault);
600    }
601 
UserPromptrstudio::session::module_context::UserPrompt602    UserPrompt(int type,
603               const std::string& caption,
604               const std::string& message,
605               const std::string& yesLabel,
606               const std::string& noLabel,
607               bool includeCancel,
608               bool yesIsDefault)
609    {
610       commonInit(type,
611                  caption,
612                  message,
613                  yesLabel,
614                  noLabel,
615                  includeCancel,
616                  yesIsDefault);
617    }
618 
619    int type;
620    std::string caption;
621    std::string message;
622    std::string yesLabel;
623    std::string noLabel;
624    bool includeCancel;
625    bool yesIsDefault;
626 
627 private:
commonInitrstudio::session::module_context::UserPrompt628    void commonInit(int type,
629                    const std::string& caption,
630                    const std::string& message,
631                    const std::string& yesLabel,
632                    const std::string& noLabel,
633                    bool includeCancel,
634                    bool yesIsDefault)
635    {
636       this->type = type;
637       this->caption = caption;
638       this->message = message;
639       this->yesLabel = yesLabel;
640       this->noLabel = noLabel;
641       this->includeCancel = includeCancel;
642       this->yesIsDefault = yesIsDefault;
643    }
644 };
645 
646 UserPrompt::Response showUserPrompt(const UserPrompt& userPrompt);
647 
648 struct PackratContext
649 {
PackratContextrstudio::session::module_context::PackratContext650    PackratContext() :
651       available(false),
652       applicable(false),
653       packified(false),
654       modeOn(false)
655    {
656    }
657 
658    bool available;
659    bool applicable;
660    bool packified;
661    bool modeOn;
662 };
663 
664 // implemented in SessionPackrat.cpp
665 bool isRequiredPackratInstalled();
666 PackratContext packratContext();
667 core::json::Object packratContextAsJson();
668 core::json::Object packratOptionsAsJson();
669 
670 // implemented in SessionRenv.cpp
671 bool isRequiredRenvInstalled();
672 bool isRenvActive();
673 core::json::Value renvContextAsJson();
674 core::json::Value renvOptionsAsJson();
675 
676 // R command invocation -- has two representations, one to be submitted
677 // (shellCmd_) and one to show the user (cmdString_)
678 class RCommand
679 {
680 public:
RCommand(const core::FilePath & rBinDir)681    explicit RCommand(const core::FilePath& rBinDir)
682       : shellCmd_(buildRCmd(rBinDir))
683    {
684 #ifdef _WIN32
685       cmdString_ = "Rcmd.exe";
686 #else
687       cmdString_ = "R CMD";
688 #endif
689 
690       // set escape mode to files-only. this is so that when we
691       // add the group of extra arguments from the user that we
692       // don't put quotes around it.
693       shellCmd_ << core::shell_utils::EscapeFilesOnly;
694    }
695 
operator <<(const std::string & arg)696    RCommand& operator<<(const std::string& arg)
697    {
698       if (!arg.empty())
699       {
700          cmdString_ += " " + arg;
701          shellCmd_ << arg;
702       }
703       return *this;
704    }
705 
operator <<(const core::FilePath & arg)706    RCommand& operator<<(const core::FilePath& arg)
707    {
708       cmdString_ += " " + arg.getAbsolutePath();
709       shellCmd_ << arg;
710       return *this;
711    }
712 
713 
commandString() const714    const std::string& commandString() const
715    {
716       return cmdString_;
717    }
718 
shellCommand() const719    const core::shell_utils::ShellCommand& shellCommand() const
720    {
721       return shellCmd_;
722    }
723 
724 private:
725    static core::shell_utils::ShellCommand buildRCmd(
726                                  const core::FilePath& rBinDir);
727 
728 private:
729    std::string cmdString_;
730    core::shell_utils::ShellCommand shellCmd_;
731 };
732 
733 
734 class ViewerHistoryEntry
735 {
736 public:
ViewerHistoryEntry()737    ViewerHistoryEntry() {}
ViewerHistoryEntry(const std::string & sessionTempPath)738    explicit ViewerHistoryEntry(const std::string& sessionTempPath)
739       : sessionTempPath_(sessionTempPath)
740    {
741    }
742 
empty() const743    bool empty() const { return sessionTempPath_.empty(); }
744 
745    std::string url() const;
746 
sessionTempPath() const747    const std::string& sessionTempPath() const { return sessionTempPath_; }
748 
749    core::Error copy(const core::FilePath& sourceDir,
750                     const core::FilePath& destinationDir) const;
751 
752 private:
753    std::string sessionTempPath_;
754 };
755 
756 void addViewerHistoryEntry(const ViewerHistoryEntry& entry);
757 
758 struct QuartoNavigate
759 {
QuartoNavigaterstudio::session::module_context::QuartoNavigate760    QuartoNavigate() : website(false) {}
emptyrstudio::session::module_context::QuartoNavigate761    bool empty() const { return !website && source.empty(); }
navWebsiterstudio::session::module_context::QuartoNavigate762    static QuartoNavigate navWebsite()
763    {
764       QuartoNavigate nav;
765       nav.website = true;
766       return nav;
767    }
navDocrstudio::session::module_context::QuartoNavigate768    static QuartoNavigate navDoc(const std::string& source, const std::string& output)
769    {
770       QuartoNavigate nav;
771       nav.website = false;
772       nav.source = source;
773       nav.output = output;
774       return nav;
775    }
776    bool website;
777    std::string source;
778    std::string output;
779 };
780 
781 
782 void viewer(const std::string& url,
783             int height = 0, // pass 0 for no height change, // pass -1 for maximize
784             const QuartoNavigate& quartoNav = QuartoNavigate());
785 
786 void clearViewerCurrentUrl();
787 std::string viewerCurrentUrl(bool mapped = true);
788 
789 core::Error recursiveCopyDirectory(const core::FilePath& fromDir,
790                                    const core::FilePath& toDir);
791 
792 bool isSessionTempPath(core::FilePath filePath);
793 
794 std::string sessionTempDirUrl(const std::string& sessionTempPath);
795 
796 core::Error uniqueSaveStem(const core::FilePath& directoryPath,
797                            const std::string& base,
798                            std::string* pStem);
799 
800 core::Error uniqueSaveStem(const core::FilePath& directoryPath,
801                            const std::string& base,
802                            const std::string& delimiter,
803                            std::string* pStem);
804 
805 core::json::Object plotExportFormat(const std::string& name,
806                                     const std::string& extension);
807 
808 
809 core::Error createSelfContainedHtml(const core::FilePath& sourceFilePath,
810                                     const core::FilePath& targetFilePath);
811 
812 bool isUserFile(const core::FilePath& filePath);
813 
814 
815 struct SourceMarker
816 {
817    enum Type {
818       Error   = 0,
819       Warning = 1,
820       Box     = 2,
821       Info    = 3,
822       Style   = 4,
823       Usage   = 5,
824       Empty   = 99
825    };
826 
SourceMarkerrstudio::session::module_context::SourceMarker827    SourceMarker()
828       : type(Empty)
829    {
830    }
831 
SourceMarkerrstudio::session::module_context::SourceMarker832    SourceMarker(Type type,
833                 const core::FilePath& path,
834                 int line,
835                 int column,
836                 const core::html_utils::HTML& message,
837                 bool showErrorList)
838       : type(type),
839         path(path),
840         line(line),
841         column(column),
842         message(message),
843         showErrorList(showErrorList)
844    {
845    }
846 
operator boolrstudio::session::module_context::SourceMarker847    explicit operator bool() const
848    {
849       return type != Empty;
850    }
851 
852    Type type;
853    core::FilePath path;
854    int line;
855    int column;
856    core::html_utils::HTML message;
857    bool showErrorList;
858 };
859 
860 SourceMarker::Type sourceMarkerTypeFromString(const std::string& type);
861 
862 core::json::Array sourceMarkersAsJson(const std::vector<SourceMarker>& markers);
863 
864 struct SourceMarkerSet
865 {
SourceMarkerSetrstudio::session::module_context::SourceMarkerSet866    SourceMarkerSet() {}
867 
SourceMarkerSetrstudio::session::module_context::SourceMarkerSet868    SourceMarkerSet(const std::string& name,
869                    const std::vector<SourceMarker>& markers)
870       : name(name),
871         markers(markers)
872    {
873    }
874 
SourceMarkerSetrstudio::session::module_context::SourceMarkerSet875    SourceMarkerSet(const std::string& name,
876                    const core::FilePath& basePath,
877                    const std::vector<SourceMarker>& markers)
878       : name(name),
879         basePath(basePath),
880         markers(markers)
881    {
882    }
883 
emptyrstudio::session::module_context::SourceMarkerSet884    bool empty() const { return name.empty(); }
885 
886    std::string name;
887    core::FilePath basePath;
888    std::vector<SourceMarker> markers;
889 };
890 
891 enum MarkerAutoSelect
892 {
893    MarkerAutoSelectNone = 0,
894    MarkerAutoSelectFirst = 1,
895    MarkerAutoSelectFirstError = 2
896 };
897 
898 void showSourceMarkers(const SourceMarkerSet& markerSet,
899                        MarkerAutoSelect autoSelect);
900 
901 
902 bool isLoadBalanced();
903 
904 bool usingMingwGcc49();
905 
906 bool isWebsiteProject();
907 bool isBookdownWebsite();
908 bool isBookdownProject();
909 bool isBlogdownProject();
910 bool isDistillProject();
911 std::string websiteOutputDir();
912 std::vector<core::FilePath> bookdownBibliographies();
913 std::vector<std::string> bookdownBibliographiesRelative();
914 std::vector<std::string> bookdownZoteroCollections();
915 core::json::Value bookdownXRefIndex();
916 core::FilePath bookdownCSL();
917 
918 core::FilePath extractOutputFileCreated(const core::FilePath& inputDir,
919                                         const std::string& output);
920 
921 bool isPathViewAllowed(const core::FilePath& path);
922 
923 void onBackgroundProcessing(bool isIdle);
924 
925 void initializeConsoleCtrlHandler();
926 
927 bool isPythonReplActive();
928 
929 
930 core::Error perFilePathStorage(const std::string& scope,
931                                const core::FilePath& filePath,
932                                bool directory,
933                                core::FilePath* pStorage);
934 
935 // returns -1 if no error was found in the output
936 int jupyterErrorLineNumber(const std::vector<std::string>& srcLines,
937                            const std::string& output);
938 
939 std::vector<core::FilePath> ignoreContentDirs();
940 bool isIgnoredContent(const core::FilePath& filePath, const std::vector<core::FilePath>& ignoreDirs);
941 
942 std::string getActiveLanguage();
943 core::Error adaptToLanguage(const std::string& language);
944 
945 // paths to pandoc and pandoc-citeproc suitable for passing to the shell
946 // (string_utils::utf8ToSystem has been called on them)
947 std::string pandocPath();
948 std::string pandocCiteprocPath();
949 
950 core::Error runPandoc(const std::vector<std::string>& args,
951                       const std::string& input,
952                       core::system::ProcessResult* pResult);
953 
954 core::Error runPandocAsync(const std::vector<std::string>& args,
955                            const std::string& input,
956                            const boost::function<void(const core::system::ProcessResult&)>& onCompleted);
957 
958 core::Error runPandocCiteproc(const std::vector<std::string>& args, core::system::ProcessResult* pResult);
959 
960 core::Error runPandocCiteprocAsync(const std::vector<std::string>& args,
961                                    const boost::function<void(const core::system::ProcessResult&)>& onCompleted);
962 
963 } // namespace module_context
964 } // namespace session
965 } // namespace rstudio
966 
967 #endif // SESSION_MODULE_CONTEXT_HPP
968 
969