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 }