<commit_before>;;; jscs.el --- Consistent JavaScript editing using JSCS  -*- lexical-binding: t; -*-

;; Copyright (C) 2015  papaeye

;; Author: papaeye <<EMAIL>>
;; Keywords: languages, convenience
;; Version: 0.1.0
;; Homepage: https://github.com/papaeye/emacs-jscs
;; Package-Requires: ((emacs "24.1") (langfmt "0.1.0"))

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; jscs.el provides consistent JavaScript editing using JSCS.
;;
;; Installation:
;;
;; 1. Install JSCS <http://jscs.info/>.
;;
;; 2. Put jscs.el and langfmt.el <https://github.com/papaeye/go-mode.el>
;;    somewhere in your `load-path'.
;;
;; 3. Add the following code into your .emacs:
;;
;;     (autoload 'jscs-indent-apply "jscs" nil t)
;;     (autoload 'jscs-fix "jscs" nil t)
;;     (autoload 'jscs-fix-before-save "jscs" nil t)
;;
;; Usage:
;;
;; To apply JSCS indentation rules to JavaScript modes,
;; add the following code into your .emacs:
;;
;;     (with-eval-after-load 'js
;;       (add-hook 'js-mode-hook #'jscs-indent-apply))
;;
;;     (with-eval-after-load 'js2-mode
;;       (add-hook 'js2-mode-hook #'jscs-indent-apply))
;;
;; To run "jscs --fix" interactively, run \\[jscs-fix].
;;
;; To run "jscs --fix" on the current buffer when saving,
;; add the following code into your .emacs:
;;
;;     (add-hook 'before-save-hook #'jscs-fix-before-save)

;;; Code:

(require 'json)

(require 'langfmt)

(defvar js-indent-level)
(defvar js2-basic-offset)

;; JSCS exit codes
(defconst jscs-exit-code-style-errors 2)
(defconst jscs-exit-missing-config 4)

(defgroup jscs nil
  "Consistent JavaScript editing using JSCS"
  :group 'tools)

(defcustom jscs-command "jscs"
  "The 'jscs' command."
  :type 'string
  :group 'jscs)

(defvar jscs--presets-path
  (expand-file-name "../lib/node_modules/jscs/presets"
		    (file-name-directory (executable-find jscs-command))))

(defun jscs--read-jscsrc ()
  (let ((dir (locate-dominating-file default-directory ".jscsrc")))
    (when dir
      (json-read-file (expand-file-name ".jscsrc" dir)))))

(defun jscs--read-preset (name)
  (let ((preset (expand-file-name (concat name ".json") jscs--presets-path)))
    (if (file-readable-p preset)
	(json-read-file preset)
      (error "Preset %s is not found" name))))

(defun jscs--config-list (config)
  (let ((preset (cdr (assq 'preset config))))
    (if (stringp preset)
	(cons config (jscs--config-list (jscs--read-preset preset)))
      (list config))))

(defun jscs-indent--rule-validate-indentation (config)
  (let ((indent (cdr (assq 'validateIndentation config))))
    (prog1 indent
      (when (listp indent)
	(setq indent (cdr (assq 'value indent))))
      (cond
       ((integerp indent)
	(cond
	 ((memq major-mode '(js-mode json-mode))
	  (setq-local js-indent-level indent))
	 ((eq major-mode 'js2-mode)
	  (setq-local js2-basic-offset indent)))
	(setq indent-tabs-mode nil))
       ((string= indent "\t")
	(setq indent-tabs-mode t))))))

(defun jscs-indent--rule-maximum-line-length (config)
  (let ((rule (cdr (assq 'maximumLineLength config)))
	tab-size)
    (prog1 rule
      (when (listp rule)
	(setq tab-size (cdr (assq 'tabSize rule)))
	(when (integerp tab-size)
	  (setq tab-width tab-size))))))

(defvar jscs-indent--rule-functions
  (list #'jscs-indent--rule-validate-indentation
	#'jscs-indent--rule-maximum-line-length))

(defun jscs-indent--apply (config)
  (let ((config-list (jscs--config-list config)))
    (dolist (func jscs-indent--rule-functions)
      (let ((tail config-list)
	    done)
	(while (and (not done) tail)
	  (setq done (funcall func (car tail)))
	  (setq tail (cdr tail)))))))

;;;###autoload
(defun jscs-indent-apply ()
  (interactive)
  (let ((jscsrc (jscs--read-jscsrc)))
    (when jscsrc
      (jscs-indent--apply jscsrc))))

;;;###autoload (autoload 'jscs-fix "jscs" nil t)
;;;###autoload (autoload 'jscs-fix-before-save "jscs" nil t)
(define-langfmt jscs-fix
  "Format the current buffer according to the JSCS tool."
  :group 'jscs
  :modes '(js-mode js2-mode js3-mode)
  :runner #'jscs-fix--runner
  :error-filter #'jscs-fix--error-filter)

(defun jscs-fix--runner (tmpfile patchbuf errbuf)
  (let ((exit (call-process jscs-command nil errbuf nil
			    "--fix" "--reporter" "inline" tmpfile)))
    (cond
     ((= exit jscs-exit-missing-config)
      (message "No configuration found"))
     (t
      (if (zerop (call-process-region (point-min) (point-max) "diff"
				      nil patchbuf nil "-n" "-" tmpfile))
	  (message (if (zerop exit)
		       "Buffer is already jscs-fixed"
		     "Could not apply jscs-fix"))
	(langfmt-apply-rcs-patch patchbuf)
	(message (if (zerop exit)
		     "Applied jscs-fix"
		   "Applied jscs-fix partially")))))
    (when errbuf
      (if (zerop exit)
	  (langfmt-kill-error-buffer errbuf)
	(jscs-fix--process-errors (buffer-file-name) tmpfile errbuf)))))

(defun jscs-fix--error-filter (filename tmpfile)
  (while (search-forward-regexp
	  (concat "^\\(?:"
		  (regexp-quote tmpfile)
		  "\\): line \\([0-9]+\\), col \\([0-9]+\\), \\(.+\\)")
	  nil t)
    (replace-match (concat (file-name-nondirectory filename)
			   ":" (match-string 1) ":" (match-string 2)
			   ": " (match-string 3))
		   t t)))

(provide 'jscs)
;;; jscs.el ends here<commit_msg>Use new define-langfmt macro of langfmt 0.2.0alpha<commit_after>;;; jscs.el --- Consistent JavaScript editing using JSCS  -*- lexical-binding: t; -*-

;; Copyright (C) 2015  papaeye

;; Author: papaeye <<EMAIL>>
;; Keywords: languages, convenience
;; Version: 0.2.0alpha
;; Homepage: https://github.com/papaeye/emacs-jscs
;; Package-Requires: ((emacs "24.1") (langfmt "0.2.0alpha"))

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; jscs.el provides consistent JavaScript editing using JSCS.
;;
;; Installation:
;;
;; 1. Install JSCS <http://jscs.info/>.
;;
;; 2. Put jscs.el and langfmt.el <https://github.com/papaeye/go-mode.el>
;;    somewhere in your `load-path'.
;;
;; 3. Add the following code into your .emacs:
;;
;;     (autoload 'jscs-indent-apply "jscs" nil t)
;;     (autoload 'jscs-fix "jscs" nil t)
;;     (autoload 'jscs-fix-before-save "jscs" nil t)
;;
;; Usage:
;;
;; To apply JSCS indentation rules to JavaScript modes,
;; add the following code into your .emacs:
;;
;;     (with-eval-after-load 'js
;;       (add-hook 'js-mode-hook #'jscs-indent-apply))
;;
;;     (with-eval-after-load 'js2-mode
;;       (add-hook 'js2-mode-hook #'jscs-indent-apply))
;;
;; To run "jscs --fix" interactively, run \\[jscs-fix].
;;
;; To run "jscs --fix" on the current buffer when saving,
;; add the following code into your .emacs:
;;
;;     (add-hook 'before-save-hook #'jscs-fix-before-save)

;;; Code:

(require 'json)

(require 'langfmt)

(defvar js-indent-level)
(defvar js2-basic-offset)

;; JSCS exit codes
(defconst jscs-exit-code-style-errors 2)
(defconst jscs-exit-missing-config 4)

(defgroup jscs nil
  "Consistent JavaScript editing using JSCS"
  :group 'tools)

(defcustom jscs-command "jscs"
  "The 'jscs' command."
  :type 'string
  :group 'jscs)

(defvar jscs--presets-path
  (expand-file-name "../lib/node_modules/jscs/presets"
		    (file-name-directory (executable-find jscs-command))))

(defun jscs--read-jscsrc ()
  (let ((dir (locate-dominating-file default-directory ".jscsrc")))
    (when dir
      (json-read-file (expand-file-name ".jscsrc" dir)))))

(defun jscs--read-preset (name)
  (let ((preset (expand-file-name (concat name ".json") jscs--presets-path)))
    (if (file-readable-p preset)
	(json-read-file preset)
      (error "Preset %s is not found" name))))

(defun jscs--config-list (config)
  (let ((preset (cdr (assq 'preset config))))
    (if (stringp preset)
	(cons config (jscs--config-list (jscs--read-preset preset)))
      (list config))))

(defun jscs-indent--rule-validate-indentation (config)
  (let ((indent (cdr (assq 'validateIndentation config))))
    (prog1 indent
      (when (listp indent)
	(setq indent (cdr (assq 'value indent))))
      (cond
       ((integerp indent)
	(cond
	 ((memq major-mode '(js-mode json-mode))
	  (setq-local js-indent-level indent))
	 ((eq major-mode 'js2-mode)
	  (setq-local js2-basic-offset indent)))
	(setq indent-tabs-mode nil))
       ((string= indent "\t")
	(setq indent-tabs-mode t))))))

(defun jscs-indent--rule-maximum-line-length (config)
  (let ((rule (cdr (assq 'maximumLineLength config)))
	tab-size)
    (prog1 rule
      (when (listp rule)
	(setq tab-size (cdr (assq 'tabSize rule)))
	(when (integerp tab-size)
	  (setq tab-width tab-size))))))

(defvar jscs-indent--rule-functions
  (list #'jscs-indent--rule-validate-indentation
	#'jscs-indent--rule-maximum-line-length))

(defun jscs-indent--apply (config)
  (let ((config-list (jscs--config-list config)))
    (dolist (func jscs-indent--rule-functions)
      (let ((tail config-list)
	    done)
	(while (and (not done) tail)
	  (setq done (funcall func (car tail)))
	  (setq tail (cdr tail)))))))

;;;###autoload
(defun jscs-indent-apply ()
  (interactive)
  (let ((jscsrc (jscs--read-jscsrc)))
    (when jscsrc
      (jscs-indent--apply jscsrc))))

;;;###autoload (autoload 'jscs-fix "jscs" nil t)
;;;###autoload (autoload 'jscs-fix-before-save "jscs" nil t)
(define-langfmt jscs-fix
  "Format the current buffer according to the JSCS tool."
  :group 'jscs
  :modes '(js-mode js2-mode js3-mode)
  :format-command jscs-command
  :format-args '("--fix" "--reporter" "inline")
  :format-handler #'jscs-fix--format-handler
  :diff-handler #'jscs-fix--diff-handler
  :error-filter #'jscs-fix--error-filter)

(defun jscs-fix--format-handler (exit-status)
  (cond
   ((= exit-status jscs-exit-missing-config)
    (message "No configuration found"))
   ((and (/= exit-status 0)
	 (/= exit-status jscs-exit-code-style-errors))
    (message "Could not apply jscs-fix"))))

(defun jscs-fix--diff-handler (exit-status no-diff-p)
  (if no-diff-p
      (message (if (zerop exit-status)
		   "Buffer is already jscs-fixed"
		 "Could not apply jscs-fix"))
    (message (if (zerop exit-status)
		 "Applied jscs-fix"
	       "Applied jscs-fix partially"))))

(defun jscs-fix--error-filter (filename tmpfile)
  (while (search-forward-regexp
	  (concat "^\\(?:"
		  (regexp-quote tmpfile)
		  "\\): line \\([0-9]+\\), col \\([0-9]+\\), \\(.+\\)")
	  nil t)
    (replace-match (concat (file-name-nondirectory filename)
			   ":" (match-string 1) ":" (match-string 2)
			   ": " (match-string 3))
		   t t)))

(provide 'jscs)
;;; jscs.el ends here
{'fraction_non_alphanumeric': 0.16979507490959186, 'fraction_numerical': 0.006371620458067849, 'mean_word_length': 3.4433817903596022, 'pattern_counts': {'":': 6, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 4, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1381245', 'n_tokens_mistral': 4404, 'n_tokens_neox': 4256, 'n_words': 1145}