diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index fdc3930..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,35 +0,0 @@ -version: 2 -shared: &shared - machine: - image: ubuntu-1604:201903-01 - steps: - - checkout - # This command will pick up $VERSION from the environment. - - run: >- - make docker - CMD="make -k compile checkdoc longlines" -jobs: - emacs-25: - <<: *shared - environment: - VERSION: "25" - emacs-26: - <<: *shared - environment: - VERSION: "26" - emacs-27: - <<: *shared - environment: - VERSION: "27" - emacs-git: - <<: *shared - environment: - VERSION: "master" -workflows: - version: 2 - ci: - jobs: - - emacs-25 - - emacs-26 - - emacs-27 - - emacs-git diff --git a/.github/workflows/formatters.yml b/.github/workflows/formatters.yml new file mode 100644 index 0000000..b114eea --- /dev/null +++ b/.github/workflows/formatters.yml @@ -0,0 +1,23 @@ +name: Changed formatters +on: [pull_request] +jobs: + formatters: + runs-on: ubuntu-latest + steps: + - name: Checkout pull request + uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Fetch master + run: | + git fetch + - name: Test changed formatters + run: | + set -euo pipefail + + mkdir -p .tmp + make docker CMD="make fmt-changed > .tmp/changed" + export FORMATTERS="$(< .tmp/changed)" + if [[ -n "${FORMATTERS}" ]]; then + make fmt-build fmt-docker CMD="make fmt-test" + fi diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..34fde49 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,16 @@ +name: Lint +on: [push] +jobs: + lint: + runs-on: ubuntu-latest + strategy: + matrix: + emacs_version: [25, 26, 27, "master"] + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Run linters + env: + VERSION: ${{ matrix.emacs_version }} + run: >- + make docker CMD="make lint" diff --git a/.gitignore b/.gitignore index c531d98..9a66d3e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ *.elc +.log +.tmp +auto diff --git a/Makefile b/Makefile index f80d759..1f3ccd2 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,8 @@ CMD ?= EMACS ?= emacs +TAG ?= latest + # The order is important for compilation. for_compile := *.el for_checkdoc := *.el @@ -17,7 +19,7 @@ help: ## Show this message column -t -s'|' >&2 .PHONY: lint -lint: compile checkdoc longlines ## Build project and run all linters +lint: compile checkdoc longlines fmt-lint ## Build project and run all linters .PHONY: compile compile: ## Check for byte-compiler errors @@ -52,3 +54,23 @@ clean: ## Remove build artifacts .PHONY: docker docker: ## Start a Docker shell; e.g. make docker VERSION=25.3 @scripts/docker.bash "$(VERSION)" "$(CMD)" + +.PHONY: fmt-build # env vars: FORMATTERS, TAG +fmt-build: ## Build a Docker image with formatters installed + @test/formatters/build-image.bash + +.PHONY: fmt-docker # env var: TAG +fmt-docker: ## Start a Docker shell for testing formatters + @scripts/docker-run.bash -e FORMATTERS "apheleia-formatters:$(TAG)" "$(CMD)" + +.PHONY: fmt-lint +fmt-lint: ## Do basic linting for formatter configuration + @test/formatters/run-func.bash apheleia-ft-lint + +.PHONY: fmt-check +fmt-changed: ## Get list of changed formatters on this PR + @test/formatters/run-func.bash apheleia-ft-changed + +.PHONY: fmt-test # env var: FORMATTERS +fmt-test: ## Actually run formatter tests + @test/formatters/run-func.bash apheleia-ft-test diff --git a/README.md b/README.md index 705d722..f5fa043 100644 --- a/README.md +++ b/README.md @@ -218,7 +218,32 @@ Apheleia exposes some hooks for advanced customization: ## Contributing Please see [the contributor guide for my -projects](https://github.com/raxod502/contributor-guide). +projects](https://github.com/raxod502/contributor-guide) for general +information, and the following sections for Apheleia-specific details. + +### Adding a formatter + +I have done my best to make it straightforward to add a formatter. You +just follow these steps: + +1. Install your formatter on your machine so you can test. +2. Create an entry in `apheleia-formatters` with how to run it. (See + the docstring of this variable for explanation about the available + keywords.) +3. Add entries for the relevant major modes in `apheleia-mode-alist`. +4. See if it works for you! +5. Add a file at `test/formatters/installers/yourformatter.bash` which + explains how to install the formatter on Ubuntu. This will be used + by CI. +6. Test with `make fmt-build FORMATTERS=yourformatter` to do the + installation, then `make fmt-docker` to start a shell with the + formatter available. Verify it runs in this environment. +7. Add an example input (pre-formatting) and output (post-formatting) + file at `test/formatters/samplecode/yourformatter/in.whatever` and + `test/formatters/samplecode/yourformatter/out.whatever`. +8. Verify that the tests are passing, using `make fmt-test + FORMATTERS=yourformatter` from inside the `fmt-docker` shell. +9. Submit a pull request, CI should now be passing! ## Acknowledgements diff --git a/apheleia.el b/apheleia.el index 84ae1b4..2d40f47 100644 --- a/apheleia.el +++ b/apheleia.el @@ -700,10 +700,11 @@ formatter being run, for diagnostic purposes." (fish-indent . ("fish_indent")) (gofmt . ("gofmt")) (google-java-format . ("google-java-format" "-")) - (isort . ("isort" "--stdout" "-")) - (latexindent . ("latexindent")) + (isort . ("isort" "-")) + (latexindent . ("latexindent" "--logfile=/dev/null")) (mix-format . ("mix" "format" "-")) - (ocamlformat . ("ocamlformat" "-" "--name" filepath)) + (ocamlformat . ("ocamlformat" "-" "--name" filepath + "--enable-outside-detected-project")) (prettier . (npx "prettier" "--stdin-filepath" filepath)) (rustfmt . ("rustfmt" "--quiet" "--emit" "stdout")) (terraform . ("terraform" "fmt" "-"))) diff --git a/scripts/check-line-length.bash b/scripts/check-line-length.bash index c6a040d..3dd4cd1 100755 --- a/scripts/check-line-length.bash +++ b/scripts/check-line-length.bash @@ -6,6 +6,8 @@ set -o pipefail find=( find . -name .git -prune -o + -name .log -prune -o + -path ./test/formatters -prune -o -name "*.elc" -o -type f -print ) diff --git a/scripts/docker-pid1.bash b/scripts/docker-pid1.bash new file mode 100755 index 0000000..cee0dff --- /dev/null +++ b/scripts/docker-pid1.bash @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +set -euo pipefail + +cat <<"EOF" > /etc/sudoers.d/apheleia +%sudo ALL=(ALL:ALL) NOPASSWD: ALL +EOF + +groupadd -g "$(stat -c %g "$PWD")" -o -p '!' -r apheleia +useradd -u "$(stat -c %u "$PWD")" -g "$(stat -c %g "$PWD")" \ + -o -p '!' -m -N -l -s /usr/bin/bash -G sudo apheleia + +runuser -u apheleia touch /home/apheleia/.sudo_as_admin_successful + +if (( "$#" == 0 )) || [[ -z "$1" ]]; then + set -- bash +fi + +if (( "$#" == 1 )) && [[ "$1" == *" "* ]]; then + set -- bash -c "$1" +fi + +exec runuser -u apheleia -- "$@" diff --git a/scripts/docker-run.bash b/scripts/docker-run.bash new file mode 100755 index 0000000..e7de1f4 --- /dev/null +++ b/scripts/docker-run.bash @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -euo pipefail + +repo="$(git rev-parse --show-toplevel)" + +docker=(docker) +if [[ "$OSTYPE" != darwin* ]] && [[ "$EUID" != 0 ]]; then + docker=(sudo -E "${docker[@]}") +fi + +it=() + +if [[ -t 0 ]]; then + it+=(-it) +fi + +exec "${docker[@]}" run "${it[@]}" --rm -v "${repo}:/src" \ + --entrypoint=/src/scripts/docker-pid1.bash "$@" diff --git a/scripts/docker.bash b/scripts/docker.bash index d846122..812cded 100755 --- a/scripts/docker.bash +++ b/scripts/docker.bash @@ -22,4 +22,11 @@ docker build . -t "apheleia:$tag" \ --build-arg "UID=$UID" \ --build-arg "VERSION=$tag" -docker run -it --rm -v "$PWD:/home/docker/src" "apheleia:$tag" "${args[@]}" +it=() + +if [[ -t 0 ]]; then + it+=(-it) +fi + +docker run "${it[@]}" --rm -v "$PWD:/home/docker/src" \ + "apheleia:$tag" "${args[@]}" diff --git a/test/formatters/Dockerfile b/test/formatters/Dockerfile new file mode 100644 index 0000000..4807552 --- /dev/null +++ b/test/formatters/Dockerfile @@ -0,0 +1,13 @@ +# Ubuntu 20.04 LTS supported until April 2025 +FROM ubuntu:20.04 + +WORKDIR /build +COPY install-common.bash /build/ +RUN ./install-common.bash + +ARG FORMATTERS +COPY install-formatters.bash /build/ +COPY installers /build/installers/ +RUN ./install-formatters.bash + +WORKDIR /src diff --git a/test/formatters/apheleia-ft.el b/test/formatters/apheleia-ft.el new file mode 100755 index 0000000..9e2a288 --- /dev/null +++ b/test/formatters/apheleia-ft.el @@ -0,0 +1,296 @@ +;; -*- lexical-binding: t -*- + +;; `apheleia-ft' - short for `apheleia-formatter-tests'. The functions +;; in here are not part of the public interface of Apheleia and +;; breaking changes may occur at any time. + +(require 'apheleia) + +(require 'cl-lib) +(require 'map) + +(defvar apheleia-ft--test-dir + (file-name-directory + (or load-file-name buffer-file-name)) + "Directory containing this module.") + +(defun apheleia-ft--get-formatters (&optional all) + "Return list of strings naming the formatters to run. +This is determined by the environment variable FORMATTERS, +defaulting to all known formatters if the environment variable is +not set. + +If ALL is non-nil, unconditionally return all formatters." + (let ((env-var (or (getenv "FORMATTERS") ""))) + (cond + ((or all (string-empty-p env-var)) + (mapcar #'symbol-name (map-keys apheleia-formatters))) + (t + (split-string env-var "[ ,]+"))))) + +(defun apheleia-ft--get-formatters-from-ref (ref) + "Check out given Git REF and return `apheleia-formatters' from there. +Return an Elisp data structure, same as the `apheleia-formatters' +already in memory on the current branch." + (let ((old-apheleia (make-temp-file "apheleia-" nil ".el")) + (stderr-file (make-temp-file "apheleia-ft-stderr-"))) + (with-temp-file old-apheleia + (let ((exit-status + (call-process + "git" + nil (list (current-buffer) stderr-file) nil + "show" (format "%s:apheleia.el" ref)))) + (unless (zerop exit-status) + (with-temp-buffer + (insert-file-contents stderr-file) + (princ (buffer-string))) + (error "Failed to 'git show %s:apheleia.el', got exit status %S" + ref exit-status)))) + (with-temp-buffer + (call-process + (if invocation-directory + (expand-file-name invocation-name invocation-directory) + invocation-name) + nil (current-buffer) nil + "--batch" "-l" old-apheleia "--eval" "(prin1 apheleia-formatters)") + (goto-char (point-min)) + (read (current-buffer))))) + +(defun apheleia-ft--get-formatters-for-pull-request () + "Return list of formatter string names that were touched in this PR. +This means their commands in `apheleia-formatters' are different +from how they appear on master, or they were added relative to +master." + (let ((old-formatters (apheleia-ft--get-formatters-from-ref "origin/master")) + (new-formatters apheleia-formatters) + (touched-formatters nil)) + (map-do + (lambda (formatter command) + (unless (equal command (alist-get formatter old-formatters)) + (push (symbol-name formatter) touched-formatters))) + new-formatters) + touched-formatters)) + +(defun apheleia-ft-changed () + "Print to stdout a comma-delimited list of formatters changed in this PR." + (princ (concat + (string-join + (apheleia-ft--get-formatters-for-pull-request) ",") + "\n"))) + +(defun apheleia-ft--read-file (filename) + "Return the contents of FILENAME as a string." + (with-temp-buffer + (insert-file-contents filename) + (buffer-string))) + +(defun apheleia-ft--write-temp-file (contents extension) + "Write file CONTENTS string to temporary file with given EXTENSION. +Return the filename." + (unless (or (string-prefix-p "." extension) (string-empty-p extension)) + (setq extension (concat "." extension))) + (make-temp-file "apheleia-ft-file-" nil extension contents)) + +(defun apheleia-ft--input-files (formatter) + "For given FORMATTER, return list of input files used in test cases. +These are absolute filepaths beginning with \"in.\"." + (directory-files + (apheleia-ft--path-join + apheleia-ft--test-dir + "samplecode" formatter) + 'full + "^in\\.")) + +(defun apheleia-ft--path-join (component &rest components) + "Join COMPONENT and COMPONENTS together, left to right. +Return an absolute path." + (let ((result component)) + (while (setq component (pop components)) + (setq result (expand-file-name component result))) + result)) + +(defun apheleia-ft--print-diff (lhs-name lhs rhs-name rhs) + "Print a Git-style line-wise diff between two strings. +LHS-NAME is a human-readable name for the LHS string, same for +RHS-NAME and RHS." + (with-temp-buffer + (let* ((lhs-file (apheleia-ft--write-temp-file lhs lhs-name)) + (rhs-file (apheleia-ft--write-temp-file rhs rhs-name)) + (stderr-file (make-temp-file "apheleia-ft-stderr-")) + (exit-status + (call-process + "git" nil (list (current-buffer) stderr-file) nil "diff" + "--no-index" lhs-file rhs-file))) + (unless (memq exit-status '(0 1)) + (with-temp-buffer + (insert-file-contents stderr-file) + (princ (buffer-string))) + (error "Git diff exited with status %S" exit-status)) + (princ (buffer-string))))) + +(defun apheleia-ft-lint () + "Lint general file structure for formatter tests. +This validates that necessary support files exist for every +formatter defined in apheleia.el, and that they are well-formed, +and no extraneous ones exist. + +This operation is intended to be fast and simple, and does not +involve running any formatters." + (interactive) + (let ((formatters (mapcar #'symbol-name (map-keys apheleia-formatters))) + (installers + (mapcar + (lambda (filename) + (string-remove-suffix ".bash" filename)) + (directory-files + (apheleia-ft--path-join + apheleia-ft--test-dir "installers") + nil "\\.bash$"))) + (samplecode-dirs + (directory-files + (apheleia-ft--path-join + apheleia-ft--test-dir "samplecode") + nil "^[^.]"))) + (dolist (formatter formatters) + (unless (member formatter installers) + (error "Missing installer script at installers/%s.bash" formatter))) + (dolist (installer installers) + (unless (member installer formatters) + (error "Spurious installer script at installers/%s.bash" installer))) + (dolist (formatter formatters) + (unless (member formatter samplecode-dirs) + (error "Missing sample code dir at samplecode/%s" formatter)) + (let ((in-files + (directory-files + (apheleia-ft--path-join + apheleia-ft--test-dir "samplecode" formatter) + nil "^in")) + (out-files nil) + (all-files + (directory-files + (apheleia-ft--path-join + apheleia-ft--test-dir "samplecode" formatter) + nil "^[^.]"))) + (unless in-files + (error "Empty sample code dir at samplecode/%s" formatter)) + (dolist (in-file in-files) + (let ((out-file (replace-regexp-in-string "^in" "out" in-file))) + (unless (file-exists-p + (apheleia-ft--path-join + apheleia-ft--test-dir "samplecode" formatter out-file)) + (error "Input file %s is has no corresponding output file %s" + in-file out-file)) + (push out-file out-files))) + (dolist (file all-files) + (unless (or (member file in-files) + (member file out-files)) + (error "Spurious sample code file at samplecode/%s/%s" + formatter file))))) + (dolist (samplecode-dir samplecode-dirs) + (unless (member samplecode-dir formatters) + (error + "Spurious sample code directory at samplecode/%s" + samplecode-dir)))) + (message "[format-test] linting passed")) + +(defun apheleia-ft-test (&rest formatters) + "Run tests for provided FORMATTERS. +Interactively, select a single formatter to test using +`completing-read'. If FORMATTERS is not provided (or, +interactively, with prefix argument), fall back to the FORMATTERS +environment variable, defaulting to all formatters." + (interactive + (unless (or current-prefix-arg noninteractive) + (list (completing-read "Formatter: " (apheleia-ft--get-formatters))))) + (dolist (formatter (or formatters (apheleia-ft--get-formatters))) + (dolist (in-file (apheleia-ft--input-files formatter)) + (let ((extension (file-name-extension in-file)) + (in-text (apheleia-ft--read-file in-file)) + (in-temp-real-file nil) + (in-temp-file nil) + (out-temp-file nil) + (command (alist-get (intern formatter) apheleia-formatters)) + (syms nil) + (stdout-buffer nil) + (stderr-file (make-temp-file "apheleia-ft-stderr-")) + (default-directory temporary-file-directory) + (exit-status nil) + (out-file (replace-regexp-in-string + "/in\\([^/]+\\)" "/out\\1" in-file 'fixedcase))) + (mapc + (lambda (arg) + (when (memq arg '(file filepath input output inplace)) + (cl-pushnew arg syms))) + command) + (when (or (memq 'file syms) (memq 'filepath syms)) + (setq in-temp-real-file (apheleia-ft--write-temp-file + in-text extension))) + (when (or (memq 'input syms) (memq 'inplace syms)) + (setq in-temp-file (apheleia-ft--write-temp-file + in-text extension)) + (when (memq 'inplace syms) + (setq out-temp-file in-temp-file))) + (when (memq 'output syms) + (setq out-temp-file (apheleia-ft--write-temp-file + "" extension))) + (setq command + (mapcar + (lambda (arg) + (pcase arg + ((or `file `filepath) + in-temp-real-file) + ((or `input `inplace) + in-temp-file) + (`output + out-temp-file) + (_ arg))) + command)) + (setq command (delq 'npx command)) + (setq stdout-buffer (get-buffer-create + (format "*apheleia-ft-stdout-%S" formatter))) + (with-current-buffer stdout-buffer + (erase-buffer)) + (setq exit-status + (apply + #'call-process + (car command) + (unless (or (memq 'file syms) + (memq 'input syms) + (memq 'inplace syms)) + in-file) + (list stdout-buffer stderr-file) + nil + (cdr command))) + ;; Verify that formatter succeeded. + (unless (zerop exit-status) + (with-temp-buffer + (insert-file-contents stderr-file) + (princ (buffer-string))) + (error + "Formatter %s exited with status %S" formatter exit-status)) + ;; Verify that formatter has not touched original file. + (when in-temp-real-file + (let ((in-text-now (apheleia-ft--read-file in-temp-real-file))) + (unless (string= in-text in-text-now) + (apheleia-ft--print-diff + "original" in-text + "updated" in-text-now) + (error "Formatter %s modified original file in place" formatter)))) + ;; Verify that formatter formatted correctly. + (let ((out-text + (if (or (memq 'output syms) (memq 'inplace syms)) + (apheleia-ft--read-file out-temp-file) + (with-current-buffer stdout-buffer + (buffer-string)))) + (expected-out-text + (apheleia-ft--read-file out-file))) + (unless (string= out-text expected-out-text) + (apheleia-ft--print-diff + "expected" expected-out-text + "actual" out-text) + (error "Formatter %s did not format as expected" formatter))) + (princ (format + "[format-test] success: formatter %s (file %s)\n" + formatter (file-name-nondirectory in-file))))))) + +(provide 'apheleia-ft) diff --git a/test/formatters/build-image.bash b/test/formatters/build-image.bash new file mode 100755 index 0000000..a118d12 --- /dev/null +++ b/test/formatters/build-image.bash @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -euo pipefail + +echo >&2 "build-image.bash: tagging apheleia-formatters:${TAG:-latest}" + +if [[ -n "${FORMATTERS:-}" ]]; then + echo "build-image.bash: will install these formatters: ${FORMATTERS}" +else + echo "build-image.bash: will install all formatters by default" +fi + +cd "$(dirname "$0")" + +docker=(docker) +if [[ "$OSTYPE" != darwin* ]] && [[ "$EUID" != 0 ]]; then + docker=(sudo -E "${docker[@]}") +fi + +exec "${docker[@]}" build . \ + -t "apheleia-formatters:${TAG:-latest}" \ + --build-arg "FORMATTERS=${FORMATTERS:-}" diff --git a/test/formatters/install-common.bash b/test/formatters/install-common.bash new file mode 100755 index 0000000..2e812a2 --- /dev/null +++ b/test/formatters/install-common.bash @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +set -euxo pipefail + +export DEBIAN_FRONTEND=noninteractive +apt-get update +apt-get install -y curl gnupg lsb-release + +curl -fsSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - + +ubuntu_name="$(lsb_release -cs)" +node_repo="$(curl -fsSL https://deb.nodesource.com/setup_current.x | grep NODEREPO= | grep -Eo 'node_[0-9]+\.x' | head -n1)" + +tee -a /etc/apt/sources.list.d/nodejs.list >/dev/null < 0 )); then + echo >&2 "Installing the following ${#FORMATTERS[@]} formatter(s):" + for f in "${FORMATTERS[@]}"; do + echo >&2 " * ${f}" + done +else + echo >&2 "Installing NO formatters" +fi + +echo >&2 "-- Setting up --" + +export DEBIAN_FRONTEND=noninteractive +apt-get update + +orig_wd="${PWD}" + +mkdir /tmp/apheleia-work +cd /tmp/apheleia-work + +latest_release() { + curl -fsSL "https://api.github.com/repos/$1/releases/latest" | jq -r .tag_name +} + +echo >&2 "-- Will install ${#FORMATTERS[@]} formatter(s) --" + +for f in "${FORMATTERS[@]}"; do + echo >&2 "-- Installing formatter ${f} --" + . "${orig_wd}/installers/${f}.bash" +done + +echo >&2 "-- Cleaning up --" + +rm -rf /var/lib/apt/lists/* +rm -rf /tmp/apheleia-work diff --git a/test/formatters/installers/black.bash b/test/formatters/installers/black.bash new file mode 100644 index 0000000..b1b5b34 --- /dev/null +++ b/test/formatters/installers/black.bash @@ -0,0 +1 @@ +apt-get install -y black diff --git a/test/formatters/installers/brittany.bash b/test/formatters/installers/brittany.bash new file mode 100644 index 0000000..118870b --- /dev/null +++ b/test/formatters/installers/brittany.bash @@ -0,0 +1,11 @@ +# PPA recommended at https://www.haskell.org/cabal/download.html +apt-key adv --keyserver keyserver.ubuntu.com --recv-keys FF3AEACEF6F88286 +echo "deb [arch=amd64] http://ppa.launchpad.net/hvr/ghc/ubuntu $(lsb_release -cs) main" > /etc/apt/sources.list.d/brittany.list + +apt-get update +apt-get install -y cabal-install-3.4 ghc +ln -s /opt/cabal/bin/cabal /usr/local/bin/ + +cabal v2-update +cabal v2-install brittany +cp -L "$HOME/.cabal/bin/brittany" /usr/local/bin/ diff --git a/test/formatters/installers/clang-format.bash b/test/formatters/installers/clang-format.bash new file mode 100644 index 0000000..49f65c2 --- /dev/null +++ b/test/formatters/installers/clang-format.bash @@ -0,0 +1 @@ +apt-get install -y clang-format diff --git a/test/formatters/installers/fish-indent.bash b/test/formatters/installers/fish-indent.bash new file mode 100644 index 0000000..0dd768e --- /dev/null +++ b/test/formatters/installers/fish-indent.bash @@ -0,0 +1 @@ +apt-get install -y fish diff --git a/test/formatters/installers/gofmt.bash b/test/formatters/installers/gofmt.bash new file mode 100644 index 0000000..6e54ccd --- /dev/null +++ b/test/formatters/installers/gofmt.bash @@ -0,0 +1 @@ +apt-get install -y golang-go diff --git a/test/formatters/installers/google-java-format.bash b/test/formatters/installers/google-java-format.bash new file mode 100644 index 0000000..7b69a5f --- /dev/null +++ b/test/formatters/installers/google-java-format.bash @@ -0,0 +1,12 @@ +apt-get install -y default-jre + +ver="$(latest_release google/google-java-format | sed 's/^v//')" + +mkdir /opt/google-java-format +wget "https://github.com/google/google-java-format/releases/download/v${ver}/google-java-format-${ver}-all-deps.jar" -O /opt/google-java-format/google-java-format.jar + +cat <<"EOF" > /usr/local/bin/google-java-format +#!/bin/sh +exec java -jar /opt/google-java-format/google-java-format.jar "$@" +EOF +chmod +x /usr/local/bin/google-java-format diff --git a/test/formatters/installers/isort.bash b/test/formatters/installers/isort.bash new file mode 100644 index 0000000..3e76ce5 --- /dev/null +++ b/test/formatters/installers/isort.bash @@ -0,0 +1 @@ +apt-get install -y isort python3-setuptools diff --git a/test/formatters/installers/latexindent.bash b/test/formatters/installers/latexindent.bash new file mode 100644 index 0000000..85d9e00 --- /dev/null +++ b/test/formatters/installers/latexindent.bash @@ -0,0 +1 @@ +apt-get install -y texlive-extra-utils diff --git a/test/formatters/installers/mix-format.bash b/test/formatters/installers/mix-format.bash new file mode 100644 index 0000000..f09283d --- /dev/null +++ b/test/formatters/installers/mix-format.bash @@ -0,0 +1 @@ +apt-get install -y elixir diff --git a/test/formatters/installers/ocamlformat.bash b/test/formatters/installers/ocamlformat.bash new file mode 100644 index 0000000..b7ce7f1 --- /dev/null +++ b/test/formatters/installers/ocamlformat.bash @@ -0,0 +1,5 @@ +apt-get install -y opam + +opam init -n --disable-sandboxing --root /opt/ocamlformat +opam install ocamlformat -y --root /opt/ocamlformat +ln -s /opt/ocamlformat/default/bin/ocamlformat /usr/local/bin/ diff --git a/test/formatters/installers/prettier.bash b/test/formatters/installers/prettier.bash new file mode 100644 index 0000000..09ba518 --- /dev/null +++ b/test/formatters/installers/prettier.bash @@ -0,0 +1 @@ +npm install -g prettier diff --git a/test/formatters/installers/rustfmt.bash b/test/formatters/installers/rustfmt.bash new file mode 100644 index 0000000..7ce08d8 --- /dev/null +++ b/test/formatters/installers/rustfmt.bash @@ -0,0 +1,5 @@ +ver="$(latest_release rust-lang/rustfmt | sed 's/^v//')" + +wget "https://github.com/rust-lang/rustfmt/releases/download/v${ver}/rustfmt_linux-x86_64_v${ver}.tar.gz" -O rustfmt.tar.gz +tar -xf rustfmt.tar.gz +cp rustfmt*/rustfmt /usr/local/bin/ diff --git a/test/formatters/installers/terraform.bash b/test/formatters/installers/terraform.bash new file mode 100644 index 0000000..2f4299f --- /dev/null +++ b/test/formatters/installers/terraform.bash @@ -0,0 +1,5 @@ +curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add - +echo "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" > /etc/apt/sources.list.d/terraform.list + +apt-get update +apt-get install -y terraform diff --git a/test/formatters/run-func.bash b/test/formatters/run-func.bash new file mode 100755 index 0000000..22e7489 --- /dev/null +++ b/test/formatters/run-func.bash @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -euo pipefail + +cd "$(dirname "$0")" +repo="$(git rev-parse --show-toplevel)" + +exec emacs --batch -L "${repo}" -L . -l apheleia-ft -f "$1" diff --git a/test/formatters/samplecode/black/in.py b/test/formatters/samplecode/black/in.py new file mode 100644 index 0000000..21cdcdb --- /dev/null +++ b/test/formatters/samplecode/black/in.py @@ -0,0 +1,4 @@ +def asdjf ( l, + a): + 3 + +4 diff --git a/test/formatters/samplecode/black/out.py b/test/formatters/samplecode/black/out.py new file mode 100644 index 0000000..0a63d13 --- /dev/null +++ b/test/formatters/samplecode/black/out.py @@ -0,0 +1,3 @@ +def asdjf(l, a): + 3 + +4 diff --git a/test/formatters/samplecode/brittany/in.hs b/test/formatters/samplecode/brittany/in.hs new file mode 100644 index 0000000..4734e5a --- /dev/null +++ b/test/formatters/samplecode/brittany/in.hs @@ -0,0 +1,10 @@ +-- | Foo performs foo and sometimes bar. + +foo :: Thoroughness + -> Int -> Int +foo t x = if x > 20 + then case t of + Thorough -> x + 50 + Somewhat -> x + 20 + NotAtAll -> 0 + else 10 + 1 diff --git a/test/formatters/samplecode/brittany/out.hs b/test/formatters/samplecode/brittany/out.hs new file mode 100644 index 0000000..32cfeb9 --- /dev/null +++ b/test/formatters/samplecode/brittany/out.hs @@ -0,0 +1,9 @@ +-- | Foo performs foo and sometimes bar. + +foo :: Thoroughness -> Int -> Int +foo t x = if x > 20 + then case t of + Thorough -> x + 50 + Somewhat -> x + 20 + NotAtAll -> 0 + else 10 + 1 diff --git a/test/formatters/samplecode/clang-format/in.c b/test/formatters/samplecode/clang-format/in.c new file mode 100644 index 0000000..07aa90b --- /dev/null +++ b/test/formatters/samplecode/clang-format/in.c @@ -0,0 +1,2 @@ +// https://www.ioccc.org/2020/burton/prog.c +int main(int b,char**i){long long n=B,a=I^n,r=(a/b&a)>>4,y=atoi(*++i),_=(((a^n/b)*(y>>T)|y>>S)&r)|(a^r);printf("%.8s\n",(char*)&_);} diff --git a/test/formatters/samplecode/clang-format/out.c b/test/formatters/samplecode/clang-format/out.c new file mode 100644 index 0000000..40e9e60 --- /dev/null +++ b/test/formatters/samplecode/clang-format/out.c @@ -0,0 +1,6 @@ +// https://www.ioccc.org/2020/burton/prog.c +int main(int b, char **i) { + long long n = B, a = I ^ n, r = (a / b & a) >> 4, y = atoi(*++i), + _ = (((a ^ n / b) * (y >> T) | y >> S) & r) | (a ^ r); + printf("%.8s\n", (char *)&_); +} diff --git a/test/formatters/samplecode/fish-indent/in.fish b/test/formatters/samplecode/fish-indent/in.fish new file mode 100644 index 0000000..c5e7063 --- /dev/null +++ b/test/formatters/samplecode/fish-indent/in.fish @@ -0,0 +1,10 @@ + switch (uname) + case Linux + echo Hi Tux! + case Darwin + echo Hi Hexley! + case FreeBSD NetBSD DragonFly + echo Hi Beastie! + case '*' + echo Hi, stranger! + end diff --git a/test/formatters/samplecode/fish-indent/out.fish b/test/formatters/samplecode/fish-indent/out.fish new file mode 100644 index 0000000..1bc4e84 --- /dev/null +++ b/test/formatters/samplecode/fish-indent/out.fish @@ -0,0 +1,10 @@ +switch (uname) + case Linux + echo Hi Tux! + case Darwin + echo Hi Hexley! + case FreeBSD NetBSD DragonFly + echo Hi Beastie! + case '*' + echo Hi, stranger! +end diff --git a/test/formatters/samplecode/gofmt/in.go b/test/formatters/samplecode/gofmt/in.go new file mode 100644 index 0000000..f726448 --- /dev/null +++ b/test/formatters/samplecode/gofmt/in.go @@ -0,0 +1,11 @@ +package asdfasdf +func + +Factorial(x int) int { + if x <= + 1 { + return x +} + return x * Factorial(x - 1, +) + } diff --git a/test/formatters/samplecode/gofmt/out.go b/test/formatters/samplecode/gofmt/out.go new file mode 100644 index 0000000..9e9b1f7 --- /dev/null +++ b/test/formatters/samplecode/gofmt/out.go @@ -0,0 +1,9 @@ +package asdfasdf + +func Factorial(x int) int { + if x <= + 1 { + return x + } + return x * Factorial(x-1) +} diff --git a/test/formatters/samplecode/google-java-format/in.java b/test/formatters/samplecode/google-java-format/in.java new file mode 100644 index 0000000..877f756 --- /dev/null +++ b/test/formatters/samplecode/google-java-format/in.java @@ -0,0 +1,16 @@ + package + main + ; + + public + class +Main { public + static +void main + ( String arg[] +){System.out.println + ( + "Hello, world" +) + +;}} diff --git a/test/formatters/samplecode/google-java-format/out.java b/test/formatters/samplecode/google-java-format/out.java new file mode 100644 index 0000000..f23c5a8 --- /dev/null +++ b/test/formatters/samplecode/google-java-format/out.java @@ -0,0 +1,7 @@ +package main; + +public class Main { + public static void main(String arg[]) { + System.out.println("Hello, world"); + } +} diff --git a/test/formatters/samplecode/isort/in.py b/test/formatters/samplecode/isort/in.py new file mode 100644 index 0000000..5bd6f7f --- /dev/null +++ b/test/formatters/samplecode/isort/in.py @@ -0,0 +1,20 @@ +from my_lib import Object + +import os + +from my_lib import Object3 + +from my_lib import Object2 + +import sys + +from third_party import lib15, lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14 + +import sys + +from __future__ import absolute_import + +from third_party import lib3 + +print("Hey") +print("yo") diff --git a/test/formatters/samplecode/isort/out.py b/test/formatters/samplecode/isort/out.py new file mode 100644 index 0000000..9d6689f --- /dev/null +++ b/test/formatters/samplecode/isort/out.py @@ -0,0 +1,11 @@ +from __future__ import absolute_import + +import os +import sys + +from my_lib import Object, Object2, Object3 +from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, + lib10, lib11, lib12, lib13, lib14, lib15) + +print("Hey") +print("yo") diff --git a/test/formatters/samplecode/latexindent/in.tex b/test/formatters/samplecode/latexindent/in.tex new file mode 100644 index 0000000..9a3c300 --- /dev/null +++ b/test/formatters/samplecode/latexindent/in.tex @@ -0,0 +1,9 @@ +\begin{filecontents}{mybib.bib} +@online{strawberryperl, +title="Strawberry Perl", +url="http://strawberryperl.com/"} +@online{cmhblog, +title="A Perl script ... +url="... +} +\end{filecontents} diff --git a/test/formatters/samplecode/latexindent/out.tex b/test/formatters/samplecode/latexindent/out.tex new file mode 100644 index 0000000..67e03e9 --- /dev/null +++ b/test/formatters/samplecode/latexindent/out.tex @@ -0,0 +1,9 @@ +\begin{filecontents}{mybib.bib} + @online{strawberryperl, + title="Strawberry Perl", + url="http://strawberryperl.com/"} + @online{cmhblog, + title="A Perl script ... + url="... + } +\end{filecontents} diff --git a/test/formatters/samplecode/mix-format/in.erl b/test/formatters/samplecode/mix-format/in.erl new file mode 100644 index 0000000..21f17df --- /dev/null +++ b/test/formatters/samplecode/mix-format/in.erl @@ -0,0 +1,7 @@ + :c + = + List.last( + [ +:a,:b, + :c +]) diff --git a/test/formatters/samplecode/mix-format/out.erl b/test/formatters/samplecode/mix-format/out.erl new file mode 100644 index 0000000..9ced1e1 --- /dev/null +++ b/test/formatters/samplecode/mix-format/out.erl @@ -0,0 +1,6 @@ +:c = + List.last([ + :a, + :b, + :c + ]) diff --git a/test/formatters/samplecode/ocamlformat/in.ml b/test/formatters/samplecode/ocamlformat/in.ml new file mode 100644 index 0000000..dc3f123 --- /dev/null +++ b/test/formatters/samplecode/ocamlformat/in.ml @@ -0,0 +1,9 @@ +(* https://github.com/ocaml-ppx/ocamlformat/blob/main/test/passing/tests/alignment.ml.ref *) + +let file_contents = [ +] + @ [ + foo + ] @ [ + bar + ] diff --git a/test/formatters/samplecode/ocamlformat/out.ml b/test/formatters/samplecode/ocamlformat/out.ml new file mode 100644 index 0000000..1c449c6 --- /dev/null +++ b/test/formatters/samplecode/ocamlformat/out.ml @@ -0,0 +1,3 @@ +(* https://github.com/ocaml-ppx/ocamlformat/blob/main/test/passing/tests/alignment.ml.ref *) + +let file_contents = [] @ [ foo ] @ [ bar ] diff --git a/test/formatters/samplecode/prettier/in.css b/test/formatters/samplecode/prettier/in.css new file mode 100644 index 0000000..55d5d36 --- /dev/null +++ b/test/formatters/samplecode/prettier/in.css @@ -0,0 +1,13 @@ + body + +{ + padding-left : 11em; +font-family + : Georgia, + + "Times New Roman", + Times, serif; + color: purple; + background-color: + #d8da3d + } diff --git a/test/formatters/samplecode/prettier/in.html b/test/formatters/samplecode/prettier/in.html new file mode 100644 index 0000000..87c9e3e --- /dev/null +++ b/test/formatters/samplecode/prettier/in.html @@ -0,0 +1 @@ +

