LispMode improvements
I stopped development of my LispMode plugin on the 3rd of 4th November. I had to focus on other activites, which include newLISP coding. I had some time to better test the plugin, and I noticed, that it really helps me in work. So I decided to spend one day per week on LispMode development, because I find it really worth it.
Today, I made a new version available. I fixed some bugs (e.g. indetnation bugs), and I added a smart tab feature. I also decided to make two versions available for download: one tested, and one that is still in development, but has all the implemented features.
The next big feature to add is indentation of the selected text (or, perhaps, the highlighted form?). Right now, I have to do some conceptual work in this area, because the problem isn’t an easy one.
I’ll try to explain it.
Let’s suppose we have a let form:
-
(let ( name1 exp1 name2 exp2 … ) …)
Of course, such a form can be written in many ways. Lisp reader cares only about parenthesis, names and expressions, and they all should be separated with some whitespace characters. The problem is that we, humans, want the code to be as much readable, as possible, so we often use some indentation scheme. An example is presented below:
-
(let ( name1 exp1
-
name2 exp2
-
… )
-
… )
The form above looks fine, but usually all the names are of different length (number of characters), and so are expressions. So it’s easy to end up with a mess:
-
(let ( n (exp1)
-
some-longer-name (exp2)
-
a-name (exp3) )
-
… )
I think that the form would be more readable, when a simple indentation scheme is used:
-
(let ( n (exp1)
-
some-longer-name (exp2)
-
a-name (exp3) )
-
… )
The problem is simple: it’s difficult to indent everything like that in the first place, and it’s very unconvenient to reindent the code later. So I thought that the LispMode plugin could apply such an indentation scheme to some code…
Well, implementing this particular indentation scheme is not a hard task. I do realize, however, that there are many various indentation schemes. And that’s the problem – how to give a plugin’s user ability to apply his/her own indentation schemes?
One way to solve such a problem would be to invent some language to describe indentation schemes. Yes, it could work, but it would require a plugin’s user to learn that language in order to use this functionality. Well, the language wouldn’t probably be that hard to learn, after all. Anyway, I’m not sure whether it’s the best way to go.
LispMode plugin for jEdit
Some time ago I started to work on a jEdit plugin for newLISP (and, probably, other Lisp dialects) source files editing. The plugin is still in its alpha stage, and I’m still learning how to write jEdit plugins, but on the other side, some features are already working:
- functions’ list is displayed (the list might be used to navigate the source file)
- smart indenting after the ENTER key was pressed
- forms are highlighted while the cursor is moved
- incomplete form brackets are also highlighted (in red) while the cursor is moved
The plugin still needs a lot of work to be completed. All the features have to be improved. Besides, so far my only motivation for developing this plugin was my need for such a tool. Thus, the plugin is adapted to my personal needs and habits.
The good news is: you can use the plugin, if you’d like to.
The bad news:
- The plugin has been hardly tested. It shoudn’t delete your files, nor crash you computer. It’s possible, though, that the features won’t be working as they should. (I’ll appreciate any bug reports!)
- So far, I’ve had time to work on the plugin. Development take me more and more time, though. It’s possible, that in the future I’ll have to focus on other tasks, and I’ll work on the plugin after hours. It’ll slow down the development.
Anyway, if you like the plugin, and if you’d like to propose some features, please let me know.
Oh, and one more thing. Right now, the plugin works only for buffers which use the newlisp jEdit mode.
Rename page feature for newLISP wiki
newLISP wiki allows a nice interface to manage wiki, in the Files:
The only thing that is missing is renaming pages. A smart rename should allow user to rename a page, and rename all the references to that page. In this article, I’ll try to document my implementation of this feature.
First of all, I learned about the way newLISP wiki handles HTTP requests. Whole wiki (I think) is placed in index.cgi file. Its starting point is placed after all the auxiliary functions, and then some typical page parts are set (like title and copyright). After this short introducing code (let me name it this way), conditional expressions start. They are responsible for detecting the user’s request and for handling it.
There are many different requests possible, like edit, backup and restore. In my case, I was interested in the files request, since it was the request which displayed a list of all pages, and actions that could be taken on them (like edit or delete). I wanted to add a new action, rename.
So, I wrote a new request, presented below. I added it after all the rest of requests, in hope I won’t brake anything.
-
;; request to rename a page
-
(if (CGI:get "rename")
-
(if (CGI:get "continue")
-
(letn ( page-old (CGI:get "rename")
-
page-old-filename (replace " " (dup page-old 1) "_")
-
page-new (CGI:get "new")
-
page-new-filename (replace " " (dup page-new 1) "_") )
-
(if (= (length (trim page-new)) 0)
-
(let ( body (append {<p>You haven’t entered any new name for the page } page-old {.</p>}
-
{<p>Try again on <a href="index.cgi?rename=} page-old
-
{">rename page</a>, go to }
-
{<a href="index.cgi?page=} page-old-filename {">} page-old {</a>, or }
-
{get back to <a href="index.cgi">Home</a>.</p>} ) )
-
(set ‘body body)
-
(set ‘page-name "Rename")
-
(CGI:put-page SETUP:template)
-
(exit))
-
(begin
-
(change-refs page-old page-new)
-
(rename-file (append "pages/" page-old-filename) (append "pages/" page-new-filename))
-
(display-page "Home")
-
(exit))))
-
(letn ( page-name (CGI:get "rename")
-
bdy (append {<p>You can rename the page } page-name {.</p>}
-
{<form action="index.cgi" method="post">}
-
{<input type="hidden" name="rename" value="} page-name {" />}
-
{<input type="hidden" name="continue" value="" />}
-
{<p>New page name: <input type="text" name="new" /></p>}
-
{<p>Do you really want to rename the page ? }
-
{<input type="submit" name="submit" class="button" value="Yes" /></p>}
-
{</form>}
-
{<p>You can cancel this action and get back to <a href="index.cgi">Home</a>.</p>}) )
-
(set ‘body bdy)
-
(set ‘page-name "Rename")
-
(CGI:put-page SETUP:template)
-
(exit))))
The request handling code can be splitted into two different parts. The first part starts in the line 23 and is responsible for displaying a confirmation message. User can confirm, that he wants to rename the page, or cancel the action. If user confirms, that the page should be renamed, the second part handles the request (it starts in the line 4). It simply changes all the references to the old page to new values and renames the page file (lines 19-22).
The code responsible for changing all the references to the old page name is shown below.
-
;; changes all references to the given page-old
-
;; to references to the given page-new
-
(define (change-refs page-old page-new)
-
(let ( page-old-filename (replace "_" (dup page-old 1) ".")
-
rexp (append {\[\[} page-old {\]\]})
-
subst (append {[[} page-new {]]})
-
files (slice (directory "pages/") 2) )
-
(dolist (fle files)
-
(let ( content (get-content fle) )
-
(if (find rexp content 0)
-
(begin
-
(replace rexp content subst 512)
-
(write-file (append "pages/" fle) content)))))))
Its job is simple: take a look at each file in the “pages” directory, and if you find some reference to the old page, change it to a reference to the new page.
The last thing I changed was adding a proper link to my request in the files table. It was so simple, that I think no code is needed to explain it.
After applying all those changes, I prepared a patch. Now everyone can modify newLISP wiki, and add TOC and rename functionality to this wonderful, small piece of software.
TOC for newLISP wiki
I was looking for some “whiteboard” to sketch my notes for many years. Right now, I think I’ve found it. newLISP wiki is a small and really simple piece of software. It doesn’t require any database server (IMO overkill for any small website/application), it doesn’t require installation of any web server (newLISP interpreter can be used for that purpose). It simply runs and does its job.
Nothing is perfect, though, and newLISP wiki is no exception. I’ve been using it for more than month now, and I can list features it lacks (from my POV, at least).
The most wanted feature for me is table of contents. newLISP wiki allows to easily use headings for denoting sections in pages, but when page content grows, it gets more and more difficult to find particular section. In such situation, table of contents (TOC) might be crucial. By TOC I mean a list of page headings with links. It helps to quickly get to any section (denoted with a heading), in a single click.
Although newLISP wiki, in its latest version, doesn’t support any kind of TOC, it’s open source, and thus can be extended by anyone who knows how to do it. Therefore I decided to add this feature on my own. In this article, I’ll try to document what exactly I did, and why.
First of all, almost all newLISP wiki code is placed in the index.cgi file. I wanted any “[toc]” tag to be replaced with a TOC, generated after parsing page’s code. In order to accomplish this task, I had to modify code responsible for generating the page HTML code. As I found out, the code was located in format-page function.
This function’s job is quite simple: it parses the given page content, replacing some tags/regular expressions with proper content. So, my job was simply to replace all found “[toc]” tags (actually, in most cases only one such tag will be used on page) with proper HTML content, generated while the page was being parsed. That’s not all, though. All the headings should be made links to the TOC (it’s an easy way to quickly get back to TOC), and should be made anchors. Of course, if no “[toc]” tag is used on a page, headings shouldn’t be made links/anchors. And the last thing: my modifications should not interfere with the existing code. I didn’t want to break anything.
How did I achieve it?
I decided to use newLISP contexts. By using new context for any information related to TOC, I could be sure that no existing code was broken. All I could break was my own code. Therefore, I decided to initialize the TOC context at the beginning of the format-page function, by calling my init-toc function:
-
;; creates an empty TOC context
-
(define (init-toc)
-
(set ‘TOC:toc (list SETUP:toc-start))
-
(set ‘TOC:lvl 1)
-
(set ‘TOC:anchor 0)
-
(set ‘TOC:found false))
Then, before the current line was parsed, I called my update-toc function:
-
;; checks the given line and updates TOC data
-
(define (update-toc line)
-
-
(define (append-toc content level)
-
(unless (> level SETUP:toc-level)
-
(while (> level TOC:lvl)
-
(push "<ul>" TOC:toc)
-
(++ TOC:lvl))
-
(while (< level TOC:lvl)
-
(push "</ul>" TOC:toc)
-
(– TOC:lvl))
-
(push (append SETUP:toc-elm-start {<a href="#a} (string TOC:anchor) {">} content {</a>} SETUP:toc-elm-end) TOC:toc)))
-
-
(cond
-
((find "======(.*)======" line 512) (append-toc $1 5))
-
((find "=====(.*)=====" line 512) (append-toc $1 4))
-
((find "====(.*)====" line 512) (append-toc $1 3))
-
((find "===(.*)===" line 512) (append-toc $1 2))
-
((find "==(.*)==" line 512) (append-toc $1 1))
-
((find {\[toc\]} line 512) (set ‘TOC:found true)) ))
This function’s job was simple: gather any information about all the found headings, and update the generated TOC.
I had also to modify the way all headings were processed, by inserting some additional tags to them (in order to easily find them all later). Therefore I wrote the function get-anchor, which was responsible for generating the needed HTML code:
-
;; returns an anchored content for TOC, if needed
-
;; this function also updates the anchor number
-
(define (get-anchor content level)
-
(if (> level SETUP:toc-level)
-
content
-
(let ( str (append {[TOC]<a name="a} (string TOC:anchor) {" href="#toc">[TOC]} content {[TOC]</a>[TOC]}) )
-
(++ TOC:anchor)
-
str)))
Finally, when the page content was parsed, I had to check, whether the “[toc]” tag was found. If it was found, the generated TOC HTML code should replace the tag, and all the headings should be made links (and anchors). However, if no “[toc]” was found, all the headings should be left unmodified. I code it all in finalize-toc function:
-
;; applies all the changes needed to properly display (or not) TOC
-
(define (finalize-toc)
-
(if TOC:found
-
(begin
-
(push SETUP:toc-end TOC:toc)
-
(reverse TOC:toc)
-
(let ( str-toc (join TOC:toc "") )
-
(replace {\[toc\]} page str-toc 512)
-
(replace {\[TOC\]</a>\[TOC\]} page "</a>" 512)
-
(replace {\[TOC\]<a name(.*)\[TOC\]} page (append "<a name" $1) 512)))
-
(begin
-
(replace {\[TOC\]</a>\[TOC\]} page "" 512)
-
(replace {\[TOC\]<a name.*\[TOC\]} page "" 512))))
And that’s could be all, folks. The code works (at least I haven’t found any bug so far), and I already use it. But I wasn’t very happy with the output I got. So I decided to add some CSS to make the generated TOC look better:
-
/* table of contents */
-
.toc {
-
border: 1px #117 dashed;
-
background-color: #eee;
-
}
Nothing special, I know, but newLISP wiki default look is also simple, and I didn’t want to break this convention.
Update: I realized, that the TOC feature for newLISP wiki is more complicated, and might need some more documentation.
The user’s documentation
The idea of table of contents is simple: collect the headers on a page, make them HTML anchors, and provide some table of contents with links to those headers. With the TOC feature, TOC is placed wherever the <code>[toc]</code> tag is found on a page.
It should be noted, that there are many kinds of headers. The TOC feature allows you to configure, to which level the TOC entries should be generated. You can set it in the pages/setup.lsp file, by setting the <code>toc-level</code> name to a number in range from 1 up to 5. By default it’s set to 2.
More advanced settings
You can also have full control on the HTML code that the TOC feature generates.
The TOC HTML code starts with string bound to the name <code>toc-start</code>. By default, it’s <code>{<div><a name=”toc”>Table of contents:</a><br /><ul>}</code>. The TOC ends with string bound to the name <code>toc-end</code>, which is by default <code>{</ul></div>}</code>.
Each TOC element (a link to some header found on a page) starts with HTML code bound to the name <code>toc-elm-start</code> and ends with <code>toc-elm-end</code>. By default, they’re set to <code><li></code> and <code></li></code>.

