Skip to content

Commit 4b20089

Browse files
committed
syntax_tree() show results of token_table() as a tree
Annotated with the original source code. Glitches: - non-ascii unicode characters cause shifts in the source code annotation - multi-line tokens cause shifts in the line numbers
1 parent 56c59a8 commit 4b20089

1 file changed

Lines changed: 103 additions & 7 deletions

File tree

R/tree-sitter.R

Lines changed: 103 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ ts_languages <- c(
66
toml = 4L
77
)
88

9+
ts_detected_languages <- c(
10+
r = "r",
11+
md = "markdown",
12+
markdown = "markdown",
13+
rmd = "markdown",
14+
Rmarkdown = "markdown",
15+
yaml = "yaml",
16+
yml = "yaml",
17+
toml = "toml"
18+
)
19+
920
s_expr <- function(
1021
code,
1122
language = c("r", "markdown", "markdown-inline", "yaml", "toml"),
@@ -18,14 +29,99 @@ s_expr <- function(
1829
}
1930

2031
token_table <- function(
21-
code,
22-
language = c("r", "markdown", "markdown-inline", "yaml", "toml"),
23-
ranges = NULL
32+
file = NULL,
33+
language = NULL,
34+
ranges = NULL,
35+
text = NULL
2436
) {
25-
language <- tolower(language)
26-
language <- ts_languages[match.arg(language)]
27-
if (is.character(code)) code <- charToRaw(paste(code, collapse = "\n"))
28-
call_with_cleanup(c_token_table, code, language, ranges)
37+
if (is.null(text) + is.null(file) != 1) {
38+
stop(
39+
"Invalid arguments in `token_table()`: exactly one of ",
40+
"`file` and `text` must be given."
41+
)
42+
}
43+
if (is.null(text)) {
44+
text <- readBin(file, "raw", n = file.size(file))
45+
if (is.null(language)) {
46+
ext <- tolower(tools::file_ext(file))
47+
if (!ext %in% names(ts_detected_languages)) {
48+
stop(
49+
"Cannot detect language in `token_table(), need to specify",
50+
"it explicitly"
51+
)
52+
}
53+
language <- ts_detected_languages[ext]
54+
}
55+
} else if (is.null(language)) {
56+
stop("Invalid arguments in `token_table()`: need to specify `language`.")
57+
} else {
58+
language <- match.arg(tolower(language), names(ts_languages))
59+
}
60+
language <- ts_languages[language]
61+
if (is.character(text)) text <- charToRaw(paste(text, collapse = "\n"))
62+
tab <- call_with_cleanup(c_token_table, text, language, ranges)
63+
lvls <- seq_len(nrow(tab))
64+
tab$children <- I(unname(split(lvls, factor(tab$parent, levels = lvls))))
65+
attr(tab, "file") <- file
66+
tab
67+
}
68+
69+
syntax_tree <- function(
70+
file = NULL,
71+
language = NULL,
72+
ranges = NULL,
73+
text = NULL
74+
) {
75+
tokens <- token_table(file, language, ranges = ranges, text = text)
76+
77+
type <- tokens$type
78+
fn <- attr(tokens, "file")
79+
if (cli::ansi_has_hyperlink_support() && !is.null(fn)) {
80+
type <- cli::style_hyperlink(
81+
type,
82+
sprintf(
83+
"file://%s:%d:%d",
84+
normalizePath(fn, mustWork = NA),
85+
tokens$start_row + 1L,
86+
tokens$start_column + 1
87+
)
88+
)
89+
}
90+
91+
linum <- tokens$start_row + 1
92+
linum <- ifelse(duplicated(linum), "", as.character(linum))
93+
linum <- format(linum, justify = "right")
94+
# this is the spacer we need to put in for multi-line tokens
95+
nlspc <- paste0("\n\t", strrep(" ", nchar(linum[1])), "|")
96+
code <- ifelse(
97+
is.na(tokens$code),
98+
"",
99+
paste0(strrep(" ", tokens$start_column), tokens$code)
100+
)
101+
102+
# we put in a \t, and later use it to align the lines vertically
103+
treetab <- data_frame(
104+
id = as.character(tokens$id),
105+
children = lapply(tokens$children, as.character),
106+
label = paste0(
107+
type,
108+
"\t",
109+
linum,
110+
"|",
111+
gsub("\n", nlspc, code, fixed = TRUE)
112+
)
113+
)
114+
tree <- cli::tree(treetab)
115+
116+
# align lines vertically. the size of the alignment is measured
117+
# without the ANSI sequences, but then the substitution uses the
118+
# full ANSI string
119+
tabpos <- regexpr("\t", cli::ansi_strip(tree), fixed = TRUE)
120+
maxtab <- max(tabpos)
121+
tabpos2 <- regexpr("\t", tree, fixed = TRUE)
122+
regmatches(tree, tabpos2) <- strrep(" ", maxtab - tabpos + 4)
123+
124+
tree
29125
}
30126

31127
code_query <- function(

0 commit comments

Comments
 (0)