diff --git a/flymake-ruff.el b/flymake-ruff.el index 77f7187..7dd7380 100644 --- a/flymake-ruff.el +++ b/flymake-ruff.el @@ -35,10 +35,53 @@ (defvar flymake-ruff--output-regex "\\(.*\\):\\([0-9]+\\):\\([0-9]+\\): \\([A-Za-z0-9]+\\):? \\(.*\\)") +(defvar flymake-ruff--no-config-tramp-dirs + nil + "List of TRAMP directories that ruff checked for config in but found none. This prevents repeatedly looking for config files that do not exist.") + (defconst flymake-ruff--default-configs '(".ruff.toml" "ruff.toml" "pyproject.toml") "Default configuration files supported by Ruff.") +(defun flymake-ruff--get-config () + "Look for configuration files supported by Ruff in project root. +When project is on remote host, cache config using `default-directory' as key." + (if (and (tramp-handle-file-remote-p default-directory)) + (when (not (seq-contains-p flymake-ruff--no-config-tramp-dirs default-directory #'string-equal)) + (let ((cache-dir (expand-file-name + (concat "ruff-config-" (sha1 default-directory)) + "/tmp"))) + (if (file-directory-p cache-dir) + ;; Return cached config if exists + (seq-find #'file-readable-p + (mapcar (lambda (f) + (expand-file-name f cache-dir)) + flymake-ruff--default-configs)) + ;; Create cache and return config path + (if-let* ((project-current (project-current)) + (config (seq-find + #'tramp-handle-file-readable-p + (mapcar (lambda (f) + (tramp-handle-expand-file-name f (project-root project-current))) + flymake-ruff--default-configs))) + (temp-file (tramp-handle-expand-file-name (tramp-handle-file-name-nondirectory config) cache-dir)) + (temp-copy (tramp-handle-file-local-copy config))) + (progn + (make-directory cache-dir) + (copy-file temp-copy temp-file t) + (delete-file temp-copy) + temp-file) + (progn + (push default-directory flymake-ruff--no-config-tramp-dirs) + nil))))) + + ;; Local project path + (when-let ((project-current (project-current))) + (seq-find #'file-readable-p + (mapcar (lambda (f) + (expand-file-name f (project-root project-current))) + flymake-ruff--default-configs))))) + (defun flymake-ruff--check-buffer () "Generate a list of diagnostics for the current buffer." (let ((code-buffer (current-buffer)) @@ -52,14 +95,8 @@ ;; check if the current buffer belongs to a project before ;; trying to build a path using `project-current' otherwise it ;; will fail silently - (let* ((config (and (project-current) - (seq-find - #'file-readable-p - (mapcar - (lambda (f) - (expand-file-name f (project-root (project-current)))) - flymake-ruff--default-configs)))) - (args (if config + (let* ((config (flymake-ruff--get-config)) + (args (if config ;; for version > 0.5 the work "check" is ;; included so we need to extract it and put it ;; before --config argument @@ -94,6 +131,14 @@ "Load hook for the current buffer to tell flymake to run checker." (interactive) (when (derived-mode-p 'python-mode 'python-ts-mode) + ;; If cached TRAMP config file exists for current buffer, remove it so that cache can be refreshed on reverting buffer. + (when-let* ((_remote (file-remote-p default-directory)) + (cache-dir (expand-file-name + (concat "ruff-config-" (sha1 default-directory)) + "/tmp")) + (_cache_exist (file-directory-p cache-dir))) + (delete-directory cache-dir t)) + (add-hook 'flymake-diagnostic-functions #'flymake-ruff--run-checker nil t))) (defun flymake-ruff--run-checker (report-fn &rest _args)