Comment définir un opérateur qui se met à jour pendant qu'il prend des entrées?

9

J'ai défini un mappage d'opérateur qui prend une région de texte, puis demande une chaîne d'entrée, puis aligne la région avec Tabular en utilisant la chaîne d'entrée comme argument. Ça marche bien.

Je l'ai implémenté comme ceci, en utilisant vim-operator-user pour aider à définir un nouvel opérateur:

map \aa <Plug>(operator-align)
call operator#user#define('align', 'Align')
function! Align(motion_wiseness)
    let expr = input("align: ")
    if len(expr) != 0
        execute "'[,']Tabularize /".expr
    endif
endfunction


function! Getchar()
    let c = getchar()
    if c =~ '^\d\+$'
        let c = nr2char(c)
    endif
    return c
endfunction

Ensuite, je me suis demandé si je pouvais le mettre à jour à la volée tout en entrant l'expression régulière pour l'aligner. Le problème avec l'approche actuelle est que vous devez annuler puis refaire si vous n'utilisez pas l'expression correcte.

Pour la tentative interactive, j'ai fait ceci:

map \== <Plug>(operator-align-interactive)
call operator#user#define('align-interactive', 'AlignInteractive')
function! AlignInteractive(motion_wiseness)
    let prompt = "Align: "
    echon prompt
    let expr = ""
    let c = Getchar()
     " CR has to be checked for separately as it acts as putting the cursor back to zero position
    while c != "\<Esc>" && c != "\<CR>"
        if c == "\<BS>"
            if len(expr) != 0
                let expr = expr[0:-2]
                echon "\<CR>".substitute(expr, ".", " ", "g")
                echon "\<CR>".prompt.expr
            endif
        else
            let expr .= c
            echon c
            let cmd = "'[,']Tabularize /".expr
            execute cmd 
        endif
        let c = Getchar()
    endwhile
endfunction

Cela devrait fonctionner mais l'alignement ne se fait pas avant d'appuyer sur Entrée, c'est-à-dire après que j'aie fini de saisir l'entrée, ce qui signifie en fait que cela fonctionne de la même manière que la fonction non interactive. Je me demande si le problème pourrait être quelque chose comme l'écran / le contenu n'étant pas mis à jour pendant l'exécution d'un opérateur, seulement après.

Toutes les idées sur ce que pourrait être le problème sont appréciées!

tusj
la source

Réponses:

8

Vous devez le faire undoet redrawsi vous voulez voir immédiatement les changements dans le tampon.

Voici ce qui fonctionne:

function! AlignInteractive(motion_wiseness)
  let prompt = "Align: "
  let undo = 0
  let expr = ""
  let range = line("'[").','.line("']")
  let failed = 0
  let accept = 0

  echon prompt
  let c = Getchar()

  while c != "\<Esc>" && c != "\<c-c>"
    let undo = len(expr)

    if c == "\<CR>"
      let accept = 1
      break
    elseif c == "\<BS>"
      if len(expr)
        let expr = expr[0:-2]
      endif
    else
      let expr .= c
    endif

    if undo && !failed
      silent! undo
    endif

    if len(expr)
      try
        call match('', expr)
        let failed = 0
        execute range."Tabularize /".expr
      catch //
        let failed = 1
      endtry
    endif

    redraw

    echon prompt
    if failed
      echohl ErrorMsg
    endif
    echon expr
    echohl None
    let c = Getchar()
  endwhile

  if !accept && len(expr) && !failed
    silent! undo
  endif
endfunction

Explication

La rangevariable stocke les marques '[et ']. C'est pour le bon sens.

Une variable appelée undoest définie en fonction de la longueur précédente de expr. Cela signifie que chaque fois qu'il y a une entrée de l'utilisateur, la prochaine itération peut être annulée en toute sécurité avant de s'exécuter Tabularize.

call match('', expr)teste l'expression des erreurs de modèle. S'il échoue, undone doit pas être exécuté. Des échecs de modèle peuvent se produire lorsque vous tapez des atomes tels que \zs.

redraweffacera la ligne de commande. C'est pourquoi l'invite complète est imprimée à chaque itération.

Si le motif contient une erreur, il est mis en surbrillance avec ErrorMsg.

acceptest réglé lorsque vous <cr>appuyez sur. Si c'est faux, annulez les modifications (le cas échéant). Tout ce qui rompt la boucle est annulé.

Plugin existant

Il existe un plugin appelé vim-easy-align qui peut faire ce que vous essayez. Vous pouvez vous inspirer de son script .

Tommy A
la source
Merci beaucoup pour les explications claires! Je ne savais pas qu'un alignement facile pouvait faire ça. La fonctionnalité d'annulation était la prochaine chose que je voulais implémenter, mais je suis resté bloqué lors de la mise à jour.
tusj
Pas de problème 🙂 Je viens de mettre à jour la réponse pour mettre en surbrillance le motif s'il a une erreur.
Tommy A
1
Il y a un petit bogue dans l'implémentation actuelle, en raison d'une exigence non écrite: si Esc ou <CC> a été tapé, l'opération doit être annulée. J'ai corrigé le premier cas en ajoutant: if c == "\<Esc>" && undo silent! undo endif Je ne sais pas comment détecter pour <CC> cependant.
tusj
@tusj J'ai mis à jour la réponse.
Tommy A