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