0
(cl-defun filename-from-buffer (&key get-ext get-path)
  (let ((filename (buffer-name))
        (pathname (buffer-file-name)))
    (setq ret (car (split-string filename "\\.")))
    (when get-path
      (setq ret (car (split-string pathname "\\."))))
    (when get-ext
      (setq ret filename))
    (when (and get-ext get-path)
      (setq ret pathname))
    ret))

I am fairly new to Emacs and Lisp (eLisp) and I wanted a function that would return the name of the file which is open in the current buffer. The function should take some optional arguments determining whether we want the full path, the extension or just the sole name of the file.

I feel like my implementation is pretty naive and it depends on the order of lines as well because I could not figure out how to do an if-else chain and return within. So is there a more elegant/more secure or just a better way to do this? By better, I mean something that uses the functionality of language which is already provided, an implementation that does not depend on the order of cases and/or something that does branching in a better way.

  • There are functions for all your aims: file-name-nondirectory, file-name-sans-extension, file-name-extension. If-else-statement: (if PREDICATE IF-BRANCH ELSE-BRANCH) where PREDICATE IF-BRANCH and ELSE-BRANCH are expressions. You can also use (cond (EXPR FORM) ...). – Tobias Oct 25 at 8:02
  • buffer-name often contains the base file name, but not always. You want to use (file-name-nondirectory buffer-file-name) instead. Also the GNU convention is to only use "path" for lists of directories, like load-path, and to use "file name" otherwise, including for fully qualified absolute file names. – Stefan Oct 25 at 12:01
  • The question is too broad or primarily opinion-based, so it risks being closed. This site is about specific Q&A, not general what-is-better discussion or requests for advice. Maybe try a discussion site such as Reddit for such a question. – Drew Oct 25 at 14:57
  • It is a bad idea to setq ret without locally binding it in a let. Because it changes the global variable ret (if there is one). Reason is: Emacs uses dynamic extend for variables by default. Second: emacs provides portable functions to extract parts of the filename file-name-..., your code wouldn't work with unixes. – jue Oct 25 at 15:14
  • @jue what do you mean, "without binding it in a let". I have bound it inside the (let (...) (setq ret)) block. Do you mean I should do it like this: (let ((ret nil)) (setq ret "value"))? – scribe Oct 25 at 21:34
0

You can use cond, which corresponds to "if"-"else if"-"else" chains in other languages. It picks the first clause whose condition is non-nil.

Here we don't need a temporary variable, since we're just going to return the value from the cond clauses directly. The last clause has t, the "true" value in Lisp, as its condition, so it will be picked if none of the preceding clauses were picked.

(cl-defun filename-from-buffer (&key get-ext get-path)
  (let ((filename (buffer-name))
        (pathname (buffer-file-name)))
    (cond
     ((and get-ext get-path)
      pathname)
     (get-path
      (file-name-sans-extension pathname))
     (get-ext
      filename)
     (t
      (file-name-sans-extension filename)))))

Emacs Lisp also has if, but you only get an "if" clause and an "else" clause, no "else if":

(if some-condition
    value-if-true
  value-if-false)

Though you could achieve that by nesting several ifs in each other, it's more elegant to use cond in such a situation. Nested ifs would look like this:

(if (and get-ext get-path)
    pathname
  (if get-path
      (file-name-sans-extension pathname)
    (if get-ext
        (file-name-extension filename)
      (file-name-sans-extension filename))))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.