2014-03-03 00:51:16 -07:00
|
|
|
/* global atom */
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var beautifyJS = require('js-beautify');
|
|
|
|
var beautifyHTML = require('js-beautify').html;
|
|
|
|
var beautifyCSS = require('js-beautify').css;
|
2014-05-12 13:44:05 -06:00
|
|
|
var fs = require('fs');
|
|
|
|
var path = require('path');
|
|
|
|
var cc = require('config-chain');
|
|
|
|
var nopt = require('nopt');
|
|
|
|
var extend = require('extend');
|
|
|
|
|
|
|
|
// TODO: Copied from jsbeautify, please update it from time to time
|
|
|
|
var knownOpts = {
|
|
|
|
// Beautifier
|
|
|
|
'indent_size': Number,
|
|
|
|
'indent_char': String,
|
|
|
|
'indent_level': Number,
|
|
|
|
'indent_with_tabs': Boolean,
|
|
|
|
'preserve_newlines': Boolean,
|
|
|
|
'max_preserve_newlines': Number,
|
|
|
|
'space_in_paren': Boolean,
|
|
|
|
'jslint_happy': Boolean,
|
|
|
|
// TODO: expand-strict is obsolete, now identical to expand. Remove in future version
|
|
|
|
'brace_style': ['collapse', 'expand', 'end-expand', 'expand-strict'],
|
|
|
|
'break_chained_methods': Boolean,
|
|
|
|
'keep_array_indentation': Boolean,
|
|
|
|
'unescape_strings': Boolean,
|
|
|
|
'wrap_line_length': Number,
|
|
|
|
'e4x': Boolean,
|
|
|
|
// HTML-only
|
|
|
|
'max_char': Number, // obsolete since 1.3.5
|
|
|
|
'unformatted': [String, Array],
|
|
|
|
'indent_inner_html': [Boolean],
|
|
|
|
'indent_scripts': ['keep', 'separate', 'normal'],
|
|
|
|
// CLI
|
|
|
|
'version': Boolean,
|
|
|
|
'help': Boolean,
|
|
|
|
'files': [path, Array],
|
|
|
|
'outfile': path,
|
|
|
|
'replace': Boolean,
|
|
|
|
'quiet': Boolean,
|
|
|
|
'type': ['js', 'css', 'html'],
|
|
|
|
'config': path
|
|
|
|
};
|
2014-03-03 00:51:16 -07:00
|
|
|
|
2014-03-12 00:03:34 -06:00
|
|
|
var Subscriber = require('emissary').Subscriber;
|
|
|
|
var plugin = module.exports;
|
|
|
|
Subscriber.extend(plugin);
|
|
|
|
|
2014-05-12 13:44:05 -06:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
function cleanOptions(data, types) {
|
|
|
|
nopt.clean(data, types);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2014-05-14 03:37:53 -06:00
|
|
|
function getCursors(editor) {
|
|
|
|
var cursors = editor.getCursors();
|
|
|
|
var posArray = [];
|
|
|
|
for (var idx = 0; idx < cursors.length; idx++) {
|
|
|
|
var cursor = cursors[idx];
|
|
|
|
var bufferPosition = cursor.getBufferPosition();
|
|
|
|
posArray.push([bufferPosition.row, bufferPosition.column]);
|
|
|
|
}
|
|
|
|
return posArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
function setCursors(editor, posArray) {
|
2014-05-14 15:05:19 -06:00
|
|
|
for (var idx = 0; idx < posArray.length; idx++) {
|
2014-05-14 03:37:53 -06:00
|
|
|
var bufferPosition = posArray[idx];
|
2014-05-14 15:05:19 -06:00
|
|
|
if (idx === 0) {
|
2014-05-14 03:37:53 -06:00
|
|
|
editor.setCursorBufferPosition(bufferPosition);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
editor.addCursorAtBufferPosition(bufferPosition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-03 00:51:16 -07:00
|
|
|
function beautify() {
|
|
|
|
var text;
|
|
|
|
var editor = atom.workspace.getActiveEditor();
|
|
|
|
var isSelection = !! editor.getSelectedText();
|
|
|
|
var softTabs = editor.softTabs;
|
|
|
|
var tabLength = editor.getTabLength();
|
|
|
|
|
|
|
|
var beautifyOptions = {
|
|
|
|
'indent_size': softTabs ? tabLength : 1,
|
2014-05-14 15:05:19 -06:00
|
|
|
'indent_char': softTabs ? ' ' : '\t',
|
|
|
|
'indent_with_tabs': !softTabs
|
2014-03-03 00:51:16 -07:00
|
|
|
};
|
|
|
|
|
2014-05-12 13:44:05 -06:00
|
|
|
// Look for .jsbeautifierrc in file and home path, check env variables
|
|
|
|
var editedFilePath = editor.getPath();
|
2014-05-14 15:05:19 -06:00
|
|
|
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;
|
|
|
|
}
|
2014-05-12 13:44:05 -06:00
|
|
|
var cfg = cc(
|
|
|
|
cleanOptions(cc.env('jsbeautify_'), knownOpts),
|
2014-05-14 15:05:19 -06:00
|
|
|
rcInRecursiveCwd,
|
|
|
|
rcInHomePath
|
2014-05-12 13:44:05 -06:00
|
|
|
).list;
|
|
|
|
// cc(...).snapshot SHOULD contain the same what I construct below,
|
|
|
|
// however I have not the faintest idea why it doesn't work here.
|
|
|
|
// It works at js-beautify cli, but not here. Weird.
|
|
|
|
var collectedConfig = {};
|
|
|
|
for (var idx = cfg.length - 1; idx >= 0; idx--) {
|
|
|
|
collectedConfig = extend(cfg[idx], collectedConfig);
|
|
|
|
}
|
|
|
|
// Override the indenting options from the editor
|
|
|
|
beautifyOptions = extend(collectedConfig, beautifyOptions);
|
|
|
|
|
2014-03-03 00:51:16 -07:00
|
|
|
if (isSelection) {
|
|
|
|
text = editor.getSelectedText();
|
|
|
|
} else {
|
|
|
|
text = editor.getText();
|
|
|
|
}
|
2014-05-14 15:05:19 -06:00
|
|
|
var oldText = text;
|
2014-03-03 00:51:16 -07:00
|
|
|
|
|
|
|
switch (editor.getGrammar().name) {
|
2014-05-14 03:37:53 -06:00
|
|
|
case 'JavaScript':
|
|
|
|
text = beautifyJS(text, beautifyOptions);
|
|
|
|
break;
|
|
|
|
case 'HTML':
|
|
|
|
case 'XML':
|
|
|
|
text = beautifyHTML(text, beautifyOptions);
|
|
|
|
break;
|
|
|
|
case 'CSS':
|
|
|
|
text = beautifyCSS(text, beautifyOptions);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
2014-03-03 00:51:16 -07:00
|
|
|
}
|
2014-05-14 15:05:19 -06:00
|
|
|
if (oldText !== text) {
|
|
|
|
var posArray = getCursors(editor);
|
|
|
|
var origScrollTop = editor.getScrollTop();
|
|
|
|
if (isSelection) {
|
|
|
|
editor.setTextInBufferRange(
|
|
|
|
editor.getSelectedBufferRange(),
|
|
|
|
text
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
editor.setText(text);
|
|
|
|
}
|
|
|
|
setCursors(editor, posArray);
|
|
|
|
// Let the scrollTop setting run after all the save related stuff is run,
|
|
|
|
// otherwise setScrollTop is not working, probably because the cursor
|
|
|
|
// addition happens asynchronously
|
|
|
|
setTimeout(function () {
|
|
|
|
editor.setScrollTop(origScrollTop);
|
|
|
|
}, 0);
|
2014-03-03 00:51:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-12 00:03:34 -06:00
|
|
|
function handleSafeEvent() {
|
2014-05-14 03:37:53 -06:00
|
|
|
atom.workspace.eachEditor(function (editor) {
|
2014-03-12 00:03:34 -06:00
|
|
|
var buffer = editor.getBuffer();
|
|
|
|
plugin.unsubscribe(buffer);
|
|
|
|
|
2014-05-12 13:44:05 -06:00
|
|
|
if (atom.config.get('atom-beautify.beautifyOnSave')) {
|
2014-03-12 00:03:34 -06:00
|
|
|
var events = 'will-be-saved';
|
|
|
|
plugin.subscribe(buffer, events, beautify);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
plugin.configDefaults = {
|
|
|
|
beautifyOnSave: false
|
|
|
|
};
|
|
|
|
|
2014-05-14 03:37:53 -06:00
|
|
|
plugin.activate = function () {
|
2014-03-12 00:03:34 -06:00
|
|
|
handleSafeEvent();
|
2014-05-12 23:37:54 -06:00
|
|
|
plugin.subscribe(atom.config.observe(
|
|
|
|
'atom-beautify.beautifyOnSave',
|
2014-05-12 13:44:05 -06:00
|
|
|
handleSafeEvent));
|
2014-03-03 00:51:16 -07:00
|
|
|
return atom.workspaceView.command('beautify', beautify);
|
2014-05-14 03:37:53 -06:00
|
|
|
};
|