Add 'src/jexer/' from commit 'cf01c92f5809a0732409e280fb0f32f27393618d'
[fanfix.git] / src / jexer / teditor / Word.java
1 /*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2019 Kevin Lamonte
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29 package jexer.teditor;
30
31 import jexer.bits.CellAttributes;
32 import jexer.bits.StringUtils;
33
34 /**
35 * A Word represents text that was entered by the user. It can be either
36 * whitespace or non-whitespace.
37 *
38 * Very dumb highlighting is supported, it has no sense of parsing (not even
39 * comments). For now this only highlights some Java keywords and
40 * puctuation.
41 */
42 public class Word {
43
44 // ------------------------------------------------------------------------
45 // Variables --------------------------------------------------------------
46 // ------------------------------------------------------------------------
47
48 /**
49 * The color to render this word as on screen.
50 */
51 private CellAttributes color = new CellAttributes();
52
53 /**
54 * The default color for the TEditor class.
55 */
56 private CellAttributes defaultColor = null;
57
58 /**
59 * The text highlighter to use.
60 */
61 private Highlighter highlighter = null;
62
63 /**
64 * The actual text of this word. Average word length is 6 characters,
65 * with a lot of shorter ones, so start with 3.
66 */
67 private StringBuilder text = new StringBuilder(3);
68
69 // ------------------------------------------------------------------------
70 // Constructors -----------------------------------------------------------
71 // ------------------------------------------------------------------------
72
73 /**
74 * Construct a word with one character.
75 *
76 * @param ch the first character of the word
77 * @param defaultColor the color for unhighlighted text
78 * @param highlighter the highlighter to use
79 */
80 public Word(final int ch, final CellAttributes defaultColor,
81 final Highlighter highlighter) {
82
83 this.defaultColor = defaultColor;
84 this.highlighter = highlighter;
85 text.append(Character.toChars(ch));
86 }
87
88 /**
89 * Construct a word with an empty string.
90 *
91 * @param defaultColor the color for unhighlighted text
92 * @param highlighter the highlighter to use
93 */
94 public Word(final CellAttributes defaultColor,
95 final Highlighter highlighter) {
96
97 this.defaultColor = defaultColor;
98 this.highlighter = highlighter;
99 }
100
101 // ------------------------------------------------------------------------
102 // Word -------------------------------------------------------------------
103 // ------------------------------------------------------------------------
104
105 /**
106 * Get the color used to display this word on screen.
107 *
108 * @return the color
109 */
110 public CellAttributes getColor() {
111 return new CellAttributes(color);
112 }
113
114 /**
115 * Set the color used to display this word on screen.
116 *
117 * @param color the color
118 */
119 public void setColor(final CellAttributes color) {
120 color.setTo(color);
121 }
122
123 /**
124 * Get the text to display.
125 *
126 * @return the text
127 */
128 public String getText() {
129 return text.toString();
130 }
131
132 /**
133 * Get the on-screen display length.
134 *
135 * @return the number of cells needed to display this word
136 */
137 public int getDisplayLength() {
138 // For now, just use the text length. In the future, this will be a
139 // grapheme count.
140
141 // TODO: figure out how to handle the tab character. Do we have a
142 // global tab stops list and current word position?
143 return StringUtils.width(text.toString());
144 }
145
146 /**
147 * See if this is a whitespace word. Note that empty string is
148 * considered whitespace.
149 *
150 * @return true if this word is whitespace
151 */
152 public boolean isWhitespace() {
153 if (text.length() == 0) {
154 return true;
155 }
156 if (Character.isWhitespace(text.charAt(0))) {
157 return true;
158 }
159 return false;
160 }
161
162 /**
163 * Perform highlighting.
164 */
165 public void applyHighlight() {
166 color.setTo(defaultColor);
167 if (highlighter == null) {
168 return;
169 }
170 String key = text.toString();
171 CellAttributes newColor = highlighter.getColor(key);
172 if (newColor != null) {
173 color.setTo(newColor);
174 }
175 }
176
177 /**
178 * Add a character to this word. If this is a whitespace character
179 * adding to a non-whitespace word, create a new word and return that;
180 * similarly if this a non-whitespace character adding to a whitespace
181 * word, create a new word and return that. Note package private access:
182 * this is only called by Line to figure out highlighting boundaries.
183 *
184 * @param ch the new character to add
185 * @return either this word (if it was added), or a new word that
186 * contains ch
187 */
188 public Word addChar(final int ch) {
189 if (text.length() == 0) {
190 text.append(Character.toChars(ch));
191 return this;
192 }
193
194 // Give the highlighter the option to split here.
195 if (highlighter != null) {
196 if (highlighter.shouldSplit(ch)
197 || highlighter.shouldSplit(text.charAt(0))
198 ) {
199 Word newWord = new Word(ch, defaultColor, highlighter);
200 return newWord;
201 }
202 }
203
204 // Highlighter didn't care, so split at whitespace.
205 if (Character.isWhitespace(text.charAt(0))
206 && Character.isWhitespace(ch)
207 ) {
208 // Adding to a whitespace word, keep at it.
209 text.append(Character.toChars(ch));
210 return this;
211 }
212 if (!Character.isWhitespace(text.charAt(0))
213 && !Character.isWhitespace(ch)
214 ) {
215 // Adding to a non-whitespace word, keep at it.
216 text.append(Character.toChars(ch));
217 return this;
218 }
219
220 // Switching from whitespace to non-whitespace or vice versa, so
221 // split here.
222 Word newWord = new Word(ch, defaultColor, highlighter);
223 return newWord;
224 }
225
226 }