From 546d983f912557df9e76a959f40f93ed4054d390 Mon Sep 17 00:00:00 2001 From: Jean-Sebastien Legare Date: Fri, 17 Oct 2014 00:22:35 -0700 Subject: [PATCH 1/8] [compat] Missing fs.existsSync on node v0.6.12 Patch fs to get missing existsSync. --- jshint-mode.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jshint-mode.js b/jshint-mode.js index b8c4bba..c6cfb5f 100644 --- a/jshint-mode.js +++ b/jshint-mode.js @@ -14,6 +14,10 @@ var http = require('http'), JSLINT = require('./jslint'), JSHINT = require('./jshint'); +if (!fs.existsSync) { + fs.existsSync = require('path').existsSync; +} + var hinters = { jshint: JSHINT.JSHINT, jslint: JSLINT.JSLINT From ccd1ef9d874d62843de1d5d01459d5aa8b4a0b6c Mon Sep 17 00:00:00 2001 From: Jean-Sebastien Legare Date: Fri, 17 Oct 2014 00:31:24 -0700 Subject: [PATCH 2/8] Support for multiple jshint-mode servers running in parallel (one per port) Allows multiple emacs programs to be open and benefit from jshint-mode at the same time. A server is started on the lowest unused port in the (configurable) range 3003 to 3100. The server gets a new argument --lastport INT. The flymake-hook filters stdout for the "Server started" message and extracts the port from there. --- flymake-jshint.el | 45 +++++++++++++++++++++++++++++++++++++++------ jshint-mode.js | 27 ++++++++++++++++++++++----- 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/flymake-jshint.el b/flymake-jshint.el index fd93c91..06000de 100644 --- a/flymake-jshint.el +++ b/flymake-jshint.el @@ -36,6 +36,11 @@ :type 'integer :group 'flymake-jshint) +(defcustom jshint-mode-lastport 3100 + "Defines the higher end of the port range to run on." + :type 'integer + :group 'flymake-jshint) + (defcustom jshint-mode-host "127.0.0.1" "The host the jshint-mode server runs on." :type 'string @@ -48,23 +53,51 @@ (setq jshint-process "jshint-mode-server") (setq jshint-buffer "*jshint-mode*") +(setq jshint-dynamic-port nil) ;; server prints on stdout. we grep for it +(setq jshint-process-output nil) +(setq jshint-start-regex "\\`Started[^:]+://\\([^:]+\\):\\([0-9]+\\)[\.]") + +;; The server might try different ports before starting. +;; We locate the port in the stdout messages. +(defun jshint-extract-port-filter (proc string) + ;; the following block just adds the output to the process buffer + (when (buffer-live-p (process-buffer proc)) + (with-current-buffer (process-buffer proc) + (let ((moving (= (point) (process-mark proc)))) + (save-excursion + ;; Insert the text, advancing the process marker. + (goto-char (process-mark proc)) + (insert string) + (set-marker (process-mark proc) (point))) + (if moving (goto-char (process-mark proc)))))) + + (when (eq jshint-dynamic-port nil) + (setq jshint-process-output (concat jshint-process-output string)) ;; all text received so far + (save-match-data + (when (string-match jshint-start-regex jshint-process-output) + (setq jshint-dynamic-port (string-to-number (match-string 2 jshint-process-output))) + (message + (concat "jshint server has started on " jshint-mode-host ":" + (number-to-string jshint-dynamic-port))) + (set-process-filter (get-process jshint-process) nil))))) (defun jshint-mode-init () "Start the jshint-mode server." (interactive) (if (eq (process-status jshint-process) 'run) - 'started + (if (not (eq jshint-dynamic-port nil)) + 'started + 'starting) (start-process jshint-process jshint-buffer jshint-mode-node-program (expand-file-name (concat jshint-mode-location "/jshint-mode.js")) "--host" jshint-mode-host - "--port" (number-to-string jshint-mode-port)) + "--port" (number-to-string jshint-mode-port) + "--lastport" (number-to-string jshint-mode-lastport)) + (set-process-filter (get-process jshint-process) 'jshint-extract-port-filter) (set-process-query-on-exit-flag (get-process jshint-process) nil) - (message - (concat "jshint server has started on " jshint-mode-host ":" - (number-to-string jshint-mode-port))) 'starting )) @@ -78,7 +111,7 @@ (let* ((temp-file (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace)) (local-file (file-relative-name temp-file (file-name-directory buffer-file-name))) - (jshint-url (format "http://%s:%d/check" jshint-mode-host jshint-mode-port)) + (jshint-url (format "http://%s:%d/check" jshint-mode-host jshint-dynamic-port)) (jshintrc (if (string= "" jshint-mode-jshintrc) (expand-file-name ".jshintrc" diff --git a/jshint-mode.js b/jshint-mode.js index c6cfb5f..642357f 100644 --- a/jshint-mode.js +++ b/jshint-mode.js @@ -82,11 +82,12 @@ function _getConfig(filePath) { } } -var port = getOpt("--port") || 3003, +var port = parseInt(getOpt("--port"), 10) || 3003, +lastPort = parseInt(getOpt("--lastport"),10) || 3003, host = getOpt("--host") || "127.0.0.1", config = {}; -http.createServer(function(req, res) { +var server = http.createServer(function(req, res) { if (req.url === '/check' && req.method.toUpperCase() === 'POST') { var form = new formidable.IncomingForm(); form.parse(req, function(err, fields, files) { @@ -106,6 +107,22 @@ http.createServer(function(req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end("hello from jshint-mode"); -}).listen(port, host); - -console.log('Started JSHint server at http:// ' + host + ':' + port); +}); + +server.on('listening', function () { + console.log('Started JSHint server at http://' + host + ':' + port + '.'); +}); + +server.on('error', function (err) { + if (err.errno === "EADDRINUSE") { + if (port >= lastPort) { + console.error("Error occurred during '" + err.syscall + "':", err.code); + process.exit(2); + } else { + // find the next available port + port += 1; + server.listen(port, host); + } + } +}); +server.listen(port, host); From dc4c30095919e5f20734e0324c895529f0317fa8 Mon Sep 17 00:00:00 2001 From: Jean-Sebastien Legare Date: Fri, 17 Oct 2014 00:34:18 -0700 Subject: [PATCH 3/8] [flymake cursor] Convenience tool to show flymake errors in minibuffer --- flymake-cursor.el | 167 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 flymake-cursor.el diff --git a/flymake-cursor.el b/flymake-cursor.el new file mode 100644 index 0000000..0f0b8c5 --- /dev/null +++ b/flymake-cursor.el @@ -0,0 +1,167 @@ +;;; flymake-cursor.el --- displays flymake error msg in minibuffer after delay +;; +;; Author : ?? +;; origin : http://paste.lisp.org/display/60617,1/raw +;; Maintainer : Dino Chiesa +;; : Donald Curtis +;; Created : May 2011 +;; Modified : December 2012 +;; Version : 0.1.5 +;; Keywords : languages mode flymake +;; X-URL : http://www.emacswiki.org/emacs/flymake-cursor.el +;; Last-saved : <2012-Dec-20 09:49:28> +;; +;; ------------------------------------------------------- +;; +;; License: None. This code is in the Public Domain. +;; +;; +;; Additional functionality that makes flymake error messages appear +;; in the minibuffer when point is on a line containing a flymake +;; error. This saves having to mouse over the error, which is a +;; keyboard user's annoyance. +;; ------------------------------------------------------- +;; +;; This flymake-cursor module displays the flymake error in the +;; minibuffer, after a short delay. It is based on code I found roaming +;; around on the net, unsigned and unattributed. I suppose it's public +;; domain, because, while there is a "License" listed in it, there +;; is no license holder, no one to own the license. +;; +;; This version is modified slightly from that code. The post-command fn +;; defined in this code does not display the message directly. Instead +;; it sets a timer, and when the timer fires, the timer event function +;; displays the message. +;; +;; The reason to do this: the error message is displayed only if the +;; user doesn't do anything, for about one second. This way, if the user +;; scrolls through a buffer and there are myriad errors, the minibuffer +;; is not constantly being updated. +;; +;; If the user moves away from the line with the flymake error message +;; before the timer expires, then no error is displayed in the minibuffer. +;; +;; I've also updated the names of the defuns. They all start with flyc now. +;; +;; To use this, include this line in your .emacs: +;; +;; ;; enhancements for displaying flymake errors +;; (require 'flymake-cursor) +;; +;; You can, of course, put that in an eval-after-load clause. +;; +;; ------------------------------------------------------- +;; +;; Update 2012-03-06 by Donald Curtis +;; -- +;; Added some autoload statements and the closing comment to make +;; compatible with package.el parser. +;; +;; Update 2012-12-20 by Jeremy Moore +;; -- +;; Alter post-command-hook's local value via add-hook so that it plays +;; nicely with other packages. +;; + + +(require 'cl) + +(defvar flyc--e-at-point nil + "Error at point, after last command") + +(defvar flyc--e-display-timer nil + "A timer; when it fires, it displays the stored error message.") + +(defun flyc/maybe-fixup-message (errore) + "pyflake is flakey if it has compile problems, this adjusts the +message to display, so there is one ;)" + (cond ((not (or (eq major-mode 'Python) (eq major-mode 'python-mode) t))) + ((null (flymake-ler-file errore)) + ;; normal message do your thing + (flymake-ler-text errore)) + (t ;; could not compile error + (format "compile error, problem on line %s" (flymake-ler-line errore))))) + +(defun flyc/show-stored-error-now () + "Displays the stored error in the minibuffer." + (interactive) + (let ((editing-p (= (minibuffer-depth) 0))) + (if (and flyc--e-at-point editing-p) + (progn + (message "%s" (flyc/maybe-fixup-message flyc--e-at-point)) + (setq flyc--e-display-timer nil))))) + + +(defun flyc/-get-error-at-point () + "Gets the first flymake error on the line at point." + (let ((line-no (line-number-at-pos)) + flyc-e) + (dolist (elem flymake-err-info) + (if (eq (car elem) line-no) + (setq flyc-e (car (second elem))))) + flyc-e)) + + +;;;###autoload +(defun flyc/show-fly-error-at-point-now () + "If the cursor is sitting on a flymake error, display +the error message in the minibuffer." + (interactive) + (if flyc--e-display-timer + (progn + (cancel-timer flyc--e-display-timer) + (setq flyc--e-display-timer nil))) + (let ((error-at-point (flyc/-get-error-at-point))) + (if error-at-point + (progn + (setq flyc--e-at-point error-at-point) + (flyc/show-stored-error-now))))) + + +;;;###autoload +(defun flyc/show-fly-error-at-point-pretty-soon () + "If the cursor is sitting on a flymake error, grab the error, +and set a timer for \"pretty soon\". When the timer fires, the error +message will be displayed in the minibuffer. + +This allows a post-command-hook to NOT cause the minibuffer to be +updated 10,000 times as a user scrolls through a buffer +quickly. Only when the user pauses on a line for more than a +second, does the flymake error message (if any) get displayed. + +" + (if flyc--e-display-timer + (cancel-timer flyc--e-display-timer)) + + (let ((error-at-point (flyc/-get-error-at-point))) + (if error-at-point + (setq flyc--e-at-point error-at-point + flyc--e-display-timer + (run-at-time "0.9 sec" nil 'flyc/show-stored-error-now)) + (setq flyc--e-at-point nil + flyc--e-display-timer nil)))) + + +;;;###autoload +(eval-after-load "flymake" + '(progn + + (defadvice flymake-goto-next-error (after flyc/display-message-1 activate compile) + "Display the error in the mini-buffer rather than having to mouse over it" + (flyc/show-fly-error-at-point-now)) + + (defadvice flymake-goto-prev-error (after flyc/display-message-2 activate compile) + "Display the error in the mini-buffer rather than having to mouse over it" + (flyc/show-fly-error-at-point-now)) + + (defadvice flymake-mode (before flyc/post-command-fn activate compile) + "Add functionality to the post command hook so that if the +cursor is sitting on a flymake error the error information is +displayed in the minibuffer (rather than having to mouse over +it)" + (add-hook 'post-command-hook 'flyc/show-fly-error-at-point-pretty-soon t t)))) + + +(provide 'flymake-cursor) + +;;; flymake-cursor.el ends here From c73fae53dbe2c4edd50e12ede8c0da99fe0c7fc3 Mon Sep 17 00:00:00 2001 From: Jean-Sebastien Legare Date: Fri, 12 Aug 2016 17:21:26 -0700 Subject: [PATCH 4/8] [coverage] Inspect .js.in files (preprocessed source files / autotools) --- flymake-jshint.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flymake-jshint.el b/flymake-jshint.el index 06000de..64b9644 100644 --- a/flymake-jshint.el +++ b/flymake-jshint.el @@ -124,7 +124,7 @@ jshint-url))))) (setq flymake-allowed-file-name-masks - (cons '(".+\\.js$" + (cons '(".+\\.js\\(\\.in\\)?$" flymake-jshint-init flymake-simple-cleanup flymake-get-real-file-name) From cff30e7dcd29b3e57e4565f9fc9fb92fd7c0aa2c Mon Sep 17 00:00:00 2001 From: Jean-Sebastien Legare Date: Fri, 12 Aug 2016 17:22:34 -0700 Subject: [PATCH 5/8] [lint] Long line split --- jshint-emacs.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/jshint-emacs.js b/jshint-emacs.js index d86cb71..0931e03 100755 --- a/jshint-emacs.js +++ b/jshint-emacs.js @@ -1,8 +1,8 @@ #!/usr/bin/env node -var conf = '\n(add-to-list \'load-path "' + __dirname + '")\n' - + '(require \'flymake-jshint)\n' - + '(add-hook \'javascript-mode-hook\n' - + ' (lambda () (flymake-mode t)))'; +var conf = '\n(add-to-list \'load-path "' + __dirname + '")\n' + + '(require \'flymake-jshint)\n' + + '(add-hook \'javascript-mode-hook\n' + + '(lambda () (flymake-mode t)))'; -console.log(conf); \ No newline at end of file +console.log(conf); From a44cfde975c2b12e2cb4ae952112d5a2aec2cdb5 Mon Sep 17 00:00:00 2001 From: Jean-Sebastien Legare Date: Fri, 12 Aug 2016 17:25:07 -0700 Subject: [PATCH 6/8] [logging] Print amount of time (ms) taken to lint input file. --- jshint-mode.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jshint-mode.js b/jshint-mode.js index 642357f..8cc6d7b 100644 --- a/jshint-mode.js +++ b/jshint-mode.js @@ -93,11 +93,13 @@ var server = http.createServer(function(req, res) { form.parse(req, function(err, fields, files) { var mode = (fields.mode && fields.mode == "jslint") ? "jslint" : "jshint"; + var now = new Date().getTime(); console.log('Applying \'' + mode + '\' to: ' + (fields.filename || 'anonymous')); var config = _getConfig(fields.jshintrc); var results = lintify(mode, fields.source, fields.filename, config); + console.log('Took ' + (new Date().getTime() - now) + 'ms to lint ' + (fields.filename || 'anonymous')); res.writeHead(200, {'Content-Type': 'text/plain'}); res.end(results); }); From 2db42ca25fc5d2d7fea7d138d7e7b4eec73ae9c7 Mon Sep 17 00:00:00 2001 From: Jean-Sebastien Legare Date: Fri, 12 Aug 2016 17:35:31 -0700 Subject: [PATCH 7/8] [bugfix] Allow flymake to digest errors on long lines faster. (e.g. minified sections) This adds an HTTP POST parameter called 'showCode' to enable/disable showing code evidence lines after each error line. Hiding error code snippets dramatically reduces the amount of data returned. Errors in minified content would cause the full line to be returned for each error. Flymake is slow at parsing content, in blocks of 4KiB, to the point where using jshint-mode on minified files or files with long lines caused emacs to hang. the default value for showCode is '0', hiding content. --- jshint-mode.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/jshint-mode.js b/jshint-mode.js index 8cc6d7b..5704327 100644 --- a/jshint-mode.js +++ b/jshint-mode.js @@ -28,7 +28,7 @@ function getOpt(key) { return index !== -1 ? process.argv[index + 1] : false; } -function outputErrors(errors) { +function outputErrors(errors, showCode) { var e, i, output = []; @@ -40,17 +40,19 @@ function outputErrors(errors) { e = errors[i]; if (e) { out('Lint at line ' + e.line + ' character ' + e.character + ': ' + e.reason); - out((e.evidence || '').replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1")); - out(''); + if (showCode) { + out((e.evidence || '').replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1")); + out(''); + } } } return output.join(''); } -function lintify(mode, sourcedata, filename, config) { +function lintify(mode, sourcedata, filename, showCode, config) { var passed = hinters[mode](sourcedata, config); return passed ? "js: No problems found in " + filename + "\n" - : outputErrors(hinters[mode].errors); + : outputErrors(hinters[mode].errors, showCode); } // This is copied from jshint mode, that's how they load the config file @@ -92,13 +94,13 @@ var server = http.createServer(function(req, res) { var form = new formidable.IncomingForm(); form.parse(req, function(err, fields, files) { var mode = (fields.mode && fields.mode == "jslint") ? "jslint" : "jshint"; - + var showCode = (fields.showCode && fields.showCode === "1") ? true : false; var now = new Date().getTime(); console.log('Applying \'' + mode + '\' to: ' + (fields.filename || 'anonymous')); var config = _getConfig(fields.jshintrc); - var results = lintify(mode, fields.source, fields.filename, config); + var results = lintify(mode, fields.source, fields.filename, showCode, config); console.log('Took ' + (new Date().getTime() - now) + 'ms to lint ' + (fields.filename || 'anonymous')); res.writeHead(200, {'Content-Type': 'text/plain'}); res.end(results); From 3dffa6fe15453243f82fecba1153ab765b84921f Mon Sep 17 00:00:00 2001 From: Jean-Sebastien Legare Date: Fri, 12 Aug 2016 17:35:59 -0700 Subject: [PATCH 8/8] [documentation] Add server usage. --- jshint-mode.js | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/jshint-mode.js b/jshint-mode.js index 5704327..874141d 100644 --- a/jshint-mode.js +++ b/jshint-mode.js @@ -1,6 +1,37 @@ /* HTTP interface to JSHint. - curl --form source="