Find other file / Teach me elisp

I want ff-find-other-file with specific functionality. I want it to be able to detect different related C++ headers for example. I also want it to ask if I want to create certain files. For example, I could have a project with a header “foo.hpp”. I would want it to ask me if I wanted to make “foo.cpp” on command if it doesn’t exist. But I would also want it to be able to make “fwd/foo.hpp” or “foo_fwd.hpp”. I don’t know if the ff-find-other-file supports this.

I was unaware of this package, the ff functions. I’ve been using a custom function for locating related files. The cool aspect of the package seems to be that it can find header files in many different directories.

I looked at the package and I could not find functions for programmatic use, but this seems to work.

(defun my/ff-find-other-file ()
  (interactive)
  (let ((ff-always-try-to-create nil ))
    (unless (ff-get-other-file)
      (when (yes-or-no-p "Create?: ")
	        (let ((ff-always-try-to-create t))
	         (ff-get-other-file))))))
2 Likes

Thanks. That is helpful. But I would like more complex patterns / the ability to have more then two related files.

I’ll post my current approach. I used to have two functions (goto-primary file and a goto-secondary-file) but I changed that to a rotate between the files approach. I haven’t refactored the code.

(setq primary-ext-table
      #s(hash-table size 20 data (
				  ;; Org
				  "\\.org$"   (".org_archive" ".bib")
				  "\\.org_archive$"   (".org")
				  "\\.bib$"   (".org")
				  ;; Angular
				  "\\.module.ts$"   (".model.ts" ".component.ts")
				  "\\.component.ts$"   (".model.ts" ".html")
				  "\\.component.html$"   (".component.scss" ".ts")
				  "\\.component.scss$"   (".component.html" ".ts")
				  ;; txt|pdf
				  "\\.txt$"   (".pdf")
				  "\\.pdf$"   (".txt")
				  ;; C/C++
				  "\\.c$\\|\\.cpp$"   (".hpp" ".h")
				  "\\.hpp$" (".c" ".cpp")
				  "\\.h$" (".c" ".cpp")
				  ;; Fractal
				  "\\.hbs$\\|\\.mustache$\\|\\.twig$\\|\\.nunj"   (".scss")
				  "\\.scss$"   (".config.json" ".config.js" ".config.yaml" ".config.yml" ".hbs" ".mustache" ".twig" ".nunj")
				  "\\.js$\\|\\.json$\\|\\.yaml$\\|\\.yml$"   (".hbs" ".mustache" ".twig" ".nunj" ))))

;;@ Find corresponding files
(defun my/get--corresponding-file-extension (file hash-table)
   "Returns the  corresponding extensions if  FILE has one  of the
extensions included in the keys of HASH-TABLE"
    (let ((extension nil))
    (maphash (lambda (key value)
		 (when (string-match key file)
		    (setq extension  value)))
	    hash-table)
    extension))

(defun my/find--corresponding-file (file extensions)
  "Find a corresponding file for FILE based on the EXTENSIONS to search for."
  (if (null extensions)
      nil
    (let* ((base-filename (file-name-sans-extension file))
	   (searched-filename
	    (concat base-filename (car extensions)))
	   ;; files with two or more dots in extension.
	   (searched-filename2  
	    (concat (replace-regexp-in-string "\\..*" "" base-filename)
		    (car extensions))))
      (cond
       ((file-exists-p searched-filename)
	searched-filename)
       ((file-exists-p searched-filename2)
	 searched-filename2)
       (t (my/find--corresponding-file file (cdr extensions)))))))

(defun my/goto-primary-file ()
  "Go to the primary file that corresponds to the current buffer"
  (interactive)
  (let* ((extensions  (my/get--corresponding-file-extension  (replace-regexp-in-string "<.*" ""  (buffer-name))
							 primary-ext-table))
	 (file (my/find--corresponding-file (buffer-name) extensions)))
    (if file
	(find-file file)
      (error "Unable to find a corresponding file"))))