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