Inserting a newline in a multiline zsh command pulled from history - zsh

Sometimes I use multiline commands in zsh:
❯ echo \
> a \
> multiline \
> command
When editing the command after pulling it from a history search, I can change the content of individual lines. However, I can't figure out how to insert another line:
# I want to insert another line after "multiline"...
❯ echo \
> a \
> multiline \ # but hitting <return> here just runs the command, even though there's a backslash at the end of the line
> command
How can I insert a newline in the middle of a multiline command pulled from history?

You can use ESC-Return.
FWIW, I tested it on Debian Jessie, zsh 5.0.7 and it works there.

You can use self-insert-unmeta to bind Alt+Return to insert a literal newline without accepting the command:
bindkey '^[^M' self-insert-unmeta
To use your example: Hitting Alt+Return at the cursor position (#)
% echo \
a \
multiline \#
command
will get you this:
% echo \
a \
multiline \
#
command
This works not only when editing history, but also when typing commands. So you can prepare several commands in a script like fashion and accept them with a single Return.
For example pressing Alt+Return instead of # in this example:
% echo command 1#
echo command 2#
echo command 3
will do the same thing as the command echo command 1; echo command 2; echo command 3 and produce this output:
command 1
command 2
command 3

(A summary of answers from https://unix.stackexchange.com/questions/6620/how-to-edit-command-line-in-full-screen-editor-in-zsh)
zsh comes with a function that can be used to open the current command line in your favorite editor. Add the following lines to your .zshrc:
autoload -z edit-command-line
zle -N edit-command-line
bindkey "^X^E" edit-command-line
The first line loads the function. The second line creates a new widget for the Z shell line editor (zle) from the function of the same name. The third line binds the widget to Control-X Control-E. If you use the vi bindings rather than emacs key bindings, use something like
bindkey -M vicmd v edit-command-line
instead (which binds the widget to v in vicmd mode).

If using bindkey -v mode, you can also use the default o/O commands from vicmd mode to add a newline line and enter insert mode in it, respectively above or below the current line.

Just to note, if you want to comment in a multiline command, you could use:
❯ echo `#first comment` \
a `#second comment` \
multiline \
command

CTRL + Enter (Return) for Windows/WSL
CTRL +X CTRL+E for Mac
Edited as per the comment below

Sounds like an appropriate place to use a shell script file instead no?
#!/bin/zsh
my
commands
here
I can even add a new line at a later time.

Related

How to set command line args with the space delimited contents of the first command line argument in zsh

I will be getting one command line argument in the script I'm writing which will itself be a space delimited list of the actual command line arguments. I'd like to set the arguments of the current script with these arguments. How might I accomplish that?
I'd like to use set -- but I'm not sure how this would work.
E.g.
Given arguments to my script: -a -b -c
echo $1 # prints "-a -b -c"
You can do this with set -- "${(z)1}". This will split $1 into words, handling quoting the same way the shell itself does:
% cat script.zsh
#!/usr/bin/env zsh
set -- "${(z)1}"
for arg; do
echo "==$arg=="
done
% ./script.zsh "-a -b -c -d'has spaces'"
==-a==
==-b==
==-c==
==-d'has spaces'==
If you also want to remove a level of quotes, use "${(#Q)${(z)1}}" instead.

Using bindkey to call a function in zsh requires pressing enter after function runs

I'm new to zsh and am trying to bind a key sequence to a function with the following in my .zshrc:
say_hello(){
echo "hello"
}
zle -N say_hello
bindkey '^Y' say_hello
Pressing Ctrl-Y will call the function and I'll see "hello" printed to the terminal but after I need to press Enter again before I'm given another zsh prompt. Calling the function by just typing in say_hello at the zsh prompt and pressing Enter does what I want - I see hello printed and then I'm given another zsh prompt. How can I get this behavior when binding the function to a key sequence?
Above is a simple example, really the function I'm trying to write is below:
my_cd() {
if [[ "$#" -ne 0 ]]; then
cd $(autojump $#)
return
fi
dir_to_cd_to=$(fasd_cd -dl | fzf --height 40% --reverse --inline-info)
# above isn't so important - dir_to_cd_to could be obtained in any way
cd "$dir_to_cd_to"
}
zle -N my_cd
bindkey -v '^Y' 'my_cd'
To display messages in a zle widget, you're supposed to use zle -M rather than echo. echo will output your message at whatever the current cursor position is which isn't especially helpful. If you really want to use echo, calling zle reset-prompt afterwards will redraw a fresh prompt. If you don't want a potential mess in your terminal, consider starting with \r to move the cursor to the beginning of the line and ending with $termcap[ce] to clear to the end of the line.

zscroll text in the tmux status bar

I am trying to configure my .tmux.conf to display a scrolling string of text. For what it is worth, the scrolling text is an aviation weather observation that is scraped using a really ugly bash script that is defined in .zshrc as follows:
function scrollMetar {
curl -s "https://www.aviationweather.gov/metar/data?ids=kjyo&format=raw&date=&hours=0" | awk '/Data\ starts\ here/{getline; print}' | html2text | zscroll -l 14 -n 0
}
I want to take the scrolling output from the scrollMetar command and have that scroll on the tmux status line.
My current .tmux.conf section looks like this:
set-option -g status-left "\
#[fg=colour7, bg=colour241]#{?client_prefix,#[bg=colour167],} ❐ #S \
#[fg=colour241, bg=colour237] \
#(echo 'TEST TEXT') \
#{?window_zoomed_flag, 🔍,} "
Where the echo 'TEST TEXT' is should be where the scrollMetar would go, but when inserted doesn't output anything.
I am guessing that this maybe a limitation of tmux, but I would be grateful for any advice and I am completely open to alternate implementations.
Okay, so it seems like .tmux.conf did not like calling the function, or could not find it. Putting the function in a executable shell script fixed the problem.

Insert first word of previous command in ZSH command line

In zsh (and bash), I can do ALT+. to insert the last argument of the previous command into the command line.
Is there something similar for the first word?
Example: After running /bin/ls -l /tmp, I want it to insert /bin/ls.
found this in a .zshrc
insert-first-word () { zle insert-last-word -- -1 1 }
zle -N insert-first-word
bindkey '^[_' insert-first-word
then alt + _
I found Alt+number+dot and Alt+comma in Zsh and Bash which basically answers my question:
In zsh, you can do ALT+0+.
It is a bit unwieldy to press three buttons for this, but it is built-in and works.

How can I send selected text (or a line) in TextMate2 to R running on Terminal

As you know version 2 of TextMate is on the way and the current development version is very promising: https://github.com/textmate/textmate/blob/master/README.md
In my case I am using R in terminal (MacOSX Mountain Lion) and I develop my code with TextMate2. With the previous version of TextMate (1.5.11) I used the following trick to send selected text or lines to my terminal window:
-> See How can I send selected text (or a line) in TextMate to R running on Terminal
This trick worked perfectly for me but I cannot figure out how to get a similar behaviour with TextMate2.
Any idea? I thank you in advance for your precious help.
Actually based on a previous answer (How can I send selected text (or a line) in TextMate to R running on Terminal), I created my own Bundle in TextMate 2 using the following code:
#!/bin/bash
source "$TM_SUPPORT_PATH/lib/bash_init.sh" # might not be necessary
# input is selection or document
rawText="$(cat | sed 's/ / /g;')"
osascript -e 'on run(theCode)' \
-e ' tell application "Terminal"' \
-e ' do script theCode in window 1' \
-e ' end tell' \
-e 'end run' -- "$rawText"
open "txmt://open?line=$(($TM_LINE_NUMBER+1))&column=1000000" &
See the screenchot below.
The only problem is that when you select a chunk of text, the cursor is jumping to the first line of the document, which is a very irritating behaviour. Changing 'Input" from 'Line' to 'Selection' doe not solve the issue.
Any thoughts?
This works for me, and it correctly goes to the end of the selection.
I just added the osascript part in the previous answer, and put it in the code that was in the original R bundle written by Hans-Jörg Bibiko.
Set "scope selector" to "source.r" and "output" to "discard".
Set "Input" to "line" and it does what I need: send the line if nothing is selected, and send the selection otherwise.
edit: it works perfectly with selections, but not with line. When you do not select text it just re-runs everything from the top of the file
edit2: solved, I wrote to Hans-Jörg Bibiko and he pointed me to the "Input" selection.
#!/usr/bin/env bash
# input is selection or document
rawText="$(cat | sed 's/ / /g;')"
curDir=''
if [[ ${#TM_DIRECTORY} -gt 0 ]]; then
curDir="$TM_DIRECTORY"
fi
osascript -e 'on run(theCode)' \
-e ' tell application "Terminal"' \
-e ' do script theCode in window 1' \
-e ' end tell' \
-e 'end run' -- "$rawText"
if [ "$TM_LINE_NUMBER" != "" ]; then
"$TM_MATE" -l "$(($TM_LINE_NUMBER+1)):1000000"
elif [[ $TM_SELECTION =~ [1-9][0-9]*:?[0-9]*-([1-9][0-9]*):?[0-9]* ]]; then
# Regular Selection
"$TM_MATE" -l "$((${BASH_REMATCH[1]}+1)):1000000"
elif [[ $TM_SELECTION =~ [1-9][0-9]*:?[0-9]*x([1-9][0-9]*):?[0-9]* ]]; then
# Block (option) selection
"$TM_MATE" -l "$((${BASH_REMATCH[1]}+1)):1000000"
else
"$TM_MATE"
fi
A bit of an indirect answer : I use the R bundle in Textmate 2 (which also worked in Textmate 1). Just select the lines you want to run and "Send selection to / R App" ( I have it binded to command-R but I'm not sure if it's the original binding)
First time it opens the R app and execute code. Subsequent times it just pastes the code and run it.
It's not exactly "send to terminal" but still works
I got it working with a few changes on bhaibeka's answer. Apparently $TM_LINE_NUMBER is empty and that makes the cursor jump to the first line of the document. By getting rid of the last line, it solves part of the problem.
#!/bin/bash
[[ -f "${TM_SUPPORT_PATH}/lib/bash_init.sh" ]] && . "${TM_SUPPORT_PATH}/lib/bash_init.sh"
rawText="`cat`"
osascript -e 'on run(theCode)' \
-e ' tell application "Terminal"' \
-e ' do script theCode in window 1' \
-e ' end tell' \
-e 'end run' -- "$rawText" > /dev/null
The other problem is how to move the cursor to the end of the selection. I made it work by inserting the 'empty' output at the end of the selection (on the right panel: Output:"Insert After Input" and Format: "Text"). Probably this is not the most elegant way to do it but it works.

Resources