1 /*
   2  * Copyright (c) 2002-2013, the original author or authors.
   3  *
   4  * This software is distributable under the BSD license. See the terms of the
   5  * BSD license in the documentation provided with this software.
   6  *
   7  * http://www.opensource.org/licenses/bsd-license.php
   8  */
   9 package jdk.internal.jline.console;
  10 
  11 /**
  12  * The kill ring class keeps killed text in a fixed size ring. In this
  13  * class we also keep record of whether or not the last command was a
  14  * kill or a yank. Depending on this, the class may behave
  15  * different. For instance, two consecutive kill-word commands fill
  16  * the same slot such that the next yank will return the two
  17  * previously killed words instead that only the last one. Likewise
  18  * yank pop requires that the previous command was either a yank or a
  19  * yank-pop.
  20  */
  21 public final class KillRing {
  22 
  23     /**
  24      * Default size is 60, like in emacs.
  25      */
  26     private static final int DEFAULT_SIZE = 60;
  27 
  28     private final String[] slots;
  29     private int head = 0;
  30     private boolean lastKill = false;
  31     private boolean lastYank = false;
  32 
  33     /**
  34      * Creates a new kill ring of the given size.
  35      */
  36     public KillRing(int size) {
  37         slots = new String[size];
  38     }
  39 
  40     /**
  41      * Creates a new kill ring of the default size. {@see DEFAULT_SIZE}.
  42      */
  43     public KillRing() {
  44         this(DEFAULT_SIZE);
  45     }
  46 
  47     /**
  48      * Resets the last-yank state.
  49      */
  50     public void resetLastYank() {
  51         lastYank = false;
  52     }
  53 
  54     /**
  55      * Resets the last-kill state.
  56      */
  57     public void resetLastKill() {
  58         lastKill = false;
  59     }
  60 
  61     /**
  62      * Returns {@code true} if the last command was a yank.
  63      */
  64     public boolean lastYank() {
  65         return lastYank;
  66     }
  67 
  68     /**
  69      * Adds the string to the kill-ring. Also sets lastYank to false
  70      * and lastKill to true.
  71      */
  72     public void add(String str) {
  73         lastYank = false;
  74 
  75         if (lastKill) {
  76             if (slots[head] != null) {
  77                 slots[head] += str;
  78                 return;
  79             }
  80         }
  81 
  82         lastKill = true;
  83         next();
  84         slots[head] = str;
  85     }
  86 
  87     /**
  88      * Adds the string to the kill-ring product of killing
  89      * backwards. If the previous command was a kill text one then
  90      * adds the text at the beginning of the previous kill to avoid
  91      * that two consecutive backwards kills followed by a yank leaves
  92      * things reversed.
  93      */
  94     public void addBackwards(String str) {
  95         lastYank = false;
  96 
  97         if (lastKill) {
  98             if (slots[head] != null) {
  99                 slots[head] = str + slots[head];
 100                 return;
 101             }
 102         }
 103 
 104         lastKill = true;
 105         next();
 106         slots[head] = str;
 107     }
 108 
 109     /**
 110      * Yanks a previously killed text. Returns {@code null} if the
 111      * ring is empty.
 112      */
 113     public String yank() {
 114         lastKill = false;
 115         lastYank = true;
 116         return slots[head];
 117     }
 118 
 119     /**
 120      * Moves the pointer to the current slot back and returns the text
 121      * in that position. If the previous command was not yank returns
 122      * null.
 123      */
 124     public String yankPop() {
 125         lastKill = false;
 126         if (lastYank) {
 127             prev();
 128             return slots[head];
 129         }
 130         return null;
 131     }
 132 
 133     /**
 134      * Moves the pointer to the current slot forward. If the end of
 135      * the slots is reached then points back to the beginning.
 136      */
 137     private void next() {
 138         if (head == 0 && slots[0] == null) {
 139             return;
 140         }
 141         head++;
 142         if (head == slots.length) {
 143             head = 0;
 144         }
 145     }
 146 
 147     /**
 148      * Moves the pointer to the current slot backwards. If the
 149      * beginning of the slots is reached then traverses the slot
 150      * backwards until one with not null content is found.
 151      */
 152     private void prev() {
 153         head--;
 154         if (head == -1) {
 155             int x = (slots.length - 1);
 156             for (; x >= 0; x--) {
 157                 if (slots[x] != null) {
 158                     break;
 159                 }
 160             }
 161             head = x;
 162         }
 163     }
 164 }