├── etc ├── info.png ├── buttercup.png ├── checkdoc.png └── list-installed.png ├── .github ├── dependabot.yml └── workflows │ └── test.yml ├── .gitignore ├── Makefile ├── Eask ├── CHANGELOG.md ├── easky-package.el ├── README.md ├── LICENSE └── easky.el /etc/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-eask/easky/HEAD/etc/info.png -------------------------------------------------------------------------------- /etc/buttercup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-eask/easky/HEAD/etc/buttercup.png -------------------------------------------------------------------------------- /etc/checkdoc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-eask/easky/HEAD/etc/checkdoc.png -------------------------------------------------------------------------------- /etc/list-installed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-eask/easky/HEAD/etc/list-installed.png -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: 'github-actions' 5 | directory: '/' 6 | schedule: 7 | interval: 'daily' 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Eask: -------------------------------------------------------------------------------- 1 | ;; -*- mode: eask; lexical-binding: t -*- 2 | 3 | (package "easky" 4 | "0.1.0" 5 | "Control the Eask command-line interface") 6 | 7 | (website-url "https://github.com/emacs-eask/easky") 8 | (keywords "maint" "easky") 9 | 10 | (package-file "easky.el") 11 | (files "easky-*.el") 12 | 13 | (script "test" "echo \"Error: no test specified\" && exit 1") 14 | 15 | (source "gnu") 16 | (source "melpa") 17 | (source "jcs-elpa") 18 | 19 | (depends-on "emacs" "27.1") 20 | (depends-on "eask-mode") 21 | (depends-on "eask") 22 | (depends-on "ansi") 23 | (depends-on "lv") 24 | (depends-on "marquee-header") 25 | 26 | (setq network-security-level 'low) ; see https://github.com/jcs090218/setup-emacs-windows/issues/156#issuecomment-932956432 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 6 | 7 | ## 0.1.1 (Unreleased) 8 | > Released N/A 9 | 10 | * Makes `easky-init` convert directory names to Lisp package names ([#3](../../pull/3)) 11 | * feat: Add test `ecukes` command ([#5](../../pull/5)) 12 | * feat: Add generate test commands ([`602e43a`](../../commit/602e43a558e95823ac0b3e84f48db7c987da132a)) 13 | * feat Add `recompile` ([`778d2c0`](../../commit/778d2c0ac495ec8336e068e992614b63e4e70556)) 14 | * feat: Add `install-vc` and `install-file` commands ([`070c734`](../../commit/070c734bdc5fafdf0de060f9584b1a037681eac2)) 15 | * feat Add `org-lint` support ([`8c9f234`](../../commit/8c9f23446e3e728fc9fb12e3cc02b9b67c1e837d)) 16 | 17 | ## 0.1.0 18 | > Released Oct 08, 2023 19 | 20 | * Initial release 21 | -------------------------------------------------------------------------------- /.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 | - 27.2 24 | - 28.2 25 | - 29.4 26 | - 30.2 27 | experimental: [false] 28 | include: 29 | - os: ubuntu-latest 30 | emacs-version: snapshot 31 | experimental: true 32 | - os: macos-latest 33 | emacs-version: snapshot 34 | experimental: true 35 | - os: windows-latest 36 | emacs-version: snapshot 37 | experimental: true 38 | exclude: 39 | - os: macos-latest 40 | emacs-version: 27.2 41 | 42 | steps: 43 | - uses: actions/checkout@v6 44 | 45 | - uses: jcs090218/setup-emacs@master 46 | with: 47 | version: ${{ matrix.emacs-version }} 48 | 49 | - uses: emacs-eask/setup-eask@master 50 | with: 51 | version: 'snapshot' 52 | 53 | - name: Run tests 54 | run: 55 | make ci 56 | -------------------------------------------------------------------------------- /easky-package.el: -------------------------------------------------------------------------------- 1 | ;;; easky-package.el --- Control Eask's package module -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2022-2025 Shen, Jen-Chieh 4 | 5 | ;; This file is not part of GNU Emacs. 6 | 7 | ;; This program is free software: you can redistribute it and/or modify 8 | ;; it under the terms of the GNU General Public License as published by 9 | ;; the Free Software Foundation, either version 3 of the License, or 10 | ;; (at your option) any later version. 11 | 12 | ;; This program is distributed in the hope that it will be useful, 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ;; GNU General Public License for more details. 16 | 17 | ;; You should have received a copy of the GNU General Public License 18 | ;; along with this program. If not, see . 19 | 20 | ;;; Commentary: 21 | ;; 22 | ;; This module simulate most important functionality from `package.el' 23 | ;; 24 | 25 | ;;; Code: 26 | 27 | (require 'package) 28 | 29 | (require 'easky) 30 | 31 | ;; 32 | ;; (@* "Externals" ) 33 | ;; 34 | 35 | (defvar github-elpa-working-dir) 36 | (defvar github-elpa-archive-dir) 37 | (defvar github-elpa-recipes-dir) 38 | 39 | (declare-function package-recompile "ext:package.el") 40 | (declare-function package-recompile-all "ext:package.el") 41 | (declare-function package-upgrade "ext:package.el") 42 | (declare-function package-upgrade-all "ext:package.el") 43 | 44 | ;; 45 | ;; (@* "Compat" ) 46 | ;; 47 | 48 | (defun easky-package--call-safely (ver func) 49 | "Call FUNC interactively safely after checking the Emacs VER." 50 | (if (version< emacs-version ver) 51 | (user-error 52 | (concat (format "`%s' is not supported in your version of Emacs; " func) 53 | (format "consider upgrading it to Emacs %s or later" ver))) 54 | (call-interactively func))) 55 | 56 | ;; 57 | ;; (@* "Core" ) 58 | ;; 59 | 60 | (defmacro easky-package--setup (body &rest unwind) 61 | "Execute BODY without touching the Eask-file global variables. 62 | 63 | The form UNWIND is use to revert package information." 64 | (declare (indent 1) (debug t)) 65 | `(unwind-protect (easky--setup (package-initialize t) ,body) ,@unwind)) 66 | 67 | (defun easky-package--revert-info (&rest _) 68 | "Revert package inforamtion after we have displayed in Package Menu." 69 | (package-initialize t)) 70 | 71 | ;;;###autoload 72 | (defun easky-package-refresh-contents () 73 | "Like command `package-refresh-contents' but in Eask sandbox." 74 | (interactive) 75 | (easky-package--setup 76 | (call-interactively #'package-refresh-contents) 77 | (easky-package--revert-info))) 78 | 79 | ;;;###autoload 80 | (defun easky-list-packages () 81 | "List packages." 82 | (interactive) 83 | (easky-package--setup 84 | (package-list-packages t) 85 | ;; XXX: We revert information after it's done displaying! 86 | (add-hook 'package-menu-mode-hook #'easky-package--revert-info))) 87 | 88 | ;;;###autoload 89 | (defalias 'easky-package-list-packages 'easky-list-packages) 90 | 91 | ;;;###autoload 92 | (defun easky-list-installed-packages () 93 | "List packages." 94 | (interactive) 95 | (easky-package--setup 96 | (progn 97 | (package-activate-all) ; refresh once! 98 | (if package-activated-list 99 | (package-show-package-list package-activated-list) 100 | (user-error 101 | (concat 102 | "No installed packges, try the following options:\n\n" 103 | " [1] 'M-x easky-package-install'\n" 104 | " [2] Add dependencies to your Eask-file, and 'M-x easky-install-deps'")))) 105 | ;; XXX: We revert information after it's done displaying! 106 | (add-hook 'package-menu-mode-hook #'easky-package--revert-info))) 107 | 108 | ;;;###autoload 109 | (defun easky-package-install () 110 | "Install a package to Eask sandbox." 111 | (interactive) 112 | (easky-package--setup 113 | (call-interactively #'package-install) 114 | (easky-package--revert-info))) 115 | 116 | ;;;###autoload 117 | (defun easky-package-delete () 118 | "Delete a package from Eask sandbox." 119 | (interactive) 120 | (easky-package--setup 121 | (call-interactively #'package-delete) 122 | (easky-package--revert-info))) 123 | 124 | ;;;###autoload 125 | (defun easky-package-reinstall () 126 | "Reinstall a package in Eask sandbox." 127 | (interactive) 128 | (easky-package--setup 129 | (call-interactively #'package-reinstall) 130 | (easky-package--revert-info))) 131 | 132 | ;;;###autoload 133 | (defun easky-package-recompile () 134 | "Recompile a package in Eask sandbox." 135 | (interactive) 136 | (easky-package--setup 137 | (easky-package--call-safely "29.0.50" #'package-recompile) 138 | (easky-package--revert-info))) 139 | 140 | ;;;###autoload 141 | (defun easky-package-recompile-all () 142 | "Recompile all packages in Eask sandbox." 143 | (interactive) 144 | (easky-package--setup 145 | (easky-package--call-safely "29.0.50" #'package-recompile-all) 146 | (easky-package--revert-info))) 147 | 148 | ;;;###autoload 149 | (defun easky-describe-package () 150 | "Describe a package from Eask source." 151 | (interactive) 152 | (easky-package--setup 153 | (call-interactively #'describe-package) 154 | (easky-package--revert-info))) 155 | 156 | ;;;###autoload 157 | (defun easky-package-upgrade () 158 | "Update a package from Eask sandbox." 159 | (interactive) 160 | (easky-package--setup 161 | (easky-package--call-safely "29.0.50" #'package-upgrade) 162 | (easky-package--revert-info))) 163 | 164 | ;;;###autoload 165 | (defun easky-package-upgrade-all () 166 | "Update all packages from Eask sandbox." 167 | (interactive) 168 | (easky-package--setup 169 | (easky-package--call-safely "29.0.50" #'package-upgrade-all) 170 | (easky-package--revert-info))) 171 | 172 | (provide 'easky-package) 173 | ;;; easky-package.el ends here 174 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) 2 | [![JCS-ELPA](https://raw.githubusercontent.com/jcs-emacs/badges/master/elpa/v/easky.svg)](https://jcs-emacs.github.io/jcs-elpa/#/easky) 3 | [![MELPA](https://melpa.org/packages/easky-badge.svg)](https://melpa.org/#/easky) 4 | [![MELPA Stable](https://stable.melpa.org/packages/easky-badge.svg)](https://stable.melpa.org/#/easky) 5 | 6 | 7 | # easky 8 | > Control Eask CLI in Emacs 9 | 10 | [![CI](https://github.com/emacs-eask/easky/actions/workflows/test.yml/badge.svg)](https://github.com/emacs-eask/easky/actions/workflows/test.yml) 11 | 12 | `Easky` is the interface to control [Eask CLI][], 13 | it's a package manager and sandbox tools to test and manage your elisp packages. 14 | 15 | 16 | **Table of Contents** 17 | 18 | - [🏆 Features](#-features) 19 | - [💾 Installation](#-installation) 20 | - [🔍 Step 1. Install Eask CLI](#-step-1-install-eask-cli) 21 | - [🔍 Step 2. Install `Easky` (this package)](#-step-2-install-easky-this-package) 22 | - [package.el](#packageel) 23 | - [use-package](#use-package) 24 | - [Manual installation](#manual-installation) 25 | - [🔰 Quick start](#-quick-start) 26 | - [📇 Support Commands](#-support-commands) 27 | - [🔈 Create](#-create) 28 | - [🏭 Generate](#-generate) 29 | - [📚 Documentation](#-documentation) 30 | - [💻 Execution](#-execution) 31 | - [🧹 Cleaning](#-cleaning) 32 | - [📝 Linting](#-linting) 33 | - [🔗 Linking](#-linking) 34 | - [🔍 Testing](#-testing) 35 | - [🔗 Control DSL](#-control-dsl) 36 | - [📈 Checker](#-checker) 37 | - [🔧 Customization](#-customization) 38 | - [🧪 Variables](#-variables) 39 | - [🔌 Plugins](#-plugins) 40 | - [📦 `package` module](#-package-module) 41 | - [🌟 Other packages you may be interested](#-other-packages-you-may-be-interested) 42 | - [🛠️ Contribute](#-contribute) 43 | - [🔬 Development](#-development) 44 | - [⚜️ License](#-license) 45 | 46 | 47 | 48 | ## 🏆 Features 49 | 50 | Easky is out of the box and comes along with many features. 51 | 52 | | Eask-file Management | List installed packages | 53 | |-------------------------|---------------------------------------------| 54 | | ![info](./etc/info.png) | ![list-installed](./etc/list-installed.png) | 55 | 56 | | Linting with `checkdoc` | Testing with `buttercup` | 57 | |---------------------------------|-----------------------------------| 58 | | ![checkdoc](./etc/checkdoc.png) | ![buttercup](./etc/buttercup.png) | 59 | 60 | ## 💾 Installation 61 | 62 | ### 🔍 Step 1. Install Eask CLI 63 | 64 | Download binaries from their [release page](https://github.com/emacs-eask/cli/releases) 65 | , and extract the file somewhere on your machine. Then add the path `/path/to/eask/` 66 | to environment `PATH`, so you can access it anywhere. 67 | 68 | For more installation options, see https://emacs-eask.github.io/Getting-Started/Install-Eask/. 69 | 70 | > ⚠ Warning 71 | > 72 | > Make sure the executable `eask` has the permission to execute! Use `chmod` 73 | > command if needed! 74 | 75 | ### 🔍 Step 2. Install `Easky` (this package) 76 | 77 | #### package.el 78 | 79 | This package is available from [JCS-ELPA](https://jcs-emacs.github.io/jcs-elpa/). 80 | Install from these repositories then you should be good to go! 81 | 82 | Normally, you don't need to add `(require 'easky)` to your configuration since 83 | most `easky` commands are autoload and can be called without loading the module! 84 | 85 | #### use-package 86 | 87 | If you use [use-package](https://www.emacswiki.org/emacs/UsePackage), add the 88 | following to your `init.el` file: 89 | 90 | ```elisp 91 | (use-package easky :ensure t) 92 | ``` 93 | 94 | or with `straight.el`: 95 | 96 | ```elisp 97 | (use-package easky 98 | :straight (easky :type git :host github :repo "emacs-eask/easky")) 99 | ``` 100 | 101 | #### Manual installation 102 | 103 | Copy all `.el` files in this repository to `~/.emacs.d/lisp` and add the 104 | following: 105 | 106 | ```elisp 107 | (add-to-list 'load-path "~/.emacs.d/lisp/") 108 | (require 'easky) 109 | ``` 110 | 111 | ## 🔰 Quick start 112 | 113 | The easiest to use this package, do: 114 | 115 | ``` 116 | M-x easky 117 | ``` 118 | 119 | That's it! Then you should be able to select the command you like to use through 120 | `completing-read`. 121 | 122 | Some conditions may make Eask CLI unusable: 123 | 124 | 1. `eask` is missing from your `PATH`, see also `eask-api-executable` 125 | 2. You don't have an Eask-file in your project or current directory 126 | 3. Invalid Eask-file (syntax error, invalid rules, etc) 127 | 128 | ## 📇 Support Commands 129 | 130 | All-in-one commands, these are commands we recommend you start with! 131 | 132 | | Commands | Description | 133 | |------------------|------------------------------------------------| 134 | | `easky` | Start Eask by selecting the through completion | 135 | | `easky-create` | Master command for `create` commands | 136 | | `easky-generate` | Master command for `generate` commands | 137 | | `easky-clean` | Master command for `clean` commands | 138 | | `easky-link` | Master command for `link` commands | 139 | | `easky-lint` | Master command for `lint` commands | 140 | | `easky-test` | Master command for `test` commands | 141 | | `easky-source` | Master command for `source` commands | 142 | 143 | The following are the functions provided by `easky`, you can `M-x` with these 144 | commands: 145 | 146 | | Commands | Description | 147 | |----------------------|-----------------------------------------------------------------| 148 | | `easky-init` | Create Eask-file and initialize it | 149 | | `easky-info` | Print Eask-file information | 150 | | `easky-locate` | Print Eask installed location | 151 | | `easky-compile` | Byte-compile elc files. | 152 | | `easky-recompile` | Byte-recompile elc files. | 153 | | `easky-search` | Search available packages with query. | 154 | | `easky-files` | List all package files. | 155 | | `easky-archives` | List in used archives | 156 | | `easky-keywords` | List available keywords that can be used in the header section. | 157 | | `easky-run` | Execute Eask's script (with completion) | 158 | | `easky-package` | Package (build) your package | 159 | | `easky-install` | Install packages, see also `easky-package-install`. | 160 | | `easky-uninstall` | Untnstall packages, see also `easky-package-delete`. | 161 | | `easky-reinstall` | Reinstall packages, see also `easky-package-reinstall`. | 162 | | `easky-upgrade` | Upgrade packages., see also `easky-package-update`. | 163 | | `easky-install-deps` | Install all package dependencies | 164 | | `easky-install-file` | Install packages through files. | 165 | | `easky-install-vc` | Install packages through version controls. | 166 | | `easky-recipe` | Recommend me a recipe format. | 167 | | `easky-help` | Print Eask help manual | 168 | | `easky-version` | Print Eask version | 169 | | `easky-upgrade-eask` | Upgrade Eask CLI | 170 | | `easky-stop` | Terminate the current process | 171 | | `easky-bump` | Bump version for your package or Eask-file. | 172 | | `easky-cat` | View filename(s). | 173 | | `easky-concat` | Concatenate all source files. | 174 | | `easky-loc` | Print LOC information. | 175 | | `easky-path` | Print the PATH (`exec-path`) from Eask sandbox. | 176 | | `easky-load-path` | Print the `load-path' from Eask sandbox. | 177 | 178 | ### 🔈 Create 179 | 180 | | Commands | Description | 181 | |---------------------------|----------------------------------------| 182 | | `easky-create-package` | Create a new elisp package. | 183 | | `easky-create-elpa` | Create a new ELPA using `github-elpa`. | 184 | | `easky-create-el-project` | Create a new ELPA using `el-project`. | 185 | 186 | ### 🏭 Generate 187 | 188 | | Commands | Description | 189 | |-------------------------------------|-------------------------------------------------| 190 | | `easky-generate-autoloads` | Generate `-autoloads.el` file, and print it out | 191 | | `easky-generate-pkg-file` | Generate `-pkg.el`, and printed it out | 192 | | `easky-generate-license` | Generate LICENSE file | 193 | | `easky-generate-ignore` | Generate ignore file | 194 | | `easky-generate-test` | Master command for `generate test` | 195 | | `easky-generate-test-ert` | Setup test files for [ert][] tests | 196 | | `easky-generate-test-ert-runner` | Setup test files for [ert-runner][] | 197 | | `easky-generate-test-buttercup` | Setup test files for [buttercup][] | 198 | | `easky-generate-test-ecukes` | Setup test files for [ecukes][] | 199 | | `easky-generate-workflow` | Master command for `generate workflow` | 200 | | `easky-generate-workflow-circle-ci` | Generate [CircleCI][] test file | 201 | | `easky-generate-workflow-github` | Generate [GitHub Actions][] test file | 202 | | `easky-generate-workflow-gitlab` | Generate [GitLab Runner][] test file | 203 | | `easky-generate-workflow-travis-ci` | Generate [Travis CI][] test file | 204 | 205 | ### 📚 Documentation 206 | 207 | Commands used to build documentation site. 208 | 209 | | Commands | Description | 210 | |--------------|----------------------| 211 | | `easky-docs` | Build documentation. | 212 | 213 | ### 💻 Execution 214 | 215 | Sometimes you would want to execute some command directly! 216 | 217 | | Commands | Description | 218 | |----------------|---------------------------| 219 | | `easky-eask` | Run the Eask CLI directly | 220 | | `easky-exec` | Run `eask exec` | 221 | | `easky-emacs` | Run `eask emacs` | 222 | | `easky-eval` | Run `eask eval` | 223 | | `easky-load` | Run `eask load` | 224 | | `easky-docker` | Run `eask docker` | 225 | 226 | ### 🧹 Cleaning 227 | 228 | Commands to keep your project clean: 229 | 230 | | Commands | Description | 231 | |-------------------------|------------------------------------------------------| 232 | | `easky-clean-workspace` | Clean up `.eask` directory | 233 | | `easky-clean-elc` | Remove byte compiled files generated by eask compile | 234 | | `easky-clean-dist` | Delete dist subdirectory | 235 | | `easky-clean-autoloads` | Remove generated autoloads file | 236 | | `easky-clean-pkg-file` | Remove generated pkg-file | 237 | | `easky-clean-log-file` | Remove all generated log files | 238 | | `easky-clean-all` | Do all cleaning tasks | 239 | 240 | ### 📝 Linting 241 | 242 | Commands to help you lint your packages: 243 | 244 | | Commands | Description | 245 | |----------------------------|---------------------------| 246 | | `easky-lint-checkdoc` | Run checkdoc | 247 | | `easky-lint-check-declare` | Run check-declare | 248 | | `easky-lint-elint` | Run elint | 249 | | `easky-lint-elsa` | Run elsa | 250 | | `easky-lint-indent` | Run indent-linet | 251 | | `easky-lint-package` | Run package-lint | 252 | | `easky-lint-regexps` | Run relint | 253 | | `easky-lint-keywords` | Run keywords linter | 254 | | `easky-lint-license` | Run license linter | 255 | | `easky-lint-org` | Run org-lint on Org files | 256 | 257 | > 💡 These extenral tools are automatically installed in your sandbox! 258 | 259 | ### 🔗 Linking 260 | 261 | Commands to link local packages: 262 | 263 | | Commands | Description | 264 | |---------------------|------------------------------| 265 | | `easky-link-add` | Link a local package | 266 | | `easky-link-delete` | Delete local linked packages | 267 | | `easky-link-list` | List all project links | 268 | 269 | ### 🔍 Testing 270 | 271 | Commands to help you test your packages: 272 | 273 | | Commands | Description | 274 | |-------------------------|-----------------------------------------| 275 | | `easky-test-ert` | Run [ert][] tests | 276 | | `easky-test-ert-runner` | Run [ert][] test through [ert-runner][] | 277 | | `easky-test-buttercup` | Run [buttercup][] tests | 278 | | `easky-test-ecukes` | Run [ecukes][] tests | 279 | | `easky-test-melpazoid` | Run [melpazoid][] tests | 280 | 281 | > 💡 These external tools are automatically installed in your sandbox! 282 | 283 | ### 🔗 Control DSL 284 | 285 | Commands to control DSL: 286 | 287 | | Commands | Description | 288 | |-----------------------|--------------------------| 289 | | `easky-source-add` | Add an archive source | 290 | | `easky-source-delete` | Delete an archive source | 291 | | `easky-source-list` | List all sources | 292 | 293 | ## 📈 Checker 294 | 295 | Commands to check your Eask-file. 296 | 297 | | Commands | Description | 298 | |-----------------|------------------------| 299 | | `easky-analyze` | Run Eask-file checker. | 300 | 301 | ## 🔧 Customization 302 | 303 | ### 🧪 Variables 304 | 305 | List of variables that interact with `easky`'s behaviours. 306 | 307 | - `easky-strip-header` - output header while displaying. (Default: `t`) 308 | - `easky-display-function` - Function to display Easky's result. (Default: `#'lv-message`) 309 | - `easky-focus-p` - Select window after command execution. (Default: `nil`) 310 | - `easky-move-point-for-output` - Controls whether interpreter output moves point to the end of the output. (Default: `nil`) 311 | - `easky-timeout-seconds` - Timeout seconds for running too long process. (Default: `30`) 312 | - `easky-show-tip` - Weather to show tip on waiting the output buffer (Default: `t`) 313 | 314 | ## 🔌 Plugins 315 | 316 | `easky` comes with a couple of useful additions that can be used along with it. 317 | 318 | ### 📦 `package` module 319 | 320 | package module extends `package.el` so you can manage your package dependencies 321 | (from sandbox) through `package.el`. 322 | 323 | | Commads | Description | 324 | |----------------------------------|--------------------------------------------------------------| 325 | | `easky-package-refresh-contents` | Extends `package-refresh-contents`, see also `easky-refresh` | 326 | | `easky-list-packages` | Extends `list-packages` | 327 | | `easky-list-installed-packages` | List installed packages | 328 | | `easky-package-install` | Extends `package-install`, see also `easky-install` | 329 | | `easky-package-delete` | Extends `package-delete`, see also `easky-uninstall` | 330 | | `easky-package-reinstall` | Extends `package-reinstall`, see also `easky-reinstall` | 331 | | `easky-package-recompile` | Extends `package-recompile` | 332 | | `easky-package-recompile-all` | Extends `package-recompile-all` | 333 | | `easky-describe-package` | Extends `describe-package` | 334 | | `easky-package-update` | Extends `package-update` | 335 | | `easky-package-update-all` | Extends `package-update-all` | 336 | 337 | ## 🌟 Other packages you may be interested 338 | 339 | - [company-eask](https://github.com/emacs-eask/company-eask) - Company backend for Eask-file 340 | - [eldoc-eask](https://github.com/emacs-eask/eldoc-eask) - Eldoc support for Eask-file 341 | - [flycheck-eask](https://github.com/flycheck/flycheck-eask) - Eask support in Flycheck 342 | - [flymake-eask](https://github.com/flymake/flymake-eask) - Eask support in Flymake 343 | 344 | ## 🛠️ Contribute 345 | 346 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com) 347 | [![Elisp styleguide](https://img.shields.io/badge/elisp-style%20guide-purple?logo=gnuemacs&logoColor=white)](https://github.com/bbatsov/emacs-lisp-style-guide) 348 | [![Donate on paypal](https://img.shields.io/badge/paypal-donate-1?logo=paypal&color=blue)](https://www.paypal.me/jcs090218) 349 | [![Become a patron](https://img.shields.io/badge/patreon-become%20a%20patron-orange.svg?logo=patreon)](https://www.patreon.com/jcs090218) 350 | 351 | If you would like to contribute to this project, you may either 352 | clone and make pull requests to this repository. Or you can 353 | clone the project and establish your own branch of this tool. 354 | Any methods are welcome! 355 | 356 | ### 🔬 Development 357 | 358 | To run the test locally, you will need the following tools: 359 | 360 | - [Eask](https://emacs-eask.github.io/) 361 | - [Make](https://www.gnu.org/software/make/) (optional) 362 | 363 | Install all dependencies and development dependencies: 364 | 365 | ```sh 366 | eask install-deps --dev 367 | ``` 368 | 369 | To test the package's installation: 370 | 371 | ```sh 372 | eask package 373 | eask install 374 | ``` 375 | 376 | To test compilation: 377 | 378 | ```sh 379 | eask compile 380 | ``` 381 | 382 | **🪧 The following steps are optional, but we recommend you follow these lint results!** 383 | 384 | The built-in `checkdoc` linter: 385 | 386 | ```sh 387 | eask lint checkdoc 388 | ``` 389 | 390 | The standard `package` linter: 391 | 392 | ```sh 393 | eask lint package 394 | ``` 395 | 396 | *📝 P.S. For more information, find the Eask manual at https://emacs-eask.github.io/.* 397 | 398 | ## ⚜️ License 399 | 400 | This program is free software; you can redistribute it and/or modify 401 | it under the terms of the GNU General Public License as published by 402 | the Free Software Foundation, either version 3 of the License, or 403 | (at your option) any later version. 404 | 405 | This program is distributed in the hope that it will be useful, 406 | but WITHOUT ANY WARRANTY; without even the implied warranty of 407 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 408 | GNU General Public License for more details. 409 | 410 | You should have received a copy of the GNU General Public License 411 | along with this program. If not, see . 412 | 413 | See [`LICENSE`](./LICENSE.txt) for details. 414 | 415 | 416 | 417 | 418 | [Eask CLI]: https://github.com/emacs-eask/cli 419 | 420 | [CircleCI]: https://circleci.com/ 421 | [GitHub Actions]: https://github.com/features/actions 422 | [GitLab Runner]: https://docs.gitlab.com/runner/ 423 | [Travis CI]: https://www.travis-ci.com/ 424 | 425 | [ert]: https://www.gnu.org/software/emacs/manual/html_node/ert/ 426 | [ert-runner]: https://github.com/rejeep/ert-runner.el 427 | [buttercup]: https://github.com/jorgenschaefer/emacs-buttercup 428 | [ecukes]: https://github.com/ecukes/ecukes 429 | [melpazoid]: https://github.com/riscy/melpazoid 430 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /easky.el: -------------------------------------------------------------------------------- 1 | ;;; easky.el --- Control the Eask command-line interface -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2022-2025 Shen, Jen-Chieh 4 | 5 | ;; Author: Shen, Jen-Chieh 6 | ;; Maintainer: Shen, Jen-Chieh 7 | ;; URL: https://github.com/emacs-eask/easky 8 | ;; Version: 0.1.0 9 | ;; Package-Requires: ((emacs "27.1") (eask-mode "0.1.0") (eask "0.1.0") (ansi "0.4.1") (lv "0.0") (marquee-header "0.1.0")) 10 | ;; Keywords: maint easky 11 | 12 | ;; This file is not part of GNU Emacs. 13 | 14 | ;; This program is free software: you can redistribute it and/or modify 15 | ;; it under the terms of the GNU General Public License as published by 16 | ;; the Free Software Foundation, either version 3 of the License, or 17 | ;; (at your option) any later version. 18 | 19 | ;; This program is distributed in the hope that it will be useful, 20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | ;; GNU General Public License for more details. 23 | 24 | ;; You should have received a copy of the GNU General Public License 25 | ;; along with this program. If not, see . 26 | 27 | ;;; Commentary: 28 | ;; 29 | ;; Control the Eask command-line interface. 30 | ;; 31 | 32 | ;;; Code: 33 | 34 | (require 'ansi-color) 35 | (require 'cl-lib) 36 | (require 'frame) 37 | (require 'files) 38 | 39 | (require 'eask-mode) 40 | (require 'eask-api) 41 | (require 'eask-core) 42 | (require 'ansi) ; we need `ansi' to run through Eask API 43 | (require 'lv) 44 | (require 'marquee-header) 45 | (eval-when-compile 46 | (require 'subr-x)) 47 | 48 | (defgroup easky nil 49 | "Control Eask in Emacs." 50 | :prefix "easky-" 51 | :group 'tool 52 | :link '(url-link :tag "Repository" "https://github.com/emacs-eask/easky")) 53 | 54 | (defcustom easky-strip-header t 55 | "Remove output header while displaying." 56 | :type 'boolean 57 | :group 'easky) 58 | 59 | (defcustom easky-display-function #'lv-message 60 | "Function to display Easky's result." 61 | :type 'function 62 | :group 'easky) 63 | 64 | (defcustom easky-focus-p nil 65 | "Select window after command execution." 66 | :type 'boolean 67 | :group 'easky) 68 | 69 | (defcustom easky-move-point-for-output nil 70 | "Controls whether interpreter output moves point to the end of the output." 71 | :type 'boolean 72 | :group 'easky) 73 | 74 | (defcustom easky-timeout-seconds 30 75 | "Timeout seconds for running too long process." 76 | :type 'number 77 | :group 'easky) 78 | 79 | (defcustom easky-show-tip t 80 | "If non-nil, show the tip in the lv window." 81 | :type 'boolean 82 | :group 'easky) 83 | 84 | (defcustom easky-annotation-ratio 2.5 85 | "Ratio align from the right to display `completin-read' annotation." 86 | :type 'float 87 | :group 'easky) 88 | 89 | (defcustom easky-extra-args '("--show-hidden") 90 | "Eask's extra arguments." 91 | :type '(list string) 92 | :group 'easky) 93 | 94 | (defconst easky-buffer-name "*easky*" 95 | "Buffer name for process file.") 96 | 97 | (defvar easky--timeout-timer nil 98 | "Timeout if execute for too long.") 99 | 100 | ;; 101 | ;; (@* "Externals" ) 102 | ;; 103 | 104 | (defvar github-elpa-working-dir) 105 | (defvar github-elpa-archive-dir) 106 | (defvar github-elpa-recipes-dir) 107 | 108 | ;; 109 | ;; (@* "Util" ) 110 | ;; 111 | 112 | (defmacro easky--inhibit-log (&rest body) 113 | "Execute BODY without write it to message buffer." 114 | (declare (indent 0) (debug t)) 115 | `(let (message-log-max) ,@body)) 116 | 117 | (defun easky-command (&rest args) 118 | "Form command string. 119 | 120 | Rest argument ARGS is the Eask's CLI arguments." 121 | (setq args (append args easky-extra-args)) 122 | (concat (or eask-api-executable "eask") " " 123 | (mapconcat #'shell-quote-argument (cl-remove-if #'null args) " "))) 124 | 125 | (defun easky--completing-frame-offset (options) 126 | "Return frame offset while `completing-read'. 127 | 128 | Argument OPTIONS ia an alist use to calculate the frame offset." 129 | (max (eask-seq-str-max (mapcar #'cdr options)) 130 | (/ (frame-width) easky-annotation-ratio))) 131 | 132 | ;; 133 | ;; (@* "Compat" ) 134 | ;; 135 | 136 | ;; TODO: Remove this after we dropped version 27.x! 137 | (defun easky--ansi-color-apply-on-region (start end &optional preserve-sequences) 138 | "Compatible version of function `ansi-color-apply-on-region'. 139 | 140 | Arguments START, END and PRESERVE-SEQUENCES is the same to original function." 141 | (if (version< emacs-version "28.1") 142 | (ansi-color-apply-on-region start end) 143 | (ansi-color-apply-on-region start end preserve-sequences))) 144 | 145 | ;; 146 | ;; (@* "Core" ) 147 | ;; 148 | 149 | (defun easky--valid-source (&optional path) 150 | "Return t if PATH has a valid Eask-file." 151 | (when-let* ((files (eask--find-files (or path default-directory))) 152 | (file (car files))) 153 | file)) 154 | 155 | (defvar easky--error-message nil 156 | "Set to non-nil when error occurs while loading Eask-file.") 157 | 158 | (defconst easky-ignore-functions 159 | '( eask-debug eask-log eask-info eask-warn eask-error) 160 | "List of functions that we wish to enabled since.") 161 | 162 | (defun easky-load-eask (&optional path) 163 | "Load Eask-file from PATH." 164 | (eask--silent (eask-file-try-load (or path default-directory))) 165 | eask-file) 166 | 167 | (defun easky--ignore-error (&optional arg0 &rest args) 168 | "Record error. 169 | 170 | We use number to name our arguments, ARG0 and ARGS." 171 | (setq easky--error-message 172 | (or (ignore-errors (apply #'format arg0 args)) ; Record message when valid 173 | t))) ; fallback to t 174 | 175 | (defmacro easky--ignore-env (&rest body) 176 | "Execute BODY with valid Eask environment." 177 | (declare (indent 0) (debug t)) 178 | ;; This will maintain your Eask-file information! 179 | `(eask--save-eask-file-state 180 | (dolist (func easky-ignore-functions) (advice-add func :override #'easky--ignore-error)) 181 | ,@body 182 | (dolist (func easky-ignore-functions) (advice-remove func #'easky--ignore-error)))) 183 | 184 | (defun easky--setup-eask-env () 185 | "Set up for eask environment." 186 | (setenv "EASK_HASCOLORS" (if (or (display-graphic-p) (display-color-cells)) 187 | "true" 188 | nil))) 189 | 190 | (defmacro easky--setup (&rest body) 191 | "Execute BODY without touching the Eask-file global variables." 192 | (declare (indent 0) (debug t)) 193 | `(cond 194 | ;; Executable not found! 195 | ((not (eask-api-executable)) 196 | (user-error 197 | (concat 198 | "No executable named `eask` in the PATH environment, make sure:\n\n" 199 | " [1] You have installed eask-cli and added to your PATH\n" 200 | " [2] You can manually set variable `eask-api-executable' to point to eask executable" 201 | "\n\nFor more information, find the manual at https://emacs-eask.github.io/"))) 202 | ;; Invalid Eask Project! 203 | ((not (easky--valid-source)) 204 | (user-error 205 | (concat 206 | "Error execute Easky command, invalid Eask source:\n\n" 207 | " [1] Make sure you have a valid Eask-file in your current workspace\n" 208 | " [2] Make sure you have Eask-file in upper directory" 209 | "\n\nYou can creat Eask-file by doing 'M-x eask-init'"))) 210 | ;; Okay! Good to go! 211 | (t (easky--setup-eask-env) 212 | (let* (eask--initialized-p 213 | easky--error-message ; init error message 214 | (eask-lisp-root (eask-api-lisp-root)) 215 | (default-directory (file-name-directory (easky--valid-source))) 216 | (user-emacs-directory (expand-file-name (concat ".eask/" emacs-version "/"))) 217 | (package-user-dir (expand-file-name "elpa" user-emacs-directory)) 218 | (user-init-file (locate-user-emacs-file "init.el")) 219 | (custom-file (locate-user-emacs-file "custom.el")) 220 | eask-depends-on-recipe-p ; make sure github-elpa creates directory 221 | (github-elpa-working-dir (expand-file-name "./temp-elpa/.working/" user-emacs-directory)) 222 | (github-elpa-archive-dir (expand-file-name "./temp-elpa/packages/" user-emacs-directory)) 223 | (github-elpa-recipes-dir (expand-file-name "./temp-elpa/recipes/" user-emacs-directory)) 224 | (package-activated-list)) ; make sure package.el does not change 225 | (easky--ignore-env 226 | (if (and (ignore-errors (easky-load-eask)) ; Error loading Eask file! 227 | (not easky--error-message)) ; The message is stored here! 228 | (progn ,@body) 229 | (user-error 230 | (concat 231 | (when (stringp easky--error-message) 232 | (format "[ERROR] %s\n\n" easky--error-message)) 233 | "Error loading Eask-file, few suggestions: \n\n" 234 | " [1] Lint your Eask-file with command `eask analyze [EASK-FILE]`\n" 235 | " [2] Make sure your Eask-file doesn't contain any invalid syntax" 236 | "\n\nHere are useful tools to help you edit Eask-file:\n\n" 237 | " | Package | Description | Repository URL |\n" 238 | " | ------------- | --------------------------------- | ------------------------------------------- |\n" 239 | " | company-eask | Company backend for Eask-file | https://github.com/emacs-eask/company-eask |\n" 240 | " | eldoc-eask | Eldoc support for Eask-file | https://github.com/emacs-eask/eldoc-eask |\n" 241 | " | flycheck-eask | Eask support in Flycheck | https://github.com/flycheck/flycheck-eask |\n" 242 | " | flymake-eask | Eask support in Flymake | https://github.com/flymake/flymake-eask |")))))))) 243 | 244 | ;; 245 | ;; (@* "Tip" ) 246 | ;; 247 | 248 | (defconst easky-tips 249 | '("💡 Some commands may take longer time to complete; raise the timeout if needed `easky-timeout-seconds'? (Default: 30s)" 250 | "💡 Try 'M-x easky' to see all available commands!" 251 | "💡 Easky uses `marquee-header' to display tip and `lv' to display message" 252 | "💡 The full output can be seen in the `*easky*' buffer; use `M-x easky-to-buffer` to see the result!" 253 | "💡 You can use `eask create' to create an Elisp project" 254 | "💡 Make sure you have all dependencies installed before you compile it!" 255 | "💡 `eask info` command prints out the package information!") 256 | "List of tips.") 257 | 258 | ;; XXX: Some commands can wait amount of time, display tip can help a little. 259 | (defun easky--pick-tips () 260 | "Return a tip." 261 | (let ((index (random (length easky-tips)))) 262 | (nth index easky-tips))) 263 | 264 | ;; 265 | ;; (@* "Display" ) 266 | ;; 267 | 268 | (defun easky-buffer () 269 | "Return easky buffer." 270 | (get-buffer-create easky-buffer-name)) 271 | 272 | (defun easky-to-buffer () 273 | "Display easky buffer." 274 | (interactive) 275 | (pop-to-buffer (easky-buffer) `((display-buffer-in-direction) (dedicated . t)))) 276 | 277 | (defvar easky-process nil 278 | "Singleton process.") 279 | 280 | (defun easky--strip-headers (str) 281 | "Strip command headers from STR, and leave only the execution result." 282 | (with-temp-buffer 283 | (insert str) 284 | (goto-char (point-min)) 285 | (when (or (search-forward "Loading Eask file" nil t) 286 | (search-forward "Checking system" nil t)) 287 | (forward-line 1)) 288 | (let ((content (string-trim (buffer-substring (point) (point-max))))) 289 | (if (string-empty-p content) 290 | (buffer-string) ; try to print something, don't let the user left unknown 291 | content)))) 292 | 293 | (defun easky--default-filter (proc output) 294 | "Default filter for PROC's OUTPUT." 295 | (with-current-buffer (process-buffer proc) 296 | (goto-char (point-max)) 297 | (let ((inhibit-read-only t) 298 | (start (point)) 299 | (lv-first (not (window-live-p lv-wnd))) 300 | content) 301 | (insert output) 302 | (easky--ansi-color-apply-on-region start (point) t) ; apply in buffer 303 | ;; Strip header! 304 | (setq content (if easky-strip-header 305 | (easky--strip-headers (buffer-string)) 306 | (buffer-string))) 307 | ;; Display it! 308 | (cl-case easky-display-function 309 | (`lv-message 310 | (funcall easky-display-function "%s" content)) 311 | (t 312 | (funcall easky-display-function content))) 313 | ;; Post actions 314 | (when (easky-lv-message-p) 315 | ;; Variable `lv-first' will prevent display different on every flush! 316 | (when (and easky-show-tip lv-first) 317 | (with-selected-window lv-wnd 318 | (marquee-header-notify (easky--pick-tips) :loop t) 319 | ;; XXX: The `header-line-format' will actually block the first line 320 | ;; of the content. It's okay since most commands have the output 321 | ;; more than one line. Except command `easky-version' only outputs 322 | ;; one line information (it only prints version number). Then it 323 | ;; will be blocked entirely! 324 | ;; 325 | ;; This line redisplays, and re-fit the window once again. 326 | (lv-message content))) 327 | ;; Move to end of buffer! 328 | (when easky-move-point-for-output 329 | (with-selected-window lv-wnd 330 | ;; XXX: Don't go above max lin, it will shift! 331 | (goto-char (1- (point-max))))) 332 | ;; Apply color in lv buffer! 333 | (with-current-buffer (window-buffer lv-wnd) 334 | (ansi-color-apply-on-region (point-min) (point-max))))))) 335 | 336 | (defun easky--default-sentinel (process &optional _event) 337 | "Default sentinel for PROCESS." 338 | (when (memq (process-status process) '(exit signal)) 339 | (easky--inhibit-log 340 | (cl-case (process-status process) 341 | (`signal (message "Easky task exit with error code")) ; TODO: print with error code! 342 | (`exit (message "Easky task completed")))) 343 | (delete-process process) 344 | (setq easky-process nil) 345 | ;; XXX: This is only for lv-message! 346 | (when (easky-lv-message-p) 347 | (add-hook 'pre-command-hook #'easky--pre-command-once) 348 | (when easky-focus-p 349 | (select-window lv-wnd))))) 350 | 351 | (defun easky--output-buffer (cmd) 352 | "Output CMD to buffer." 353 | (easky-stop) 354 | ;; XXX: Make sure we only have one process running! 355 | (unless easky-process 356 | (let ((prev-dir default-directory)) 357 | (with-current-buffer (easky-buffer) 358 | (setq default-directory prev-dir) ; hold `default-directory' 359 | (buffer-disable-undo) 360 | (read-only-mode 1) 361 | (let ((inhibit-read-only t)) 362 | (erase-buffer) 363 | (goto-char (point-min))) 364 | (let* ((program (car (split-string cmd))) 365 | (proc-name (format "easky-process-%s" program)) 366 | (process (start-file-process-shell-command proc-name (current-buffer) cmd))) 367 | (set-process-filter process #'easky--default-filter) 368 | (set-process-sentinel process #'easky--default-sentinel) 369 | (setq easky-process process) 370 | ;; Set timeout! 371 | (when (timerp easky--timeout-timer) 372 | (cancel-timer easky--timeout-timer)) 373 | (setq easky--timeout-timer (run-with-timer easky-timeout-seconds 374 | nil #'easky--kill-process))))))) 375 | 376 | (defun easky--kill-process () 377 | "Kill process." 378 | (when (and easky-process (eq (process-status easky-process) 'run)) 379 | (message "Easky process timed out, %s (running over %s seconds)" 380 | (process-name easky-process) 381 | easky-timeout-seconds) 382 | (kill-process easky-process) 383 | (setq easky-process nil))) 384 | 385 | (defun easky-stop () 386 | "Stop Easky process." 387 | (interactive) 388 | (when (and easky-process 389 | (yes-or-no-p "Easky is still busy, kill it anyway? ")) 390 | (delete-process easky-process) 391 | (setq easky-process nil) 392 | (when (easky-lv-message-p) 393 | (remove-hook 'pre-command-hook #'easky--pre-command-once) 394 | (remove-hook 'post-command-hook #'easky--post-command-once) 395 | (lv-delete-window)))) 396 | 397 | (defun easky-lv-message-p () 398 | "Return t if using lv to display message." 399 | (equal easky-display-function #'lv-message)) 400 | 401 | (defmacro easky--display (cmd) 402 | "Display CMD output." 403 | (declare (indent 0) (debug t)) 404 | `(easky--setup (easky--output-buffer ,cmd))) 405 | 406 | ;; 407 | ;; (@* "Pre-command / Post-command" ) 408 | ;; 409 | 410 | (defun easky--pre-command-once (&rest _) 411 | "One time pre-command after Easky command." 412 | ;; XXX: We pass on to next post-command! 413 | (remove-hook 'pre-command-hook #'easky--pre-command-once) 414 | (add-hook 'post-command-hook #'easky--post-command-once)) 415 | 416 | (defun easky--post-command-once (&rest _) 417 | "One time post-command after Easky command." 418 | ;; XXX: This will allow us to scroll in the lv's window! 419 | (unless (equal lv-wnd (selected-window)) 420 | ;; Once we select window other than lv's window, then we kill it! 421 | (remove-hook 'post-command-hook #'easky--post-command-once) 422 | (lv-delete-window))) 423 | 424 | ;; 425 | ;; (@* "All in one commands" ) 426 | ;; 427 | 428 | (defun easky-parse-help-manual (help-cmd subcmd-index) 429 | "Return an alist regarding help manual from HELP-CMD. 430 | 431 | Argument HELP-CMD is a string contain option `--help'. SUBCMD-INDEX is the 432 | index to target subcommand. 433 | 434 | The format is in (command . description)." 435 | (let ((manual (shell-command-to-string help-cmd)) 436 | (data)) 437 | (with-temp-buffer 438 | (insert manual) 439 | (goto-char (point-min)) 440 | (search-forward "Commands:") 441 | (forward-line 1) 442 | (while (not (string-empty-p (string-trim (thing-at-point 'line)))) 443 | (beginning-of-line) 444 | (let ((command) 445 | (description)) 446 | (forward-symbol (or subcmd-index 1)) 447 | (setq command (symbol-at-point)) 448 | (search-forward " " (line-end-position)) 449 | (search-forward-regexp "[^ \t]" (line-end-position)) 450 | (let ((start (1- (point)))) 451 | (setq description (buffer-substring start (if (search-forward " " (line-end-position) t) 452 | (point) 453 | (line-end-position))))) 454 | (push (cons (eask-2str command) (string-trim description)) data)) 455 | (forward-line 1))) 456 | (reverse data))) 457 | 458 | (defmacro easky--exec-with-help (help-cmd subcmd-index prompt &rest body) 459 | "Execute command with help manual parsed. 460 | 461 | For arguments HELP-CMD and SUBCMD-INDEX, see function `easky-parse-help-manual' 462 | for more information. 463 | 464 | Argument PROMPT is the first prompt to show for the current help command. BODY 465 | is the implementation." 466 | (declare (indent 3) (debug t)) 467 | `(let* ((options (easky-parse-help-manual ,help-cmd ,subcmd-index)) 468 | (offset (easky--completing-frame-offset options)) 469 | (command (completing-read 470 | ,prompt 471 | (lambda (string predicate action) 472 | (if (eq action 'metadata) 473 | `(metadata 474 | (display-sort-function . ,#'identity) 475 | (annotation-function 476 | . ,(lambda (cand) 477 | (concat (propertize " " 'display `((space :align-to (- right ,offset)))) 478 | (cdr (assoc cand options)))))) 479 | (complete-with-action action options string predicate))) 480 | nil t))) 481 | ,@body)) 482 | 483 | ;;;###autoload 484 | (defun easky () 485 | "Start Eask." 486 | (interactive) 487 | (easky--exec-with-help 488 | (easky-command "--help") 1 "Select `eask' command: " 489 | (let ((command (intern (format "easky-%s" command)))) 490 | (if (fboundp command) 491 | (call-interactively command) 492 | (user-error "Command %s not implemented yet, please consider report it to us!" command))))) 493 | 494 | ;;;###autoload 495 | (defun easky-create () 496 | "Start Eask create." 497 | (interactive) 498 | (easky--exec-with-help 499 | (easky-command "create" "--help") 2 "Select `eask create' command: " 500 | (let ((command (intern (format "easky-create-%s" command)))) 501 | (if (fboundp command) 502 | (call-interactively command) 503 | (user-error "Command %s not implemented yet, please consider report it to us!" command))))) 504 | 505 | ;;;###autoload 506 | (defun easky-generate () 507 | "Start Eask generate." 508 | (interactive) 509 | (easky--exec-with-help 510 | (easky-command "generate" "--help") 2 "Select `eask generate' command: " 511 | (let ((command (intern (format "easky-generate-%s" command)))) 512 | (if (fboundp command) 513 | (call-interactively command) 514 | (user-error "Command %s not implemented yet, please consider report it to us!" command))))) 515 | 516 | ;;;###autoload 517 | (defun easky-clean () 518 | "Start Eask clean." 519 | (interactive) 520 | (easky--exec-with-help 521 | (easky-command "clean" "--help") 2 "Select `eask clean' command: " 522 | (let ((command (intern (format "easky-clean-%s" command)))) 523 | (if (fboundp command) 524 | (call-interactively command) 525 | (user-error "Command %s not implemented yet, please consider report it to us!" command))))) 526 | 527 | ;;;###autoload 528 | (defun easky-link () 529 | "Start Eask link." 530 | (interactive) 531 | (easky--exec-with-help 532 | (easky-command "link" "--help") 2 "Select `eask link' command: " 533 | (let ((command (intern (format "easky-link-%s" command)))) 534 | (if (fboundp command) 535 | (call-interactively command) 536 | (user-error "Command %s not implemented yet, please consider report it to us!" command))))) 537 | 538 | ;;;###autoload 539 | (defun easky-lint () 540 | "Start Eask lint." 541 | (interactive) 542 | (easky--exec-with-help 543 | (easky-command "lint" "--help") 2 "Select `eask lint' command: " 544 | (let ((command (intern (format "easky-lint-%s" command)))) 545 | (if (fboundp command) 546 | (call-interactively command) 547 | (user-error "Command %s not implemented yet, please consider report it to us!" command))))) 548 | 549 | ;;;###autoload 550 | (defun easky-test () 551 | "Start Eask test." 552 | (interactive) 553 | (easky--exec-with-help 554 | (easky-command "test" "--help") 2 "Select `eask test' command: " 555 | (let ((command (intern (format "easky-test-%s" command)))) 556 | (if (fboundp command) 557 | (call-interactively command) 558 | (user-error "Command %s not implemented yet, please consider report it to us!" command))))) 559 | 560 | ;;;###autoload 561 | (defun easky-source () 562 | "Start Eask source." 563 | (interactive) 564 | (easky--exec-with-help 565 | (easky-command "source" "--help") 2 "Select `eask source' command: " 566 | (let ((command (intern (format "easky-source-%s" command)))) 567 | (if (fboundp command) 568 | (call-interactively command) 569 | (user-error "Command %s not implemented yet, please consider report it to us!" command))))) 570 | 571 | ;; 572 | ;; (@* "Commands" ) 573 | ;; 574 | 575 | (defconst easky-exec-files-options 576 | '(("All files (Default)" . "Select all files defined in your Eask-file") 577 | ("Select file" . "Select a file through completing-read") 578 | ("Enter wildcards" . "Enter wildcards pattern")) 579 | "Options for command `analyze'.") 580 | 581 | (defmacro easky--exec-with-files (prompt form-1 form-2 form-3) 582 | "Execute command with file selected. 583 | 584 | Argument PROMPT is a string to ask the user regarding the file action. 585 | 586 | Arguments FORM-1, FORM-2 and FORM-3 are execution by each file action." 587 | (declare (indent 1) (debug t)) 588 | `(let* ((offset (easky--completing-frame-offset easky-exec-files-options)) 589 | (option 590 | (completing-read 591 | ,prompt 592 | (lambda (string predicate action) 593 | (if (eq action 'metadata) 594 | `(metadata 595 | (display-sort-function . ,#'identity) 596 | (annotation-function 597 | . ,(lambda (cand) 598 | (concat (propertize " " 'display `((space :align-to (- right ,offset)))) 599 | (cdr (assoc cand easky-exec-files-options)))))) 600 | (complete-with-action action easky-exec-files-options string predicate))) 601 | nil t nil nil (nth 0 easky-exec-files-options))) 602 | (index (cl-position option (mapcar #'car easky-exec-files-options) :test 'string=))) 603 | (pcase index 604 | (0 ,form-1) 605 | (1 ,form-2) 606 | (2 ,form-3)))) 607 | 608 | (defun easky--select-el-files (candidate) 609 | "Return t if CANDIDATE is either directory or an elisp file." 610 | (or (and (string-suffix-p ".el" candidate) 611 | (not (string= dir-locals-file candidate))) 612 | (file-directory-p candidate))) 613 | 614 | (defun easky--select-files (candidate ext) 615 | "Return t if CANDIDATE is either directory or file with EXT." 616 | (or (string-suffix-p ext candidate) 617 | (file-directory-p candidate))) 618 | 619 | (defun easky--select-org-files (candidate) 620 | "Return t if CANDIDATE is either directory or an org file." 621 | (easky--select-files candidate ".org")) 622 | 623 | (defun easky--select-feature-files (candidate) 624 | "Return t if CANDIDATE is either directory or a feature file." 625 | (easky--select-files candidate ".feature")) 626 | 627 | ;;;###autoload 628 | (defun easky-help () 629 | "Print Eask help manual." 630 | (interactive) 631 | (easky--output-buffer (easky-command "--help"))) 632 | 633 | ;;;###autoload 634 | (defun easky-version () 635 | "Print Eask version." 636 | (interactive) 637 | (easky--output-buffer (easky-command "--version"))) 638 | 639 | ;;;###autoload 640 | (defun easky-info () 641 | "Print Eask-file information." 642 | (interactive) 643 | (easky--display (easky-command "info"))) 644 | 645 | ;;;###autoload 646 | (defun easky-status () 647 | "Display the state of the workspace." 648 | (interactive) 649 | (easky--display (easky-command "status"))) 650 | 651 | ;;;###autoload 652 | (defun easky-locate () 653 | "Print Eask installed location." 654 | (interactive) 655 | (easky--output-buffer (easky-command "locate"))) 656 | 657 | ;;;###autoload 658 | (defun easky-compile () 659 | "Byte-compile elc files." 660 | (interactive) 661 | (easky--exec-with-files "Select `compile' action: " 662 | (easky--display (easky-command "compile")) 663 | (let ((file (read-file-name "Select file for `compile': " 664 | nil nil t nil #'easky--select-el-files))) 665 | (easky--display (easky-command "compile" file))) 666 | (let ((wildcards (read-string "Wildcards: "))) 667 | (easky--display (easky-command "compile" wildcards))))) 668 | 669 | ;;;###autoload 670 | (defun easky-recompile () 671 | "Byte-recompile elc files." 672 | (interactive) 673 | (easky--exec-with-files "Select `recompile' action: " 674 | (easky--display (easky-command "recompile")) 675 | (let ((file (read-file-name "Select file for `recompile': " 676 | nil nil t nil #'easky--select-el-files))) 677 | (easky--display (easky-command "recompile" file))) 678 | (let ((wildcards (read-string "Wildcards: "))) 679 | (easky--display (easky-command "recompile" wildcards))))) 680 | 681 | ;;;###autoload 682 | (defun easky-search (query) 683 | "Search available packages with QUERY. 684 | 685 | This can be replaced with `easky-package-install' command." 686 | (interactive 687 | (list (read-string "Query: "))) 688 | (easky--display (easky-command "search" query))) 689 | 690 | ;;;###autoload 691 | (defun easky-files () 692 | "Print the list of all package files." 693 | (interactive) 694 | (easky--display (easky-command "files"))) 695 | 696 | ;;;###autoload 697 | (defun easky-archives () 698 | "Print used archives." 699 | (interactive) 700 | (let ((all (completing-read 701 | "List all available archives? (yes or no) " 702 | '("Yes" "No") nil t nil nil "No"))) 703 | (easky--display (easky-command "archives" 704 | (when (string= all "Yes") "--all"))))) 705 | 706 | ;;;###autoload 707 | (defun easky-keywords () 708 | "List available keywords that can be used in the header section." 709 | (interactive) 710 | (easky--display (easky-command "keywords"))) 711 | 712 | ;;;###autoload 713 | (defun easky-bump () 714 | "Bump version for your package or Eask-file." 715 | (interactive) 716 | (let ((levels (read-string "Levels: "))) 717 | (easky--display (easky-command "bump" levels)))) 718 | 719 | ;;;###autoload 720 | (defun easky-cat () 721 | "View filename(s)." 722 | (interactive) 723 | (let ((wildcards (read-string "Wildcards: "))) 724 | (easky--display (easky-command "cat" wildcards)))) 725 | 726 | ;;;###autoload 727 | (defun easky-concat () 728 | "Concatenate all source files." 729 | (interactive) 730 | (easky--display (easky-command "concat"))) 731 | 732 | ;;;###autoload 733 | (defun easky-loc () 734 | "Print LOC information." 735 | (interactive) 736 | (let ((pattern (read-string "Files: "))) 737 | (easky--display (easky-command "loc" pattern)))) 738 | 739 | ;;;###autoload 740 | (defun easky-path () 741 | "Print the PATH (`exec-path') from Eask sandbox." 742 | (interactive) 743 | (easky--display (easky-command "path"))) 744 | 745 | ;;;###autoload 746 | (defalias 'easky-exec-path #'easky-path) 747 | 748 | ;;;###autoload 749 | (defun easky-load-path () 750 | "Print the `load-path' from Eask sandbox." 751 | (interactive) 752 | (easky--display (easky-command "load-path"))) 753 | 754 | ;;;###autoload 755 | (defun easky-init (dir) 756 | "Initialize Eask-file in DIR." 757 | (interactive 758 | (list (read-directory-name "Where you want to place your Eask-file: "))) 759 | (let* ((eask-api-strict-p) 760 | (files (eask-api-files dir)) 761 | (new-name (expand-file-name "Eask" dir)) 762 | (base-name) 763 | (invalid-name) 764 | (continue)) 765 | (when (and files 766 | (setq continue 767 | (yes-or-no-p (concat "Eask-file already exist,\n\n " 768 | (mapconcat #'identity files "\n ") 769 | "\n\nContinue the creation? ")))) 770 | (while (or (file-exists-p new-name) invalid-name) 771 | (setq new-name (read-file-name 772 | (format 773 | (concat (if invalid-name 774 | "[?] Invalid filename `%s', " 775 | "[?] Filename `%s' already taken, ") 776 | "try another one: ") 777 | (file-name-nondirectory (directory-file-name new-name))) 778 | dir nil nil nil 779 | #'eask-api-check-filename) 780 | base-name (file-name-nondirectory (directory-file-name new-name)) 781 | invalid-name (not (eask-api-check-filename base-name))) 782 | (easky--inhibit-log (message "Checking filename...")) 783 | (sleep-for 0.2))) 784 | (when continue 785 | ;; Starting Eask-file creation! 786 | (let* ((project-dir (file-name-nondirectory (directory-file-name dir))) 787 | (project-name (eask-guess-package-name project-dir)) 788 | (package-name (read-string (format "package name: (%s) " project-name) nil nil project-name)) 789 | (version (read-string "version: (1.0.0) " nil nil "1.0.0")) 790 | (description (read-string "description: ")) 791 | (guess-entry-point (format "%s.el" project-name)) 792 | (entry-point (read-string (format "entry point: (%s) " guess-entry-point) 793 | nil nil guess-entry-point)) 794 | (emacs-version (read-string "emacs version: (26.1) " nil nil "26.1")) 795 | (website (read-string "website: ")) 796 | (keywords (read-string "keywords: ")) 797 | (keywords (if (string-match-p "," keywords) 798 | (split-string keywords ",[ \t\n]*" t "[ ]+") 799 | (split-string keywords "[ \t\n]+" t "[ ]+"))) 800 | (keywords (mapconcat (lambda (s) (format "%S" s)) keywords " ")) 801 | (content (format 802 | "(package \"%s\" 803 | \"%s\" 804 | \"%s\") 805 | 806 | (website-url \"%s\") 807 | (keywords %s) 808 | 809 | (package-file \"%s\") 810 | 811 | (script \"test\" \"echo \\\"Error: no test specified\\\" && exit 1\") 812 | 813 | (source \"gnu\") 814 | 815 | (depends-on \"emacs\" \"%s\") 816 | " 817 | package-name version description website keywords 818 | entry-point emacs-version))) 819 | (lv-message (with-temp-buffer ; colorized 820 | (insert content) 821 | (delay-mode-hooks (funcall #'eask-mode)) 822 | (ignore-errors (font-lock-ensure)) 823 | (buffer-string))) 824 | (unwind-protect 825 | (when (yes-or-no-p (format "About to write to %s:\n\nIs this Okay? " new-name)) 826 | (write-region content nil new-name) 827 | (find-file new-name)) 828 | (lv-delete-window)))))) 829 | 830 | ;;;###autoload 831 | (defun easky-package (dir) 832 | "Package your package to DIR." 833 | (interactive 834 | (list (read-directory-name "Destination: " nil nil nil "dist"))) ; default to dist 835 | (easky--display (easky-command "package" "--dest" dir))) 836 | 837 | ;;;###autoload 838 | (defun easky-refresh () 839 | "Package your package to DIR." 840 | (interactive) 841 | (easky--display (easky-command "refresh"))) 842 | 843 | ;;;###autoload 844 | (defun easky-recipe () 845 | "Recommend me a recipe format." 846 | (interactive) 847 | (easky--display (easky-command "recipe"))) 848 | 849 | ;;;###autoload 850 | (defun easky-outdated () 851 | "List outdated packages." 852 | (interactive) 853 | (easky--display (easky-command "outdated"))) 854 | 855 | ;;;###autoload 856 | (defun easky-upgrade-eask () 857 | "Upgrade Eask CLI." 858 | (interactive) 859 | (easky--display (easky-command "upgrade-eask"))) 860 | 861 | ;; 862 | ;;; Documentation 863 | 864 | ;;;###autoload 865 | (defun easky-docs () 866 | "Build documentation." 867 | (interactive) 868 | (let ((pattern (read-string "Files: "))) 869 | (easky--display (easky-command "docs" pattern)))) 870 | 871 | ;; 872 | ;;; Eask-file Checker 873 | 874 | (defconst easky-analyze-options 875 | '(("All Eask-files (Default)" . "Check all eask files") 876 | ("Pick a Eask-file" . "Select an Eask-file through completing-read")) 877 | "Options for command `analyze'.") 878 | 879 | (defun easky-analyze-collection (string predicate action) 880 | "Collection arguments for function `easky-analyze'. 881 | 882 | Arguments STRING, PREDICATE and ACTION are default value for collection 883 | argument." 884 | (if (eq action 'metadata) 885 | (let ((offset (easky--completing-frame-offset easky-analyze-options))) 886 | `(metadata 887 | (display-sort-function . ,#'identity) 888 | (annotation-function 889 | . ,(lambda (cand) 890 | (concat (propertize " " 'display `((space :align-to (- right ,offset)))) 891 | (cdr (assoc cand easky-analyze-options))))))) 892 | (complete-with-action action easky-analyze-options string predicate))) 893 | 894 | ;;;###autoload 895 | (defun easky-analyze (action) 896 | "Run Eask-file checker. 897 | 898 | Argument ACTION is used to select checker's action." 899 | (interactive 900 | (list (completing-read "Select `analyze' action: " 901 | #'easky-analyze-collection nil t nil nil 902 | (car (nth 0 easky-analyze-options))))) 903 | (let* ((options (mapcar #'car easky-analyze-options)) 904 | (index (cl-position action options :test 'string=))) 905 | (pcase index 906 | (0 (easky--display (easky-command "analyze"))) 907 | (1 (let ((file (read-file-name "Select file for `analyze': " 908 | nil nil t nil 909 | (lambda (cand) 910 | (or (eask-api-check-filename cand) 911 | (file-directory-p cand)))))) 912 | (easky--display (easky-command "analyze" file))))))) 913 | 914 | ;; 915 | ;;; Execution 916 | 917 | ;;;###autoload 918 | (defun easky-eask (args) 919 | "Run the Eask CLI directly with ARGS." 920 | (interactive 921 | (list (read-string "eask "))) 922 | (easky--display (easky-command args))) 923 | 924 | ;;;###autoload 925 | (defun easky-exec (args) 926 | "Run eask exec with ARGS." 927 | (interactive 928 | (list (read-string "eask exec "))) 929 | (easky--display (easky-command "exec" args))) 930 | 931 | ;;;###autoload 932 | (defun easky-emacs (args) 933 | "Run eask emacs with ARGS." 934 | (interactive 935 | (list (read-string "eask emacs "))) 936 | (easky--display (easky-command "emacs" args))) 937 | 938 | ;;;###autoload 939 | (defun easky-eval (args) 940 | "Run eask eval with ARGS." 941 | (interactive 942 | (list (read-string "eask eval "))) 943 | (easky--display (easky-command "eval" args))) 944 | 945 | ;;;###autoload 946 | (defun easky-load () 947 | "Run eask load." 948 | (interactive) 949 | (easky--exec-with-files "Select `load' action: " 950 | (easky--display (easky-command "load")) 951 | (let ((file (read-file-name "Select file for `test ert': " 952 | nil nil t nil #'easky--select-el-files))) 953 | (easky--display (easky-command "load" file))) 954 | (let ((wildcards (read-string "Wildcards: "))) 955 | (easky--display (easky-command "load" wildcards))))) 956 | 957 | ;;;###autoload 958 | (defun easky-run () 959 | "Run Eask's custom command/script." 960 | (interactive) 961 | (easky--exec-with-help 962 | "eask run --help" 2 "Select `eask run' command: " 963 | (let ((command (intern (format "easky-run-%s" command)))) 964 | (if (fboundp command) 965 | (call-interactively command) 966 | (user-error "Command %s not implemented yet, please consider report it to us!" command))))) 967 | 968 | ;;;###autoload 969 | (defun easky-run-command () 970 | "Execute Eask's command." 971 | (interactive) 972 | (easky--setup 973 | (if eask-commands 974 | (let* ((selected-command 975 | (completing-read 976 | "Run Eask's command: " 977 | (lambda (string predicate action) 978 | (if (eq action 'metadata) 979 | `(metadata 980 | (display-sort-function . ,#'identity) 981 | (annotation-function 982 | . ,(lambda (cand) 983 | (concat (propertize " " 'display `((space :align-to (- right)))) 984 | (cdr (assoc cand eask-commands)))))) 985 | (complete-with-action action eask-commands string predicate))) 986 | nil t))) 987 | (easky--display (easky-command "run" "command" selected-command))) 988 | (message (concat 989 | "Not finding any command to run, you can add one by adding the line below to your Eask-file:\n\n" 990 | " (eask-defcommand my-command ...)" 991 | "\n\nThen re-run this command once again!"))))) 992 | 993 | ;;;###autoload 994 | (defun easky-run-script () 995 | "Execute Eask's script." 996 | (interactive) 997 | (easky--setup 998 | (if eask-scripts 999 | (let* ((offset (easky--completing-frame-offset eask-scripts)) 1000 | (selected-script 1001 | (completing-read 1002 | "Run Eask's script: " 1003 | (lambda (string predicate action) 1004 | (if (eq action 'metadata) 1005 | `(metadata 1006 | (display-sort-function . ,#'identity) 1007 | (annotation-function 1008 | . ,(lambda (cand) 1009 | (concat (propertize " " 'display `((space :align-to (- right ,offset)))) 1010 | (cdr (assoc cand eask-scripts)))))) 1011 | (complete-with-action action eask-scripts string predicate))) 1012 | nil t))) 1013 | (easky--display (easky-command "run" "script" selected-script))) 1014 | (message (concat 1015 | "Not finding any script to run, you can add one by adding the line below to your Eask-file:\n\n" 1016 | " (script \"test\" \"echo Hi!~\")" 1017 | "\n\nThen re-run this command once again!"))))) 1018 | 1019 | ;;;###autoload 1020 | (defun easky-docker () 1021 | "Run eask docker." 1022 | (interactive) 1023 | (let ((version (read-string "Emacs version: (minimum 26.1) ")) 1024 | (command (read-string "Eask command: "))) 1025 | (easky--display (easky-command "docker" version command)))) 1026 | 1027 | ;; 1028 | ;;; Install 1029 | 1030 | (defconst easky-exec-packages-options 1031 | '(("Current Package (Default)" . "Operate with current package defined in Eask-file") 1032 | ("Specified" . "Specify packages through read-string")) 1033 | "Options for command `packages'.") 1034 | 1035 | (defmacro easky--exec-with-packages (prompt form-1 form-2) 1036 | "Execute command with packages selected. 1037 | 1038 | Argument PROMPT is a string to ask the user regarding the file action. 1039 | 1040 | Arguments FORM-1 and FORM-2 are execution by each file action." 1041 | (declare (indent 1) (debug t)) 1042 | `(let* ((offset (easky--completing-frame-offset easky-exec-packages-options)) 1043 | (option 1044 | (completing-read 1045 | ,prompt 1046 | (lambda (string predicate action) 1047 | (if (eq action 'metadata) 1048 | `(metadata 1049 | (display-sort-function . ,#'identity) 1050 | (annotation-function 1051 | . ,(lambda (cand) 1052 | (concat (propertize " " 'display `((space :align-to (- right ,offset)))) 1053 | (cdr (assoc cand easky-exec-packages-options)))))) 1054 | (complete-with-action action easky-exec-packages-options string predicate))) 1055 | nil t nil nil (nth 0 easky-exec-packages-options))) 1056 | (index (cl-position option (mapcar #'car easky-exec-packages-options) :test 'string=))) 1057 | (pcase index 1058 | (0 ,form-1) 1059 | (1 ,form-2)))) 1060 | 1061 | ;;;###autoload 1062 | (defun easky-install () 1063 | "Install packages." 1064 | (interactive) 1065 | (easky--exec-with-packages "Select `install' action: " 1066 | (easky--display (easky-command "install")) 1067 | (let ((pattern (read-string "Specify packages: "))) 1068 | (easky--display (easky-command "install" pattern))))) 1069 | 1070 | ;;;###autoload 1071 | (defun easky-uninstall () 1072 | "Uninstall packages." 1073 | (interactive) 1074 | (easky--exec-with-packages "Select `uninstall' action: " 1075 | (easky--display (easky-command "uninstall")) 1076 | (let ((pattern (read-string "Specify packages: "))) 1077 | (easky--display (easky-command "uninstall" pattern))))) 1078 | 1079 | ;;;###autoload 1080 | (defun easky-reinstall () 1081 | "Reinstall packages." 1082 | (interactive) 1083 | (easky--exec-with-packages "Select `reinstall' action: " 1084 | (easky--display (easky-command "reinstall")) 1085 | (let ((pattern (read-string "Specify packages: "))) 1086 | (easky--display (easky-command "reinstall" pattern))))) 1087 | 1088 | ;;;###autoload 1089 | (defun easky-upgrade () 1090 | "Upgrade packages." 1091 | (interactive) 1092 | (easky--exec-with-packages "Select `upgrade' action: " 1093 | (easky--display (easky-command "upgrade")) 1094 | (let ((pattern (read-string "Specify packages: "))) 1095 | (easky--display (easky-command "upgrade" pattern))))) 1096 | 1097 | ;;;###autoload 1098 | (defun easky-install-deps () 1099 | "Update all packages from Eask sandbox." 1100 | (interactive) 1101 | (let ((install-dev (completing-read 1102 | "Install development dependencies? (yes or no) " 1103 | '("Yes" "No") nil t nil nil "No"))) 1104 | (easky--display (easky-command "install-deps" (when (string= install-dev "Yes") 1105 | "--dev"))))) 1106 | 1107 | ;;;###autoload 1108 | (defun easky-install-file () 1109 | "Install packages through files." 1110 | (interactive) 1111 | (let ((pattern (read-string "Specify files: "))) 1112 | (easky--display (easky-command "install-file" pattern)))) 1113 | 1114 | ;;;###autoload 1115 | (defun easky-install-vc () 1116 | "Install packages through version controls." 1117 | (interactive) 1118 | (let ((pattern (read-string "Specify specifications: "))) 1119 | (easky--display (easky-command "install-vc" pattern)))) 1120 | 1121 | ;; 1122 | ;;; Create 1123 | 1124 | ;;;###autoload 1125 | (defun easky-create-package () 1126 | "Create a new elisp package." 1127 | (interactive) 1128 | (user-error 1129 | (concat "This command is currently not supported; please use the command line " 1130 | "with the command `eask create package`!"))) 1131 | 1132 | ;;;###autoload 1133 | (defun easky-create-elpa () 1134 | "Create a new ELPA using `github-elpa'." 1135 | (interactive) 1136 | (user-error 1137 | (concat "This command is currently not supported; please use the command line " 1138 | "with the command `eask create elpa`!"))) 1139 | 1140 | ;;;###autoload 1141 | (defun easky-create-el-project () 1142 | "Create a new project with `el-project'." 1143 | (interactive) 1144 | (user-error 1145 | (concat "This command is currently not supported; please use the command line " 1146 | "with the command `eask create el-project`!"))) 1147 | 1148 | ;; 1149 | ;;; Generate 1150 | ;;; 1151 | 1152 | ;;;###autoload 1153 | (defun easky-generate-autoloads () 1154 | "Generate autloads file." 1155 | (interactive) 1156 | (easky--display (easky-command "generate" "autoloads"))) 1157 | 1158 | ;;;###autoload 1159 | (defun easky-generate-pkg-file () 1160 | "Generate pkg-file and printed it out." 1161 | (interactive) 1162 | (easky--display (easky-command "generate" "pkg-file"))) 1163 | 1164 | ;;;###autoload 1165 | (defun easky-generate-license () 1166 | "Generate license file." 1167 | (interactive) 1168 | (let ((license-type (read-string "License type: ")) 1169 | (filename (read-file-name "New LICENSE filename: "))) 1170 | (easky--display (easky-command "generate" "license" license-type 1171 | (when filename "-o") 1172 | (when filename filename))))) 1173 | 1174 | ;;;###autoload 1175 | (defun easky-generate-ignore () 1176 | "Generate ignore file." 1177 | (interactive) 1178 | (let ((ignore-type (read-string "Ignore type: ")) 1179 | (filename (read-file-name "New ignore filename: "))) 1180 | (easky--display (easky-command "generate" "ignore" ignore-type 1181 | (when filename "-o") 1182 | (when filename filename))))) 1183 | 1184 | ;;;###autoload 1185 | (defun easky-generate-test () 1186 | "Generate workflow file." 1187 | (interactive) 1188 | (easky--exec-with-help 1189 | (easky-command "generate" "test" "--help") 3 "Select `eask generate test' command: " 1190 | (let ((command (intern (format "easky-generate-test-%s" command)))) 1191 | (if (fboundp command) 1192 | (call-interactively command) 1193 | (user-error "Command %s not implemented yet, please consider report it to us!" command))))) 1194 | 1195 | ;;;###autoload 1196 | (defun easky-generate-test-ert () 1197 | "Setup test files for ert tests." 1198 | (interactive) 1199 | (let* ((prompt (format "Name of the unit tests: ")) 1200 | (filenames (read-string prompt))) 1201 | (easky--display (apply #'easky-command (append '("generate" "test" "ert") 1202 | (split-string filenames " ")))))) 1203 | 1204 | ;;;###autoload 1205 | (defun easky-generate-test-ert-runner () 1206 | "Setup test files for ert-runner." 1207 | (interactive) 1208 | (let* ((prompt (format "Name of the unit tests: ")) 1209 | (filenames (read-string prompt))) 1210 | (easky--display (apply #'easky-command (append '("generate" "test" "ert-runner") 1211 | (split-string filenames " ")))))) 1212 | 1213 | ;;;###autoload 1214 | (defun easky-generate-test-buttercup () 1215 | "Setup test files for buttercup." 1216 | (interactive) 1217 | (easky--display (easky-command "generate" "test" "buttercup"))) 1218 | 1219 | ;;;###autoload 1220 | (defun easky-generate-test-ecukes () 1221 | "Setup test files for ecukes." 1222 | (interactive) 1223 | (easky--display (easky-command "generate" "test" "ecukes"))) 1224 | 1225 | ;;;###autoload 1226 | (defun easky-generate-workflow () 1227 | "Generate workflow file." 1228 | (interactive) 1229 | (easky--exec-with-help 1230 | (easky-command "generate" "workflow" "--help") 3 "Select `eask generate workflow' command: " 1231 | (let ((command (intern (format "easky-generate-workflow-%s" command)))) 1232 | (if (fboundp command) 1233 | (call-interactively command) 1234 | (user-error "Command %s not implemented yet, please consider report it to us!" command))))) 1235 | 1236 | ;;;###autoload 1237 | (defun easky-generate-workflow-circle-ci () 1238 | "Generate CircleCI test file." 1239 | (interactive) 1240 | (let* ((dir (expand-file-name ".circleci/")) 1241 | (prompt (format "Filename to create `%s`: " dir)) 1242 | (filename (read-string prompt "config.yml"))) 1243 | (easky--display (easky-command "generate" "workflow" "circle-ci" filename)))) 1244 | 1245 | ;;;###autoload 1246 | (defun easky-generate-workflow-github () 1247 | "Generate GitHub Actions test file." 1248 | (interactive) 1249 | (let* ((dir (expand-file-name ".github/workflows/")) 1250 | (prompt (format "Filename to create `%s`: " dir)) 1251 | (filename (read-string prompt "test.yml"))) 1252 | (easky--display (easky-command "generate" "workflow" "github" filename)))) 1253 | 1254 | ;;;###autoload 1255 | (defun easky-generate-workflow-gitlab () 1256 | "Generate GitLab Runner test file." 1257 | (interactive) 1258 | (let* ((dir default-directory) 1259 | (prompt (format "Filename to create `%s`: " dir)) 1260 | (filename (read-string prompt ".gitlab-ci.yml"))) 1261 | (easky--display (easky-command "generate" "workflow" "gitlab" filename)))) 1262 | 1263 | ;;;###autoload 1264 | (defun easky-generate-workflow-travis-ci () 1265 | "Generate Travis CI test file." 1266 | (interactive) 1267 | (let* ((dir default-directory) 1268 | (prompt (format "Filename to create `%s`: " dir)) 1269 | (filename (read-string prompt ".travis.yml"))) 1270 | (easky--display (easky-command "generate" "workflow" "gitlab" filename)))) 1271 | 1272 | ;; 1273 | ;;; Cleaning 1274 | 1275 | ;;;###autoload 1276 | (defun easky-clean-workspace () 1277 | "Clean up .eask directory." 1278 | (interactive) 1279 | (easky--display (easky-command "clean" "workspace"))) 1280 | 1281 | ;;;###autoload 1282 | (defalias 'easky-clean-.eask #'easky-clean-workspace) 1283 | 1284 | ;;;###autoload 1285 | (defun easky-clean-elc () 1286 | "Remove byte compiled files generated by eask compile." 1287 | (interactive) 1288 | (easky--display (easky-command "clean" "elc"))) 1289 | 1290 | ;;;###autoload 1291 | (defun easky-clean-dist (dest) 1292 | "Delete dist subdirectory. 1293 | 1294 | Argument DEST is the destination folder, default is set to `dist'." 1295 | (interactive 1296 | (list (read-directory-name "Destination: " nil nil nil "dist"))) 1297 | (easky--display (easky-command "clean" "dist" dest))) 1298 | 1299 | ;;;###autoload 1300 | (defun easky-clean-autoloads () 1301 | "Remove generated autoloads file." 1302 | (interactive) 1303 | (easky--display (easky-command "clean" "autoloads"))) 1304 | 1305 | ;;;###autoload 1306 | (defun easky-clean-pkg-file () 1307 | "Remove generated pkg-file." 1308 | (interactive) 1309 | (easky--display (easky-command "clean" "pkg-file"))) 1310 | 1311 | ;;;###autoload 1312 | (defun easky-clean-log-file () 1313 | "Remove all generated log files." 1314 | (interactive) 1315 | (easky--display (easky-command "clean" "log-file"))) 1316 | 1317 | ;;;###autoload 1318 | (defun easky-clean-all () 1319 | "Do all cleaning tasks." 1320 | (interactive) 1321 | (easky--display (easky-command "clean" "all"))) 1322 | 1323 | ;; 1324 | ;;; Linking 1325 | 1326 | ;;;###autoload 1327 | (defun easky-link-add () 1328 | "Link a local package." 1329 | (interactive) 1330 | (let ((name (read-string "Link name: ")) 1331 | (path (read-directory-name "Source directory: "))) 1332 | (easky--display (easky-command "link" "add" name path)))) 1333 | 1334 | ;;;###autoload 1335 | (defun easky-link-delete () 1336 | "Delete local linked packages." 1337 | (interactive) 1338 | (easky--setup 1339 | (let* 1340 | ((links (eask-link-list)) 1341 | (offset (easky--completing-frame-offset links)) 1342 | (link (completing-read 1343 | "Select a link: " 1344 | (lambda (string predicate action) 1345 | (if (eq action 'metadata) 1346 | `(metadata 1347 | (display-sort-function . ,#'identity) 1348 | (annotation-function 1349 | . ,(lambda (cand) 1350 | (concat (propertize " " 'display `((space :align-to (- right ,offset)))) 1351 | (cdr (assoc cand links)))))) 1352 | (complete-with-action action links string predicate))) 1353 | nil t))) 1354 | (easky--display (easky-command "link" "delete" link))))) 1355 | 1356 | ;;;###autoload 1357 | (defun easky-link-list () 1358 | "List all project links." 1359 | (interactive) 1360 | (easky--display (easky-command "link" "list"))) 1361 | 1362 | ;; 1363 | ;;; Linting 1364 | 1365 | ;;;###autoload 1366 | (defun easky-lint-checkdoc () 1367 | "Run checkdoc." 1368 | (interactive) 1369 | (easky--exec-with-files "Select `lint checkdoc' action: " 1370 | (easky--display (easky-command "lint" "checkdoc")) 1371 | (let ((file (read-file-name "Select file for `lint checkdoc': " 1372 | nil nil t nil #'easky--select-el-files))) 1373 | (easky--display (easky-command "lint" "checkdoc" file))) 1374 | (let ((wildcards (read-string "Wildcards: "))) 1375 | (easky--display (easky-command "lint" "checkdoc" wildcards))))) 1376 | 1377 | ;;;###autoload 1378 | (defun easky-lint-check-declare () 1379 | "Run check-declare." 1380 | (interactive) 1381 | (easky--exec-with-files "Select `lint check-declare' action: " 1382 | (easky--display (easky-command "lint" "check-declare")) 1383 | (let ((file (read-file-name "Select file for `lint check-declare': " 1384 | nil nil t nil #'easky--select-el-files))) 1385 | (easky--display (easky-command "lint" "check-declare" file))) 1386 | (let ((wildcards (read-string "Wildcards: "))) 1387 | (easky--display (easky-command "lint" "check-declare" wildcards))))) 1388 | 1389 | ;;;###autoload 1390 | (defun easky-lint-elint () 1391 | "Run elint." 1392 | (interactive) 1393 | (easky--exec-with-files "Select `lint elint' action: " 1394 | (easky--display (easky-command "lint" "elint")) 1395 | (let ((file (read-file-name "Select file for `lint elint': " 1396 | nil nil t nil #'easky--select-el-files))) 1397 | (easky--display (easky-command "lint" "elint" file))) 1398 | (let ((wildcards (read-string "Wildcards: "))) 1399 | (easky--display (easky-command "lint" "elint" wildcards))))) 1400 | 1401 | ;;;###autoload 1402 | (defun easky-lint-elsa () 1403 | "Run elsa." 1404 | (interactive) 1405 | (easky--exec-with-files "Select `lint elsa' action: " 1406 | (easky--display (easky-command "lint" "elsa")) 1407 | (let ((file (read-file-name "Select file for `lint elsa': " 1408 | nil nil t nil #'easky--select-el-files))) 1409 | (easky--display (easky-command "lint" "elsa" file))) 1410 | (let ((wildcards (read-string "Wildcards: "))) 1411 | (easky--display (easky-command "lint" "elsa" wildcards))))) 1412 | 1413 | ;;;###autoload 1414 | (defun easky-lint-indent () 1415 | "Run indent-linet." 1416 | (interactive) 1417 | (easky--exec-with-files "Select `lint indent' action: " 1418 | (easky--display (easky-command "lint" "indent")) 1419 | (let ((file (read-file-name "Select file for `lint indent': " 1420 | nil nil t nil #'easky--select-el-files))) 1421 | (easky--display (easky-command "lint" "indent" file))) 1422 | (let ((wildcards (read-string "Wildcards: "))) 1423 | (easky--display (easky-command "lint" "indent" wildcards))))) 1424 | 1425 | ;;;###autoload 1426 | (defun easky-lint-package () 1427 | "Run package-lint." 1428 | (interactive) 1429 | (easky--exec-with-files "Select `lint package' action: " 1430 | (easky--display (easky-command "lint" "package")) 1431 | (let ((file (read-file-name "Select file for `lint package': " 1432 | nil nil t nil #'easky--select-el-files))) 1433 | (easky--display (easky-command "lint" "package" file))) 1434 | (let ((wildcards (read-string "Wildcards: "))) 1435 | (easky--display (easky-command "lint" "package" wildcards))))) 1436 | 1437 | ;;;###autoload 1438 | (defun easky-lint-regexps () 1439 | "Run relint." 1440 | (interactive) 1441 | (easky--exec-with-files "Select `lint regexps' action: " 1442 | (easky--display (easky-command "lint" "regexps")) 1443 | (let ((file (read-file-name "Select file for `lint regexps': " 1444 | nil nil t nil #'easky--select-el-files))) 1445 | (easky--display (easky-command "lint" "regexps" file))) 1446 | (let ((wildcards (read-string "Wildcards: "))) 1447 | (easky--display (easky-command "lint" "regexps" wildcards))))) 1448 | 1449 | ;;;###autoload 1450 | (defalias 'easky-lint-relint #'easky-lint-regexps) 1451 | 1452 | ;;;###autoload 1453 | (defun easky-lint-keywords () 1454 | "Run keywords linter." 1455 | (interactive) 1456 | (easky--display (easky-command "lint" "keywords"))) 1457 | 1458 | ;;;###autoload 1459 | (defun easky-lint-license () 1460 | "Run license linter." 1461 | (interactive) 1462 | (easky--display (easky-command "lint" "license"))) 1463 | 1464 | ;;;###autoload 1465 | (defun easky-lint-org () 1466 | "Run org-lint on Org files." 1467 | (interactive) 1468 | (easky--exec-with-files "Select `lint org' action: " 1469 | (easky--display (easky-command "lint" "org")) 1470 | (let ((file (read-file-name "Select file for `lint org': " 1471 | nil nil t nil #'easky--select-org-files))) 1472 | (easky--display (easky-command "lint" "org" file))) 1473 | (let ((wildcards (read-string "Wildcards: "))) 1474 | (easky--display (easky-command "lint" "org" wildcards))))) 1475 | 1476 | ;; 1477 | ;;; Testing 1478 | 1479 | ;;;###autoload 1480 | (defun easky-test-activate () 1481 | "Run activate test." 1482 | (interactive) 1483 | (easky--exec-with-files "Select `test activate' action: " 1484 | (easky--display (easky-command "test" "activate")) 1485 | (let ((file (read-file-name "Select file for `test activate': " 1486 | nil nil t nil #'easky--select-el-files))) 1487 | (easky--display (easky-command "test" "activate" file))) 1488 | (let ((wildcards (read-string "Wildcards: "))) 1489 | (easky--display (easky-command "test" "activate" wildcards))))) 1490 | 1491 | ;;;###autoload 1492 | (defun easky-test-ert () 1493 | "Run ert test." 1494 | (interactive) 1495 | (easky--exec-with-files "Select `test ert' action: " 1496 | (easky--display (easky-command "test" "test ert")) 1497 | (let ((file (read-file-name "Select file for `test ert': " 1498 | nil nil t nil #'easky--select-el-files))) 1499 | (easky--display (easky-command "test" "ert" file))) 1500 | (let ((wildcards (read-string "Wildcards: "))) 1501 | (easky--display (easky-command "test" "ert" wildcards))))) 1502 | 1503 | ;;;###autoload 1504 | (defun easky-test-ert-runner () 1505 | "Run ert test through `ert-runner'." 1506 | (interactive) 1507 | (easky--exec-with-files "Select `test ert-runner' action: " 1508 | (easky--display (easky-command "test" "ert-runner")) 1509 | (let ((file (read-file-name "Select file for `test ert-runner': " 1510 | nil nil t nil #'easky--select-el-files))) 1511 | (easky--display (easky-command "test" "ert-runner" file))) 1512 | (let ((wildcards (read-string "Wildcards: "))) 1513 | (easky--display (easky-command "test" "ert-runner" wildcards))))) 1514 | 1515 | ;;;###autoload 1516 | (defun easky-test-buttercup () 1517 | "Run buttercup test." 1518 | (interactive) 1519 | (easky--exec-with-files "Select `test buttercup' action: " 1520 | (easky--display (easky-command "test" "buttercup")) 1521 | (let ((file (read-file-name "Select file for `test buttercup': " 1522 | nil nil t nil #'easky--select-el-files))) 1523 | (easky--display (easky-command "test" "buttercup" file))) 1524 | (let ((wildcards (read-string "Wildcards: "))) 1525 | (easky--display (easky-command "test" "buttercup" wildcards))))) 1526 | 1527 | ;;;###autoload 1528 | (defun easky-test-ecukes () 1529 | "Run ecukes test." 1530 | (interactive) 1531 | (easky--exec-with-files "Select `test ecukes' action: " 1532 | (easky--display (easky-command "test" "ecukes")) 1533 | (let ((file (read-file-name "Select file for `test ecukes': " 1534 | nil nil t nil #'easky--select-feature-files))) 1535 | (easky--display (easky-command "test" "ecukes" file))) 1536 | (let ((wildcards (read-string "Wildcards: "))) 1537 | (easky--display (easky-command "test" "ecukes" wildcards))))) 1538 | 1539 | ;;;###autoload 1540 | (defun easky-test-melpazoid () 1541 | "Run melpazoid test." 1542 | (interactive) 1543 | (easky--exec-with-files "Select `test melpazoid' action: " 1544 | (easky--display (easky-command "test" "melpazoid")) 1545 | (let ((dir (read-directory-name "Select directory for `test melpazoid': "))) 1546 | (easky--display (easky-command "test" "melpazoid" dir))) 1547 | nil)) 1548 | 1549 | ;; 1550 | ;;; Control DSL 1551 | 1552 | ;;;###autoload 1553 | (defun easky-source-add () 1554 | "Add an archive source." 1555 | (interactive) 1556 | (let ((name (read-string "Source name to add: ")) 1557 | (path (read-string "Location/URL: "))) 1558 | (easky--display (easky-command "source" "add" name path)))) 1559 | 1560 | ;;;###autoload 1561 | (defun easky-source-delete () 1562 | "Delete an archive source." 1563 | (interactive) 1564 | (let ((name (read-string "Source name to delete: "))) 1565 | (easky--display (easky-command "source" "delete" name)))) 1566 | 1567 | ;;;###autoload 1568 | (defun easky-source-list () 1569 | "List all sources." 1570 | (interactive) 1571 | (easky--display (easky-command "source" "list"))) 1572 | 1573 | (provide 'easky) 1574 | ;;; easky.el ends here 1575 | --------------------------------------------------------------------------------