ฉันสามารถสร้างไดเรกทอรีที่ไม่มีอยู่ในขณะที่สร้างไฟล์ใหม่ใน emacs ได้หรือไม่?


29

ใน emacs ฉันสร้างไฟล์โดยไปที่มันด้วย Cx Cf /home/myself/new_directory/file.txtสมมติว่าผมอยากจะสร้าง

หาก new_directory ยังไม่มีอยู่จะมีวิธีสร้างขึ้นระหว่างการสร้างfile.txtโดยไม่มีขั้นตอนพิเศษหรือไม่? (ฉันกำลังคิดถึงสิ่งที่ต้องการใช้การ-pตั้งค่าสถานะเป็นmkdirใน Linux)

ฉันรู้สึกว่ามีการกดแป้นแตกต่างจาก Cx Cf ที่สามารถทำได้ แต่ฉันจำไม่ได้ว่ามันคืออะไร


emacs ไม่เทียบเท่ากับการวางคำสั่งขณะแก้ไข? ใน vi คุณทำได้:!mkdir -p ~/new_dir/to/some/crazy/path
DaveParillo

3
@DaveParillo: แน่นอนมีM-!ตัวอย่างเช่น
Nikana Reklawyks

ฉันใช้ปลั๊กอินโหมโรง ( github.com/bbatsov/prelude ) เมื่อใดก็ตามที่ฉันสร้างไฟล์เช่นด้านบนจะแจ้งให้ฉันด้วย "สร้างไดเรกทอรี ... " ฉันสามารถเลือก "y" จากนั้นจะถามฉันว่า "ไม่มีไฟล์สร้างหรือไม่ (y หรือ n)" ฉันเลือก y ซึ่งสร้างไฟล์ใหม่ เมื่อฉันบันทึกไฟล์มันจะสร้างไฟล์ที่มีข้อมูลด้านบน
Pramod

คำตอบ:


25

นอกจากนี้คุณยังสามารถแนะนำฟังก์ชั่นfind-fileเพื่อสร้างไดเรกทอรีที่จำเป็นอย่างโปร่งใส

(defadvice find-file (before make-directory-maybe (filename &optional wildcards) activate)
  "Create parent directory if not exists while visiting file."
  (unless (file-exists-p filename)
    (let ((dir (file-name-directory filename)))
      (unless (file-exists-p dir)
        (make-directory dir)))))

เพียงแค่ใส่ในที่ของคุณ.emacsแล้วใช้C-x C-fตามปกติ


2
ทางออกที่ยอดเยี่ยม ฉันชอบที่การพัฒนาสิ่งเล็ก ๆ สามารถเปิดโลกใหม่ของคำใบ้เพื่อให้ emacs ทำสิ่งที่ดีกว่า (ใช่ฉันไม่เคยรู้defadviceมาก่อน)
Nikana Reklawyks

18

เมื่อฉันระบุชื่อพา ธ ที่มีส่วนประกอบที่ไม่มีอยู่find-file(เช่นCx Cf ) ให้ข้อความพิเศษที่บอกฉัน

ใช้ Mx make-directory RET RET เพื่อสร้างไดเรกทอรีและพาเรนต์

เนื่องจากไฟล์ไม่ได้ถูกสร้างขึ้นจนกว่าคุณจะบันทึกบัฟเฟอร์เป็นครั้งแรกคุณสามารถเรียกใช้ได้make-directoryทันทีหลังจากบัฟเฟอร์ใหม่ของคุณมาถึงหรือคุณสามารถทำเวลาอื่นก่อนที่คุณจะต้องบันทึกไฟล์ จากบัฟเฟอร์ที่ต้องการไดเรกทอรีให้พิมพ์Mx make-directory RET RET (จะให้ไดเรกทอรีสร้าง (ค่าเริ่มต้นมาจากชื่อพา ธ ของบัฟเฟอร์) RET ที่สองคือการยอมรับค่าเริ่มต้น)


14

