Mercurial > projects > dwt-addons
annotate dwtx/jface/text/FindReplaceDocumentAdapter.d @ 156:a9566845f1cb
...
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 25 Aug 2008 19:03:46 +0200 |
parents | 000f9136b8f7 |
children | 7926b636c282 |
rev | line source |
---|---|
129 | 1 /******************************************************************************* |
2 * Copyright (c) 2000, 2008 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 * Cagatay Calli <ccalli@gmail.com> - [find/replace] retain caps when replacing - https://bugs.eclipse.org/bugs/show_bug.cgi?id=28949 | |
11 * Cagatay Calli <ccalli@gmail.com> - [find/replace] define & fix behavior of retain caps with other escapes and text before \C - https://bugs.eclipse.org/bugs/show_bug.cgi?id=217061 | |
12 * Port to the D programming language: | |
13 * Frank Benoit <benoit@tionex.de> | |
14 *******************************************************************************/ | |
15 module dwtx.jface.text.FindReplaceDocumentAdapter; | |
16 | |
131 | 17 import dwtx.jface.text.IDocumentPartitioningListener; // packageimport |
18 import dwtx.jface.text.DefaultTextHover; // packageimport | |
19 import dwtx.jface.text.AbstractInformationControl; // packageimport | |
20 import dwtx.jface.text.TextUtilities; // packageimport | |
21 import dwtx.jface.text.IInformationControlCreatorExtension; // packageimport | |
22 import dwtx.jface.text.AbstractInformationControlManager; // packageimport | |
23 import dwtx.jface.text.ITextViewerExtension2; // packageimport | |
24 import dwtx.jface.text.IDocumentPartitioner; // packageimport | |
25 import dwtx.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport | |
26 import dwtx.jface.text.ITextSelection; // packageimport | |
27 import dwtx.jface.text.Document; // packageimport | |
28 import dwtx.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport | |
29 import dwtx.jface.text.ITextListener; // packageimport | |
30 import dwtx.jface.text.BadPartitioningException; // packageimport | |
31 import dwtx.jface.text.ITextViewerExtension5; // packageimport | |
32 import dwtx.jface.text.IDocumentPartitionerExtension3; // packageimport | |
33 import dwtx.jface.text.IUndoManager; // packageimport | |
34 import dwtx.jface.text.ITextHoverExtension2; // packageimport | |
35 import dwtx.jface.text.IRepairableDocument; // packageimport | |
36 import dwtx.jface.text.IRewriteTarget; // packageimport | |
37 import dwtx.jface.text.DefaultPositionUpdater; // packageimport | |
38 import dwtx.jface.text.RewriteSessionEditProcessor; // packageimport | |
39 import dwtx.jface.text.TextViewerHoverManager; // packageimport | |
40 import dwtx.jface.text.DocumentRewriteSession; // packageimport | |
41 import dwtx.jface.text.TextViewer; // packageimport | |
42 import dwtx.jface.text.ITextViewerExtension8; // packageimport | |
43 import dwtx.jface.text.RegExMessages; // packageimport | |
44 import dwtx.jface.text.IDelayedInputChangeProvider; // packageimport | |
45 import dwtx.jface.text.ITextOperationTargetExtension; // packageimport | |
46 import dwtx.jface.text.IWidgetTokenOwner; // packageimport | |
47 import dwtx.jface.text.IViewportListener; // packageimport | |
48 import dwtx.jface.text.GapTextStore; // packageimport | |
49 import dwtx.jface.text.MarkSelection; // packageimport | |
50 import dwtx.jface.text.IDocumentPartitioningListenerExtension; // packageimport | |
51 import dwtx.jface.text.IDocumentAdapterExtension; // packageimport | |
52 import dwtx.jface.text.IInformationControlExtension; // packageimport | |
53 import dwtx.jface.text.IDocumentPartitioningListenerExtension2; // packageimport | |
54 import dwtx.jface.text.DefaultDocumentAdapter; // packageimport | |
55 import dwtx.jface.text.ITextViewerExtension3; // packageimport | |
56 import dwtx.jface.text.IInformationControlCreator; // packageimport | |
57 import dwtx.jface.text.TypedRegion; // packageimport | |
58 import dwtx.jface.text.ISynchronizable; // packageimport | |
59 import dwtx.jface.text.IMarkRegionTarget; // packageimport | |
60 import dwtx.jface.text.TextViewerUndoManager; // packageimport | |
61 import dwtx.jface.text.IRegion; // packageimport | |
62 import dwtx.jface.text.IInformationControlExtension2; // packageimport | |
63 import dwtx.jface.text.IDocumentExtension4; // packageimport | |
64 import dwtx.jface.text.IDocumentExtension2; // packageimport | |
65 import dwtx.jface.text.IDocumentPartitionerExtension2; // packageimport | |
66 import dwtx.jface.text.Assert; // packageimport | |
67 import dwtx.jface.text.DefaultInformationControl; // packageimport | |
68 import dwtx.jface.text.IWidgetTokenOwnerExtension; // packageimport | |
69 import dwtx.jface.text.DocumentClone; // packageimport | |
70 import dwtx.jface.text.DefaultUndoManager; // packageimport | |
71 import dwtx.jface.text.IFindReplaceTarget; // packageimport | |
72 import dwtx.jface.text.IAutoEditStrategy; // packageimport | |
73 import dwtx.jface.text.ILineTrackerExtension; // packageimport | |
74 import dwtx.jface.text.IUndoManagerExtension; // packageimport | |
75 import dwtx.jface.text.TextSelection; // packageimport | |
76 import dwtx.jface.text.DefaultAutoIndentStrategy; // packageimport | |
77 import dwtx.jface.text.IAutoIndentStrategy; // packageimport | |
78 import dwtx.jface.text.IPainter; // packageimport | |
79 import dwtx.jface.text.IInformationControl; // packageimport | |
80 import dwtx.jface.text.IInformationControlExtension3; // packageimport | |
81 import dwtx.jface.text.ITextViewerExtension6; // packageimport | |
82 import dwtx.jface.text.IInformationControlExtension4; // packageimport | |
83 import dwtx.jface.text.DefaultLineTracker; // packageimport | |
84 import dwtx.jface.text.IDocumentInformationMappingExtension; // packageimport | |
85 import dwtx.jface.text.IRepairableDocumentExtension; // packageimport | |
86 import dwtx.jface.text.ITextHover; // packageimport | |
87 import dwtx.jface.text.ILineTracker; // packageimport | |
88 import dwtx.jface.text.Line; // packageimport | |
89 import dwtx.jface.text.ITextViewerExtension; // packageimport | |
90 import dwtx.jface.text.IDocumentAdapter; // packageimport | |
91 import dwtx.jface.text.TextEvent; // packageimport | |
92 import dwtx.jface.text.BadLocationException; // packageimport | |
93 import dwtx.jface.text.AbstractDocument; // packageimport | |
94 import dwtx.jface.text.AbstractLineTracker; // packageimport | |
95 import dwtx.jface.text.TreeLineTracker; // packageimport | |
96 import dwtx.jface.text.ITextPresentationListener; // packageimport | |
97 import dwtx.jface.text.Region; // packageimport | |
98 import dwtx.jface.text.ITextViewer; // packageimport | |
99 import dwtx.jface.text.IDocumentInformationMapping; // packageimport | |
100 import dwtx.jface.text.MarginPainter; // packageimport | |
101 import dwtx.jface.text.IPaintPositionManager; // packageimport | |
102 import dwtx.jface.text.TextPresentation; // packageimport | |
103 import dwtx.jface.text.IFindReplaceTargetExtension; // packageimport | |
104 import dwtx.jface.text.ISlaveDocumentManagerExtension; // packageimport | |
105 import dwtx.jface.text.ISelectionValidator; // packageimport | |
106 import dwtx.jface.text.IDocumentExtension; // packageimport | |
107 import dwtx.jface.text.PropagatingFontFieldEditor; // packageimport | |
108 import dwtx.jface.text.ConfigurableLineTracker; // packageimport | |
109 import dwtx.jface.text.SlaveDocumentEvent; // packageimport | |
110 import dwtx.jface.text.IDocumentListener; // packageimport | |
111 import dwtx.jface.text.PaintManager; // packageimport | |
112 import dwtx.jface.text.IFindReplaceTargetExtension3; // packageimport | |
113 import dwtx.jface.text.ITextDoubleClickStrategy; // packageimport | |
114 import dwtx.jface.text.IDocumentExtension3; // packageimport | |
115 import dwtx.jface.text.Position; // packageimport | |
116 import dwtx.jface.text.TextMessages; // packageimport | |
117 import dwtx.jface.text.CopyOnWriteTextStore; // packageimport | |
118 import dwtx.jface.text.WhitespaceCharacterPainter; // packageimport | |
119 import dwtx.jface.text.IPositionUpdater; // packageimport | |
120 import dwtx.jface.text.DefaultTextDoubleClickStrategy; // packageimport | |
121 import dwtx.jface.text.ListLineTracker; // packageimport | |
122 import dwtx.jface.text.ITextInputListener; // packageimport | |
123 import dwtx.jface.text.BadPositionCategoryException; // packageimport | |
124 import dwtx.jface.text.IWidgetTokenKeeperExtension; // packageimport | |
125 import dwtx.jface.text.IInputChangedListener; // packageimport | |
126 import dwtx.jface.text.ITextOperationTarget; // packageimport | |
127 import dwtx.jface.text.IDocumentInformationMappingExtension2; // packageimport | |
128 import dwtx.jface.text.ITextViewerExtension7; // packageimport | |
129 import dwtx.jface.text.IInformationControlExtension5; // packageimport | |
130 import dwtx.jface.text.IDocumentRewriteSessionListener; // packageimport | |
131 import dwtx.jface.text.JFaceTextUtil; // packageimport | |
132 import dwtx.jface.text.AbstractReusableInformationControlCreator; // packageimport | |
133 import dwtx.jface.text.TabsToSpacesConverter; // packageimport | |
134 import dwtx.jface.text.CursorLinePainter; // packageimport | |
135 import dwtx.jface.text.ITextHoverExtension; // packageimport | |
136 import dwtx.jface.text.IEventConsumer; // packageimport | |
137 import dwtx.jface.text.IDocument; // packageimport | |
138 import dwtx.jface.text.IWidgetTokenKeeper; // packageimport | |
139 import dwtx.jface.text.DocumentCommand; // packageimport | |
140 import dwtx.jface.text.TypedPosition; // packageimport | |
141 import dwtx.jface.text.IEditingSupportRegistry; // packageimport | |
142 import dwtx.jface.text.IDocumentPartitionerExtension; // packageimport | |
143 import dwtx.jface.text.AbstractHoverInformationControlManager; // packageimport | |
144 import dwtx.jface.text.IEditingSupport; // packageimport | |
145 import dwtx.jface.text.IMarkSelection; // packageimport | |
146 import dwtx.jface.text.ISlaveDocumentManager; // packageimport | |
147 import dwtx.jface.text.DocumentEvent; // packageimport | |
148 import dwtx.jface.text.DocumentPartitioningChangedEvent; // packageimport | |
149 import dwtx.jface.text.ITextStore; // packageimport | |
150 import dwtx.jface.text.JFaceTextMessages; // packageimport | |
151 import dwtx.jface.text.DocumentRewriteSessionEvent; // packageimport | |
152 import dwtx.jface.text.SequentialRewriteTextStore; // packageimport | |
153 import dwtx.jface.text.DocumentRewriteSessionType; // packageimport | |
154 import dwtx.jface.text.TextAttribute; // packageimport | |
155 import dwtx.jface.text.ITextViewerExtension4; // packageimport | |
156 import dwtx.jface.text.ITypedRegion; // packageimport | |
157 | |
158 | |
129 | 159 import dwt.dwthelper.utils; |
160 | |
161 import java.util.regex.Matcher; | |
162 import java.util.regex.Pattern; | |
163 import java.util.regex.PatternSyntaxException; | |
164 | |
165 import dwtx.core.runtime.Assert; | |
166 | |
167 | |
168 /** | |
169 * Provides search and replace operations on | |
170 * {@link dwtx.jface.text.IDocument}. | |
171 * <p> | |
172 * Replaces | |
173 * {@link dwtx.jface.text.IDocument#search(int, String, bool, bool, bool)}. | |
174 * | |
175 * @since 3.0 | |
176 */ | |
177 public class FindReplaceDocumentAdapter : CharSequence { | |
178 | |
179 /** | |
180 * Internal type for operation codes. | |
181 */ | |
182 private static class FindReplaceOperationCode { | |
183 } | |
184 | |
185 // Find/replace operation codes. | |
147 | 186 private static const FindReplaceOperationCode FIND_FIRST= new FindReplaceOperationCode(); |
187 private static const FindReplaceOperationCode FIND_NEXT= new FindReplaceOperationCode(); | |
188 private static const FindReplaceOperationCode REPLACE= new FindReplaceOperationCode(); | |
189 private static const FindReplaceOperationCode REPLACE_FIND_NEXT= new FindReplaceOperationCode(); | |
129 | 190 |
191 /** | |
192 * Retain case mode constants. | |
193 * @since 3.4 | |
194 */ | |
147 | 195 private static const int RC_MIXED= 0; |
196 private static const int RC_UPPER= 1; | |
197 private static const int RC_LOWER= 2; | |
198 private static const int RC_FIRSTUPPER= 3; | |
129 | 199 |
200 | |
201 /** | |
202 * The adapted document. | |
203 */ | |
204 private IDocument fDocument; | |
205 | |
206 /** | |
207 * State for findReplace. | |
208 */ | |
209 private FindReplaceOperationCode fFindReplaceState= null; | |
210 | |
211 /** | |
212 * The matcher used in findReplace. | |
213 */ | |
214 private Matcher fFindReplaceMatcher; | |
215 | |
216 /** | |
217 * The match offset from the last findReplace call. | |
218 */ | |
219 private int fFindReplaceMatchOffset; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
220 |
129 | 221 /** |
222 * Retain case mode | |
223 */ | |
224 private int fRetainCaseMode; | |
225 | |
226 /** | |
227 * Constructs a new find replace document adapter. | |
228 * | |
229 * @param document the adapted document | |
230 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
231 public this(IDocument document) { |
129 | 232 Assert.isNotNull(document); |
233 fDocument= document; | |
234 } | |
235 | |
236 /** | |
237 * Returns the location of a given string in this adapter's document based on a set of search criteria. | |
238 * | |
239 * @param startOffset document offset at which search starts | |
240 * @param findString the string to find | |
241 * @param forwardSearch the search direction | |
242 * @param caseSensitive indicates whether lower and upper case should be distinguished | |
243 * @param wholeWord indicates whether the findString should be limited by white spaces as | |
244 * defined by Character.isWhiteSpace. Must not be used in combination with <code>regExSearch</code>. | |
245 * @param regExSearch if <code>true</code> findString represents a regular expression | |
246 * Must not be used in combination with <code>wholeWord</code>. | |
247 * @return the find or replace region or <code>null</code> if there was no match | |
248 * @throws BadLocationException if startOffset is an invalid document offset | |
249 * @throws PatternSyntaxException if a regular expression has invalid syntax | |
250 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
251 public IRegion find(int startOffset, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord, bool regExSearch) { |
129 | 252 Assert.isTrue(!(regExSearch && wholeWord)); |
253 | |
254 // Adjust offset to special meaning of -1 | |
255 if (startOffset is -1 && forwardSearch) | |
256 startOffset= 0; | |
257 if (startOffset is -1 && !forwardSearch) | |
258 startOffset= length() - 1; | |
259 | |
260 return findReplace(FIND_FIRST, startOffset, findString, null, forwardSearch, caseSensitive, wholeWord, regExSearch); | |
261 } | |
262 | |
263 /** | |
264 * Stateful findReplace executes a FIND, REPLACE, REPLACE_FIND or FIND_FIRST operation. | |
265 * In case of REPLACE and REPLACE_FIND it sends a <code>DocumentEvent</code> to all | |
266 * registered <code>IDocumentListener</code>. | |
267 * | |
268 * @param startOffset document offset at which search starts | |
269 * this value is only used in the FIND_FIRST operation and otherwise ignored | |
270 * @param findString the string to find | |
271 * this value is only used in the FIND_FIRST operation and otherwise ignored | |
272 * @param replaceText the string to replace the current match | |
273 * this value is only used in the REPLACE and REPLACE_FIND operations and otherwise ignored | |
274 * @param forwardSearch the search direction | |
275 * @param caseSensitive indicates whether lower and upper case should be distinguished | |
276 * @param wholeWord indicates whether the findString should be limited by white spaces as | |
277 * defined by Character.isWhiteSpace. Must not be used in combination with <code>regExSearch</code>. | |
278 * @param regExSearch if <code>true</code> this operation represents a regular expression | |
279 * Must not be used in combination with <code>wholeWord</code>. | |
280 * @param operationCode specifies what kind of operation is executed | |
281 * @return the find or replace region or <code>null</code> if there was no match | |
282 * @throws BadLocationException if startOffset is an invalid document offset | |
283 * @throws IllegalStateException if a REPLACE or REPLACE_FIND operation is not preceded by a successful FIND operation | |
284 * @throws PatternSyntaxException if a regular expression has invalid syntax | |
285 */ | |
156 | 286 private IRegion findReplace(FindReplaceOperationCode operationCode, int startOffset, String findString, String replaceText, bool forwardSearch, bool caseSensitive, bool wholeWord, bool regExSearch) { |
129 | 287 |
288 // Validate option combinations | |
289 Assert.isTrue(!(regExSearch && wholeWord)); | |
290 | |
291 // Validate state | |
292 if ((operationCode is REPLACE || operationCode is REPLACE_FIND_NEXT) && (fFindReplaceState !is FIND_FIRST && fFindReplaceState !is FIND_NEXT)) | |
293 throw new IllegalStateException("illegal findReplace state: cannot replace without preceding find"); //$NON-NLS-1$ | |
294 | |
295 if (operationCode is FIND_FIRST) { | |
296 // Reset | |
297 | |
298 if (findString is null || findString.length() is 0) | |
299 return null; | |
300 | |
301 // Validate start offset | |
302 if (startOffset < 0 || startOffset >= length()) | |
303 throw new BadLocationException(); | |
304 | |
305 int patternFlags= 0; | |
306 | |
307 if (regExSearch) { | |
308 patternFlags |= Pattern.MULTILINE; | |
309 findString= substituteLinebreak(findString); | |
310 } | |
311 | |
312 if (!caseSensitive) | |
313 patternFlags |= Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE; | |
314 | |
315 if (wholeWord) | |
316 findString= "\\b" + findString + "\\b"; //$NON-NLS-1$ //$NON-NLS-2$ | |
317 | |
318 if (!regExSearch && !wholeWord) | |
319 findString= asRegPattern(findString); | |
320 | |
321 fFindReplaceMatchOffset= startOffset; | |
322 if (fFindReplaceMatcher !is null && fFindReplaceMatcher.pattern().pattern().equals(findString) && fFindReplaceMatcher.pattern().flags() is patternFlags) { | |
323 /* | |
324 * Commented out for optimization: | |
325 * The call is not needed since FIND_FIRST uses find(int) which resets the matcher | |
326 */ | |
327 // fFindReplaceMatcher.reset(); | |
328 } else { | |
329 Pattern pattern= Pattern.compile(findString, patternFlags); | |
330 fFindReplaceMatcher= pattern.matcher(this); | |
331 } | |
332 } | |
333 | |
334 // Set state | |
335 fFindReplaceState= operationCode; | |
336 | |
337 if (operationCode is REPLACE || operationCode is REPLACE_FIND_NEXT) { | |
338 if (regExSearch) { | |
339 Pattern pattern= fFindReplaceMatcher.pattern(); | |
340 String prevMatch= fFindReplaceMatcher.group(); | |
341 try { | |
342 replaceText= interpretReplaceEscapes(replaceText, prevMatch); | |
343 Matcher replaceTextMatcher= pattern.matcher(prevMatch); | |
344 replaceText= replaceTextMatcher.replaceFirst(replaceText); | |
345 } catch (IndexOutOfBoundsException ex) { | |
346 throw new PatternSyntaxException(ex.getLocalizedMessage(), replaceText, -1); | |
347 } | |
348 } | |
349 | |
350 int offset= fFindReplaceMatcher.start(); | |
351 int length= fFindReplaceMatcher.group().length(); | |
352 | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
353 if (cast(IRepairableDocumentExtension)fDocument |
134 | 354 && (cast(IRepairableDocumentExtension)fDocument).isLineInformationRepairNeeded(offset, length, replaceText)) { |
129 | 355 String message= TextMessages.getString("FindReplaceDocumentAdapter.incompatibleLineDelimiter"); //$NON-NLS-1$ |
356 throw new PatternSyntaxException(message, replaceText, offset); | |
357 } | |
358 | |
359 fDocument.replace(offset, length, replaceText); | |
360 | |
361 if (operationCode is REPLACE) { | |
362 return new Region(offset, replaceText.length()); | |
363 } | |
364 } | |
365 | |
366 if (operationCode !is REPLACE) { | |
367 if (forwardSearch) { | |
368 | |
369 bool found= false; | |
370 if (operationCode is FIND_FIRST) | |
371 found= fFindReplaceMatcher.find(startOffset); | |
372 else | |
373 found= fFindReplaceMatcher.find(); | |
374 | |
375 if (operationCode is REPLACE_FIND_NEXT) | |
376 fFindReplaceState= FIND_NEXT; | |
377 | |
378 if (found && fFindReplaceMatcher.group().length() > 0) | |
379 return new Region(fFindReplaceMatcher.start(), fFindReplaceMatcher.group().length()); | |
380 return null; | |
381 } | |
382 | |
383 // backward search | |
384 bool found= fFindReplaceMatcher.find(0); | |
385 int index= -1; | |
386 int length= -1; | |
387 while (found && fFindReplaceMatcher.start() + fFindReplaceMatcher.group().length() <= fFindReplaceMatchOffset + 1) { | |
388 index= fFindReplaceMatcher.start(); | |
389 length= fFindReplaceMatcher.group().length(); | |
390 found= fFindReplaceMatcher.find(index + 1); | |
391 } | |
392 fFindReplaceMatchOffset= index; | |
393 if (index > -1) { | |
394 // must set matcher to correct position | |
395 fFindReplaceMatcher.find(index); | |
396 return new Region(index, length); | |
397 } | |
398 return null; | |
399 } | |
400 | |
401 return null; | |
402 } | |
403 | |
404 /** | |
405 * Substitutes \R in a regex find pattern with (?>\r\n?|\n) | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
406 * |
129 | 407 * @param findString the original find pattern |
408 * @return the transformed find pattern | |
409 * @throws PatternSyntaxException if \R is added at an illegal position (e.g. in a character set) | |
410 * @since 3.4 | |
411 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
412 private String substituteLinebreak(String findString) { |
129 | 413 int length= findString.length(); |
414 StringBuffer buf= new StringBuffer(length); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
415 |
129 | 416 int inCharGroup= 0; |
417 int inBraces= 0; | |
418 bool inQuote= false; | |
419 for (int i= 0; i < length; i++) { | |
420 char ch= findString.charAt(i); | |
421 switch (ch) { | |
422 case '[': | |
423 buf.append(ch); | |
424 if (! inQuote) | |
425 inCharGroup++; | |
426 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
427 |
129 | 428 case ']': |
429 buf.append(ch); | |
430 if (! inQuote) | |
431 inCharGroup--; | |
432 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
433 |
129 | 434 case '{': |
435 buf.append(ch); | |
436 if (! inQuote && inCharGroup is 0) | |
437 inBraces++; | |
438 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
439 |
129 | 440 case '}': |
441 buf.append(ch); | |
442 if (! inQuote && inCharGroup is 0) | |
443 inBraces--; | |
444 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
445 |
129 | 446 case '\\': |
447 if (i + 1 < length) { | |
448 char ch1= findString.charAt(i + 1); | |
449 if (inQuote) { | |
450 if (ch1 is 'E') | |
451 inQuote= false; | |
452 buf.append(ch).append(ch1); | |
453 i++; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
454 |
129 | 455 } else if (ch1 is 'R') { |
456 if (inCharGroup > 0 || inBraces > 0) { | |
457 String msg= TextMessages.getString("FindReplaceDocumentAdapter.illegalLinebreak"); //$NON-NLS-1$ | |
458 throw new PatternSyntaxException(msg, findString, i); | |
459 } | |
460 buf.append("(?>\\r\\n?|\\n)"); //$NON-NLS-1$ | |
461 i++; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
462 |
129 | 463 } else { |
464 if (ch1 is 'Q') { | |
465 inQuote= true; | |
466 } | |
467 buf.append(ch).append(ch1); | |
468 i++; | |
469 } | |
470 } else { | |
471 buf.append(ch); | |
472 } | |
473 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
474 |
129 | 475 default: |
476 buf.append(ch); | |
477 break; | |
478 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
479 |
129 | 480 } |
481 return buf.toString(); | |
482 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
483 |
129 | 484 /** |
485 * Interprets current Retain Case mode (all upper-case,all lower-case,capitalized or mixed) | |
486 * and appends the character <code>ch</code> to <code>buf</code> after processing. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
487 * |
129 | 488 * @param buf the output buffer |
489 * @param ch the character to process | |
490 * @since 3.4 | |
491 */ | |
492 private void interpretRetainCase(StringBuffer buf, char ch) { | |
493 if (fRetainCaseMode is RC_UPPER) | |
494 buf.append(Character.toUpperCase(ch)); | |
495 else if (fRetainCaseMode is RC_LOWER) | |
496 buf.append(Character.toLowerCase(ch)); | |
497 else if (fRetainCaseMode is RC_FIRSTUPPER) { | |
498 buf.append(Character.toUpperCase(ch)); | |
499 fRetainCaseMode= RC_MIXED; | |
500 } else | |
501 buf.append(ch); | |
502 } | |
503 | |
504 /** | |
505 * Interprets escaped characters in the given replace pattern. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
506 * |
129 | 507 * @param replaceText the replace pattern |
508 * @param foundText the found pattern to be replaced | |
509 * @return a replace pattern with escaped characters substituted by the respective characters | |
510 * @since 3.4 | |
511 */ | |
512 private String interpretReplaceEscapes(String replaceText, String foundText) { | |
513 int length= replaceText.length(); | |
514 bool inEscape= false; | |
515 StringBuffer buf= new StringBuffer(length); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
516 |
129 | 517 /* every string we did not check looks mixed at first |
518 * so initialize retain case mode with RC_MIXED | |
519 */ | |
520 fRetainCaseMode= RC_MIXED; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
521 |
129 | 522 for (int i= 0; i < length; i++) { |
523 final char ch= replaceText.charAt(i); | |
524 if (inEscape) { | |
525 i= interpretReplaceEscape(ch, i, buf, replaceText, foundText); | |
526 inEscape= false; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
527 |
129 | 528 } else if (ch is '\\') { |
529 inEscape= true; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
530 |
129 | 531 } else if (ch is '$') { |
532 buf.append(ch); | |
533 | |
534 /* | |
535 * Feature in java.util.regex.Matcher#replaceFirst(String): | |
536 * $00, $000, etc. are interpreted as $0 and | |
537 * $01, $001, etc. are interpreted as $1, etc. . | |
538 * If we support \0 as replacement pattern for capturing group 0, | |
539 * it would not be possible any more to write a replacement pattern | |
540 * that appends 0 to a capturing group (like $0\0). | |
541 * The fix is to interpret \00 and $00 as $0\0, and | |
542 * \01 and $01 as $0\1, etc. | |
543 */ | |
544 if (i + 2 < length) { | |
545 char ch1= replaceText.charAt(i + 1); | |
546 char ch2= replaceText.charAt(i + 2); | |
547 if (ch1 is '0' && '0' <= ch2 && ch2 <= '9') { | |
548 buf.append("0\\"); //$NON-NLS-1$ | |
549 i++; // consume the 0 | |
550 } | |
551 } | |
552 } else { | |
553 interpretRetainCase(buf, ch); | |
554 } | |
555 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
556 |
129 | 557 if (inEscape) { |
558 // '\' as last character is invalid, but we still add it to get an error message | |
559 buf.append('\\'); | |
560 } | |
561 return buf.toString(); | |
562 } | |
563 | |
564 /** | |
565 * Interprets the escaped character <code>ch</code> at offset <code>i</code> | |
566 * of the <code>replaceText</code> and appends the interpretation to <code>buf</code>. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
567 * |
129 | 568 * @param ch the escaped character |
569 * @param i the offset | |
570 * @param buf the output buffer | |
571 * @param replaceText the original replace pattern | |
572 * @param foundText the found pattern to be replaced | |
573 * @return the new offset | |
574 * @since 3.4 | |
575 */ | |
156 | 576 private int interpretReplaceEscape(char ch, int i, StringBuffer buf, String replaceText, String foundText) { |
129 | 577 int length= replaceText.length(); |
578 switch (ch) { | |
579 case 'r': | |
580 buf.append('\r'); | |
581 break; | |
582 case 'n': | |
583 buf.append('\n'); | |
584 break; | |
585 case 't': | |
586 buf.append('\t'); | |
587 break; | |
588 case 'f': | |
589 buf.append('\f'); | |
590 break; | |
591 case 'a': | |
592 buf.append('\u0007'); | |
593 break; | |
594 case 'e': | |
595 buf.append('\u001B'); | |
596 break; | |
597 case 'R': //see http://www.unicode.org/unicode/reports/tr18/#Line_Boundaries | |
598 buf.append(TextUtilities.getDefaultLineDelimiter(fDocument)); | |
599 break; | |
600 /* | |
601 * \0 for octal is not supported in replace string, since it | |
602 * would conflict with capturing group \0, etc. | |
603 */ | |
604 case '0': | |
605 buf.append('$').append(ch); | |
606 /* | |
607 * See explanation in "Feature in java.util.regex.Matcher#replaceFirst(String)" | |
608 * in interpretReplaceEscape(String) above. | |
609 */ | |
610 if (i + 1 < length) { | |
611 char ch1= replaceText.charAt(i + 1); | |
612 if ('0' <= ch1 && ch1 <= '9') { | |
613 buf.append('\\'); | |
614 } | |
615 } | |
616 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
617 |
129 | 618 case '1': |
619 case '2': | |
620 case '3': | |
621 case '4': | |
622 case '5': | |
623 case '6': | |
624 case '7': | |
625 case '8': | |
626 case '9': | |
627 buf.append('$').append(ch); | |
628 break; | |
629 | |
630 case 'c': | |
631 if (i + 1 < length) { | |
632 char ch1= replaceText.charAt(i + 1); | |
134 | 633 interpretRetainCase(buf, cast(wchar)(ch1 ^ 64)); |
129 | 634 i++; |
635 } else { | |
636 String msg= TextMessages.getFormattedString("FindReplaceDocumentAdapter.illegalControlEscape", "\\c"); //$NON-NLS-1$ //$NON-NLS-2$ | |
637 throw new PatternSyntaxException(msg, replaceText, i); | |
638 } | |
639 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
640 |
129 | 641 case 'x': |
642 if (i + 2 < length) { | |
643 int parsedInt; | |
644 try { | |
645 parsedInt= Integer.parseInt(replaceText.substring(i + 1, i + 3), 16); | |
646 if (parsedInt < 0) | |
647 throw new NumberFormatException(); | |
648 } catch (NumberFormatException e) { | |
649 String msg= TextMessages.getFormattedString("FindReplaceDocumentAdapter.illegalHexEscape", replaceText.substring(i - 1, i + 3)); //$NON-NLS-1$ | |
650 throw new PatternSyntaxException(msg, replaceText, i); | |
651 } | |
134 | 652 interpretRetainCase(buf, cast(wchar) parsedInt); |
129 | 653 i+= 2; |
654 } else { | |
655 String msg= TextMessages.getFormattedString("FindReplaceDocumentAdapter.illegalHexEscape", replaceText.substring(i - 1, length)); //$NON-NLS-1$ | |
656 throw new PatternSyntaxException(msg, replaceText, i); | |
657 } | |
658 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
659 |
129 | 660 case 'u': |
661 if (i + 4 < length) { | |
662 int parsedInt; | |
663 try { | |
664 parsedInt= Integer.parseInt(replaceText.substring(i + 1, i + 5), 16); | |
665 if (parsedInt < 0) | |
666 throw new NumberFormatException(); | |
667 } catch (NumberFormatException e) { | |
668 String msg= TextMessages.getFormattedString("FindReplaceDocumentAdapter.illegalUnicodeEscape", replaceText.substring(i - 1, i + 5)); //$NON-NLS-1$ | |
669 throw new PatternSyntaxException(msg, replaceText, i); | |
670 } | |
134 | 671 interpretRetainCase(buf, cast(wchar) parsedInt); |
129 | 672 i+= 4; |
673 } else { | |
674 String msg= TextMessages.getFormattedString("FindReplaceDocumentAdapter.illegalUnicodeEscape", replaceText.substring(i - 1, length)); //$NON-NLS-1$ | |
675 throw new PatternSyntaxException(msg, replaceText, i); | |
676 } | |
677 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
678 |
129 | 679 case 'C': |
680 if(foundText.toUpperCase().equals(foundText)) // is whole match upper-case? | |
681 fRetainCaseMode= RC_UPPER; | |
682 else if (foundText.toLowerCase().equals(foundText)) // is whole match lower-case? | |
683 fRetainCaseMode= RC_LOWER; | |
684 else if(Character.isUpperCase(foundText.charAt(0))) // is first character upper-case? | |
685 fRetainCaseMode= RC_FIRSTUPPER; | |
686 else | |
687 fRetainCaseMode= RC_MIXED; | |
688 break; | |
689 | |
690 default: | |
691 // unknown escape k: append uninterpreted \k | |
692 buf.append('\\').append(ch); | |
693 break; | |
694 } | |
695 return i; | |
696 } | |
697 | |
698 /** | |
699 * Converts a non-regex string to a pattern | |
700 * that can be used with the regex search engine. | |
701 * | |
702 * @param string the non-regex pattern | |
703 * @return the string converted to a regex pattern | |
704 */ | |
705 private String asRegPattern(String string) { | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
706 StringBuffer out_= new StringBuffer(string.length()); |
129 | 707 bool quoting= false; |
708 | |
709 for (int i= 0, length= string.length(); i < length; i++) { | |
710 char ch= string.charAt(i); | |
711 if (ch is '\\') { | |
712 if (quoting) { | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
713 out_.append("\\E"); //$NON-NLS-1$ |
129 | 714 quoting= false; |
715 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
716 out_.append("\\\\"); //$NON-NLS-1$ |
129 | 717 continue; |
718 } | |
719 if (!quoting) { | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
720 out_.append("\\Q"); //$NON-NLS-1$ |
129 | 721 quoting= true; |
722 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
723 out_.append(ch); |
129 | 724 } |
725 if (quoting) | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
726 out_.append("\\E"); //$NON-NLS-1$ |
129 | 727 |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
728 return out_.toString(); |
129 | 729 } |
730 | |
731 /** | |
732 * Substitutes the previous match with the given text. | |
733 * Sends a <code>DocumentEvent</code> to all registered <code>IDocumentListener</code>. | |
734 * | |
735 * @param text the substitution text | |
736 * @param regExReplace if <code>true</code> <code>text</code> represents a regular expression | |
737 * @return the replace region or <code>null</code> if there was no match | |
738 * @throws BadLocationException if startOffset is an invalid document offset | |
739 * @throws IllegalStateException if a REPLACE or REPLACE_FIND operation is not preceded by a successful FIND operation | |
740 * @throws PatternSyntaxException if a regular expression has invalid syntax | |
741 * | |
742 * @see DocumentEvent | |
743 * @see IDocumentListener | |
744 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
745 public IRegion replace(String text, bool regExReplace) { |
129 | 746 return findReplace(REPLACE, -1, null, text, false, false, false, regExReplace); |
747 } | |
748 | |
749 // ---------- CharSequence implementation ---------- | |
750 | |
751 /* | |
752 * @see java.lang.CharSequence#length() | |
753 */ | |
754 public int length() { | |
755 return fDocument.getLength(); | |
756 } | |
757 | |
758 /* | |
759 * @see java.lang.CharSequence#charAt(int) | |
760 */ | |
761 public char charAt(int index) { | |
762 try { | |
763 return fDocument.getChar(index); | |
764 } catch (BadLocationException e) { | |
765 throw new IndexOutOfBoundsException(); | |
766 } | |
767 } | |
768 | |
769 /* | |
770 * @see java.lang.CharSequence#subSequence(int, int) | |
771 */ | |
772 public CharSequence subSequence(int start, int end) { | |
773 try { | |
774 return fDocument.get(start, end - start); | |
775 } catch (BadLocationException e) { | |
776 throw new IndexOutOfBoundsException(); | |
777 } | |
778 } | |
779 | |
780 /* | |
781 * @see java.lang.Object#toString() | |
782 */ | |
783 public String toString() { | |
784 return fDocument.get(); | |
785 } | |
786 } |