├── .github ├── dependabot.yml └── workflows │ └── test.yml ├── .gitignore ├── Eask ├── Makefile ├── README.md └── isortify.el /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: daily 7 | -------------------------------------------------------------------------------- /.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 | continue-on-error: ${{ matrix.experimental }} 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | os: [ubuntu-latest, macos-latest, windows-latest] 22 | emacs-version: 23 | - 26.3 24 | - 27.2 25 | - 28.2 26 | - 29.4 27 | - 30.1 28 | experimental: [false] 29 | include: 30 | - os: ubuntu-latest 31 | emacs-version: snapshot 32 | experimental: true 33 | - os: macos-latest 34 | emacs-version: snapshot 35 | experimental: true 36 | - os: windows-latest 37 | emacs-version: snapshot 38 | experimental: true 39 | exclude: 40 | - os: macos-latest 41 | emacs-version: 26.3 42 | - os: macos-latest 43 | emacs-version: 27.2 44 | 45 | steps: 46 | - uses: actions/checkout@v4 47 | 48 | - uses: jcs090218/setup-emacs@master 49 | with: 50 | version: ${{ matrix.emacs-version }} 51 | 52 | - uses: emacs-eask/setup-eask@master 53 | with: 54 | version: 'snapshot' 55 | 56 | - name: Run tests 57 | run: 58 | make ci 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore these directories 2 | /.git 3 | /recipes 4 | 5 | # ignore log files 6 | /.log 7 | 8 | # ignore generated files 9 | *.elc 10 | 11 | # eask packages 12 | .eask/ 13 | dist/ 14 | 15 | # packaging 16 | *-autoloads.el 17 | *-pkg.el 18 | 19 | # OS generated 20 | .DS_Store 21 | -------------------------------------------------------------------------------- /Eask: -------------------------------------------------------------------------------- 1 | ;; -*- mode: eask; lexical-binding: t -*- 2 | 3 | (package "isortify" 4 | "0.0.1" 5 | "(automatically) format python buffers using isort") 6 | 7 | (website-url "https://github.com/proofit404/isortify") 8 | (keywords "convenience" "isort") 9 | 10 | (package-file "isortify.el") 11 | 12 | (script "test" "echo \"Error: no test specified\" && exit 1") 13 | 14 | (source 'gnu) 15 | (source 'melpa) 16 | 17 | (depends-on "emacs" "25") 18 | (depends-on "pythonic") 19 | 20 | (setq network-security-level 'low) ; see https://github.com/jcs090218/setup-emacs-windows/issues/156#issuecomment-932956432 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | EMACS ?= emacs 2 | EASK ?= eask 3 | 4 | .PHONY: clean checkdoc lint package install compile test 5 | 6 | ci: clean package install compile 7 | 8 | package: 9 | @echo "Packaging..." 10 | $(EASK) package 11 | 12 | install: 13 | @echo "Installing..." 14 | $(EASK) install 15 | 16 | compile: 17 | @echo "Compiling..." 18 | $(EASK) compile 19 | 20 | test: 21 | @echo "Testing..." 22 | $(EASK) test ert ./test/*.el 23 | 24 | checkdoc: 25 | @echo "Run checkdoc..." 26 | $(EASK) lint checkdoc 27 | 28 | lint: 29 | @echo "Run package-lint..." 30 | $(EASK) lint package 31 | 32 | clean: 33 | $(EASK) clean all 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![MELPA](https://melpa.org/packages/isortify-badge.svg)](https://melpa.org/#/isortify) 2 | 3 | # isortify 4 | > (automatically) format python buffers using isort 5 | 6 | [![CI](https://github.com/pythonic-emacs/isortify/actions/workflows/test.yml/badge.svg)](https://github.com/pythonic-emacs/isortify/actions/workflows/test.yml) 7 | 8 | ## 💾 Quickstart 9 | 10 | To automatically format all Python buffers before saving, add the function 11 | `isortify-mode` to `python-mode-hook`: 12 | 13 | ```elisp 14 | (add-hook 'python-mode-hook 'isortify-mode) 15 | ``` 16 | -------------------------------------------------------------------------------- /isortify.el: -------------------------------------------------------------------------------- 1 | ;;; isortify.el --- (automatically) format python buffers using isort 2 | 3 | ;; Copyright (C) 2016-2018 Artem Malyshev 4 | 5 | ;; Author: Artem Malyshev 6 | ;; Homepage: https://github.com/proofit404/isortify 7 | ;; Version: 0.0.1 8 | ;; Package-Requires: ((emacs "25") (pythonic "0.1.0")) 9 | ;; Keywords: convenience isort 10 | 11 | ;; This file is free software; you can redistribute it and/or modify 12 | ;; it under the terms of the GNU General Public License as published 13 | ;; by the Free Software Foundation; either version 3, or (at your 14 | ;; option) any later version. 15 | ;; 16 | ;; This file is distributed in the hope that it will be useful, 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;; GNU General Public License for more details. 20 | ;; 21 | ;; For a full copy of the GNU General Public License 22 | ;; see . 23 | 24 | ;;; Commentary: 25 | ;; 26 | ;; Isortify uses isort to format a Python buffer. It can be called 27 | ;; explicitly on a certain buffer, but more conveniently, a minor-mode 28 | ;; 'isortify-mode' is provided that turns on automatically running isort 29 | ;; on a buffer before saving. 30 | ;; 31 | ;; Installation: 32 | ;; 33 | ;; Add isortify.el to your load-path. 34 | ;; 35 | ;; To automatically format all Python buffers before saving, add the function 36 | ;; isortify-mode to python-mode-hook: 37 | ;; 38 | ;; (add-hook 'python-mode-hook 'isortify-mode) 39 | ;; 40 | ;;; Code: 41 | 42 | (require 'pythonic) 43 | 44 | (defvar isortify-multi-line-output nil) 45 | 46 | (defvar isortify-trailing-comma nil) 47 | 48 | (defvar isortify-known-first-party nil) 49 | 50 | (defvar isortify-known-third-party nil) 51 | 52 | (defvar isortify-lines-after-imports nil) 53 | 54 | (defvar isortify-line-width nil) 55 | 56 | (defun isortify-call-bin (input-buffer output-buffer) 57 | "Call process isort on INPUT-BUFFER saving the output to OUTPUT-BUFFER. 58 | 59 | Return isort process the exit code." 60 | (with-current-buffer input-buffer 61 | (let ((process 62 | (pythonic-start-process :process "isortify" 63 | :buffer output-buffer 64 | :sentinel (lambda (process event)) 65 | :args `("-m" "isort" ,@(isortify-call-args))))) 66 | (process-send-region process (point-min) (point-max)) 67 | (process-send-eof process) 68 | (process-send-eof process) ;; Close pipe twice on purpose to prevent deadlock. 69 | (accept-process-output process nil nil t) 70 | (while (process-live-p process) 71 | (accept-process-output process nil nil t)) 72 | (process-exit-status process)))) 73 | 74 | (defun isortify-call-args () 75 | "Collect CLI arguments for isort process." 76 | (let (args) 77 | (when (string= "ipython" python-shell-interpreter) 78 | (push "--" args)) 79 | (when isortify-multi-line-output 80 | (push "--multi-line" args) 81 | (push (number-to-string isortify-multi-line-output) args)) 82 | (when isortify-trailing-comma 83 | (push "--trailing-comma" args)) 84 | (when isortify-known-first-party 85 | (dolist (project isortify-known-first-party) 86 | (push "--project" args) 87 | (push project args))) 88 | (when isortify-known-third-party 89 | (dolist (thirdparty isortify-known-third-party) 90 | (push "--thirdparty" args) 91 | (push thirdparty args))) 92 | (when isortify-lines-after-imports 93 | (push "--lines-after-imports" args) 94 | (push (number-to-string isortify-lines-after-imports) args)) 95 | (when isortify-line-width 96 | (push "--line-width" args) 97 | (push (number-to-string isortify-line-width) args)) 98 | (push "-" args) 99 | (reverse args))) 100 | 101 | ;;;###autoload 102 | (defun isortify-buffer (&optional display) 103 | "Try to isortify the current buffer. 104 | 105 | Show isort output, if isort exit abnormally and DISPLAY is t." 106 | (interactive (list t)) 107 | (let* ((original-buffer (current-buffer)) 108 | (original-point (point)) 109 | (original-window-pos (window-start)) 110 | (tmpbuf (get-buffer-create "*isortify*"))) 111 | ;; This buffer can be left after previous isort invocation. It 112 | ;; can contain error message of the previous run. 113 | (with-current-buffer tmpbuf 114 | (erase-buffer)) 115 | (condition-case err 116 | (if (not (zerop (isortify-call-bin original-buffer tmpbuf))) 117 | (error "Isort failed, see %s buffer for details" (buffer-name tmpbuf)) 118 | (with-current-buffer tmpbuf 119 | (ansi-color-filter-region (point-min) (point-max)) 120 | (copy-to-buffer original-buffer (point-min) (point-max))) 121 | (kill-buffer tmpbuf) 122 | (goto-char original-point) 123 | (set-window-start (selected-window) original-window-pos)) 124 | (error (message "%s" (error-message-string err)) 125 | (when display 126 | (pop-to-buffer tmpbuf)))))) 127 | 128 | ;;;###autoload 129 | (define-minor-mode isortify-mode 130 | "Automatically run isort before saving." 131 | :lighter " Isort" 132 | (if isortify-mode 133 | (add-hook 'before-save-hook 'isortify-buffer nil t) 134 | (remove-hook 'before-save-hook 'isortify-buffer t))) 135 | 136 | (provide 'isortify) 137 | 138 | ;;; isortify.el ends here 139 | --------------------------------------------------------------------------------