@@ -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+
920s_expr <- function (
1021 code ,
1122 language = c(" r" , " markdown" , " markdown-inline" , " yaml" , " toml" ),
@@ -18,14 +29,99 @@ s_expr <- function(
1829}
1930
2031token_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
31127code_query <- function (
0 commit comments