AutoLISP function bad argument in AutoCAD - lisp

(defun gml2dwg (odabir)
;; RANDOM CODE ;;
;; This is the part where I should open a filepath "odabir"
(setq datoteka (open odabir "r"))
;; CODE IS CONTINUED )
(defun c:gml2dwg (/ odabir)
(setq odabir (getstring "Odabir:"))
(gml2dwg odabir)
(princ) )
(defun c:gmlimport (/ allfiles fpath)
(setq allfiles (vl-directory-files "C:\\Users\\Admin\\Documents\\gml2" "*.gml"))
(foreach file allfiles
((setq fpath (strcat "C:\\Users\\Admin\\Documents\\gml2\\" file))
(gml2dwg fpath)) )
(princ) )
So, as shown above, i have first long lisp function gml2dwg that get a gml file as an input and draws the polygons from the file in autocad. The function can only take one file as an input, so I have problems inputing 6000+ gml files into cad. I wrote two other functions, where c:gml2dwg is the one able to take parameters since gml2dwg can't be used as a command. The third - c:gmlimport is used to get all files from a directory and loop it through the c:gml2dwg, but all I get is this error:
********* Pogreška: bad argument type: stringp nil! ********** Cannot invoke (command) from error without prior call to
(push-error-using-command). Converting (command) calls to
(command-s) is recommended.
The first function works fine in the form of calling it in the VLISP console (gml2dwg "somefilepath").
Can anyone tell me what is wrong in the other two functions? It's something with arguments/parameters probably or setting the variables, but I'm an amateur in lisp so I need your help to figure it out. Thank you.

If I don't misunderstand, gml2dwg is a LISP defined command :
(defun c:gml2dwg ...)
If so, you cannot call gml2dwg with the command function and pass it arguments.
You need to split the c:gml2dwg function into 2 functions:
1) a standard LISP function which takes 2 arguments: some option ("k"?) and the file path. This function contains the code which draws the polygon according to the arguments.
(defun gml2dwg (option fpath) ...)
2) a LISP defined command which gets user inputs and call the gml2dwg function passing it the results of inputs.
(defun c:gml2dwg (/ option fpath ...)
(setq option ...)
(setq fpath ...)
(gml2dwg option fpath)
(princ)
)
This way, you can call the gml2dwg function from c:gmlimport:
(defun c:gmlimport (/ allfiles fpath)
(setq allfiles (vl-directory-files
"C:\\Users\\Admin\\Documents\\gml2"
"*.gml"
)
)
(foreach file allfiles
(setq fpath (strcat "C:\\Users\\Admin\\Documents\\gml2\\" file))
(gml2dwg "k" fpath)
)
(princ)
)
Note: I removed a superfluous opening parenthesis.

Related

How to create and write into text file in Lisp (continued)

https://stackoverflow.com/a/9495670/12407473
From the question above, when I try to work the code I get
"Error: no function definition: STR".
Can anyone tell me why it does not work for me?? Thank you!
(with-open-file (str "/.../filename.txt"
:direction :output
:if-exists :supersede
:if-does-not-exist :create)
(format str "write anything ~%"))
As noted by others in the comments, the example code that you are using is Common LISP, not AutoLISP (the dialect of LISP used by AutoCAD). As such, functions such as str, with-open-file, and format are not defined in the AutoLISP dialect.
In AutoLISP, the general approach would be as follows:
(if (setq des (open "C:\\YourFolder\\YourFile.txt" "w"))
(progn
(write-line "Your text string" des)
(close des)
)
)
Commented, that is:
;; If the following expression returns a non-nil value
(if
;; Assign the result of the following expression to the symbol 'des'
(setq des
;; Attempt to acquire a file descriptor for a file with the supplied
;; filepath. The open mode argument of "w" will automatically create
;; a new file if it doesn't exist, and will replace an existing file
;; if it does exist. If a file cannot be created or opened for writing,
;; open will return nil.
(open "C:\\YourFolder\\YourFile.txt" "w")
) ;; end setq
;; Here, 'progn' merely evaluates the following set of expressions and
;; returns the result of the last evaluated expression. This enables
;; us to pass a set of expressions as a single 'then' argument to the
;; if function.
(progn
;; Write the string "Your text string" to the file
;; The write-line function will automatically append a new-line
;; to the end of the string.
(write-line "Your text string" des)
;; Close the file descriptor
(close des)
) ;; end progn
;; Else the file could not be opened for writing
) ;; end if

Create autoloaded function from macro

