GNU screenで継続行をコピーする際に、`\`等を除く方法

Emacs等で、折り返して表示されている長いURLをコピーしたい場合、
折り返しを示す`\`を手で消す必要があって面倒だったので、
screenでコピーした文字列に対して、`\`を消して行を連結するフィルタをかけて、
ペーストしてそのまま使える形にする方法です。

(ペースト後の編集が容易なエディタ以外の、)
シェルプロンプトやlynx/w3mにペーストして、
編集無しにそのまま使う用途を主に想定しています。

継続行以外にも、以下のようなフィルタも便利だと思います。

* エディタ起動
* 改行を削除
* 改行をスペースに置換
* インデントを削除
* grep出力のファイル名だけ抽出
* URL抽出
* HTMLタグ除去
* 引用記号`> `を追加

### .screenrc
screenからは、`C-a g e`や`C-a g +`等で各フィルタを選んで実行します。

```:.screenrc
bind g command -c buffilt
bind -c buffilt \\ eval writebuf screen 'stuff "scrbuf.sh rmwrap-emacs&&exit^M"'
bind -c buffilt > eval writebuf screen 'stuff "scrbuf.sh rmwrap-vim&&exit^M"'
bind -c buffilt + eval writebuf screen 'stuff "scrbuf.sh rmwrap-mutt&&exit^M"'
bind -c buffilt ^E eval writebuf screen 'stuff "scrbuf.sh rmwrap-width&&exit^M"'
bind -c buffilt $ eval writebuf screen 'stuff "scrbuf.sh picklastarg&&exit^M"'
bind -c buffilt ' ' eval writebuf screen 'stuff "scrbuf.sh join-with-blank&&exit^M"'
bind -c buffilt e eval writebuf screen 'stuff "scrbuf.sh edit&&exit^M"'
bind -c buffilt g eval writebuf screen 'stuff "scrbuf.sh grep2l&&exit^M"'
bind -c buffilt h eval writebuf screen 'stuff "scrbuf.sh rmhtmltag&&exit^M"'
bind -c buffilt i eval writebuf screen 'stuff "scrbuf.sh rmindent&&exit^M"'
bind -c buffilt j eval writebuf screen 'stuff "scrbuf.sh join&&exit^M"'
bind -c buffilt q eval writebuf screen 'stuff "scrbuf.sh quote&&exit^M"'
bind -c buffilt u eval writebuf screen 'stuff "scrbuf.sh pickurl&&exit^M"'
```

### scrbuf.sh: ペーストバッファの内容を置換
screenでコピーしたペーストバッファ内容を、各フィルタにかけて、フィルタ出力内容をペーストバッファにセットするスクリプトです。

https://gist.github.com/deton/5027300222758d2ed12b

```sh:scrbuf.sh
#!/bin/sh
# apply filter and overwrite screen-xchg file
SRC=~/tmp/screen-xchg
DEST=~/tmp/screen-xchg2
case "$1" in
	rmwrap-emacs)
		rmwrap-emacs.awk $SRC >$DEST
		;;
	rmwrap-vim)
		rmwrap-vim.awk $SRC >$DEST
		;;
	rmwrap-mutt)
		rmwrap-mutt.awk $SRC >$DEST
		;;
	rmwrap-width)
		vim -X --noplugin -e -s <~/lib/scrbuf/rmwrap-width.vim $SRC >$DEST
		;;
	edit)
		cp $SRC $DEST && $EDITOR $DEST
		;;
	join)
		tr -d '\n' <$SRC >$DEST
		;;
	join-with-blank)
		tr '\n' ' ' <$SRC >$DEST
		;;
	rmindent)
		# メール内でURLをインデントする際に使われる全角空白を除去
		sed -e 's/^[　 ]*//' $SRC >$DEST
		;;
	grep2l)
		awk '/[[:graph:]]:/{print substr($0,1,index($0,":")-1)}' $SRC >$DEST
		;;
	pickurl)
		sed -e 's/.*\(https\?:\/\/[-a-zA-Z0-9_\.@:\/~#!?\&=+%]\+\).*$/\1/' $SRC >$DEST
		;;
        picklastarg)
                awk '{print $NF}' $SRC >$DEST
                ;;
	rmhtmltag)
		sed -e 's/<[^>]*>//g' -e 's/\&nbsp;/ /g' -e 's/\&quot;/"/g' -e 's/^ *//' $SRC >$DEST
		;;
	quote)
		# copy from lynx to memo with quote(> )
		# (remove numbered label from link like "[32]link")
		sed -e 's/\[[0-9][0-9]*\]//g' $SRC | nkf -f74 -w -m0 | sed -e 's/^/> /' >$DEST
		;;
	*)
		echo "$0: unknown filter: $1"
		exit 2
		;;

esac
EXITCODE=$?
if [ $EXITCODE -ne 0 ]; then
	echo "$0: filter($1) exit code is not zero: $EXITCODE"
	exit 1
fi
# シェルプロンプトに貼り付ける際に便利なように、最後の改行を削除。
printf %s "$(cat $DEST)" > $SRC
screen -X eval readbuf 'echo ""'

