diff options
author | David Green | 2012-03-14 19:20:52 +0000 |
---|---|---|
committer | David Green | 2012-03-14 19:20:52 +0000 |
commit | 43a1f32f2ef4bab9623fbf70651fcf36bc138782 (patch) | |
tree | 80449ee1d3b3d8d18eb96d253bb3d92fb8448ffd /org.eclipse.mylyn.tasks.index.ui/src/org | |
parent | 3f028a97da934953415e71e2781deb73917859ce (diff) | |
download | org.eclipse.mylyn.tasks-43a1f32f2ef4bab9623fbf70651fcf36bc138782.tar.gz org.eclipse.mylyn.tasks-43a1f32f2ef4bab9623fbf70651fcf36bc138782.tar.xz org.eclipse.mylyn.tasks-43a1f32f2ef4bab9623fbf70651fcf36bc138782.zip |
bug 373178: content assist for search index does not handle completions with spaces properly
https://bugs.eclipse.org/bugs/show_bug.cgi?id=373178
Change-Id: Iebf157c5534477e3bcfdb0f963b3025b691fd43a
Diffstat (limited to 'org.eclipse.mylyn.tasks.index.ui/src/org')
5 files changed, 250 insertions, 158 deletions
diff --git a/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/AbstractIndexReference.java b/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/AbstractIndexReference.java new file mode 100644 index 000000000..6e3fc8062 --- /dev/null +++ b/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/AbstractIndexReference.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2012 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.tasks.index.ui; + +import org.eclipse.mylyn.internal.tasks.index.core.TaskListIndex; + +/** + * @author David Green + */ +public abstract class AbstractIndexReference { + + public abstract TaskListIndex index(); +} diff --git a/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/ContentProposal.java b/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/ContentProposal.java index b84387678..daed4fd54 100644 --- a/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/ContentProposal.java +++ b/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/ContentProposal.java @@ -30,6 +30,10 @@ class ContentProposal implements IContentProposal { this(content, label, description, content.length()); } + public ContentProposal(String content, String contentSuffix, String label, String description) { + this(content + contentSuffix, label, description, content.length()); + } + public ContentProposal(String content, String label, String description, int cursorPosition) { this.content = content; this.cursorPosition = cursorPosition; diff --git a/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/ContentProposalProvider.java b/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/ContentProposalProvider.java new file mode 100644 index 000000000..19d004098 --- /dev/null +++ b/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/ContentProposalProvider.java @@ -0,0 +1,221 @@ +/******************************************************************************* + * Copyright (c) 2012 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.tasks.index.ui; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.regex.Pattern; + +import org.eclipse.jface.fieldassist.IContentProposal; +import org.eclipse.jface.fieldassist.IContentProposalProvider; +import org.eclipse.mylyn.internal.tasks.core.AbstractTask; +import org.eclipse.mylyn.internal.tasks.core.TaskList; +import org.eclipse.mylyn.internal.tasks.index.core.TaskListIndex; +import org.eclipse.mylyn.tasks.core.data.AbstractTaskSchema; +import org.eclipse.mylyn.tasks.core.data.AbstractTaskSchema.Field; +import org.eclipse.mylyn.tasks.core.data.TaskAttribute; +import org.eclipse.osgi.util.NLS; + +/** + * A content proposal provider for proposals on a task list index and task list. + * + * @author David Green + */ +public class ContentProposalProvider implements IContentProposalProvider { + + private static final Pattern PATTERN_WHITESPACE = Pattern.compile("\\s"); //$NON-NLS-1$ + + private static final Pattern PATTERN_SPECIAL_CHARACTERS = Pattern.compile("([+&\\|!\\(\\)\\{\\}\\[\\]^\"~\\*\\?:\\\\-])"); //$NON-NLS-1$ + + private final AbstractIndexReference reference; + + private final TaskList taskList; + + public ContentProposalProvider(TaskList taskList, AbstractIndexReference reference) { + this.taskList = taskList; + this.reference = reference; + } + + public static class ProposalContentState { + + public String beforePrefixContent; + + public String fieldPrefix = ""; //$NON-NLS-1$ + + public String prefix = ""; //$NON-NLS-1$ + + public String suffix = ""; //$NON-NLS-1$ + + public ProposalContentState(String contents) { + beforePrefixContent = contents; + } + + } + + public IContentProposal[] getProposals(String contents, int position) { + List<IContentProposal> proposals = new ArrayList<IContentProposal>(10); + + ProposalContentState contentState = computeProposalContentState(contents, position); + + // if we have a field prefix + if (contentState.fieldPrefix.length() > 0) { + AbstractTaskSchema.Field indexField = computeIndexField(contentState.fieldPrefix); + + // if it's a person field then suggest + // people from the task list + if (indexField != null && TaskAttribute.TYPE_PERSON.equals(indexField.getType())) { + computePersonProposals(proposals, contentState); + } + + } else { + + // suggest field name prefixes + for (Field field : reference.index().getIndexedFields()) { + + // searching on URL is not useful + if (field.equals(TaskListIndex.FIELD_IDENTIFIER)) { + continue; + } + + String indexKey = field.getIndexKey(); + if (indexKey.startsWith(contentState.prefix)) { + String description; + if (TaskListIndex.FIELD_CONTENT.equals(field)) { + description = Messages.IndexSearchHandler_hint_content; + } else if (TaskListIndex.FIELD_PERSON.equals(field)) { + description = Messages.IndexSearchHandler_hint_person; + } else { + description = NLS.bind(Messages.IndexSearchHandler_hint_generic, field.getLabel()); + } + proposals.add(new ContentProposal(contentState.beforePrefixContent + indexKey + ":", //$NON-NLS-1$ + contentState.suffix, indexKey, description)); + + if (TaskAttribute.TYPE_DATE.equals(field.getType()) + || TaskAttribute.TYPE_DATETIME.equals(field.getType())) { + computeDateRangeProposals(proposals, contentState, field); + } + } + } + } + + return proposals.toArray(new IContentProposal[proposals.size()]); + } + + public ProposalContentState computeProposalContentState(String contents, int position) { + ProposalContentState contentState = new ProposalContentState(contents); + + if (position >= 0 && position <= contents.length()) { + int i = position; + while (i > 0 && !Character.isWhitespace(contents.charAt(i - 1)) && contents.charAt(i - 1) != ':') { + --i; + } + if (i > 0 && contents.charAt(i - 1) == ':') { + int fieldEnd = i - 1; + int fieldStart = i - 1; + while (fieldStart > 0 && Character.isLetter(contents.charAt(fieldStart - 1))) { + --fieldStart; + } + contentState.fieldPrefix = contents.substring(fieldStart, fieldEnd); + } + + contentState.prefix = contents.substring(i, position); + contentState.beforePrefixContent = contents.substring(0, i); + + if (position < contents.length()) { + i = position; + while (i < contents.length()) { + if (Character.isWhitespace(contents.charAt(i))) { + break; + } + ++i; + } + contentState.suffix = contents.substring(i); + } + } + return contentState; + } + + public Field computeIndexField(String fieldPrefix) { + for (Field field : reference.index().getIndexedFields()) { + if (field.getIndexKey().equals(fieldPrefix)) { + return field; + } + } + return null; + } + + public void computeDateRangeProposals(List<IContentProposal> proposals, ProposalContentState contentState, + Field field) { + // for date fields give suggestion of date range search + String description; + final Date now = new Date(); + final Date dateSearchUpperBound; + final Date dateSearchOneWeekLowerBound; + { + GregorianCalendar calendar = new GregorianCalendar(); + + calendar.setTime(now); + calendar.add(Calendar.DAY_OF_WEEK, 1); // one day in future due to GMT conversion in index + dateSearchUpperBound = calendar.getTime(); + + calendar.setTime(now); + calendar.add(Calendar.DAY_OF_WEEK, -7); + dateSearchOneWeekLowerBound = calendar.getTime(); + } + + description = NLS.bind(Messages.IndexSearchHandler_Generic_date_range_search_1_week, field.getLabel()); + + String label = NLS.bind(Messages.IndexSearchHandler_Past_week_date_range_label, field.getIndexKey()); + + String queryText = reference.index().computeQueryFieldDateRange(field, dateSearchOneWeekLowerBound, + dateSearchUpperBound); + + if (queryText.startsWith(contentState.prefix)) { + proposals.add(new ContentProposal(contentState.beforePrefixContent + queryText, contentState.suffix, label, + description)); + } + } + + public void computePersonProposals(List<IContentProposal> proposals, ProposalContentState contentState) { + Set<String> addresses = new TreeSet<String>(); + + Collection<AbstractTask> allTasks = taskList.getAllTasks(); + for (AbstractTask task : allTasks) { + addAddresses(addresses, task); + } + + for (String address : addresses) { + if (address.startsWith(contentState.prefix)) { + String proposalContent = address; + proposalContent = PATTERN_SPECIAL_CHARACTERS.matcher(proposalContent).replaceAll("\\\\$1"); //$NON-NLS-1$ + if (PATTERN_WHITESPACE.matcher(proposalContent).find()) { + proposalContent = "\"" + proposalContent + "\""; //$NON-NLS-1$//$NON-NLS-2$ + } + proposals.add(new ContentProposal(contentState.beforePrefixContent + proposalContent, + contentState.suffix, address, null)); + } + } + } + + private void addAddresses(Set<String> addresses, AbstractTask task) { + String name = task.getOwner(); + if (name != null && name.trim().length() > 0) { + addresses.add(name.trim()); + } + } +} diff --git a/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/IndexReference.java b/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/IndexReference.java index 5181cbf43..bfdc8001e 100644 --- a/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/IndexReference.java +++ b/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/IndexReference.java @@ -23,7 +23,7 @@ import org.eclipse.mylyn.tasks.core.IRepositoryManager; * @author David Green * @author Steffen Pingel */ -public class IndexReference { +public class IndexReference extends AbstractIndexReference { private static TaskListIndex theIndex; @@ -49,6 +49,7 @@ public class IndexReference { */ private TaskListIndex index; + @Override public TaskListIndex index() { synchronized (IndexReference.class) { if (index == null) { diff --git a/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/IndexSearchHandler.java b/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/IndexSearchHandler.java index 61beb9d35..0a2dce97c 100644 --- a/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/IndexSearchHandler.java +++ b/org.eclipse.mylyn.tasks.index.ui/src/org/eclipse/mylyn/internal/tasks/index/ui/IndexSearchHandler.java @@ -11,29 +11,15 @@ package org.eclipse.mylyn.internal.tasks.index.ui; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; -import java.util.regex.Pattern; import org.eclipse.jface.fieldassist.ContentProposalAdapter; -import org.eclipse.jface.fieldassist.IContentProposal; import org.eclipse.jface.fieldassist.IContentProposalProvider; import org.eclipse.jface.fieldassist.TextContentAdapter; import org.eclipse.jface.layout.GridLayoutFactory; -import org.eclipse.mylyn.internal.tasks.core.AbstractTask; import org.eclipse.mylyn.internal.tasks.index.core.TaskListIndex; import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin; import org.eclipse.mylyn.internal.tasks.ui.search.AbstractSearchHandler; -import org.eclipse.mylyn.tasks.core.data.AbstractTaskSchema; import org.eclipse.mylyn.tasks.core.data.AbstractTaskSchema.Field; -import org.eclipse.mylyn.tasks.core.data.TaskAttribute; -import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; @@ -52,154 +38,12 @@ import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter; */ public class IndexSearchHandler extends AbstractSearchHandler { - private static final Pattern PATTERN_WHITESPACE = Pattern.compile("\\s"); //$NON-NLS-1$ - - private static final Pattern PATTERN_SPECIAL_CHARACTERS = Pattern.compile("([+&\\|!\\(\\)\\{\\}\\[\\]^\"~\\*\\?:\\\\-])"); //$NON-NLS-1$ - - private class ContentProposalProvider implements IContentProposalProvider { - - public IContentProposal[] getProposals(String contents, int position) { - List<IContentProposal> proposals = new ArrayList<IContentProposal>(10); - - String beforePrefixContent = contents; - String fieldPrefix = ""; //$NON-NLS-1$ - String prefix = ""; //$NON-NLS-1$ - if (position >= 0 && position <= contents.length()) { - int i = position; - while (i > 0 && !Character.isWhitespace(contents.charAt(i - 1)) && contents.charAt(i - 1) != ':') { - --i; - } - if (i > 0 && contents.charAt(i - 1) == ':') { - int fieldEnd = i - 1; - int fieldStart = i - 1; - while (fieldStart > 0 && Character.isLetter(contents.charAt(fieldStart - 1))) { - --fieldStart; - } - fieldPrefix = contents.substring(fieldStart, fieldEnd); - } - - prefix = contents.substring(i, position); - beforePrefixContent = contents.substring(0, i); - } - - // if we have a field prefix - if (fieldPrefix.length() > 0) { - AbstractTaskSchema.Field indexField = computeIndexField(fieldPrefix); - - // if it's a person field then suggest - // people from the task list - if (indexField != null && TaskAttribute.TYPE_PERSON.equals(indexField.getType())) { - computePersonProposals(proposals, beforePrefixContent, prefix); - } - - } else { - - // suggest field name prefixes - for (Field field : reference.index().getIndexedFields()) { - - // searching on URL is not useful - if (field.equals(TaskListIndex.FIELD_IDENTIFIER)) { - continue; - } - - String indexKey = field.getIndexKey(); - if (indexKey.startsWith(prefix)) { - String description; - if (TaskListIndex.FIELD_CONTENT.equals(field)) { - description = Messages.IndexSearchHandler_hint_content; - } else if (TaskListIndex.FIELD_PERSON.equals(field)) { - description = Messages.IndexSearchHandler_hint_person; - } else { - description = NLS.bind(Messages.IndexSearchHandler_hint_generic, field.getLabel()); - } - proposals.add(new ContentProposal(beforePrefixContent + indexKey + ":", //$NON-NLS-1$ - indexKey, description)); - - if (TaskAttribute.TYPE_DATE.equals(field.getType()) - || TaskAttribute.TYPE_DATETIME.equals(field.getType())) { - computeDateRangeProposals(proposals, beforePrefixContent, prefix, field); - } - } - } - } - - return proposals.toArray(new IContentProposal[proposals.size()]); - } - - public void computeDateRangeProposals(List<IContentProposal> proposals, String beforePrefixContent, - String prefix, Field field) { - // for date fields give suggestion of date range search - String description; - final Date now = new Date(); - final Date dateSearchUpperBound; - final Date dateSearchOneWeekLowerBound; - { - GregorianCalendar calendar = new GregorianCalendar(); - - calendar.setTime(now); - calendar.add(Calendar.DAY_OF_WEEK, 1); // one day in future due to GMT conversion in index - dateSearchUpperBound = calendar.getTime(); - - calendar.setTime(now); - calendar.add(Calendar.DAY_OF_WEEK, -7); - dateSearchOneWeekLowerBound = calendar.getTime(); - } - - description = NLS.bind(Messages.IndexSearchHandler_Generic_date_range_search_1_week, field.getLabel()); - - String label = NLS.bind(Messages.IndexSearchHandler_Past_week_date_range_label, field.getIndexKey()); - - String queryText = reference.index().computeQueryFieldDateRange(field, dateSearchOneWeekLowerBound, - dateSearchUpperBound); - - if (queryText.startsWith(prefix)) { - proposals.add(new ContentProposal(beforePrefixContent + queryText, label, description)); - } - } - - public void computePersonProposals(List<IContentProposal> proposals, String beforePrefixContent, String prefix) { - Set<String> addresses = new TreeSet<String>(); - - Collection<AbstractTask> allTasks = TasksUiPlugin.getTaskList().getAllTasks(); - for (AbstractTask task : allTasks) { - addAddresses(addresses, task); - } - - for (String address : addresses) { - if (address.startsWith(prefix)) { - String proposalContent = address; - proposalContent = PATTERN_SPECIAL_CHARACTERS.matcher(proposalContent).replaceAll("\\\\$1"); //$NON-NLS-1$ - if (PATTERN_WHITESPACE.matcher(proposalContent).find()) { - proposalContent = "\"" + proposalContent + "\""; //$NON-NLS-1$//$NON-NLS-2$ - } - proposals.add(new ContentProposal(beforePrefixContent + proposalContent, address, null)); - } - } - } - - private void addAddresses(Set<String> addresses, AbstractTask task) { - String name = task.getOwner(); - if (name != null && name.trim().length() > 0) { - addresses.add(name.trim()); - } - } - } - private IndexReference reference; public IndexSearchHandler() { reference = new IndexReference(); } - public Field computeIndexField(String fieldPrefix) { - for (Field field : reference.index().getIndexedFields()) { - if (field.getIndexKey().equals(fieldPrefix)) { - return field; - } - } - return null; - } - @Override public Composite createSearchComposite(Composite parent) { Composite container = new Composite(parent, SWT.NULL); @@ -231,7 +75,7 @@ public class IndexSearchHandler extends AbstractSearchHandler { @Override public void adaptTextSearchControl(Text textControl) { - IContentProposalProvider proposalProvider = new ContentProposalProvider(); + IContentProposalProvider proposalProvider = new ContentProposalProvider(TasksUiPlugin.getTaskList(), reference); final ContentAssistCommandAdapter adapter = new ContentAssistCommandAdapter(textControl, new TextContentAdapter(), proposalProvider, null, new char[0], true); adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE); |