I am using Spacemacs to write a program in Clojure. I would like to be able to collapse docstrings. I've tried selecting the docstring and pressing z a, however, that ends up collapsing the entire function body.
Specifically, I would like to be able to turn this:
(defn flip-and-vectorize
"Returns a vector with the arguments flipped so that
`(flip-and-vectorize 1 2)`
returns the following vector
`[2 1]`"
[a b]
[b a])
into something akin to this
(defn flip-and-vectorize
"..."
[a b]
[b a])
Edit:
Even being able to collapse arbitrary lines would be acceptable; meaning the collapsed version of the above function could look something like this:
(defn flip-and-vectorize
...
[a b]
[b a])
This would mean that the collapsing logic would not need to understand what a "docstring" was, but would merely collapse the selected lines.
There are two options:
https://github.com/magnars/fold-this.el
https://github.com/mrkkrp/vimish-fold
Both these packages support folding regions. As in, it can fold whatever is selected. The difference is the latter has more features. Especially the ability to remember folds even if you close and re-open a buffer.
To use them, follow the instructions on their github page. But, when it says to run package-install, instead in spacemacs you want to open your .spacemacs with SPC f e d and add the package to your dotspacemacs-additional-packages as such:
dotspacemacs-additional-packages '(fold-this)
Fold-this is more straightforward to setup, because it has less features.
In either case, you'll need to add your own keybindings. Or just select the region to fold, and go SPC SPC fold-this.
If using vimish-fold, you need to select the region to fold, and call SPC SPC vimish-fold to fold, and then you can use C-` thereafter to toggle the fold on and off. Or call SPC SPC vimish-fold-delete to remove the fold.
Related
On high level: I'm trying to create an association list in which value is a result of function execution. What I get instead is an expression which represents that function, and which needs to be wrapped into "eval" to get it to work. I'm trying to understand why, and what makes the behavior different from regular lists.
In more details:
I'm putting together a configuration for org agenda which has a common functionality for all the environments, but then I want to make it possible for a specific environment to add something extra. So, I know that on all the machines org-agenda-files need to include these two dirs: "~/Documents/Org" and "~/Downloads/Org" but I want to let a specific machine to register more dirs in addition to those two and which would only be visible to that machine.
So I build an association list in which machine name is the key, and the value is a list of dirs that need to be handled on that node in addition to those shared by all.
The code looks like this:
;; default-agenda-files are shared by all the environments
(setq default-agenda-files
'("~/Documents/Org" "~/Downloads/Org"))
;; in addition to default, I want to register project-abc
;; dirs for nodeABC and project-xyz dirs for nodeXYZ
(setq per-node-agenda-file-mappings
'(("nodeABC" . (append default-agenda-files
'("~/Projects/project-abc/doc/"
"~/Projects/project-abc/notes")))
'("nodeXYZ" . (append default-agenda-files
'("~/Projects/project-xyz/doc"
"~/Projects/project-xyz/notes")))))
The code further sets org-agenda-files depending on the machine name.
Here's the problem. If I do
(alist-get "nodeABC" per-node-agenda-file-mappings nil nil 'string-equal)
I get
(append default-agenda-files '("~/Projects/project-abc/doc/" "~/Projects/project-abc/notes"))
rather than
("~/Documents/Org" "~/Downloads/Org" "~/Projects/project-abc/doc/" "~/Projects/project-abc/notes")
I can solve it by doing
(eval (alist-get "nodeABC" per-node-agenda-file-mappings nil nil 'string-equal))
Then everything works.
But I'm trying to understand what's going on there and why same thing doesn't happen with regular lists (with regular lists, evaluation does happen at the time of assignment). Is there a way to make evaluation happen at the time of assignment in this case too? I double-checked, same thing happens if I use hash-table instead of association list.
There's nothing different about using alists from other lists. What you want here is to evaluate some things and not evaluate others. Just do that: quote only the things you don't want evaluated.
You don't need to explicitly invoke eval - Lisp already invokes it implicitly. All you need to do is not evaluate things that you want to treat as data. Here, that means strings (those are constant anyway, so evaluating them makes no difference) and any lists that you want to be just, well, lists - e.g., '("~/Projects/project-abc/doc/" "~/Projects/project-abc/notes").
You want to use backquote instead of quote, and use comma before the (append...) sexp:
(setq per-node-agenda-file-mappings
`(("nodeABC" . ,(append default-agenda-files
'("~/Projects/project-abc/doc/"
"~/Projects/project-abc/notes")))
("nodeXYZ" . ,(append default-agenda-files
'("~/Projects/project-xyz/doc"
"~/Projects/project-xyz/notes")))))
Or this:
(setq per-node-agenda-file-mappings
`(("nodeABC" ,#(append default-agenda-files
'("~/Projects/project-abc/doc/"
"~/Projects/project-abc/notes")))
("nodeXYZ" ,#(append default-agenda-files
'("~/Projects/project-xyz/doc"
"~/Projects/project-xyz/notes")))))
Or this:
(setq per-node-agenda-file-mappings
(list (cons "nodeABC" (append default-agenda-files
'("~/Projects/project-abc/doc/"
"~/Projects/project-abc/notes")))
(cons "nodeXYZ" (append default-agenda-files
'("~/Projects/project-xyz/doc"
"~/Projects/project-xyz/notes")))))
Or just this, since the only thing you need to evaluate is variable default-agenda-files:
(setq per-node-agenda-file-mappings
`(("nodeABC" ,#default-agenda-files
"~/Projects/project-abc/doc/" "~/Projects/project-abc/notes")
("nodeXYZ" ,#default-agenda-files
"~/Projects/project-xyz/doc" "~/Projects/project-xyz/notes")))
See the Elisp manual, node Backquote.
Here's some code I wrote, using clojure.core.match, which performs a pretty common programmng task. A function takes some "commands" (or "objects", "records", or whatever you prefer to call them), has to do something different with each type, and has to destructure them to figure out exactly what to do, and different command types might have to be destructured differently:
(defn action->edits [g action]
"Returns vector of edits needed to perform action in graph g."
(match action
[:boost from to]
[[:add-edge from to 1.0]]
[:retract from to]
[[:remove-edge from to]]
[:normalize from to] ; a change has just been made to from->to
(map (fn [that] [:remove-edge from that])
(successors-except g from to))
[:recip-normalize to from] ; a change has just been made to from->to
[]
[:reduce-to-unofficial from to competitor]
[[:remove-edge from to] (make-competitive-edge from competitor]))
I'm mostly imitating the way people commonly use the pmatch macro in Scheme. I'd like to know what's the idiomatic way to do this in Clojure.
Here's what I like about the above code:
It's very readable.
It was effortless to write.
Here's what I don't like:
Accessing the from and to fields from anywhere but inside a match macro is extremely unreadable and error-prone. For example, to extract the from element from most of the action vectors, you write (action 1). That code will break if I ever add a new action, and it breaks right now on :recip-normalize.
The code generated by match is inefficient: it searches by repeatedly throwing and catching exceptions. It doesn't just generate a big nested if.
I experimented a little with representing the commands as maps, but it seemed to get verbose, and the name of the command doesn't stand out well, greatly reducing readability:
(match action
{:action :boost :from from :to to}
[{:edit :add-edge :from from :to to :weight 1.0}]
{:action :retract :from from :to to}
[{:edit :remove-edge :from from :to to}]
. . .)
Probably future versions of match will generate better code, but the poor code generated now (and lack of support for records) suggests that in Clojure, people have been handling this kind of thing happily for years without match. So how do you do this kind of thing in Clojure?
I would utilize clojure's build-in destructuring facilities, since I do not see a requirement for core.match here - but I might be missing something.
For example:
(defn action->edits [g [action from to]]
(condp = action
:boost "boosting"
:retract "retracting"
:normalize-ksp-style (recur g [:boost from to])
nil))
(action->edits 2 [:normalize-ksp-style 1 2])
;=> "boosting"
Since version 22 of Emacs, we can use \,(function) for manipualting (parts of) the regex-search result before replacing it. But – this is mentioned often, but nonetheless still the truth – we can use this construct only in the standard interactive way. (Interactive like: By pressing C-M-% or calling query-replace-regexp with M-x.)
As an example:
If we have
[Foo Bar 1900]
and want to get
[Foo Bar \function{foo1900}{1900}]
we can use:
M-x query-replace-regexp <return>
\[\([A-Za-z-]+\)\([^0-9]*\) \([0-9]\{4\}\)\]
[\1\2 \\function{\,(downcase \1)\3}{\3}]
to get it done. So this can be done pretty easy.
In my own defun, I can use query only by replacing without freely modifying the match, or modify the prepared replaced string without any querying. The only way I see, is to serialize it in such a way:
(defun form-to-function ()
(interactive)
(goto-char (point-min))
(while (query-replace-regexp
"\\[\\([A-Za-z-]+\\)\\([^0-9]*\\) \\([0-9]\\{4\\}\\)\\]"
"[\\1\\2 \\\\function{\\1\\3}{\\3}]" ))
(goto-char (point-min))
(while (search-forward-regexp "\\([a-z0-9]\\)" nil t)
(replace-match (downcase (match-string 1)) t nil)
)
)
For me the query is important, because I can't be sure, what the buffer offers me (= I can't be sure, the author used this kind of string always in the same manner).
I want to use an elisp function, because it is not the only recurring replacement (and also not only one buffer (I know about dired-do-query-replace-regexp but I prefer working buffer-by-buffer with replace-defuns)).
At first I thought I only miss something like a query-replace-match to use instead of replace-match. But I fear, I am also missing the easy and flexible way of rearrange the string the the query-replace-regexp.
So I think, I need a \, for use in an defun. And I really wonder, if I am the only one, who is missing this feature.
If you want your rsearch&replace to prompt the user, that means you want it to be interactive, so it's perfectly OK to call query-replace-regexp (even if the byte-compiler will tell you that this is meant for interactive use only). If the warning bothers you, you can either wrap the call in with-no-warnings or call perform-replace instead.
The docstring of perform-replace sadly doesn't (or rather "didn't" until today) say what is the format of the replacements argument, but you can see it in the function's code:
;; REPLACEMENTS is either a string, a list of strings, or a cons cell
;; containing a function and its first argument. The function is
;; called to generate each replacement like this:
;; (funcall (car replacements) (cdr replacements) replace-count)
;; It must return a string.
The query-replace-function can handle replacement not only as a string, but as a list including the manipulating elements. The use of concat archives building an string from various elements.
So one who wants to manipulate the search match by a function before inserting the replacement can use query-replace-regexp also in a defun.
(defun form-to-function ()
(interactive)
(goto-char (point-min))
(query-replace-regexp
"\\[\\([A-Za-z-]+\\)\\([^0-9]*\\) \\([0-9]\\{4\\}\\)\\]"
(quote (replace-eval-replacement concat "[\\1\\2 \\\\function{"
(replace-quote (downcase (match-string 1))) "\\3}{\\3}]")) nil ))
match-string 1 returns the first expression of our regexp-search.
`replace-quote' helps us doublequoting the following expression.
concat forms a string from the following elements.
and
replace-eval-replacement is not documented.
Why it is in use here nevertheless, is because of emacs seems to use it internally, while performing the first »interactive« query-replace-regexp call. At least is it given by asking emacs with repeat-complex-command.
I came across repeat-complex-command (bound to [C-x M-:].) while searching for an answer in the source code of query-replace-regexp.
So an easy to create defun could be archieved by performing the standard search and replace way as told in the question and after first sucess pressing [C-x M-:] results in an already Lisp formed command, which can be copied and pasted in a defun.
Edit (perform-replace)
As Stefan mentioned, one can use perform-replace to avoid using query-replace-regexp.
Such a function could be:
(defun form-to-function ()
(interactive)
(goto-char (point-min))
(while (perform-replace
"\\[\\([A-Za-z-]+\\)\\([^0-9]*\\) \\([0-9]\\{4\\}\\)\\]"
(quote (replace-eval-replacement concat "[\\1\\2 \\\\function{"
(replace-quote (downcase (match-string 1))) "\\3}{\\3}]"))
t t nil)))
The first boolean (t) is a query flag, the second is the regexp switch. So it works also perfectly, but it didn't help finding the replacement expression as easy as in using \,.
Is it somehow possible to highlight all occurrences of a function and (jump to) its definition in the Clojure-editor LightTable?
If I for example have the following code
(defn a [x] (* x x))
(str "abc" (a 4))
;; ... some more code ...
(a 2)
When I'm with my cursor at one of the a's, I'd like to have all a's that are related to the function beeing highlighted. So the a in the String abc should not be highlighted.
It would be also nice to jump directly to the point where a is defined. (I tried Strg + ., but that did not work)
The highlighting functionality is implemented through a plugin, just go to "Plugin Manager" and search for match-highlighter. If you don't like the style you just need to fork the project and tweak the CSS
If you fancy to collaborate:
https://github.com/Gozala/lt.plugins.match-highlighter
The jump to definition hasn't being implemented yet afaik, keep an eye on the list of plugins if you fancy something else
https://github.com/LightTable/plugin-metadata
I've been trying to get a simple reg-ex working in Clojure to test a string for some SQL reserved words (select, from, where etc.) but just can't get it to work:
(defn areserved? [c]
(re-find #"select|from|where|order by|group by" c))
(I split a string by spaces then go over all the words)
Help would be greatly appreciated,
Thanks!
EDIT: My first goal (after only reading some examples and basic Clojure materials) is to parse a string and return for each part of it (i.e. words) what "job" they have in the statement (a reserved word, a string etc.).
What I have so far:
(use '[clojure.string :only (join split)])
(defn isdigit? [c]
(re-find #"[0-9]" c))
(defn isletter? [c]
(re-find #"[a-zA-Z]" c))
(defn issymbol? [c]
(re-find #"[\(\)\[\]!\.+-><=\?*]" c))
(defn isstring? [c]
(re-find #"[\"']" c))
(defn areserved? [c]
(if (re-find #"select|from|where|order by|group by" c)
true
false))
(defn what-is [token]
(let [c (subs token 0 1)]
(cond
(isletter? c) :word
(areserved? c) :reserved
(isdigit? c) :number
(issymbol? c) :symbol
(isstring? c) :string)))
(defn checkr [token]
{:token token
:type (what-is token)})
(defn goparse [sql-str]
(map checkr (.split sql-str " ")))
Thanks for all the help guys! it's great to see so much support for such a relatively new language (at least for me :) )
I'm not entirely sure what you want exactly, but here's a couple of variations to coerce your first regex match to a boolean:
(defn areserved? [c]
(string?
(re-find #"select|from|where|order by|group by"c)))
(defn areserved? [c]
(if (re-find #"select|from|where|order by|group by"c)
true
false))
UPDATE in response to question edit:
Thanks for posting more code. Unfortunately there are a number of issues here that we could
try to address by patching your existing code in a simplistic and naïve fashion, but it will
only get you so far, before you hit the next problem with this single iteration approach.
#alex is correct, that your areserved? method will fail to match order by if you have already
split your string by white space. That said, a simple fix is to treat order and by as separate keywords (which they are, even though they always appear together).
The next issue is that the areserved? function will match keywords in a string, but you are dispatching it against a character in the what-is function. You nearly always get a match in your cond for isletter?, so you will everything is marked as a 'word'.
All in all, it looks like you are trying to do too much work in a single application of map.
I'm not sure if you are just doing this for fun to play with Clojure (which is admirable - keep going!), in which case, maybe it doesn't matter if you press on with this simple parsing approach... you'll definitely learn something; but if you would like to take it further and parse SQL more successfully, then I would suggest that you may find it helpful to to read a little on Lexing, Parsing and building Abstract Syntax Trees (AST).
Brian Carper has written about using the Java parser generator "ANTLR" from Clojure - it's a few years old, but might be worth looking at.
You also might be able to get some transferrable ideas from this chapter from the F# programming book on lexing and parsing SQL.