# 貼り付ける内容が長いと貼り付けできない
#mv $DEST $SRC
#screen -X register . "$(cat $SRC|sed -e 's/\$/\\$/g')"
```

### 各フィルタスクリプト

(なお、screen以外でも、端末エミュレータ等で表示文字列をコピーする際にも、同じフィルタスクリプトを使用可能。)

#### エディタ起動
#### `\`で表示された継続行を連結

```awk:rmwrap-emacs.awk
#!/usr/bin/awk -f
# remove emacs wrap markers of long line.
/\\$/ {
	sub(/\\$/, "");
	line = rm2ndmark($0, line);
	line = line $0;
	next;
}
line != "" {
	line = rm2ndmark($0, line);
	printf("%s%s\n", line, $0);
	line = "";
	next;
}
{
	print;
}
END {
	if (line != "") {
		print line;
	}
}

# remove second '\' for multi-byte
function rm2ndmark(curline, prevline) {
	if (curline ~ /^[^ -~[:cntrl:]]/ && prevline ~ /\\$/) {
		sub(/\\$/, "", prevline);
	}
	return prevline;
}
```

#### `>`で表示された継続行を連結

```awk:rmwrap-vim.awk
#!/usr/bin/awk -f
# remove vim wrap markers of long line.
/>$/ {
	line = rmmark($0, line);
	line = line $0;
	next;
}
line != "" {
	line = rmmark($0, line);
	printf("%s%s\n", line, $0);
	line = "";
	next;
}
{
	print;
}
END {
	if (line != "") {
		print line;
	}
}

# remove wrap marker '>' for multi-byte
function rmmark(curline, prevline) {
	if (curline ~ /^[^ -~[:cntrl:]]/ && prevline ~ />$/) {
		sub(/>$/, "", prevline);
	}
	return prevline;
}
```

#### `+`で表示された継続行を連結

```awk:rmwrap-mutt.awk
#!/usr/bin/awk -f
# remove mutt wrap markers of long line.
/^+/ {
	sub(/^+/, "");
	line = line $0;
	next;
}
line != "" {
	print line;
	# FALLTHRU
}
{
	line = $0;
}
END {
	if (line != "") {
		print line;
	}
}
```

#### ウィンドウ幅いっぱいの行の次行が最初のカラムから始まっていたら継続行とみなして連結
ウィンドウ幅いっぱいかどうかの判定を行うため、vimのstrdisplaywidth()を使用。

```vim:rmwrap-width.vim
function! JoinByWidth(start, end, width)
  execute a:end
  let lnum = line('.')
  let curline = getline(lnum)
  while lnum > a:start
    -
    let lnum -= 1
    let prevline = getline('.')
    let prevwidth = strdisplaywidth(prevline, 1)
    if curline =~ '^\S' && prevwidth == a:width
      j!
    elseif strwidth(matchstr(curline, '.')) > 1 && prevwidth == a:width - 1
      " wide char
      j!
    "elseif curline =~ '^ \S' && prevwidth == a:width
    "  j!
    "elseif curline =~ '^\S' && prevwidth == a:width - 1
    "  j!
    endif
    let curline = prevline
  endwhile
endfunction
call JoinByWidth(1, '$', winwidth(0))
1,$p
```

#### 改行を削除
コピーする際に、行を連結するモードにしそこねてコピーしてしまったが、行を連結して使いたい場合用。

#### 改行をスペースに置換
`ls -lrt`して最後の数個のファイルをコピーして、
コマンドライン引数として使いたい場合用
(`ls -rt|tail -4`等を実行し直すかわりに)

#### インデントを削除
メール中でURLをインデントするのに使われている全角空白を削除する用途。
screenのコピーでURL先頭まで移動してコピー` $ `するより、
`Y`や`y `で行単位でコピーする方が楽な気がするので。

あるいは、lynxで、整形してインデント付きで表示されている内容を、
矩形選択しないでコピーしてしまった場合に、インデントを削除する用途。

#### grep出力のファイル名だけ抽出
普通に`grep`した結果から、ファイル名だけコピーするかわりに、
1行まるごとコピーして、`:`より前をフィルタで抽出。
grepに時間がかかると、`grep -l`し直したくないので。
例えばgrepした結果のファイルをlessで見る場合向け。
単に`grep -l`し直す場合は以下のような関数を定義しておけばOK。

```bash:.bashrc
function lg() {
        less $($(history -p '!-1:0') -l $(history -p '!*'))
}
```

#### URL抽出
メールやHTMLソース表示やMarkdown表示から、URLの先頭と末尾までカーソル移動してコピーするかわりに、
1行まるごとコピーして、URLを抽出。

#### 最後のパラメータ抽出
シェルで`!$`はよく使うので同様に。
1行まるごとコピーして、最後のパラメータを抽出。

#### HTMLタグ除去
HTMLソースからのコピー時に、対象文字列の先頭と末尾それぞれにカーソルを移動してマークを付けるのは面倒なので、
1行まるごとコピーして、HTMLタグを除去。

#### 引用記号`> `を追加
lynxで表示中の記事の一部をコピーして、
メモに引用記号を付けてペーストしたい場合向け。

## 参考: 各アプリでの継続行の表示
### Emacs
`\`もしくは次の文字が日本語等の場合`\\`。

http://www.emacswiki.org/emacs/LineWrap

### Vim
次の文字がマルチバイト文字の場合`>`。
そうでない場合は継続行かどうか不明。

以下の設定をすると継続行かどうかは表示を見てわかるようになりますが、普段はしてないので。

```vim:.vimrc
se nu
```

### muttやlynxのHTMLソース表示
継続行の行頭に`+`。

muttの場合は、以下の設定をすることで`+`追加をオフにできます。

```:.muttrc
set markers=no
```