Comentando múltiples líneas de código, especificadas por números de línea, usando vi o vim

20

Aprendí de esta pregunta de Stack Overflow que es posible usar vi/ vimcomentar un rango específico de números de línea. Por ejemplo, supongamos que tengo el siguiente script bash:

#!/bin/bash

This
is
my
very
very
great
script

Ahora supongamos que yo quiero comentar números de línea 6 a 8 (que contengan las palabras very, veryy great) con el #carácter de comentario. En vi/ vim, simplemente puedo escribir :6,8s/^/#para obtener lo siguiente:

#!/bin/bash

This
is
my
#very
#very
#great
script

que comenta las líneas 6 a 8.

Mi pregunta es, ¿es posible escribir un delineador similar que elimine el #carácter de comentario de las líneas 6 a 8 (pero no otras líneas comentadas en el archivo)?

Dicho esto, me doy cuenta de que hay un debate sobre si realmente estoy usando vio vim. En la práctica, abro un archivo script.shcon el comando vi script.sh. Además, cuando escribo el comando which vi, obtengo /usr/bin/vi. Sin embargo, cuando simplemente escribo viy presiono Enter, obtengo esto:

~                              VIM - Vi IMproved
~
~                               version 7.2.330
~                           by Bram Moolenaar et al.
~                 Vim is open source and freely distributable
~
~                           Sponsor Vim development!
~                type  :help sponsor<Enter>    for information
~
~                type  :q<Enter>               to exit
~                type  :help<Enter>  or  <F1>  for on-line help
~                type  :help version7<Enter>   for version info

lo que parece sugerir que realmente estoy usando vim. Estoy accediendo a un clúster remoto de Ubuntu Linux usando SSH desde mi PC. No estoy usando una GUI de Ubuntu Linux.

Andrés
fuente

Respuestas:

22

Puedes usar:

:6,8s/^#//

Pero es mucho más fácil usar el modo de selección Bloquear visual: vaya al comienzo de la línea 6, presione Ctrl-v, vaya a la línea 8 y presione x.

También existe el complemento "The NERD Commenter" .

jofel
fuente
2
NERD Commenteres el camino a seguir aquí en mi opinión! +1 para eso
usuario1146332
7

Sé que su pregunta especifica el uso de vio, vimpero aquí hay algunas otras opciones para hacerlo sin tener que abrir manualmente el archivo:

  • Perl

    perl -ne 'if($. <=8 && $. >= 6){s/^\s*#//;}print' foo.sh 
    
  • Versión Perl> = 5.10

    perl -ne '$. ~~ [6..8] && s/^\s*#//;print' foo.sh 
    

    Esto imprimirá el contenido del archivo, puede redirigir a otro ( > new_file.sh) o usar ipara editar el archivo en su lugar:

    perl -i -ne '$. ~~ [6..8] && s/^\s*#//;print' foo.sh 
    
  • sed

    sed '6,8 s/^ *#//' foo.sh
    

    Nuevamente, para que esto edite el archivo original en su lugar, use i:

    sed -i '6,8 s/^ *#//' foo.sh
    
  • awk/ gawketc:

    gawk '(NR<=8 && NR>= 6){sub("^ *#","")}{print}' foo.sh
    
  • Puro bash:

    c=1; while read line; do 
      if [ $c -ge 6 ] && [ $c -le 8 ]; then 
         echo "${line/\#/}"
      else 
         echo $line 
      fi
      let c++; done < foo.sh
    
terdon
fuente
1
No es tanto una cuestión de "tener que abrir manualmente el archivo", porque generalmente decides qué líneas comentar durante la inspección visual, mientras editas :) Pero claro, es una buena respuesta para completar.
Paulo Almeida
2
@PauloAlmeida tienes razón, por supuesto. Simplemente pensé que podría ser útil ya que el OP ya conoce los números de línea (debido al primer comando utilizado para comentarlos) y, en cualquier caso, las herramientas que muestro se pueden aplicar a una variedad de problemas.
terdon
4

vies un enlace simbólico vimen la mayoría de las distribuciones de GNU / Linux, por lo que de hecho lo está utilizando vimcuando escribe vi.

Para eliminar los comentarios, puede escribir: :6,8s/^#//o :6,8s/^\s*#//para descartar algún espacio inicial antes del # símbolo.

lgeorget
fuente
1
Muchas gracias. Parece que puede haber errores tipográficos. Tal vez debería ser :6,8s/^#//y 6,8s/^\s*#//?
Andrew
3

Probablemente lo estés usando vim.tiny. En cualquier caso, puede eliminar los comentarios iniciales con:

:6,8s/^#//

