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

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

Related

AutoLISP function bad argument in AutoCAD

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

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

How to define whole line comment syntax in Emacs?

I want the sequence // to start a comment when it is at the beginning of a line. But inside of a line it should not start any comments.
// this is a comment
This is a URL: http://example.com
Is it possible?
I'd do it this way:
(defvar my-foo-mode-syntax-table
(let ((st (make-syntax-table)))
;; Add other entries appropriate for my-foo-mode.
(modify-syntax-entry ?/ ". 12" st)
(modify-syntax-entry ?\n "> " st)
st))
(defvar my-foo-font-lock-keywords
;; Add other rules appropriate for my-foo-mode.
())
(define-derived-mode my-foo-mode nil "My-Foo"
(setq-local font-lock-keywords '(my-foo-font-lock-keywords))
;; Add other settings appropriate for my-foo-mode.
(setq-local syntax-propertize-function
(syntax-propertize-rules ("./\\(/+\\)" (1 ".")))))
Notice: No need for any special font-lock rule since font-lock automatically highlights comments for you, based on the syntax-tables.
You can do this by writing a syntax-propertize-function
I have written an example major mode that shows this below.
Emacs built in parsing will call your syntax-propertize-function so that it can manually set the syntax-table text property on lines starting with //.
(define-derived-mode my-syntax-test-mode fundamental-mode
"A major mode where // denotes a comment but only if it is at the beginning of a line."
:syntax-table (make-syntax-table)
(setq mode-name "my syntax test")
;; our mode will use `apply-my-custom-syntax-table-appropriately' to manually set
;; the syntax-table text property on lines starting with //"
(setq syntax-propertize-function 'apply-my-custom-syntax-table-appropriately)
;; change `comment-dwim` to handle this type of comments correctly
(local-set-key [remap comment-dwim] 'my-comment-dwim))
(defvar my-custom-syntax-table
;; syntax table where // starts a comment and \n ends it
(let ((table (make-syntax-table)))
(modify-syntax-entry ?/ "< 1" table)
(modify-syntax-entry ?/ "< 2" table)
(modify-syntax-entry ?\n "> " table)
table))
(defun apply-my-custom-syntax-table-appropriately (beg end)
(save-excursion
(save-restriction
(widen)
(goto-char beg)
;; for every line between points BEG and END
(while (and (not (eobp)) (< (point) end))
(beginning-of-line)
;; if it starts with a //
(when (looking-at "^//")
;; remove current syntax-table property
(remove-text-properties (1- (line-beginning-position))
(1+ (line-end-position))
'(syntax-table))
;; set syntax-table property to our custom one
;; for the whole line including the beginning and ending newlines
(add-text-properties (1- (line-beginning-position))
(1+ (line-end-position))
(list 'syntax-table my-custom-syntax-table)))
(forward-line 1)))))
(defun my-comment-dwim (arg)
(interactive "*P")
(require 'newcomment)
(save-excursion
(let ((comment-start "//") (comment-end "")
(comment-column 0)
;; don't indent comments
(comment-style 'plain))
;; create the region containing current line if there is no active region
(unless (use-region-p)
(end-of-line)
(push-mark (line-beginning-position))
(setq mark-active t))
(comment-dwim nil))))
Answer: Use regexps
Short and simple
(define-derived-mode my-foo-mode prog-mode "My-Foo"
(setq-local font-lock-keywords t)
(setq-local syntax-propertize-function
(syntax-propertize-rules
((rx line-start (* whitespace) (group "//")) (1 "<"))
((rx (group "\n")) (1 ">")))))
That is all you need, but read on if you'd like to know more.
Explanation
This is based on #stefan's excellent solution which uses syntax-propertize-function to add to a syntax-table. While simpler isn't always better, #stefan's answer does more than what the original question asked for, so I've created this answer for people who only need a small hint or who just want to modify an existing mode.
It turns out directly manipulating a syntax table is unnecessary since the function syntax-propertize-rules makes it easy to map from regular expressions to syntax classes. For example, the syntax class < means "start of comment" and > means "end of comment". (See the Emacs lisp manual.)
I set font-lock-keywords to t as that is the minimum needed to enable syntax highlighting. If you are editing an existing mode, it likely already sets that variable and will not need to be changed.
And, finally, I use Emacs' rx function because it makes regular expressions sane in Lisp. (If you like Lisp, regular expressions, and sanity, I highly recommend using rx.)
About syntax-propertize-rules
I was going to link to the documentation for syntax-propertize-rules, but the Emacs manual does not (as of Emacs 28.1) even mention it. Until that gets remedied, I'll paste here the builtin documentation from C-hf:
syntax-propertize-rules is a Lisp macro in ‘syntax.el’.
(syntax-propertize-rules &rest RULES)
Probably introduced at or before Emacs version 24.1.
Make a function that applies RULES for use in
‘syntax-propertize-function’. The function will scan the buffer,
applying the rules where they match. The buffer is scanned a single
time, like "lex" would, rather than once per rule.
Each RULE can be a symbol, in which case that symbol’s value should
be, at macro-expansion time, a precompiled set of rules, as returned
by ‘syntax-propertize-precompile-rules’.
Otherwise, RULE should have the form (REGEXP HIGHLIGHT1 ...
HIGHLIGHTn), where REGEXP is an expression (evaluated at time of
macro-expansion) that returns a regexp, and where HIGHLIGHTs have the
form (NUMBER SYNTAX) which means to apply the property SYNTAX to the
chars matched by the subgroup NUMBER of the regular expression, if
NUMBER did match. SYNTAX is an expression that returns a value to
apply as ‘syntax-table’ property. Some expressions are handled
specially:
if SYNTAX is a string, then it is converted with ‘string-to-syntax’;
if SYNTAX has the form (prog1 EXP . EXPS) then the value returned by EXP will be applied to the buffer before running EXPS and if EXP is
a string it is also converted with ‘string-to-syntax’. The SYNTAX
expression is responsible to save the ‘match-data’ if needed for
subsequent HIGHLIGHTs. Also SYNTAX is free to move point, in which
case RULES may not be applied to some parts of the text or may be
applied several times to other parts.
Note: back-references in REGEXPs do not work.

Read from a file into a Emacs lisp list

I have the following file data.txt
A
B
C
D
I would like to read the contents of this file into a Lisp list, like
(defun read-list-from-file (fn)
(interactive)
(list "A" "B" "C" "D"))
(defun my-read ()
(interactive)
(let (( mylist (read-list-from-file "data.txt")))
(print mylist t)))
How can I modify read-list-from-file such that it returns the same list, but instead reads from the file given as input argument fn.. Each line in the file should be a separate item in the list..
This code:
(with-current-buffer
(find-file-noselect "~/data.txt")
(split-string
(save-restriction
(widen)
(buffer-substring-no-properties
(point-min)
(point-max)))
"\n" t))
UPD:
Here's a version with insert-file-contents:
(defun slurp (f)
(with-temp-buffer
(insert-file-contents f)
(buffer-substring-no-properties
(point-min)
(point-max))))
(split-string
(slurp "~/data.txt") "\n" t)
Much easier, than creating a temporary buffer, is to use f file manipulation library's f-read function that returns textual content of a file (default coding UTF-8).
f is a third-party that you need to install from MELPA, read Xah Lee's tutorial on how to use Emacs package management system. Then you could use either split-string or s string manipulation library's s-split (which is a simple wrapper around the former function):
(s-split "\n" (f-read "~/data.txt") t) ; ("A" "B" "C" "D")
Note: third parameter to s-split set to t omits empty strings. You could use s-lines, but as textual files on *nix systems usually contain a trailing newline, the returned list would be ("A" "B" "C" "D" ""). You can remove the last element with dash list manipulation library's butlast function, but this works in O(n), so probably stick to s-split, unless you want empty lines to be preserved in the list.

Find and replace without regexp in dired

I'm trying to convert a website into an ebook, and there is a huge chunk of html at the beginning of every page which I want to delete. As you can imagine, using Q results in no matches due to something in the big chunk not being escaped properly. When I try and regex the problem away, I get stack overflow.
What I really need is a way to find and replace in dired without regex, in the usual M-% way. Is this possible?
The regexp behaviour is optional in theory, but the function calls in question hard-code that assumption in a few places. I think the simplest solution is to make copies which don't set the regexp flags when they run.
(eval-after-load 'dired
'(define-key dired-mode-map (kbd "C-c Q") 'my-dired-do-query-replace))
(defun my-dired-do-query-replace (from to &optional delimited)
"Do `query-replace' of FROM with TO, on all marked files.
Third arg DELIMITED (prefix arg) means replace only word-delimited matches.
If you exit (\\[keyboard-quit], RET or q), you can resume the query replace
with the command \\[tags-loop-continue]."
(interactive
(let ((common
(query-replace-read-args
"Query replace in marked files" nil t)))
(list (nth 0 common) (nth 1 common) (nth 2 common))))
(require 'dired-aux)
(dolist (file (dired-get-marked-files nil nil 'dired-nondirectory-p))
(let ((buffer (get-file-buffer file)))
(if (and buffer (with-current-buffer buffer
buffer-read-only))
(error "File `%s' is visited read-only" file))))
(my-tags-query-replace
from to delimited '(dired-get-marked-files nil nil 'dired-nondirectory-p)))
(defun my-tags-query-replace (from to &optional delimited file-list-form)
"Do `query-replace' of FROM with TO on all files listed in tags table.
Third arg DELIMITED (prefix arg) means replace only word-delimited matches.
If you exit (\\[keyboard-quit], RET or q), you can resume the query replace
with the command \\[tags-loop-continue].
Fourth arg FILE-LIST-FORM non-nil means initialize the replacement loop.
Fifth and sixth arguments START and END are accepted, for compatibility
with `query-replace', and ignored.
If FILE-LIST-FORM is non-nil, it is a form to evaluate to
produce the list of files to search.
See also the documentation of the variable `tags-file-name'."
(interactive (query-replace-read-args "Tags query replace" nil t))
(require 'etags)
(setq tags-loop-scan `(let ,(unless (equal from (downcase from))
'((case-fold-search nil)))
(if (search-forward ',from nil t)
;; When we find a match, move back
;; to the beginning of it so perform-replace
;; will see it.
(goto-char (match-beginning 0))))
tags-loop-operate `(perform-replace ',from ',to t nil ',delimited
nil multi-query-replace-map))
(tags-loop-continue (or file-list-form t)))