Inserisci un numero crescente per ogni riga in una selezione o una corrispondenza


10

Ho un problema che posso pensare a due approcci generali per la risoluzione, ma non conosco i dettagli per nessuno dei due approcci.

...
Level 1:    cũng    also
Level 1:    và      and
Level 1:    như     like; such as
Level 2:    các     plural marker
Level 2:    của     belonging to
...

Per ogni riga che inizia "Livello n" voglio inserire un numero, iniziando con "01". Per semplicità anteponiamo il numero.

Approccio 1: selezionare manualmente tutte le linee con lo stesso livello. Invoca la magia che presto imparerò.

Approccio 2: scrivi una ricerca e sostituisci che corrisponda a tutte le righe con un determinato Livello che ad ogni corrispondenza include un numero nel testo di sostituzione, che aumenta di uno ad ogni corrispondenza.

Ho trovato domande simili su StackOverflow o su altri siti Vim, ma ognuno sembra avere uno o più dei seguenti problemi:

  1. Si tratta di inserire il numero di riga corrente anziché un numero arbitrario ma incrementale.
  2. Non azzera il numero.
  3. In realtà non funziona per le selezioni sul mio Vim 7.4 in esecuzione su Windows 7. (Questi causano l'errore E481: No range allowed.)

Sto eseguendo gVim in Windows con mswin.vim ma una soluzione che funziona su tutte le installazioni Vanilla Vim senza dover personalizzare l'installazione potrebbe essere la migliore.


I migliori tag che mi sono venuti in mente per la domanda non esistono: selezione e intervallo, quindi sentiti libero di ri-taggare o creare uno di quei tag.
hippietrail,

1
Questo plugin non è una soluzione completa per il tuo problema, ma è estremamente utile per aggiungere colonne di numeri: VisIncr . Documenti qui . FWIW.
lcd047

Risposte:


17

Simile alla risposta su https://vi.stackexchange.com/a/818/227 , è possibile utilizzare il comando globale.

Con esso puoi istruire Vim a cercare linee che corrispondono a un modello, e quindi eseguire comandi su di esso.

Nel tuo caso, desideri anteporre il testo alle righe che iniziano con "Livello N:", quindi il nostro comando globale potrebbe essere

:g/^Level \d:/{COMMANDS}

Utilizzo del comando sostitutivo (sostituzione di espressioni regolari) per il comando

I comandi sono più divertenti. Di solito mi piace fare una sostituzione delle espressioni regolari per cose come questa, poiché è facile usare le variabili.

Esempio per la tua domanda

:let i = 1 | g/^Level \d:/s/^/\=printf("%02d ", i)/ | let i = i+1

Come funziona

Nella sezione di sostituzione di un comando di sostituzione può essere presente un'espressione.

La prima cosa che faremo è impostare una variabile icome numero iniziale. Ho scelto 1, ma qualsiasi numero lo farà.let i = 1

Quindi eseguiamo il nostro comando globale, che ci imposta per fare un'azione su linee abbinate. g/^Level \d:/

Avremo il nostro comando globale inserire il valore e incrementare il nostro contatore usando il comando di sostituzione e il comando let. s/^/\=printf("%02d ", i)/ | let i = i+1

L'espressione regolare del comando di sostituzione trova l'inizio della riga ^e la sostituisce con un'espressione e la nostra espressione sarà il risultato di una stampa formattata. Come nel linguaggio C, printf di vim accetta parametri di formattazione. %02dsignifica convertire un argomento come se fosse un numero decimale d, occupando almeno 2 spazi 2e pad con 0 0. Per dettagli e altre opzioni di conversione (inclusa la formattazione in virgola mobile), vedere :help printf. Diamo a printf la nostra variabile di conteggio ie ci dà 01la prima volta, 02la seconda volta, ecc. Questo viene utilizzato dal comando di sostituzione per sostituire l'inizio della riga, inserendo efficacemente il risultato della stampa all'inizio.

Nota che ho messo uno spazio dopo la d: "%02d ". Non l'hai chiesto nella domanda (e non ho visto l'output di esempio), ma sospettavo che volessi separare il numero dalla parola "Livello". Rimuovere lo spazio dalla stringa assegnata a printf per avere il numero inserito proprio accanto alla L in Level.

Infine, questo let i = i + 1aumenta il nostro contatore dopo ogni sostituzione.

Questo può essere applicato generalmente per sostituire parti di linee che sono soddisfatte da altri criteri con dati funzionali arbitrari.

Utilizzo dei comandi normali combinati

