Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 11668 | Differences between
and this patch

Collapse All | Expand All

(-)plugin.properties (+2 lines)
Lines 152-157 Link Here
152
command.toggleOverwrite.name = Toggle Overwrite
152
command.toggleOverwrite.name = Toggle Overwrite
153
command.toggleInsertMode.description = Toggle insert mode
153
command.toggleInsertMode.description = Toggle insert mode
154
command.toggleInsertMode.name = Toggle Insert Mode
154
command.toggleInsertMode.name = Toggle Insert Mode
155
command.hippieCompletion.description = Context insensitive completion
156
command.hippieCompletion.name = Hippie Completion
155
command.windowEnd.description = Go to the end of the window
157
command.windowEnd.description = Go to the end of the window
156
command.windowEnd.name = Window End
158
command.windowEnd.name = Window End
157
command.windowStart.description = Go to the start of the window
159
command.windowStart.description = Go to the start of the window
(-)plugin.xml (+13 lines)
Lines 426-431 Link Here
426
            categoryId="org.eclipse.ui.category.edit"
426
            categoryId="org.eclipse.ui.category.edit"
427
            id="org.eclipse.ui.edit.text.toggleInsertMode">
427
            id="org.eclipse.ui.edit.text.toggleInsertMode">
428
      </command>
428
      </command>
429
      <command
430
            name="%command.hippieCompletion.name"
431
            description="%command.hippieCompletion.description"
432
            categoryId="org.eclipse.ui.category.edit"
433
            id="org.eclipse.ui.edit.text.hippieCompletion">
434
      </command>
435
      <!-- TODO Keybinding -->
436
      <keyBinding
437
            commandId="org.eclipse.ui.edit.text.hippieCompletion"
438
            contextId="org.eclipse.ui.textEditorScope"
439
            keySequence="Alt+/"
440
            keyConfigurationId="org.eclipse.ui.defaultAcceleratorConfiguration">
441
       </keyBinding>
429
	
442
	
430
	  <keyBinding
443
	  <keyBinding
431
	        commandId="org.eclipse.ui.edit.text.delete.line"
444
	        commandId="org.eclipse.ui.edit.text.delete.line"
(-)src/org/eclipse/ui/texteditor/AbstractTextEditor.java (+5 lines)
Lines 4291-4296 Link Here
4291
		action.setActionDefinitionId(ITextEditorActionDefinitionIds.TOGGLE_INSERT_MODE);
4291
		action.setActionDefinitionId(ITextEditorActionDefinitionIds.TOGGLE_INSERT_MODE);
4292
		setAction(ITextEditorActionConstants.TOGGLE_INSERT_MODE, action);
4292
		setAction(ITextEditorActionConstants.TOGGLE_INSERT_MODE, action);
4293
4293
4294
		action = new HippieCompleteAction(EditorMessages.getResourceBundle(), "Editor.HippieCompletion.", this); //$NON-NLS-1$
4295
		// TODO action.setHelpContextId(IAbstractTextEditorHelpContextIds.HIPPIE_COMPLETION_ACTION);
4296
		action.setActionDefinitionId(ITextEditorActionDefinitionIds.HIPPIE_COMPLETION);
4297
		setAction(ITextEditorActionConstants.HIPPIE_COMPLETION, action);
4298
		
