Skip to content

Commit

Permalink
Change config and allow selection beautify
Browse files Browse the repository at this point in the history
  • Loading branch information
Hooky committed Oct 8, 2016
1 parent d028974 commit 54078fd
Show file tree
Hide file tree
Showing 13 changed files with 370 additions and 144 deletions.
1 change: 1 addition & 0 deletions .jshintrc
Expand Up @@ -7,6 +7,7 @@
"futurehostile": true,
"lastsemic": true,
"latedef": true,
"indent": 1,
"maxdepth": 5,
"maxerr": 100,
"noarg": true,
Expand Down
40 changes: 26 additions & 14 deletions README.md
@@ -1,13 +1,11 @@
# js-beautify for VS Code

[![Build Status](https://api.travis-ci.org/HookyQR/VSCodeBeautify.svg?branch=master)](https://travis-ci.org/HookyQR/VSCodeBeautify)
[![Build status](https://ci.appveyor.com/api/projects/status/mu73cgat3r1t0weu/branch/master?svg=true)](https://ci.appveyor.com/project/HookyQR/vscodebeautify)
[![Licence](https://img.shields.io/github/license/HookyQR/VSCodeBeautify.svg)](https://github.com/HookyQR/VSCodeBeautify)
[![Build Status](https://api.travis-ci.org/HookyQR/VSCodeBeautify.svg?branch=master)](https://travis-ci.org/HookyQR/VSCodeBeautify) [![Build status](https://ci.appveyor.com/api/projects/status/mu73cgat3r1t0weu/branch/master?svg=true)](https://ci.appveyor.com/project/HookyQR/vscodebeautify) [![Licence](https://img.shields.io/github/license/HookyQR/VSCodeBeautify.svg)](https://github.com/HookyQR/VSCodeBeautify)
[![VS Code Marketplace](http://vsmarketplacebadge.apphb.com/version-short/HookyQR.beautify.svg) ![Rating](http://vsmarketplacebadge.apphb.com/rating-short/HookyQR.beautify.svg) ![Installs](http://vsmarketplacebadge.apphb.com/installs/HookyQR.beautify.svg)](https://marketplace.visualstudio.com/items?itemName=HookyQR.beautify)

Beautify `javascript`, `JSON`, `CSS`, `Sass`, and `HTML` in Visual Studio Code.

VS Code uses js-beautify internally, but it lacks the ability to modify the style you wish to use. This extension enables running [js-beautify](http://jsbeautifier.org/) in VS Code, _AND_ honouring any `.jsbeautifyrc` file in the open file's path tree to load *your* code styling. Run with **F1** `Beautify`.
VS Code uses js-beautify internally, but it lacks the ability to modify the style you wish to use. This extension enables running [js-beautify](http://jsbeautifier.org/) in VS Code, _AND_ honouring any `.jsbeautifyrc` file in the open file's path tree to load *your* code styling. Run with **F1** `Beautify` (to beautify a selection) or **F1** `Beautify file`.

### How we determine what settings to use:

Expand All @@ -21,7 +19,7 @@ otherwise...
5. If **`"beautify.editorconfig"` is set to `true`**: editorconfig settings are searched for (See http://editorconfig.org/) and are merged in.


### VS Code | .jsbeautifyrc settings map:
### VS Code | .jsbeautifyrc settings map:

.jsbeautifyrc setting | VS Code setting
--- | ---
Expand Down Expand Up @@ -61,27 +59,36 @@ Will result in the `indent_size` being set to 4 for Javascript and HTML, but set

If the file is unsaved, or the type is undetermined, you'll be prompted for which beautifier to use.

Extra (permanent) file extensions may be added under user or workspace settings as shown below. (Defaults shown)
You can contol which file types, extensions, or specific file names should be beautified with the `beautify.language` setting.

_Note:_ This used to be controlled by the `beautify.*Files` settings. If you still have those settings in your configuration, you'll be told that they're deprecated. Note that you may have to fix your global and project settings before the notification stops.

```javascript
{
"beautify.CSSfiles": ["css", "scss"],
"beautify.JSfiles": ["js", "json", "jsbeautifyrc", "jshintrc"],
"beautify.HTMLfiles": ["htm", "html"]
"beautify.language": {
"js": {
"type": ["javascript", "json"],
"filename": [".jshintrc", ".jsbeautify"]
// "ext": ["js", "json"]
// ^^ to set extensions to be beautified using the javascript beautifier
},
"css": ["css", "scss"],
"html": ["htm", "html"]
// ^^ providing just an array sets the VS Code file type
}
}
```

Beautify on save can be enabled for all, or just specific file types. Files that you do not wish to be beautified can be excluded in the user or workspace settings files. Settings examples:

```javascript
"beautify.onSave": true, // beautify HTML, CSS, JavaScript, and JSON on save
//or
"beautify.onSave": ["js","json"], //only beautify JavaScript and JSON files on save
"beautify.onSave": true, // beautify all matching types using the selected beautifier
"beautify.onSave": ["js","css"], // only beautify those that match the js and css types

"beautify.onSaveIgnore": [
//don't beautify any file in any 'minified' directory, at any depth:
// don't beautify any file in any 'minified' directory, at any depth:
"**/minified/**",
//don't minify any file that contains '.min.', '_min.', '-min.' in the filename (This is the default ignore setting):
// don't minify any file that contains '.min.', '_min.', '-min.' in the filename (This is the default ignore setting):
"**/*+(.|_|-)min.*",
]
```
Expand All @@ -91,6 +98,11 @@ If you wish to exclude the files that are included by default, set `"beautify.on
Embedded version of js-beautify is v1.6.4.

## Changes:
### 0.4.0: 08 Oct 2016
* Change settings structure
* Reload file association settings when user config is changed
* Allow beautify of (primary) selected lines only

### 0.3.0: 03 Oct 2016
* Add editorconfig as a settings source

Expand Down
207 changes: 135 additions & 72 deletions extension.js
@@ -1,43 +1,14 @@
"use strict";
const vscode = require('vscode'),
beautify = require('js-beautify'),
path = require('path'),
minimatch = require('minimatch'),
options = require('./options');
const dumpError = e => {
if (e) console.log('beautify err:', e);
return [];
};

const extMatch = n => ({
pattern: n.startsWith("**/") ? n : ("**/" + n)
});

const getBeautifyType = function(doc, dontAsk) {
if (doc.languageId === 'javascript') return 'js';
if (doc.languageId === 'json') return 'js';
if (doc.languageId === 'html') return 'html';
if (doc.languageId === 'css') return 'css';
if (doc.languageId === 'scss') return 'css';
const type = doc.isUntitled ? "" : path.extname(doc.fileName)
.toLowerCase();
const cfg = vscode.workspace.getConfiguration('beautify');
//if a type is set on the window, use that
//check if the file is in the users json schema set
const jsSchema = vscode.workspace.getConfiguration('json')
.schemas;
if (jsSchema.length) {
let matcher = [];
jsSchema.forEach(schema => {
if (typeof schema.fileMatch === 'string') matcher.push(extMatch(schema.fileMatch));
else matcher = matcher.concat(schema.fileMatch.map(extMatch));
});
if (vscode.languages.match(matcher, doc)) return "js";
}
if (cfg.HTMLfiles.indexOf(type) >= 0 || (type[0] === '.' && cfg.HTMLfiles.indexOf(type.slice(1)) >= 0)) return 'html';
else if (cfg.CSSfiles.indexOf(type) >= 0 || (type[0] === '.' && cfg.CSSfiles.indexOf(type.slice(1)) >= 0)) return 'css';
else if (cfg.JSfiles.indexOf(type) >= 0 || (type[0] === '.' && cfg.JSfiles.indexOf(type.slice(1)) >= 0)) return 'js';
if (dontAsk) return;
const getBeautifyType = () => {
return new Promise((resolve, reject) => {
//Ask what they want to do:
return vscode.window.showQuickPick([{
Expand Down Expand Up @@ -65,10 +36,10 @@ function beautifyDoc(doc, range, type, formattingOptions) {
"Beautify can't get the file information because the editor won't supply it. (File probably too large)");
throw "";
}
return Promise.resolve(type ? type : getBeautifyType(doc))
return Promise.resolve(type ? type : getBeautifyType())
.then(type => options(doc, type, formattingOptions)
.then(config => {
const original = doc.getText(doc.validateRange(range));
const original = doc.getText(range);
return beautify[type](original, config);
}));
}
Expand All @@ -78,34 +49,142 @@ function documentEdit(range, newText) {
}

function extendRange(doc, rng) {
const r = new vscode.Range(new vscode.Position(rng.start.line, 0), rng.end.translate(0, Infinity));
let end = rng.end;
if (end.character === 0) end = end.translate(-1, Number.MAX_VALUE);
else end = end.translate(0, Number.MAX_VALUE);
const r = new vscode.Range(new vscode.Position(rng.start.line, 0), end);
return doc.validateRange(r);
}

function fullRange(doc) {
return doc.validateRange(new vscode.Range(0, 0, Number.MAX_VALUE, Number.MAX_VALUE));
}

const fullEdit = (doc, formattingOptions) => {
const type = getBeautifyType(doc, true);
function fullEdit(type, doc, formattingOptions) {
const rng = fullRange(doc);
return beautifyDoc(doc, rng, type, formattingOptions)
.then(newText => documentEdit(rng, newText), dumpError);
};
}

const rangeEdit = (doc, rng, formattingOptions) => {
const type = getBeautifyType(doc, true);
function rangeEdit(type, doc, rng, formattingOptions) {
rng = extendRange(doc, rng);
return beautifyDoc(doc, rng, type, formattingOptions)
.then(newText => documentEdit(rng, newText), dumpError);
}

const register = (type, selector, partial) => {
if (partial) return vscode.languages.registerDocumentRangeFormattingEditProvider(selector, {
provideDocumentRangeFormattingEdits: rangeEdit.bind(0, type)
});
else return vscode.languages.registerDocumentFormattingEditProvider(selector, {
provideDocumentFormattingEdits: fullEdit.bind(0, type)
});
};
class Formatters {
constructor() {
this.available = {
js: beautify.js,
css: beautify.css,
html: beautify.html
};
this.configTypes = {
type: 1,
ext: 1,
filename: 1
};
this.handlers = {};
}
configure() {
let cfg = vscode.workspace.getConfiguration('beautify.language');
let beautifyCfg = vscode.workspace.getConfiguration('beautify');
let js = beautifyCfg.JSFiles;
let css = beautifyCfg.CSSFiles;
let html = beautifyCfg.HTMLFiles;
if (js || css || html) {
cfg = {};
if (js) cfg.js = {
ext: js
};
if (css) cfg.css = {
ext: css
};
if (html) cfg.html = {
ext: html
};
vscode.window.showInformationMessage(
"`beautify.*Files` setting is deprecated. please use `beautify.language` instead. Open settings ->",
"Global", "Workspace")
.then(open => {
if (open) {
vscode.commands.executeCommand(`workbench.action.open${open}Settings`);
}
});
}
cfg = cfg || {};
this.dispose();
for (let a in cfg) {
if (!(a in this.available)) continue;
// dispose of the current
let selector = [];
if (Array.isArray(cfg[a])) {
selector = [].concat(cfg[a]);
} else {
for (let b in cfg[a]) {
let adder;
switch (b) {
case 'type':
adder = cfg[a][b];
break;
case 'ext':
adder = [{
pattern: `**/*.{${cfg[a][b].join(',')}}`
}];
break;
case 'filename':
adder = [{
pattern: `**/{${cfg[a][b].join(',')}}`
}];
break;
default:
continue;
}
selector = selector.concat(adder);
}
}
this.handlers[a] = {
selector,
full: register(a, selector),
partial: register(a, selector, true)
};
}
}
getFormat(doc) {
for (let a in this.handlers) {
if (vscode.languages.match(this.handlers[a].selector, doc)) {
return a;
}
}
}
dispose() {
for (let a in this.handlers) {
this.handlers[a].full.dispose();
this.handlers[a].partial.dispose();
}
this.handlers = {};
}
}

const formatters = new Formatters();
formatters.configure();

function beautifyOnSave(doc) {
if (doc.beautified) {
delete doc.beautified;
return;
}
const cfg = vscode.workspace.getConfiguration('beautify');
if (!cfg.onSave) return;

let matcher = cfg.onSaveIgnore || ["**/*+(.|_|-)min.*"];
if (typeof matcher === 'string') matcher = [matcher];
if (Array.isArray(matcher)) {
Expand All @@ -116,15 +195,9 @@ function beautifyOnSave(doc) {
}
if (matcher.some(m => minimatch(fName, m))) return;
}
let refType = doc.languageId;
if (refType === 'javascript') refType = 'js';
if (['js', 'json', 'html', 'css', 'sass'].indexOf(refType) === -1) {
refType = getBeautifyType(doc, true);
if (!refType) return;
}

const refType = formatters.getFormat(doc);
if (cfg.onSave === true || (Array.isArray(cfg.onSave) && cfg.onSave.indexOf(refType) >= 0)) {
if (refType === 'json') refType = 'js';
if (refType === 'sass') refType = 'css';
let range = fullRange(doc);
//determine a default options
return beautifyDoc(doc, range, refType)
Expand All @@ -138,41 +211,31 @@ function beautifyOnSave(doc) {
.then(() => 1, () => 1);
}
}

//register on activation
function activate(context) {
context.subscriptions.push(vscode.commands.registerCommand('HookyQR.beautify', () => {
const active = vscode.window.activeTextEditor;
if (!active) return;
if (!active.document) return;
let range = fullRange(active.document);
return beautifyDoc(active.document, range)
let range = active.selection;
if (range.isEmpty) range = fullRange(active.document);
else range = extendRange(active.document, range);
const type = formatters.getFormat(active.document);
return beautifyDoc(active.document, range, type)
.then(newText => active.edit(editor => editor.replace(range, newText)), dumpError);
}));
// setupFormatters(context.subscriptions);
// context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(()=>{
// setupFormatters(context.subscriptions);
// }));
context.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider('html', {
provideDocumentFormattingEdits: fullEdit
}));
context.subscriptions.push(vscode.languages.registerDocumentRangeFormattingEditProvider('html', {
provideDocumentRangeFormattingEdits: rangeEdit
}));
context.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider(['css', 'sass'], {
provideDocumentFormattingEdits: fullEdit
}));
context.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider('scss', {
provideDocumentFormattingEdits: fullEdit
}));
context.subscriptions.push(vscode.languages.registerDocumentRangeFormattingEditProvider(['css', 'sass'], {
provideDocumentRangeFormattingEdits: rangeEdit
}));
context.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider(['javascript', 'json'], {
provideDocumentFormattingEdits: fullEdit
}));
context.subscriptions.push(vscode.languages.registerDocumentRangeFormattingEditProvider(['javascript', 'json'], {
provideDocumentRangeFormattingEdits: rangeEdit
context.subscriptions.push(vscode.commands.registerCommand('HookyQR.beautifyFile', () => {
const active = vscode.window.activeTextEditor;
if (!active) return;
if (!active.document) return;
let range = fullRange(active.document);
const type = formatters.getFormat(active.document);
return beautifyDoc(active.document, range, type)
.then(newText => active.edit(editor => editor.replace(range, newText)), dumpError);
}));
context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(formatters.configure.bind(formatters)));

vscode.workspace.onDidSaveTextDocument(beautifyOnSave);
}
exports.activate = activate;

0 comments on commit 54078fd

Please sign in to comment.