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
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.
### Package Options
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
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_char": " ",
"other": " ",
"indent_level": 0,
"indent_with_tabs": false,
"preserve_newlines": true,
"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_level': Number,
'indent_with_tabs': Boolean,
'indent_handlebars': Boolean,
'preserve_newlines': Boolean,
'max_preserve_newlines': Number,
'space_in_paren': Boolean,
@ -49,24 +50,8 @@ var Subscriber = require('emissary').Subscriber;
var plugin = module.exports;
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() {
return process.env.HOME || process.env.USERPROFILE;
return process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
}
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() {
var text;
var editor = atom.workspace.getActiveEditor();
var isSelection = !! editor.getSelectedText();
var softTabs = editor.softTabs;
var tabLength = editor.getTabLength();
var beautifyOptions = {
var defaultOptions = {
'indent_size': softTabs ? tabLength : 1,
'indent_char': softTabs ? ' ' : '\t',
'indent_with_tabs': !softTabs
@ -111,53 +166,32 @@ function beautify() {
// Look for .jsbeautifierrc in file and home path, check env variables
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;
}
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);
var externalOptions;
if (rcInRecursiveCwd) {
externalOptions = JSON.parse(fs.readFileSync(rcInRecursiveCwd, {
if (configPath) {
var strip = require('strip-json-comments');
try {
externalOptions = JSON.parse(strip(fs.readFileSync(configPath, {
encoding: 'utf8'
}));
} else if (rcInHomePath) {
externalOptions = JSON.parse(fs.readFileSync(rcInHomePath, {
encoding: 'utf8'
}));
})));
} catch (e) {
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
if (!containsNested) {
collectedConfig = externalOptions;
} else {
for (key in externalOptions) {
_.merge(collectedConfig, externalOptions[key]);
}
}
beautifyOptions = extend(collectedConfig, beautifyOptions);
beautifyOptions = cleanOptions(beautifyOptions, knownOpts);
// Get the path to the config file
var projectOptions = getConfig(editedFilePath);
var homeOptions = getConfig(getUserHome());
if (isSelection) {
text = editor.getSelectedText();
@ -166,17 +200,68 @@ function beautify() {
}
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) {
case 'JavaScript':
text = beautifyJS(text, beautifyOptions);
text = beautifyJS(text, getOptions('js', allOptions));
break;
case 'Handlebars':
defaultOptions.indent_handlebars = true; // jshint ignore: line
case 'HTML (Liquid)':
case 'HTML':
case 'XML':
text = beautifyHTML(text, beautifyOptions);
text = beautifyHTML(text, getOptions('html', allOptions));
break;
case 'Sass':
case 'SCSS':
case 'LESS':
case 'CSS':
text = beautifyCSS(text, beautifyOptions);
text = beautifyCSS(text, getOptions('css', allOptions));
break;
default:
return;
@ -202,7 +287,7 @@ function beautify() {
}
}
function handleSafeEvent() {
function handleSaveEvent() {
atom.workspace.eachEditor(function (editor) {
var buffer = editor.getBuffer();
plugin.unsubscribe(buffer);
@ -219,9 +304,9 @@ plugin.configDefaults = {
};
plugin.activate = function () {
handleSafeEvent();
handleSaveEvent();
plugin.subscribe(atom.config.observe(
'atom-beautify.beautifyOnSave',
handleSafeEvent));
handleSaveEvent));
return atom.workspaceView.command('beautify', beautify);
};

View File

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