How can I get create an autoloaded function from a macro function factory? For example, say I have a macro to create alignment functions as follows, but I want to be able to specify an option so the expanded macro has an autoload cookie.
(defmacro align-by (name doc regex)
"Alignment function factory."
(declare (indent 1))
(let ((fn (intern name)))
`(defun ,fn (start end)
,doc
(interactive "r")
(align-regexp start end ,regex))))
(align-by "align-backslash"
"Align backslashes at end of line in region."
"\\(\\s-*\\)\\\\$")
I know I can write this, but can I avoid needing to write this for every function?
;;;###autoload (autoload 'align-backslash "this-file")
It's not clear where the macro would pick up the name of the file to be autoloaded - you do not pass a file name to the macro, currently.
Assuming that the file name comes from a file that is being visited when the macro is expanded, this will do it:
(defmacro align-by (name doc regex)
"Alignment function factory."
(declare (indent 1))
(let ((fn (intern name)))
`(progn
,(and (buffer-file-name)
`(autoload ',name ,(buffer-file-name)))
(defun ,fn (start end)
,doc
(interactive "r")
(align-regexp start end ,regex)))))
Testing:
(macroexpand '(align-by "align-backslash"
"Align backslashes at end of line in region."
"\\(\\s-*\\)\\\\$"))
C-u C-x C-e shows that that gives this when the current buffer is not visiting a file:
(progn
(autoload '"align-backslash" nil)
(defun align-backslash
(start end)
"Align backslashes at end of line in region."
(interactive "r")
(align-regexp start end "\\(\\s-*\\)\\\\$")))
And it gives this when the buffer is visiting file foo.el, where ".../foo.el" is really the absolute file name for foo.el:
(progn
(autoload '"align-backslash" ".../foo.el")
(defun align-backslash
(start end)
"Align backslashes at end of line in region."
(interactive "r")
(align-regexp start end "\\(\\s-*\\)\\\\$")))
The code that picks up the ;;;###autoload cookies does expand the macros before looking at the code, so you should be able to just place a ;;;###autoload cookie right before a (align-by ...) expression and get the right autoload placed in the <foo>-autoloads.el file.
The problem, tho is that your macro is probably not going to be defined at the time the autoloads are created, so the macro expansion will not actually happen. Maybe a M-x report-emacs-bug is in order.
As emacs manual mentioned
If you write a function definition with an unusual macro that is not one of the known and recognized function definition methods, use of an ordinary magic autoload comment would copy the whole definition into loaddefs.el. That is not desirable. You can put the desired autoload call into loaddefs.el instead by writing this:
;;;###autoload (autoload 'foo "myfile")
(mydefunmacro foo
...)
Your align-by is like mydefunmacro in the manual example. It is not known function definition method and it is not supported by autoload mechanism.
So there are three alternatives:
Extend the list of supported types (defun, defmacro, cl-defun, defclass,...) by your special macros. Then you can use simple ;;;###autoload "decorator".
Invent your own mechanism of "myfile" parsing (without executing) and "loaddefs" populating by necessary autoload definitions.
Use more complicated construction (with (autoload 'align-backslash "myfile")) as function defintion method.
If you rewrite align-by like this (without intern):
(defmacro align-by-defun (name doc regex)
"Alignment function factory."
(declare (indent 1))
`(defun ,name (start end)
,doc
(interactive "r")
(align-regexp start end ,regex)))
;;;###autoload (autoload 'align-backslash "myfile")
(align-by-defun align-backslash
"Align backslashes at end of line in region."
"\\(\\s-*\\)\\\\$")
you can see that align-by is just a function definition method (like cl-defun).

Adding hook on elisp function

I'm using Emacs.
Is there any way to add hook on a function?
Assume that there is a markdown-export function.
It is designed to export HTML file into current directory where current working 'markdown file' exsits.
But, I want to export HTML file into another directory. How can I do that without modification on Emacs markdown plugin (markdown-mode.el)?
This is markdown-mode.el's export function:
(defun markdown-export (&optional output-file)
"Run Markdown on the current buffer, save to file, and return the filename.
If OUTPUT-FILE is given, use that as the filename. Otherwise, use the filename
generated by `markdown-export-file-name', which will be constructed using the
current filename, but with the extension removed and replaced with .html."
(interactive)
(unless output-file
(setq output-file (markdown-export-file-name ".html")))
(when output-file
(let* ((init-buf (current-buffer))
(init-point (point))
(init-buf-string (buffer-string))
(output-buffer (find-file-noselect output-file))
(output-buffer-name (buffer-name output-buffer)))
(run-hooks 'markdown-before-export-hook)
(markdown-standalone output-buffer-name)
(with-current-buffer output-buffer
(run-hooks 'markdown-after-export-hook)
(save-buffer))
;; if modified, restore initial buffer
(when (buffer-modified-p init-buf)
(erase-buffer)
(insert init-buf-string)
(save-buffer)
(goto-char init-point))
output-file)))
=====================================================================
I have made an advice to save exported HTML at temp directory
Here is the code.
(defadvice markdown-export (around set-temp-path-for-exported-file activate)
(ad-set-arg 0 (format "%s/%s" "~/.emacs.d/temp-dir" (file-name-nondirectory buffer-file-name)))
ad-do-it)
Thanks!!!!!!!!!!!!!!
In this case you do not need to hook on this function since it already accepts the filename as an argument, unfortunately it does not accept the filename when called interactively. As a workaround you can define a simple wrapper around the function like follows
(defun my-markdown-export (&optional file)
(interactive (list (ido-read-file-name "Export as: ")))
(markdown-export file))
The advice mechanism is a bit like having hooks for any arbitrary function, but here you have actual hooks you can use, as well as a function argument which addresses your requirement directly.
So you can:
(a) Pass the function any arbitrary output filename.
(b) Use the provided markdown-before-export-hook to setq whichever variables you need to (which at a glance looks like output-file, output-buffer, and output-buffer-name).

"Wrong type argument: stringp, cons" when calling apply function on concatenated lists

I wanted to write some functionality in Emacs which will allow me to run my favorite editor and file manager in directory where my current buffer file resides. I'm not familiar with Lisp so this code may be ugly, anyway:
(setq terminal-program "rxvt-unicode")
(defun buffer-dir-name ()
(file-name-directory buffer-file-name))
(defun terminal-option-buffer-dir ()
(let ((dir (format "'%s'" (buffer-dir-name))))
`("-cd" ,dir)))
(setq terminal-option-ranger '("-e" "ranger"))
(defun run-terminal ()
(interactive)
(start-process "terminal" nil terminal-program) (terminal-option-buffer-dir))
;; outdated, see below
(defun run-file-manager ()
(interactive)
(let ((args (append (terminal-option-buffer-dir) terminal-option-ranger)))
(message (type-of args)
(apply 'start-process "filemanager" nil terminal-program args))))
Function run-terminal works fine. But when I try to run run-file-manager I'm experiencing following error: Wrong type argument: stringp, cons. Why? Documentation says that return value of append function is a list, not cons.
After Drew response I saw that run-file-manager function has some trash left after my debugging. Now it looks as follow:
(defun run-file-manager ()
(interactive)
(let ((args (append (terminal-option-buffer-dir) terminal-option-ranger)))
(apply 'start-process "filemanager" nil terminal-program args)))
;; (apply 'start-process "filemanager" nil terminal-program '("-cd" "/opt/" "-e" "ranger"))))
Now I have an another issue. When I call this function it does nothing. But if first invocation of apply is commented and second one is uncommented it works as I expect: it runs ranger in terminal in /opt directory. Any ideas?
I solved my problem, which was slightly different from that in question title. Problem was that function terminal-option-buffer-dir was returning -cd option with valued starting with ' not / which is required by urxvt.
I debugged that by setting parameter BUFFER of start-process function to "*Messages*".
The error msg says that something in run-file-manager was expecting a string and got the symbol cons instead.
message expects a string as its first argument. But type-of returns a symbol. In this case, it returns the symbol cons.

Emacs custom command line argument

From the documentation I can see I can access command line arguments (command-line-args).
I'd like to add my own arguments but Emacs complains at start up that it doesn't recognize them.
E.g.
emacs -my_argument
I get:
command-line-1: Unknown option `-my_argument'
What's a proper way to define my custom arguments and provide information to my Emacs session?
Is there a way to pop an argument from a command line?
Add something like this to your ~/.emacs, ~/.emacs.el, or ~/.emacs.d/init.el file:
(defun my-argument-fn (switch)
(message "i was passed -my_argument"))
(add-to-list 'command-switch-alist '("-my_argument" . my-argument-fn))
Then you can execute emacs -my_argument and it should print i was passed -my_argument to the minibuffer. You can find more information in the GNU elisp reference.
As stated in another post you can add your custom switches to command-switch-alist and emacs will call the handler function for any matching switch passed in on the command line. However, this operation is done after your .emacs file has been evaluated. This is fine for most cases but you may wish for a command line argument to alter the execution path or behaviour of your .emacs evaluation; I often do this to enable/disable configuration chunks (mainly for debugging).
To achieve this you can read command-line-args and check for your switch manually and then delete it from the list, this will stop emacs complaining about an unknown argument.
(setq my-switch-found (member "-myswitch" command-line-args))
(setq command-line-args (delete "-myswitch" command-line-args))
Which can alter your .emacs evaluation like so:
(unless my-switch-found
(message "Didn't find inhibit switch, loading some config.")
...)
And you could build this into a single step:
;; This was written in SO text-box, not been tested.
(defun found-custom-arg (switch)
(let ((found-switch (member switch command-line-args)))
(setq command-line-args (delete switch command-line-args))
found-switch))
(unless (found-custom-arg "-myswitch")
(message "Loading config...")
...)
For those who are interested, here is a code snip to show how to process custom arguments in Emacs lisp. In this case, I am processing an argument --suffix / -S to variable _suffix.
I pulled the idea from a BSD-Lite Script Emacs script.
(setq _suffix nil)
;; Process cli args
(while command-line-args-left
(setq k (car command-line-args-left))
(setq command-line-args-left (cdr command-line-args-left))
(setq command-line-args (delete k command-line-args))
(cond
(or (string-equal k "--cs-suffix")
(string-equal k "-S"))
(setq _suffix (intern (car command-line-args-left)))
(setq command-line-args-left (cdr command-line-args-left))
(setq command-line-args (delete _suffix command-line-args))
)))
This will roll through command-line-args-left and remove them all from command-line-args which will prevent Emacs from complaining.