I have a Shiny app that generates computer code dynamically based on user inputs and presents it to the user so they can see exactly what query is being sent to a database. I have prism syntax highlighting working for code that is directly in the user interface function, so I know that prism is working; but for code that is generated in the server and sent to the user via renderText
and htmlOutput
the highlighting doesn't work.
Here is an image of a simple example:
Which was made with this R file (and prism.css and prism.js in the www
folder of the Shiny app)
ui <- shinyUI(fluidPage(
tags$head(
tags$link(rel = "stylesheet", type = "text/css", href = "prism.css")
),
tags$body(
tags$script(src="prism.js")
),
HTML("<pre><code class='language-sql'>SELECT * FROM mytable WHERE 1=2
-- this chunk should be syntax highlighted and it is
</code></pre>"),
HTML("<pre>SELECT * FROM mytable WHERE 1=2
-- this chunk should not be syntax highlighted
</code></pre>"),
htmlOutput("sql")
)
)
# Define the server code
server <- function(input, output) {
txt <- "<pre><code class='language-sql'>SELECT * FROM mytable WHERE 1=2
-- this chunk should be syntax highlighted but isn't for some reason,
-- presumably connected to it getting to the UI via renderText and htmlOutput
</code></pre>"
output$sql <- renderText(txt)
}
In the DOM Explorer I can see that in the webpage generated by Shiny, the third (not working) chunk is within a <div class="shiny-html-output shiny-bound-output">
, then is correctly wrapped in <pre>
and <code class="language-sql">
tags like the first chunk. So being wrapped in that div is what is stopping prism.js from doing its highlighting thing.
What should I do to have the third chunk highlighted like the first?
Prism.js runs as soon as its loaded, so any code blocks dynamically added afterward won't get highlighted. One option would be to load prism.js
dynamically in the server function as well.
output$sql <- renderUI({
tagList(
tags$script(src = "prism.js"),
HTML(txt)
)
})
But this is not very robust. You could easily load multiple copies of the script. And if you make it a shiny::singleton
or use htmltools::htmlDependency
to only load the script once, you could easily find yourself back in the original situation.
A better solution - Prism provides an API that lets you programmatically highlight code blocks: http://prismjs.com/extending.html#api
How about running Prism.highlightAll()
after rendering any code block?
library(shiny)
prismCodeBlock <- function(code) {
tagList(
HTML(code),
tags$script("Prism.highlightAll()")
)
}
prismDependencies <- tags$head(
tags$script(src = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.8.4/prism.min.js"),
tags$link(rel = "stylesheet", type = "text/css",
href = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.8.4/themes/prism.min.css")
)
prismSqlDependency <- tags$head(
tags$script(src = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.8.4/components/prism-sql.min.js")
)
ui <- fluidPage(
prismDependencies,
prismSqlDependency,
HTML("<pre><code class='language-sql'>SELECT * FROM mytable WHERE 1=2
-- this chunk should be syntax highlighted and it is
</code></pre>"),
HTML("<pre>SELECT * FROM mytable WHERE 1=2
-- this chunk should not be syntax highlighted
</code></pre>"),
htmlOutput("sql")
)
server <- function(input, output) {
txt <- "<pre><code class='language-sql'>SELECT * FROM mytable WHERE 1=2
-- this chunk should be syntax highlighted but isn't for some reason,
-- presumably connected to it getting to the UI via renderText and htmlOutput
</code></pre>"
output$sql <- renderUI({
prismCodeBlock(txt)
})
}
shinyApp(ui, server)
For further improvement, you could use Prism.highlightElement()
to be more efficient. Could also create an HTML widget out of these Prism code blocks to abstract away the messy details.