gchp / iota

A terminal-based text editor written in Rust
MIT License
1.63k stars 81 forks source link

Transactional semantics for data modification #52

Open pythonesque opened 9 years ago

pythonesque commented 9 years ago

LAYERS

High level

Data modification should be of the form:

The ? is where we calculate the series of very simple operations from higher level actions. We would calculate cursor position here too, I think.

This is a very rough draft idea and I am highly open to changes.

"High level" user facing language example (semantics, not syntax!):

TRANSACTION1 := MAP (POSITION(character) WHERE LINE(character) = LINE($CURSOR) OR LINE(character) = NEXT(LINE($CURSOR))) (position => [])

TRANSACTION2 := DELETE character WHERE POSITION(character) = POSITION(NEXT($CURSOR))"

Note that this is data modification. Cursor math is entirely separate from data modification--such queries do not move the cursor at all intrinsically, but may be updated in individual transactions (which can see values updated by old transactions):

POSITION($CURSOR) := MAX(POSITION(character)) WHERE DELETED(TRANSACTION1,character)
crespyl commented 9 years ago

SQL for text? Hmmm...

Let me see if I'm understanding this correctly:

TRANSACTION1 := MAP (POSITION(character) WHERE LINE(character) = LINE($CURSOR) OR LINE(character) = NEXT(LINE($CURSOR))) (position => [])

POSITION(character) says we're collecting a SET of buffer offsets to individual characters LINE(thing) is the number of newlines preceding thing NEXT(number) is number + 1

This thing:

LINE(character) = LINE($CURSOR) OR LINE(character) = NEXT(LINE($CURSOR))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

is a conditional matching any characters (or buffer positions) on the same line as the cursor or one line below the cursor.

and this thing:

(position => [])
 ^^^^^^^^    ^^

describes a (function? relation? (same difference?)) between buffer positions and some value (nothing, in this case).

Together,

MAP ( <property-of-thing> WHERE <conditional-expression> ) ( <relation-from-property-of-thing-to-value> )

describes an operation in which all the matching <property-of-thing>s are sent to <relation-from-property-of-things-to-value>.

Finally, TRANSACTION1 now describes the equivalent of typing dj in a vi style editor.

Alternatively, one could replace (position => []) with (position => ['z']), and replace every character on both lines with 'z', or perhaps (position => UPPERCASE(CHARACTER(POSITION))) and obtain the vi operation gUj.

At some point, the editor can now take this labeled transformation and (compile?/optimize?) it before applying it to the buffer.

Is that about right?

This is a really interesting idea, and I'm having a lot of fun thinking about it, but I have to wonder what performance is going to be like, especially if we're going to be evaluating complex clauses very often. If pressing the delete key involves evaluating a WHERE clause that searches the entire buffer for one position, that seems sub-optimal. That said, I use SQL in my day job, and while I don't know much about the implementation details, the compile+optimize steps in existing SQL engines clearly shows that it can be done efficiently.

I think building all edit operations around something like this would be really cool, but we'll have to take care deciding which primitives to support and how to handle query optimization.

I posted some thoughts on a slightly different approach over in #31, though this seems much more general.

pythonesque commented 9 years ago

It's actually not terribly far off from the selection methods vim exposes. vim demonstrates important optimizations that make it fast, most of which come down to optimizing sensible defaults:

So most queries are only going to be looking at a handful of characters.

withoutboats commented 9 years ago

I think this is a really interesting idea & I'd like to see it pursued further.

vinipsmaker commented 9 years ago

Maybe implement an EDSL inspired by this. MongoDB-like declarative language which the compiler can check for (instead of running on SQL injections and runtime errors)?