2

I have my project files set in this format:

/home/user/proj/source
/home/user/proj/source/src1
/home/user/proj/source/src1
/home/user/proj/header ...etc

I have a way to find the project path when viewing any source file

"/home/user/proj"

Also, (buffer-file-name) gives the full absolute path of a given source file.

How to write a lisp function that extract the relative path of a source file?

Meaning, if I am viewing

/home/user/proj/source/src1/file.c

I would like to have the path

"source/src1/file.c"

The following function gives me the project path:

(defun upward-find-file (filename &optional startdir)
  (let ((dirname (expand-file-name
          (if startdir startdir ".")))
    (found nil) ; found is set as a flag to leave loop if we find it
    (top nil))  ; top is set when we get
            ; to / so that we only check it once
    ; While we've neither been at the top last time nor have we found
    ; the file.
    (while (not (or found top))
      ; If we're at / set top flag.
      (if (string= (expand-file-name dirname) "/")
      (setq top t))
      ; Check for the file
      (if (file-exists-p (expand-file-name filename dirname))
      (setq found t)
    ; If not, move up a directory
    (setq dirname (expand-file-name ".." dirname))))
    ; return statement
    (if found (concat dirname "/") nil)))

I always have "Makefile" in the main project folder, so

(setq dirname (upward-find-file "Makefile" startdir))

Takes care of that.

4

4 回答 4

5

尝试locate-dominating-filefile-relative-name

(let ((fname (buffer-file-name)))
  (file-relative-name fname (locate-dominating-file fname "Makefile")))

如果找不到任何东西,NB会locate-dominiating-file返回。nil

于 2013-08-21T22:55:02.810 回答
2

这个怎么样:

(defun file-name-make-relative (filename reference)
  (interactive)
  (let ((reduced-path-reference)
        (common-pos 0)
        (depth 0)
        (pos 0)
        (retval ""))
    (while (eq (aref filename common-pos) (aref reference common-pos))
      (setq common-pos (+ common-pos 1)))
    (setq reduced-path-reference (substring reference (+ common-pos 1)))
    (while (< pos (length (substring reference (+ common-pos 1))))
      (if (eq (aref reduced-path-reference pos) (aref "/" 0))
          (setq depth (+ depth 1)))
      (setq pos (+ pos 1)))
    (dotimes (i depth)
      (setq retval (concat retval "../")))
    (setq retval (concat retval (substring filename common-pos)))
    retval))

它还没有经过非常彻底的测试,但是,在我的简单测试用例中,它按预期工作。给定一个文件filename和一个参考目录reference(必须有斜杠,我不记得哪个函数会自动执行此操作,有人可以评论吗?)此函数将计算从referenceto的相对路径filename

例子:

(file-name-make-relative "/usr/local/bin/exec" "/usr/local/root/bin/")

结果:

"../../bin/exec"
于 2013-08-21T22:12:34.300 回答
1

Install f.el,一个全面的文件和目录操作库。然后运行函数f-relative

(f-relative "some/path/is/long" "some/path/was/short") ; => "../../is/long"
于 2014-03-18T10:29:36.680 回答
0

您可以根据需要调整此代码:

(defun java-package-name (file)
  "Generates package name for FILE, based on path."
  (let* ((f (file-name-directory file))
         (rem
          (car
           (sort
            (delq nil
                  (mapcar
                   (lambda(x)
                     (and (string-match (expand-file-name x) f) 
                          (substring f (match-end 0))))
                   (parse-colon-path (getenv "CLASSPATH"))))
            (lambda (a b) (< (length a) (length b)))))))
    (cond
     ((null rem)
      "Not on CLASSPATH.")
     ((= 0 (length rem))
      "At root of CLASSPATH")
     (t
      (mapconcat
       #'downcase
       (delete "" (split-string rem "[\\\\/]"))
       ".")))))
于 2013-08-21T20:23:27.203 回答