Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

javascript - How to enable syntax highlighting in R Shiny app with htmlOutput

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:

enter image description here

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...