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 */
9package 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 */
21public 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}
165