4294
		PropertyDialogAction openProperties= new PropertyDialogAction(
4299
		PropertyDialogAction openProperties= new PropertyDialogAction(
4295
				getSite().getShell(),
4300
				getSite().getShell(),
4296
				new ISelectionProvider() {
4301
				new ISelectionProvider() {
(-)src/org/eclipse/ui/texteditor/ITextEditorActionConstants.java (+7 lines)
Lines 517-520 Link Here
517
	 * @since 3.1
517
	 * @since 3.1
518
	 */
518
	 */
519
	static final String QUICKDIFF_TOGGLE= "QuickDiff.Toggle"; //$NON-NLS-1$
519
	static final String QUICKDIFF_TOGGLE= "QuickDiff.Toggle"; //$NON-NLS-1$
520
521
	/** 
522
	 * Name of the action for emacs style hippie completion. 
523
	 * Value: <code>"HIPPIE_COMPLETION"</code>
524
	 * @since 3.1
525
	 */
526
	static final String HIPPIE_COMPLETION= "HIPPIE_COMPLETION"; //$NON-NLS-1$
520
}
527
}
(-)src/org/eclipse/ui/texteditor/ITextEditorActionDefinitionIds.java (+7 lines)
Lines 485-488 Link Here
485
	 * @since 3.1
485
	 * @since 3.1
486
	 */
486
	 */
487
	static final String LINENUMBER_TOGGLE= "org.eclipse.ui.editors.lineNumberToggle"; //$NON-NLS-1$
487
	static final String LINENUMBER_TOGGLE= "org.eclipse.ui.editors.lineNumberToggle"; //$NON-NLS-1$
488
489
	/**
490
	 * Action definition ID of the edit -> text complete action
491
	 * Value: <code>"org.eclipse.ui.edit.text.hippieCompletion"</code>).
492
	 * @since 3.1
493
	 */
494
	public static final String HIPPIE_COMPLETION= "org.eclipse.ui.edit.text.hippieCompletion"; //$NON-NLS-1$
488
}
495
}
(-)src/org/eclipse/ui/texteditor/HippieCompleteAction.java (+595 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials 
4
 * are made available under the terms of the Common Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/cpl-v10.html
7
 * 
8
 * Contributors:
9
 *     Genady Beryozkin, me@genady.org - initial API and implementation
10
 *     IBM Corporation - fixes and cleaning
11
 *******************************************************************************/
12
package org.eclipse.ui.texteditor;
13
14
import java.util.ArrayList;
15
import java.util.HashSet;
16
import java.util.Iterator;
17
import java.util.LinkedList;
18
import java.util.List;
19
import java.util.ResourceBundle;
20
21
import org.eclipse.core.runtime.IStatus;
22
import org.eclipse.core.runtime.Status;
23
24
import org.eclipse.jface.text.Assert;
25
import org.eclipse.jface.text.BadLocationException;
26
import org.eclipse.jface.text.DocumentEvent;
27
import org.eclipse.jface.text.FindReplaceDocumentAdapter;
28
import org.eclipse.jface.text.IDocument;
29
import org.eclipse.jface.text.IDocumentListener;
30
import org.eclipse.jface.text.IRegion;
31
import org.eclipse.jface.text.ITextSelection;
32
import org.eclipse.jface.viewers.ISelectionChangedListener;
33
import org.eclipse.jface.viewers.SelectionChangedEvent;
34
35
import org.eclipse.ui.IEditorInput;
36
import org.eclipse.ui.IEditorPart;
37
import org.eclipse.ui.IEditorReference;
38
import org.eclipse.ui.IWorkbenchWindow;
39
40
import org.eclipse.ui.internal.texteditor.TextEditorPlugin;
41
42
/**
43
 * This class implements the emacs style completion action.
44
 * Completion action is a stateful action, as the user may invoke
45
 * it several times in a row in order to scroll the possible completions.
46
 * 
47
 * TODO: Sort by editor type
48
 * TODO: Provide history option
49
 * 
50
 * @since 3.1
51
 * @author Genady Beryozkin, me@genady.org
52
 */
53
public class HippieCompleteAction extends TextEditorAction {
54
	
55
	/**
56
	 * Regular expression that is used to find words.
57
	 */
58
	// unicode identifier part
59
//	private static final String COMPLETION_WORD_REGEX= "[\\p{L}[\\p{Mn}[\\p{Pc}[\\p{Nd}[\\p{Nl}]]]]]+"; //$NON-NLS-1$
60
	// java identifier part (unicode id part + currency symbols)
61
	private static final String COMPLETION_WORD_REGEX= "[\\p{L}[\\p{Mn}[\\p{Pc}[\\p{Nd}[\\p{Nl}[\\p{Sc}]]]]]]+"; //$NON-NLS-1$
62
	// with a 1.5 JRE, you can do this:
63
//	private static final String COMPLETION_WORD_REGEX= "\\p{javaUnicodeIdentifierPart}+"; //$NON-NLS-1$
64
//	private static final String COMPLETION_WORD_REGEX= "\\p{javaJavaIdentifierPart}+"; //$NON-NLS-1$
65
	
66
	/**
67
	 * Is completion case sensitive? Even if set to <code>false</code>, the case of the prefix
68
	 * won't be changed.
69
	 */
70
	private static final boolean CASE_SENSITIVE= true;
71
		
72
	/**
73
     * The document that will be manipulated
74
	 */
75
	private IDocument fDocument;
76
	
77
	/** 
78
	 * The completion state that is used to continue the iteration over suggestions
79
	 */
80
	private CompletionState fLastCompletion= null;
81
	
82
	/**
83
	 * Modification lock that will prevent invalidation of the completion state when the
84
	 * completion action modifies the document
85
	 */
86
	private boolean fModifyingLock= false;
87
	
88
	SelectionChangeListener fSelectionListener;
89
	DocumentChangeListener fDocumentListener;
90
	
91
	/*
92
	 * @see org.eclipse.ui.texteditor.TextEditorAction#setEditor(org.eclipse.ui.texteditor.ITextEditor)
93
	 */
94
	public void setEditor(ITextEditor editor) {
95
		clearState(); // make sure to remove listers before the editor changes!
96
		super.setEditor(editor);
97
	}
98
	
99
	/**
100
	 * This class accompanies the {@link HippieCompleteAction#updateState()} method
101
	 * to invalidate completion state when the selection changes.
102
	 */
103
	class SelectionChangeListener implements ISelectionChangedListener {
104
		public void selectionChanged(SelectionChangedEvent event) {
105
			if (!fModifyingLock) {
106
				clearState();
107
			}
108
		}
109
	}
110
	
111
	/**
112
	 * Invalidate the completion state when the document contents changes not
113
	 * as the result of the completion action itself. This is needed since
114
	 * the {@link HippieCompleteAction#update()} method is only called when the
115
	 * {@link HippieCompleteAction#fModifyingLock} is already false.
116
	 */
117
	class DocumentChangeListener implements IDocumentListener {
118
		public void documentAboutToBeChanged(DocumentEvent event) {
119
		}
120
		
121
		public void documentChanged(DocumentEvent event) {
122
			if (!fModifyingLock) {
123
				clearState();
124
			}
125
		}
126
	}
127
	
128
	/**
129
	 * Perform the next completion.
130
	 */
131
	private void completeNext() {
132
		// we don't wish to receive events on our own changes
133
		fModifyingLock= true;
134
		try {
135
			try {
136
				fDocument.replace(fLastCompletion.startOffset, fLastCompletion.length, fLastCompletion.suggestions[fLastCompletion.nextSuggestion]);
137
			} catch (BadLocationException e) {
138
				// we should never get here. different from other places to notify the user.
139
				log(e);
140
				clearState();
141
				return;
142
			}
143
			
144
			// advance the suggestion state
145
			fLastCompletion.advance();
146
			
147
			// move the caret to the insertion point
148
			((AbstractTextEditor) getTextEditor()).getSourceViewer().setSelectedRange(fLastCompletion.startOffset + fLastCompletion.length, 0);
149
		} finally {
150
			// allow changes
151
			fModifyingLock= false;
152
		}
153
	}	
154
155
	/**
156
	 * This class represents the state of the last completion process. Each time
157
	 * the user moves to a new position and calls this action an instance of
158
	 * this inner class is created and saved in {@link #fLastCompletion}.
159
	 */
160
	private static class CompletionState {
161
		
162
		/** The list of suggestions that was computed when the completion
163
		 * action was first invoked 
164
		 */
165
		final String[] suggestions;
166
		
167
		/** The caret position at which we insert the suggestions */
168
		final int startOffset;
169
		
170
		/** The length of the last suggestion string */
171
		int length;
172
		
173
		/** The index of next suggestion (index into the suggestion array) */
174
		int nextSuggestion;
175
		
176
		CompletionState(String[] suggestions, int startOffset) {
177
			this.suggestions= suggestions;
178
			this.startOffset= startOffset;
179
			length= 0;
180
			nextSuggestion= 0;
181
		}
182
		
183
		public void advance() {
184
			length= suggestions[nextSuggestion].length();
185
			nextSuggestion= (nextSuggestion + 1) % suggestions.length;	
186
		}
187
	}
188
	
189
	/**
190
	 * @param bundle the resource bundle
191
	 * @param prefix a prefix to be prepended to the various resource keys
192
	 *        (described in <code>ResourceAction</code> constructor), or
193
	 *        <code>null</code> if none
194
	 * @param editor the text editor
195
	 */
196
	protected HippieCompleteAction(ResourceBundle bundle, String prefix, ITextEditor editor) {
197
		super(bundle, prefix, editor);
198
	}
199
	
200
	/**
201
	 * Invalidates the cached completions, removes all registered listeners and
202
	 * sets the cached document to <code>null</code>.
203
	 */
204
	private void clearState() {
205
		fLastCompletion= null;
206
		
207
		ITextEditor editor= getTextEditor();
208
		if (editor != null && fSelectionListener != null)
209
			editor.getSelectionProvider().removeSelectionChangedListener(fSelectionListener);
210
		
211
		if (fDocument != null && fDocumentListener != null)
212
			fDocument.removeDocumentListener(fDocumentListener);
213
		
214
		fDocument= null;
215
	}
216
	
217
	/*
218
	 * @see org.eclipse.jface.action.Action#run()
219
	 */
220
	public void run() {
221
		if (!isStateValid())
222
			updateState();
223
		
224
		if (isStateValid())
225
			completeNext();
226
	}
227
	
228
	/**
229
	 * Update the completion state. The completion cache is updated with the
230
	 * completions based on the currently displayed document and the current
231
	 * selection. To track the validity of the cached state, listeners are
232
	 * registered with the editor and document, and the current document is
233
	 * cached.
234
	 */
235
	private void updateState() {
236
		Assert.isNotNull(getTextEditor());
237
		
238
		clearState();
239
		
240
		IDocument document= getCurrentDocument();
241
		if (document != null) {
242
			fDocument= document;
243
			
244
			String[] suggestions;
245
			try {
246
				String prefix= getCurrentPrefix();
247
				if (prefix == null) {
248
					notifyUser();
249
					return;
250
				}
251
				suggestions= getSuggestions(prefix);
252
			} catch (BadLocationException e) {
253
				log(e);
254
				return;
255
			}
256
			
257
			// if it is single empty suggestion
258
			if (suggestions.length == 1) {
259
				notifyUser();
260
				return;
261
			}
262
			
263
			installListeners();
264
			fLastCompletion= new CompletionState(suggestions, getSelectionOffset());
265
		}
266
	}
267
	
268
	/**
269
	 * Returns <code>true</code> if the current completion state is still
270
	 * valid given the current document and selection.
271
	 * 
272
	 * @return <code>true</code> if the cached state is valid,
273
	 *         <code>false</code> otherwise
274
	 */
275
	private boolean isStateValid() {
276
		return fDocument != null 
277
				&& fDocument.equals(getCurrentDocument()) 
278
				&& fLastCompletion != null 
279
				&& fLastCompletion.startOffset + fLastCompletion.length == getSelectionOffset();
280
	}
281
	
282
	/**
283
	 * Returns the document currently displayed in the editor, or
284
	 * <code>null</code>
285
	 * 
286
	 * @return the document currently displayed in the editor, or
287
	 *         <code>null</code>
288
	 */
289
	private IDocument getCurrentDocument() {
290
		ITextEditor editor= getTextEditor();
291
		if (editor == null)
292
			return null;
293
		IDocumentProvider provider= editor.getDocumentProvider();
294
		if (provider == null)
295
			return null;
296
		
297
		IDocument document= provider.getDocument(editor.getEditorInput());
298
		return document;
299
	}
300
	
301
	/**
302
	 * Returns the current selection (or caret) offset.
303
	 * 
304
	 * @return the current selection (or caret) offset
305
	 */
306
	private int getSelectionOffset() {
307
		return ((ITextSelection) getTextEditor().getSelectionProvider().getSelection()).getOffset();
308
	}
309
	
310
	/**
311
	 * Installs the selection and document listeners. Both editor and cached
312
	 * document must be valid.
313
	 */
314
	private void installListeners() {
315
		ITextEditor editor= getTextEditor();
316
		Assert.isNotNull(editor);
317
		Assert.isNotNull(fDocument);
318
		
319
		if (fSelectionListener == null)
320
			fSelectionListener= new SelectionChangeListener();
321
		editor.getSelectionProvider().addSelectionChangedListener(fSelectionListener);
322
323
		if (fDocumentListener == null)
324
			fDocumentListener= new DocumentChangeListener();
325
		fDocument.addDocumentListener(fDocumentListener);
326
	}
327
	
328
	/**
329
	 * Notifies the user that there are no suggestions.
330
	 */
331
	private void notifyUser() {
332
		// TODO notify via status line?
333
		getTextEditor().getSite().getShell().getDisplay().beep();
334
	}
335
	
336
	/**
337
	 * Create the array of suggestions. It scans all open text editors and
338
	 * prefers suggestions from the currently open editor. It also adds the
339
	 * empty suggestion at the end.
340
	 * @param prefix the prefix to search for
341
	 * @return the list of all possible suggestions in the currently open editors
342
	 * @throws BadLocationException if accessing the current document fails
343
	 */
344
	public String[] getSuggestions(String prefix) throws BadLocationException {
345
		
346
		IWorkbenchWindow window= getTextEditor().getSite().getWorkbenchWindow();
347
		IEditorReference editorsArray[]= window.getActivePage().getEditorReferences();
348
		
349
		ArrayList documentsVector= new ArrayList();
350
		for (int i= 0; i < editorsArray.length; i++) {
351
			IEditorPart realEditor= editorsArray[i].getEditor(false);
352
			if (realEditor instanceof ITextEditor) {
353
				ITextEditor textEditor= (ITextEditor)realEditor;
354
				IEditorInput input= textEditor.getEditorInput();
355
				IDocument doc= textEditor.getDocumentProvider().getDocument(input);
356
				
357
				documentsVector.add(doc);
358
			}
359
		}
360
		documentsVector.remove(fDocument);
361
		
362
		ArrayList suggestions= createSuggestionsFromOpenDocument(prefix);
363
		suggestions.addAll(createSuggestions(prefix, documentsVector));
364
		// add the empty suggestion
365
		suggestions.add(""); //$NON-NLS-1$
366
		
367
		ArrayList uniqueSuggestions= getUnique(suggestions);
368
		
369
		return (String[]) uniqueSuggestions.toArray(new String[0]);
370
	}
371
	
372
	/**
373
     * Return the list of suggestions from the current document. First the document is searched
374
     * backwards from the caret position and then forwards. 
375
	 * @param prefix the completion prefix
376
	 * @return all possible completions that were found in the current document
377
	 * @throws BadLocationException if accessing the document fails
378
	 */
379
	private ArrayList createSuggestionsFromOpenDocument(String prefix) throws BadLocationException {
380
		int selectionOffset= getSelectionOffset();
381
		
382
		ArrayList completions= new ArrayList();
383
		completions.addAll(getBackwardsSuggestions(fDocument, prefix, selectionOffset));
384
		completions.addAll(getForwardSuggestions(fDocument, prefix, selectionOffset));
385
		
386
		return completions;
387
	}
388
	
389
	/**
390
     * Create the suggestions list based on the provided prefix and the documents list.
391
     * Suggestions will be sorted such that the first suggestions come from the first document, 
392
     * etc.
393
	 * @param prefix the completion prefix
394
	 * @param documentsVector {@link ArrayList} of {@link IDocument}s.
395
	 * @return the list of completions, excluding the empty completion
396
	 */
397
    public static List createSuggestions(String prefix, ArrayList documentsVector) {
398
		// collect the suggestions from all the documents
399
		LinkedList suggestions= new LinkedList();
400
		
401
		for (int i= 0; i < documentsVector.size(); i++) {
402
			IDocument doc= (IDocument) documentsVector.get(i);
403
			
404
			try {
405
				suggestions.addAll(getForwardSuggestions(doc, prefix));
406
			} catch (BadLocationException e) {
407
				// log & ignore - a different document than the current has 
408
				// been concurrently modified
409
				log(e);
410
			}
411
		}
412
		
413
		return suggestions;
414
	}
415
	
416
	/**
417
	 * Remove duplicate suggestions (excluding the prefix), leaving the closest to list head. 
418
	 * @param suggestions a list of suggestions.
419
	 * @return a list of unique completion suggestions.
420
	 */
421
	public static ArrayList getUnique(List suggestions) {
422
		HashSet seenAlready= new HashSet();
423
		ArrayList uniqueSuggestions= new ArrayList();
424
		
425
		for (Iterator i= suggestions.iterator(); i.hasNext();) {
426
			String suggestion= (String) i.next();
427
			if (!seenAlready.contains(suggestion)) {
428
				seenAlready.add(suggestion);
429
				uniqueSuggestions.add(suggestion);
430
			}
431
		}
432
		return uniqueSuggestions;
433
	}
434
	
435
	/**
436
	 * Copied from {@link FindReplaceDocumentAdapter}.
437
	 * 
438
	 * Converts a non-regex string to a pattern
439
	 * that can be used with the regex search engine.
440
	 * 
441
	 * @param string the non-regex pattern
442
	 * @return the string converted to a regex pattern
443
	 */
444
	private static String asRegPattern(String string) {
445
		StringBuffer out= new StringBuffer(string.length());
446
		boolean quoting= false;
447
		
448
		for (int i= 0, length= string.length(); i < length; i++) {
449
			char ch= string.charAt(i);
450
			if (ch == '\\') {
451
				if (quoting) {
452
					out.append("\\E"); //$NON-NLS-1$
453
					quoting= false;
454
				}
455
				out.append("\\\\"); //$NON-NLS-1$
456
				continue;								
457
			}
458
			if (!quoting) {
459
				out.append("\\Q"); //$NON-NLS-1$
460
				quoting= true;
461
			}
462
			out.append(ch);
463
		}
464
		if (quoting)
465
			out.append("\\E"); //$NON-NLS-1$
466
		
467
		return out.toString();
468
	}
469
	
470
	/**
471
	 * Return the list of completion suggestions the correspond to the provided
472
	 * prefix
473
	 * 
474
	 * @param document the document to be scanned
475
	 * @param prefix the prefix to search for
476
	 * @param firstPosition the initial position in the document that
477
     *          the search will start from. In order to search from
478
     *          the beginning of the document use <code>firstPosition=0</code>.
479
	 * @return an {@link ArrayList} of possible completions, excluding the common prefix
480
	 * @throws BadLocationException if there is some error scanning the document.
481
     * 
482
	 * @see #getForwardSuggestions(IDocument, String)
483
	 */
484
	public static ArrayList getForwardSuggestions(IDocument document, 
485
			String prefix, int firstPosition) throws BadLocationException {
486
		ArrayList res= new ArrayList();
487
		
488
		FindReplaceDocumentAdapter searcher= new FindReplaceDocumentAdapter(document);
489
		
490
		// search only at word boundaries 
491
		String searchPattern= "\\b" + asRegPattern(prefix); //$NON-NLS-1$
492
		
493
		IRegion reg= searcher.find(firstPosition, searchPattern, true, CASE_SENSITIVE, false, true);
494
		while (reg != null) {
495
			// try to complete to a word. case is irrelevant here.
496
			IRegion word= searcher.find(reg.getOffset(), COMPLETION_WORD_REGEX, true, true, false, true);
497
			if (word.getLength() > reg.getLength() ) { // empty suggestion will be added later
498
				String found= document.get(word.getOffset(), word.getLength());
499
				res.add(found.substring(prefix.length()));
500
			}
501
			int nextPos= word.getOffset() + word.getLength();
502
			if (nextPos >= document.getLength() ) {
503
				break;
504
			}
505
			reg= searcher.find(nextPos, searchPattern, true, CASE_SENSITIVE, false, true);
506
		}
507
		
508
		return res;
509
	}
510
	
511
	/**
512
	 * Search for possible completions in the backward direction.
513
	 * @param document the document to be scanned
514
	 * @param prefix the completion prefix
515
	 * @param firstPosition the caret position
516
     * @return a list of suggestions from the caret position to the beginning of the document.
517
	 * @throws BadLocationException if any error occurs
518
	 */
519
    public static ArrayList getBackwardsSuggestions(IDocument document, String prefix, int firstPosition) throws BadLocationException {
520
		ArrayList res= new ArrayList();
521
		
522
		FindReplaceDocumentAdapter searcher= new FindReplaceDocumentAdapter(document);
523
		
524
		// search only at word boundaries 
525
		String searchPattern= "\\b" + asRegPattern(prefix); //$NON-NLS-1$
526
		
527
		IRegion reg= searcher.find(firstPosition, searchPattern, false, CASE_SENSITIVE, false, true);
528
		while (reg != null) {
529
			// try to complete to a word. case is of no matter here
530
			IRegion word= searcher.find(reg.getOffset(), COMPLETION_WORD_REGEX, true, true, false, true);
531
			if (word.getLength() > reg.getLength() ) { // empty suggestion will be added later
532
				String found= document.get(word.getOffset(), word.getLength());
533
				res.add(found.substring(prefix.length()));
534
			}
535
			int nextPos= word.getOffset() - 1;
536
			if (nextPos < 0 ) {
537
				break;
538
			}
539
			reg= searcher.find(nextPos, searchPattern, false, CASE_SENSITIVE, false, true);
540
		}
541
		
542
		return res;
543
	}
544
	
545
	/**
546
     * The method is equivalent to <code>getForwardSuggestions(document, prefix, 0)</code> 
547
	 * @param document the document to scan
548
	 * @param prefix the completion prefix
549
     * @return the list of completions that are result of this document scan.
550
     * @throws BadLocationException if some error occurs. Should not be ever thrown.
551
     * 
552
	 * @see #getForwardSuggestions(IDocument, String, int)
553
	 */
554
    public static ArrayList getForwardSuggestions(IDocument document, 
555
            String prefix) throws BadLocationException {
556
		return getForwardSuggestions(document, prefix, 0);
557
	}
558
	
559
	/**
560
	 * Return the part of a word before the caret.
561
	 * If the caret is not at a middle/end of a word, 
562
	 * returns null.
563
	 * @return the prefix at the current cursor position that 
564
     *      will be used in the search for possible completions
565
	 * @throws BadLocationException if accessing the document fails
566
	 */
567
	private String getCurrentPrefix() throws BadLocationException {
568
		ITextSelection selection= (ITextSelection) getTextEditor().getSelectionProvider().getSelection();
569
		if (selection.getLength() > 0) {
570
			return null;
571
		}
572
		int pos= selection.getOffset();
573
		
574
		int prevNonAlpha= pos;
575
		while (prevNonAlpha > 0 && Character.isJavaIdentifierPart(fDocument.getChar(prevNonAlpha-1))) {
576
			prevNonAlpha--;
577
		}
578
		if (prevNonAlpha != pos) {
579
			return fDocument.get(prevNonAlpha, pos - prevNonAlpha);
580
		}
581
		return null;
582
	}
583
	
584
	/**
585
	 * Logs the exception.
586
	 * 
587
	 * @param e the exception
588
	 */
589
	private static void log(BadLocationException e) {
590
		String msg= e.getLocalizedMessage();
591
		if (msg == null)
592
			msg= "unable to access the document"; //$NON-NLS-1$
593
		TextEditorPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, IStatus.OK, msg, e));
594
	}
595
}

Return to bug 11668