Mercurial > projects > dwt-addons
annotate dwtx/jface/text/FindReplaceDocumentAdapter.d @ 159:7926b636c282
...
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Wed, 27 Aug 2008 01:57:58 +0200 |
parents | a9566845f1cb |
children | 3678e4f1a766 |
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 | |
129 | 158 import dwt.dwthelper.utils; |
159 | 159 import dwtx.dwtxhelper.regex; |
129 | 160 |
161 import dwtx.core.runtime.Assert; | |
162 | |
163 | |
164 /** | |
165 * Provides search and replace operations on | |
166 * {@link dwtx.jface.text.IDocument}. | |
167 * <p> | |
168 * Replaces | |
169 * {@link dwtx.jface.text.IDocument#search(int, String, bool, bool, bool)}. | |
170 * | |
171 * @since 3.0 | |
172 */ | |
173 public class FindReplaceDocumentAdapter : CharSequence { | |
174 | |
175 /** | |
176 * Internal type for operation codes. | |
177 */ | |
178 private static class FindReplaceOperationCode { | |
179 } | |
180 | |
181 // Find/replace operation codes. | |
147 | 182 private static const FindReplaceOperationCode FIND_FIRST= new FindReplaceOperationCode(); |
183 private static const FindReplaceOperationCode FIND_NEXT= new FindReplaceOperationCode(); | |
184 private static const FindReplaceOperationCode REPLACE= new FindReplaceOperationCode(); | |
185 private static const FindReplaceOperationCode REPLACE_FIND_NEXT= new FindReplaceOperationCode(); | |
129 | 186 |
187 /** | |
188 * Retain case mode constants. | |
189 * @since 3.4 | |
190 */ | |
147 | 191 private static const int RC_MIXED= 0; |
192 private static const int RC_UPPER= 1; | |
193 private static const int RC_LOWER= 2; | |
194 private static const int RC_FIRSTUPPER= 3; | |
129 | 195 |
196 | |
197 /** | |
198 * The adapted document. | |
199 */ | |
200 private IDocument fDocument; | |
201 | |
202 /** | |
203 * State for findReplace. | |
204 */ | |
205 private FindReplaceOperationCode fFindReplaceState= null; | |
206 | |
207 /** | |
208 * The matcher used in findReplace. | |
209 */ | |
210 private Matcher fFindReplaceMatcher; | |
211 | |
212 /** | |
213 * The match offset from the last findReplace call. | |
214 */ | |
215 private int fFindReplaceMatchOffset; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
216 |
129 | 217 /** |
218 * Retain case mode | |
219 */ | |
220 private int fRetainCaseMode; | |
221 | |
222 /** | |
223 * Constructs a new find replace document adapter. | |
224 * | |
225 * @param document the adapted document | |
226 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
227 public this(IDocument document) { |
129 | 228 Assert.isNotNull(document); |
229 fDocument= document; | |
230 } | |
231 | |
232 /** | |
233 * Returns the location of a given string in this adapter's document based on a set of search criteria. | |
234 * | |
235 * @param startOffset document offset at which search starts | |
236 * @param findString the string to find | |
237 * @param forwardSearch the search direction | |
238 * @param caseSensitive indicates whether lower and upper case should be distinguished | |
239 * @param wholeWord indicates whether the findString should be limited by white spaces as | |
240 * defined by Character.isWhiteSpace. Must not be used in combination with <code>regExSearch</code>. | |
241 * @param regExSearch if <code>true</code> findString represents a regular expression | |
242 * Must not be used in combination with <code>wholeWord</code>. | |
243 * @return the find or replace region or <code>null</code> if there was no match | |
244 * @throws BadLocationException if startOffset is an invalid document offset | |
245 * @throws PatternSyntaxException if a regular expression has invalid syntax | |
246 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
247 public IRegion find(int startOffset, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord, bool regExSearch) { |
129 | 248 Assert.isTrue(!(regExSearch && wholeWord)); |
249 | |
250 // Adjust offset to special meaning of -1 | |
251 if (startOffset is -1 && forwardSearch) | |
252 startOffset= 0; | |
253 if (startOffset is -1 && !forwardSearch) | |
254 startOffset= length() - 1; | |
255 | |
256 return findReplace(FIND_FIRST, startOffset, findString, null, forwardSearch, caseSensitive, wholeWord, regExSearch); | |
257 } | |
258 | |
259 /** | |
260 * Stateful findReplace executes a FIND, REPLACE, REPLACE_FIND or FIND_FIRST operation. | |
261 * In case of REPLACE and REPLACE_FIND it sends a <code>DocumentEvent</code> to all | |
262 * registered <code>IDocumentListener</code>. | |
263 * | |
264 * @param startOffset document offset at which search starts | |
265 * this value is only used in the FIND_FIRST operation and otherwise ignored | |
266 * @param findString the string to find | |
267 * this value is only used in the FIND_FIRST operation and otherwise ignored | |
268 * @param replaceText the string to replace the current match | |
269 * this value is only used in the REPLACE and REPLACE_FIND operations and otherwise ignored | |
270 * @param forwardSearch the search direction | |
271 * @param caseSensitive indicates whether lower and upper case should be distinguished | |
272 * @param wholeWord indicates whether the findString should be limited by white spaces as | |
273 * defined by Character.isWhiteSpace. Must not be used in combination with <code>regExSearch</code>. | |
274 * @param regExSearch if <code>true</code> this operation represents a regular expression | |
275 * Must not be used in combination with <code>wholeWord</code>. | |
276 * @param operationCode specifies what kind of operation is executed | |
277 * @return the find or replace region or <code>null</code> if there was no match | |
278 * @throws BadLocationException if startOffset is an invalid document offset | |
279 * @throws IllegalStateException if a REPLACE or REPLACE_FIND operation is not preceded by a successful FIND operation | |
280 * @throws PatternSyntaxException if a regular expression has invalid syntax | |
281 */ | |
156 | 282 private IRegion findReplace(FindReplaceOperationCode operationCode, int startOffset, String findString, String replaceText, bool forwardSearch, bool caseSensitive, bool wholeWord, bool regExSearch) { |
129 | 283 |
284 // Validate option combinations | |
285 Assert.isTrue(!(regExSearch && wholeWord)); | |
286 | |
287 // Validate state | |
288 if ((operationCode is REPLACE || operationCode is REPLACE_FIND_NEXT) && (fFindReplaceState !is FIND_FIRST && fFindReplaceState !is FIND_NEXT)) | |
289 throw new IllegalStateException("illegal findReplace state: cannot replace without preceding find"); //$NON-NLS-1$ | |
290 | |
291 if (operationCode is FIND_FIRST) { | |
292 // Reset | |
293 | |
294 if (findString is null || findString.length() is 0) | |
295 return null; | |
296 | |
297 // Validate start offset | |
298 if (startOffset < 0 || startOffset >= length()) | |
299 throw new BadLocationException(); | |
300 | |
301 int patternFlags= 0; | |
302 | |
303 if (regExSearch) { | |
304 patternFlags |= Pattern.MULTILINE; | |
305 findString= substituteLinebreak(findString); | |
306 } | |
307 | |
308 if (!caseSensitive) | |
309 patternFlags |= Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE; | |
310 | |
311 if (wholeWord) | |
312 findString= "\\b" + findString + "\\b"; //$NON-NLS-1$ //$NON-NLS-2$ | |
313 | |
314 if (!regExSearch && !wholeWord) | |
315 findString= asRegPattern(findString); | |
316 | |
317 fFindReplaceMatchOffset= startOffset; | |
318 if (fFindReplaceMatcher !is null && fFindReplaceMatcher.pattern().pattern().equals(findString) && fFindReplaceMatcher.pattern().flags() is patternFlags) { | |
319 /* | |
320 * Commented out for optimization: | |
321 * The call is not needed since FIND_FIRST uses find(int) which resets the matcher | |
322 */ | |
323 // fFindReplaceMatcher.reset(); | |
324 } else { | |
325 Pattern pattern= Pattern.compile(findString, patternFlags); | |
326 fFindReplaceMatcher= pattern.matcher(this); | |
327 } | |
328 } | |
329 | |
330 // Set state | |
331 fFindReplaceState= operationCode; | |
332 | |
333 if (operationCode is REPLACE || operationCode is REPLACE_FIND_NEXT) { | |
334 if (regExSearch) { | |
335 Pattern pattern= fFindReplaceMatcher.pattern(); | |
336 String prevMatch= fFindReplaceMatcher.group(); | |
337 try { | |
338 replaceText= interpretReplaceEscapes(replaceText, prevMatch); | |
339 Matcher replaceTextMatcher= pattern.matcher(prevMatch); | |
340 replaceText= replaceTextMatcher.replaceFirst(replaceText); | |
341 } catch (IndexOutOfBoundsException ex) { | |
342 throw new PatternSyntaxException(ex.getLocalizedMessage(), replaceText, -1); | |
343 } | |
344 } | |
345 | |
346 int offset= fFindReplaceMatcher.start(); | |
347 int length= fFindReplaceMatcher.group().length(); | |
348 | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
349 if (cast(IRepairableDocumentExtension)fDocument |
134 | 350 && (cast(IRepairableDocumentExtension)fDocument).isLineInformationRepairNeeded(offset, length, replaceText)) { |
129 | 351 String message= TextMessages.getString("FindReplaceDocumentAdapter.incompatibleLineDelimiter"); //$NON-NLS-1$ |
352 throw new PatternSyntaxException(message, replaceText, offset); | |
353 } | |
354 | |
355 fDocument.replace(offset, length, replaceText); | |
356 | |
357 if (operationCode is REPLACE) { | |
358 return new Region(offset, replaceText.length()); | |
359 } | |
360 } | |
361 | |
362 if (operationCode !is REPLACE) { | |
363 if (forwardSearch) { | |
364 | |
365 bool found= false; | |
366 if (operationCode is FIND_FIRST) | |
367 found= fFindReplaceMatcher.find(startOffset); | |
368 else | |
369 found= fFindReplaceMatcher.find(); | |
370 | |
371 if (operationCode is REPLACE_FIND_NEXT) | |
372 fFindReplaceState= FIND_NEXT; | |
373 | |
374 if (found && fFindReplaceMatcher.group().length() > 0) | |
375 return new Region(fFindReplaceMatcher.start(), fFindReplaceMatcher.group().length()); | |
376 return null; | |
377 } | |
378 | |
379 // backward search | |
380 bool found= fFindReplaceMatcher.find(0); | |
381 int index= -1; | |
382 int length= -1; | |
383 while (found && fFindReplaceMatcher.start() + fFindReplaceMatcher.group().length() <= fFindReplaceMatchOffset + 1) { | |
384 index= fFindReplaceMatcher.start(); | |
385 length= fFindReplaceMatcher.group().length(); | |
386 found= fFindReplaceMatcher.find(index + 1); | |
387 } | |
388 fFindReplaceMatchOffset= index; | |
389 if (index > -1) { | |
390 // must set matcher to correct position | |
391 fFindReplaceMatcher.find(index); | |
392 return new Region(index, length); | |
393 } | |
394 return null; | |
395 } | |
396 | |
397 return null; | |
398 } | |
399 | |
400 /** | |
401 * 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
|
402 * |
129 | 403 * @param findString the original find pattern |
404 * @return the transformed find pattern | |
405 * @throws PatternSyntaxException if \R is added at an illegal position (e.g. in a character set) | |
406 * @since 3.4 | |
407 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
408 private String substituteLinebreak(String findString) { |
129 | 409 int length= findString.length(); |
410 StringBuffer buf= new StringBuffer(length); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
411 |
129 | 412 int inCharGroup= 0; |
413 int inBraces= 0; | |
414 bool inQuote= false; | |
415 for (int i= 0; i < length; i++) { | |
416 char ch= findString.charAt(i); | |
417 switch (ch) { | |
418 case '[': | |
419 buf.append(ch); | |
420 if (! inQuote) | |
421 inCharGroup++; | |
422 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
423 |
129 | 424 case ']': |
425 buf.append(ch); | |
426 if (! inQuote) | |
427 inCharGroup--; | |
428 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
429 |
129 | 430 case '{': |
431 buf.append(ch); | |
432 if (! inQuote && inCharGroup is 0) | |
433 inBraces++; | |
434 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
435 |
129 | 436 case '}': |
437 buf.append(ch); | |
438 if (! inQuote && inCharGroup is 0) | |
439 inBraces--; | |
440 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
441 |
129 | 442 case '\\': |
443 if (i + 1 < length) { | |
444 char ch1= findString.charAt(i + 1); | |
445 if (inQuote) { | |
446 if (ch1 is 'E') | |
447 inQuote= false; | |
448 buf.append(ch).append(ch1); | |
449 i++; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
450 |
129 | 451 } else if (ch1 is 'R') { |
452 if (inCharGroup > 0 || inBraces > 0) { | |
453 String msg= TextMessages.getString("FindReplaceDocumentAdapter.illegalLinebreak"); //$NON-NLS-1$ | |
454 throw new PatternSyntaxException(msg, findString, i); | |
455 } | |
456 buf.append("(?>\\r\\n?|\\n)"); //$NON-NLS-1$ | |
457 i++; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
458 |
129 | 459 } else { |
460 if (ch1 is 'Q') { | |
461 inQuote= true; | |
462 } | |
463 buf.append(ch).append(ch1); | |
464 i++; | |
465 } | |
466 } else { | |
467 buf.append(ch); | |
468 } | |
469 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
470 |
129 | 471 default: |
472 buf.append(ch); | |
473 break; | |
474 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
475 |
129 | 476 } |
477 return buf.toString(); | |
478 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
479 |
129 | 480 /** |
481 * Interprets current Retain Case mode (all upper-case,all lower-case,capitalized or mixed) | |
482 * 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
|
483 * |
129 | 484 * @param buf the output buffer |
485 * @param ch the character to process | |
486 * @since 3.4 | |
487 */ | |
488 private void interpretRetainCase(StringBuffer buf, char ch) { | |
489 if (fRetainCaseMode is RC_UPPER) | |
490 buf.append(Character.toUpperCase(ch)); | |
491 else if (fRetainCaseMode is RC_LOWER) | |
492 buf.append(Character.toLowerCase(ch)); | |
493 else if (fRetainCaseMode is RC_FIRSTUPPER) { | |
494 buf.append(Character.toUpperCase(ch)); | |
495 fRetainCaseMode= RC_MIXED; | |
496 } else | |
497 buf.append(ch); | |
498 } | |
499 | |
500 /** | |
501 * Interprets escaped characters in the given replace pattern. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
502 * |
129 | 503 * @param replaceText the replace pattern |
504 * @param foundText the found pattern to be replaced | |
505 * @return a replace pattern with escaped characters substituted by the respective characters | |
506 * @since 3.4 | |
507 */ | |
508 private String interpretReplaceEscapes(String replaceText, String foundText) { | |
509 int length= replaceText.length(); | |
510 bool inEscape= false; | |
511 StringBuffer buf= new StringBuffer(length); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
512 |
129 | 513 /* every string we did not check looks mixed at first |
514 * so initialize retain case mode with RC_MIXED | |
515 */ | |
516 fRetainCaseMode= RC_MIXED; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
517 |
129 | 518 for (int i= 0; i < length; i++) { |
519 final char ch= replaceText.charAt(i); | |
520 if (inEscape) { | |
521 i= interpretReplaceEscape(ch, i, buf, replaceText, foundText); | |
522 inEscape= false; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
523 |
129 | 524 } else if (ch is '\\') { |
525 inEscape= true; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
526 |
129 | 527 } else if (ch is '$') { |
528 buf.append(ch); | |
529 | |
530 /* | |
531 * Feature in java.util.regex.Matcher#replaceFirst(String): | |
532 * $00, $000, etc. are interpreted as $0 and | |
533 * $01, $001, etc. are interpreted as $1, etc. . | |
534 * If we support \0 as replacement pattern for capturing group 0, | |
535 * it would not be possible any more to write a replacement pattern | |
536 * that appends 0 to a capturing group (like $0\0). | |
537 * The fix is to interpret \00 and $00 as $0\0, and | |
538 * \01 and $01 as $0\1, etc. | |
539 */ | |
540 if (i + 2 < length) { | |
541 char ch1= replaceText.charAt(i + 1); | |
542 char ch2= replaceText.charAt(i + 2); | |
543 if (ch1 is '0' && '0' <= ch2 && ch2 <= '9') { | |
544 buf.append("0\\"); //$NON-NLS-1$ | |
545 i++; // consume the 0 | |
546 } | |
547 } | |
548 } else { | |
549 interpretRetainCase(buf, ch); | |
550 } | |
551 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
552 |
129 | 553 if (inEscape) { |
554 // '\' as last character is invalid, but we still add it to get an error message | |
555 buf.append('\\'); | |
556 } | |
557 return buf.toString(); | |
558 } | |
559 | |
560 /** | |
561 * Interprets the escaped character <code>ch</code> at offset <code>i</code> | |
562 * 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
|
563 * |
129 | 564 * @param ch the escaped character |
565 * @param i the offset | |
566 * @param buf the output buffer | |
567 * @param replaceText the original replace pattern | |
568 * @param foundText the found pattern to be replaced | |
569 * @return the new offset | |
570 * @since 3.4 | |
571 */ | |
156 | 572 private int interpretReplaceEscape(char ch, int i, StringBuffer buf, String replaceText, String foundText) { |
129 | 573 int length= replaceText.length(); |
574 switch (ch) { | |
575 case 'r': | |
576 buf.append('\r'); | |
577 break; | |
578 case 'n': | |
579 buf.append('\n'); | |
580 break; | |
581 case 't': | |
582 buf.append('\t'); | |
583 break; | |
584 case 'f': | |
585 buf.append('\f'); | |
586 break; | |
587 case 'a': | |
588 buf.append('\u0007'); | |
589 break; | |
590 case 'e': | |
591 buf.append('\u001B'); | |
592 break; | |
593 case 'R': //see http://www.unicode.org/unicode/reports/tr18/#Line_Boundaries | |
594 buf.append(TextUtilities.getDefaultLineDelimiter(fDocument)); | |
595 break; | |
596 /* | |
597 * \0 for octal is not supported in replace string, since it | |
598 * would conflict with capturing group \0, etc. | |
599 */ | |
600 case '0': | |
601 buf.append('$').append(ch); | |
602 /* | |
603 * See explanation in "Feature in java.util.regex.Matcher#replaceFirst(String)" | |
604 * in interpretReplaceEscape(String) above. | |
605 */ | |
606 if (i + 1 < length) { | |
607 char ch1= replaceText.charAt(i + 1); | |
608 if ('0' <= ch1 && ch1 <= '9') { | |
609 buf.append('\\'); | |
610 } | |
611 } | |
612 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
613 |
129 | 614 case '1': |
615 case '2': | |
616 case '3': | |
617 case '4': | |
618 case '5': | |
619 case '6': | |
620 case '7': | |
621 case '8': | |
622 case '9': | |
623 buf.append('$').append(ch); | |
624 break; | |
625 | |
626 case 'c': | |
627 if (i + 1 < length) { | |
628 char ch1= replaceText.charAt(i + 1); | |
134 | 629 interpretRetainCase(buf, cast(wchar)(ch1 ^ 64)); |
129 | 630 i++; |
631 } else { | |
632 String msg= TextMessages.getFormattedString("FindReplaceDocumentAdapter.illegalControlEscape", "\\c"); //$NON-NLS-1$ //$NON-NLS-2$ | |
633 throw new PatternSyntaxException(msg, replaceText, i); | |
634 } | |
635 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
636 |
129 | 637 case 'x': |
638 if (i + 2 < length) { | |
639 int parsedInt; | |
640 try { | |
641 parsedInt= Integer.parseInt(replaceText.substring(i + 1, i + 3), 16); | |
642 if (parsedInt < 0) | |
643 throw new NumberFormatException(); | |
644 } catch (NumberFormatException e) { | |
645 String msg= TextMessages.getFormattedString("FindReplaceDocumentAdapter.illegalHexEscape", replaceText.substring(i - 1, i + 3)); //$NON-NLS-1$ | |
646 throw new PatternSyntaxException(msg, replaceText, i); | |
647 } | |
134 | 648 interpretRetainCase(buf, cast(wchar) parsedInt); |
129 | 649 i+= 2; |
650 } else { | |
651 String msg= TextMessages.getFormattedString("FindReplaceDocumentAdapter.illegalHexEscape", replaceText.substring(i - 1, length)); //$NON-NLS-1$ | |
652 throw new PatternSyntaxException(msg, replaceText, i); | |
653 } | |
654 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
655 |
129 | 656 case 'u': |
657 if (i + 4 < length) { | |
658 int parsedInt; | |
659 try { | |
660 parsedInt= Integer.parseInt(replaceText.substring(i + 1, i + 5), 16); | |
661 if (parsedInt < 0) | |
662 throw new NumberFormatException(); | |
663 } catch (NumberFormatException e) { | |
664 String msg= TextMessages.getFormattedString("FindReplaceDocumentAdapter.illegalUnicodeEscape", replaceText.substring(i - 1, i + 5)); //$NON-NLS-1$ | |
665 throw new PatternSyntaxException(msg, replaceText, i); | |
666 } | |
134 | 667 interpretRetainCase(buf, cast(wchar) parsedInt); |
129 | 668 i+= 4; |
669 } else { | |
670 String msg= TextMessages.getFormattedString("FindReplaceDocumentAdapter.illegalUnicodeEscape", replaceText.substring(i - 1, length)); //$NON-NLS-1$ | |
671 throw new PatternSyntaxException(msg, replaceText, i); | |
672 } | |
673 break; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
674 |
129 | 675 case 'C': |
676 if(foundText.toUpperCase().equals(foundText)) // is whole match upper-case? | |
677 fRetainCaseMode= RC_UPPER; | |
678 else if (foundText.toLowerCase().equals(foundText)) // is whole match lower-case? | |
679 fRetainCaseMode= RC_LOWER; | |
680 else if(Character.isUpperCase(foundText.charAt(0))) // is first character upper-case? | |
681 fRetainCaseMode= RC_FIRSTUPPER; | |
682 else | |
683 fRetainCaseMode= RC_MIXED; | |
684 break; | |
685 | |
686 default: | |
687 // unknown escape k: append uninterpreted \k | |
688 buf.append('\\').append(ch); | |
689 break; | |
690 } | |
691 return i; | |
692 } | |
693 | |
694 /** | |
695 * Converts a non-regex string to a pattern | |
696 * that can be used with the regex search engine. | |
697 * | |
698 * @param string the non-regex pattern | |
699 * @return the string converted to a regex pattern | |
700 */ | |
701 private String asRegPattern(String string) { | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
702 StringBuffer out_= new StringBuffer(string.length()); |
129 | 703 bool quoting= false; |
704 | |
705 for (int i= 0, length= string.length(); i < length; i++) { | |
706 char ch= string.charAt(i); | |
707 if (ch is '\\') { | |
708 if (quoting) { | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
709 out_.append("\\E"); //$NON-NLS-1$ |
129 | 710 quoting= false; |
711 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
712 out_.append("\\\\"); //$NON-NLS-1$ |
129 | 713 continue; |
714 } | |
715 if (!quoting) { | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
716 out_.append("\\Q"); //$NON-NLS-1$ |
129 | 717 quoting= true; |
718 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
719 out_.append(ch); |
129 | 720 } |
721 if (quoting) | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
722 out_.append("\\E"); //$NON-NLS-1$ |
129 | 723 |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
136
diff
changeset
|
724 return out_.toString(); |
129 | 725 } |
726 | |
727 /** | |
728 * Substitutes the previous match with the given text. | |
729 * Sends a <code>DocumentEvent</code> to all registered <code>IDocumentListener</code>. | |
730 * | |
731 * @param text the substitution text | |
732 * @param regExReplace if <code>true</code> <code>text</code> represents a regular expression | |
733 * @return the replace region or <code>null</code> if there was no match | |
734 * @throws BadLocationException if startOffset is an invalid document offset | |
735 * @throws IllegalStateException if a REPLACE or REPLACE_FIND operation is not preceded by a successful FIND operation | |
736 * @throws PatternSyntaxException if a regular expression has invalid syntax | |
737 * | |
738 * @see DocumentEvent | |
739 * @see IDocumentListener | |
740 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
741 public IRegion replace(String text, bool regExReplace) { |
129 | 742 return findReplace(REPLACE, -1, null, text, false, false, false, regExReplace); |
743 } | |
744 | |
745 // ---------- CharSequence implementation ---------- | |
746 | |
747 /* | |
748 * @see java.lang.CharSequence#length() | |
749 */ | |
750 public int length() { | |
751 return fDocument.getLength(); | |
752 } | |
753 | |
754 /* | |
755 * @see java.lang.CharSequence#charAt(int) | |
756 */ | |
159 | 757 public override wchar charAt(int index) { |
129 | 758 try { |
759 return fDocument.getChar(index); | |
760 } catch (BadLocationException e) { | |
761 throw new IndexOutOfBoundsException(); | |
762 } | |
763 } | |
764 | |
765 /* | |
766 * @see java.lang.CharSequence#subSequence(int, int) | |
767 */ | |
768 public CharSequence subSequence(int start, int end) { | |
769 try { | |
770 return fDocument.get(start, end - start); | |
771 } catch (BadLocationException e) { | |
772 throw new IndexOutOfBoundsException(); | |
773 } | |
774 } | |
775 | |
776 /* | |
777 * @see java.lang.Object#toString() | |
778 */ | |
779 public String toString() { | |
780 return fDocument.get(); | |
781 } | |
782 } |