สั่นสะท้านโหมดให้ido-find-fileนั่นคือการเปลี่ยนfind-fileและให้คุณมีคุณสมบัติอื่น ๆ อีกมากมาย ตัวอย่างเช่นจะช่วยให้คุณสร้างไดเรกทอรีใหม่ในขณะที่คุณเปิดไฟล์

  • พิมพ์C-x C-fตามปกติ (ซึ่งถูกแมปใหม่ido-find-file)

  • ให้เส้นทางที่ไม่มีอยู่จริง

  • กดM-mซึ่งจะแจ้งให้ไดเรกทอรีใหม่สร้าง

  • จากนั้นระบุชื่อไฟล์ที่จะเยี่ยมชมในไดเรกทอรีที่สร้างขึ้นใหม่


ฉันไม่เข้าใจ: ทำไมกดM-mถึงจุดหนึ่งและC-x C-fถ้ามันไม่สร้างทุกอย่างโดยอัตโนมัติ หากได้รับการแจ้งเตือนให้สร้างM-! mkdir
ไดเร็ก

มีวิธีido-find-fileสร้างไดเรกทอรีโดยอัตโนมัติหรือไม่ หรือดียิ่งขึ้นถามฉันว่าฉันต้องการสร้างมันได้หรือไม่ ฉันลองใช้คำแนะนำในคำตอบของTörökGábor แต่ฉันไม่สามารถคิดได้ว่าฟังก์ชั่นใดที่จะใช้กับมัน (เนื่องจาก ido ไม่ได้โทรfind-fileโดยตรง
ทรอยแดเนียล

1

โซลูชันของฉันมาพร้อมกับโบนัส: หากคุณฆ่าบัฟเฟอร์โดยไม่บันทึก Emacs จะเสนอให้ลบไดเรกทอรีว่างเปล่าที่สร้างขึ้น (แต่ถ้าไม่มีอยู่ก่อนที่คุณจะเรียกใช้find-file):

;; Automatically create any nonexistent parent directories when
;; finding a file. If the buffer for the new file is killed without
;; being saved, then offer to delete the created directory or
;; directories.

(defun radian--advice-find-file-automatically-create-directory
    (original-function filename &rest args)
  "Automatically create and delete parent directories of files.
This is an `:override' advice for `find-file' and friends. It
automatically creates the parent directory (or directories) of
the file being visited, if necessary. It also sets a buffer-local
variable so that the user will be prompted to delete the newly
created directories if they kill the buffer without saving it."
  ;; The variable `dirs-to-delete' is a list of the directories that
  ;; will be automatically created by `make-directory'. We will want
  ;; to offer to delete these directories if the user kills the buffer
  ;; without saving it.
  (let ((dirs-to-delete ()))
    ;; If the file already exists, we don't need to worry about
    ;; creating any directories.
    (unless (file-exists-p filename)
      ;; It's easy to figure out how to invoke `make-directory',
      ;; because it will automatically create all parent directories.
      ;; We just need to ask for the directory immediately containing
      ;; the file to be created.
      (let* ((dir-to-create (file-name-directory filename))
             ;; However, to find the exact set of directories that
             ;; might need to be deleted afterward, we need to iterate
             ;; upward through the directory tree until we find a
             ;; directory that already exists, starting at the
             ;; directory containing the new file.
             (current-dir dir-to-create))
        ;; If the directory containing the new file already exists,
        ;; nothing needs to be created, and therefore nothing needs to
        ;; be destroyed, either.
        (while (not (file-exists-p current-dir))
          ;; Otherwise, we'll add that directory onto the list of
          ;; directories that are going to be created.
          (push current-dir dirs-to-delete)
          ;; Now we iterate upwards one directory. The
          ;; `directory-file-name' function removes the trailing slash
          ;; of the current directory, so that it is viewed as a file,
          ;; and then the `file-name-directory' function returns the
          ;; directory component in that path (which means the parent
          ;; directory).
          (setq current-dir (file-name-directory
                             (directory-file-name current-dir))))
        ;; Only bother trying to create a directory if one does not
        ;; already exist.
        (unless (file-exists-p dir-to-create)
          ;; Make the necessary directory and its parents.
          (make-directory dir-to-create 'parents))))
    ;; Call the original `find-file', now that the directory
    ;; containing the file to found exists. We make sure to preserve
    ;; the return value, so as not to mess up any commands relying on
    ;; it.
    (prog1 (apply original-function filename args)
      ;; If there are directories we want to offer to delete later, we
      ;; have more to do.
      (when dirs-to-delete
        ;; Since we already called `find-file', we're now in the buffer
        ;; for the new file. That means we can transfer the list of
        ;; directories to possibly delete later into a buffer-local
        ;; variable. But we pushed new entries onto the beginning of
        ;; `dirs-to-delete', so now we have to reverse it (in order to
        ;; later offer to delete directories from innermost to
        ;; outermost).
        (setq-local radian--dirs-to-delete (reverse dirs-to-delete))
        ;; Now we add a buffer-local hook to offer to delete those
        ;; directories when the buffer is killed, but only if it's
        ;; appropriate to do so (for instance, only if the directories
        ;; still exist and the file still doesn't exist).
        (add-hook 'kill-buffer-hook
                  #'radian--kill-buffer-delete-directory-if-appropriate
                  'append 'local)
        ;; The above hook removes itself when it is run, but that will
        ;; only happen when the buffer is killed (which might never
        ;; happen). Just for cleanliness, we automatically remove it
        ;; when the buffer is saved. This hook also removes itself when
        ;; run, in addition to removing the above hook.
        (add-hook 'after-save-hook
                  #'radian--remove-kill-buffer-delete-directory-hook
                  'append 'local)))))

;; Add the advice that we just defined.
(advice-add #'find-file :around
            #'radian--advice-find-file-automatically-create-directory)

;; Also enable it for `find-alternate-file' (C-x C-v).
(advice-add #'find-alternate-file :around
            #'radian--advice-find-file-automatically-create-directory)

;; Also enable it for `write-file' (C-x C-w).
(advice-add #'write-file :around
            #'radian--advice-find-file-automatically-create-directory)

(defun radian--kill-buffer-delete-directory-if-appropriate ()
  "Delete parent directories if appropriate.
This is a function for `kill-buffer-hook'. If
`radian--advice-find-file-automatically-create-directory' created
the directory containing the file for the current buffer
automatically, then offer to delete it. Otherwise, do nothing.
Also clean up related hooks."
  (when (and
         ;; Stop if there aren't any directories to delete (shouldn't
         ;; happen).
         radian--dirs-to-delete
         ;; Stop if `radian--dirs-to-delete' somehow got set to
         ;; something other than a list (shouldn't happen).
         (listp radian--dirs-to-delete)
         ;; Stop if the current buffer doesn't represent a
         ;; file (shouldn't happen).
         buffer-file-name
         ;; Stop if the buffer has been saved, so that the file
         ;; actually exists now. This might happen if the buffer were
         ;; saved without `after-save-hook' running, or if the
         ;; `find-file'-like function called was `write-file'.
         (not (file-exists-p buffer-file-name)))
    (cl-dolist (dir-to-delete radian--dirs-to-delete)
      ;; Ignore any directories that no longer exist or are malformed.
      ;; We don't return immediately if there's a nonexistent
      ;; directory, because it might still be useful to offer to
      ;; delete other (parent) directories that should be deleted. But
      ;; this is an edge case.
      (when (and (stringp dir-to-delete)
                 (file-exists-p dir-to-delete))
        ;; Only delete a directory if the user is OK with it.
        (if (y-or-n-p (format "Also delete directory `%s'? "
                              ;; The `directory-file-name' function
                              ;; removes the trailing slash.
                              (directory-file-name dir-to-delete)))
            (delete-directory dir-to-delete)
          ;; If the user doesn't want to delete a directory, then they
          ;; obviously don't want to delete any of its parent
          ;; directories, either.
          (cl-return)))))
  ;; It shouldn't be necessary to remove this hook, since the buffer
  ;; is getting killed anyway, but just in case...
  (radian--remove-kill-buffer-delete-directory-hook))

(defun radian--remove-kill-buffer-delete-directory-hook ()
  "Clean up directory-deletion hooks, if necessary.
This is a function for `after-save-hook'. Remove
`radian--kill-buffer-delete-directory-if-appropriate' from
`kill-buffer-hook', and also remove this function from
`after-save-hook'."
  (remove-hook 'kill-buffer-hook
               #'radian--kill-buffer-delete-directory-if-appropriate
               'local)
  (remove-hook 'after-save-hook
               #'radian--remove-kill-buffer-delete-directory-hook
               'local))

รุ่นมาตรฐานที่นี่


0

นอกเหนือจากการแนะนำของ @Chris สำหรับ Mx make-directory แล้วคุณสามารถเขียนฟังก์ชั่นสั้น ๆ ที่จะทำการค้นหาไฟล์จากนั้นทำการสร้างไดเรกทอรี ... คุณสามารถลองทำสิ่งนี้:

(defun bp-find-file(filename &optional wildcards)
  "finds a file, and then creates the folder if it doesn't exist"

  (interactive (find-file-read-args "Find file: " nil))
  (let ((value (find-file-noselect filename nil nil wildcards)))
    (if (listp value)
    (mapcar 'switch-to-buffer (nreverse value))
      (switch-to-buffer value)))
  (when (not (file-exists-p default-directory))
       (message (format "Creating  %s" default-directory))
       (make-directory default-directory t))
  )

มันไม่ได้สวยและมันก็กระพริบ "itx make-directory .... " ก่อนที่จะพูดว่า "กำลังสร้างไดเรกทอรี ... " แต่ถ้าไม่มีอย่างอื่นก็ควรเริ่มต้นให้คุณ


2
ในกรณีของวิธีการนี้จะเป็นการดีกว่าที่จะแนะนำfind-fileฟังก์ชั่นดั้งเดิมแทนการกำหนดใหม่เพื่อให้ฟังก์ชั่นอื่น ๆ ที่เรียกfind-fileโดยตรงสามารถได้ประโยชน์จากการปรับเปลี่ยนพฤติกรรม
TörökGábor

แต่ find-file ดูเหมือนจะไม่มีข้อโต้แย้งใด ๆ ที่สามารถบอกให้ทำเช่นนี้ ... เว้นแต่จะมีสิ่งที่มีประโยชน์ที่คุณสามารถทำได้ใน find-file-hooks ...
Brian Postow

ฉันหมายถึงสิ่งนี้: superuser.com/questions/131538/…
TörökGábor

0
(make-directory "~/bunch/of/dirs" t)

หากไดเรกทอรีของคุณมีอยู่แล้วมันจะเพิ่มคำเตือน

จากคู่มือ ( C-h f make-directory RET):

make-directory เป็นฟังก์ชั่น Lisp ที่คอมไพล์ด้วยการโต้ตอบ

(make-directory DIR &optional PARENTS)

สร้างไดเรกทอรีDIRและเลือก dirs พาเรนต์ที่ไม่มีอยู่ หากDIRมีอยู่แล้วในไดเรกทอรีให้ส่งสัญญาณข้อผิดพลาดเว้นแต่ PARENTSจะไม่ใช่ศูนย์

ทางเลือกเริ่มต้นของไดเรกทอรีที่จะสร้างคือไดเรกทอรีเริ่มต้นของบัฟเฟอร์ปัจจุบัน สิ่งนี้มีประโยชน์เมื่อคุณเยี่ยมชมไฟล์ในไดเรกทอรีที่ไม่มีอยู่

แบบไม่โต้ตอบอาร์กิวเมนต์ที่สอง (เป็นทางเลือก) PARENTSถ้าไม่ใช่ไม่มีจะบอกว่าจะสร้างไดเร็กทอรีพาเรนต์ที่ไม่มีอยู่ สิ่งนี้เกิดขึ้นตามค่าเริ่มต้น

หากการสร้างไดเรกทอรีหรือไดเรกทอรีล้มเหลวจะเกิดข้อผิดพลาดขึ้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.