I want to make a simple AutoLISP routine where I draw lines in layer "0" and afterwards change back to the original layer.
With the code below, I can draw a line but it will stay in the current layer. If I let out the last code line, I draw the line in layer "0" but afterwards there is no change back to the original layer.
(defun c:testfunction (/ OLD)
(setq OLD (getvar 'clayer))
(setvar 'clayer "0")
(command "line")
(setvar 'clayer OLD)
)
Concept of the Code
I first store the current layer in the OLD variable, then I change the layer to "0". After the LINE command I change back to the OLD layer.
Thank you in advance.
Pausing for Input
Since the AutoCAD LINE command can issue an arbitrary number of prompts to the user (depending on the number of lines they wish to draw), you'll need to include a loop within the code to allow for an arbitrary number of user inputs, before resetting the current layer.
Your current code will evaluate the expression (setvar 'clayer OLD) before the user has supplied the first line point, as there is no pause for user input.
To pause for user input in an AutoCAD command invoked from an AutoLISP program or toolbar macro, you use the backslash \.
However, since the backslash is an escape character in AutoLISP, you'll need to prefix it with another backslash in order to output a literal backslash to the command, e.g. \\.
Aside: the pause symbol also evaluates to a backslash, but since this symbol is not protected and may be redefined, I would always recommend using a literal backslash.
And so we might use:
(command "line" "\\")
However, this only pauses for a single input, and the AutoCAD LINE command will accept an arbitrary number of inputs, and so we need a way to construct a loop and determine when the user has finished with the command.
For this, we can make use of the CMDACTIVE system variable, which is a bit-coded system variable indicating the current command state - a bit code of 1 indicates that a command is active.
To test whether bit code 1 is set, we can use the AutoLISP logand function, which returns the bitwise AND of two supplied integers, e.g.:
(logand 1 3) => 1
(logand 1 2) => 0
Combining this with the CMDACTIVE system variable we have:
(logand 1 (getvar 'cmdactive))
Which will return 1 if a command is active, else 0 otherwise.
Hence the test expression for our loop can be:
(= 1 (logand 1 (getvar 'cmdactive)))
And now it just remains to construct the loop itself - for this, we can use the AutoLISP while function:
(while (= 1 (logand 1 (getvar 'cmdactive)))
(command "\\")
)
The above will continue to pause for user input whilst a command is active.
Putting it all Together
Putting everything together, we have:
(defun c:testfunction ( / old )
(setq old (getvar 'clayer))
(setvar 'clayer "0")
(command "line")
(while (= 1 (logand 1 (getvar 'cmdactive)))
(command "\\")
)
(setvar 'clayer old)
)
However, there are few other enhancements that we can make here...
Additional Enhancements
We can add a (princ) expression to the end of the function definition to suppress the value returned by the last evaluated expression. As written, the current function will return the value returned by the setvar function, which will be the name of the original layer.
We can use (princ) at the end of the definition to instead return a null symbol to the command line, and hence cleanly exit the program:
(defun c:testfunction ( / old )
(setq old (getvar 'clayer))
(setvar 'clayer "0")
(command "line")
(while (= 1 (logand 1 (getvar 'cmdactive)))
(command "\\")
)
(setvar 'clayer old)
(princ)
)
We can prefix the invocation of the AutoCAD LINE command with an underscore to account for localised versions of AutoCAD (in which the LINE command may be called something else):
(command "_line")
The use of an underscore command prefix ensures that we are invoking the non-localised English LINE command.
We can prefix the invocation of the AutoCAD LINE command with a period to account for possible redefined versions of the AutoCAD LINE command:
(command "_.line")
Since it is possible to redefine AutoCAD commands, the use of a period command prefix ensures that we are invoking the non-redefined standard LINE command.
We can add a local error handler to automatically reset the current layer to the original layer if the user presses Esc to exit the program:
(defun c:testfunction ( / *error* old )
(defun *error* ( msg )
(if old (setvar 'clayer old))
(if (not (wcmatch (strcase msg t) "*break,*cancel*,*exit*"))
(princ (strcat "\nError: " msg))
)
(princ)
)
(setq old (getvar 'clayer))
(setvar 'clayer "0")
(command "_.line")
(while (= 1 (logand 1 (getvar 'cmdactive)))
(command "\\")
)
(setvar 'clayer old)
(princ)
)
For more information on how the local error handler operates, you may wish to refer to my AutoLISP tutorial on Error Handling.
We could also test to ensure that layer 0 is not frozen before attempting to set it as the current layer, but this goes beyond the scope of this beginner's program.
Extending the Idea
With the above approach we have defined our own custom function to store the current layer, set a new current layer, invoke the LINE command, and then reset the original current layer.
But this means that the user must remember to invoke our custom version of the LINE command, rather than the standard LINE command.
What if there was a way to automatically set & reset the current layer when invoking standard AutoCAD commands? Well, through the use of Visual LISP Reactors, there is.
In my open-source Layer Director application, I demonstrate how you can use Command Reactors & LISP Reactors to automatically create, set, and reset the current layer when standard AutoCAD commands are invoked:
Related
I have a bunch of links saved in an orgmode file, say...
http://www.stackoverflow.com
http://www.google.com
http://www.github.com
I can open each one by having the cursor on the link and doing C-c C-o, and it conveniently pops up my default browser and opens that link in a tab.
Now suppose I have like 20 of these links. Is there a convenient way to apply a function like this to each line within a selected region, without recording an explicit macro?
I'd imagine it looking something like...
Select region
M-x foreach-in-region
Keystrokes to apply to each line: C-c C-o
And this is just for functions already defined. I imagine the way without would be something like...
with cursor on first line of link
F3 # to start record macro
C-c C-o
down arrow
F4
Select region (omitting the first line, since that's now already opened in my browser)
C-x C-k r
Does this exist? If not, how would I lisp this?
You should record the macro for one line, then use apply-macro-to-region-lines to execute it for all lines in region. C-x C-k r
Alternatively, you can use multiple-cursors to create a cursor on each line and C-c C-o to open all. multiple-cursors will transform your usage patterns over time for the better if you give it a chance.
(defun do-lines (fun &optional start end)
"Invoke function FUN on the text of each line from START to END."
(interactive
(let ((fn (intern (completing-read "Function: " obarray 'functionp t))))
(if (use-region-p)
(list fn (region-beginning) (region-end))
(list fn (point-min) (point-max)))))
(save-excursion
(goto-char start)
(while (< (point) end)
(funcall fun (buffer-substring (line-beginning-position) (line-end-position)))
(forward-line 1))))
Update after your comment --
Now it sounds like you want to not enter a function name but hit a key, and have the command bound to that key be applied to each line in the region (or buffer).
Something like the following will do that. However, be aware that command often have particular behavior wrt lines. For example, if you were to hit key C-k (kill-lines) then it already moves forward after each line it kills. Because do-lines does not know what kind of function (command) you will invoke, it advances to the next line after each invocation. For a command such as kill-lines this will thus do the wrong thing: it will end up advancing two lines, not one, thus skipping lines. IOW, be aware that the code for do-lines cannot compensate for what a particular function it invokes might do that might not correspond to what you expect. Instead, it does what it says it does.
(defun do-lines (command &optional start end)
"Invoke COMMAND on the text of each line from START to END."
(interactive
(let* ((key (read-key-sequence-vector "Hit key sequence: "))
(cmd (lookup-key global-map key t)))
(when (numberp cmd) (error "Not a valid key sequence"))
(unless (commandp cmd) (error "Key `%s' is not defined" (key-description key)))
(if (use-region-p)
(list cmd (region-beginning) (region-end))
(list cmd (point-min) (point-max)))))
(setq start (copy-marker start)
end (copy-marker end))
(save-excursion
(goto-char start)
(while (< (point) end)
(funcall command (buffer-substring (line-beginning-position) (line-end-position)))
(forward-line 1))))
In some situations, you can use Emacs Repeating using C-x z following by more `z'. I was trying to comment all the lines in region and it worked nicely for my use case.
The command C-x z (repeat) provides another way to repeat an Emacs
command many times
To repeat the command more than once, type additional z’s: each z repeats the command one more time
In the spirit of TIMTOWTDI[1], I'll point out a technique that works well for some situations, including the one in the OP.
If you're looking to run an external command on a line of space-separated strings (like URLs):
Select the region
Invoke M-| (Alt+Shift+\, shell-command-on-region)
Use xargs as a prefix command to the desired command (e.g., xdg-open, or x-www-browser)
For example, the full command entered for step 3 might be:
xargs -n1 xdg-open
The -n1 switch causes xargs to open invoke the given program with one argument at a time; it will run the program once for each input. If the command can handle multiple arguments at once, you can omit -n1. For example, I have a web command that can open multiple URLs as arguments, so just xargs web works.
The major benefit of this approach is, it works on anything POSIX-compliant without doing anything in advance. Disadvantages include, it only works on external commands, and it requires xargs (not included with every OS by default).
[1] There's More Than One Way To Do It, originally from Perl, but useful elsewhere.
I have a defun that conveniently adds / removes parentheses to marked expressions. (see code in the end)
As a neophyte in emacs Lisp, I only know how to make simple key-bindings without argument.
However, I believe the defun would be made more convenient if its key-binding could take two optional arguments to specify whether to add / remove (), [], or {}
My current (simple) key-binding is as below, is there a trivial way to make it argument-taking as described?
(global-set-key (kbd "C-M-( )") 'end-of-statement)
Appreciate any advice or direction tips!
(Code: by Istvan Chung)
(defun surround-with-parens ()
(interactive)
(save-excursion
(goto-char (region-beginning))
(insert "("))
(goto-char (region-end))
(insert ")"))
(defun delete-surrounded-parens ()
(interactive)
(let ((beginning (region-beginning))
(end (region-end)))
(cond ((not (eq (char-after beginning) ?\())
(error "Character at region-begin is not an open-parenthesis"))
((not (eq (char-before end) ?\)))
(error "Character at region-end is not a close-parenthesis"))
((save-excursion
(goto-char beginning)
(forward-sexp)
(not (eq (point) end)))
(error "Those parentheses are not matched"))
(t (save-excursion
(goto-char end)
(delete-backward-char 1)
(goto-char beginning)
(delete-char 1))))))
I assume the question is for surround-with-parens, as delete-surrounded-parens is probably better off just guessing the parentheses used by itself.
I also assume that you are doing this for practice more so than for actually solving the problem. If you enable electric-pair-mode in recent Emacsen, configured pairs will wrap around any active region as your command does. But it's a good mid-level emacs lisp exercise.
As the comments noted, you have multiple options.
Command Argument
A command can take arguments, as you asked for. Commands are no different from other lisp functions, except that they have an interactive spec. The interactive spec tells Emacs how to run the command when it is invoked with a key or via M-x.
Simple (interactive) will pass no arguments to the function. (interactive "r") will pass the beginning and end of the region as two arguments. Etc. You can read about the different options in C-h f interactive. You can even write lisp code to calculate the arguments there.
You could use (interactive "cWrap with: "). This would prompt the user for a key, and pass the key to your function. For example, if the user types (, your function receives 40 as the argument, the character code for the opening parenthesis. ?\( is 40 as well, and (make-string 1 40) returns "(". You'll have to create a map to figure out the closing parenthesis.
The drawback here is that you need to press two keys: The first to invoke the command, and then a key to specify the parenthesis to use.
Multiple keys
You can also bind your command to different keys – for example, C-M-( and C-M-[. You can then use this-single-command-keys to get something that specifies the last key event. Sadly, this can be somewhat tricky to translate back to a key sequence. E.g. C-M-( returns [201326632].
Multiple commands
You could also just define one command per parenthesis type, all of which simply call a common function with arguments, and bind those commands to keys.
post-self-insert-hook
Finally, you can use the same method as electric-indent-mode does: Add a function to post-self-insert-hook and decide based on char-before what you want to do.
I'm new to elisp, so please forgive me if the following approach is totally clumsy.
In the team I'm currently working with, there is an usual convention of closing python blocks with a pass statement (if they aren't ended by closing keywords like else or except or such). While unusual, this has the advantage that one can always recover the original indentation of the program if it is unintentionally changed (using emacs indent-region).
To get existing code in line with this convention, I wrote a small elisp function:
(defun python-check-indent ()
"Check if automatic indentation changes current indent, insert pass keyword if it does."
(interactive)
(move-beginning-of-line 1)
(skip-chars-forward " ")
(if
(< 0
(let (original)
(setq original (point))
(indent-for-tab-command)
(- (point) original)
)
)
(progn
(insert "pass")
(newline)
(indent-for-tab-command)
)
)
(next-line)
)
(global-set-key (kbd "C-`") 'python-check-indent)
The idea is simply to test whether hitting TAB would change the indentation, and insert a pass statement in that case. To facilitate processing longer blocks of code, it then advances to the next line.
When I run it using M-x python-check-indent, it does what I want (except that it moves around empty lines slightly), also when running it repeatedly to process several lines. However, when I run it repeatedly using the C-` keybinding, it starts messing up the code from the second invocation on.
So here are my questions: What is the difference between invoking a command with M-x ... and using its keybinding? And how could I change the function to be not affected by this difference?
emacs-version: GNU Emacs 23.3.1 (x86_64-apple-darwin, NS apple-appkit-1038.35) of 2011-03-10 on black.porkrind.org
(edit) current workaround: I'm now wrapping it inside a keyboard-macro, so it's "bound" to C-x e, and behaves properly.
The general rule is that it is best to avoid complex interactive
commands in your functions because they could be affected by all sorts
of options.
(defun python-check-indent ()
"Check if automatic indentation changes current indent, insert pass keyword if it does."
(interactive)
(goto-char (line-beginning-position))
(skip-chars-forward " ")
(when (< 0
(let (original)
(setq original (point))
(python-indent-line)
(- (point) original)))
(insert "pass\n")
(python-indent-line))
(forward-line))
However, even this is probably not good because python-indent-line's behavior depends on last-command and python-indent-trigger-commands. I think it would be best if you replaced the first invocation of python-indent-line with the code which computes the target indentation instead of actually indenting, something like (nth python-indent-current-level python-indent-levels).
PS. If you still have problems, I suggest that you use edebug and step through the function.
Main goal: making a smart "uncomment" function suitable for any mode.
(defun uncomment-mode-specific ()
(interactive)
(if (region-active-p)
(comment-region (region-beginning) (region-end) -1) ; so far so good
(if (= ";" (line-beginning-position)) ; here is the problem
(message "successful")
(message "unsuccessful"))
))
In the if statement, I would like to check for the value of the first character of the line, and if it equals the variable comment-start (which would return ";" in emacs lisp), goto beginning-of-line and delete the character. Ideas?
EDIT: More clarification as requested in a comment below follows. I would like a function to do:
1) If a region is selected, remove the comments (here that uncomment-region or comment-dwim would work as pointed out by Patrick)
ELSE:
2) If the first character of the line at point is a comment character, remove the comment character.
ELSE:
3) Search current line for comment (excluding \% or \;, mode dependent), move up to comment and kill the line starting with the comment sign.
I could not see how you would want this to work differently. It could then be bound to one key to simply remove comments, depending on the mode, using comment-start to identify the comment character.
I'm posting this as an alternative answer, since what you are trying to do is partly done already with the comment-dwim command. From the docs (you can find it using C-h f comment-dwim)
comment-dwim is an interactive compiled Lisp function in
`newcomment.el'.
It is bound to M-;.
(comment-dwim ARG)
Call the comment command you want (Do What I Mean).
If the region is active and transient-mark-mode is on, call
comment-region (unless it only consists of comments, in which
case it calls uncomment-region).
Else, if the current line is empty, call comment-insert-comment-function
if it is defined, otherwise insert a comment and indent it.
Else if a prefix ARG is specified, call comment-kill.
Else, call comment-indent.
You can configure comment-style to change the way regions are commented.
So in order to use it, simply select a region with C-SPC (set-mark-command) and move the caret to the other point and run comment-dwim.
I'm writing a derived mode, based on comint-mode. The mode is an interface to a command line program (GRASS gis), and the comint mode completion works for the programs. I'm trying to add on support for completing the arguments to the program, via completion-at-point-functions. A toy example is:
(setq my-commands
'(("ls"
("my-completion-1")
("my-completion-2"))
("mv"
("my-completion-3")
("my-completion-4"))))
(defun my-completion-at-point ()
(interactive)
(let ((pt (point)) ;; collect point
start end)
(save-excursion ;; collect the program name
(comint-bol)
(re-search-forward "\\(\\S +\\)\\s ?"))
(if (and (>= pt (match-beginning 1))
(<= pt (match-end 1)))
() ;; if we're still entering the command, pass completion on to
;; comint-completion-at-point by returning nil
(let ((command (match-string-no-properties 1)))
(when (member* command my-commands :test 'string= :key 'car)
;; If the command is one of my-commands, use the associated completions
(goto-char pt)
(re-search-backward "\\S *")
(setq start (point))
(re-search-forward "\\S *")
(setq end (point))
(list start end (cdr (assoc command my-commands)) :exclusive 'no))))))
(push 'my-completion-at-point completion-at-point-functions)
This almost works. I get normal completion of program names. However, if I have entered ls at the command line, hitting tab inserts my-completion- and doesn't offer the two options. Hitting tab again inserts my-completion- a second time, so that I now have ls my-completion-mycompletion-.
My actual code includes a few lines to check for multi-line commands, but makes no changes to the completion code. With this version of the code, I hitting tab on a line that starts with one of the program names in my-commands I am presented with a list of the possible arguments to complete the command with, but nothing is inserted in the buffer, and the list does not get narrowed by typing the first few letters of an argument.
I've been over the manual, but I can't figure out the correct way to write a completion-at-point function. Any ideas what I'm missing?
I have looked briefly at pcomplete, but the didn't really understand the 'documentation', and didn't make any progress.
The problem seems to be with the way you're finding start and end to return the boundaries of the argument at point. I didn't spend long enough debugging it to be sure of the details, but I think if you call the function interactively you'll see that it returns the same value for start and end, and this means that the completion UI doesn't know to use the argument at point to select from the completion table you've passed it.
Changing the last part of your function to the following seems to be one fix:
(when (member* command my-commands :test 'string= :key 'car)
;; If the command is one of my-commands, use the associated completions
(goto-char pt)
(let ((start
(save-excursion
(skip-syntax-backward "^ ")
(point))))
(list start pt (cdr (assoc command my-commands)) :exclusive 'no)))))))
This gives the expected results when added as an element of completion-at-point-functions.
Here I've used skip-syntax-backward instead of regexp search, which I think is slightly more idiomatic Elisp for this kind of thing. It just says to move point backwards across anything that is not in syntax class "whitespace". The skip-syntax functions return the distance moved rather than the value of point, so we have to add a call to point at the end of the save-excursion.
If you do use regexp searches in a function like this, it's usually a good idea to pass t for the fourth argument, noerror, so that it doesn't pass on errors to the user if it fails to match. This does mean that you have to check for yourself whether the return value is nil, though.
Finally, instead of push to add the completion function you might want to use add-hook as follows:
(add-hook 'completion-at-point-functions 'my-completion-at-point nil t)
This does two useful things: it checks whether your function is already in the hook before adding it, and (by passing t for the fourth argument, local) it only adds the function to the buffer-local value of the completion-at-point hook. This is almost certainly what you want, since you don't want to use these completions in every other Emacs buffer when you press the TAB key.