001: /*******************************************************************************
002: * Copyright (c) 2006, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.texteditor;
011:
012: import java.util.ArrayList;
013:
014: import org.eclipse.jface.fieldassist.IContentProposal;
015: import org.eclipse.jface.fieldassist.IContentProposalProvider;
016:
017: /**
018: * Content assist proposal provider for regular expressions.
019: * <p>
020: * Note: Replaces <code>RegExContentAssistProcessor</code> which was introduced in 3.0.
021: * </p>
022: *
023: * @since 3.2
024: */
025: final class RegExContentProposalProvider implements
026: IContentProposalProvider {
027:
028: /**
029: * Proposal computer.
030: */
031: private static class ProposalComputer {
032:
033: private static class Proposal implements IContentProposal {
034:
035: private String fContent;
036: private String fLabel;
037: private String fDescription;
038: private int fCursorPosition;
039:
040: Proposal(String content, String label, String description,
041: int cursorPosition) {
042: fContent = content;
043: fLabel = label;
044: fDescription = description;
045: fCursorPosition = cursorPosition;
046: }
047:
048: public String getContent() {
049: return fContent;
050: }
051:
052: public String getLabel() {
053: return fLabel;
054: }
055:
056: public String getDescription() {
057: return fDescription;
058: }
059:
060: public int getCursorPosition() {
061: return fCursorPosition;
062: }
063: }
064:
065: /**
066: * The whole regular expression.
067: */
068: private final String fExpression;
069: /**
070: * The document offset.
071: */
072: private final int fDocumentOffset;
073: /**
074: * The high-priority proposals.
075: */
076: private final ArrayList fPriorityProposals;
077: /**
078: * The low-priority proposals.
079: */
080: private final ArrayList fProposals;
081: /**
082: * <code>true</code> iff <code>fExpression</code> ends with an open escape.
083: */
084: private final boolean fIsEscape;
085:
086: /**
087: * Creates a new Proposal Computer.
088: * @param contents the contents of the subject control
089: * @param position the cursor position
090: */
091: public ProposalComputer(String contents, int position) {
092: fExpression = contents;
093: fDocumentOffset = position;
094: fPriorityProposals = new ArrayList();
095: fProposals = new ArrayList();
096:
097: boolean isEscape = false;
098: esc: for (int i = position - 1; i >= 0; i--) {
099: if (fExpression.charAt(i) == '\\')
100: isEscape = !isEscape;
101: else
102: break esc;
103: }
104: fIsEscape = isEscape;
105: }
106:
107: /**
108: * Computes applicable proposals for the find field.
109: * @return the proposals
110: */
111: public IContentProposal[] computeFindProposals() {
112: //characters
113: addBsProposal(
114: "\\\\", RegExMessages.displayString_bs_bs, RegExMessages.additionalInfo_bs_bs); //$NON-NLS-1$
115: addBracketProposal(
116: "\\0", 2, RegExMessages.displayString_bs_0, RegExMessages.additionalInfo_bs_0); //$NON-NLS-1$
117: addBracketProposal(
118: "\\x", 2, RegExMessages.displayString_bs_x, RegExMessages.additionalInfo_bs_x); //$NON-NLS-1$
119: addBracketProposal(
120: "\\u", 2, RegExMessages.displayString_bs_u, RegExMessages.additionalInfo_bs_u); //$NON-NLS-1$
121: addBsProposal(
122: "\\t", RegExMessages.displayString_bs_t, RegExMessages.additionalInfo_bs_t); //$NON-NLS-1$
123: addBsProposal(
124: "\\R", RegExMessages.displayString_bs_R, RegExMessages.additionalInfo_bs_R); //$NON-NLS-1$
125: addBsProposal(
126: "\\n", RegExMessages.displayString_bs_n, RegExMessages.additionalInfo_bs_n); //$NON-NLS-1$
127: addBsProposal(
128: "\\r", RegExMessages.displayString_bs_r, RegExMessages.additionalInfo_bs_r); //$NON-NLS-1$
129: addBsProposal(
130: "\\f", RegExMessages.displayString_bs_f, RegExMessages.additionalInfo_bs_f); //$NON-NLS-1$
131: addBsProposal(
132: "\\a", RegExMessages.displayString_bs_a, RegExMessages.additionalInfo_bs_a); //$NON-NLS-1$
133: addBsProposal(
134: "\\e", RegExMessages.displayString_bs_e, RegExMessages.additionalInfo_bs_e); //$NON-NLS-1$
135: addBracketProposal(
136: "\\c", 2, RegExMessages.displayString_bs_c, RegExMessages.additionalInfo_bs_c); //$NON-NLS-1$
137:
138: if (!fIsEscape)
139: addBracketProposal(
140: ".", 1, RegExMessages.displayString_dot, RegExMessages.additionalInfo_dot); //$NON-NLS-1$
141: addBsProposal(
142: "\\d", RegExMessages.displayString_bs_d, RegExMessages.additionalInfo_bs_d); //$NON-NLS-1$
143: addBsProposal(
144: "\\D", RegExMessages.displayString_bs_D, RegExMessages.additionalInfo_bs_D); //$NON-NLS-1$
145: addBsProposal(
146: "\\s", RegExMessages.displayString_bs_s, RegExMessages.additionalInfo_bs_s); //$NON-NLS-1$
147: addBsProposal(
148: "\\S", RegExMessages.displayString_bs_S, RegExMessages.additionalInfo_bs_S); //$NON-NLS-1$
149: addBsProposal(
150: "\\w", RegExMessages.displayString_bs_w, RegExMessages.additionalInfo_bs_w); //$NON-NLS-1$
151: addBsProposal(
152: "\\W", RegExMessages.displayString_bs_W, RegExMessages.additionalInfo_bs_W); //$NON-NLS-1$
153:
154: // back reference
155: addBsProposal(
156: "\\", RegExMessages.displayString_bs_i, RegExMessages.additionalInfo_bs_i); //$NON-NLS-1$
157:
158: //quoting
159: addBsProposal(
160: "\\", RegExMessages.displayString_bs, RegExMessages.additionalInfo_bs); //$NON-NLS-1$
161: addBsProposal(
162: "\\Q", RegExMessages.displayString_bs_Q, RegExMessages.additionalInfo_bs_Q); //$NON-NLS-1$
163: addBsProposal(
164: "\\E", RegExMessages.displayString_bs_E, RegExMessages.additionalInfo_bs_E); //$NON-NLS-1$
165:
166: //character sets
167: if (!fIsEscape) {
168: addBracketProposal(
169: "[]", 1, RegExMessages.displayString_set, RegExMessages.additionalInfo_set); //$NON-NLS-1$
170: addBracketProposal(
171: "[^]", 2, RegExMessages.displayString_setExcl, RegExMessages.additionalInfo_setExcl); //$NON-NLS-1$
172: addBracketProposal(
173: "[-]", 1, RegExMessages.displayString_setRange, RegExMessages.additionalInfo_setRange); //$NON-NLS-1$
174: addProposal(
175: "&&", RegExMessages.displayString_setInter, RegExMessages.additionalInfo_setInter); //$NON-NLS-1$
176: }
177: if (!fIsEscape && fDocumentOffset > 0
178: && fExpression.charAt(fDocumentOffset - 1) == '\\') {
179: addProposal(
180: "\\p{}", 3, RegExMessages.displayString_posix, RegExMessages.additionalInfo_posix); //$NON-NLS-1$
181: addProposal(
182: "\\P{}", 3, RegExMessages.displayString_posixNot, RegExMessages.additionalInfo_posixNot); //$NON-NLS-1$
183: } else {
184: addBracketProposal(
185: "\\p{}", 3, RegExMessages.displayString_posix, RegExMessages.additionalInfo_posix); //$NON-NLS-1$
186: addBracketProposal(
187: "\\P{}", 3, RegExMessages.displayString_posixNot, RegExMessages.additionalInfo_posixNot); //$NON-NLS-1$
188: }
189:
190: //boundary matchers
191: if (fDocumentOffset == 0) {
192: addPriorityProposal(
193: "^", RegExMessages.displayString_start, RegExMessages.additionalInfo_start); //$NON-NLS-1$
194: } else if (fDocumentOffset == 1
195: && fExpression.charAt(0) == '^') {
196: addBracketProposal(
197: "^", 1, RegExMessages.displayString_start, RegExMessages.additionalInfo_start); //$NON-NLS-1$
198: }
199: if (fDocumentOffset == fExpression.length()) {
200: addProposal(
201: "$", RegExMessages.displayString_end, RegExMessages.additionalInfo_end); //$NON-NLS-1$
202: }
203: addBsProposal(
204: "\\b", RegExMessages.displayString_bs_b, RegExMessages.additionalInfo_bs_b); //$NON-NLS-1$
205: addBsProposal(
206: "\\B", RegExMessages.displayString_bs_B, RegExMessages.additionalInfo_bs_B); //$NON-NLS-1$
207: addBsProposal(
208: "\\A", RegExMessages.displayString_bs_A, RegExMessages.additionalInfo_bs_A); //$NON-NLS-1$
209: addBsProposal(
210: "\\G", RegExMessages.displayString_bs_G, RegExMessages.additionalInfo_bs_G); //$NON-NLS-1$
211: addBsProposal(
212: "\\Z", RegExMessages.displayString_bs_Z, RegExMessages.additionalInfo_bs_Z); //$NON-NLS-1$
213: addBsProposal(
214: "\\z", RegExMessages.displayString_bs_z, RegExMessages.additionalInfo_bs_z); //$NON-NLS-1$
215:
216: if (!fIsEscape) {
217: //capturing groups
218: addBracketProposal(
219: "()", 1, RegExMessages.displayString_group, RegExMessages.additionalInfo_group); //$NON-NLS-1$
220:
221: //flags
222: addBracketProposal(
223: "(?)", 2, RegExMessages.displayString_flag, RegExMessages.additionalInfo_flag); //$NON-NLS-1$
224: addBracketProposal(
225: "(?:)", 3, RegExMessages.displayString_flagExpr, RegExMessages.additionalInfo_flagExpr); //$NON-NLS-1$
226:
227: //non-capturing group
228: addBracketProposal(
229: "(?:)", 3, RegExMessages.displayString_nonCap, RegExMessages.additionalInfo_nonCap); //$NON-NLS-1$
230: addBracketProposal(
231: "(?>)", 3, RegExMessages.displayString_atomicCap, RegExMessages.additionalInfo_atomicCap); //$NON-NLS-1$
232:
233: //look around
234: addBracketProposal(
235: "(?=)", 3, RegExMessages.displayString_posLookahead, RegExMessages.additionalInfo_posLookahead); //$NON-NLS-1$
236: addBracketProposal(
237: "(?!)", 3, RegExMessages.displayString_negLookahead, RegExMessages.additionalInfo_negLookahead); //$NON-NLS-1$
238: addBracketProposal(
239: "(?<=)", 4, RegExMessages.displayString_posLookbehind, RegExMessages.additionalInfo_posLookbehind); //$NON-NLS-1$
240: addBracketProposal(
241: "(?<!)", 4, RegExMessages.displayString_negLookbehind, RegExMessages.additionalInfo_negLookbehind); //$NON-NLS-1$
242:
243: //greedy quantifiers
244: addBracketProposal(
245: "?", 1, RegExMessages.displayString_quest, RegExMessages.additionalInfo_quest); //$NON-NLS-1$
246: addBracketProposal(
247: "*", 1, RegExMessages.displayString_star, RegExMessages.additionalInfo_star); //$NON-NLS-1$
248: addBracketProposal(
249: "+", 1, RegExMessages.displayString_plus, RegExMessages.additionalInfo_plus); //$NON-NLS-1$
250: addBracketProposal(
251: "{}", 1, RegExMessages.displayString_exact, RegExMessages.additionalInfo_exact); //$NON-NLS-1$
252: addBracketProposal(
253: "{,}", 1, RegExMessages.displayString_least, RegExMessages.additionalInfo_least); //$NON-NLS-1$
254: addBracketProposal(
255: "{,}", 1, RegExMessages.displayString_count, RegExMessages.additionalInfo_count); //$NON-NLS-1$
256:
257: //lazy quantifiers
258: addBracketProposal(
259: "??", 1, RegExMessages.displayString_questLazy, RegExMessages.additionalInfo_questLazy); //$NON-NLS-1$
260: addBracketProposal(
261: "*?", 1, RegExMessages.displayString_starLazy, RegExMessages.additionalInfo_starLazy); //$NON-NLS-1$
262: addBracketProposal(
263: "+?", 1, RegExMessages.displayString_plusLazy, RegExMessages.additionalInfo_plusLazy); //$NON-NLS-1$
264: addBracketProposal(
265: "{}?", 1, RegExMessages.displayString_exactLazy, RegExMessages.additionalInfo_exactLazy); //$NON-NLS-1$
266: addBracketProposal(
267: "{,}?", 1, RegExMessages.displayString_leastLazy, RegExMessages.additionalInfo_leastLazy); //$NON-NLS-1$
268: addBracketProposal(
269: "{,}?", 1, RegExMessages.displayString_countLazy, RegExMessages.additionalInfo_countLazy); //$NON-NLS-1$
270:
271: //possessive quantifiers
272: addBracketProposal(
273: "?+", 1, RegExMessages.displayString_questPoss, RegExMessages.additionalInfo_questPoss); //$NON-NLS-1$
274: addBracketProposal(
275: "*+", 1, RegExMessages.displayString_starPoss, RegExMessages.additionalInfo_starPoss); //$NON-NLS-1$
276: addBracketProposal(
277: "++", 1, RegExMessages.displayString_plusPoss, RegExMessages.additionalInfo_plusPoss); //$NON-NLS-1$
278: addBracketProposal(
279: "{}+", 1, RegExMessages.displayString_exactPoss, RegExMessages.additionalInfo_exactPoss); //$NON-NLS-1$
280: addBracketProposal(
281: "{,}+", 1, RegExMessages.displayString_leastPoss, RegExMessages.additionalInfo_leastPoss); //$NON-NLS-1$
282: addBracketProposal(
283: "{,}+", 1, RegExMessages.displayString_countPoss, RegExMessages.additionalInfo_countPoss); //$NON-NLS-1$
284:
285: //alternative
286: addBracketProposal(
287: "|", 1, RegExMessages.displayString_alt, RegExMessages.additionalInfo_alt); //$NON-NLS-1$
288: }
289:
290: fPriorityProposals.addAll(fProposals);
291: return (IContentProposal[]) fPriorityProposals
292: .toArray(new IContentProposal[fProposals.size()]);
293: }
294:
295: /**
296: * Computes applicable proposals for the replace field.
297: * @return the proposals
298: */
299: public IContentProposal[] computeReplaceProposals() {
300: if (fDocumentOffset > 0
301: && '$' == fExpression.charAt(fDocumentOffset - 1)) {
302: addProposal(
303: "", RegExMessages.displayString_dollar, RegExMessages.additionalInfo_dollar); //$NON-NLS-1$
304: } else {
305: if (!fIsEscape)
306: addProposal(
307: "$", RegExMessages.displayString_dollar, RegExMessages.additionalInfo_dollar); //$NON-NLS-1$
308: addBsProposal(
309: "\\", RegExMessages.displayString_replace_cap, RegExMessages.additionalInfo_replace_cap); //$NON-NLS-1$
310: addBsProposal(
311: "\\", RegExMessages.displayString_replace_bs, RegExMessages.additionalInfo_replace_bs); //$NON-NLS-1$
312: addBsProposal(
313: "\\R", RegExMessages.displayString_replace_bs_R, RegExMessages.additionalInfo_replace_bs_R); //$NON-NLS-1$
314: addBracketProposal(
315: "\\x", 2, RegExMessages.displayString_bs_x, RegExMessages.additionalInfo_bs_x); //$NON-NLS-1$
316: addBracketProposal(
317: "\\u", 2, RegExMessages.displayString_bs_u, RegExMessages.additionalInfo_bs_u); //$NON-NLS-1$
318: addBsProposal(
319: "\\t", RegExMessages.displayString_bs_t, RegExMessages.additionalInfo_bs_t); //$NON-NLS-1$
320: addBsProposal(
321: "\\n", RegExMessages.displayString_replace_bs_n, RegExMessages.additionalInfo_replace_bs_n); //$NON-NLS-1$
322: addBsProposal(
323: "\\r", RegExMessages.displayString_replace_bs_r, RegExMessages.additionalInfo_replace_bs_r); //$NON-NLS-1$
324: addBsProposal(
325: "\\f", RegExMessages.displayString_bs_f, RegExMessages.additionalInfo_bs_f); //$NON-NLS-1$
326: addBsProposal(
327: "\\a", RegExMessages.displayString_bs_a, RegExMessages.additionalInfo_bs_a); //$NON-NLS-1$
328: addBsProposal(
329: "\\e", RegExMessages.displayString_bs_e, RegExMessages.additionalInfo_bs_e); //$NON-NLS-1$
330: addBracketProposal(
331: "\\c", 2, RegExMessages.displayString_bs_c, RegExMessages.additionalInfo_bs_c); //$NON-NLS-1$
332: }
333: fPriorityProposals.addAll(fProposals);
334: return (IContentProposal[]) fPriorityProposals
335: .toArray(new IContentProposal[fPriorityProposals
336: .size()]);
337: }
338:
339: /**
340: * Adds a proposal.
341: *
342: * @param proposal the string to be inserted
343: * @param displayString the proposal's label
344: * @param additionalInfo the additional information
345: */
346: private void addProposal(String proposal, String displayString,
347: String additionalInfo) {
348: fProposals.add(new Proposal(proposal, displayString,
349: additionalInfo, proposal.length()));
350: }
351:
352: /**
353: * Adds a proposal.
354: *
355: * @param proposal the string to be inserted
356: * @param cursorPosition the cursor position after insertion,
357: * relative to the start of the proposal
358: * @param displayString the proposal's label
359: * @param additionalInfo the additional information
360: */
361: private void addProposal(String proposal, int cursorPosition,
362: String displayString, String additionalInfo) {
363: fProposals.add(new Proposal(proposal, displayString,
364: additionalInfo, cursorPosition));
365: }
366:
367: /**
368: * Adds a proposal to the priority proposals list.
369: *
370: * @param proposal the string to be inserted
371: * @param displayString the proposal's label
372: * @param additionalInfo the additional information
373: */
374: private void addPriorityProposal(String proposal,
375: String displayString, String additionalInfo) {
376: fPriorityProposals.add(new Proposal(proposal,
377: displayString, additionalInfo, proposal.length()));
378: }
379:
380: /**
381: * Adds a proposal. Ensures that existing pre- and postfixes are not duplicated.
382: *
383: * @param proposal the string to be inserted
384: * @param cursorPosition the cursor position after insertion,
385: * relative to the start of the proposal
386: * @param displayString the proposal's label
387: * @param additionalInfo the additional information
388: */
389: private void addBracketProposal(String proposal,
390: int cursorPosition, String displayString,
391: String additionalInfo) {
392: String prolog = fExpression.substring(0, fDocumentOffset);
393: if (!fIsEscape
394: && prolog.endsWith("\\") && proposal.startsWith("\\")) { //$NON-NLS-1$//$NON-NLS-2$
395: fProposals.add(new Proposal(proposal, displayString,
396: additionalInfo, cursorPosition));
397: return;
398: }
399: for (int i = 1; i <= cursorPosition; i++) {
400: String prefix = proposal.substring(0, i);
401: if (prolog.endsWith(prefix)) {
402: String postfix = proposal.substring(cursorPosition);
403: String epilog = fExpression
404: .substring(fDocumentOffset);
405: if (epilog.startsWith(postfix)) {
406: fPriorityProposals.add(new Proposal(proposal
407: .substring(i, cursorPosition),
408: displayString, additionalInfo,
409: cursorPosition - i));
410: } else {
411: fPriorityProposals.add(new Proposal(proposal
412: .substring(i), displayString,
413: additionalInfo, cursorPosition - i));
414: }
415: return;
416: }
417: }
418: fProposals.add(new Proposal(proposal, displayString,
419: additionalInfo, cursorPosition));
420: }
421:
422: /**
423: * Adds a proposal that starts with a backslash.
424: * Ensures that the backslash is not repeated if already typed.
425: *
426: * @param proposal the string to be inserted
427: * @param displayString the proposal's label
428: * @param additionalInfo the additional information
429: */
430: private void addBsProposal(String proposal,
431: String displayString, String additionalInfo) {
432: String prolog = fExpression.substring(0, fDocumentOffset);
433: int position = proposal.length();
434: // If the string already contains the backslash, do not include in the proposal
435: if (prolog.endsWith("\\")) { //$NON-NLS-1$
436: position--;
437: proposal = proposal.substring(1);
438: }
439:
440: if (fIsEscape) {
441: fPriorityProposals.add(new Proposal(proposal,
442: displayString, additionalInfo, position));
443: } else {
444: addProposal(proposal, position, displayString,
445: additionalInfo);
446: }
447: }
448: }
449:
450: /**
451: * <code>true</code> iff the processor is for the find field.
452: * <code>false</code> iff the processor is for the replace field.
453: */
454: private final boolean fIsFind;
455:
456: /**
457: * Creates a new completion proposal provider.
458: *
459: * @param isFind <code>true</code> if the provider is used for the 'find' field
460: * <code>false</code> if the provider is used for the 'reaplce' field
461: */
462: public RegExContentProposalProvider(boolean isFind) {
463: fIsFind = isFind;
464: }
465:
466: /*
467: * @see org.eclipse.jface.fieldassist.IContentProposalProvider#getProposals(java.lang.String, int)
468: */
469: public IContentProposal[] getProposals(String contents, int position) {
470: if (fIsFind)
471: return new ProposalComputer(contents, position)
472: .computeFindProposals();
473: return new ProposalComputer(contents, position)
474: .computeReplaceProposals();
475: }
476: }
|