| Register | FAQ | Calendar | Search | Today's Posts | Mark Forums Read |
|
#1
| |||
| |||
| Here's a short tutorial i wrote up today that is a basic collection of Emacs Lisp Idioms. The HTML version with links, syntax coloring, is at: http://xahlee.org/emacs/elisp_idioms.html The text version follows. Comments, corrections, suggestions welcome. ----------------------------- This page collects some basic emacs lisp programing patterns related to text processing. This page is grouped into 2 sections: Interactive and Batch. The Interactive section focuses on idioms of writing the type of commands you call when actively editing. For example, looking up the word under cursor in google search, replace certain words in current region, insert XML template, rename a given function in the current programing project, etc. The Batch section focuses on batch style text processing, typically the type of tasks one would do in unix shell tools or Perl. For example, find and replace on a list of given files or dir, run XML validation on a bunch of files. The idioms on this page contains only very basic ones, suitable for beginning elisp programer. You should first be familiar with basic emacs functions that get cursor position, move cursor, search text, inserting and deleting text. See Elisp Common Functions Reference. ------------------------------ Interactive Command Idioms Grabbing Text Grab the text of given beginning position and ending position. ; get the string from buffer (setq myStr (buffer-substring myStartPos myEndPos)) (setq myStr (buffer-substring-no-properties myStartPos myEndPos)) Emacs's string can have properties for the purposes of syntax coloring, active button, hypertext, etc. The “buffer-substring-no- properties” function just return a plain string without these properties. However, most functions that takes string as argument can also accept strings that has properties. Reference: Elisp Manual: Buffer-Contents. ------------------------------ Grabbing the current word, line, sentence, url, file name etc. ; grab a thing at point. The “thing” is a semantic unit. It can be a ; word, symbol, line, sentence, filename, url and others. (setq myStr (thing-at-point 'word)) ; grab the current word (setq myStr (thing-at-point 'symbol)) ; grab the current word with hyphens or underscore (setq line (thing-at-point 'line)) ; grab the current line ; grab the start and end positions of a line (or any other thing) (setq myBoundaries (bounds-of-thing-at-point 'line)) Note that, when the thing is a “symbol”, it usually means any alphanumeric sequence with dash “-” or underscore “_” characters. For example, if you are writing php reference lookup command, and the cursor is on p in “ print_r($y);”, you want to grab the whole “print_r” not just “print”. The exact meaning of symbol depends on the mode's Syntax Table. Reference: Elisp Manual: Syntax-Tables. ------------------------------ Here's a example of php reference lookup command that grabs by “symbol”. (defun php-lookup () "Look up current word in PHP ref site in a browser.\n If a region is active (a phrase), lookup that phrase." (interactive) (let (myword myurl) (setq myword (if (and transient-mark-mode mark-active) (buffer-substring-no-properties (region-beginning) (region-end)) (thing-at-point 'symbol))) (setq myurl (concat "http://us.php.net/" myword)) (browse-url myurl))) Reference: Elisp Manual: Buffer-Contents. ------------------------------ Grab Between Matching Pairs Grab the current text between delimiters such as between angle brackets “<>”, parens “()”, double quotes “""”, etc. (defun select-inside-quotes () "Select text between double straight quotes on each side of cursor." (interactive) (let (p1 p2) (skip-chars-backward "^\"") (setq p1 (point)) (skip-chars-forward "^\"") (setq p2 (point)) (goto-char p1) (push-mark p2) (setq mark-active t) ) ) ------------------------------ Acting on Region Idiom for a command that works on the current region. Let your function have 2 parameters, such as “start” and “end”, then use “(interactive "r")”, then the parameters will be filled with beginning and ending positions of the region. Example: (defun remove-hard-wrap-region (start end) "Replace newline chars in region by single spaces." (interactive "r") (let ((fill-column 90002000)) (fill-region start end))) ------------------------------ Idiom for acting on the region, if there's one, else, on the current word or thing. (defun down-case-word-or-region () "Make current word or region into lower case." (interactive) (let (pos1 pos2) (if (and transient-mark-mode mark-active) (setq pos1 (region-beginning) pos2 (region-end)) (setq pos1 (car (bounds-of-thing-at-point 'word)) pos2 (cdr (bounds-of-thing-at-point 'word)))) (downcase-region pos1 pos2) ) ) ------------------------------ Prompting and Getting Input Idiom for promping user for input as the argument to your command. Use this code “(interactive "‹code›‹promp string›")”. Example: (defun query-friends-phone (name) "..." (interactive "sEnter friend's name: ") (message "Name: %s" name) ) What the “(interactive "sEnter friend's name:")” does is that it will ask user to input something, taken as string, and becomes the value of your command's parameter. The “interactive” can be used to get other types of input. Here are some basic examples of using “interactive”. “(interactive)” makes the function available thru interactive use, where user can call it with execute-extended-command (M-x). “(interactive "s")” will prompt the user to enter a argument, taken as string, as argument to the function. “(interactive "n")” will prompt the user to enter a argument, taken as number, as argument to the function. The prompt text can follow the single-letter code string. If your function takes multiple inputs, you can promp user multiple times, using a single call “interactive”, by joining the promp code string with “\n” in between, like this: (defun query-friends-phone (name age) "..." (interactive "sEnter friend's name: \nnEnter friend's age: ") (message "Name: %s, Age: %d" name age) ) Reference: Elisp Manual: Defining-Commands. ------------------------------ Batch Style Text Processing Idioms Open a file, process it, save, close it ; open a file, process it, save, close it (defun my-process-file (fpath) "process the file at fullpath fpath ..." (let (mybuffer) (setq mybuffer (find-file fpath)) (goto-char (point-min)) ;; in case buffer already open ;; do something (save-buffer) (kill-buffer mybuffer))) For processing hundreds of files, you don't need emacs to keep undo info or fontification. This is more efficient: (defun my-process-file (fpath) "process the file at fullpath fpath ..." (let () ;; create temp buffer without undo record. first space is necessary (set-buffer (get-buffer-create " myTemp")) (insert-file-contents filePath nil nil nil t) ;; process it ... (kill-buffer " myTemp"))) ------------------------------ Find Replace strings: ; idiom for string replacement in current buffer; ; use search-forward-regexp if you need regexp (goto-char (point-min)) (while (search-forward "myStr1" nil t) (replace-match "myReplaceStr2")) (goto-char (point-min)) (while (search-forward "myStr2" nil t) (replace-match "myReplaceStr2")) ;; repeat for other strings ... Calling a shell command. ; idiom for calling a shell command (shell-command "cp /somepath/myfile.txt /somepath") ; idiom for calling a shell command and get its output (shell-command-to-string "ls") Both shell-command and shell-command-to-string will wait for the shell process to finish before continuing. To not wait, use start-process or start-process-shell-command. Reference: Elisp Manual: Asynchronous-Processes. ------------------------------ Traverse a directory recursively. In the following, my-process-file is a function that takes a file full path as input. The find-lisp-find-files will generate a list of full paths, using a regex on file name. The “mapc” will apply thefunction to elements in a list. ; idiom for traversing a directory (require 'find-lisp) (mapc 'my-process-file (find-lisp-find-files "~/web/emacs/" "\\.html $")) ------------------------------ Xah ∑ http://xahlee.org/ ☄ |
|
#2
| |||
| |||
| On 14 juin, 15:35, "xah...@gmail.com" <xah...@gmail.com> wrote: > Here's a short tutorial i wrote up today that is a basic collection of > Emacs Lisp Idioms. > > The HTML version with links, syntax coloring, is at: > http://xahlee.org/emacs/elisp_idioms.html > > The text version follows. Comments, corrections, suggestions welcome. > > ----------------------------- > This page collects some basic emacs lisp programing patterns related > to text processing. > > This page is grouped into 2 sections: Interactive and Batch. The > Interactive section focuses on idioms of writing the type of commands > you call when actively editing. For example, looking up the word under > cursor in google search, replace certain words in current region, > insert XML template, rename a given function in the current programing > project, etc. The Batch section focuses on batch style text > processing, typically the type of tasks one would do in unix shell > tools or Perl. For example, find and replace on a list of given files > or dir, run XML validation on a bunch of files. > > The idioms on this page contains only very basic ones, suitable for > beginning elisp programer. > > You should first be familiar with basic emacs functions that get > cursor position, move cursor, search text, inserting and deleting > text. See Elisp Common Functions Reference. > > ------------------------------ > Interactive Command Idioms > > Grabbing Text > > Grab the text of given beginning position and ending position. > > ; get the string from buffer > (setq myStr (buffer-substring myStartPos myEndPos)) > (setq myStr (buffer-substring-no-properties myStartPos myEndPos)) > > Emacs's string can have properties for the purposes of syntax > coloring, active button, hypertext, etc. The “buffer-substring-no- > properties” function just return a plain string without these > properties. However, most functions that takes string as argument can > also accept strings that has properties. > > Reference: Elisp Manual: Buffer-Contents. > > ------------------------------ > > Grabbing the current word, line, sentence, url, file name etc. > > ; grab a thing at point. The “thing” is a semantic unit. It can be a > ; word, symbol, line, sentence, filename, url and others. > (setq myStr (thing-at-point 'word)) ; grab the current word > (setq myStr (thing-at-point 'symbol)) ; grab the current word with > hyphens or underscore > (setq line (thing-at-point 'line)) ; grab the current line > > ; grab the start and end positions of a line (or any other thing) > (setq myBoundaries (bounds-of-thing-at-point 'line)) > Note that, when the thing is a “symbol”, it usually means any > alphanumeric sequence with dash “-” or underscore “_” characters. For > example, if you are writing php reference lookup command, and the > cursor is on p in “ print_r($y);”, you want to grab the whole > “print_r” not just “print”. The exact meaning of symbol depends on the > mode's Syntax Table. > > Reference: Elisp Manual: Syntax-Tables. > > ------------------------------ > Here's a example of php reference lookup command that grabs by > “symbol”. > > (defun php-lookup () > "Look up current word in PHP ref site in a browser.\n > If a region is active (a phrase), lookup that phrase." > (interactive) > (let (myword myurl) > (setq myword > (if (and transient-mark-mode mark-active) > (buffer-substring-no-properties (region-beginning) > (region-end)) > (thing-at-point 'symbol))) > (setq myurl > (concat "http://us.php.net/" myword)) > (browse-url myurl))) > Reference: Elisp Manual: Buffer-Contents. > > ------------------------------ > Grab Between Matching Pairs > > Grab the current text between delimiters such as between angle > brackets “<>”, parens “()”, double quotes “""”, etc. > > (defun select-inside-quotes () > "Select text between double straight quotes > on each side of cursor." > (interactive) > (let (p1 p2) > (skip-chars-backward "^\"") > (setq p1 (point)) > (skip-chars-forward "^\"") > (setq p2 (point)) > > (goto-char p1) > (push-mark p2) > (setq mark-active t) > ) > ) > > ------------------------------ > Acting on Region > > Idiom for a command that works on the current region. > > Let your function have 2 parameters, such as “start” and “end”, then > use “(interactive "r")”, then the parameters will be filled with > beginning and ending positions of the region. Example: > > (defun remove-hard-wrap-region (start end) > "Replace newline chars in region by single spaces." > (interactive "r") > (let ((fill-column 90002000)) > (fill-region start end))) > > ------------------------------ > Idiom for acting on the region, if there's one, else, on the current > word or thing. > > (defun down-case-word-or-region () > "Make current word or region into lower case." > (interactive) > (let (pos1 pos2) > (if (and transient-mark-mode mark-active) > (setq pos1 (region-beginning) > pos2 (region-end)) > (setq pos1 (car (bounds-of-thing-at-point 'word)) > pos2 (cdr (bounds-of-thing-at-point 'word)))) > (downcase-region pos1 pos2) > ) > ) > > ------------------------------ > Prompting and Getting Input > > Idiom for promping user for input as the argument to your command. > > Use this code “(interactive "‹code›‹promp string›")”. Example: > > (defun query-friends-phone (name) > "..." > (interactive "sEnter friend's name: ") > (message "Name: %s" name) > ) > > What the “(interactive "sEnter friend's name:")” does is that it will > ask user to input something, taken as string, and becomes the value of > your command's parameter. > > The “interactive” can be used to get other types of input.Here are > some basic examples of using “interactive”. > > “(interactive)” makes the function available thru interactive use, > where user can call it with execute-extended-command (M-x). > > “(interactive "s")” will prompt the user to enter a argument, taken as > string, as argument to the function. > > “(interactive "n")” will prompt the user to enter a argument, taken as > number, as argument to the function. > > The prompt text can follow the single-letter code string. > > If your function takes multiple inputs, you can promp user multiple > times, using a single call “interactive”, by joining the promp code > string with “\n” in between, like this: > > (defun query-friends-phone (name age) > "..." > (interactive "sEnter friend's name: \nnEnter friend's age: ") > (message "Name: %s, Age: %d" name age) > ) > > Reference: Elisp Manual: Defining-Commands. > > ------------------------------ > Batch Style Text Processing Idioms > > Open a file, process it, save, close it > > ; open a file, process it, save, close it > (defun my-process-file (fpath) > "process the file at fullpath fpath ..." > (let (mybuffer) > (setq mybuffer (find-file fpath)) > (goto-char (point-min)) ;; in case buffer already open > ;; do something > (save-buffer) > (kill-buffer mybuffer))) > > For processing hundreds of files, you don't need emacs to keep undo > info or fontification. This is more efficient: > > (defun my-process-file (fpath) > "process the file at fullpath fpath ..." > (let () > ;; create temp buffer without undo record. first space is > necessary > (set-buffer (get-buffer-create " myTemp")) > (insert-file-contents filePath nil nil nil t) > ;; process it ... > (kill-buffer " myTemp"))) > > ------------------------------ > Find Replace strings: > > ; idiom for string replacement in current buffer; > ; use search-forward-regexp if you need regexp > (goto-char (point-min)) > (while (search-forward "myStr1" nil t) (replace-match > "myReplaceStr2")) > (goto-char (point-min)) > (while (search-forward "myStr2" nil t) (replace-match > "myReplaceStr2")) > ;; repeat for other strings ... > > Calling a shell command. > > ; idiom for calling a shell command > (shell-command "cp /somepath/myfile.txt /somepath") > > ; idiom for calling a shell command and get its output > (shell-command-to-string "ls") > Both shell-command and shell-command-to-string will wait for the shell > process to finish before continuing. To not wait, use start-process or > start-process-shell-command. > > Reference: Elisp Manual: Asynchronous-Processes. > > ------------------------------ > Traverse a directory recursively. > > In the following, my-process-file is a function that takes a file full > path as input. The find-lisp-find-files will generate a list of full > paths, using a regex on file name. The “mapc” will apply the function > to elements in a list. > > ; idiom for traversing a directory > (require 'find-lisp) > (mapc 'my-process-file (find-lisp-find-files "~/web/emacs/" "\\.html > $")) > > ------------------------------ > > Xah > ∑http://xahlee.org/ > > ☄ Oh, you should have written this a week ago and let me save a lot of time grabbing such tips ![]() By the way, let me just add a simple way to use this in shell script. I needed a way to indent a lot of files and i wanted it to be done the emacs way. I'm not a shell guru, please forgive any bad practice. Skipping the file selection, the 'indent' scipt looks like this: -------- 8< ---------- #!/bin/sh if [ $# -eq 0 ] then echo "Usage: $0 [-u <user>] [--] <filenames>">&2 exit 1 fi for arg do case $arg in -u) shift; user=$arg; shift; continue;; --) shift; break;; *) continue;; esac done for file do files="$files \"$file\"" done if [ -z $files ]; then exit 0; fi batch=" (let (buffer) (dolist (filename '($files)) (setq buffer (find-file filename)) (indent-region (point-min) (point-max) nil) (save-buffer) (kill-buffer buffer))) " if [ -n $user ]; then user=`whoami` fi if [ -n $user ]; then user="-u $user"; fi emacs -batch $user --eval "$batch" -------- 8< ---------- Thanks Mr Lee, such tutorials are really helpfull. -Nicolas |
|
#3
| |||
| |||
| Xah wrote: «Here's a short tutorial i wrote up today that is a basic collection of Emacs Lisp Idioms. http://xahlee.org/emacs/elisp_idioms.html» Nicolas nicolas.e...@gmail.com wrote: > Oh, you should have written this a week ago and let me save a lot of > time grabbing such tips ![]() > By the way, let me just add a simple way to use this in shell script. > I needed a way to indent a lot of files and i wanted it to be done the > emacs way. I'm not a shell guru, please forgive any bad practice. > Skipping the file selection, the 'indent' scipt looks like this: > > #!/bin/sh > > ... > > for file > do > files="$files \"$file\"" > done > > if [ -z $files ]; then exit 0; fi > > batch=" > (let (buffer) > (dolist (filename '($files)) > (setq buffer (find-file filename)) > (indent-region (point-min) (point-max) nil) > (save-buffer) > (kill-buffer buffer))) > " > > ... > > emacs -batch $user --eval "$batch" Thanks. Very interesting example. This inspired me to look at running elisp from command line which i've been wanting to look at. I cleaned up my own elisp script and added a section about running elisp from command line like a perl, python script. Here's the addition: --------------- Running Elisp in Batch mode You can run a elisp program in the the Operating System's command line interfare (shell), using the “--script” option. For example: emacs --script ~/emacs/my_scripts/process_log.el Emacs has few other options and variations to control how you run a elisp script. Here's a table of main options: full option name | meaning --no-site-file | Do not load the site wide “site-start.el” --no-init-file | Do not load your init files “~/.emacs” or “default.el”. --batch | Run emacs in batch mode, use it together with “-- load” to specify a lisp file. This implies “--no-init-file” but not “-- no-site-file”. --script ‹file path› | Run emacs like “--batch” with “--load” set to “‹file path›”. --load="‹elispFilePath›" | Execute the elisp file at “‹elispFilePath›”. --user=‹user name› | Load user ‹user name›'s emacs init file (the “.emacs”). When you write a elisp script to run in batch, make sure your elisp file is self-contained, doesn't call functions in your emacs init file, call to load all libraries it needs (using “require” or “load”), has necessary load path set in the script (e.g. “(add-to-list 'load- path ‹lib path›)”), just like you would with a Perl or Python script. If you've done a clean job in your elisp script, then, all you need to use is “emacs --script ‹elisp file path›”. If your elisp program requires functions that you've defined in your emacs init file (the “.emacs”), then you should explicitly load it in your script by “(load ‹emacs init file path›)”, or, you can add the option to load it, like this: “--user=xah”. (best to actually pull out the function you need) If you are on a Mac with Carbon Emacs or Aquamacs, your emacs program will be be like this “/Applications/Emacs.app/Contents/MacOS/Emacs” instead of just “emacs”. The following is a example of a variation /Applications/Emacs.app/Contents/MacOS/Emacs --no-site-file --batch -- load="~/process_log.el" Reference: (info "(emacs)Option Index"). Xah ∑ http://xahlee.org/ ☄ |
|
#4
| |||
| |||
| With a few very minor tweaks, emacs could be pretty useful as a command line lisp system. I've encountered the following problems (although given my experience, it is highly likely someone knows a solution.) 1. If you do a (read t) to get keyboard input emacs always prompts with "Lisp expression:". I would prefer the ability to specify my own prompt via (princ "....") 2. I'd like to see a (read-string) function that reads to the eol 3. If you do: emacs --batch -Q -l file.el Emacs runs the file in the current directory just fine, however, if you compile the file and have a "file.el" and "file.elc" and want file.elc to run unless file.el has a later date you have to do: emacs --batch -Q -eval '(progn (setq load-path (list nil)) (load "file" nil t))' You should just be able to do: emacs --batch -Q -l file 4. The ability to read & write strings and s-exps to a file (directly) would be great too. With these few additions, emacs could be used as a really good command file. Blake McBride |
|
#5
| |||
| |||
| Blake McBride <blake@mcbride.name> writes: > With a few very minor tweaks, emacs could be pretty useful as a > command line lisp system. I've encountered the following problems > (although given my experience, it is highly likely someone knows a > solution.) > > 1. If you do a (read t) to get keyboard input emacs always prompts > with "Lisp expression:". I would prefer the ability to specify my own > prompt via (princ "....") > > 2. I'd like to see a (read-string) function that reads to the eol > > 3. If you do: emacs --batch -Q -l file.el > Emacs runs the file in the current directory just fine, however, if > you compile the file and have a "file.el" and "file.elc" and want > file.elc to run unless file.el has a later date you have to do: > > emacs --batch -Q -eval '(progn (setq load-path (list nil)) (load > "file" nil t))' > > You should just be able to do: emacs --batch -Q -l file > > 4. The ability to read & write strings and s-exps to a file > (directly) would be great too. > > With these few additions, emacs could be used as a really good command file. I used to think the same, and what function you miss hear can rather trivially be written in emacs lisp. However, there's no point in it, given than there are good Common Lisp implementations that you can use as shell or script (#!) interpreters. Try clisp ; have a look at http://clisp.cons.org/clash.html Also, I've noticed that CL primitives and libraries, while not specifically designed to _edit_ text, are quite powerfull to _process_ text: in general you won't be missing emacs text editing functions. -- __Pascal Bourguignon__ http://www.informatimago.com/ THIS IS A 100% MATTER PRODUCT: In the unlikely event that this merchandise should contact antimatter in any form, a catastrophic explosion will result. |
|
#6
| |||
| |||
| Blake McBride <blake@mcbride.name> writes: > With a few very minor tweaks, emacs could be pretty useful as a > command line lisp system. I've encountered the following problems > (although given my experience, it is highly likely someone knows a > solution.) Well, you could just use librep. -- David Kastrup |
|
#7
| |||
| |||
| Pascal J. Bourguignon <pjb@informatimago.com> wrote: +--------------- | Blake McBride <blake@mcbride.name> writes: | > ... emacs could be used as a really good command file. | | I used to think the same, and what function you miss hear can rather | trivially be written in emacs lisp. However, there's no point in it, | given than there are good Common Lisp implementations that you can use | as shell or script (#!) interpreters. Try clisp ... +--------------- Or, CMUCL, assuming you've added my #! hack <http://rpw3.org/hacks/lisp/site-switch-script.lisp> or equivalent. +--------------- | ...have a look at http://clisp.cons.org/clash.html +--------------- I've not tried going *that* far, but my "cmu" script, which is the usual way I fire up a CMUCL REPL, when given command-line args simply evaluates them (instead of starting a REPL). Combine that with a symlink from "~/bin/=" to "~/bin/cmu", and you get a neat little command-line "CL calculator": $ = + 1 2 3 $ = expt 2 100 1267650600228229401496703205376 $ = \* \* 1e6 1.2676506002282294e36 $ Yes, it saves the last value in a file and restores it to "*"! ;-} $ = floor 28376542876534 82736 342976973 38406 $ = get-decoded-time 54 57 3 18 6 2008 2 T 8 $ = loop for i to 25 when '(oddp i)' collect '(expt 2 i)' (2 8 32 128 512 2048 8192 32768 131072 524288 2097152 8388608 33554432) $ = format t '"2^100 = ~d~%"' '(expt 2 100)' 2^100 = 1267650600228229401496703205376 NIL $ O.k., so quoting starts getting messy when you need strings or sub-exprs. But you can always just single-quote the whole mess and be done with it: $ = '(let ((x "A String.")) (format t "Now print ~(~a~)~%" x) (values))' Now print a string. $ Where it works *really* well is if you have a whole bunch of pre-defined functions for things you commonly do, which I typically do in a script called "hwtool" (hardware debugging tool), e.g.: $ hwtool dump32 '"Show CMUCL\'s string layout."' 0x48956a28: 0x0000002a 0x0000006c 0x776f6853 0x554d4320 0x48956a38: 0x73274c43 0x72747320 0x20676e69 0x6f79616c 0x48956a48: 0x002e7475 0x00000000 0x48956a2f 0x28f0000b 0x48956a58: 0x48956a1b 0x28f0000b 0x48956a2f 0x28f0000b $ -Rob ----- Rob Warnock <rpw3@rpw3.org> 627 26th Avenue <URL:http://rpw3.org/> San Mateo, CA 94403 (650)572-2607 |
![]() |
| Thread Tools | |
| Display Modes | |
In an effort to better serve ads to our visitors, cookies are used on objectmix.com. For more information, check out our Privacy Policy.