Questo è utile per inserimenti semplici o modifiche complesse. Come con sostituto, useremo global per abbinare, ma invece della sostituzione di espressioni regolari, eseguiremo una serie di operazioni come se fossero state digitate dall'utente.

Esempio per la tua domanda

:let i = 1 | g/^Level \d:/execute "normal! I" . printf("%02d ", i) | let i = i+1

Come funziona

I valori utilizzati sono molto simili al sostituto (stiamo ancora usando printf per formattare il nostro numero per renderlo 0 riempito con 2 cifre), ma l'operazione è diversa.

Qui usiamo il comando execute, che accetta una stringa ed esegue la stringa come un comando ex ( :help :exe). Costruiamo una stringa che combina "normale! I" con i nostri dati, che sarà "normale! I01" la prima volta e "normale! I02" la seconda volta, ecc.

Il normalcomando esegue le operazioni come in modalità normale. In questo esempio, il nostro normale comando è I, che viene inserito all'inizio della riga. Se lo avessimo usato dd, eliminerebbe la riga, oaprirà una nuova riga dopo la riga corrispondente. È come se tu Istessi digitando (o qualsiasi altra operazione) in modalità normale. usiamo il !after normalper assicurarci che nessuna mappatura ci ostacoli. Vedere :help :normal.

Ciò che viene inserito è quindi il valore del nostro printf, come nel primo esempio di utilizzo di sostituto.

Questo metodo può essere più elaborato di regex, perché puoi fare cose del genere execute "normal! ^2wy" . i . "th$p", che andranno all'inizio del testo ^, avanzano di 2 parole 2w, tirano fino al carattere ith 'h' y" . i . "th, si spostano alla fine della riga $e incollano p.

È quasi come eseguire una macro, ma in realtà non utilizza un registro e può combinare stringhe da qualsiasi espressione. Trovo che questo sia molto potente.

Approccio in cui ogni livello ha il proprio contatore

Potresti desiderare che ogni livello ottenga il proprio contatore. Se si conosce il numero massimo di livelli in anticipo, è possibile effettuare le seguenti operazioni (l'aggiunta di un codice aggiuntivo per trovare il livello più grande potrebbe non essere troppo difficile, ma renderebbe questa risposta troppo lunga. Si sta allungando così com'è).

Innanzitutto, liberiamo i, nel caso in cui l'abbiamo già usato come numero intero. Non possiamo convertire i in un elenco, dobbiamo crearlo in questo modo.

:unlet! i

Quindi, consente di impostare i come elenco contenente il numero di livelli. Hai mostrato 2 nella tua domanda, ma supponiamo che 10 si divertano. Poiché l'indicizzazione dell'elenco è basata su 0 e non voglio preoccuparmi di correggerla per 1 in base al tuo elenco, creeremo solo elementi sufficienti (11) e non utilizzeremo mai l'indice 0.

:let j = 0
:let i = []
:while j < 11 | let i += [1] | let j += 1 | endwhile

Successivamente, abbiamo bisogno di un modo per ottenere il numero di livello. Fortunatamente, sostituto è disponibile anche come funzione, quindi gli daremo la nostra linea ed estrarremo il numero di livellosubstitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")

Poiché i è ora un elenco di 11 1s (ogni indice è il contatore per il nostro livello), ora possiamo regolare uno degli esempi sopra per utilizzare il risultato di questa sostituzione:

Tramite comando sostitutivo:

:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | s/^/\=printf("%02d ", i[ind])/ | let i[ind] += 1

Tramite comando normale:

:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | execute "normal! I" . printf("%02d ", i[ind]) | let i[ind] += 1

Esempio di input:

Level 1: stuff

Level 1: Stuff

Some text
Level 3: Other

Level 1: Meh

Level 2: More

Esempio di output:

01 Level 1: stuff

02 Level 1: Stuff

Some text
01 Level 3: Other

03 Level 1: Meh

01 Level 2: More

Caspita, molto enciclopedico. Ho letto solo la prima parte e mi sento come se avessi imparato qualcosa in più su Vim da quello che ho fatto negli ultimi anni. Questo è il tipo di risposta che rende Stack Exchange migliore dei siti medi di domande e risposte e mostra anche i vantaggi di avere un Vim SE specifico.
hippietrail

3

Puoi costruire da https://stackoverflow.com/a/4224454/15934 per azzerare i tuoi numeri.

" A reminder: how to start numbers at the first line
:'<,'>s/^\s*\zs/\=(line('.') - line("'<")+1).'. '

