Is there a Prolog mode for CodeMirror?

I’ve made myself a browser-based text editor using the JavaScript libraries from https://codemirror.net which works nicely except there’s no Prolog mode in the long list of languages it does syntax highlighting for.

I thought I was in luck when Google brought up GitHub - JanWielemaker/CodeMirror: In-browser code editor but there doesn’t seem to be prolog in the mode list.

For anyone interested, I’ve included my code below. I’ve put my editor at a password protected URL. You would think a browser-based text editor would be easy, but I bumped my head against two problems which needed hacking around:

  1. Browser-based JavaScript has no builtin functions for server-side file editing. I read up on its File API, but that’s strictly for files on the client machine. To get around this, you have to send text to a web application written in your favourite programming language (SWI Prolog in our case). I’ve included my editor_app.pl below which I got Nginx to link to a port proxied to /cmd
  2. The standard HTML text editing box textarea appears to be unusable. I spent several hours cursing my Prolog script since I though it wasn’t updating the file, until it occurred to me to do some more research on textarea – and discovered it doesn’t actually send the current text it holds as value, but the original unedited text it was initialised with, which is why my Prolog script was simply replacing edited files with their original text. Libraries like CodeMirror only use textarea as an object in HTML files which they completely replace. Once I started using CodeMirrors getValue and setValue instead of textarea’s value attribute, everything worked.

My index.html looks like this

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Text Editor</title>
    <link rel="stylesheet" href="/lib/codemirror.css">
    <link rel="stylesheet" href="/theme/cobalt.css">
    <script src="/lib/codemirror.js"></script>
    <script src="/mode/css/css.js"></script> 
    <script src="/mode/htmlmixed/htmlmixed.js"></script> 
    <script src="/mode/javascript/javascript.js"></script> 
    <script src="/mode/markdown/markdown.js"></script>
    <script src="/mode/sql/sql.js"></script>
    <script src="/mode/xml/xml.js"></script>
  </head>
  <body>
    
    <label id="label_myTextArea" for="content">Tell us your story:</label><br>
    <textarea id="myTextArea" name="content" class="CodeMirror" rows="50">
      It was a dark and stormy night...
    </textarea><br>

    <button id="save-button" type="button">Save</button><br>
    
    <ul>
      <li><a href="/doc/manual.html">CodeMirror Manuel</a></li>
    </ul>
    
    <script src="/scripts/myeditor.js"></script>
  </body>
</html>

My scripts/myeditor.js looks like this

let myCodeMirror = {};

function save(event) {
  let params = new URLSearchParams(document.location.search);
  if (params.has('file')) {
    fetch('/cmd', { method: 'POST'
                  , headers: {"Content-Type": "application/json"}
                  , body: JSON.stringify({ "cmd": "write"
                                         , "file": params.get('file')
			                             , "content": myCodeMirror.getValue()
                                         })
                  })
	.then(response => response.json())
    .then(data => console.log(data));
  } else {
    document.querySelector("#myTextArea").textContent = "No file to edit";
  }
}

function setup(event) {
  let params = new URLSearchParams(document.location.search);
  if (params.has('file')) {
    document.querySelector("#label_myTextArea").textContent = params.get('file');
	fetch('/cmd', { method: 'POST'
		          , headers: {"Content-Type": "application/json"}
			      , body: JSON.stringify({ "cmd": "read"
			                             , "file": params.get('file')
					                     })
	              })
      .then(response => response.json())
      .then(function(data) {
		myCodeMirror = CodeMirror.fromTextArea(myTextArea,
					     { lineNumbers: true
					     , mode: params.get('mode')
                         , theme: "cobalt"
				         });
        myCodeMirror.setValue(data.content);
	    });
    } else {
	document.querySelector("#myTextArea").textContent = "No file to edit";
    }
}

window.addEventListener("DOMContentLoaded", setup);
document.querySelector("#save-button").addEventListener("click", save);

And finally my prolog web app looks like this

:- use_module([ library(http/http_unix_daemon)
              , library(http/http_server)
              ]).

:- http_handler(/, cmd_handler, []).

cmd_handler(Request) :-
  http_read_json_dict(Request, DictIn),
  cmd_manager(DictIn.cmd, DictIn, DictOut),
  reply_json_dict(DictOut).

cmd_manager("read", DictIn, json{content: Content}) :-
  read_file_to_string(DictIn.file, Content, []).

cmd_manager("write", DictIn, json{file: DictIn.file, time: Time}) :-
  setup_call_cleanup(
    open(DictIn.file, write, Stream),
    format(Stream, "~w", [DictIn.content]),
    close(Stream)
  ),
  time_file(DictIn.file, Time).

I fire it up as swipl editor_app.pl --port=4152 --pidfile=http.pid and then my nginx config file for my secret editor URL contains

server {
  root /home/roblaing/webapps/editor;

  location / {
      auth_basic "Restricted Content";
      auth_basic_user_file /etc/nginx/.htpasswd;
  }

  location /cmd {
    proxy_pass http://localhost:4152/;
  }
}

Loading files to edit with their syntax highlighter works by passing their full path and highlighting modes as query parameters as in http://mydomain/?file=/home/roblaing/webapps/editor/scripts/myeditor.js&mode=javascript

It all seems to work pretty nicely, and I’m glad I no longer have to edit on my local machine then scp to my server. Having a Prolog syntax highlighting mode would make it perfect.

1 Like

The CodeMirror fork is just that: an old fork. Maybe I should destroy it. A Prolog mode is part of SWISH. See web/js/codemirror. This is a very basic stand-alone mode that performs some really simple highlighting and smart indentation. The real functionality comes from server side support provided by lib/highlight.pl from SWISH. You can probably add that to your server as it does (not/very little) rely on other parts of SWISH.

1 Like

Thanks @jan

I’ve placed the file at swish/prolog.js at d357234613182abcc2953d5b827d367616be8502 · SWI-Prolog/swish · GitHub in mode/prolog/prolog.js on my server.

No immediate luck in getting it to syntax highlight yet, but I’ll tinker around with it. I haven’t looked at the documentation on writing CodeMirror modes, but its marketing claims its easy.

It may require some fiddling. The Javascript side may have more SWISH dependencies. I think the stuff is setup in editor.js.