reddit / emacs

linting on save

StrangeAstronomer · 2022-06-26 05:52
Encouraged by comments in [https://www.reddit.com/r/emacs/comments/vjo43z/improving\_emacs\_performance/](https://www.reddit.com/r/emacs/comments/vjo43z/improving_emacs_performance/) \* I thought I'd have another look at my 'linting on save' snippet and ask the deep-thinkers here how I might improve it. It's almost perfect for me with **bash**(1) scripts - the only sand in the vaseline is that `next-error` skips over **shellcheck**(1) 'notes' and only points me at 'warnings' and worse. For example, in this output from **shellcheck**(1): foobar:5:18: note: Double quote to prevent globbing and word splitting. [SC2086] foobar:20:28: warning: ShellCheck can't follow non-constant source. Use a directive to specify location. [SC1090] I would like `next-error` to visit occurrences of 'note's as well as more serious blues. Do I need to futz with `next-error-function`? If so, does anyone have the code for that? Or perhaps there's some way to have **shellcheck**(1) promote 'notes' to 'warnings'? Here's my miserable code: (defun bh:check-syntax () "Check syntax for various languages." (interactive) (when (eq major-mode 'lisp-mode) (progn (emacs-lisp-byte-compile))) (when (eq major-mode 'sh-mode) (compile (format "bash -n %s && shellcheck -f gcc %s" buffer-file-name buffer-file-name) t)) (when (eq major-mode 'ruby-mode) (shell-command (format "ruby -c %s" buffer-file-name))) (when (eq major-mode 'python-mode) (shell-command (format "python -B -m py_compile %s" buffer-file-name))) (when (eq major-mode 'awk-mode) (shell-command (format "gawk --source 'BEGIN { exit(0) } END { exit(0) }' --file %s" buffer-file-name)))) (add-hook 'after-save-hook #'bh:check-syntax) \* the sub-discussion was about disabling flycheck as it's just too distracting - I prefer to do linting on save, and so do others, apparently. **EDIT:** Finally got something working, thanks to u/ieure Here's my code (sorry if it stinks). It looks like the **shellcheck**(1) code for `next-error` does not support the default syntax nor the `shellcheck -f gcc syntax` (nor any other **shellcheck**(1) output syntax). (defun bh:check-syntax () "Check syntax for various languages." (interactive) (when (eq major-mode 'lisp-mode) (progn (emacs-lisp-byte-compile))) (when (eq major-mode 'sh-mode) (compile (format "bash -n %s && shellcheck -f gcc %s" buffer-file-name buffer-file-name) t)) (when (eq major-mode 'ruby-mode) (shell-command (format "ruby -c %s" buffer-file-name))) (when (eq major-mode 'python-mode) (shell-command (format "python -B -m py_compile %s" buffer-file-name))) (when (eq major-mode 'awk-mode) (shell-command (format "gawk --source 'BEGIN { exit(0) } END { exit(0) }' --file %s" buffer-file-name)))) (setq compilation-error-regexp-alist (append '(shellcheck-mine) compilation-error-regexp-alist)) (setq compilation-error-regexp-alist-alist (append '((shellcheck-mine "^\\([^:]+\\):\\([0-9]+\\):\\([0-9]+\\): .*" 1 2 3)) compilation-error-regexp-alist-alist)) (add-hook 'after-save-hook #'bh:check-syntax)
redguardtoo · 2022-06-26 08:01
In `flymake-mode`, you can `(setq flymake-no-changes-timeout nil)`, so flymake lints in `after-save-hook` only. In defintion of `flymake-mode`, there is below code, ```lisp (add-hook 'after-change-functions 'flymake-after-change-function nil t) (add-hook 'after-save-hook 'flymake-after-save-hook nil t) (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t) ``` And `flymake-after-change-function` calls `flymake--schedule-timer-maybe` which checks `flymake-no-changes-timeout`. Emacs 28.1. I prefer `flymake` because it's very possible that someone else has already set up it for certain language. If you cares about generic performance, check my emacs distribution, https://github.com/redguardtoo/emacs.d where lots of performance improving techniques are applied. For example, I don't use `flyspell-mode` but my own package `wucuo`. It only checks visible region when user saving current buffer. So it's much faster than `flyspell-mode`.
_noctuid · 2022-06-26 11:44
Like I said in the other discussion, you can use flycheck without having constant popups, but if linting on save only is what you want, why not just keep using flycheck and have it only lint on save (by customizing `flycheck-check-syntax-automatically`)? Or you can use flymake as suggested by redguardtoo. No new code is needed. Personally, I'd just remove checking on `new-line` and increase the idle delay to check with flycheck. At this point I don't find flycheck distracting though. I automatically ignore it unless I need it.
ieure · 2022-06-26 16:04
> I would like next-error to visit occurrences of 'note's as well as more serious blues. Do I need to futz with next-error-function? If so, does anyone have the code for that? No, `next-error` uses `compilation-error-regexp-alist` to match output, you need to change that to match the other lines you want to consider errors or warnings.
StrangeAstronomer · 2022-06-26 23:21
OK, thanks ... after an enjoyable little scan through the docs and more googling, that hint led me to the following execreble chunk of code which seems to do what I want for the limited amount of testing that I've done. No doubt I'll have to improve the regex: (when (eq major-mode 'sh-mode) (progn (pushnew '(shellcheck-mine "^\\([^:]+\\):\\([0-9]+\\):\\([0-9]+\\): .*" 1 2 3) compilation-error-regexp-alist-alist) (setq compilation-error-regexp-alist '(shellcheck-mine)) (compile (format "bash -n %s && shellcheck -f gcc %s" buffer-file-name buffer-file-name) t))) Not that I can read the compile.el code (well above my pay-grade) but it seems that `compile` uses each of its dozens of regexps until it finds one that fits the data. So I'm not sure what short-circuiting the value of `compilation-error-regexp-alist` to a single regexp will do to things. Of course, I've also blundered by changing the global values of `compilation-error-regep-alist[-alist]` and I need to save and restore them.
StrangeAstronomer · 2022-06-27 02:58
Well, I think I've got something that work - I edited the OP with my solution. Thanks again.