2013-08-21 3 views
1

Я мои файлы проект, установленный в этом формате:Найти относительный путь к файлу в проекте

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

У меня есть способ, чтобы найти путь к проекту при просмотре любого исходного файла

"/home/user/proj" 

Кроме того, (buffer-file-name) дает полный абсолютный путь к данному исходному файлу.

Как написать функцию lisp, которая извлекает относительный путь исходного файла?

Значение, если я просмотр

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

Я хотел бы иметь на пути

"source/src1/file.c" 

Следующая функция дает мне путь проекта:

(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))) 

Я всегда «Makefile» в основной папке проекта, поэтому

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

Заботится об этом.

ответ

5

Попробуйте locate-dominating-file и file-relative-name.

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

N.B. locate-dominiating-file возвращает nil, если он ничего не может найти.

+0

Да. Это оно. Brilliant. – SFbay007

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 "[\\\\/]")) 
     "."))))) 
+0

Спасибо abo-abo. Не могли бы вы дать мне пример того, что для вывода вашей функции задано имя файла? Я не уверен, что понимаю, как это работает. – SFbay007

+0

CLASSPATH имеет список корневых каталогов, разделенных двоеточиями. Этот файл находится на CLASSPATH в одном или нескольких подкаталогах корневого каталога . Я ищу ближайший, т. Е. Тот, для которого относительный путь является самым коротким. Затем путь конкатенируется с точками , чтобы сформировать путь пакета Java, но вы можете удалить эту часть. Таким образом, функция возвращает 'typeinfo.factory' для файла' ~/foo/bar/typeinfo/factory/Factory.java' , если '~/foo/bar /' находится на CLASSPATH. –

1

Как об этом:

(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)) 

Она не была протестирована очень тщательно, однако, в моих простых testcases он работал, как ожидалось. Учитывая файл filename и справочный каталог reference (должен иметь завершающий слэш, я не могу вспомнить, какая функция делает это автоматически, может кто-нибудь прокомментировать?) Эта функция будет вычислять относительный путь от reference до filename.

Пример:

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

Результат:

"../../bin/exec" 
+0

Спасибо elemakil. Я полагаю, что вы имели в виду (file-name-make-relative)/usr/local/root/bin/exec ""/usr/local/root/bin/") Потому что это то, что я ищу в основном. Что-то вроде: (file-name-make-relative (buffer-file-name) "/ home/proj") И вывод должен быть: "source/src1/file.c" Ваш код "при добавлении/root/"дает ошибку при запуске – SFbay007

1

Установить f.el, полную библиотеку обработки файлов и каталогов. Затем запустить функцию f-relative:

(f-relative "some/path/is/long" "some/path/was/short") ; => "../../is/long" 
Смежные вопросы