# Have flycheck skip certain regions

I'm using flycheck to check my .tex sources but find that the syntax checking is too comprehensive. In particular, I'm interested in skipping syntax checking of certain environments such as code listings. So essentially, I'd need to some way to tell flycheck to ignore the contents of myEnv but check the rest of the document:

\begin{document}
Some text.

\begin{myEnv}
env content
\end{myEmv}

\end{document}


Perhaps an even better solution would be to allow one to use comments to manually mark regions that should be ignored:

\begin{document}
Some text.

% [BEGIN_FLYCHECK_IGNORE]
\begin{myEnv}
env content
\end{myEmv}
% [END_FLYCHECK_IGNORE]

\end{document}


This approach could potentially work in other modes that use different syntax for comments.

What is the best way to achieve this?

This is a variation of the question How can I disable a specific lint error for Emacs Lisp using Flycheck?.

The following Elisp code is the code from the answer there adapted to your case of marked regions to be ignored by flycheck (I call them noflycheck regions).

It exploits the hook-like variable flycheck-process-error-functions. The relevant section of its doc-string:

Each function in this hook must accept a single argument: A Flycheck error to process.

All functions in this hook are called in order of appearance, until a function returns non-nil. Thus, a function in this hook may return nil, to allow for further processing of the error, or any non-nil value, to indicate that the error was fully processed and inhibit any further processing.

The code registers the function noflycheck there. That function searches backwards for a bound of a noflycheck region. If the first found bound is the beginning of a noflycheck region the error is canceled by a non-nil return value.

You can customize the option noflycheck-region-regexps to change the regular expressions marking the beginning and the end of the noflycheck region. You can also determine by that option whether the markers should only be recognized in comments or only in code. Furthermore, that option also allows you to ignore markers within strings.

It is clear that many enhancements would be possible (e.g., major mode dependent noflycheck region matchers and a set of noflycheck regions instead of just one). But the implementation of those features would make the solution more incomprehensible.

(defun noflycheck--region-regexp (&optional symbol value)
"Set noflycheck--region-regexp' from customization of noflycheck-region-regexps'."
(when (prog1
(or symbol value)
(unless symbol (setq symbol 'noflycheck-region-regexps))
(unless value (setq value (default-value 'noflycheck-region-regexps))))
(set-default symbol value))
(setq noflycheck--region-regexp
(concat "\$$?:\\(" (car value) "\$$\\|" (cadr value) "\\)")
noflycheck--where (cddr value)))

(defcustom noflycheck-region-regexps '("\$BEGIN_FLYCHECK_IGNORE\$" "\$END_FLYCHECK_IGNORE\$" comment)
"Cons of markers to mark the beginning and the end of a noflycheck region.
The two regexps may not match the same string."
:type '(cons :tag ""
(regexp :tag "Begin marker")
(cons :tag ""
(regexp :tag "End marker")
(set
(const nostring :tag "Don't match in strings.")
(choice (const comment :tag "Only match in comments.")
(const code :tag "Only match in code.")))))
:set #'noflycheck--region-regexp
:group 'flycheck)

(defvar noflycheck--region-regexp nil
"Regular expression for matching beginning and end of noflycheck regions.
The regular expression is generated from noflycheck-region-regexps'
by function noflycheck--region-regexp'.
If the regular expression matches the beginning of a noflycheck region
it is captured in group 1.
If it matches the end of a noflycheck region group 1 does not match,
i.e., (match-beginning 1) gives nil.")

(defvar noflycheck--where nil
"Set by function noflycheck--region-regexp'.
Possible members:
comment Match beginning and end of noflycheck regions only in comments.
code")

(noflycheck--region-regexp)

(defsubst noflycheck-in-comment-p ()
"Non-nil if point is in comment."
(nth 4 (syntax-ppss)))

(defsubst noflycheck-in-string-p ()
"Non-nil if point is in comment."
(nth 3 (syntax-ppss)))

(defun noflycheck-re-search-backward (&rest args)
"Do re-search-forward' but consider noflycheck--where'."
(let (found)
(while (and
(setq found (apply #'re-search-backward args))
(cond
((memq 'comment noflycheck--where)
(null (noflycheck-in-comment-p)))
((memq 'nostring noflycheck--where)
(or (noflycheck-in-string-p) ;; strings only occur in code
(and (memq 'code noflycheck--where)
(noflycheck-in-comment-p))))
(memq 'code noflycheck--where)
(noflycheck-in-comment-p))))
found))

(defun noflycheck-region (err)
"Ignore flycheck if ERR is in region marked with regexps from noflycheck-regions'."
(save-excursion
(goto-char (car (flycheck-error-line-region err)))
(and (noflycheck-re-search-backward noflycheck--region-regexp nil 'noError)
(match-beginning 1))))

(defvar noflycheck-process-error-functions nil
"Like flycheck-process-error-functions'.
But should only include the filters and not the actual action.")

(defun noflycheck-hook-fun ()
"Add the noflycheck markers to ."
(require 'flycheck)
(lambda (err)
(run-hook-with-args-until-success 'noflycheck-process-error-functions err))
nil t))

(defvar flycheck-error-list-source-buffer)

(defun noflycheck-error-list-filter (errors)
"Only let through ERRORS accepted by error-list-process-error-functions'.
Works as :filter-args advice if FILTER-ARGS is non-nil."
(cl-loop for err in errors
unless
(let ((buf (flycheck-error-buffer err)))
(when (buffer-live-p buf)
(with-current-buffer buf
(run-hook-with-args-until-success 'noflycheck-process-error-functions err))))
collect err))



Tested with: emacs -Q
emacs-version: GNU Emacs 26.1 (build 2, x86_64-pc-linux-gnu, GTK+ Version 3.22.30) of 2018-05-29
Flycheck version: 32snapshot (package: 20171214.1215)
flycheck-checker: tex-lacheck

Test file:

\documentclass{article}
\usepackage{amsmath,amsfonts}
\begin{document}
Some text before the code block.
Period in the. middle of a sentence.

% [BEGIN_FLYCHECK_IGNORE]
\begin{myEnv}
env content
A sentence. with a dot in the middle.
\some \undefined \command.
\end{myEmv}
% [END_FLYCHECK_IGNORE]

Some text behind the code block.
Give Flycheck. something todo. sequence separated by space.
\end{document}


The second text line and the last text line are marked. Nothing is marked within the bounds [BEGIN_FLYCHECK_IGNORE] and [END_FLYCHECK_IGNORE].

• Many thanks for this excellent answer! After some initial testing I find that the warnings associated with the noflycheck region are not shown in the fringe but still appear if you list them using flycheck-list-errors. Is there some way to improve this? Mar 27 '19 at 9:42
• @PeterW.V.Tran-Jørgensen Please test again. Mar 27 '19 at 10:49
• That fixed the flycheck-list-errors issue, thanks! After some additional testing the only problem I found is related to the errors still appearing in flycheck-current-errors. This causes integration issues with packages such as spaceline which shows the number of errors, warning and info messages in the model-line, which is calculated as follows: (flycheck-count-errors flycheck-current-errors). Mar 27 '19 at 13:19
• @PeterW.V.Tran-Jørgensen One yould also add an appropriate :error-filter to the applied checkers. But that solution would not be that general. Mar 27 '19 at 14:42
• @PeterW.V.Tran-Jørgensen Now I advise flycheck-filter-errors instead of flycheck-error-list-filter. That should solve the problem with the wrong counts. Mar 27 '19 at 15:12