Structured Haskell Mode [SHM]: How to delete single bracket or string quote by <backspace> or <del>? - haskell

Suppose I want to remove parentheses around some expression, e.g. I want to unwrap (not True).
Pressing <backspace> with the cursor standing right after closing parenthesis does not delete it, but the cursor jumps inside the parentheses (runs shm/del).
Pressing <delete> when the cursor is placed over opening parenthesis deletes everything inside, including the parentheses themselves (runs delete-char).
The same goes with string double quote delimiters, curly brackets, square brackets, and I believe something else.
Is there some predefined function to unwrap expression in SHM? Or should I override this behaviour with some manual key bindings/actions?

structured-haskell-mode is very heavily influenced by paredit, which is where it gets the basic movement and editing bindings from. You can see what commands paredit provides either by looking it up in the Emacs help system (easiest way: open up a lisp buffer, enable paredit, C-h m) or using this handy cheatsheet (which looks reasonable but I haven't read).
Also, this video introduction to paredit for Lisp is probably worth watching.
For your specific question: M-s deletes the current level of nesting. In paredit this works for parentheses, brackets or quotes, but it looks like shm only supports parentheses at the moment. You can invoke it anywhere inside an expression:
((a |b c) d e f)
M-s
(a b c d e f)
EDIT: As #ReinHenrichs pointed out, you have to expand the selection to the whole node using M-a before using M-s to delete the surrounding parentheses, which is why it wasn't working for me earlier.
In the meantime, a decent workaround would be to rebind DEL to something like haskell-indentation-delete-backward-char (or whatever is appropriate for your setup) so that you can delete structural characters as before. From a bit of experimenation, shm seems to gracefully degrade for code that doesn't parse properly, so this isn't too inconvenient.

Put this keybinding into your emacs configuration file:
(global-set-key (kbd "C-c C-d") 'delete-pair)
Now you can place the point before the opening part of the pair (parentheses, square brackets, quotes etc.) or use M-a in SHM to go to the next outer pair and press C-c C-d to delete the pair.

Related

Insert visual vim selection in the line below at the same position

I have a tedious task of converting chords from leadsheets into an inline format.
The format I have
G A
Never gonna give you up
F#m Bm
Never gonna let you down
G A F# Bm
Never gonna run around and desert you
And the format I need would b
Never gonna g[G]ive you [A]up
Never gonna l[F#m]et you do[Bm]wn
Never gonna r[G]un a[A]round and d[F#]esert [Bm]you
The problem with a script in Ruby or Python is, that the format isn't very consistent. So there could be newlines, weird characters, sometimes the chords are seperated with a dash... I haven't looked through all of the files, but I suspect more malformed stuff.
So I thought, if I have to go through of every leadsheet by hand, at least I can save some time using vim. This is the mapping I've come up with so far:
nmap <C-X> viWc[]<ESC>PviWx<down>p
Select the chord: viW
delete it and go into insert mode: c
add brackets
exit insert mode: <ESC>
paste the register to the left: P
select the chord again, including the brackets: viW
delete the selection: x
move cursor down, and paste it: p
But I few things I can't figure out is:
Sometimes the chords already have brackets, then I don't want to surround them with more brackets. Any idea how to only add brackets, if the chord isn't already surrounded with them?
It would be cool to do this with whole lines of chords. Sometimes there are multiple chords on the same line, and selecting them one by one is tedious. Any idea on how to operate the mapping above on a whole line?
well point 1 could be solved by adding the brackets around the chords on every second line:
:g/^/s/\[\=\zs[a-zA-Z#]\+\ze]\=/\[\0\]/g|+t+|-d
Credits: https://vi.stackexchange.com/questions/34785/search-and-replace-on-odd-even-numbered-lines-using-g
This however sucks, because it moves arround the chords, so we have to remove all brackets first and replace them with space
:%s/\[\([a-zA-Z#]\+\)\]/\1 /g
Then we can do the first line again, but remove some space too. Since there are no brackets left, it gets simpler (Note we use other brackets to get ride of some side effect the following code has):
:g/^/s/\([a-zA-Z#]\+\) \{0,3\}/\{\1\} /g|+t+|-d
Also we add a trailing whitespace at the end of the line so that the df} command will not move the cursor to a wrong place
Now that we have curly brackets everywhere, we can use reverse search with ?{ and then create a macro that jumps from results to result and moves it down, replaces the curly brackets with normal brackets and then calls itself (recursive macro):
gg0
qqq
?{<CR>
qq
df}jPr]F{r[n#qq
#q
And nearly all should be done.
The result:
Never gonna g[G]ive you [A]up
Never gonna l[F#m]et you dow[Bm]n
Never gonna r[G]un a[A]round and d[F#]esert [Bm]you
Note, we have to search backwards (? instead of /) so we can delete the chords directly.
If you have problems understanding what I did, feel free to ask.

vim: replace block

Let's say I have this text:
something "something else"
something here "just another quoted block"
I want to substitute "something else" with "just another quoted block", so I do:
/quot<enter> (to jump to second quoted block searching for the string "quot")
yi" (to yank inner text for current quoted block)
?else<enter> (to jump back to the first quoted block wich contains "else")
vi" (to visually select the quoted block)
p (to paste yanked text)
This works, but I would like to know if the two last steps can be replaced by a single one, to avoid visual mode. I know it's not a huge gain keystroke-wise, but I think that the Vim philosophy would encourage what I'm trying to do, and every time I do this my mind keeps asking for this command. :-P
What I tried so far:
r (replace) replaces just one character
c (change) throws me into Insert mode and does not let me paste the text.
"_di"P
Delete inside quotes to the blackhole register; paste last yanked before cursor.
Or
ci"<Ctrl-R>0<ESC>
Change inside quotes to retrieve last yank; leave insert mode.
With my ReplaceWithRegister plugin, the last two steps would be gri". It also offers grr (replace current / [count] lines); though it only saves a little typing, I find this indispensable.
Key stroke wise, j$yi"k then vi"p is actually probably the fastest. However, if you absolutely must go into insert mode you can j$yi"k then "_ci"<C-r>" or ci"<C-r>0. The :help i_CTRL-R operator allows you to put the contents of a register into insert mode.
I usually try to keep it simple, using what I feel is more intuitive with every day commands:
j
yi"
k
ci"
<ESC>
p

Remove command with matching braces

I'm using (mac)vim with tex-suite and would like to have a single regex command (or any other way) to do the following thing:
Change
\textcolor{green}{some random text}
into
some random text
This should be done for all occurrences of \textcolor{green}{} in my tex file...
Any idea?
EDIT: I need it to recognize matching braces. Here an example :
\textcolor{green}{
with $v_\text{F}\sim10^6$m.s$^{-1}$ the massless Dirac fermions
velocity in pristine graphene}.
In my experience, things like this most often crop up during editing, and you might have the search for \textcolor{green}{ already highlighted.
In such a scenario, :global is usually my weapon of choice:
:g//norm d%diBvaBp
diBvaBp: diB (delete inner block), vaB (select block), p (put)
If you have surround.vim installed (recommend it!) you could remove the pair of braces simply doing dsB (delete surrounding {})
:g//norm d%dsB
Of course, you can combine it like
:g/\\textcolor{green}{/norm d%dsB
I just noted a potential issue when the target patterns don't start at the beginning of a line. The simplest way to get around that is
:g//norm nNd%diBvaBp
A more involved way (possibly less efficient) would be using a macro:
/\\textcolor{green}{
gg
qqd%diBvaBpnq
Followed by something like 100#q to repeat the macro
:%s,\\textcolor{green}{\([^}]\+\)},\1,g
Updated as per your updated question:
:%s,\\textcolor{green},\r-HUUHAA-&,g
:g/\\textcolor{green}/normal 0f\df}lvi{xhP$xx
:%s/\n-HUUHAA-//
Quick explanation of how it works:
Put all \textcolor{green} lines onto a line of their own, with 'special' marker -HUUHAA-
Use visual selection vi{ to select everything in between the {}, paste it outside and delete the now empty {}.
Delete leftover stuff including the marker.

goto definition (gd) while inside function call

If I am editing a function call like:
many_params(param1, "a long...string", p|aram3); // Cursor located at |
Then typing gd will jump to the definition of param3, not many_params. What is the most efficient idiom for moving the cursor to many_params, without using ^.
To clarify, please do not answer with ^, because I want an idiom that is also compatible with:
// Lets jump to func3's definition
func1(func2(123, "aaaaa"), func3("bbbbb", 3|, 4, 5));
The only idea I have is to:
go to the left opening parenthesis - F(
move one word backwards - b
so the command is F(b. However it won't work with text like func1("abc(d", 222|2)
It feels a bit dirty to me, but you can handle nested parentheses as well with ya(h. Note that this yanks the parenthesized bits to your yank buffer, which may not be desirable, but you would work around that by mapping to "_ya(h. This will go to the open paren of the enclosing parens, and subsequent executions of it will go to subsequent levels of parenthesization. I don't know of another way to get to the enclosing parentheses without being interrupted by intervening parentheticals.
If you're using this, you need to use the h instead of b in case you have multiple parentheses in a row. ...but then again, maybe the behavior with b would be desirable.

Command to surround a character with spaces in vim

I am trying to use vim properly - to aid me I've mapped my arrow keys to "" so that I am forced to use {hjlk} to move around.
This is causing me a problem when I want to just surround a character with spaces, eg:
"2+3" is better formatted "2 + 3"
Previously I would have put my cursor over the + and typed:
i[space][arrow-right][space][Esc]
That's 5 presses.
To do this without the arrow I seem to need to put the cursor over the + and go:
i[space][Esc]lli[space][Esc]
That's 8 presses.
I can convert the "li" into an "a" which reduces it to 7 presses:
i[space][Esc]la[space][Esc]
Short of writing this into a macro is there a better way of doing it? Is there some magic vim command which will allow me to do it in less than even 5 presses - and some way to generalise it so that I can do it to entire words or symbols, eg if I want to convert 3==4 to 3 == 4?
Personally, I think it makes most sense to destroy what you want to surround, and then repaste it.
c w "" ESC P
Obviously, you can replace both the object and the quotes with whatever you like. To change just one character + to be [space]+[space], you would do
s [space] [space] ESC P
on the +
The first thing that jumps to mind after reading just the title is surround.vim which is an excellent script to do all kinds of useful things along the lines of what you've described.
To solve your specific problem, I would probably position the cursor on the + and:
s[space]+[space][esc]
To change 3==4 into 3 == 4, I might position the cursor on the first =, and:
i[space][esc]ww.
i have been wondering about this as well. i tried with surround.vim, but the naive approach
S<space>
(after making a visual selection) does not work since the space is already taken up as a modifier for adding space to other surrounding character pairs. S<space><cr> adds a ^M in the output. Ss almost works but inserts a space only before.
after asking at tpope/surround.vim on github:
S<space><space>
in visual mode works. alternatively, from normal mode, ysl<space><space> works for a single character
Hah! I've been trying to figure out how to surround a block in spaces for quite a while and I finally found the right combination.
Using surround.vim you say surround selector space space.
So for this specific case I would use visual mode (a good trick for operating on single characters under the cursor BTW) thus: "vs " <- four key presses!
I also have a habit of typing things like argument lists without spaces. With this technique you can just navigate to the second argument using w and say "vws " to visually select a word and surround with spaces.
I prefer visual select mode generally. Also the alternate surround syntax "ysw " excludes the word final comma that is caught by "vw".
You could create a macro with one of the described actions and call it everytime you need it (Like amphetamachine proposed while I was writing) or you could simply search & replace:
:%s/\(\d\)\(+\|-\)\(\d\)/\1 \2 \3/g
You probably have to execute this command two times because it will only find every second occurence of +/-.
EDIT:
This will replace everything without the need to be called twice:
:%s/\d\#<=+\|-\d\#=/ \0 /g
Try positioning your cursor over the '+' and typing this:
q1i[space][right arrow][space][left arrow][esc]q
This will record a quick macro in slot 1 that you can re-use whenever you feel like it, that will surround the character under the cursor with spaces. You can re-call it with #1.
There is also the more versatile one:
q1ea[space][esc]bi[space][right arrow][esc]q
Which will surround the word under the cursor ("==" counts as a word) with spaces when you hit #1.
You could set up a mapping like this (press enter in visual mode to wrap spaces):
:vnoremap <CR> <ESC>`<i<SPACE><ESC>`>la<SPACE><ESC>h
This method allows you to use . to repeat the command at the next +.
Put your cursor over the + and type:
s[SPACE][CTRL-R]"[SPACE][ESC]
I know this is and old thread, but this might be useful to someone. I've found that the map (map it to anything else you want!)
noremap <leader>ss diwi<SPACE><C-R>"<SPACE><ESC>B
works ok both for turning 'a+b' into 'a + b' (when used over the '+' char) and for turning 'a==b' into 'a == b' (when used over either the first or the second '=' sign).
I hope it's useful to someone.

Resources