View Javadoc

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 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 }