Prep for 2019 release
[nikiroo-utils.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
33 /**
34 * A Word represents text that was entered by the user. It can be either
35 * whitespace or non-whitespace.
36 *
37 * Very dumb highlighting is supported, it has no sense of parsing (not even
38 * comments). For now this only highlights some Java keywords and
39 * puctuation.
40 */
41 public class Word {
42
43 // ------------------------------------------------------------------------
44 // Variables --------------------------------------------------------------
45 // ------------------------------------------------------------------------
46
47 /**
48 * The color to render this word as on screen.
49 */
50 private CellAttributes color = new CellAttributes();
51
52 /**
53 * The default color for the TEditor class.
54 */
55 private CellAttributes defaultColor = null;
56
57 /**
58 * The text highlighter to use.
59 */
60 private Highlighter highlighter = null;
61
62 /**
63 * The actual text of this word. Average word length is 6 characters,
64 * with a lot of shorter ones, so start with 3.
65 */
66 private StringBuilder text = new StringBuilder(3);
67
68 // ------------------------------------------------------------------------
69 // Constructors -----------------------------------------------------------
70 // ------------------------------------------------------------------------
71
72 /**
73 * Construct a word with one character.
74 *
75 * @param ch the first character of the word
76 * @param defaultColor the color for unhighlighted text
77 * @param highlighter the highlighter to use
78 */
79 public Word(final char ch, final CellAttributes defaultColor,
80 final Highlighter highlighter) {
81
82 this.defaultColor = defaultColor;
83 this.highlighter = highlighter;
84 text.append(ch);
85 }
86
87 /**
88 * Construct a word with an empty string.
89 *
90 * @param defaultColor the color for unhighlighted text
91 * @param highlighter the highlighter to use
92 */
93 public Word(final CellAttributes defaultColor,
94 final Highlighter highlighter) {
95
96 this.defaultColor = defaultColor;
97 this.highlighter = highlighter;
98 }
99
100 // ------------------------------------------------------------------------
101 // Word -------------------------------------------------------------------
102 // ------------------------------------------------------------------------
103
104 /**
105 * Get the color used to display this word on screen.
106 *
107 * @return the color
108 */
109 public CellAttributes getColor() {
110 return new CellAttributes(color);
111 }
112
113 /**
114 * Set the color used to display this word on screen.
115 *
116 * @param color the color
117 */
118 public void setColor(final CellAttributes color) {
119 color.setTo(color);
120 }
121
122 /**
123 * Get the text to display.
124 *
125 * @return the text
126 */
127 public String getText() {
128 return text.toString();
129 }
130
131 /**
132 * Get the on-screen display length.
133 *
134 * @return the number of cells needed to display this word
135 */
136 public int getDisplayLength() {
137 // For now, just use the text length. In the future, this will be a
138 // grapheme count.
139
140 // TODO: figure out how to handle the tab character. Do we have a
141 // global tab stops list and current word position?
142 return text.length();
143 }
144
145 /**
146 * See if this is a whitespace word. Note that empty string is
147 * considered whitespace.
148 *
149 * @return true if this word is whitespace
150 */
151 public boolean isWhitespace() {
152 if (text.length() == 0) {
153 return true;
154 }
155 if (Character.isWhitespace(text.charAt(0))) {
156 return true;
157 }
158 return false;
159 }
160
161 /**
162 * Perform highlighting.
163 */
164 public void applyHighlight() {
165 color.setTo(defaultColor);
166 if (highlighter == null) {
167 return;
168 }
169 String key = text.toString();
170 CellAttributes newColor = highlighter.getColor(key);
171 if (newColor != null) {
172 color.setTo(newColor);
173 }
174 }
175
176 /**
177 * Add a character to this word. If this is a whitespace character
178 * adding to a non-whitespace word, create a new word and return that;
179 * similarly if this a non-whitespace character adding to a whitespace
180 * word, create a new word and return that. Note package private access:
181 * this is only called by Line to figure out highlighting boundaries.
182 *
183 * @param ch the new character to add
184 * @return either this word (if it was added), or a new word that
185 * contains ch
186 */
187 public Word addChar(final char ch) {
188 if (text.length() == 0) {
189 text.append(ch);
190 return this;
191 }
192
193 // Give the highlighter the option to split here.
194 if (highlighter != null) {
195 if (highlighter.shouldSplit(ch)
196 || highlighter.shouldSplit(text.charAt(0))
197 ) {
198 Word newWord = new Word(ch, defaultColor, highlighter);
199 return newWord;
200 }
201 }
202
203 // Highlighter didn't care, so split at whitespace.
204 if (Character.isWhitespace(text.charAt(0))
205 && Character.isWhitespace(ch)
206 ) {
207 // Adding to a whitespace word, keep at it.
208 text.append(ch);
209 return this;
210 }
211 if (!Character.isWhitespace(text.charAt(0))
212 && !Character.isWhitespace(ch)
213 ) {
214 // Adding to a non-whitespace word, keep at it.
215 text.append(ch);
216 return this;
217 }
218
219 // Switching from whitespace to non-whitespace or vice versa, so
220 // split here.
221 Word newWord = new Word(ch, defaultColor, highlighter);
222 return newWord;
223 }
224
225 }