APEX 21.2 quickie: syntax highlighting with prism.js

To show (not to edit) pretty code inside an APEX application in the past I had used the libraries that were deployed along with APEX, like CodeMirror (see https://svenweller.wordpress.com/2015/12/07/apex-5-syntax-highlighting/) and CkEditor. In APEX 21 CkEditor got a new version and CodeMirror is not supplied anymore since several APEX versions now. But there is a new very lightweight alternative, which is prism.

In my use case I need this to quickly present currently running edition based plsql code.

Implementation

Step 1) Load code into a hidden page item

I use a before region process to load the data (=code) into an item. Assuming item name P1_CODE for further references.

Step 2) Add prism files to the page

Javascript file urls

#PRISMJS_DIRECTORY#prism.js

CSS file urls

#PRISMJS_DIRECTORY#prism.css

Note that the substitution variable PRISMJS_DIRECTORY is automatically provided and resolved to the path “/i/libraries/prismjs/1.24.1/” and is updated in future versions.

Step 3) Show item in html region and choose the language

Add this to the html code section of a static region.

Note that you should set the correct language that you want to show. In my case it is language-plsql.

<pre><code class="language-plsql">&P1_CODE.</code></pre>

The value in the item is automatically html escaped. In case the logic that loads the code into the item did already escape it, you can also choose not to escape it again, by using an escape filter like &P1_CODE!RAW. . Just be aware of potential security issues, if you do not do that correctly.

A list of possible languages can be found here. However not all of those languages are added in the APEX deployment.

Step 4) Customize a few colors

I didn’t like the coloring for plsql or sql code. Especially I prefer to show comments in green. This can be changed with a few lines of css.

Add this to the inline css section of the page

/* prism plsql colorization */
.token.comment {
  color: #119b01;
}
.token.string {
  color: #0c6cb97c;
}
.token.keyword {
  color: #8C009B;
  font-weight: bold;
}

Very quick and easy to implement.

If you want different settings, simply inspect the code snippet using the browser. That is one of the advantages of prism. The code is tokenized using span tags and the appropriate classes. We can easily see what kind of token a certain element is and simply change the style for the class we want.

Result

Here is a crude overview how the result looks like

Additional considerations

prism has the possibility to add many additional features. However those features come as plugins that are packed directly into the .css and .js files. To use them, one would have to add/replace the library that is used by APEX. I didn’t investigate in detail, which plugins are already inclued. For example line numbering does NOT seem to work out of the box.

This would do the trick with the line numbers, if the line-numbers plugin is integrated.

<pre class="line-numbers" data-start="50"><code class="language-plsql">&P1_CODE.</code></pre>

Conclusion

For simple requirements prism allows a very easy and quick way to add syntax highlighting to code. For more complex requirements (like editing code) a different library might be better suited. The possibility to have dedicated subtitution strings for several libraries is very welcome.

One additional note. I tried to use the markdown editor item type as an alternative, but the effort to make it work for my simple requirements, was too high. A markdown editor simply serves a different purpose.

RDS as tabs

APEX quickie: set region display selector dynamically

A region display selector (RDS) can be configured so that it remembers the last selected region or tab. However sometimes we might want to navigate from another page directly to a specific tab no matter what the last remembered session state was. 

Here is how to do that.

My example uses a region display selector set to “View single region” and three color coded regions. The template options are choosen in such a way, that the regions are displayed directly below the selector, with no additional margin.

The region display selector (RDS) uses the session storage of the browser to remember the active tab. This can be investigated using the browsers developer tools.
Find web-storage, navigate to the Session Storage and look at the keys.

The Key here is using a prefix that consists of three parts. The application id (in my case 87230) the page number (2) and the region static id (DEMO). The name of the key is “activeTab”.  So the full name of the key is .87230.2.DEMO.activeTab

We can read and set the local storage and the session storage using APEX javascript apis. Link to the docs

The following code can read the session storage for the current page and will set a key to a specific value.

let sesStorage = apex.storage.getScopedSessionStorage({
       useAppId:true, 
       usePageId:true});
sesStorage.setItem( "DEMO.activeTab", "#REGION3" );

Typically we want to set the target tab from inside a different page. Set the target display selector, then navigate to that target page. This is the more logical thing to do, otherwise we could simply click on the link or sent this click event to the appropriate item. 

// choose target region via region display selector
// static id = "REGION3"
$('#REGION3_tab a').trigger('click');
If we are currently on a different page, we need to construct the prefix for the key by ourself. That is why the following code sets usePageId to false. It would be possible to read the session state for the whole application, but I prefere to read only the needed parts. So we construct the key prefix by ourself and this includes the appId.

let sesStorage = apex.storage.getScopedSessionStorage({
       prefix:"."+&APP_ID.+".2", 
       useAppId:false, 
       usePageId:false});
sesStorage.setItem( "DEMO.activeTab", "#REGION3" );
I tested this useing a static select list with a dynamic action that essentially run this code. And the result is as expected. The 3rd region is selected. Also the developer tools show that the value was changed. rds_result Fairly easy, once we know what to do. In the future I might use this session storage for other things too.