Por supuesto, si los inserta de una manera diferente (por ejemplo, con un espacio adicional), es posible que deba eliminar cualquier otra cosa que haya. Con vim completo, seleccionar visualmente columnas e insertar / eliminar caracteres es una manera más fácil de hacer lo mismo.

Paulo Almeida
fuente
3

Personalmente, mi forma favorita es usar el modo de bloqueo visual

ctrl+ vpara ingresar al modo de bloqueo visual, use las teclas de flecha o hjkl para seleccionar las líneas y presione xo del.

¿Los quieres de vuelta?

ctrl+ vhacer la selección, luego I(mayúscula i)#Esc

exussum
fuente
3

Que yo sepa, vi suele ser un enlace simbólico de vim hoy en día (tratar which vio type viy luego siga los enlaces simbólicos). Quizás, incluso /usr/bin/vi-> /etc/alternatives/vi-> /usr/bin/vim.basic.

Personalmente, para eliminar varias líneas de comentarios, prefiero seleccionar un bloque vertical mediante CtrlVy eliminarlo. Si necesita agregar un símbolo de comentario a varias líneas, podría CtrlV, entonces ShiftI, escribir #y Esc, y se agregará un comentario a varias líneas.

Boris Burkov
fuente
2

Las respuestas anteriores, usando

:6,8s/^#//

son la solución perfecta, pero un poco engorroso de escribir. Esto se puede simplificar definiendo nuevos comandos en ~/.vimrc.

command -range=% C :<line1>,<line2>s/^/#/
command -range=% D :<line1>,<line2>s/^#//

Y solo puedes escribir

:6,8C
:6,8D

para colocar / eliminar el comando.

Si te gusta el modo visual, puedes definir mapas

map <F7> :s/^/#/<CR>
map <F8> :s/^#//<CR>

De tal manera que solo tiene que seleccionar un rango de línea en modo visual, y presionar F7y F8para poner y eliminar comentarios respectivamente.

Bernhard
fuente
1

Existe este complemento que cambia la vida tpopellamadovim-commentary

https://github.com/tpope/vim-commentary

Este complemento proporciona :

  • Cordura
  • Comentarios correctamente sangrados
  • No comenta líneas vacías / innecesarias

Uso :

  • Instalar a través de Vundle (o patógeno, supongo).
  • Resalta tu texto y presiona :que aparecerá como:<,'>
  • Escriba Comentario aquí :<,'>Commentaryy presione Entrar.
  • Bom. Tu amigo hecho.
Weston Ganger
fuente
1

Esta respuesta está aquí a 1) muestra el código correcto para pegar en una .vimrcde conseguir vim 7.4+hacer el bloque de comentar / descomentar manteniendo nivel de sangría con 1 acceso directo en el modo visual y 2) para explicarlo.

Aquí está el código:

let b:commentChar='//'
autocmd BufNewFile,BufReadPost *.[ch]    let b:commentChar='//'
autocmd BufNewFile,BufReadPost *.cpp    let b:commentChar='//'
autocmd BufNewFile,BufReadPost *.py    let b:commentChar='#'
autocmd BufNewFile,BufReadPost *.*sh    let b:commentChar='#'
function! Docomment ()
  "make comments on all the lines we've grabbed
  execute '''<,''>s/^\s*/&'.escape(b:commentChar, '\/').' /e'
endfunction
function! Uncomment ()
  "uncomment on all our lines
  execute '''<,''>s/\v(^\s*)'.escape(b:commentChar, '\/').'\v\s*/\1/e'
endfunction
function! Comment ()
  "does the first line begin with a comment?
  let l:line=getpos("'<")[1]
  "if there's a match
  if match(getline(l:line), '^\s*'.b:commentChar)>-1
    call Uncomment()
  else
    call Docomment()
  endif
endfunction
vnoremap <silent> <C-r> :<C-u>call Comment()<cr><cr>

