Library for manipulating text ranges and selections, and assorted other programs that use that
bililiteRange.evim.js
puts all of the other projects together. It creates a text editor from an element, using
bililiteRange.ex.js
commands
and using my toolbar
project to create buttons, and my status
project to display messages and errors.
<div id=toolbar></div>
<textarea id=editor></textarea>
<div id=statusbar></div>
or
<div id=toolbar></div>
<pre contenteditable id=editor></pre>
<div id=statusbar></div>
rng.evim(document.getElementById('toolbar'), document.getElementById('statusbar'));
The first argument to evim is the element to contain the toolbar, and the second is the element to contain the message line and
the input element for ex
commands.
An editor with live Markdown conversion and some silly toolbar buttons and key mappings.
It includes a number of key mappings that are based on VIM, but that I found more useful.
All commands start with ctrl-o
(like evim).
There are really only four commands:
ctrl-o :
allows entry of any ex command. To accomodate my fat fingers, ctrl-o ;
, ctrl-o ctrl-:
and ctrl-o ctrl-;
also bring up the command entry box.ctrl-o [fF] any-character
Finds the next (for f
) or previous (for F
) occurence of the character. Works for printable characters
only, not space, tab or newline.ctrl-o [verb] [object]
uses bililiteRange
find
to select “objects”.ctrl-o j
joins lines.Possible values for verb
include:
range.bounds('to', object, true).bounds('endbounds')
t
: go to the end of the current object. Mnemonic: “to”. Uses range.bounds('to', object, false).bounds('endbounds')
b
: go to the beginning of the current object. Mnemonic: “back”. Uses range.bounds('from', object, false).bounds('startbounds')
B
: go to the end of the previous object. Mnemonic: “really back”. Uses range.bounds('from', object, true).bounds('startbounds')
i
: select the entire current object (but not the separators). Mnemonic: “inner”. Uses range.bounds('whole', object, false)
a
: select the entire current object and its separators (for some objects, only the ending separators). Mnemonic: “all”. Uses
range.bounds('whole', object, true)
; see that for which separators are selectedPossible values for object
include (see the documentation for the definitions of these objects):
w
: wordW
: bigwords
: sentencep
: paragraph[
: section(
: parentheses'
: single-quote delimited quote"
: double-quote delimited quote<
: angle-bracket delimited strings (uses rng.bounds('to', [/</, />/])
When evim
is called, rng.data.global
and rng.data.autoindent
are set to true
. A listener
for map
events is set up (see below) to create buttons and key mappings, and
rng.ex('source .exrc')
is executed. So rng.data.reader
should be set appropriately
before calling $.fn.ex
. For example, with the default reader
on localStorage
, you could do:
localStorage.setItem('.exrc', `
file untitled
set magic off
map ^s write
map ^b sendkeys "<strong>{selection}{mark}</strong>"
map! Save command=write observe=savestatus
`);
evim
allows for two kinds of map: map
creates key mappings, and map!
creates toolbar buttons. The ex
command map
triggers a map
event with a left hand side and a right hand side (they are separated by the first space; use
quotes to include spaces in the left hand side.
For key mappings, the left hand side is the key descriptor to pass to the keydown
handler, using my keymap
project. The right hand side is the ex
command to execute with the current selection as the default address.
So, in the .exrc
above, ctrl-S is mapped to range.ex('%%write')
(the %%
address means the current selection).
control-B is mapped to a sendkeys
command to surround the selection with the <strong>
tag.
To map multiple keys, put the left hand side in quotes:
rng.ex('map "^o j" join');
For buttons, the left hand side is the name of the button. There are two syntaxes for the right hand side. The simple one is just the command:
rng.ex('map! Hello append Hello');
This does:
toolbar.button(lefthandside, righthandside); // in this case, toolbar.button('Hello', 'append Hello');
The complex syntax is in the form: command="command string" title="Tooltip title" observe="attribute to observe"
(in any order, and all but command
are optional).
This does:
button = toolbar.button(lefthandside, command, title);
if (observe) toolbar.observerElement(button, observe);
So buttons can be set to observe changes in the element (remembering that monitored bililiteRange data changes attributes named, for instance data-foo
). The button will
then get a class equal to the value of the attribute of the range element named observe
.
So
rng.ex('map! Save command=write observe=data-savestatus' title="Save the text to the current file");`
will do:
button = toolbar.button('Save', 'write', 'Save the text to the current file');
toolbar.observerElement(button, 'data-savestatus');
and the savestatus
option is set to dirty
when the text is edited and clean
when it is saved, so the class of the button will change and you can use CSS to
style it appropriately.
See the documentation for toolbar
for the details about toolbars and buttons.