parinfer / parinfer.js

Let's simplify the way we write Lisp
https://shaunlebron.github.io/parinfer
MIT License
1.76k stars 40 forks source link

Parinfer.js

Pronounced "par-in-fur". "par" rhymes with car and star; "fur" rhymes with blur and stir

See the Parinfer Home Page for the original animated demo page.

Parinfer is a proof-of-concept editor mode for Lisp programming languages. It simplifies the way we write Lisp by auto-adjusting parens when indentation changes and vice versa. The hope is to make basic Lisp-editing easier for newcomers and experts alike, while still allowing existing plugins like Paredit to satisfy the need for more advanced operations.

npm package

This library is published on npm under the package name parinfer

## using npm
npm install parinfer

## using yarn
yarn add parinfer

Usage

Parinfer consists of a couple pure functions of your text, returning new text with corrected parens or indentation.

// 'parinfer' is a global object if not used as Node module.
var parinfer = require('parinfer');

// Run Indent Mode on the given text:
var result = parinfer.indentMode("(def foo [a b");
console.log(result.text);
// prints:
// (def foo [a b])

// Run Paren Mode on the given text:
var result = parinfer.parenMode("(def foo\n[a b\nc])");
console.log(result.text);
// prints:
// (def foo
//  [a b
//   c])

Integrating with an Editor or REPL

See integrating.md

API

smartMode(text[, options])
indentMode(text[, options])
parenMode(text[, options])

Runs Indent Mode or Paren Mode on the given text. Smart Mode is currently something in between.

Arguments:

Returns an object with the following properties:

Test API

You can use our testing API for a fast, visual way to specify options and verify results. This allows all metadata required by and returned from Parinfer to be specified inside the text using our annotation syntax.

See here for Annotation Syntax details

// Currently only supported in Node
var parinferTest = require('parinfer/test');

Test Example

The following code is a quick way to verify behavior of Indent Mode. The | is parsed as the cursor and removed from the text before processing.

parinterTest.indentMode(`
(def foo
  "|
  "(a b)
      c")
`);

This returns the processed text below, with | reinserted to show cursor result, and an ^ error annotation line since a string was not closed:

(def foo
  "|
  "(a b)
      c")
       ^ error: unclosed-quote

Test Usage

parinferTest.smartMode(inputText, extras); // returns string
parinferTest.indentMode(inputText, extras); // returns string
parinferTest.parenMode(inputText, extras);  // returns string

extras allows us to specify options for which there is no annotation syntax yet:

You can also use the input/output functions directly:

parinferTest.parseInput(inputText, extras); // returns {text, options}
parinferTest.parseOutput(inputText, extras); // returns result

parinferTest.printOutput(result, extras);   // returns string

// `result` is returned by main indentMode or parenMode functions

Development

Code: parinfer.js is implemented in ECMAScript 5 for easy speed and portability. Also:

Documentation: Code is documented in code.md.

Performance: To run a performance stress test:

node test/perf.js

Testing: See test/cases/ directory for testing details. Or just run the following:

npm install
npm test

A stable core for editor plugins

Want to use Parinfer on a team? Introduce Parlinter as your project's linter!

The behavior and implementation of the Parinfer library is stable and canonicalized. To allow different editors to use it, we have ported the implementation to the languages required by the plugin APIs of most major text editors. All language ports pass the same comprehensive test suite to help ensure consistent behavior.

implemented in link relevant editor
JavaScript parinfer.js (here) Atom, VSCode, LightTable
Rust parinfer-rust Vim
Python parinfer.py Sublime Text
Kotlin (JVM) parinfer-jvm Cursive IDE, Nightcode
Emacs Lisp parinfer-elisp Emacs
Vim Script parinfer-viml Vim
Lua parinfer-lua TextAdept

Status Update 2019 (Smart Mode)

Smart Mode (available in [demo]) was an experiment to eliminate switching between Indent Mode and Paren Mode—by looking at a change and determining whether to run Indent Mode or Paren Mode. It is well tested and worked great in our sandboxes, but we found that the majority of editor APIs do not allow us to integrate Smart Mode's rules safely.

For example, if we don't catch a search/replace change in multiple locations of your document, but we infer from the next typing operation that we should run Indent Mode, then Smart Mode will make its decision without knowing the previous search/replace operation took place—thereby breaking its promise of choosing the best mode, and unsafely modifying your code.

The larger problem is that Smart Mode requires the synchronous interception of every type of change coming from the editor. It must decide the right thing to do for input changes at single/multiple cursors, search/replace, copy/paste, advanced macro operations, buffer refreshes from changes on disk, and maybe some others we haven't thought of yet. The interface for receiving these kinds of changes from the editor are not consistent—they either come in asynchronously or sychronously or not at all. This forces us to resort to computing diffs, a lossy mapping from changes to patches.

We have made separate attempts to implement Smart Mode in Cursive, Vim, Atom, and Emacs through some wrangling that made integration very difficult and delicate, and ultimately incomplete. Editors simply are not yet designed to allow an ideal version of Parinfer to exist—probably because nothing like Parinfer has demanded them before. The practicality of requesting these (likely non-trivial) changes on the editor is to be determined.

License

MIT License