{"id":3187,"date":"2014-04-03T15:40:55","date_gmt":"2014-04-03T21:40:55","guid":{"rendered":"http:\/\/bililite.com\/blog\/?p=3187"},"modified":"2014-04-10T23:34:55","modified_gmt":"2014-04-11T05:34:55","slug":"new-bililiterange-plugin-ex","status":"publish","type":"post","link":"https:\/\/bililite.com\/blog\/2014\/04\/03\/new-bililiterange-plugin-ex\/","title":{"rendered":"New bililiteRange plugin, <code>ex<\/code>"},"content":{"rendered":"<p>There have been lots of times that I've wanted to be able to keep my hand on the keyboard when editing, rather than running off to the mouse all the time. There's an <a href=\"http:\/\/coolwanglu.github.io\/vim.js\/web\/vim.html\">implementation of VIM in Javascript<\/a> but I figured I would learn something by doing it myself. My goal is <a href=\"http:\/\/en.wikipedia.org\/wiki\/Vi\u200e\">vi<\/a>, not <a href=\"http:\/\/en.wikipedia.org\/wiki\/Vim_(text_editor)\">vim<\/a>, since I don't need anything that sophisticated.<\/p>\r\n<p>The first step is implementing the line-oriented part of vi, called ex, based on the <a href=\"http:\/\/ex-vi.sourceforge.net\/ex.html\">manual<\/a> from the <a href=\"http:\/\/ex-vi.sourceforge.net\/\">sourceforge project<\/a>. My version is based on <a href=\"https:\/\/github.com\/dwachss\/bililiteRange\/blob\/master\/bililiteRange.js\">bililiteRange<\/a>, and depends on the <a href=\"https:\/\/github.com\/dwachss\/bililiteRange\/blob\/master\/bililiteRange.util.js\">bililiteRange utilities<\/a> and <a href=\"https:\/\/github.com\/dwachss\/bililiteRange\/blob\/master\/bililiteRange.undo.js\">undo plugin<\/a>.<\/p>\r\n\r\n<p>Use it simply as <code class=\"language-javascript\" >bililiteRange(textarea).ex('%s\/foo\/bar\/');<\/code>, passing the ex command to the <code class=\"language-javascript\" >ex()<\/code> function. The biggest difference from real ex is that this uses javascript regular expressions, rather than the original ex ones. Thus <code >s\/\\w\/x\/<\/code> rather than <code>s\/[:class:]\/x\/<\/code>, and use <code>?\/...\/<\/code> rather than <code>?...?<\/code> to search backwards (the question mark is used in Javascript regular expressions so I don't want to use it as a delimiter).<\/p>\r\n\r\n<p><a href=\"\/blog\/blogfiles\/vi\/ex.html\">See a demo<\/a>.<\/p>\r\n<p><a href=\"https:\/\/github.com\/dwachss\/bililiteRange\/blob\/master\/bililiteRange.ex.js\">See the code on github<\/a>.<\/p>\r\n\r\n<!--more-->\r\n\r\n<h3>Command Syntax<\/h3>\r\n<p>The syntax of the function is trivial: <code class=\"language-javascript\" >bililiteRange(someelement).ex(excommand {String} [, defaultAddress {String}])<\/code>, where <code class=\"language-javascript\" >excommand<\/code> is described below, and <code class=\"language-javascript\" >defaultAddress<\/code> is the address of the line to use if no address is given in the command string; the default is <code class=\"language-javascript\" >'.'<\/code>.<\/p>\r\n\r\n<p>Ex commands are in the form of <code>addressrange command variant parameter<\/code>; all parts are optional. White space can separate the parts but are not necessary if it is unambiguous. Multiple commands can be entered in one string, separated by <code>|<\/code>. To include <code>|<\/code> or other special characters in the <code>parameter<\/code> (that's the only place it would be relevant), enclose in double quotes, as a JSON string.Thus <code class=\"language-javascript\" >ex('a hello | a bye')<\/code> appends two lines, <code>hello<\/code> and <code >bye<\/code>, while <code class=\"language-javascript\" >ex('a \"hello | a bye\"')<\/code> appends one line, <code>hello | a bye<\/code>.<\/p>\r\n<p>The parser tries to imitate ex's too-clever-by-half automatic closing of regular expressions (and by extension, strings). This means that an address or a parameter that contains an unmatched <code>\/<\/code> or <code>\"<\/code> will have that delimiter added at the end. So <code class=\"language-javascript\" >ex('a his\/hers')<\/code> will in fact append <code>his\/hers\/<\/code>. Use JSON strings to avoid that problem. So <code class=\"language-javascript\" >ex('a \"his\/hers\"')<\/code> or <code class=\"language-javascript\" >ex('a \"his\\\"hers\"')<\/code>.<\/p>\r\n<p>Syntax errors or undefined commands are thrown as <code class=\"language-javascript\" >throw new Error(errormessage)<\/code>.<\/p>\r\n<p>Non-error messages (like the list of options from <code class=\"language-javascript\" >set<\/code>) are returned in a field in the range called <code class=\"language-javascript\" >exMessage<\/code>. Thus <code class=\"language-javascript\" >console.log( bililiteRange(element).ex('set').exMessage )<\/code>.<\/p>\r\n\r\n<p><code>variant<\/code> simply means optionally appending a <code>!<\/code> to the command name. For many commands, this changes the behavior slightly. E.g., <code>append text<\/code> respects the value of the <code>autoindent<\/code> option, but <code>append! text<\/code> does the <em>opposite<\/em> of the  <code>autoindent<\/code> option.<\/p>\r\n\r\n<h2>Command Completion<\/h2>\r\n<p>The parser first looks up the command in the object <code class=\"language-javascript\" >bililiteRange.ex.commands<\/code>. If it is defined as a function, executes it. If it is defined as a string, looks <em>that<\/em> value up in <code class=\"language-javascript\" >bililiteRange.ex.commands<\/code> with this algorithm (so infinite loops are possible!). If it not defined at all, treats it as a abbreviation and returns the first member of <code class=\"language-javascript\" >bililiteRange.ex.commands<\/code> for which the command is a prefix.<\/p>\r\n<p>So if <code class=\"language-javascript\" >bililiteRange.ex.commands<\/code> were:<\/p>\r\n<pre><code class=\"language-javascript\" >{\r\n  c: 'cut',\r\n  copy: function(){...}\r\n  cut: function(){...}\r\n}<\/code><\/pre>\r\n<p>Then <code>c<\/code> would execute the <code>cut<\/code> function and <code>co<\/code> would execute the copy function.<\/p>\r\n\r\n<h3>Addresses<\/h3>\r\n<p>Ex is line oriented; every command works on one or more complete lines, which are delimited by <code class=\"language-javascript\" >'|'<\/code>. Address ranges are of the form <code>x ([,;] x)*<\/code> meaning one or more address parts separated by commas or semicolons. Each address part (described below) may be followed by a positive or negative offset. Each address part evaluates to a line number, then the offset is added or subtracted to get the \"current line\". This is pushed onto a stack of lines numbers. The final range that the command operates on is the range of lines between the top two lines on that stack (inclusive). If only one address  part is given, then the range is that line alone. If there is no address, then the default address passed to <code class=\"language-javascript\">ex()<\/code> is used. The default for <em>that<\/em> is <code class=\"language-javascript\">'.'<\/code>.<\/p>\r\n\r\n<p>The \"starting line\" is the first line of the range passed in. Separating address parts with a semicolon instead of a comma resets the \"starting line\" to the \"current line\". Thus if the current line is line 4, <code class=\"language-javascript\" >'1,.+1'<\/code> means lines 1 through 5 but <code class=\"language-javascript\" >'1;.+1'<\/code> means lines 1 through 2.<\/p>\r\n\r\n<p>Address parts can be any of the following:<\/p>\r\n<dl>\r\n<dt>%%<\/dt>\r\n<dd>This is my own extension. Pushes both the starting and ending line of the original range onto the stack. If this is the only address, then does not change the range at all (useful for creating commands that are not line-oriented).<\/p>\r\n\r\n<dt>. (a single period)<\/dt>\r\n<dd>Pushes the current line.<\/dd>\r\n\r\n<dt>$<\/dt>\r\n<dd>Pushes the last line of the text.<\/dd>\r\n\r\n<dt>Any number<\/dt>\r\n<dd>Pushes that number<\/dd>\r\n\r\n<dt>'x (a single quote mark followed by a lower-case letter)<\/dt>\r\n<dd>Pushes the value of the corresponding mark (see the mark command)<\/dd>\r\n\r\n<dt>'' (two single quote marks)<\/dt>\r\n<dd>Pushes the current line of the previous command (so you can go back).<\/dd>\r\n\r\n<dt>%<\/dt>\r\n<dd>Equivalent to <code>0,$<\/code>; all the lines.<\/dd>\r\n\r\n<dt>\/re\/flags<\/dt>\r\n<dd>Searches for the regular expression <code class=\"language-javascript\" >\/re\/<\/code> from the current line. Pushes the first line found that matches, or the current line if not found. An empty regular expression (<code>\/\/<\/code>, or, since the parser closes regular expressions, just <code>\/<\/code>) uses the previous regular expression.<\/dd>\r\n<dd>The following flags are valid (this is an extension of real Javascript regular expressions):\r\n\r\n<ul>\r\n<li><code>i<\/code>: Force a case-insensitive search.<\/li>\r\n<li><code>I<\/code>: Force a case-<em>sensitive<\/em> search.<\/li>\r\n<li>If neither <code>i<\/code> or <code>I<\/code> is set, uses the value of the <code>ignorecase<\/code> option.<\/li>\r\n<li><code>m<\/code>: Use multiline mode (<code>^<\/code> and <code>$<\/code> match the end of lines rather than just the end of text).<\/li>\r\n<li><code>M<\/code>: Do not use multiline mode.<\/li>\r\n<li>By default, uses multiline mode.<\/li>\r\n<li><code>w<\/code>: Search wraps around to the beginning of the text if needed .<\/li>\r\n<li><code>W<\/code>: Search does not wrap around.<\/li>\r\n<li>If neither <code>w<\/code> or <code>W<\/code> is set, uses the value of the <code>wrapscan<\/code> option.<\/li>\r\n<li>The <code>g<\/code> flag is legal but is ignored (since this is only looking for the first match<\/li>\r\n<\/ul>\r\n<\/dl>\r\n<\/dd>\r\n\r\n<dt>?\/re\/flags<\/dt>\r\n<dd>Search backwards for <code class=\"language-javascript\" >\/re\/<\/code> as above.<\/dd>\r\n\r\n<\/dl>\r\n\r\n<h3>Commands<\/h3>\r\n<dl>\r\n\r\n<dt><code>append<\/code> or <code>a<\/code><\/dt>\r\n<dd>Inserts <code>parameter<\/code> as a new line after the address range. Add multiple lines with escaped newlines in a JSON string, as <code class=\"language-javascript\" >ex('a \"first\\nsecond\\nthird\"')<\/code>. If the <code>autoindent<\/code> option is set, copies any whitespace at the beginning of the current line to the beginning of each of the appended lines. If <code>variant<\/code> is set (i.e. <code>append!<\/code>), toggles the <code>autoindent<\/code> option for this command.<\/dd>\r\n\r\n<dt><code>change<\/code> or <\/code>c<\/code><\/dt>\r\n<dd>Replaces the address range with <code>parameter<\/code>, respecting the <code>autoindent<\/code> option as with <code>append<\/code> (and its variant). the deleted text is pushed onto a \"delete stack\", to be popped with <code>put<\/code>. This is the analogue of the clipboard in modern GUI's, but it is a stack--the contents can only be used once (but see the <code>delete<\/code> command with named registers).<\/dd>\r\n\r\n<dt><code>copy<\/code> or <code>transcribe<\/code> or <code>t<\/code><\/dt>\r\n<dd><code>parameter<\/code> is interpreted as an address range as above, and the text of the original addressed range is copied after that. Thus <code class=\"language-javascript\" >ex('1,2 copy 4')<\/code> copies lines 1  and 2 after line 4. Note that this is different from what modern GUI's call \"copy\", copying the text to the clipboard; for that use <code>yank<\/code>.<\/dd>\r\n\r\n<dt><code>delete<\/code> or <code>del<\/code><\/dt>\r\n<dd>Deletes the address range. Interprets <code>parameter<\/code> as an optional letter followed by an optional number. If the letter is not present, then the deleted text is pushed onto the delete stack. If a lower-case letter is present, that is taken as the name of a register and the text is stored there. If an upper-case letter is present, the deleted text is <em>appended<\/em> to the corresponding lower-case letter register. If the number is present, then the affected range is the last line of the address range and the n-1 following lines. Thus, <code>1,2 delete<\/code> deletes lines 1 and 2, but <code>1,2 delete 2<\/code> deletes lines 2 and 3.<\/dd>\r\n\r\n<dt><code>global<\/code> or <code>g<\/code><\/dt>\r\n<dd>This one is hard to explain. Interprets <code>parameter<\/code> as a regular expression followed by a command, then executes the command on all the lines in the address range that match the regular expression. If <code>variant<\/code> is set, then executes the command on all the lines that do <em>not<\/em> match the regular expression.<\/dd>\r\n<dd>For example, <code>1,4 g \/foo\/ d<\/code> would delete all lines from 1 to 4 that contain \"foo\". <code>% g! \/^\\d\/ s \/^\/\"\\t\"<\/code> would prepend a tab to every line that does not start with a digit.<\/dd>\r\n<dd>Since a <code>|<\/code> (vertical bar) is used to separate individual commands, we can't use that to separate the commands passed in <code>parameter<\/code>. The real ex uses newlines to separate those, but it's hard to type that, so I use the character <code>\\n<\/code> to separate commands. So, to insert a blank line before and prepend a tab to lines that start with a digit, use <code>% g! \/^\\d\/ i \\n s \/^\/\"\\t\"<\/code>.<\/dd>\r\n<dd>A fluke of the way I parse regular expressions is that flags to the search expression have to be at the <em>end<\/em> of <code>parameter<\/code>, like: <code>% g \/foo\/ d \/i<\/code>  to delete all lines that contain <code>foo<\/code>, case insensitive. Setting the <code>ignorecase<\/code> option beforehand would work as well.<dd>\r\n\r\n<dt><code>insert<\/code> or <code>i<\/code><\/dt>\r\n<dd>Same as <code>append<\/code>, but adds <code>parameter<\/code> <em>before<\/em> the address range.<\/dd>\r\n\r\n<dt><code>join<\/code> or <code>j<\/code><\/dt>\r\n<dd>Joins all the lines in the address range, collapsing whitespace at the beginning and end of the lines into a single space (uses <code class=\"language-javascript\" >replace(\/\\s*\\n\\s*\/g, ' ')<\/code>. If <code>variant<\/code> is set, don't adjust whitespace (uses <code class=\"language-javascript\" >replace(\/\\n\/, '')<\/code>. If <code>parameter<\/code> is present, interpreted as a number of lines to join; the address range is considered to be the last line of the actual address range and the next <code >parameter-1<\/code> lines.<\/dd>\r\n<dd>If there is only one line in the address range, joins it to the next line.<\/dd>\r\n\r\n<dt><code>mark<\/code> or <code>k<\/code><dt>\r\n<dd><code>parameter<\/code> should be a single lowercase letter (this is not enforced, but the only way to <em>refer<\/em> to a mark is with a single lowercase letter, so anything else is write-only). Assigns the current address range to that mark, and makes it live, so it will remain attached to that text even if the text around it is edited (see the description for <a href=\"http:\/\/bililite.com\/blog\/2013\/02\/08\/bililiterange-plugins\/\" title=\"bililiteRange Plugins\">bililiteRange.live<\/a>).<\/dd>\r\n\r\n<dt><code>move<\/code> or <code>m<\/code><\/dt>\r\n<dd>Same as <code>copy<\/code> above, but the original address range is deleted after copying.<\/dd>\r\n\r\n<dt><code>noglobal<\/code> or <code>v<\/code><\/dt>\r\n<dd>Same as <code>global!<\/code><\/dd>\r\n\r\n<dt><code>print<\/code><\/dt>\r\n<dd>Just selects the address range. If no command is given, this is the default.<\/dd>\r\n\r\n<dt><code>put<\/code><\/dt>\r\n<dd><code>parameter<\/code>, if present, should be a single lowercase letter (anything else is not an error, but there's no defined way to get anything into any other register). If <code>parameter<\/code> is present, then inserted text is from that register. If not, pop the delete stack. Then use <code>append<\/code> above to  insert that text.<\/dd>\r\n\r\n<dt><code>redo<\/code><\/dt>\r\n<dd>Does <a href=\"http:\/\/bililite.com\/blog\/2013\/12\/25\/bililiterange-undo\/\" title=\"bililiteRange undo\"><code class=\"language-javascript\" >bililiteRange(element).redo()<\/code><\/a>.<\/dd>\r\n\r\n<dt><code>set<\/code><\/dt>\r\n<dd>Sets or displays options. If <code>parameter<\/code> is not set, sets <code class=\"language-javascript\" >bililiteRange(element).exMessage<\/code> to <code class=\"language-javascript\" >JSON.stringify(bililiteRange(element).data())<\/code>. If <code>parameter<\/code> is set, it is interpreted as a space-delimited list of commands, in the following forms:\r\n<dl>\r\n<dt><code>option=value<\/code><\/dt><dd>Does the command <code>option value<\/code>. Note that each option is actually a command, with the parameter being the value to set that option to.<\/dd>\r\n<dt><code>option<\/code><\/dt><dd>Does the command <code>option on<\/code>.<\/dd>\r\n<dt><code>nooption<\/code> (that is, the literal string \"no\" followed by the name of the option)<\/dt><dd>Does the command <code>option no<\/code>.<\/dd>\r\n<dt><code>option?<\/code><\/dt><dd>Does <code>option ?<\/code>, which sets <code class=\"language-javascript\" >bililiteRange(element).exMessage<\/code> to the value of that option. Note that each option overwrites the previous, so only the last will be displayed.<\/dd>\r\n<\/dl>\r\n<\/dd>\r\n\r\n<dt><code>substitute<\/code> or <code>s<\/code> or <code>&<\/code><\/dt>\r\n<dd><code>parameter<\/code> is a regular expression followed by a string to be used as a replacement, as <code class=\"language-javascript\" >\/regexp\/replacement\/flags<\/code> (note that the flags are <em>after<\/em> the replacement, and the final delimiter is optional if there are no flags). Use JSON string notation if needed for the replacement, as <code class=\"language-javascript\" >\/regexp\/\"replacement_string\"<\/code> (adding <code class=\"language-javascript\" >\/flags<\/code> if needed).<\/dd>\r\n<dd>Note that as a quirk of the way I parse regular expressions, flags are read first, then the replacement string. That means that <code class=\"language-javascript\" >\/regexp\/i<\/code> is read as \"replace <code class=\"language-javascript\" >\/regexp\/i<\/code> with an empty string\", not \"replace <code class=\"language-javascript\" >\/regexp\/<\/code> with <code class=\"language-javascript\" >\"i\"<\/code>\".<\/dd>\r\n<dd>Just uses <code class=\"language-javascript\" >addressrange.text().replace(regexp, replacement)<\/code>, so <code>replacement<\/code> can use <code class=\"language-javascript\" >\"$1\"<\/code> etc.<\/dd>\r\n<dd>If no <code>parameter<\/code> is given, repeats the last search.<\/dd>\r\n\r\n<dt><code>undo<\/code><\/dt>\r\n<dd>Does <a href=\"http:\/\/bililite.com\/blog\/2013\/12\/25\/bililiterange-undo\/\" title=\"bililiteRange undo\"><code class=\"language-javascript\" >bililiteRange(element).undo()<\/code><\/a>.<\/dd>\r\n\r\n<dt><code>yank<\/code> or <code>y<\/code><\/dt>\r\n<dd>Does the same as <code>delete<\/code> above, moving the text into the register named in <code>parameter<\/code> or onto the delete stack, but does not delete the text. Analagous to <code>copy<\/code> in a modern GUI.<\/dd>\r\n\r\n<dt><code>=<\/code><\/dd>\r\n<dd>Sets <code class=\"language-javascript\" >exMessage<\/code> to the current lines; either <code class=\"language-javascript\">\"[n]\"<\/code> for a single line, or <code class=\"language-javascript\">\"[m,n]\"<\/code> for a range.<\/dd>\r\n\r\n<dt><code>~<\/code><\/dt>\r\n<dd>Search for the last replacement text (uses <code class=\"language-javascript\" >new RegExp()<\/code>, so special characters are not quoted), and replaces with <code>parameter<\/code>.<\/dd>\r\n\r\n<dt><code>&gt;<\/code><\/dt>\r\n<dd>Indents the lines of the address range by prepending a tab character. If <code>parameter<\/code> is set, prepend that many tabs.<\/dd>\r\n<dd>Feel free to start a tab-versus-spaces religious war.<\/dd>\r\n\r\n<dt><code>&lt;<\/code><\/dt>\r\n<dd>Unindents the lines of the address range by removing a leading tab character or <code>shiftwidth<\/code> spaces. If <code>parameter<\/code> is set, remove that many tabs or sets of spaces.<\/dd>\r\n\r\n<dt><code>!<\/code><\/dt>\r\n<dd>\"Shell\" escape&mdash;actually, Javascript escape. Does <code class=\"language-javascript\" >eval(parameter)<\/code>, with <code class=\"language-javascript\" >this<\/code> set to the bililiteRange covering the address range, and if the result is not <code class=\"language-javascript\" >undefined<\/code>, replaces the address range with that text. For instance, to change a line to upper case: <code>2 ! this.text().toUpperCase()<\/code>.<\/dd>\r\n\r\n<\/dl>\r\n\r\n<h3>Options<\/h3>\r\n<p>The option-setting commands are just commands of the form <code>option value<\/code> to set and <code>option ?<\/code> to return the value in <code class=\"language-javascript\" >exNessage<\/code>. For boolean options, <code>value<\/code> of <code>off<\/code>, <code>false<\/code> or <code>no<\/code> set it to <code class=\"language-javascript\" >false<\/code>; <code>toggle<\/code> toggles the value, and any other value (including leaving it blank) sets it to <code class=\"language-javascript\" >true<\/code>.\r\n\r\n<dl>\r\n<dt><code>autoindent<\/code> or <code>ai<\/code><\/dt>\r\n<dd>If <code class=\"language-javascript\" >true<\/code>, then <code>append<\/code>, <code>change<\/code> and <code>insert<\/code> will copy initial whitespace from the first line of the address range to all inserted lines. Use the variant (<code>append!<\/code> etc.) to toggle this for one command. Default: <code class=\"language-javascript\" >false<\/code>.<\/dd>\r\n\r\n<dt><code>shiftwidth<\/code> or <code>hardtabs<\/code> or <code>ht<\/code> or <code>sw<\/code> or <code>tabstop<\/code> or <code>ts<\/code> (in the real ex these are all different, but here they are all synonyms)<\/dt>\r\n<dd>Number of spaces in a tab. Uses <a href=\"http:\/\/css-tricks.com\/almanac\/properties\/t\/tab-size\/\"><code class=\"language-css\" >tabsize<\/code><\/a> to change the displayed text, if supported. Default: <code class=\"language-javascript\" >8<\/code>.<\/dd>\r\n\r\n<dt><code>wrapscan<\/code><\/dt>\r\n<dd>If <code class=\"language-javascript\" >true<\/code>, regular expression searches wrap around to the beginning of the text if not found from the current line to the end (for backward searches, wrap to the end if not found before the current line). Force wrap around by using the <code>w<\/code> flag; disable wraparound by using the <code>W<\/code> flag. Default: <code class=\"language-javascript\" >true<\/code>.<\/dd>\r\n<\/dl>\r\n\r\n<h3>State<\/h3>\r\n<p>The state of the editor (including the values of the options and the locations of the marks) are kept in the <code>data<\/code> attached to the element. Thus, calling <code class=\"language-javascript\" >ex()<\/code> on the same <em>element<\/em>, even if the <em>bililiteRange<\/em> is different, works correctly. That state is returned with <code class=\"language-javascript\" >bililiteRange(element).data()<\/code>, and you can extend the editor commands and take advantage of that object.<\/p>\r\n\r\n<p>The registers (the stored text from <code>delete<\/code> and <code>yank<\/code>) is stored in a singleton shared by all editor instances, called <code class=\"language-javascript\" >bililiteRange.ex.registers<\/code>. This is an array (so the delete stack just uses code class=\"language-javascript\" >bililiteRange.ex.registers.unshift(text)<\/code> and code class=\"language-javascript\" >text = bililiteRange.ex.registers.shift()<\/code>). Named registers are simply added to that array with code class=\"language-javascript\" >bililiteRange.ex.registers['a'] = text<\/code>. <code class=\"language-javascript\" >shift<\/code> and <code class=\"language-javascript\" >unshift<\/code> are used rather than <code class=\"language-javascript\" >push<\/code> and <code class=\"language-javascript\" >pop<\/code>, so the most recent text is code class=\"language-javascript\" >bililiteRange.ex.registers[0]<\/code>.<\/p>\r\n<p>Unfortunately, browser security keeps Javascript from directly manipulating the clipboard, so I can't integrate this with the browser's cut\/paste, but the fact that this is exposed means you can display it to the user for direct manipulation.<\/p>\r\n\r\n<h3>Extending<\/h3>\r\n<p>The <code class=\"language-javascript\" >bililiteRange.ex<\/code> namespace is used to expose some of the objects and methods that can extend the interpreter.<\/p>\r\n<dl>\r\n<dt><code class=\"language-javascript\" >bililiteRange.ex.commands<\/code><\/dt>\r\n<dd>Described above under \"Command Completion\". This is an object with the keys being the name of the command and the value either being string (to define a synonym, so you can do: <code class=\"language-javascript\" >bililiteRange.ex.commands.cut = 'delete'<\/code>) or a function with the signature <code class=\"language-javascript\" >function(parameter, variant)<\/code>. <code class=\"language-javascript\" >this<\/code> is set to the bililiteRange being edited, with bounds set to the address range. <code class=\"language-javascript\" >parameter<\/code> is a string, and <code class=\"language-javascript\" >variant<\/code> is a Boolean, set to <code class=\"language-javascript\" >true<\/code> if the command was followed by a \"!\".<\/dd>\r\n<dd>Legal command names are <code class=\"language-javascript\" >\/[a-zA-Z=&~&gt;&lt;]+\/<\/code> (letters and a few special characters; note that numbers and underscores are <em>not<\/em> legal). So \r\n<pre><code class=\"language-javascript\" >bililiteRange.ex.commands['hello~world'] = function (parameter, variant) { \/\/ note the tilde between 'hello' and 'world'\r\n  this.text( variant ? 'Hello, world' : 'Goodbye, world')\r\n}<\/code><\/pre> works, but <code class=\"language-javascript\" >bililiteRange.ex.commands['hello-world'] = function...<\/code>, while perfectly legal Javascript, will never be executed by ex since the parser will not recognize <code class=\"language-javascript\">'hello-world'<\/code> (with the dash).<\/dd>\r\n<dd>For instance, there's no <code>write<\/code> command; there are too many inconsistent ways to implement persistent storage. If you wanted to implement a <code>write<\/code> command using <a href=\"http:\/\/diveintohtml5.info\/storage.html\">local storage<\/a>, you could do:\r\n<pre><code class=\"language-javascript\" >\r\nbililiteRange.ex.createOption('file', 'Untitled'); \/\/ see the createOption method below\r\nbililite.ex.commands.write = function (parameter, variant){\r\n  var state = this.data(); \/\/ 'this' in a command is the bililiteRange\r\n  if (parameter) state.file = parameter; \/\/ allow changing the \"file\" name\r\n  var key = 'ex.'+state.file;\r\n  localStorage[key] = this.all();\r\n};\r\nbililite.ex.commands.read = function (parameter, variant){ \/\/ this isn't exactly the semantics of the original ex read command\r\n  if (!parameter) return; \/\/ need a file name\r\n  var key = 'ex.'+parameter;\r\n  if (!(key in localStorage)) throw new Error (parameter+' not found');\r\n  this.all(localStorage[key]);\r\n  this.data().file = parameter;\r\n}\r\n<\/code><\/pre>\r\n<\/dd>\r\n\r\n<dt><code class=\"language-javascript\" >bililiteRange.ex.createOption (name {String}, value {Any | undefined})<\/code><\/dt>\r\n<dd>Does <code class=\"language-javascript\" >bililiteRange.data(name, {value: value})<\/code>, then creates an <code>ex<\/code> command <code>name<\/code> for setting that option. The actual command created is based on the type of <code>value<\/code>, which is the default value. So <code class=\"language-javascript\" >bililiteRange.ex.createOption('happy', true)<\/code> creates a boolean option named <code>happy<\/code>, and this can be set with <code class=\"language-javascript\" >rng.ex('set happy=false')<\/code> or <code class=\"language-javascript\" >rng.ex('happy toggle')<\/code>.<\/dd>\r\n\r\n<dt><code class=\"language-javascript\" >bililiteRange.ex.createRE (s {String}, ignoreCase {Boolean})<\/code><\/dt>\r\n<dd>Creates an enhanced regular expression from <code>s<\/code>. Takes a string in the form expected by <code>substitute<\/code> above, meaning delimiter-regular-expression-string-delimiter-flags, with any of the flags allowed in the <code>\/re\/<\/code> addresses above. Note that the delimiter can actually be any character, though using <code class=\"language-javascript\" >\"\/\"<\/code> makes sense. <code>ignoreCase<\/code> is the default if the <code>i<\/code> or <code>I<\/code> flags are not used; normally you would use <code class=\"language-javascript\" >this.data().ignorecase<\/code>.<\/dd>\r\n<dd>If the regular expression part of <code>s<\/code> is missing (it just starts delimiter-delimiter) then it uses the regular expression part from the last <code>createRe()<\/code>\r\n<dd>The regular expression is formed with <code class=\"language-javascript\" >new RegExp('regular expression part of s', 'legal flags')<\/code>, where <code class=\"language-javascript\" >'legal flags'<\/code> are <code>i<\/code> and <code>m<\/code>; <code>g<\/code> never present. It is then extended with the following fields:\r\n<dl>\r\n<dt><code>flags<\/code><\/dt>\r\n<dd>The actual flags used internally (<code class=\"language-javascript\" >\/[imwIMW]*\/)<\/code>.<\/dd>\r\n<dt><code>rest<\/code><\/dt>\r\n<dd>The rest of the string, the part that was not part of the regular expression or the flags.<\/dd>\r\n<dt><code>toJSON<\/code><\/dt>\r\n<dd>Function used by <code class=\"language-javascript\" >JSON.stringify()<\/code> so that the regular expression can be displayed. Normal RegExp's are ignored by <code class=\"language-javascript\" >JSON.stringify()<\/code>.<\/dd>\r\n<\/dl>\r\n<\/dd>\r\n\r\n<dt><code class=\"language-javascript\" >bililiteRange.ex.splitCommands (s {String}, delim {String})<\/code><\/dt>\r\n<dd>Simple parser that basically does <code class=\"language-javascript\">s.split(delim)<\/code>, where <code>delim<\/code> is a string, not a regular expression, but knows about strings (contained in <code>\"<\/code>) and regular expressions (contained in <code>\/<\/code>) and does not split on a delimiter inside one of those. Also, to be consistent with <code>ex<\/code>, automatically closes open strings and regular expressions, which can cause problems if you <em>meant<\/em> to have a slash, not a regular expression. In that case, enclose the whole thing in quotes.<\/dd>\r\n<dd>An example: <code class=\"language-javascript\">bililiteRange.ex.splitCommands ('a=1 b=\"1 2\" c=\/3 4', ' ')<\/code> returns <code class=\"language-javascript\">['a=1', 'b=\"1 2\"', 'c=\/3 4\/']<\/code>. Note the automatic closing of <code>\/3 4\/<\/code>.<\/dd>\r\n\r\n<dt><code class=\"language-javascript\" >bililiteRange.ex.string (s {String})<\/code><\/dt>\r\n<dd>First, trims whitespace off the ends of <code>s<\/code>. If <code>s<\/code> starts with a quote mark (<code>\"<\/code>), then returns <code class=\"language-javascript\" >JSON.parse(s)<\/code>; otherwise returns the trimmed string. If <code>s<\/code> is undefined, returns the empty string.<\/dd>\r\n\r\n<dt><code class=\"language-javascript\" >bililiteRange.ex.toID (s {String})<\/code><\/dt>\r\n<dd>Encodes <code>s<\/code> so it is a legal ex identifier (like <code class=\"language-javascript\" >encodeURI<\/code>). Legal identifiers match <code class=\"language-javascript\" >\/(!|[a-zA-Z=&~><]+)\/<\/code>. Note that numbers are not allowed, and that a single exclamation point is, but not any other use of the exclamation point (that is used for command variants).<\/dd>\r\n<\/dl>\r\n\r\n<p>It has been education writing this; I'm not sure it will be useful to anyone but I am presenting it to my discerning public. If you exist.<\/p>","protected":false},"excerpt":{"rendered":"There have been lots of times that I've wanted to be able to keep my hand on the keyboard when editing, rather than running off to the mouse all the time. There's an implementation of VIM in Javascript but I figured I would learn something by doing it myself. My goal is vi, not vim, [&hellip;]","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[18,10],"tags":[],"_links":{"self":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3187"}],"collection":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/comments?post=3187"}],"version-history":[{"count":65,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3187\/revisions"}],"predecessor-version":[{"id":3355,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3187\/revisions\/3355"}],"wp:attachment":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/media?parent=3187"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/categories?post=3187"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/tags?post=3187"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}