Cómo funciona:

  • let b:commentChar='//': Esto crea una variable en vim. el baquí se refiere al alcance, que en este caso está contenido en el búfer, es decir, el archivo abierto actualmente. Sus caracteres de comentario son cadenas y deben estar entre comillas, las comillas no son parte de lo que se sustituirá al alternar comentarios.

  • autocmd BufNewFile,BufReadPost *...: Los comandos automáticos se disparan en diferentes cosas, en este caso, se disparan cuando un nuevo archivo o el archivo de lectura termina con una cierta extensión. Una vez activado, ejecute el siguiente comando, que nos permite cambiar el tipo commentCharde archivo dependiendo. Hay otras formas de hacer esto, pero son más confusas para los novatos (como yo).

  • function! Docomment(): Las funciones se declaran comenzando functiony terminando con endfunction. Las funciones deben comenzar con una capital. los !asegura que esta función sobrescribe los anteriores funciones definidas como Docomment()con esta versión de Docomment(). Sin el !, tuve errores, pero eso podría deberse a que estaba definiendo nuevas funciones a través de la línea de comando vim.

  • execute '''<,''>s/^\s*/&'.escape(b:commentChar, '\/').' /e': Ejecutar llama a un comando. En este caso, estamos ejecutando substitute, que puede tomar un rango (por defecto, esta es la línea actual), como %para todo el búfer o '<,'>para la sección resaltada. ^\s*es regex para que coincida con el inicio de una línea seguida de cualquier cantidad de espacio en blanco, que luego se agrega a (debido a &). El .que aquí se utiliza para la concatenación de cadenas, ya que escape()no se puede ajustar entre comillas. escape()le permite escapar de caracteres commentCharque coinciden con los argumentos (en este caso, \y /) al anteponerlos con un \. Después de esto, volvemos a concatenar con el final de nuestra substitutecadena, que tiene elebandera. Esta bandera nos permite fallar en silencio, lo que significa que si no encontramos una coincidencia en una línea determinada, no gritaremos al respecto. En general, esta línea nos permite poner un carácter de comentario seguido de un espacio justo antes del primer texto, lo que significa que mantenemos nuestro nivel de sangría.

  • execute '''<,''>s/\v(^\s*)'.escape(b:commentChar, '\/').'\v\s*/\1/e': Esto es similar a nuestro último gran comando largo. Único a este, tenemos \v, lo que asegura que no tengamos que escapar de nuestro (), y 1, que se refiere al grupo que hicimos con nuestro (). Básicamente, estamos haciendo coincidir una línea que comienza con cualquier cantidad de espacio en blanco y luego nuestro carácter de comentario seguido de cualquier cantidad de espacio en blanco, y solo mantenemos el primer conjunto de espacios en blanco. Nuevamente, efracasemos silenciosamente si no tenemos un carácter de comentario en esa línea.

  • let l:line=getpos("'<")[1]: establece una variable muy similar a lo que hicimos con nuestro carácter de comentario, pero se lrefiere al ámbito local (local para esta función). getpos()obtiene la posición de, en este caso, el inicio de nuestro resaltado, y [1]significa que solo nos importa el número de línea, no otras cosas como el número de columna.

  • if match(getline(l:line), '^\s*'.b:commentChar)>-1: ya sabes cómo iffunciona. match()comprueba si lo primero contiene lo segundo, por lo que tomamos la línea en la que comenzamos a resaltar y verificamos si comienza con un espacio en blanco seguido de nuestro carácter de comentario. match()devuelve el índice donde esto es cierto, y -1si no se encontraron coincidencias. Dado que ifevalúa todos los números distintos de cero para que sean verdaderos, tenemos que comparar nuestra salida para ver si es mayor que -1. La comparación en vimdevuelve 0 si es falso y 1 si es verdadero, que es lo que ifquiere ver para evaluar correctamente.

  • vnoremap <silent> <C-r> :<C-u>call Comment()<cr><cr>: vnoremapsignifica asignar el siguiente comando en modo visual, pero no asignarlo de forma recursiva (es decir, no cambiar ningún otro comando que pueda usarse de otras maneras). Básicamente, si eres un principiante vim, siempre úsalo noremappara asegurarte de no romper cosas. <silent>significa "No quiero tus palabras, solo tus acciones" y le dice que no imprima nada en la línea de comando. <C-r>es lo que estamos asignando, que es ctrl + r en este caso (tenga en cuenta que todavía puede usar Cr normalmente para "rehacer" en modo normal con esta asignación). C-ues un poco confuso, pero básicamente se asegura de que no pierdas el rastro de tu resaltado visual (de acuerdo con esta respuesta , tu comando comienza con '<,'>lo que queremos).callaquí solo le dice a vim que ejecute la función que nombramos, y se <cr>refiere a enterpresionar el botón. Tenemos que presionarlo una vez para llamar realmente a la función (de lo contrario, solo escribimos call function()en la línea de comando, y debemos presionarlo nuevamente para que nuestros sustitutos pasen por todo el camino (no estoy seguro de por qué, pero lo que sea).

De todos modos, espero que esto ayude. Esto tomará todo lo resaltado con v, Vo C-v, verifique si la primera línea está comentada, en caso afirmativo, intente descomentar todas las líneas resaltadas, y si no, agregue una capa adicional de caracteres de comentario a cada línea. Este es mi comportamiento deseado; No solo quería que cambiara si cada línea en el bloque estaba comentada o no, por lo que funciona perfectamente para mí después de hacer múltiples preguntas sobre el tema.

jeremysprofile
fuente