Mercurial > projects > dwt-addons
annotate dwtx/jface/text/source/DefaultCharacterPairMatcher.d @ 146:75302ef3f92f
final
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sun, 24 Aug 2008 22:34:04 +0200 |
parents | 6dcb0baaa031 |
children | f70d9508c95c |
rev | line source |
---|---|
129 | 1 /******************************************************************************* |
2 * Copyright (c) 2006, 2007 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 * Christian Plesner Hansen (plesner@quenta.org) - initial API and implementation | |
10 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.jface.text.source.DefaultCharacterPairMatcher; | |
14 | |
131 | 15 import dwtx.jface.text.source.ISharedTextColors; // packageimport |
16 import dwtx.jface.text.source.ILineRange; // packageimport | |
17 import dwtx.jface.text.source.IAnnotationPresentation; // packageimport | |
18 import dwtx.jface.text.source.IVerticalRulerInfoExtension; // packageimport | |
19 import dwtx.jface.text.source.ICharacterPairMatcher; // packageimport | |
20 import dwtx.jface.text.source.TextInvocationContext; // packageimport | |
21 import dwtx.jface.text.source.LineChangeHover; // packageimport | |
22 import dwtx.jface.text.source.IChangeRulerColumn; // packageimport | |
23 import dwtx.jface.text.source.IAnnotationMap; // packageimport | |
24 import dwtx.jface.text.source.IAnnotationModelListenerExtension; // packageimport | |
25 import dwtx.jface.text.source.ISourceViewerExtension2; // packageimport | |
26 import dwtx.jface.text.source.IAnnotationHover; // packageimport | |
27 import dwtx.jface.text.source.ContentAssistantFacade; // packageimport | |
28 import dwtx.jface.text.source.IAnnotationAccess; // packageimport | |
29 import dwtx.jface.text.source.IVerticalRulerExtension; // packageimport | |
30 import dwtx.jface.text.source.IVerticalRulerColumn; // packageimport | |
31 import dwtx.jface.text.source.LineNumberRulerColumn; // packageimport | |
32 import dwtx.jface.text.source.MatchingCharacterPainter; // packageimport | |
33 import dwtx.jface.text.source.IAnnotationModelExtension; // packageimport | |
34 import dwtx.jface.text.source.ILineDifferExtension; // packageimport | |
35 import dwtx.jface.text.source.LineNumberChangeRulerColumn; // packageimport | |
36 import dwtx.jface.text.source.IAnnotationAccessExtension; // packageimport | |
37 import dwtx.jface.text.source.ISourceViewer; // packageimport | |
38 import dwtx.jface.text.source.AnnotationModel; // packageimport | |
39 import dwtx.jface.text.source.ILineDifferExtension2; // packageimport | |
40 import dwtx.jface.text.source.IAnnotationModelListener; // packageimport | |
41 import dwtx.jface.text.source.IVerticalRuler; // packageimport | |
42 import dwtx.jface.text.source.DefaultAnnotationHover; // packageimport | |
43 import dwtx.jface.text.source.SourceViewer; // packageimport | |
44 import dwtx.jface.text.source.SourceViewerConfiguration; // packageimport | |
45 import dwtx.jface.text.source.AnnotationBarHoverManager; // packageimport | |
46 import dwtx.jface.text.source.CompositeRuler; // packageimport | |
47 import dwtx.jface.text.source.ImageUtilities; // packageimport | |
48 import dwtx.jface.text.source.VisualAnnotationModel; // packageimport | |
49 import dwtx.jface.text.source.IAnnotationModel; // packageimport | |
50 import dwtx.jface.text.source.ISourceViewerExtension3; // packageimport | |
51 import dwtx.jface.text.source.ILineDiffInfo; // packageimport | |
52 import dwtx.jface.text.source.VerticalRulerEvent; // packageimport | |
53 import dwtx.jface.text.source.ChangeRulerColumn; // packageimport | |
54 import dwtx.jface.text.source.ILineDiffer; // packageimport | |
55 import dwtx.jface.text.source.AnnotationModelEvent; // packageimport | |
56 import dwtx.jface.text.source.AnnotationColumn; // packageimport | |
57 import dwtx.jface.text.source.AnnotationRulerColumn; // packageimport | |
58 import dwtx.jface.text.source.IAnnotationHoverExtension; // packageimport | |
59 import dwtx.jface.text.source.AbstractRulerColumn; // packageimport | |
60 import dwtx.jface.text.source.ISourceViewerExtension; // packageimport | |
61 import dwtx.jface.text.source.AnnotationMap; // packageimport | |
62 import dwtx.jface.text.source.IVerticalRulerInfo; // packageimport | |
63 import dwtx.jface.text.source.IAnnotationModelExtension2; // packageimport | |
64 import dwtx.jface.text.source.LineRange; // packageimport | |
65 import dwtx.jface.text.source.IAnnotationAccessExtension2; // packageimport | |
66 import dwtx.jface.text.source.VerticalRuler; // packageimport | |
67 import dwtx.jface.text.source.JFaceTextMessages; // packageimport | |
68 import dwtx.jface.text.source.IOverviewRuler; // packageimport | |
69 import dwtx.jface.text.source.Annotation; // packageimport | |
70 import dwtx.jface.text.source.IVerticalRulerListener; // packageimport | |
71 import dwtx.jface.text.source.ISourceViewerExtension4; // packageimport | |
72 import dwtx.jface.text.source.AnnotationPainter; // packageimport | |
73 import dwtx.jface.text.source.IAnnotationHoverExtension2; // packageimport | |
74 import dwtx.jface.text.source.OverviewRuler; // packageimport | |
75 import dwtx.jface.text.source.OverviewRulerHoverManager; // packageimport | |
76 | |
77 | |
129 | 78 import dwt.dwthelper.utils; |
79 import java.util.HashSet; | |
80 import java.util.Set; | |
81 | |
82 import dwtx.core.runtime.Assert; | |
83 import dwtx.jface.text.BadLocationException; | |
84 import dwtx.jface.text.IDocument; | |
85 import dwtx.jface.text.IDocumentExtension3; | |
86 import dwtx.jface.text.IRegion; | |
87 import dwtx.jface.text.ITypedRegion; | |
88 import dwtx.jface.text.Region; | |
89 import dwtx.jface.text.TextUtilities; | |
90 | |
91 /** | |
92 * A character pair matcher that matches a specified set of character | |
93 * pairs against each other. Only characters that occur in the same | |
94 * partitioning are matched. | |
95 * | |
96 * @since 3.3 | |
97 */ | |
98 public class DefaultCharacterPairMatcher : ICharacterPairMatcher { | |
99 | |
100 private int fAnchor= -1; | |
146 | 101 private const CharPairs fPairs; |
102 private const String fPartitioning; | |
129 | 103 |
104 /** | |
105 * Creates a new character pair matcher that matches the specified | |
106 * characters within the specified partitioning. The specified | |
107 * list of characters must have the form | |
108 * <blockquote>{ <i>start</i>, <i>end</i>, <i>start</i>, <i>end</i>, ..., <i>start</i>, <i>end</i> }</blockquote> | |
109 * For instance: | |
110 * <pre> | |
111 * char[] chars = new char[] {'(', ')', '{', '}', '[', ']'}; | |
112 * new SimpleCharacterPairMatcher(chars, ...); | |
113 * </pre> | |
114 * | |
115 * @param chars a list of characters | |
116 * @param partitioning the partitioning to match within | |
117 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
118 public this(char[] chars, String partitioning) { |
129 | 119 Assert.isLegal(chars.length % 2 is 0); |
120 Assert.isNotNull(partitioning); | |
121 fPairs= new CharPairs(chars); | |
122 fPartitioning= partitioning; | |
123 } | |
124 | |
125 /** | |
126 * Creates a new character pair matcher that matches characters | |
127 * within the default partitioning. The specified list of | |
128 * characters must have the form | |
129 * <blockquote>{ <i>start</i>, <i>end</i>, <i>start</i>, <i>end</i>, ..., <i>start</i>, <i>end</i> }</blockquote> | |
130 * For instance: | |
131 * <pre> | |
132 * char[] chars = new char[] {'(', ')', '{', '}', '[', ']'}; | |
133 * new SimpleCharacterPairMatcher(chars); | |
134 * </pre> | |
135 * | |
136 * @param chars a list of characters | |
137 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
138 public this(char[] chars) { |
129 | 139 this(chars, IDocumentExtension3.DEFAULT_PARTITIONING); |
140 } | |
141 | |
142 /* @see ICharacterPairMatcher#match(IDocument, int) */ | |
143 public IRegion match(IDocument doc, int offset) { | |
144 if (doc is null || offset < 0 || offset > doc.getLength()) return null; | |
145 try { | |
146 return performMatch(doc, offset); | |
147 } catch (BadLocationException ble) { | |
148 return null; | |
149 } | |
150 } | |
151 | |
152 /* | |
153 * Performs the actual work of matching for #match(IDocument, int). | |
154 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
133
diff
changeset
|
155 private IRegion performMatch(IDocument doc, int caretOffset) { |
129 | 156 final int charOffset= caretOffset - 1; |
157 final char prevChar= doc.getChar(Math.max(charOffset, 0)); | |
158 if (!fPairs.contains(prevChar)) return null; | |
159 final bool isForward= fPairs.isStartCharacter(prevChar); | |
160 fAnchor= isForward ? ICharacterPairMatcher.LEFT : ICharacterPairMatcher.RIGHT; | |
161 final int searchStartPosition= isForward ? caretOffset : caretOffset - 2; | |
162 final int adjustedOffset= isForward ? charOffset : caretOffset; | |
163 final String partition= TextUtilities.getContentType(doc, fPartitioning, charOffset, false); | |
164 final DocumentPartitionAccessor partDoc= new DocumentPartitionAccessor(doc, fPartitioning, partition); | |
165 int endOffset= findMatchingPeer(partDoc, prevChar, fPairs.getMatching(prevChar), | |
166 isForward, isForward ? doc.getLength() : -1, | |
167 searchStartPosition); | |
168 if (endOffset is -1) return null; | |
169 final int adjustedEndOffset= isForward ? endOffset + 1: endOffset; | |
170 if (adjustedEndOffset is adjustedOffset) return null; | |
171 return new Region(Math.min(adjustedOffset, adjustedEndOffset), | |
172 Math.abs(adjustedEndOffset - adjustedOffset)); | |
173 } | |
174 | |
175 /** | |
176 * Searches <code>doc</code> for the specified end character, <code>end</code>. | |
177 * | |
178 * @param doc the document to search | |
179 * @param start the opening matching character | |
180 * @param end the end character to search for | |
181 * @param searchForward search forwards or backwards? | |
182 * @param boundary a boundary at which the search should stop | |
183 * @param startPos the start offset | |
184 * @return the index of the end character if it was found, otherwise -1 | |
185 * @throws BadLocationException | |
186 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
133
diff
changeset
|
187 private int findMatchingPeer(DocumentPartitionAccessor doc, char start, char end, bool searchForward, int boundary, int startPos) { |
129 | 188 int pos= startPos; |
189 while (pos !is boundary) { | |
190 final char c= doc.getChar(pos); | |
191 if (doc.isMatch(pos, end)) { | |
192 return pos; | |
193 } else if (c is start && doc.inPartition(pos)) { | |
194 pos= findMatchingPeer(doc, start, end, searchForward, boundary, | |
195 doc.getNextPosition(pos, searchForward)); | |
196 if (pos is -1) return -1; | |
197 } | |
198 pos= doc.getNextPosition(pos, searchForward); | |
199 } | |
200 return -1; | |
201 } | |
202 | |
203 /* @see ICharacterPairMatcher#getAnchor() */ | |
204 public int getAnchor() { | |
205 return fAnchor; | |
206 } | |
207 | |
208 /* @see ICharacterPairMatcher#dispose() */ | |
209 public void dispose() { } | |
210 | |
211 /* @see ICharacterPairMatcher#clear() */ | |
212 public void clear() { | |
213 fAnchor= -1; | |
214 } | |
215 | |
216 /** | |
217 * Utility class that wraps a document and gives access to | |
218 * partitioning information. A document is tied to a particular | |
219 * partition and, when considering whether or not a position is a | |
220 * valid match, only considers position within its partition. | |
221 */ | |
222 private static class DocumentPartitionAccessor { | |
223 | |
146 | 224 private const IDocument fDocument; |
225 private const String fPartitioning, fPartition; | |
129 | 226 private ITypedRegion fCachedPartition; |
227 | |
228 /** | |
229 * Creates a new partitioned document for the specified document. | |
230 * | |
231 * @param doc the document to wrap | |
232 * @param partitioning the partitioning used | |
233 * @param partition the partition managed by this document | |
234 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
235 public this(IDocument doc, String partitioning, |
129 | 236 String partition) { |
237 fDocument= doc; | |
238 fPartitioning= partitioning; | |
239 fPartition= partition; | |
240 } | |
241 | |
242 /** | |
243 * Returns the character at the specified position in this document. | |
244 * | |
245 * @param pos an offset within this document | |
246 * @return the character at the offset | |
247 * @throws BadLocationException | |
248 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
133
diff
changeset
|
249 public char getChar(int pos) { |
129 | 250 return fDocument.getChar(pos); |
251 } | |
252 | |
253 /** | |
254 * Returns true if the character at the specified position is a | |
255 * valid match for the specified end character. To be a valid | |
256 * match, it must be in the appropriate partition and equal to the | |
257 * end character. | |
258 * | |
259 * @param pos an offset within this document | |
260 * @param end the end character to match against | |
261 * @return true exactly if the position represents a valid match | |
262 * @throws BadLocationException | |
263 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
133
diff
changeset
|
264 public bool isMatch(int pos, char end) { |
129 | 265 return getChar(pos) is end && inPartition(pos); |
266 } | |
267 | |
268 /** | |
269 * Returns true if the specified offset is within the partition | |
270 * managed by this document. | |
271 * | |
272 * @param pos an offset within this document | |
273 * @return true if the offset is within this document's partition | |
274 */ | |
275 public bool inPartition(int pos) { | |
276 final ITypedRegion partition= getPartition(pos); | |
277 return partition !is null && partition.getType().equals(fPartition); | |
278 } | |
279 | |
280 /** | |
281 * Returns the next position to query in the search. The position | |
282 * is not guaranteed to be in this document's partition. | |
283 * | |
284 * @param pos an offset within the document | |
285 * @param searchForward the direction of the search | |
286 * @return the next position to query | |
287 */ | |
288 public int getNextPosition(int pos, bool searchForward) { | |
289 final ITypedRegion partition= getPartition(pos); | |
290 if (partition is null) return simpleIncrement(pos, searchForward); | |
291 if (fPartition.equals(partition.getType())) | |
292 return simpleIncrement(pos, searchForward); | |
293 if (searchForward) { | |
294 int end= partition.getOffset() + partition.getLength(); | |
295 if (pos < end) | |
296 return end; | |
297 } else { | |
298 int offset= partition.getOffset(); | |
299 if (pos > offset) | |
300 return offset - 1; | |
301 } | |
302 return simpleIncrement(pos, searchForward); | |
303 } | |
304 | |
305 private int simpleIncrement(int pos, bool searchForward) { | |
306 return pos + (searchForward ? 1 : -1); | |
307 } | |
308 | |
309 /** | |
310 * Returns partition information about the region containing the | |
311 * specified position. | |
312 * | |
313 * @param pos a position within this document. | |
314 * @return positioning information about the region containing the | |
315 * position | |
316 */ | |
317 private ITypedRegion getPartition(int pos) { | |
318 if (fCachedPartition is null || !contains(fCachedPartition, pos)) { | |
319 Assert.isTrue(pos >= 0 && pos <= fDocument.getLength()); | |
320 try { | |
321 fCachedPartition= TextUtilities.getPartition(fDocument, fPartitioning, pos, false); | |
322 } catch (BadLocationException e) { | |
323 fCachedPartition= null; | |
324 } | |
325 } | |
326 return fCachedPartition; | |
327 } | |
328 | |
329 private static bool contains(IRegion region, int pos) { | |
330 int offset= region.getOffset(); | |
331 return offset <= pos && pos < offset + region.getLength(); | |
332 } | |
333 | |
334 } | |
335 | |
336 /** | |
337 * Utility class that encapsulates access to matching character pairs. | |
338 */ | |
339 private static class CharPairs { | |
340 | |
146 | 341 private const char[] fPairs; |
129 | 342 |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
343 public this(char[] pairs) { |
129 | 344 fPairs= pairs; |
345 } | |
346 | |
347 /** | |
348 * Returns true if the specified character pair occurs in one | |
349 * of the character pairs. | |
350 * | |
351 * @param c a character | |
352 * @return true exactly if the character occurs in one of the pairs | |
353 */ | |
354 public bool contains(char c) { | |
355 return getAllCharacters().contains(new Character(c)); | |
356 } | |
357 | |
358 private Set/*<Character>*/ fCharsCache= null; | |
359 /** | |
360 * @return A set containing all characters occurring in character pairs. | |
361 */ | |
362 private Set/*<Character>*/ getAllCharacters() { | |
363 if (fCharsCache is null) { | |
364 Set/*<Character>*/ set= new HashSet/*<Character>*/(); | |
365 for (int i= 0; i < fPairs.length; i++) | |
366 set.add(new Character(fPairs[i])); | |
367 fCharsCache= set; | |
368 } | |
369 return fCharsCache; | |
370 } | |
371 | |
372 /** | |
373 * Returns true if the specified character opens a character pair | |
374 * when scanning in the specified direction. | |
375 * | |
376 * @param c a character | |
377 * @param searchForward the direction of the search | |
378 * @return whether or not the character opens a character pair | |
379 */ | |
380 public bool isOpeningCharacter(char c, bool searchForward) { | |
381 for (int i= 0; i < fPairs.length; i += 2) { | |
382 if (searchForward && getStartChar(i) is c) return true; | |
383 else if (!searchForward && getEndChar(i) is c) return true; | |
384 } | |
385 return false; | |
386 } | |
387 | |
388 /** | |
389 * Returns true of the specified character is a start character. | |
390 * | |
391 * @param c a character | |
392 * @return true exactly if the character is a start character | |
393 */ | |
394 public bool isStartCharacter(char c) { | |
395 return this.isOpeningCharacter(c, true); | |
396 } | |
397 | |
398 /** | |
399 * Returns the matching character for the specified character. | |
400 * | |
401 * @param c a character occurring in a character pair | |
402 * @return the matching character | |
403 */ | |
404 public char getMatching(char c) { | |
405 for (int i= 0; i < fPairs.length; i += 2) { | |
406 if (getStartChar(i) is c) return getEndChar(i); | |
407 else if (getEndChar(i) is c) return getStartChar(i); | |
408 } | |
409 Assert.isTrue(false); | |
410 return '\0'; | |
411 } | |
412 | |
413 private char getStartChar(int i) { | |
414 return fPairs[i]; | |
415 } | |
416 | |
417 private char getEndChar(int i) { | |
418 return fPairs[i + 1]; | |
419 } | |
420 | |
421 } | |
422 | |
423 } |