diff --git a/CHANGELOG.md b/CHANGELOG.md index 87267f9c..d0330cb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how * chore: Alias `pack` to `package` command (e209d7c8152a17de81613f09b380a2f5ad05697a) * fix(build.yml): Release with `.tar.gz` instead of `.zip` on Unix-like system (#214 and #216) * feat: Add `loc` command (#227) +* fix: Allow initialize in other scopes (#229) ## 0.9.x > Released Nov 17, 2023 diff --git a/cmds/core/init.js b/cmds/core/init.js index 21953976..57ac0003 100644 --- a/cmds/core/init.js +++ b/cmds/core/init.js @@ -54,181 +54,6 @@ exports.handler = async (argv) => { break; } } else { - await create_eask_file(); + await UTIL.e_call(argv, 'core/init'); } }; - -var instance; /* `readline` instance */ - -async function create_eask_file(dir) { - let basename = path.basename(process.cwd()); - instance = readline.createInterface({ - input: process.stdin, - output: process.stdout - }); - - let new_name = path.join(process.cwd(), 'Eask'); - - // Search for existing Eask-files! - let files = fs.readdirSync(process.cwd()).filter(fn => fn.match('Eask')); - let continue_op = false; - - if (files.length != 0) { - // Print out all existing Eask-files, and ask for continuation! - console.log('Eask-file is already exists,'); - console.log(''); - for (let index in files) { - console.log(' ' + path.join(process.cwd(), files[index])); - } - console.log(''); - await ask(`Continue the creation? (yes) `, (answer) => { continue_op = answer; }); - - // Abort if declined! - if (continue_op != '' && continue_op != 'yes') { - process.exit(0); - } - - // Ask for new name unitl the filename is available! - let new_basename = path.basename(new_name); - let invalid_name = false; - - // Ask for new name until we found one that meets our requirements! - while (fs.existsSync(new_name) || invalid_name) { - let prompt; - - // Handle invalid file name! - if (invalid_name) { - prompt = `[?] Invalid filename '${new_basename}', `; - } else { - prompt = `[?] Filename '${new_basename}' already taken, `; - } - - // Ask for new name! - await ask(prompt + `try another one: `, - (answer) => { - new_name = path.join(process.cwd(), answer); - new_basename = answer; - invalid_name = !check_eask_filename(answer); - }); - } - } - - // Starting writing Eask-file! - let name, version, description, entry_point, emacs_version, website_url, keywords; - - let guessed_package_name = guess_package_name(basename); - let guessed_entry_point = guess_entry_point(basename); - - await ask(`package name: (${guessed_package_name}) `, (answer) => { - name = answer || guessed_package_name; - }); - await ask(`version: (1.0.0) `, (answer) => { version = answer || '1.0.0'; }); - await ask(`description: `, (answer) => { description = answer; }); - await ask(`entry point: (${guessed_entry_point}) `, (answer) => { - entry_point = answer || guessed_entry_point; - }); - await ask(`emacs version: (26.1) `, (answer) => { emacs_version = answer || '26.1'; }); - await ask(`website: `, (answer) => { website_url = answer; }); - await ask(`keywords: `, (answer) => { keywords = answer; }); - - name = remove_double_quotes(name); - version = remove_double_quotes(version); - description = remove_double_quotes(description); - entry_point = remove_double_quotes(entry_point); - emacs_version = remove_double_quotes(emacs_version); - website_url = remove_double_quotes(website_url); - keywords = remove_double_quotes(keywords); - - keywords = keywords.split(/[, ]+/).join('" "'); - - let content = `(package "${name}" - "${version}" - "${description}") - -(website-url "${website_url}") -(keywords "${keywords}") - -(package-file "${entry_point}") - -(script "test" "echo \\\"Error: no test specified\\\" && exit 1") - -(source "gnu") - -(depends-on "emacs" "${emacs_version}") -`; - - await ask(`About to write to ${new_name}: - -${content} - -Is this OK? (yes) `, - (answer) => { - if (answer == '' || answer == 'yes') { - fs.writeFile(new_name, content, (err) => { if (err) console.log(err); }); - } - }); - instance.close(); -} - -/** - * Ask question with callback - * @param { string } question - question to ask for user input - * @param { callback } callback - callback with user's answer - */ -function ask(question, callback) { - return new Promise((resolve, reject) => { - instance.question(question, (answer) => { callback(answer); resolve(); }); - }); -} - -/** - * Return true if NAME is a valid Eask-file filename. - * @param { string } name - either a filename or file path. - * @return { boolean } - True for valid filename. - */ -function check_eask_filename(name) { - let base = path.basename(name); - let prefix; - if (base.startsWith('Easkfile')) prefix = 'Easkfile'; - else if (base.startsWith('Eask')) prefix = 'Eask'; - else return false; - let suffix = base.replace(prefix, ''); - return suffix == '' || /^[.][0-9]/.test(suffix); -} - -/** - * Return the guessed project name by its basename. - * @param { String } basename - The working directory name. - * @return Guessed project name. - */ -function guess_package_name(basename) { - basename = basename.toLowerCase(); - basename = basename.replaceAll('emacs-', ''); - basename = basename.replaceAll('-emacs', ''); - basename = basename.replace(/[-.]el$/, ''); - return basename; -} - -/** - * Return the guessed entry point by its basname. - * @param { String } basename - The working directory name. - * @return Guessed entry point filename. - */ -function guess_entry_point(basename) { - basename = guess_package_name(basename); - return basename + ".el"; -} - -/** - * Remove all double quotes from string. - * @param { String } str - String to remove all double quotes. - * @return String has been removed all double quotes. - */ -function remove_double_quotes(str) { - return str.replaceAll('\"', ''); -} - -/* - * Module Exports - */ -module.exports.create_eask_file = create_eask_file; diff --git a/cmds/create/elpa.js b/cmds/create/elpa.js index 43b7ed1f..062747cc 100644 --- a/cmds/create/elpa.js +++ b/cmds/create/elpa.js @@ -19,8 +19,6 @@ const child_process = require('child_process'); -const init = require('../core/init'); - exports.command = ['elpa ']; exports.desc = 'Create a new ELPA using github-elpa'; exports.builder = yargs => yargs @@ -61,6 +59,6 @@ exports.handler = async (argv) => { /* Operations after _cloned */ async function _cloned(argv) { console.warn('Initialize the Eask-file for your project...'); - await init.create_eask_file(); - UTIL.e_call(argv, 'create/elpa'); + await UTIL.e_call(argv, 'core/init'); + await UTIL.e_call(argv, 'create/elpa'); } diff --git a/cmds/create/package.js b/cmds/create/package.js index 324c5616..e5e13e5b 100644 --- a/cmds/create/package.js +++ b/cmds/create/package.js @@ -19,8 +19,6 @@ const child_process = require('child_process'); -const init = require('../core/init'); - exports.command = ['package ', 'pkg ']; exports.desc = 'Create a new package'; exports.builder = yargs => yargs @@ -61,6 +59,6 @@ exports.handler = async (argv) => { /* Operations after _cloned */ async function _cloned(argv) { console.warn('Initialize the Eask-file for your project...'); - await init.create_eask_file(); - UTIL.e_call(argv, 'create/package'); + await UTIL.e_call(argv, 'core/init'); + await UTIL.e_call(argv, 'create/package'); } diff --git a/docs/content/Development-API/_index.en.md b/docs/content/Development-API/_index.en.md index 93f8a1c0..637943f4 100644 --- a/docs/content/Development-API/_index.en.md +++ b/docs/content/Development-API/_index.en.md @@ -107,6 +107,14 @@ Points to `lisp` directory from the project root. (message "%s" eask-lisp-root) ; path/to/eask/cli/lisp/ ``` +## 🔍 Function: eask-working-directory () + +Return the working directory of the program going to be executed. + +```elisp +(message "%s" (eask-working-directory)) ; path/to/current/work/space/ +``` + ## 🔍 Function: eask-command () Return the current command in string. Suppose the command is: @@ -166,7 +174,7 @@ Call another eask script. ## 🔍 Function: eask-import (`url`) -Load and evaluate the script from a url. +Load and evaluate the script from the url. ```elisp (eask-import "https://raw.githubusercontent.com/emacsmirror/emacswiki.org/master/yes-no.el") @@ -176,7 +184,7 @@ Load and evaluate the script from a url. ## 🔍 Macro: eask-defvc< (`version` &rest `body`) -Define scope if Emacs version is below specific version. +Define the scope if the Emacs version is below a specific version. `VERSION` is an integer and will be compared with `emacs-major-version`. @@ -292,7 +300,7 @@ The following output is with Emacs 28.1: Return a list of Eask files from DIR. -Consider following directory tree: +Consider the following directory tree: ``` . root @@ -311,7 +319,7 @@ The following output is with Emacs 28.1: Find the Eask-file from START-PATH. -Consider following directory tree: +Consider the following directory tree: ``` .project diff --git a/docs/content/Development-API/_index.zh-tw.md b/docs/content/Development-API/_index.zh-tw.md index 634f1c1a..2c5dea21 100644 --- a/docs/content/Development-API/_index.zh-tw.md +++ b/docs/content/Development-API/_index.zh-tw.md @@ -106,6 +106,14 @@ $ eask -- args0 args1 (message "%s" eask-lisp-root) ; path/to/eask/cli/lisp/ ``` +## 🔍 函式: eask-working-directory () + +傳回將要執行的程式的工作目錄。 + +```elisp +(message "%s" (eask-working-directory)) ; path/to/current/work/space/ +``` + ## 🔍 函式: eask-command () 返回字符串中的當前命令。假設命令是: diff --git a/lisp/_prepare.el b/lisp/_prepare.el index ec15a757..a45229bf 100644 --- a/lisp/_prepare.el +++ b/lisp/_prepare.el @@ -157,8 +157,8 @@ will return `lint/checkdoc' with a dash between two subcommands." These commands will first respect the current workspace. If the current workspace has no valid Eask-file; it will load global workspace instead." - (member (eask-command) '("init/cask" "init/eldev" "init/keg" - "init/source" + (member (eask-command) '("init" "init/source" "init/cask" "init/eldev" "init/keg" + "create/package" "create/elpa" "bump" "cat" "keywords" "repl" "generate/ignore" "generate/license" "test/melpazoid"))) @@ -1023,6 +1023,12 @@ Argument BODY are forms for execution." (defalias keyword (symbol-function old)))) result)) +(defun eask-working-directory () + "Return the working directory of the program going to be executed." + (cond ((eask-config-p) user-emacs-directory) + ((eask-global-p) (expand-file-name "../../" user-emacs-directory)) + (t default-directory))) + (defvar eask-file nil "The Eask file's filename.") (defvar eask-file-root nil "The Eask file's directory.") diff --git a/lisp/core/init.el b/lisp/core/init.el new file mode 100644 index 00000000..11b81f1a --- /dev/null +++ b/lisp/core/init.el @@ -0,0 +1,90 @@ +;;; core/init.el --- Initialize project to use Eask -*- lexical-binding: t; -*- + +;;; Commentary: +;; +;; Initialize project to use Eask +;; +;; $ eask init +;; + +;;; Code: + +(let ((dir (file-name-directory (nth 1 (member "-scriptload" command-line-args))))) + (load (expand-file-name "_prepare.el" + (locate-dominating-file dir "_prepare.el")) + nil t)) + +(defun eask-init--check-filename (name) + "Return non-nil if NAME is a valid Eask-file." + (when-let* ((name (file-name-nondirectory (directory-file-name name))) + (prefix (cond ((string-prefix-p "Easkfile" name) "Easkfile") + ((string-prefix-p "Eask" name) "Eask")))) + (let ((suffix (car (split-string name prefix t)))) + (or (null suffix) + (string-match-p "^[.][.0-9]*$" suffix))))) + +(eask-start + (let* ((dir (eask-working-directory)) + (files (eask--all-files dir)) + (new-name (expand-file-name "Eask" dir)) + (base-name) + (invalid-name) + (continue t)) + (when (and files + (setq continue + (yes-or-no-p (concat "Eask-file already exist,\n\n " + (mapconcat #'identity files "\n ") + "\n\nContinue the creation? ")))) + (while (or (file-exists-p new-name) invalid-name) + (setq new-name (read-file-name + (format + (concat (if invalid-name + "[?] Invalid filename `%s', " + "[?] Filename `%s' already taken, ") + "try another one: ") + (file-name-nondirectory (directory-file-name new-name))) + dir nil nil nil + #'eask-init--check-filename) + base-name (file-name-nondirectory (directory-file-name new-name)) + invalid-name (not (eask-init--check-filename base-name))))) + (when continue + ;; Starting Eask-file creation! + (let* ((project-dir (file-name-nondirectory (directory-file-name dir))) + (project-name (eask-guess-package-name project-dir)) + (package-name (read-string (format "package name: (%s) " project-name) nil nil project-name)) + (version (read-string "version: (1.0.0) " nil nil "1.0.0")) + (description (read-string "description: ")) + (guess-entry-point (format "%s.el" project-name)) + (entry-point (read-string (format "entry point: (%s) " guess-entry-point) + nil nil guess-entry-point)) + (emacs-version (read-string "emacs version: (26.1) " nil nil "26.1")) + (website (read-string "website: ")) + (keywords (read-string "keywords: ")) + (keywords (if (string-match-p "," keywords) + (split-string keywords ",[ \t\n]*" t "[ ]+") + (split-string keywords "[ \t\n]+" t "[ ]+"))) + (keywords (mapconcat (lambda (s) (format "%S" s)) keywords " ")) + (content (format + "(package \"%s\" + \"%s\" + \"%s\") + +(website-url \"%s\") +(keywords %s) + +(package-file \"%s\") + +(script \"test\" \"echo \\\"Error: no test specified\\\" && exit 1\") + +(source \"gnu\") + +(depends-on \"emacs\" \"%s\") +" + package-name version description website keywords + entry-point emacs-version)) + (prompt (format "About to write to %s:\n\nIs this Okay? " new-name))) + (when (yes-or-no-p prompt) + (write-region content nil new-name) + (find-file new-name)))))) + +;;; core/init.el ends here