1
2
3
4
5
6
7
8
9 package jline.console.completer;
10
11 import jline.console.ConsoleReader;
12 import jline.console.CursorBuffer;
13
14 import java.io.IOException;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Locale;
20 import java.util.ResourceBundle;
21 import java.util.Set;
22
23
24
25
26
27
28
29
30
31
32
33 public class CandidateListCompletionHandler
34 implements CompletionHandler
35 {
36
37
38 public boolean complete(final ConsoleReader reader, final List<CharSequence> candidates, final int pos) throws
39 IOException
40 {
41 CursorBuffer buf = reader.getCursorBuffer();
42
43
44 if (candidates.size() == 1) {
45 CharSequence value = candidates.get(0);
46
47
48 if (value.equals(buf.toString())) {
49 return false;
50 }
51
52 setBuffer(reader, value, pos);
53
54 return true;
55 }
56 else if (candidates.size() > 1) {
57 String value = getUnambiguousCompletions(candidates);
58 setBuffer(reader, value, pos);
59 }
60
61 printCandidates(reader, candidates);
62
63
64 reader.drawLine();
65
66 return true;
67 }
68
69 public static void setBuffer(final ConsoleReader reader, final CharSequence value, final int offset) throws
70 IOException
71 {
72 while ((reader.getCursorBuffer().cursor > offset) && reader.backspace()) {
73
74 }
75
76 reader.putString(value);
77 reader.setCursorPosition(offset + value.length());
78 }
79
80
81
82
83
84
85
86 public static void printCandidates(final ConsoleReader reader, Collection<CharSequence> candidates) throws
87 IOException
88 {
89 Set<CharSequence> distinct = new HashSet<CharSequence>(candidates);
90
91 if (distinct.size() > reader.getAutoprintThreshold()) {
92
93 reader.print(Messages.DISPLAY_CANDIDATES.format(candidates.size()));
94 reader.flush();
95
96 int c;
97
98 String noOpt = Messages.DISPLAY_CANDIDATES_NO.format();
99 String yesOpt = Messages.DISPLAY_CANDIDATES_YES.format();
100 char[] allowed = {yesOpt.charAt(0), noOpt.charAt(0)};
101
102 while ((c = reader.readCharacter(allowed)) != -1) {
103 String tmp = new String(new char[]{(char) c});
104
105 if (noOpt.startsWith(tmp)) {
106 reader.println();
107 return;
108 }
109 else if (yesOpt.startsWith(tmp)) {
110 break;
111 }
112 else {
113 reader.beep();
114 }
115 }
116 }
117
118
119 if (distinct.size() != candidates.size()) {
120 Collection<CharSequence> copy = new ArrayList<CharSequence>();
121
122 for (CharSequence next : candidates) {
123 if (!copy.contains(next)) {
124 copy.add(next);
125 }
126 }
127
128 candidates = copy;
129 }
130
131 reader.println();
132 reader.printColumns(candidates);
133 }
134
135
136
137
138
139
140 private String getUnambiguousCompletions(final List<CharSequence> candidates) {
141 if (candidates == null || candidates.isEmpty()) {
142 return null;
143 }
144
145
146 String[] strings = candidates.toArray(new String[candidates.size()]);
147
148 String first = strings[0];
149 StringBuilder candidate = new StringBuilder();
150
151 for (int i = 0; i < first.length(); i++) {
152 if (startsWith(first.substring(0, i + 1), strings)) {
153 candidate.append(first.charAt(i));
154 }
155 else {
156 break;
157 }
158 }
159
160 return candidate.toString();
161 }
162
163
164
165
166 private boolean startsWith(final String starts, final String[] candidates) {
167 for (String candidate : candidates) {
168 if (!candidate.startsWith(starts)) {
169 return false;
170 }
171 }
172
173 return true;
174 }
175
176 private static enum Messages
177 {
178 DISPLAY_CANDIDATES,
179 DISPLAY_CANDIDATES_YES,
180 DISPLAY_CANDIDATES_NO,;
181
182 private static final
183 ResourceBundle
184 bundle =
185 ResourceBundle.getBundle(CandidateListCompletionHandler.class.getName(), Locale.getDefault());
186
187 public String format(final Object... args) {
188 if (bundle == null)
189 return "";
190 else
191 return String.format(bundle.getString(name()), args);
192 }
193 }
194 }