Smart Copy in Vim: Intelligently Removing Hard Line Breaks

By

Vim 的软换行并不好用。

Vim’s soft line wrapping isn’t very practical.

set tw=0
set wrap
set linebreak

当如上设置后,虽然文本可以根据窗口宽度自动换行,但 gq 的显示格式化总是不太完美,尤其是中英文混用的时候。我最终还是使用了硬换行,以达到稳定一致的显示效果。

With the above settings, although the text can wrap automatically based on the window width, the display formatting ofgqis never perfect, especially when mixing Chinese and English.
I ultimately resorted to using hard line breaks to achieve stable and consistent display results.

set tw=120
set nowrap
set nolinebreak

这个时候就暴露出一个问题,当我需要把 Vim 里面的文本拷贝到其他文本编辑环境使用的时候,这些硬换行还需要我自己去掉。

This exposes a problem: when I need to copy text from Vim to other text editing environments, I have to remove thesehard line breaks myself.

一个解决方案是:

One solution is:

fun! s:SmartCopy()
" 1. Validate textwidth setting
" Must be set (>0) and at least 2 to allow for a meaningful limit calculation.
if &tw <= 1
echohl ErrorMsg
if &tw == 0
echom "Error: 'textwidth' (tw) is not set. Please set it (e.g., :set tw=80) before using this command."
else
echom "Error: 'textwidth' (tw) must be at least 2. Current value: " . &tw
endif
echohl None
return
endif
" 2. Copy the visual selection to the system clipboard register
normal! "+y
" 3. Get the content from the register
let l:text = @+
" 4. Split text into lines.
" The 'keepempty' argument (1) ensures that consecutive newlines result in empty strings in the list.
let l:lines = split(l:text, "\n", 1)
let l:result = []
for i in range(len(l:lines))
let l:line = l:lines[i]
" Check if we are at the last element of the list
if i == len(l:lines) - 1
" Last line: just add it, no trailing newline needed
call add(l:result, l:line)
else
" Not the last line. Decide whether to append a newline after this line.
" Case A: Current line is EMPTY string.
" This implies the original text had consecutive newlines (\n\n or more).
" Logic: Always preserve the structure of paragraph breaks.
if l:line == ""
call add(l:result, l:line . "\n")
" Case B: Current line has content (Single newline in original text).
else
" Get the last character of the current line
let l:last_char = matchstr(l:line, '.$')
" Calculate the limit based on the last character type
if l:last_char != '' && char2nr(l:last_char) < 128
" ASCII character at line end: use larger margin (20) for English
" Adjust this value as needed based on the maximum word length in your text
let l:max_word_length = 20
else
" Non-ASCII character (e.g., Chinese) at line end: use smaller margin (2)
let l:max_word_length = 2
endif
" Calculate the dynamic limit for this line
let l:limit = &tw - l:max_word_length
" Calculate visual width (handles Chinese chars and Tabs correctly)
let l:current_width = strdisplaywidth(l:line)
if l:current_width <= l:limit
" Line is short enough (<= l:limit): Assume user intended this break. Keep it.
call add(l:result, l:line . "\n")
else
" Line is long (> l:limit): Assume this is an auto-wrap hard break. Remove it.
" Check if we need to add a space when removing the newline
" Re-check the last character (we already have it in l:last_char)
if l:last_char != '' && char2nr(l:last_char) < 128
" ASCII character at line end: add a space
call add(l:result, l:line . " ")
else
" Non-ASCII character (e.g., Chinese) or empty: don't add space
call add(l:result, l:line)
endif
endif
endif
endif
endfor
" 5. Join the list back into a single string.
let l:text = join(l:result, "")
" 6. Write the processed text back to the system clipboard register
let @+ = l:text
" 7. Restore the visual selection highlight
normal! gv
endfun
" Alt+Shift+Y
:vn <A-S-Y> :call <SID>SmartCopy()<CR>

使用 Alt+Shift+Y 拷贝文本时,将执行以下逻辑:
若发现连续 2 个或以上的换行符,则保留。
对于有内容的行,先判断行尾字符类型:若是 ASCII 字符,则阈值为(textwidth-20);否则为(textwidth-2)。
若该行的列位置超过阈值,则视作自动硬换行并删除;否则,视作用户手动添加的硬换行并保留。
在删除硬换行时,若行尾字符为 ASCII 字符,则添加一个空格,否则不添加。
注意:
首先,确保文本没有行尾或行首空格。这在 Vim 中很容易修复。
如果用户手动在阈值列之后加上硬换行,那也没办法区分,需要手动修正,但这种情况较少。
处理添加空格的方法比较简单,可能有些情况下需要手动调整。
但总体来看,应该是减少了工作量的。

Using Alt+Shift+Y to copy a block of text applies the following logic:
If two or more consecutive line breaks are found, they are preserved.
For lines containing content, first determine the line-ending character type: if it is an ASCII character, the threshold is (textwidth-20); otherwise, it is (textwidth-2).
If the column position of the line exceeds the threshold, it is treated as an automatic hard line break and deleted; otherwise, it is treated as a manually added hard line break and preserved.
When deleting a hard line break, if the line-ending character is an ASCII character, a space is added; otherwise, nothing is added.
Notes:
First, ensure the text has no trailing or leading spaces. This is easy to fix in Vim.
If a user manually adds a hard line break after the threshold column, there is no way to distinguish it, and manual correction is required, although this situation is relatively rare.
The method of adding spaces is simple, and in some cases, manual adjustment may be needed.
Overall, however, it should reduce the workload.

这样就可以很方便的把 Vim 里面的文本转移到其他文本编辑器继续处理。

This makes it convenient to transfer text from Vim to other text editors for further processing.

Discover more from ezha

Subscribe now to keep reading and get access to the full archive.

Continue reading