Ma per semplificare l'azione dei numeri di riempimento, sceglierei un paio di funzioni e un comando:

command!  -range=% -nargs=? PrependNumbers <line1>,<line2>call s:Prepend(<args>)

function! s:ReplExpr(nb_digits, number)
  return repeat('0', a:nb_digits - strlen(a:number)).a:number
endfunction

function! s:Prepend(...) range
  let pattern = a:0 > 0 ? '\ze'. a:1 : '^'
  let nb_values = (a:lastline - a:firstline) + 1
  let nb_digits = strlen(nb_values)
  exe ':'.a:firstline.','a:lastline.'s#'.pattern.'#\=s:ReplExpr(nb_digits, 1+ line(".")-'.a:firstline.')." "#'
endfunction

Quindi, seleziona le tue linee e digita :PrependNumber(vedrai :'<,'>PrependNumber. [Nota: il comando accetta un parametro opzionale: lo schema prima del quale verrà inserito il numero]


Ma non line(".")significa "utilizzare il numero di riga corrente"? Quello era il mio problema 1. con le risposte precedenti che ho trovato.
hippietrail,

1
Sì. Ecco perché sottraggo il numero di riga della prima riga nell'intervallo.
Luc Hermitte,

Ah OK, non l'ho ancora testato perché non riesco nemmeno a ricordare come inserire una sceneggiatura in Vim ... Devo leggere prima quello (-:
hippietrail

1
Mentre sei sotto Windows, copia incolla il codice in $HOME/vimfiles/plugin/whatevernameyouwish.vim. O anche il tuo $HOME/_vimrc(nome file di Windows) se preferisci (per la prima volta). Se non sei sicuro di cosa sia $ HOME sulla tua macchina, chiedi a Vim cosa pensa che sia ->:echo $HOME
Luc Hermitte,

1
Non è nemmeno necessario :sourcese lo script vim è installato correttamente nella directory corretta ({rtp} / plugin o {rtp} / ftplugin / {filetype} / ftplugin per plugin specifici del tipo di file). :sourceè ciò con cui abbiamo dovuto giocare più di un decennio e mezzo fa.
Luc Hermitte,

2

Potresti avvicinarti registrando una macro. A partire dal file originale, anteporre la prima istanza con il numero.

01 Level 1:    cũng    also
Level 1:    và      and
Level 1:    như     like; such as
Level 2:    các     plural marker
Level 2:    của     belonging to

Spostare il cursore su 01, selezionare e strattonarlo con yiw. Ora vuoi registrare le tue azioni da questo punto.

qq/^Level 1<CR>P<C-A>A<space><esc>0yiwq

  • qq Avviare una macro nel registro q
  • /^Level 1<CR> Cerca una riga che inizia con "Livello 1"
  • P incolla prima del cursore (contiene il tuo numero)
  • <C-A> incrementa il numero
  • A<space><esc> Inserisci uno spazio dopo il numero
  • 0 Vai all'inizio
  • yiw strappare il numero corrente
  • q Termina la macro

Quindi ripetere questa macro utilizzando @q.


1
E ' <C-A>Ctrl + A? Questo seleziona tutto in Vim su Windows.
hippietrail,

È Control + a. In ogni caso dovrebbe aumentare un numero. Se hai modificato le tue combinazioni di tasti per eseguire azioni Windows anziché azioni Vim, non sarai in grado di fare la maggior parte delle cose che la gente pubblica qui.
jecxjo,

No, la versione di Vim per Windows include attacchi diversi a causa della saggezza infinita di qualcuno. Sto solo usando gli attacchi out-of-the-box alla vaniglia. Non ho mai cambiato un'associazione chiave Vim in oltre vent'anni, che posso ricordare (-:
hippietrail

Non dovrebbe essere il caso, la mia installazione di Windows ha normali collegamenti VIM. Potrebbe essere possibile che tu abbia installato la build di vim di qualcun altro? O potresti eseguire vim in modalità "Facile"? credo che Windows installi le icone del desktop con le opzioni di modalità normale e facile.
jecxjo,

3
No, è perché l'installazione predefinita in Windows carica mswin.vim. Se crei il tuo vimrc e non carichi mswin.vim, otterrai i normali collegamenti di vim. Tengo un unico vimrc per tutte le mie installazioni (Linux, Mac e Windows) e non ho mai a che fare con mswin.vim. Per maggiori informazioni su questo argomento vedere stackoverflow.com/questions/289681/...
jecxjo
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.