Skip to content

simon-brooke/fedit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 

Repository files navigation

fedit

An in-core editor for Clojure data structures and, particularly, functions.

Usage

Preliminary, incomplete, alpha quality code. This implements a structure editor in a terminal, not a display editor in the tradition of InterLisp's DEdit. I do intend to follow up with a display editor, but this is exploratory proof-of-concept code.

Note that to work with this, you need to start with

lein trampoline repl

rather than just

lein repl

I've read explanations of why this is, but I don't claim to fully understand them. Treat it as magic, but trust me on this.

To edit an arbitrary s-expression:

(sedit sexpr)

This pretty much works now; it returns an edited copy of the s-expression. Maps are not yet handled.

To edit a function definition

(fedit 'name-of-function)

NOTE: This is broken, see working notes below; but it is showing promise.

Still to do

Function metadata

Currently, Clojure metadata on a function symbol as follows:

{
	:arglists ([sexpr]),
	:ns #<Namespace fedit.core>,
	:name sedit, :column 1,
	:doc "Edit an S-Expression, and return a modified version of it",
	:line 74,
	:file "fedit/core.clj"
}

In order to be able to recover the source of a function which has not yet been committed to the file system, it would be necessary to store the source s-expression on the metadata. You cannot add new metadata to an existing symbol (? check this), but, again, as the package reloader effectively does so, there must be a way, although it may be dark and mysterious. Obviously if we're smashing and rebinding the function's compiled form we're doing something dark and mysterious anyway.

Generating/persisting packages

Editing a function which is in an existing package has problems associated with it. We cannot easily save it back to its original file, as that will throw out the line numbering of every other definition in the file. Also, critically, files contain textual comments which are not read by the reader, and consequently would be smashed by overwriting the old definition with the new definition.

Consequently I'm thinking that a revised package manager for Clojure-with-in-core-editing should generate new packages with names of the form packagename_serial; that when files are edited in core, the serial number should be incremented to above the highest existing serial number for that package, and the new package (with the new serial number) should depend on the next-older version of the package (obviously, recursively). The function (use 'packagename) should be rewritten so if passed a package name without a version number part, it would seek the highest numbered version of the specified package available on the path.

At the end of a Clojure session (or, actually, at any stage within a session) the user could issue a directive

(persist-edits)

Until this directive had been called, none of the in-core edits which had been made in the session would be saved. When the directive was made, the persister would go through all functions/symbols which had been edited during the session, and if they had package metadata would immediately save them; if they had no package metadata would prompt for it.

Working notes

20130919 13:20

The function 'source-fn' in package 'clojure.repl' returns, as a string, the source of the function (or macro) whose name is passed to it as argument. It does this by checking the metadata associated with the function object using the 'meta' function. This metadata (if present) is a map containing the keys ':file' and ':line'. I'm guessing, therefore, that this metadata is set up while reading the source file.

The function 'read-string' can be used to parse a string into an S-expression. I'm taking it as read that the string returned by source-fn will always be a single well-formed S-expression.

As a first pass, I'll write a function sedit which takes an s-expression as argument, pretty prints it to the screen, and awaits a key stroke from the user. The following keys will be recognised:

  • A: ['CAR'] call sedit recursively on the CAR of the current s-expression; return a cons of the result of this with the cdr of the current s-expression. Obviously, only available if the current s-expression is a list with at least one element.
  • D: ['CDR'] call sedit recursively on the CDR of the current s-expression; return a cons of the CAR of the current s-expression with the result of this. Obviously, only available if the current s-expression is a list.
  • S: ['Substitute'] read a new s-expression from the user and return it in place of the current s-exression
  • X: ['Cut'] return nil.

20130920 10:37

Now sort-of working. One change can be made to an s-expression, and it can be made anywhere in the s-expression. For some reason having made one change you can't then navigate further into the s-expression to make another change; I suspect this is a lazy-evaluation problem, but I haven't yet fixed it.

Also, the 'clear screen' functionality is extremely crude, and you have to type a carriage return after every command character, which slows down the user interaction badly. For a proof-of-concept demonstrator that isn't critical, but if anyone is actually going to use this thing it needs to be fixed.

I've written a wrapper round sedit called fedit, which grabs the source of a function from its metadata and passes it to sedit, attempting to redefine the function from the result; this fails for the basic clojure 'all data is immutable' reason. But, when you invoke (use 'namespace :reload), it is able to redefine all the functions, so I must be able to work out how this is done.

License

Copyright © 2013 Simon Brooke stillyet-github@googlemail.com

Distributed under the Eclipse Public License, the same as Clojure.

About

fedit - An in-core editor for Clojure data structures and, particularly, functions.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published