1 | /*
|
---|
2 | * 02/21/2005
|
---|
3 | *
|
---|
4 | * CodeTemplate.java - A "template" (macro) for commonly-typed code.
|
---|
5 | *
|
---|
6 | * This library is distributed under a modified BSD license. See the included
|
---|
7 | * RSyntaxTextArea.License.txt file for details.
|
---|
8 | */
|
---|
9 | package org.fife.ui.rsyntaxtextarea.templates;
|
---|
10 |
|
---|
11 | import java.io.IOException;
|
---|
12 | import java.io.ObjectInputStream;
|
---|
13 | import javax.swing.text.BadLocationException;
|
---|
14 | import javax.swing.text.Caret;
|
---|
15 | import javax.swing.text.Element;
|
---|
16 |
|
---|
17 | import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
|
---|
18 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
---|
19 | import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
|
---|
20 |
|
---|
21 |
|
---|
22 | /**
|
---|
23 | * A code template that inserts static text before and after the caret.<p>
|
---|
24 | *
|
---|
25 | * For example, you can associate the identifier <code>forb</code>
|
---|
26 | * (short for "for-block") with the following code:<p>
|
---|
27 | *
|
---|
28 | * <pre>
|
---|
29 | * for (<caret>) {
|
---|
30 | *
|
---|
31 | * }
|
---|
32 | * </pre>
|
---|
33 | *
|
---|
34 | * Then, whenever you type <code>forb</code> followed by a trigger
|
---|
35 | * (e.g., a space) into a text area with this <code>CodeTemplate</code>,
|
---|
36 | * the code snippet is added in place of <code>forb</code>. Further,
|
---|
37 | * the caret is placed at the position denoted by <code><caret></code>.
|
---|
38 | *
|
---|
39 | * @author Robert Futrell
|
---|
40 | * @version 0.1
|
---|
41 | * @see CodeTemplate
|
---|
42 | */
|
---|
43 | public class StaticCodeTemplate extends AbstractCodeTemplate {
|
---|
44 |
|
---|
45 | private static final long serialVersionUID = 1;
|
---|
46 |
|
---|
47 | /**
|
---|
48 | * The code inserted before the caret position.
|
---|
49 | */
|
---|
50 | private String beforeCaret;
|
---|
51 |
|
---|
52 | /**
|
---|
53 | * The code inserted after the caret position.
|
---|
54 | */
|
---|
55 | private String afterCaret;
|
---|
56 |
|
---|
57 | /**
|
---|
58 | * Cached value representing whether <code>beforeCaret</code> contains
|
---|
59 | * one or more newlines.
|
---|
60 | */
|
---|
61 | private transient int firstBeforeNewline;
|
---|
62 |
|
---|
63 | /**
|
---|
64 | * Cached value representing whether <code>afterCaret</code> contains
|
---|
65 | * one or more newlines.
|
---|
66 | */
|
---|
67 | private transient int firstAfterNewline;
|
---|
68 |
|
---|
69 | private static final String EMPTY_STRING = "";
|
---|
70 |
|
---|
71 |
|
---|
72 | /**
|
---|
73 | * Constructor. This constructor only exists to support persistance
|
---|
74 | * through serialization.
|
---|
75 | */
|
---|
76 | public StaticCodeTemplate() {
|
---|
77 | }
|
---|
78 |
|
---|
79 |
|
---|
80 | /**
|
---|
81 | * Constructor.
|
---|
82 | *
|
---|
83 | * @param id The ID of this code template.
|
---|
84 | * @param beforeCaret The text to place before the caret.
|
---|
85 | * @param afterCaret The text to place after the caret.
|
---|
86 | */
|
---|
87 | public StaticCodeTemplate(String id, String beforeCaret, String afterCaret){
|
---|
88 | super(id);
|
---|
89 | setBeforeCaretText(beforeCaret);
|
---|
90 | setAfterCaretText(afterCaret);
|
---|
91 | }
|
---|
92 |
|
---|
93 |
|
---|
94 | /**
|
---|
95 | * Returns the text that will be placed after the caret.
|
---|
96 | *
|
---|
97 | * @return The text.
|
---|
98 | * @see #setAfterCaretText
|
---|
99 | */
|
---|
100 | public String getAfterCaretText() {
|
---|
101 | return afterCaret;
|
---|
102 | }
|
---|
103 |
|
---|
104 |
|
---|
105 | /**
|
---|
106 | * Returns the text that will be placed before the caret.
|
---|
107 | *
|
---|
108 | * @return The text.
|
---|
109 | * @see #setBeforeCaretText
|
---|
110 | */
|
---|
111 | public String getBeforeCaretText() {
|
---|
112 | return beforeCaret;
|
---|
113 | }
|
---|
114 |
|
---|
115 |
|
---|
116 | /**
|
---|
117 | * Returns the "after caret" text, with each new line indented by
|
---|
118 | * the specified amount.
|
---|
119 | *
|
---|
120 | * @param indent The amount to indent.
|
---|
121 | * @return The "after caret" text.
|
---|
122 | */
|
---|
123 | private String getAfterTextIndented(String indent) {
|
---|
124 | return getTextIndented(getAfterCaretText(), firstAfterNewline, indent);
|
---|
125 | }
|
---|
126 |
|
---|
127 |
|
---|
128 | /**
|
---|
129 | * Returns the "before caret" text, with each new line indented by
|
---|
130 | * the specified amount.
|
---|
131 | *
|
---|
132 | * @param indent The amount to indent.
|
---|
133 | * @return The "before caret" text.
|
---|
134 | */
|
---|
135 | private String getBeforeTextIndented(String indent) {
|
---|
136 | return getTextIndented(getBeforeCaretText(),firstBeforeNewline,indent);
|
---|
137 | }
|
---|
138 |
|
---|
139 |
|
---|
140 | /**
|
---|
141 | * Returns text with newlines indented by the specifed amount.
|
---|
142 | *
|
---|
143 | * @param text The original text.
|
---|
144 | * @param firstNewline The index of the first '\n' character.
|
---|
145 | * @param indent The amount to indent.
|
---|
146 | * @return The indented text.
|
---|
147 | */
|
---|
148 | private String getTextIndented(String text,int firstNewline,String indent) {
|
---|
149 | if (firstNewline==-1) {
|
---|
150 | return text;
|
---|
151 | }
|
---|
152 | int pos = 0;
|
---|
153 | int old = firstNewline+1;
|
---|
154 | StringBuffer sb = new StringBuffer(text.substring(0, old));
|
---|
155 | sb.append(indent);
|
---|
156 | while ((pos=text.indexOf('\n', old))>-1) {
|
---|
157 | sb.append(text.substring(old, pos+1));
|
---|
158 | sb.append(indent);
|
---|
159 | old = pos+1;
|
---|
160 | }
|
---|
161 | if (old<text.length()) {
|
---|
162 | sb.append(text.substring(old));
|
---|
163 | }
|
---|
164 | return sb.toString();
|
---|
165 | }
|
---|
166 |
|
---|
167 |
|
---|
168 | /**
|
---|
169 | * Invokes this code template. The changes are made to the given text
|
---|
170 | * area.
|
---|
171 | *
|
---|
172 | * @param textArea The text area to operate on.
|
---|
173 | * @throws BadLocationException If something bad happens.
|
---|
174 | */
|
---|
175 | public void invoke(RSyntaxTextArea textArea) throws BadLocationException {
|
---|
176 |
|
---|
177 | Caret c = textArea.getCaret();
|
---|
178 | int dot = c.getDot();
|
---|
179 | int mark = c.getMark();
|
---|
180 | int p0 = Math.min(dot, mark);
|
---|
181 | int p1 = Math.max(dot, mark);
|
---|
182 | RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
|
---|
183 | Element map = doc.getDefaultRootElement();
|
---|
184 |
|
---|
185 | int lineNum = map.getElementIndex(dot);
|
---|
186 | Element line = map.getElement(lineNum);
|
---|
187 | int start = line.getStartOffset();
|
---|
188 | int end = line.getEndOffset()-1; // Why always "-1"?
|
---|
189 | String s = textArea.getText(start,end-start);
|
---|
190 | int len = s.length();
|
---|
191 |
|
---|
192 | // endWS is the end of the leading whitespace
|
---|
193 | // of the current line.
|
---|
194 | int endWS = 0;
|
---|
195 | while (endWS<len && RSyntaxUtilities.isWhitespace(s.charAt(endWS))) {
|
---|
196 | endWS++;
|
---|
197 | }
|
---|
198 | s = s.substring(0, endWS);
|
---|
199 | p0 -= getID().length();
|
---|
200 | String beforeText = getBeforeTextIndented(s);
|
---|
201 | String afterText = getAfterTextIndented(s);
|
---|
202 | doc.replace(p0,p1-p0, beforeText+afterText, null);
|
---|
203 | textArea.setCaretPosition(p0+beforeText.length());
|
---|
204 |
|
---|
205 | }
|
---|
206 |
|
---|
207 |
|
---|
208 | /**
|
---|
209 | * Called when reading a serialized version of this document. This is
|
---|
210 | * overridden to initialize the transient members of this class.
|
---|
211 | *
|
---|
212 | * @param in The input stream to read from.
|
---|
213 | * @throws ClassNotFoundException Never.
|
---|
214 | * @throws IOException If an IO error occurs.
|
---|
215 | */
|
---|
216 | private void readObject(ObjectInputStream in) throws ClassNotFoundException,
|
---|
217 | IOException {
|
---|
218 | in.defaultReadObject();
|
---|
219 | // "Resetting" before and after text to the same values will replace
|
---|
220 | // nulls with empty srings, and set transient "first*Newline" values.
|
---|
221 | setBeforeCaretText(this.beforeCaret);
|
---|
222 | setAfterCaretText(this.afterCaret);
|
---|
223 | }
|
---|
224 |
|
---|
225 |
|
---|
226 | /**
|
---|
227 | * Sets the text to place after the caret.
|
---|
228 | *
|
---|
229 | * @param afterCaret The text.
|
---|
230 | * @see #getAfterCaretText()
|
---|
231 | */
|
---|
232 | public void setAfterCaretText(String afterCaret) {
|
---|
233 | this.afterCaret = afterCaret==null ? EMPTY_STRING : afterCaret;
|
---|
234 | firstAfterNewline = this.afterCaret.indexOf('\n');
|
---|
235 | }
|
---|
236 |
|
---|
237 |
|
---|
238 | /**
|
---|
239 | * Sets the text to place before the caret.
|
---|
240 | *
|
---|
241 | * @param beforeCaret The text.
|
---|
242 | * @see #getBeforeCaretText()
|
---|
243 | */
|
---|
244 | public void setBeforeCaretText(String beforeCaret) {
|
---|
245 | this.beforeCaret = beforeCaret==null ? EMPTY_STRING : beforeCaret;
|
---|
246 | firstBeforeNewline = this.beforeCaret.indexOf('\n');
|
---|
247 | }
|
---|
248 |
|
---|
249 |
|
---|
250 | /**
|
---|
251 | * Returns a string representation of this template for debugging
|
---|
252 | * purposes.
|
---|
253 | *
|
---|
254 | * @return A string representation of this template.
|
---|
255 | */
|
---|
256 | public String toString() {
|
---|
257 | return "[StaticCodeTemplate: id=" + getID() +
|
---|
258 | ", text=" + getBeforeCaretText() + "|" + getAfterCaretText() + "]";
|
---|
259 | }
|
---|
260 |
|
---|
261 |
|
---|
262 | } |
---|