Minify HTML and any CSS or JS included in your markup

diff --git a/test/formatters/samplecode/prettier/in.js b/test/formatters/samplecode/prettier/in.js new file mode 100644 index 0000000..fd56713 --- /dev/null +++ b/test/formatters/samplecode/prettier/in.js @@ -0,0 +1,4 @@ +function HelloWorld({greeting = "hello", greeted = '"World"', silent = false, onMouseOver,}) { + + if(!greeting){return null}; + } diff --git a/test/formatters/samplecode/prettier/in.json b/test/formatters/samplecode/prettier/in.json new file mode 100644 index 0000000..28dc75f --- /dev/null +++ b/test/formatters/samplecode/prettier/in.json @@ -0,0 +1 @@ +{"arrowParens":"always","bracketSpacing":true,"embeddedLanguageFormatting":"auto","htmlWhitespaceSensitivity":"css","insertPragma":false,"jsxBracketSameLine":false,"jsxSingleQuote":false,"printWidth":80,"proseWrap":"preserve","quoteProps":"as-needed","requirePragma":false,"semi":true,"singleQuote":false,"tabWidth":2,"trailingComma":"es5","useTabs":false,"vueIndentScriptAndStyle":false} diff --git a/test/formatters/samplecode/prettier/in.scss b/test/formatters/samplecode/prettier/in.scss new file mode 100644 index 0000000..e824587 --- /dev/null +++ b/test/formatters/samplecode/prettier/in.scss @@ -0,0 +1 @@ +/* Define standard variables and values for website */$bgcolor: lightblue;$textcolor: darkblue;$fontsize: 18px;/* Use the variables */body{background-color: $bgcolor; color: $textcolor; font-size: $fontsize;} diff --git a/test/formatters/samplecode/prettier/in.ts b/test/formatters/samplecode/prettier/in.ts new file mode 100644 index 0000000..c75d40f --- /dev/null +++ b/test/formatters/samplecode/prettier/in.ts @@ -0,0 +1 @@ +interface GreetingSettings{greeting: string; duration?: number; color?: string;}declare function greet(setting: GreetingSettings): void; diff --git a/test/formatters/samplecode/prettier/in.yml b/test/formatters/samplecode/prettier/in.yml new file mode 100644 index 0000000..19f33ef --- /dev/null +++ b/test/formatters/samplecode/prettier/in.yml @@ -0,0 +1,13 @@ +--- +- hosts: + all + + tasks: + - name: + Get software for apt repository management. + apt: + state: present + + + name: + - python3-pycurl diff --git a/test/formatters/samplecode/prettier/out.css b/test/formatters/samplecode/prettier/out.css new file mode 100644 index 0000000..9158996 --- /dev/null +++ b/test/formatters/samplecode/prettier/out.css @@ -0,0 +1,6 @@ +body { + padding-left: 11em; + font-family: Georgia, "Times New Roman", Times, serif; + color: purple; + background-color: #d8da3d; +} diff --git a/test/formatters/samplecode/prettier/out.html b/test/formatters/samplecode/prettier/out.html new file mode 100644 index 0000000..7702be8 --- /dev/null +++ b/test/formatters/samplecode/prettier/out.html @@ -0,0 +1,5 @@ +

+ Minify HTML and any + CSS or + JS included in your markup +

diff --git a/test/formatters/samplecode/prettier/out.js b/test/formatters/samplecode/prettier/out.js new file mode 100644 index 0000000..ec9cfe4 --- /dev/null +++ b/test/formatters/samplecode/prettier/out.js @@ -0,0 +1,10 @@ +function HelloWorld({ + greeting = "hello", + greeted = '"World"', + silent = false, + onMouseOver, +}) { + if (!greeting) { + return null; + } +} diff --git a/test/formatters/samplecode/prettier/out.json b/test/formatters/samplecode/prettier/out.json new file mode 100644 index 0000000..59bb3b4 --- /dev/null +++ b/test/formatters/samplecode/prettier/out.json @@ -0,0 +1,19 @@ +{ + "arrowParens": "always", + "bracketSpacing": true, + "embeddedLanguageFormatting": "auto", + "htmlWhitespaceSensitivity": "css", + "insertPragma": false, + "jsxBracketSameLine": false, + "jsxSingleQuote": false, + "printWidth": 80, + "proseWrap": "preserve", + "quoteProps": "as-needed", + "requirePragma": false, + "semi": true, + "singleQuote": false, + "tabWidth": 2, + "trailingComma": "es5", + "useTabs": false, + "vueIndentScriptAndStyle": false +} diff --git a/test/formatters/samplecode/prettier/out.scss b/test/formatters/samplecode/prettier/out.scss new file mode 100644 index 0000000..9ae1829 --- /dev/null +++ b/test/formatters/samplecode/prettier/out.scss @@ -0,0 +1,9 @@ +/* Define standard variables and values for website */ +$bgcolor: lightblue; +$textcolor: darkblue; +$fontsize: 18px; /* Use the variables */ +body { + background-color: $bgcolor; + color: $textcolor; + font-size: $fontsize; +} diff --git a/test/formatters/samplecode/prettier/out.ts b/test/formatters/samplecode/prettier/out.ts new file mode 100644 index 0000000..143b5a7 --- /dev/null +++ b/test/formatters/samplecode/prettier/out.ts @@ -0,0 +1,6 @@ +interface GreetingSettings { + greeting: string; + duration?: number; + color?: string; +} +declare function greet(setting: GreetingSettings): void; diff --git a/test/formatters/samplecode/prettier/out.yml b/test/formatters/samplecode/prettier/out.yml new file mode 100644 index 0000000..4163e5e --- /dev/null +++ b/test/formatters/samplecode/prettier/out.yml @@ -0,0 +1,10 @@ +--- +- hosts: all + + tasks: + - name: Get software for apt repository management. + apt: + state: present + + name: + - python3-pycurl diff --git a/test/formatters/samplecode/rustfmt/in.rs b/test/formatters/samplecode/rustfmt/in.rs new file mode 100644 index 0000000..e09c014 --- /dev/null +++ b/test/formatters/samplecode/rustfmt/in.rs @@ -0,0 +1,12 @@ +fn foo() { + println!("a"); +} + + + +fn bar() { + println!("b"); + + + println!("c"); +} diff --git a/test/formatters/samplecode/rustfmt/out.rs b/test/formatters/samplecode/rustfmt/out.rs new file mode 100644 index 0000000..28db64c --- /dev/null +++ b/test/formatters/samplecode/rustfmt/out.rs @@ -0,0 +1,9 @@ +fn foo() { + println!("a"); +} + +fn bar() { + println!("b"); + + println!("c"); +} diff --git a/test/formatters/samplecode/terraform/in.tf b/test/formatters/samplecode/terraform/in.tf new file mode 100644 index 0000000..7fd0456 --- /dev/null +++ b/test/formatters/samplecode/terraform/in.tf @@ -0,0 +1,6 @@ + resource "google_sql_user" "user" { + name = "camunda" + instance =google_sql_database_instance.camunda-db.name + + password ="futurice" + } diff --git a/test/formatters/samplecode/terraform/out.tf b/test/formatters/samplecode/terraform/out.tf new file mode 100644 index 0000000..ca64220 --- /dev/null +++ b/test/formatters/samplecode/terraform/out.tf @@ -0,0 +1,6 @@ +resource "google_sql_user" "user" { + name = "camunda" + instance = google_sql_database_instance.camunda-db.name + + password = "futurice" +}