├── .github └── workflows │ └── test.yml ├── .gitignore ├── Cask ├── Eask ├── Elsafile.el ├── README.md └── flycheck-elsa.el /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | test: 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | os: [ubuntu-latest, macos-latest, windows-latest] 21 | emacs-version: 22 | - 26.3 23 | - 27.2 24 | - 28.2 25 | - snapshot 26 | 27 | steps: 28 | - uses: actions/checkout@v3 29 | 30 | - uses: jcs090218/setup-emacs@master 31 | with: 32 | version: ${{ matrix.emacs-version }} 33 | 34 | # Remove expired DST Root CA X3 certificate. Workaround for 35 | # https://debbugs.gnu.org/cgi/bugreport.cgi?bug=51038 bug on Emacs 27.x or lower. 36 | # https://github.com/jcs090218/setup-emacs-windows/issues/156#issuecomment-1126671598 37 | - name: Workaround for Emacs 27.x or lower's Windows build from GNU FTP 38 | if: ${{ runner.os == 'Windows' && (matrix.emacs-version == '26.3' || matrix.emacs-version == '27.2') }} 39 | run: | 40 | gci cert:\LocalMachine\Root\DAC9024F54D8F6DF94935FB1732638CA6AD77C13 41 | gci cert:\LocalMachine\Root\DAC9024F54D8F6DF94935FB1732638CA6AD77C13 | Remove-Item 42 | 43 | - uses: emacs-eask/setup-eask@master 44 | with: 45 | version: 'snapshot' 46 | 47 | - name: Run tests 48 | run: | 49 | eask clean all 50 | eask package 51 | eask install 52 | eask install-deps 53 | eask compile 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cask/* 2 | .eask/* 3 | .elsa/* 4 | 5 | /dist 6 | -------------------------------------------------------------------------------- /Cask: -------------------------------------------------------------------------------- 1 | (source melpa) 2 | (source gnu) 3 | 4 | (package "flycheck-elsa" "1.0.0" "Flycheck for Elsa.") 5 | 6 | (depends-on "seq" "2.0") 7 | (depends-on "emacs" "25") 8 | 9 | (development 10 | (depends-on "trinary" "0.0.1") 11 | (depends-on "elsa" "0.0.1")) 12 | -------------------------------------------------------------------------------- /Eask: -------------------------------------------------------------------------------- 1 | (package "flycheck-elsa" "1.0.0" "Flycheck for Elsa") 2 | 3 | (website-url "https://github.com/emacs-elsa/flycheck-elsa") 4 | (keywords "convenience") 5 | 6 | (package-file "flycheck-elsa.el") 7 | 8 | (files "flycheck-elsa-*.el") 9 | 10 | (script "test" "echo \"Error: no test specified\" && exit 1") 11 | 12 | (source 'gnu) 13 | (source 'melpa) 14 | 15 | (depends-on "emacs" "25") 16 | (depends-on "flycheck" "0.14") 17 | (depends-on "seq" "2.0") 18 | 19 | (development 20 | (depends-on "trinary" "0.0.1") 21 | (depends-on "elsa" "0.0.1")) 22 | -------------------------------------------------------------------------------- /Elsafile.el: -------------------------------------------------------------------------------- 1 | (register-extensions 2 | dash 3 | eieio 4 | cl 5 | elsa 6 | seq 7 | flycheck 8 | ) 9 | 10 | (register-ruleset 11 | dead-code 12 | style 13 | error 14 | variables 15 | elsa 16 | ) 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![MELPA](https://melpa.org/packages/flycheck-elsa-badge.svg)](https://melpa.org/#/flycheck-elsa) 2 | [![CI](https://github.com/emacs-elsa/flycheck-elsa/actions/workflows/test.yml/badge.svg)](https://github.com/emacs-elsa/flycheck-elsa/actions/workflows/test.yml) 3 | 4 | # flycheck-elsa - Flycheck for Elsa 5 | 6 | Integration of [Elsa](https://github.com/emacs-elsa/Elsa) into [Flycheck](https://github.com/flycheck/flycheck). 7 | 8 | ## Installation 9 | 10 | Install `flycheck-elsa` from [MELPA](http://melpa.org/) or [MELPA 11 | Stable](http://stable.melpa.org/) and add the following to your 12 | `init.el`: 13 | 14 | ``` emacs-lisp 15 | (add-hook 'emacs-lisp-mode-hook #'flycheck-elsa-setup) 16 | ``` 17 | 18 | ## How do I use this? 19 | 20 | The recommended way to use Elsa is with [Eask][Eask] or [Cask][Cask]. 21 | 22 | ```emacs-lisp 23 | (setq flycheck-elsa-backend 'cask) ; or 'eask 24 | ``` 25 | 26 | ### Eask 27 | 28 | This method uses [Eask][Eask] and installs Elsa from [MELPA][MELPA]. 29 | 30 | 1. Make sure you have [Eask][Eask] installed and presented in your PATH environment. 31 | 2. You need an Eask-file in your project, you can create it via `eask init` command 32 | 3. Add `(depends-on "elsa")` to your Eask-file 33 | 4. Run `eask install-deps` 34 | 35 | You are ready to go! Open an elisp file and `M-x flycheck-mode`! 36 | 37 | ### Cask 38 | 39 | We require that `cask` executable is usable from Emacs. You can test 40 | this by evaluating `(executable-find "cask")`. If this returns `nil`, 41 | you need to add your cask directory to `exec-path`. With the default 42 | Cask installation evaluating the following snippet should be enough: 43 | 44 | ``` emacs-lisp 45 | (push (format "/home/%s/.cask/bin/" (user-login-name)) exec-path) 46 | ``` 47 | 48 | You can also use the amazing 49 | [exec-path-from-shell](https://github.com/purcell/exec-path-from-shell) 50 | to initialize your `exec-path` from your shell's `$PATH`. 51 | 52 | ## Usage 53 | 54 | Just use Flycheck as usual in your [Cask](https://github.com/cask/cask) projects. 55 | 56 | [Cask]: https://github.com/cask/cask 57 | [Eask]: https://github.com/emacs-eask/cli 58 | [MELPA]: https://melpa.org 59 | -------------------------------------------------------------------------------- /flycheck-elsa.el: -------------------------------------------------------------------------------- 1 | ;;; flycheck-elsa.el --- Flycheck for Elsa -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2018 Matúš Goljer 4 | 5 | ;; Author: Matúš Goljer 6 | ;; Maintainer: Matúš Goljer 7 | ;; Version: 1.0.0 8 | ;; Created: 23rd August 2018 9 | ;; Package-requires: ((emacs "25") (flycheck "0.14") (seq "2.0")) 10 | ;; Keywords: convenience 11 | ;; Homepage: https://github.com/emacs-elsa/flycheck-elsa 12 | 13 | ;; This program is free software; you can redistribute it and/or 14 | ;; modify it under the terms of the GNU General Public License 15 | ;; as published by the Free Software Foundation; either version 3 16 | ;; of the License, or (at your option) any later version. 17 | 18 | ;; This program is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; You should have received a copy of the GNU General Public License 24 | ;; along with this program. If not, see . 25 | 26 | ;;; Commentary: 27 | 28 | ;; Flycheck integration for Elsa. See README.md 29 | 30 | ;;; Code: 31 | 32 | (require 'flycheck) 33 | (require 'cl-lib) 34 | (require 'seq) 35 | 36 | (defgroup flycheck-elsa nil 37 | "Flycheck integration for Elsa" 38 | :prefix "flycheck-elsa-" 39 | :group 'flycheck 40 | :link '(url-link :tag "Github" "https://github.com/emacs-elsa/flycheck-elsa")) 41 | 42 | (defcustom flycheck-elsa-backend 'cask 43 | "Choose what backend to use to power it up. 44 | The value can only be the symbol `cask' or `eask'. 45 | 46 | If value is `cask', then Cask will be used as the backend to check for the 47 | current file. This means you must have a Cask-file presented in your project. 48 | 49 | If value is `eask', the conditions are very similar to Cask. But instead, you 50 | will need an Eask-file and not Cask-file." 51 | :group 'flycheck-elsa 52 | :type '(choice 53 | (const :tag "Cask" 'cask) 54 | (const :tag "Eask" 'eask))) 55 | 56 | (defcustom flycheck-elsa-ignored-files-regexps '( 57 | "\\`Cask\\'" 58 | "\\`Eask\\'" 59 | ) 60 | "List of regular expressions matching files which should be ignored by Elsa." 61 | :group 'flycheck-elsa 62 | :type '(repeat regexp)) 63 | 64 | (defun flycheck-elsa--config-file () 65 | "Return the target configuration filename by variable `flycheck-elsa-backend'." 66 | (cl-case flycheck-elsa-backend 67 | (`cask "Cask") 68 | (`eask "Eask") 69 | (t (user-error "Unknown Elsa backend: %s" flycheck-elsa-backend)))) 70 | 71 | (defun flycheck-elsa--locate-config-dir () 72 | "Return dir located config file. If missing, return nil." 73 | (when-let* ((filename (buffer-file-name)) 74 | (config (flycheck-elsa--config-file)) 75 | (file (locate-dominating-file filename config))) 76 | (file-name-directory file))) 77 | 78 | (defun flycheck-elsa--elsa-dependency-p () 79 | "Return non-nil if elsa listed in config file dependency." 80 | (let ((config-dir (flycheck-elsa--locate-config-dir)) 81 | (config (flycheck-elsa--config-file))) 82 | (with-temp-buffer 83 | (insert-file-contents (expand-file-name config config-dir)) 84 | (let* ((contents (read (format "(%s)" (buffer-string)))) 85 | (deps (append 86 | (mapcan 87 | (lambda (elm) 88 | (and (eq 'depends-on (car elm)) (list elm))) 89 | contents) 90 | (mapcan 91 | (lambda (elm) 92 | (and (eq 'development (car elm)) 93 | (mapcan 94 | (lambda (elm) 95 | (and (eq 'depends-on (car elm)) (list elm))) 96 | (cdr elm)))) 97 | contents)))) 98 | (and (delq 'nil (mapcar 99 | (lambda (elm) (string= "elsa" (nth 1 elm))) deps)) 100 | t))))) ; normarize return value 101 | 102 | (defun flycheck-elsa--enable-p () 103 | "Return non-nil if we can enable Elsa in current buffer. 104 | 105 | We require that the project is managed by config file and that Elsa is 106 | listed as a dependency." 107 | (when-let (config-dir (flycheck-elsa--locate-config-dir)) 108 | (let ((default-directory config-dir)) 109 | (and (buffer-file-name) 110 | (not (seq-find (lambda (f) (string-match-p f (buffer-file-name))) 111 | flycheck-elsa-ignored-files-regexps)) 112 | (flycheck-elsa--elsa-dependency-p))))) 113 | 114 | (defun flycheck-elsa--working-directory (&rest _) 115 | "Return the working directory where the checker should run." 116 | (if (buffer-file-name) 117 | (flycheck-elsa--locate-config-dir) 118 | default-directory)) 119 | 120 | (defun flycheck-elsa-command () 121 | "Return a list of strings for the command to execute and its arguments. 122 | The result depends on the variable `flycheck-elsa-backend'." 123 | (cl-case flycheck-elsa-backend 124 | (`cask '("cask" "exec" "elsa")) 125 | (`eask '("eask" "exec" "elsa")) 126 | (t (user-error "Unknown Elsa backend: %s" flycheck-elsa-backend)))) 127 | 128 | (flycheck-define-checker emacs-lisp-elsa 129 | "An Emacs Lisp checker using Elsa." 130 | :command (;; flycheck forces us to pass a string as first argument: 131 | ;; https://github.com/flycheck/flycheck/issues/1515 132 | "emacs" 133 | (eval (cdr (flycheck-elsa-command))) 134 | source-inplace) 135 | :working-directory flycheck-elsa--working-directory 136 | :predicate flycheck-elsa--enable-p 137 | :error-filter flycheck-increment-error-columns 138 | :error-patterns 139 | ((error line-start (file-name) ":" line ":" column ":error:" (message (one-or-more not-newline) (* "\n" (one-or-more " ") (one-or-more not-newline)))) 140 | (warning line-start (file-name) ":" line ":" column ":warning:" (message (one-or-more not-newline) (* "\n" (one-or-more " ") (one-or-more not-newline)))) 141 | (info line-start (file-name) ":" line ":" column ":notice:" (message (one-or-more not-newline) (* "\n" (one-or-more " ") (one-or-more not-newline))))) 142 | :modes (emacs-lisp-mode)) 143 | 144 | (defun flycheck-elsa--setup-executable () 145 | "Configure `flycheck-emacs-lisp-elsa-executable'." 146 | (setq-local flycheck-emacs-lisp-elsa-executable 147 | (car (flycheck-elsa-command)))) 148 | 149 | ;;;###autoload 150 | (defun flycheck-elsa-setup () 151 | "Setup Flycheck with Elsa." 152 | (interactive) 153 | (add-to-list 'flycheck-checkers 'emacs-lisp-elsa) 154 | (add-hook 'flycheck-before-syntax-check-hook #'flycheck-elsa--setup-executable)) 155 | 156 | (provide 'flycheck-elsa) 157 | ;;; flycheck-elsa.el ends here 158 | --------------------------------------------------------------------------------