Suppose, I have a string with directory name: "/sites/all/modules". And I want to remove the last component of it, producing: "/sites/all". I tried, creating a function, but it returns: "/sites/all/modules".
(defun remove-last-dir (dir)
(replace-regexp-in-string "(.*)/.+" "\1" dir))
(print (remove-last-dir "/sites/all/modules"))
ELISP> (directory-file-name (file-name-directory "/sites/all/modules"))
"/sites/all"
file-name-directory gets the directory component of the given path. Emacs considers paths ending in / to be directories, so the return value will have a trailing /
directory-file-name returns a non-directory version of the path (which means it strips a trailing / from the value, if there is one).
Note that if your original argument was (or could be) /sites/all/modules/ then you would want to call directory-file-name on that first (as otherwise file-name-directory would just return the original value, because the "directory component" of a directory path is itself).
I wrote you some code in ELisp that should do the trick:
(defun remove-last-dir (dir)
(let* ((splits (cdr (split-string dir "/")))
(res (mapconcat 'identity (butlast splits) "/")))
(concat "/" res)))
Hope this helps!
Wrote the efficient solution:
(defun remove-last-dir (dir)
(string-match "\\(.*\\)/" dir)
(match-string 1 dir))
(print (remove-last-dir "/sites/all/modules"))
Prints:
"/sites/all"
While #phils's answer is correct and idiomatic for Emacs without third-party libraries, if you work with files and filepaths in your Elisp frequently, i recommend installing f.el library, which introduces a vast amount of file and directory related functions. f-dirname is what you need:
(f-dirname "path/to/file.ext") ;; => "path/to"
(f-dirname "path/to/directory") ;; => "path/to"
(f-dirname "/") ;; => nil
Check out other functions as well, if you need more control over paths.
Related
My question is almost answered at the below link. However, I have a slight addition to the below question (Please check the "Similar Question").
Similar Question
My question is that:
If an entire folder was deleted, only this folder should be listed in the output (the content of this folder should not be listed additionally). As an example:
before/Fotos/Nope-2018/globe
before/Fotos/Nope-2018/globe/dc-40.jpg
before/Fotos/Nope-2018/globe/dc-41.jpg
before/Fotos/Nope-2018/globe/dc-42.jpg
before/Fotos/Nope-2018/globe/dc-43.jpg
If all the files starting with /globe (including /globe) is deleted, the output should not contain all the items but only "globe". How to achieve this as an addition to achieving the previously asked question in the given the link?
Any help is appreciated.
I'm making a few assumptions:
The output of your "Similar Question" includes the folder (aka directory) name if and only if all its files/sub-folders have been deleted.
The file paths are delimited by a slash.
You can take the output of the "Similar Question" and post-process it such that no element of the resultant collection is a prefix substring of any other element.
The following shows one way of accomplishing that.
user> (def deleted-files-raw
["Fotos/Nope-2018/globe"
"Fotos/Nope-2018/glob"
"Fotos/Nope-2018/globe/asia"
"Fotos/Nope-2018/globe/asia/dc-40.jpg"
"Fotos/Nope-2018/globe/dc-40.jpg"
"Fotos/Nope-2018/world/dc-40.jpg"
"Fotos/Nope-2018/globe/dc-41.jpg"])
#'user/deleted-files-raw
user> (defn remove-files-deleted-dirs
"`coll` is a collection of /-separated pathnames.
Returns a collection of pathnames such that none of the pathnames
are prefix substrings of any other pathname,
assuming a / as the separator."
[coll]
(reduce (fn [acc x]
(if (clojure.string/starts-with? x (str (last acc) "/"))
acc
(conj acc x)))
[]
(sort coll)))
#'user/remove-files-deleted-dirs
user> (remove-files-deleted-dirs deleted-files-raw)
["Fotos/Nope-2018/glob"
"Fotos/Nope-2018/globe"
"Fotos/Nope-2018/world/dc-40.jpg"]
user>
I am trying to write an elisp function to read each word in a file into a pair. I want the first item of the pair to be the string sorted lexicographically, and the second item to be untouched.
Given the example file:
cat
cow
dog
I want the list to look like:
(act cat)
(cow cow)
(dgo dog)
My best crack at it is:
(defun get-file (filename)
(with-open-file (stream filename)
(loop for word = (read-line stream nil)
while word
collect ((sort word #'char-lessp) word))))
It compiles correctly in Emacs lisp interaction mode. However, when I try to
run it by executing
(get-file "~/test.txt")
I end up in the Emacs debugger, and it's not telling me anything useful . . .
Debugger entered--Lisp error: (void-function get-file)
(get-file "~/test.txt")
eval((get-file "~/test.txt") nil)
eval-last-sexp-1(t)
eval-last-sexp(t)
eval-print-last-sexp(nil)
call-interactively(eval-print-last-sexp nil nil)
command-execute(eval-print-last-sexp)
I am a lisp beginner, and have no idea what is wrong.
Thanks,
Justin
Vanilla Emacs
First, let's use Emacs's built-in functions only. There's no built-in function to sort strings in Emacs, so you first should convert a string to a list, sort, then convert the sorted list back to a string. This is how you convert a string to a list:
(append "cat" nil) ; => (99 97 116)
A string converted to a list becomes a list of characters, and characters are represented as numbers in Elisp. Then you sort the list and convert it to a string:
(concat (sort (append "cat" nil) '<)) ; => "act"
There's no built-in function to load file contents directly into a variable, but you can load them into a temporary buffer. Then you can return the entire temporary buffer as a string:
(with-temp-buffer
(insert-file-contents-literally "file.txt")
(buffer-substring-no-properties (point-min) (point-max))
This will return the string "cat\ncow\ndog\n", so you'll need to split it:
(split-string "cat\ncow\ndog\n") ; => ("cat" "cow" "dog")
Now you need to traverse this list and convert each item into a pair of sorted item and original item:
(mapcar (lambda (animal)
(list (concat (sort (append animal nil) '<)) animal))
'("cat" "cow" "dog"))
;; returns
;; (("act" "cat")
;; ("cow" "cow")
;; ("dgo" "dog"))
Full code:
(mapcar
(lambda (animal)
(list (concat (sort (append animal nil) '<)) animal))
(split-string
(with-temp-buffer
(insert-file-contents-literally "file.txt")
(buffer-substring-no-properties (point-min) (point-max)))))
Common Lisp Emulation
One of the Emacs built-in packages is cl.el, and there's no reason not to use it in your code. Therefore I lied, when I said there is no built-in functions to sort strings and the above is the only way to do the task using built-in functions. So let's use cl.el.
cl-sort a string (or any sequence):
(cl-sort "cat" '<) ; => "act"
cl-mapcar is more versatile than Emacs's built-in mapcar, but here you can use either of them.
There is a problem with cl-sort, it is destructive, meaning it modifies the argument in-place. We use local variable animal inside the anonymous function twice, and we don't want to garble the original animal. Therefore we should pass a copy of a sequence into it:
(lambda (animal)
(list (cl-sort (copy-sequence animal) '<) animal))
The resulting code becomes:
(cl-mapcar
(lambda (animal)
(list (cl-sort (copy-sequence animal) '<) animal))
(split-string
(with-temp-buffer
(insert-file-contents-literally "file.txt")
(buffer-substring-no-properties (point-min) (point-max)))))
seq.el
In Emacs 25 a new sequence manipulation library was added, seq.el. Alternative to mapcar is seq-map, alternative to CL's cl-sort is seq-sort. The full code becomes:
(seq-map
(lambda (animal)
(list (seq-sort animal '<) animal))
(split-string
(with-temp-buffer
(insert-file-contents-literally "file.txt")
(buffer-substring-no-properties (point-min) (point-max)))))
dash, s, f
Usually the best solution to work with sequences and files is to reach directly for these 3 third-party libraries:
dash for list manipulation
s for string manipulation
f for file manipulation.
Their Github pages explain how to install them (installation is very simple). However for this particular problem they are a bit suboptimal. For example, -sort from dash only sorts lists, so we would have to get back to our string->list->string conversion:
(concat (-sort '< (append "cat" nil))) ; => "act"
s-lines from s leaves empty strings in files. On GNU/Linux text files usually end with newline at the end, so splitting your file would look like:
(s-lines "cat\ncow\ndog\n") ; => ("cat" "cow" "dog" "")
s-split supports an optional argument to omit empty lines, but it's separator argument is a regex (note that you need both \n and \r for portability):
(s-split "[\n\r]" "cat\ncow\ndog\n" t) ; => ("cat" "cow" "dog")
Yet there are 2 functions which can simplify our code. -map is similar to mapcar:
(-map
(lambda (animal)
(list (cl-sort (copy-sequence animal) '<) animal))
'("cat" "cow" "dog"))
;; return
;; (("act" "cat")
;; ("cow" "cow")
;; ("dgo" "dog"))
However in dash there are anaphoric versions of functions that accept a function as an argument, such as -map. Anaphoric versions allow to use shorter syntax by exposing local variable as it and start with 2 dashes. E.g. the below are equivalent:
(-map (lambda (x) (+ x 1)) (1 2 3)) ; => (2 3 4)
(--map (+ it 1) (1 2 3)) ; => (2 3 4)
Another improvement is f-read-text from f, which simply returns contents of a file as a string:
(f-read-text "file.txt") ; => "cat\ncow\ndog\n"
Combine best of all worlds
(--map (list (cl-sort (copy-sequence it) '<) it)
(split-string (f-read-text "file.txt")))
On my emacs, either C-j or C-x C-e evaluates the form as you said. When I try to do the same with (get-file "test") the debugger complains about with-open-file being undefined. I cannot find with-open-file in cl-lib (or cl) emacs packages.
Did you require some other package? Also, I think the idiomatic way of opening file in Emacs is to temporary visit them in buffers.
Anyway, if the code was Common Lisp it would be ok except for collect ((sort ...) word), where you are not building a list but using (sort ...) in a function position. I'd use (list (sort ...) word) instead.
In emacs, I want to be able to search only the 'headers' in an org mode file.
Idea 1: Search only Visible
I could achieve this by hiding everything, then showing only the outline (S-TAB, S-TAB) and then maybe search all that is visible.(in this case it would be the whole table of content).
But how do I search only visible content? C-s searches everything.
Idea 2: use regex
I can potentially do:
C-c / / //opens regex search
\*.*heading //start with * (escaped), followed by any chars, then heading.
But at the moment it's cumbersome to type all of that. Considering I've started learning emacs like 3 hours ago, can I automate this somehow?
E.g, can I write a function to search with "*.*ARGUMENT" and tie it a hotkey? but still have the ability to go like 'next find, next find' etc..?
The use case for this is searching my notes. Some are like ~7000+ lines long and I commonly only search the headers.
[EDIT Solution 1]
#abo-abo's answer worked well for me. I now use helm-org-in-buffer-headings
I.e, I installed Melpa:
https://github.com/milkypostman/melpa#usage
Then I installed helm from the package list:
M-x package-list-packages
Then I edited my .emacs and bound a hotkey to it:
(global-set-key (kbd "C-=") 'helm-org-in-buffer-headings) ;Outline search.
I reloaded emacs and now when pressing Ctrl+= a searchable outline pops up that automatically narrows down as I type in additional characters. The usual C-n, C-p , buttons work for navigation.
Thanks!
[Edit Solution 2]
Curiosity got the best of me. After enjoying helm's heading search, I messed around with worf also. It is like helm (it uses helm) but looks nicer and I can select a 'level' of outline by pressing the number key. I hacked out just the bits necessary for heading search, if of use:
;; ——— WORF Utilities ———————————————————————————————————————————————————————————————
;; https://github.com/abo-abo/worf/blob/master/worf.el
(defun worf--pretty-heading (str lvl)
"Prettify heading STR or level LVL."
(setq str (or str ""))
(setq str (propertize str 'face (nth (1- lvl) org-level-faces)))
(let (desc)
(while (and (string-match org-bracket-link-regexp str)
(stringp (setq desc (match-string 3 str))))
(setq str (replace-match
(propertize desc 'face 'org-link)
nil nil str)))
str))
(defun worf--pattern-transformer (x)
"Transform X to make 1-9 select the heading level in `worf-goto'."
(if (string-match "^[1-9]" x)
(setq x (format "^%s" x))
x))
(defun worf-goto ()
"Jump to a heading with `helm'."
(interactive)
(require 'helm-match-plugin)
(let ((candidates
(org-map-entries
(lambda ()
(let ((comp (org-heading-components))
(h (org-get-heading)))
(cons (format "%d%s%s" (car comp)
(make-string (1+ (* 2 (1- (car comp)))) ?\ )
(if (get-text-property 0 'fontified h)
h
(worf--pretty-heading (nth 4 comp) (car comp))))
(point))))))
helm-update-blacklist-regexps
helm-candidate-number-limit)
(helm :sources
`((name . "Headings")
(candidates . ,candidates)
(action . (lambda (x) (goto-char x)
(call-interactively 'show-branches)
(worf-more)))
(pattern-transformer . worf--pattern-transformer)))))
And then tied it to a hot key:
(global-set-key (kbd "<f3>") 'worf-goto)
worf-goto from worf can do this,
so can helm-org-in-buffer-headings from helm.
worf-goto actually uses helm as a back end. In addition to helm-org-in-buffer-headings, you get:
headings are colored in the same way as in the original buffer
you can select all headings with the same level using the appropriate digit
If you have ivy installed, you can use counsel-org-goto to search headings in the current buffer or counsel-org-goto-all to search the headings in all open org-mode buffers.
It's a good option if you don't want to install the other things that come with worf.
If you don't want to rely on external packages, org, in fact, already offers this capability: the function is org-goto.
If you want it to behave in a way similar to helm-org-in-buffer-headings, you have to set org-goto-interface to outline-path-completion, for instance by adding to your init file:
(setq org-goto-interface (quote outline-path-completion))
I'm working on a C++ project with a bit uncommon layout of source and include files (well, at least it's uncommon for what I've seen so far) and am trying to come up with a helper emacs function for switching between .cpp and respective .h file (for this particular case only, so it doesn't need to be super flexible), because ff-find-other-file fails on this setup. This is also an elisp learning experience for me.
The project structure is set up as follows:
source files fall within projectname/src/namespacepath/*.cpp
respective include files fall withing projectname/include/namespacepath/*.h
In addition I may have an additional checkout of that project (projectname2/....) and switching between cpp and h should happen within project boundaries.
In other words, for a source file Foo.cpp of class Foo in namespace a::b::c I have:
project/src/a/b/c/Foo.cpp
project/include/a/b/c/Foo.h
The "project" itself is kept in a "src" directory where I keep all my sources (so the full path is something like ~/src/project/src/....), which means the function should only replace "src" with "include" for the last "src" occurence in the path.
I came up with the below elisp function; it substitues the last occurence of "src" with "include" in the current file path and "cpp" extension with "h" (or vice versa) and tries to visit the resulting file.
Since I'm new to lisp, I'd be interested in knowing if it can be made any simpler? Or perhaps ff-find-other-file can be customized to do exactly this? (and yeah, I've seen ff-search-directories, but that wouldn't help when working on multiple checkouts of the same project).
(defun alternate-include-or-src()
(interactive)
(let (
(name)
(newname "")
(repl t)
)
(setq name (nreverse (split-string (buffer-file-name) "/")))
(setq filename (car name))
(dolist (p (cdr name)) ;; iterate over reversed list of path components
(if repl ;; do the src <-> substitution only once
(if (string= p "src")
(progn
(setq p "include"
repl nil)
(setq filename (concat (file-name-sans-extension filename) ".h"))
)
(if (string= p "include")
(progn
(setq p "src"
repl nil)
(setq filename (concat (file-name-sans-extension filename) ".cpp"))
)
)
)
)
(setq newname (concat p "/" newname))
)
(setq newname (concat newname filename))
(if (file-exists-p newname)
(find-file newname)
)
)
)
I suggest you to have a look into cc-other-file-alist, for its use with ff-find-other-file. It allows custom function calls and you can save some coding:
Example:
(setq cc-other-file-alist
`(
("\\.cxx$" ,(my-look-for-other-file-1))
("\\.hxx$" ,(my-look-for-other-file-2))))
I am trying to find out which regexp causes the connect to be highlighted in this cperl-mode buffer. describe-face tells me this is a font-lock-type-face. After having a look at font-lock-keywords-alist and font-lock-keywords I dont't see where this highlight could have come from. The colorful parantheses are created by the rainbow-delimiters-mode.
Is there a function to check which regexp matched, such that connect is highlighted with this face?
I had a local font-lock hack to record in the buffer the rule that was used. The patch below might do it. Once you've applied it (and reloaded font-lock.el), you can check (with C-u C-x =) the font-lock-debug property on the buffer position whose fontification you want to understand.
=== modified file 'lisp/font-lock.el'
--- lisp/font-lock.el 2013-01-11 23:08:55 +0000
+++ lisp/font-lock.el 2013-01-13 15:28:16 +0000
## -1563,6 +1611,14 ##
;;; Keyword regexp fontification functions.
+(defvar font-lock-debug nil)
+
+(defun font-lock-debug ()
+ (interactive)
+ (set (make-local-variable 'font-lock-debug) t)
+ (make-local-variable 'font-lock-extra-managed-props)
+ (push 'font-lock-debug font-lock-extra-managed-props))
+
(defsubst font-lock-apply-highlight (highlight)
"Apply HIGHLIGHT following a match.
HIGHLIGHT should be of the form MATCH-HIGHLIGHT, see `font-lock-keywords'."
## -1577,13 +1633,16 ##
(when (eq (car-safe val) 'face)
(add-text-properties start end (cddr val))
(setq val (cadr val)))
- (cond
- ((not (or val (eq override t)))
+ (if (and (not val) (not (eq override t)))
;; If `val' is nil, don't do anything. It is important to do it
;; explicitly, because when adding nil via things like
;; font-lock-append-text-property, the property is actually
;; changed from <face> to (<face>) which is undesirable. --Stef
- nil)
+ nil
+ (if font-lock-debug
+ (font-lock-append-text-property start end 'font-lock-debug
+ (list (cons matcher highlight))))
+ (cond
((not override)
;; Cannot override existing fontification.
(or (text-property-not-all start end 'face nil)
## -1599,7 +1658,7 ##
(font-lock-append-text-property start end 'face val))
((eq override 'keep)
;; Keep existing fontification.
- (font-lock-fillin-text-property start end 'face val)))))))
+ (font-lock-fillin-text-property start end 'face val))))))))
(defsubst font-lock-fontify-anchored-keywords (keywords limit)
"Fontify according to KEYWORDS until LIMIT.
## -1621,6 +1680,7 ##
(min lead-start (point)))
limit
'font-lock-multiline t)))
+ (font-lock-append-text-property (point) limit 'font-lock-debug keywords)
(save-match-data
;; Find an occurrence of `matcher' before `limit'.
(while (and (< (point) limit)
I know of no such function. The regular expressions are compiled to be optimal, why do you need to know?
BTW, displaying font-lock-keyword shows the string nect just besides tinue.
Did you try font-lock studio? It works great for me.