@@ -1,5 +1,5 @@
#include <Rcpp.h>
using namespace Rcpp;
#include <cpp11/R.hpp>
#include <string>

// From http://developer.r-project.org/parseRd.pdf: The characters \, %, {,
// and } have special meaning in almost all parts of an Rd file. In code,
@@ -84,12 +84,12 @@ int roxygen_parse_tag(std::string string, bool is_code = false,
}
}

// [[Rcpp::export]]
int findEndOfTag(std::string string, bool is_code = false) {
[[cpp11::register]]
int findEndOfTag(std::string string, bool is_code) {
return roxygen_parse_tag(string, is_code, 1);
}

// [[Rcpp::export]]
bool rdComplete(std::string string, bool is_code = false) {
[[cpp11::register]]
bool rdComplete(std::string string, bool is_code) {
return roxygen_parse_tag(string, is_code, 0) == 1 ? true : false;
}
@@ -1,5 +1,5 @@
#include <Rcpp.h>
using namespace Rcpp;
#include <cpp11/integers.hpp>
#include <cpp11/strings.hpp>

int leadingSpacesOne(std::string line) {
int n = line.size();
@@ -12,14 +12,13 @@ int leadingSpacesOne(std::string line) {
return n;
}

// [[Rcpp::export]]
IntegerVector leadingSpaces(CharacterVector lines) {
[[cpp11::register]]
cpp11::integers leadingSpaces(cpp11::strings lines) {
int n = lines.size();
IntegerVector out(n);
cpp11::writable::integers out(n);

for(int i = 0; i < n; ++i) {
String cur = lines[i];
out[i] = leadingSpacesOne(cur);
out[i] = leadingSpacesOne(lines[i]);
}
return out;
}
@@ -1,7 +1,11 @@
#include <Rcpp.h>
using namespace Rcpp;
#include <cpp11/list.hpp>
#include <cpp11/list_of.hpp>
#include <cpp11/strings.hpp>
#include <cpp11/protect.hpp>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>

class RoxygenLine {
std::string line_;
@@ -100,9 +104,9 @@ std::string stripTrailingNewline(std::string x) {
return x;
}

// [[Rcpp::export]]
List tokenise_block(CharacterVector lines, std::string file = "",
int offset = 0) {
[[cpp11::register]]
cpp11::list tokenise_block(cpp11::strings lines, std::string file,
int offset) {
std::vector<std::string> tags, vals;
std::vector<int> rows;

@@ -141,30 +145,34 @@ List tokenise_block(CharacterVector lines, std::string file = "",
}

// Convert to a list
int n = rows.size();
ListOf<List> out(n);

for (int i = 0; i < n; ++i) {
out[i] = List::create(
_["file"] = file,
_["line"] = rows[i],
_["tag"] = tags[i],
// Rcpp::String() necessary to tag string as UTF-8
_["raw"] = Rcpp::String(stripTrailingNewline(vals[i])),
_["val"] = R_NilValue
);
out[i].attr("class") = Rcpp::CharacterVector::create("roxy_tag_" + tags[i], "roxy_tag");
R_xlen_t n = rows.size();
cpp11::writable::list out(n);

using namespace cpp11::literals;

for (R_xlen_t i = 0; i < n; ++i) {
cpp11::writable::list x({
"file"_nm = file,
"line"_nm = rows[i],
"tag"_nm = tags[i],
"raw"_nm = stripTrailingNewline(vals[i]),
"val"_nm = R_NilValue
});
std::string tag("roxy_tag_");
tag += tags[i];
x.attr("class") = {tag.c_str(), "roxy_tag"};
out[i] = x;
}
return out;
}

// [[Rcpp::export]]
CharacterVector find_includes(std::string path) {
[[cpp11::register]]
cpp11::strings find_includes(std::string path) {
std::vector<std::string> includes;

std::ifstream file(path.c_str());
if (!file.good())
stop("Failed to open %s", path);
cpp11::stop("Failed to open %s", path.c_str());

std::string rawline;
while (std::getline(file, rawline)) {
@@ -192,5 +200,5 @@ CharacterVector find_includes(std::string path) {
);
}

return wrap(includes);
return cpp11::as_sexp(includes);
}
@@ -1,5 +1,6 @@
#include <Rcpp.h>
using namespace Rcpp;
#include <cpp11/strings.hpp>
#include <vector>
#include <string>

std::vector<std::string> splitByWhitespace(std::string string) {
std::vector<std::string> out;
@@ -41,8 +42,8 @@ std::vector<std::string> splitByWhitespace(std::string string) {
return out;
}

// [[Rcpp::export]]
std::string wrapUsage(std::string string, int width = 80, int indent = 2) {
[[cpp11::register]]
std::string wrapUsage(std::string string, int width, int indent) {
std::vector<std::string> pieces = splitByWhitespace(string);
int n = pieces.size();
int cur_width = 0;
@@ -1,61 +1,61 @@
# Test low-level behaviour ----------------------------------------------------

test_that("braces must balance", {
expect_true(rdComplete("{}"))
expect_true(rdComplete("{{}}"))
expect_true(rdComplete("{}", is_code = FALSE))
expect_true(rdComplete("{{}}", is_code = FALSE))

expect_false(rdComplete("{"))
expect_false(rdComplete("}"))
expect_false(rdComplete("{", is_code = FALSE))
expect_false(rdComplete("}", is_code = FALSE))
})

test_that("can't end with escape", {
expect_false(rdComplete("\\"))
expect_false(rdComplete("\\", is_code = FALSE))
})

test_that("escaped brackets are ignored", {
expect_true(rdComplete("\\{"))
expect_true(rdComplete("\\}"))
expect_false(rdComplete("{\\}"))
expect_true(rdComplete("\\{", is_code = FALSE))
expect_true(rdComplete("\\}", is_code = FALSE))
expect_false(rdComplete("{\\}", is_code = FALSE))
})

test_that("brackets in comments are ignored", {
expect_true(rdComplete("% {"))
expect_true(rdComplete("% }"))
expect_true(rdComplete("% {", is_code = FALSE))
expect_true(rdComplete("% }", is_code = FALSE))
})

test_that("R comments don't close latex-like tags", {
expect_true(rdComplete("A comment \\code{#}."))
expect_true(rdComplete("A comment \\code{#}.", is_code = FALSE))
})

test_that("newline ends comment", {
expect_false(rdComplete("%\n{"))
expect_false(rdComplete("%\n{", is_code = FALSE))
})

test_that("escape disables comment", {
expect_false(rdComplete("\\%{"))
expect_false(rdComplete("\\%{", is_code = FALSE))
})

test_that("strings must be closed in code", {
expect_false(rdComplete("'", TRUE))
expect_false(rdComplete('"', TRUE))
expect_false(rdComplete("'", is_code = TRUE))
expect_false(rdComplete('"', is_code = TRUE))
})

test_that("strings respect escapes", {
expect_false(rdComplete("'\\'", TRUE)) # '\'
expect_true(rdComplete("'\\''", TRUE)) # '\''
expect_false(rdComplete("'\\'", is_code = TRUE)) # '\'
expect_true(rdComplete("'\\''", is_code = TRUE)) # '\''
})

test_that("braces in strings don't need to match in code", {
expect_true(rdComplete("'{{'", TRUE))
expect_true(rdComplete("'{{'", is_code = TRUE))
})

test_that("strings in code comments don't need to be closed", {
expect_true(rdComplete("# '", TRUE))
expect_true(rdComplete("# '", is_code = TRUE))
})

test_that("braces in code must match", {
expect_false(rdComplete("# {", TRUE))
expect_true(rdComplete("# {}", TRUE))
expect_false(rdComplete("# {", is_code = TRUE))
expect_true(rdComplete("# {}", is_code = TRUE))
})

# Test that incomplete Rd is caught in Rd blocks -------------------------------
@@ -1,15 +1,15 @@
# tokenise_block ----------------------------------------------------------

test_that("parses into tag and value", {
x <- tokenise_block("#' @xyz abc")
x <- tokenise_block("#' @xyz abc", file = "", offset = 0)
expect_equal(length(x), 1)

expect_equal(x[[1]]$tag, "xyz")
expect_equal(x[[1]]$raw, "abc")
})

test_that("description block gets empty tag", {
x <- tokenise_block("#' abc")
x <- tokenise_block("#' abc", file = "", offset = 0L)
expect_equal(length(x), 1)

expect_equal(x[[1]]$tag, "")
@@ -19,17 +19,17 @@ test_that("description block gets empty tag", {
test_that("multi line tags collapsed into one", {
x <- tokenise_block(c(
"#' @tag abc",
"#' def"
))
"#' def"),
file = "", offset = 0L)
expect_equal(length(x), 1)
expect_equal(x[[1]]$raw, "abc\n def")
})

test_that("description block gets empty tag when followed by tag", {
x <- tokenise_block(c(
"#' abc",
"#' @xyz abc"
))
"#' @xyz abc"),
file = "", offset = 0L)
expect_equal(length(x), 2)

expect_equal(x[[1]]$tag, "")
@@ -40,20 +40,20 @@ test_that("description block gets empty tag when followed by tag", {
})

test_that("leading whitespace is ignored", {
ref <- tokenise_block("#' abc")
ref <- tokenise_block("#' abc", file = "", offset = 0L)

expect_equal(tokenise_block(" #' abc"), ref)
expect_equal(tokenise_block(" #' abc", file = "", offset = 0L), ref)
})

test_that("need one or more #", {
ref <- tokenise_block("#' abc")
ref <- tokenise_block("#' abc", file = "", offset = 0L)

expect_equal(tokenise_block("##' abc"), ref)
expect_equal(tokenise_block("###' abc"), ref)
expect_equal(tokenise_block("##' abc", file = "", offset = 0L), ref)
expect_equal(tokenise_block("###' abc", file = "", offset = 0L), ref)
})

test_that("@@ becomes @", {
expect_equal(tokenise_block("#' @tag @@")[[1]]$raw, "@")
expect_equal(tokenise_block("#' @tag @@", file = "", offset = 0L)[[1]]$raw, "@")
})

# Inline comments ---------------------------------------------------------