Move atom package out and start new readme

master
James Long 2017-01-10 00:45:11 -05:00
parent b553718aee
commit 612e3411cb
12 changed files with 205 additions and 379 deletions

231
README.md
View File

@ -1,167 +1,114 @@
# prettier
# Prettier
This is a JavaScript pretty-printer that is opinionated. All it takes
is a width to format the code to and it does the rest. Zero config: it
just works! Integrate this into your editor to get immediate feedback,
or run it across an entire project to format all your files.
Prettier is an opinionated JavaScript formatter. It removes all
original styling and ensures that all outputted JavaScript conforms to
a consistent style.
## Details
*Warning*: This is a **beta**, but should solidify fairly quickly.
This is a fork of [recast](https://github.com/benjamn/recast)'s
printer because it already handles a lot of edge cases like handling
comments. The core algorithm has been rewritten to be based on
Wadler's "[A prettier
printer](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)"
paper, however. Recast also supported only re-printing nodes that
changed from a transformation, but we avoid that and always
pretty-print the entire AST so it's always consistent.
This goes way beyond [eslint](http://eslint.org/) and other projects
[built on it](https://github.com/feross/standard). Unlike eslint,
there aren't a million configuration options and rules. But more
importantly: **everything is fixable**. This works because prettier
never "checks" anything; it takes JavaScript as input and outputs the
formatted JavaScript as output.
That paper allows a flexible formatting that will break expressions
across lines if they get too big. This means you can sloppily write
code as you need and just format it, and it will always produce
consistent output.
In technical terms: prettier parses your JavaScript into an AST and
pretty-prints the AST, completely ignoring any of the original
formatting. Say hello to completely consistent syntax!
The core of the algorithm is implemented in `pp.js`. The printer should
use the basic formatting abstractions provided to construct a format
when printing a node. Parts of the API only exist to be compatible
with recast's previous API to ease migration, but over time we can
clean it up.
There's an extremely important piece missing from existing styling
tools: **the maximum line length**. Sure, you can tell eslint to warn
you when you have a line that's too long, but that's an after-thought
(eslint *never* knows how to fix it). The maximum line length is a
critical piece the formatter needs for laying out and wrapping code.
The following commands are available:
* **concat**
Combine an array into a single string.
* **group**
Mark a group of items which the printer should try to fit on one line.
This is the basic command to tell the printer when to break. Groups
are usually nested, and the printer will try to fit everything on one
line, but if it doesn't fit it will break the outermost group first
and try again. It will continue breaking groups until everything fits
(or there are no more groups to break).
* **multilineGroup**
This is the same as `group`, but with an additional behavior: if this
group spans any other groups that have hard breaks (see below) this
group *always* breaks. Otherwise it acts the same as `group`.
For example, an array will try to fit on one line:
For example, take the following code:
```js
[1, "foo", { bar: 2 }]
foo(arg1, arg2, arg3);
```
However, if any of the items inside the array have a hard break, the
array will *always* break as well:
That looks like the right way to format it. However, we've all run
into this situation:
```js
[
foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne());
```
Suddenly our previous format for calling function breaks down because
this is too long. What you would probably do is this instead:
```
foo(
reallyLongArg(),
omgSoManyParameters(),
IShouldRefactorThis(),
isThereSeriouslyAnotherOne()
);
```
This clearly shows that the maximum line length has a direct impact on
the style of code we desire. The fact that current style tools ignore
this means they can't really help with the situations that are
actually the most troublesome. Individuals on teams will all format
these differently according to their own rules and we lose the
consistency we sought after.
Even if we disregard line widths, it's too easy to sneak in various
styles of code in all other linters. The most strict linter I know
happily lets all these styles happen:
```js
foo({ num: 3 },
1, 2)
foo(
{ num: 3 },
1, 2)
foo(
{ num: 3 },
1,
function() {
return 2
},
3
]
2
)
```
Functions always break after the opening curly brace no matter what,
so the array breaks as well for consistent formatting. See the
implementation of `ArrayExpression` for an example.
Prettier bans all custom styling by parsing it away and re-printing
the parsed AST with its own rules that take the maximum line width
into account, wrapping code when necessary.
* **join**
## Technical Details
Join an array of items with a separator.
This printer is a fork of
[recast](https://github.com/benjamn/recast)'s printer with it's
algorithm replaced by the one described by Wadler in "[A prettier
printer](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)".
There still may be leftover code from recast that needs to be cleaned
up.
* **line**
The basic idea is that the printer takes an AST and returns an
intermediate representation of the output, and the printer uses that
to generate a string. The advantage is that the printer can "measure"
the IR and see if the output is going to fit on a line, and break if
not.
Specify a line break. If an expression fits on one line, the line
break will be replaced with a space. Line breaks always indent the
next line with the current level of indentation.
This means that most of the logic of printing an AST involves
generating an abstract representation of the output involving certain
commands. For example, `concat(["(", line, arg, line ")"])` would
represent a concatentation of opening parens, an argument, and closing
parens. But if that doesn't fit on one line, the printer can break
where `line` is specified.
* **softline**
Specify a line break. The difference from `line` is that if the
expression fits on one line, it will be replaced with nothing.
* **hardline**
Specify a line break that is **always** included in the output, no
matter if the expression fits on one line or not.
* **literalline**
Specify a line break that is **always** included in the output, and
don't indent the next line. This is used for template literals.
* **indent**
Increase the level of indentation.
### Example
For an example, here's the implementation of the `ArrayExpression` node type:
```js
return multilineGroup(concat([
"[",
indent(options.tabWidth,
concat([
line,
join(concat([",", line]),
path.map(print, "elements"))
])),
line,
"]"
]));
```
This is a group with opening and closing brackets, and possibly
indented contents. Because it's a `multilineGroup` it will always be
broken up if any of the sub-expressions are broken.
## TODO
There is a lot to do:
1. Most importantly, finish the migration of recast's printing. Many
node types have not been converted from recast's old ways of doing
things, so need to finish converting them. The easiest way to do this
is search for `\n` in the printer; there should be no uses of it
because we use `line` instead. For example see
[`DoWhileStatement`](https://github.com/jlongster/jscodefmt/blob/master/src/printer.js#L928).
2. Remove any cruft leftover from recast that we don't need
3. Polish the API (it was currently designed to be "usable" and compatible with what recast did before)
4. Better editor integration
5. Better CLI
## Contributing
```
$ git clone https://github.com/jlongster/jscodefmt.git
$ cd jscodefmt
$ npm install
$ ./bin/jscodefmt file.js
```
## Tests
A few snapshot tests are currently implemented. See `tests`. To run
the tests simply run `npm test` in the root directory.
More (rough) details can be found in [commands.md](commands.md).
Better docs will come soon.
## Editors
It's most useful when integrated with your editor, so see `editors` for
editor support. Atom and Emacs is currently supported.
Currently atom and emacs support is provided. Atom users can simply
install the `prettier-atom` package and use ctrl+alt+f to format a
file (or format on save if turned on). Emacs users should see [this
folder](https://github.com/jlongster/prettier/tree/master/editors/emacs).
More docs on editor integration will come soon. To integrate in Emacs,
add the following code. This will format the file when saved.
## Contributing
```elisp
(require 'jscodefmt)
(add-hook 'js-mode-hook
(lambda ()
(add-hook 'before-save-hook 'jscodefmt-before-save)))
```

105
commands.md Normal file
View File

@ -0,0 +1,105 @@
This is very rough documentation of the formatting commands you can
use to build a printed version of something. This will be improved
over time.
The core of the algorithm is implemented in `pp.js`. The printer should
use the basic formatting abstractions provided to construct a format
when printing a node. Parts of the API only exist to be compatible
with recast's previous API to ease migration, but over time we can
clean it up.
The following commands are available:
### concat
Combine an array into a single string.
### group
Mark a group of items which the printer should try to fit on one line.
This is the basic command to tell the printer when to break. Groups
are usually nested, and the printer will try to fit everything on one
line, but if it doesn't fit it will break the outermost group first
and try again. It will continue breaking groups until everything fits
(or there are no more groups to break).
### multilineGroup
This is the same as `group`, but with an additional behavior: if this
group spans any other groups that have hard breaks (see below) this
group *always* breaks. Otherwise it acts the same as `group`.
For example, an array will try to fit on one line:
```js
[1, "foo", { bar: 2 }]
```
However, if any of the items inside the array have a hard break, the
array will *always* break as well:
```js
[
1,
function() {
return 2
},
3
]
```
Functions always break after the opening curly brace no matter what,
so the array breaks as well for consistent formatting. See the
implementation of `ArrayExpression` for an example.
### join
Join an array of items with a separator.
### line
Specify a line break. If an expression fits on one line, the line
break will be replaced with a space. Line breaks always indent the
next line with the current level of indentation.
### softline
Specify a line break. The difference from `line` is that if the
expression fits on one line, it will be replaced with nothing.
### hardline
Specify a line break that is **always** included in the output, no
matter if the expression fits on one line or not.
### literalline
Specify a line break that is **always** included in the output, and
don't indent the next line. This is used for template literals.
### indent
Increase the level of indentation.
## Example
For an example, here's the implementation of the `ArrayExpression` node type:
```js
return multilineGroup(concat([
"[",
indent(options.tabWidth,
concat([
line,
join(concat([",", line]),
path.map(print, "elements"))
])),
line,
"]"
]));
```
This is a group with opening and closing brackets, and possibly
indented contents. Because it's a `multilineGroup` it will always be
broken up if any of the sub-expressions are broken.

1
editors/atom.md Normal file
View File

@ -0,0 +1 @@
See http://github.com/jlongster/prettier-atom

8
editors/emacs/README.md Normal file
View File

@ -0,0 +1,8 @@
Add this to your init:
```elisp
(require 'prettier-js)
(add-hook 'js-mode-hook
(lambda ()
(add-hook 'before-save-hook 'jscodefmt-before-save)))
```

View File

@ -1,4 +1,4 @@
;;; prettier.el --- utility functions to format reason code
;;; prettier-js.el --- utility functions to format reason code
;; Copyright (c) 2014 The go-mode Authors. All rights reserved.
;; Portions Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
@ -208,7 +208,5 @@ function."
(delete-file bufferfile)
(delete-file outputfile)))
(provide 'prettier)
;;; prettier.el ends here
(provide 'prettier-js)

View File

@ -1,29 +0,0 @@
## 2.4.0 - Support `standard --fix`
- Use `standard --fix` as the default formatter for Standard Style
- Keep existing `standard-format` module as a formatter option
## 1.0.0 - Honor Package Settings
- Format On Save honors any `ignore` configuration in the file's nearest package.json.
- JSX files are supported
## 0.7.0 - Multiple Styles
- Support standard and semi-standard styles using settings
- Fix: Editor selection is ignored when formatting on save
## 0.6.0 - Format selection
- If a text selection is made, `ctrl-alt-f` will format the selection only. Otherwise
`ctrl-alt-f` will format file contents as normal.
## 0.5.1 - Cursor position and syntax error handling
- Maintain cursor position after format/save
- Catch errors thrown by transform due to syntax errors
## 0.5.0 - Format on save and Javascript instead of Coffeescript
- **Format on save**: Added setting to enable format on save. Defaults to off.
- **Less irony**: This package is now written in Javascript using Javascript Standard Style.
- **Faster startup time**
## 0.1.0 - First Release
* Use [standard-format](https://github.com/maxogden/standard-format) to format current Javascript file
* `ctrl-alt-f` keybinding

View File

@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) Stephen Kubovic
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,23 +0,0 @@
# Prettier formatter for Atom
Atom package to format your Javascript using [Prettier](https://github.com/jlongster/prettier).
### Usage
#### Keybindings
Use `ctrl-alt-f` to format the current Javascript file. If a text selection is made, only the selected text will be formatted.
#### Format On Save
Automatically format your Javascript file on save by enabling the *Format On Save* package setting. This is off by default.
#### Menu
*Packages > standard-formatter > Format*
### Settings
#### formatOnSave (default: false)
Format Javascript files when saving.

View File

@ -1,5 +0,0 @@
{
"atom-text-editor": {
"ctrl-alt-f": "prettier:format"
}
}

View File

@ -1,109 +0,0 @@
/* global atom */
var path = require("path");
var findRoot = require("find-root");
var prettier = require("prettier");
module.exports = {
style: null,
fileTypes: [ ".js", ".jsx" ],
fileSupported: function(file) {
// Ensure file is a supported file type.
var ext = path.extname(file);
return !!~this.fileTypes.indexOf(ext);
},
activate: function() {
this.commands = atom.commands.add(
"atom-workspace",
"prettier:format",
function() {
this.format();
}.bind(this)
);
this.editorObserver = atom.workspace.observeTextEditors(
this.handleEvents.bind(this)
);
},
deactivate: function() {
this.commands.dispose();
this.editorObserver.dispose();
},
format: function(options) {
if (options === undefined) {
options = {};
}
var selection = typeof options.selection === "undefined"
? true
: !!options.selection;
var editor = atom.workspace.getActiveTextEditor();
if (!editor) {
// Return if the current active item is not a `TextEditor`
return;
}
var selectedText = selection ? editor.getSelectedText() : null;
var text = selectedText || editor.getText();
var cursorPosition = editor.getCursorScreenPosition();
try {
var transformed = prettier.format(text, {
printWidth: options.printWidth
});
} catch (e) {
console.log("Error transforming using prettier:", e);
transformed = text;
}
if (selectedText) {
editor.setTextInBufferRange(editor.getSelectedBufferRange(), transformed);
} else {
editor.setText(transformed);
}
editor.setCursorScreenPosition(cursorPosition);
},
handleEvents: function(editor) {
editor.getBuffer().onWillSave(
function() {
var path = editor.getPath();
if (!path)
return;
if (!editor.getBuffer().isModified())
return;
var formatOnSave = atom.config.get("prettier-atom.formatOnSave", {
scope: editor.getRootScopeDescriptor()
});
if (!formatOnSave)
return;
// Set the relative path based on the file's nearest package.json.
// If no package.json is found, use path verbatim.
var relativePath;
try {
var projectPath = findRoot(path);
relativePath = path.replace(projectPath, "").substring(1);
} catch (e) {
relativePath = path;
}
if (this.fileSupported(relativePath)) {
this.format({ selection: false });
}
}.bind(this)
);
// Uncomment this to format on resize. Not ready yet. :)
//
// if (editor.editorElement) {
// window.addEventListener("resize", e => {
// const { width } = window.document.body.getBoundingClientRect();
// const columns = width /
// editor.editorElement.getDefaultCharacterWidth() |
// 0;
// console.log(width, columns);
// this.format({ selection: false, printWidth: columns });
// });
// }
},
config: { formatOnSave: { type: "boolean", default: false } }
};

View File

@ -1,26 +0,0 @@
{
"context-menu": {
"atom-text-editor[data-grammar='source js']": [
{
"label": "Format With prettier",
"command": "prettier:format"
}
]
},
"menu": [
{
"label": "Packages",
"submenu": [
{
"label": "prettier",
"submenu": [
{
"label": "Format",
"command": "prettier:format"
}
]
}
]
}
]
}

View File

@ -1,21 +0,0 @@
{
"name": "prettier-atom",
"main": "./lib/prettier.js",
"version": "0.0.1",
"description": "Format file contents using prettier",
"keywords": [
"javascript",
"formatter"
],
"activationCommands": [],
"repository": "https://github.com/jlongster/prettier",
"license": "MIT",
"engines": {
"atom": ">=0.174.0 <2.0.0"
},
"dependencies": {
"find-root": "^0.1.1",
"prettier": "0.0.2"
},
"devDependencies": {}
}