1#' Interact with Documents open in RStudio
2#'
3#' Use these functions to interact with documents open in RStudio.
4#'
5#' @param location An object specifying the positions, or ranges, wherein text
6#'   should be inserted. See \bold{Details} for more information.
7#'
8#' @param text A character vector, indicating what text should be inserted at
9#'   each aforementioned range. This should either be length one (in which case,
10#'   this text is applied to each range specified); otherwise, it should be the
11#'   same length as the \code{ranges} list.
12#'
13#' @param id The document id. When \code{NULL} or blank, the requested operation
14#'   will apply to the currently open, or last focused, RStudio document.
15#'
16#' @param position The cursor position, typically created through
17#'   \code{\link{document_position}()}.
18#'
19#' @param ranges A list of one or more ranges, typically created
20#'   through \code{\link{document_range}()}.
21#'
22#' @param type The type of document to be created.
23#'
24#' @param execute Should the code be executed after the document
25#'   is created?
26#'
27#' @param allowConsole Allow the pseudo-id `#console` to be returned, if the \R
28#'   console is currently focused? Set this to `FALSE` if you'd always like to
29#'   target the currently-active or last-active editor in the Source pane.
30#'
31#' @details
32#'
33#' \code{location} should be a (list of) \code{\link{document_position}} or
34#' \code{\link{document_range}} object(s), or numeric vectors coercable to
35#' such objects.
36#'
37#' To operate on the current selection in a document, call \code{insertText()}
38#' with only a text argument, e.g.
39#'
40#' \preformatted{
41#'     insertText("# Hello\\n")
42#'     insertText(text = "# Hello\\n")
43#' }
44#'
45#' Otherwise, specify a (list of) positions or ranges, as in:
46#'
47#' \preformatted{
48#'     # insert text at the start of the document
49#'     insertText(c(1, 1), "# Hello\\n")
50#'
51#'     # insert text at the end of the document
52#'     insertText(Inf, "# Hello\\n")
53#'
54#'     # comment out the first 5 rows
55#'     pos <- Map(c, 1:5, 1)
56#'     insertText(pos, "# ")
57#'
58#'     # uncomment the first 5 rows, undoing the previous action
59#'     rng <- Map(c, Map(c, 1:5, 1), Map(c, 1:5, 3))
60#'     modifyRange(rng, "")
61#' }
62#'
63#' \code{modifyRange} is a synonym for \code{insertText}, but makes its intent
64#' clearer when working with ranges, as performing text insertion with a range
65#' will replace the text previously existing in that range with new text. For
66#' clarity, prefer using \code{insertText} when working with
67#' \code{\link{document_position}}s, and \code{modifyRange} when working with
68#' \code{\link{document_range}}s.
69#'
70#' @note
71#' The \code{insertText}, \code{modifyRange} and \code{setDocumentContents}
72#' functions were added with version 0.99.796 of RStudio.
73#'
74#' The \code{setCursorPosition} and \code{setSelectionRanges} functions were
75#' added with version 0.99.1111 of RStudio.
76#'
77#' The \code{documentSave} and \code{documentSaveAll} functions were added
78#' with version 1.1.287 of RStudio.
79#'
80#' The \code{documentId} and \code{documentPath} functions were added with
81#' version 1.4.843 of RStudio.
82#'
83#' @name rstudio-documents
84NULL
85
86#' @name rstudio-documents
87#' @export
88insertText <- function(location = NULL,
89                       text = NULL,
90                       id = NULL)
91{
92  # unfortunate gymnastics needed for older versions of RStudio
93  if (getVersion() < "1.4")
94  {
95    if (is.null(location) && is.null(text))
96    {
97      callFun("insertText",
98              id = id)
99    }
100    else if (is.null(location))
101    {
102      callFun("insertText",
103              text = text,
104              id = id)
105    }
106    else if (is.null(text))
107    {
108      callFun("insertText",
109              location = location,
110              id = id)
111    }
112    else
113    {
114      callFun("insertText",
115              location = location,
116              text = text,
117              id = id)
118    }
119  }
120  else
121  {
122    callFun("insertText",
123            location = location,
124            text = text,
125            id = id)
126  }
127}
128
129#' @name rstudio-documents
130#' @export
131modifyRange <- insertText
132
133#' @name rstudio-documents
134#' @export
135setDocumentContents <- function(text, id = NULL) {
136
137  location <- document_range(
138    document_position(1, 1),
139    document_position(Inf, 1)
140  )
141
142  insertText(location, text, id)
143}
144
145#' @name rstudio-documents
146#' @export
147setCursorPosition <- function(position, id = NULL) {
148  callFun("setSelectionRanges", position, id)
149}
150
151#' @name rstudio-documents
152#' @export
153setSelectionRanges <- function(ranges, id = NULL) {
154  callFun("setSelectionRanges", ranges, id)
155}
156
157#' @name rstudio-documents
158#' @export
159documentId <- function(allowConsole = TRUE) {
160  callFun("documentId", allowConsole = allowConsole)
161}
162
163#' @name rstudio-documents
164#' @export
165documentPath <- function(id = NULL) {
166  callFun("documentPath", id = id)
167}
168
169#' @name rstudio-documents
170#' @export
171documentSave <- function(id = NULL) {
172  callFun("documentSave", id)
173}
174
175#' @name rstudio-documents
176#' @export
177documentSaveAll <- function() {
178  callFun("documentSaveAll")
179}
180
181#' Retrieve Information about an RStudio Editor
182#'
183#' Returns information about an RStudio editor.
184#'
185#' The \code{selection} field returned is a list of document selection objects.
186#' A document selection is just a pairing of a document \code{range}, and the
187#' \code{text} within that range.
188#'
189#' @note
190#' The \code{getActiveDocumentContext} function was added with version 0.99.796
191#' of RStudio, while the \code{getSourceEditorContext} and the \code{getConsoleEditorContext}
192#' functions were added with version 0.99.1111.
193#'
194#' @return A \code{list} with elements:
195#' \tabular{ll}{
196#' \code{id} \tab The document ID.\cr
197#' \code{path} \tab The path to the document on disk.\cr
198#' \code{contents} \tab The contents of the document.\cr
199#' \code{selection} \tab A \code{list} of selections. See \bold{Details} for more information.\cr
200#' }
201#'
202#' @rdname rstudio-editors
203#' @name rstudio-editors
204#' @export
205getActiveDocumentContext <- function() {
206  getDocumentContext("getActiveDocumentContext")
207}
208
209#' @name rstudio-editors
210#' @export
211getSourceEditorContext <- function() {
212  getDocumentContext("getSourceEditorContext")
213}
214
215#' @name rstudio-editors
216#' @export
217getConsoleEditorContext <- function() {
218  getDocumentContext("getConsoleEditorContext")
219}
220
221#' @note The \code{documentNew} function was introduced in RStudio 1.2.640.
222#'
223#' @name rstudio-documents
224#' @export
225documentNew <- function(
226  text,
227  type = c("r", "rmarkdown", "sql"),
228  position = document_position(0, 0),
229  execute = FALSE)
230{
231  type <- match.arg(type)
232  callFun("documentNew", type, text, position[1], position[2], execute)
233}
234
235#' @param save Whether to commit unsaved changes to the document before closing it.
236#'
237#' @note The \code{documentClose} function was introduced in RStudio 1.2.1255
238#'
239#' @details
240#'
241#' \code{documentClose} accepts an ID of an open document rather than a path.
242#' You can retrieve the ID of the active document using the \code{documentId()}
243#' function.
244#'
245#' Closing is always done non-interactively; that is, no prompts are given to
246#' the user. If the user has made changes to the document but not saved them,
247#' then the \code{save} parameter governs the behavior: when \code{TRUE},
248#' unsaved changes are committed, and when \code{FALSE} they are discarded.
249#'
250#' @name rstudio-documents
251#' @export
252documentClose <- function(id = NULL, save = TRUE) {
253  callFun("documentClose", id, save)
254}
255