Merge pull request #17 from Glavin001/master

Improve usage and support
This commit is contained in:
László Károlyi 2014-06-13 18:56:19 +02:00
commit fe0dc7bccf
5 changed files with 260 additions and 76 deletions

View File

@ -1,19 +1,105 @@
# atom-beautify package # [atom-beautify](https://github.com/donaldpipowitch/atom-beautify)
[Beautify](https://github.com/einars/js-beautify) HTML, CSS and Javascript in Atom. > [Beautify](https://github.com/einars/js-beautify)
HTML (including [Handlebars](http://handlebarsjs.com/)),
CSS (including [Sass](http://sass-lang.com/) and [LESS](http://lesscss.org/))
and JavaScript in Atom.
*Attention*: A different package with a similar name exist. Maybe you want to visit this one: [Beautifier](https://atom.io/packages/atom-beautifier). ## Language Support
- JavaScript
- HTML, including
- [Handlebars](http://handlebarsjs.com/)
- XML is supported as an *experimental feature*.
- CSS, including
- [Sass](http://sass-lang.com/)
- [LESS](http://lesscss.org/)
## Usage ## Usage
Open the Command Palette, and type `Beautify`. This will beautify JS, HTML or CSS files. (XML is supported as an experimental feature.) It will only beautify selected text, if a selection is found - if not, the whole file will be beautified. Open the [Command Palette](https://github.com/atom/command-palette), and type `Beautify`.
It will only beautify selected text, if a selection is found - if not, the whole file will be beautified.
### Shortcut
You can also type `ctrl-alt-b` as a shortcut or click `Packages > Beautify` in the menu. You can also type `ctrl-alt-b` as a shortcut or click `Packages > Beautify` in the menu.
### Package Options
You can also choose to beautify on every file save. You can also choose to beautify on every file save.
You can change the current config by either using `.jsbeautifyrc` for all files or a non-standard variante with settings for every file type. You can see examples of both way inside [`examples/`](https://github.com/donaldpipowitch/atom-beautify/examples)
## Configuration
Edit your `.jsbeautifyrc` file in any of the following locations:
- Same directory as current file
- Project root
`atom-beautify` will recursively look up from the current file's directory to find `.jsbeautifyrc`.
- Your User's Home directory
**Note**: *Comments are supported in `.jsbeautifyrc` thanks to [strip-json-comments](https://github.com/sindresorhus/strip-json-comments).*
See examples of both way inside [`examples/`](https://github.com/donaldpipowitch/atom-beautify/tree/master/examples)
### Simple
See [examples/simple-jsbeautifyrc/.jsbeautifyrc](https://github.com/donaldpipowitch/atom-beautify/blob/master/examples/simple-jsbeautifyrc/.jsbeautifyrc).
```json
{
"indent_size": 2,
"indent_char": " ",
"other": " ",
"indent_level": 0,
"indent_with_tabs": false,
"preserve_newlines": true,
"max_preserve_newlines": 2,
"jslint_happy": true,
"indent_handlebars": true
}
```
### Nested
See [examples/nested-jsbeautifyrc/.jsbeautifyrc](https://github.com/donaldpipowitch/atom-beautify/blob/master/examples/nested-jsbeautifyrc/.jsbeautifyrc).
```json
{
"html": {
"brace_style": "collapse",
"indent_char": " ",
"indent_scripts": "normal",
"indent_size": 6,
"max_preserve_newlines": 1,
"preserve_newlines": true,
"unformatted": ["a", "sub", "sup", "b", "i", "u"],
"wrap_line_length": 0
},
"css": {
"indent_char": " ",
"indent_size": 4
},
"js": {
"indent_size": 2,
"indent_char": " ",
"indent_level": 0,
"indent_with_tabs": false,
"preserve_newlines": true,
"max_preserve_newlines": 2,
"jslint_happy": true
}
}
```
## Contributing
[See all contributors on GitHub](https://github.com/donaldpipowitch/atom-beautify/graphs/contributors).
Please update the [CHANGELOG.md](https://github.com/donaldpipowitch/atom-beautify/blob/master/CHANGELOG.md)
file and submit a [Pull Request on GitHub](https://help.github.com/articles/using-pull-requests).
## License ## License
MIT © [Donald Pipowitch](https://github.com/donaldpipowitch) [MIT](https://github.com/donaldpipowitch/atom-beautify/blob/master/LICENSE.md) © [Donald Pipowitch](https://github.com/donaldpipowitch)

View File

@ -1,9 +1,11 @@
{ {
"indent_size": 2, "indent_size": 2,
"indent_char": " ", "indent_char": " ",
"other": " ",
"indent_level": 0, "indent_level": 0,
"indent_with_tabs": false, "indent_with_tabs": false,
"preserve_newlines": true, "preserve_newlines": true,
"max_preserve_newlines": 2, "max_preserve_newlines": 2,
"jslint_happy": true "jslint_happy": true,
"indent_handlebars": true
} }

View File

@ -0,0 +1,10 @@
{{#if}}
{{#each}}
{{#if}}
content
{{/if}}
{{#if}}
content
{{/if}}
{{/each}}
{{/if}}

View File

@ -18,6 +18,7 @@ var knownOpts = {
'indent_char': String, 'indent_char': String,
'indent_level': Number, 'indent_level': Number,
'indent_with_tabs': Boolean, 'indent_with_tabs': Boolean,
'indent_handlebars': Boolean,
'preserve_newlines': Boolean, 'preserve_newlines': Boolean,
'max_preserve_newlines': Number, 'max_preserve_newlines': Number,
'space_in_paren': Boolean, 'space_in_paren': Boolean,
@ -49,24 +50,8 @@ var Subscriber = require('emissary').Subscriber;
var plugin = module.exports; var plugin = module.exports;
Subscriber.extend(plugin); Subscriber.extend(plugin);
function verifyExists(fullPath) {
return fs.existsSync(fullPath) ? fullPath : null;
}
function findRecursive(dir, fileName) {
var fullPath = path.join(dir, fileName);
var nextDir = path.dirname(dir);
var result = verifyExists(fullPath);
if (!result && (nextDir !== dir)) {
result = findRecursive(nextDir, fileName);
}
return result;
}
function getUserHome() { function getUserHome() {
return process.env.HOME || process.env.USERPROFILE; return process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
} }
function cleanOptions(data, types) { function cleanOptions(data, types) {
@ -96,14 +81,84 @@ function setCursors(editor, posArray) {
} }
} }
function verifyExists(fullPath) {
return fs.existsSync(fullPath) ? fullPath : null;
}
// Storage for memoized results from find file
// Should prevent lots of directory traversal &
// lookups when liniting an entire project
var findFileResults = {};
/**
* Searches for a file with a specified name starting with
* 'dir' and going all the way up either until it finds the file
* or hits the root.
*
* @param {string} name filename to search for (e.g. .jshintrc)
* @param {string} dir directory to start search from (default:
* current working directory)
*
* @returns {string} normalized filename
*/
function findFile(name, dir) {
dir = dir || process.cwd();
var filename = path.normalize(path.join(dir, name));
if (findFileResults[filename] !== undefined) {
return findFileResults[filename];
}
var parent = path.resolve(dir, '../');
if (verifyExists(filename)) {
findFileResults[filename] = filename;
return filename;
}
if (dir === parent) {
findFileResults[filename] = null;
return null;
}
return findFile(name, parent);
}
/**
* Tries to find a configuration file in either project directory
* or in the home directory. Configuration files are named
* '.jsbeautifyrc'.
*
* @param {string} config name of the configuration file
* @param {string} file path to the file to be linted
* @returns {string} a path to the config file
*/
function findConfig(config, file) {
var dir = path.dirname(path.resolve(file));
var envs = getUserHome();
var home = path.normalize(path.join(envs, config));
var proj = findFile(config, dir);
if (proj) {
return proj;
}
if (verifyExists(home)) {
return home;
}
return null;
}
function beautify() { function beautify() {
var text; var text;
var editor = atom.workspace.getActiveEditor(); var editor = atom.workspace.getActiveEditor();
var isSelection = !! editor.getSelectedText(); var isSelection = !! editor.getSelectedText();
var softTabs = editor.softTabs; var softTabs = editor.softTabs;
var tabLength = editor.getTabLength(); var tabLength = editor.getTabLength();
var beautifyOptions = { var defaultOptions = {
'indent_size': softTabs ? tabLength : 1, 'indent_size': softTabs ? tabLength : 1,
'indent_char': softTabs ? ' ' : '\t', 'indent_char': softTabs ? ' ' : '\t',
'indent_with_tabs': !softTabs 'indent_with_tabs': !softTabs
@ -111,53 +166,32 @@ function beautify() {
// Look for .jsbeautifierrc in file and home path, check env variables // Look for .jsbeautifierrc in file and home path, check env variables
var editedFilePath = editor.getPath(); var editedFilePath = editor.getPath();
var rcInRecursiveCwd;
if (editedFilePath && (rcInRecursiveCwd = findRecursive(path.dirname(
editedFilePath), '.jsbeautifyrc')) === editedFilePath) {
rcInRecursiveCwd = null;
}
var rcInHomePath;
if (editedFilePath && (rcInHomePath = verifyExists(path.join(getUserHome() ||
'', '.jsbeautifyrc'))) === editedFilePath) {
rcInHomePath = null;
}
var externalOptions; function getConfig(startPath) {
// Verify that startPath is a string
startPath = (typeof startPath === 'string') ? startPath : __dirname || '';
// Get the path to the config file
var configPath = findConfig('.jsbeautifyrc', startPath);
if (rcInRecursiveCwd) { var externalOptions;
externalOptions = JSON.parse(fs.readFileSync(rcInRecursiveCwd, { if (configPath) {
encoding: 'utf8' var strip = require('strip-json-comments');
})); try {
} else if (rcInHomePath) { externalOptions = JSON.parse(strip(fs.readFileSync(configPath, {
externalOptions = JSON.parse(fs.readFileSync(rcInHomePath, { encoding: 'utf8'
encoding: 'utf8' })));
})); } catch (e) {
} else { externalOptions = {};
externalOptions = {}; }
} } else {
externalOptions = {};
var containsNested = false;
var collectedConfig = {};
var key;
// Check to see if config file uses nested object format to split up js/css/html options
for (key in externalOptions) {
if (typeof externalOptions[key] === 'object') {
containsNested = true;
} }
return externalOptions;
} }
// Create a flat object of config options if nested format was used // Get the path to the config file
if (!containsNested) { var projectOptions = getConfig(editedFilePath);
collectedConfig = externalOptions; var homeOptions = getConfig(getUserHome());
} else {
for (key in externalOptions) {
_.merge(collectedConfig, externalOptions[key]);
}
}
beautifyOptions = extend(collectedConfig, beautifyOptions);
beautifyOptions = cleanOptions(beautifyOptions, knownOpts);
if (isSelection) { if (isSelection) {
text = editor.getSelectedText(); text = editor.getSelectedText();
@ -166,17 +200,68 @@ function beautify() {
} }
var oldText = text; var oldText = text;
// All of the options
// Listed in order from default (base) to the one with the highest priority
// Left = Default, Right = Will override the left.
var allOptions = [defaultOptions, homeOptions, projectOptions];
function getOptions(selection, allOptions) {
// Reduce all options into correctly merged options.
var options = _.reduce(allOptions, function(result, currOptions) {
var containsNested = false;
var collectedConfig = {};
var key;
// Check to see if config file uses nested object format to split up js/css/html options
for (key in currOptions) {
if (typeof currOptions[key] === 'object') {
containsNested = true;
break; // Found, break out of loop, no need to continue
}
}
// Create a flat object of config options if nested format was used
if (!containsNested) {
collectedConfig = currOptions;
} else {
// Merge with selected options
// where `selection` could be `html`, `js`, 'css', etc
_.merge(collectedConfig, currOptions[selection]);
}
return extend(result, collectedConfig);
}, {});
// TODO: Clean.
// There is a bug in nopt
// See https://github.com/npm/nopt/issues/38#issuecomment-45971505
//console.log('pre-clean', JSON.stringify(options));
//options = cleanOptions(options, knownOpts);
//console.log('post-clean', JSON.stringify(options));
return options;
}
switch (editor.getGrammar().name) { switch (editor.getGrammar().name) {
case 'JavaScript': case 'JavaScript':
text = beautifyJS(text, beautifyOptions); text = beautifyJS(text, getOptions('js', allOptions));
break; break;
case 'Handlebars':
defaultOptions.indent_handlebars = true; // jshint ignore: line
case 'HTML (Liquid)': case 'HTML (Liquid)':
case 'HTML': case 'HTML':
case 'XML': case 'XML':
text = beautifyHTML(text, beautifyOptions); text = beautifyHTML(text, getOptions('html', allOptions));
break; break;
case 'Sass':
case 'SCSS':
case 'LESS':
case 'CSS': case 'CSS':
text = beautifyCSS(text, beautifyOptions); text = beautifyCSS(text, getOptions('css', allOptions));
break; break;
default: default:
return; return;
@ -202,7 +287,7 @@ function beautify() {
} }
} }
function handleSafeEvent() { function handleSaveEvent() {
atom.workspace.eachEditor(function (editor) { atom.workspace.eachEditor(function (editor) {
var buffer = editor.getBuffer(); var buffer = editor.getBuffer();
plugin.unsubscribe(buffer); plugin.unsubscribe(buffer);
@ -219,9 +304,9 @@ plugin.configDefaults = {
}; };
plugin.activate = function () { plugin.activate = function () {
handleSafeEvent(); handleSaveEvent();
plugin.subscribe(atom.config.observe( plugin.subscribe(atom.config.observe(
'atom-beautify.beautifyOnSave', 'atom-beautify.beautifyOnSave',
handleSafeEvent)); handleSaveEvent));
return atom.workspaceView.command('beautify', beautify); return atom.workspaceView.command('beautify', beautify);
}; };

View File

@ -17,8 +17,9 @@
"dependencies": { "dependencies": {
"emissary": "^1.0.0", "emissary": "^1.0.0",
"extend": "^1.2.1", "extend": "^1.2.1",
"js-beautify": "~1.4.2", "js-beautify": "~1.5.1",
"nopt": "^2.2.1", "nopt": "^3.0.0",
"lodash": "2.4.1" "lodash": "2.4.1",
"strip-json-comments": "^0.1.3"
} }
} }