From 138b5f3683ef07c98eff9c6ac23b21a9f0e3c54a Mon Sep 17 00:00:00 2001 From: streambot Date: Wed, 22 Sep 2010 06:09:02 +0000 Subject: [PATCH] + Added "Match whole words" feature; * Fixed missed occurrences during case insensitive searching. --- AddinSettings.cs | 9 +++- Search/TextSearch.cs | 104 +++++++++++++++++++++++++++---------------- 2 files changed, 73 insertions(+), 40 deletions(-) diff --git a/AddinSettings.cs b/AddinSettings.cs index 1148085..47fa46a 100644 --- a/AddinSettings.cs +++ b/AddinSettings.cs @@ -62,9 +62,13 @@ private sealed class SingletonCreator #region Search [Category("Search")] - [DisplayName("Case sensitive"), Description("Needs reopening of documents")] + [DisplayName("Match case"), Description("Needs reopening of documents")] public bool CaseSensitiveSearch { get; set; } + [Category("Search")] + [DisplayName("Match whole words"), Description("Needs reopening of documents")] + public bool SearchWholeWordsOnly { get; set; } + #endregion #region Experemental @@ -95,6 +99,7 @@ public void ResetToDefaults() FilledMarks = false; CaseSensitiveSearch = false; + SearchWholeWordsOnly = false; } public void Load(SettingRepository repository) @@ -124,6 +129,7 @@ public void Reload() FilledMarks = settings.GetBoolSetting("FilledMarks", FilledMarks); CaseSensitiveSearch = settings.GetBoolSetting("CaseSensitiveSearch", CaseSensitiveSearch); + SearchWholeWordsOnly = settings.GetBoolSetting("SearchWholeWordsOnly", SearchWholeWordsOnly); } } @@ -145,6 +151,7 @@ public void Save() settings.SetBoolSetting("FilledMarks", FilledMarks); settings.SetBoolSetting("CaseSensitiveSearch", CaseSensitiveSearch); + settings.SetBoolSetting("SearchWholeWordsOnly", SearchWholeWordsOnly); } } } diff --git a/Search/TextSearch.cs b/Search/TextSearch.cs index a725ca5..0397f2a 100644 --- a/Search/TextSearch.cs +++ b/Search/TextSearch.cs @@ -15,8 +15,8 @@ public class TextSearch private class SearchJob { public string Value { get; set; } - public int SearchStart { get; set; } - public int SearchEnd { get; set; } + public int SearchStart { get; set; } + public int SearchEnd { get; set; } } const int SearchDelay = 250; //ms @@ -34,7 +34,8 @@ private class SearchJob public event EventHandler SearchCompleted; - private StringComparison _comparsion; + private bool _caseSensitiveSearch; + private bool _searchWholeWordsOnly; public TextSearch(IVsTextLines buffer) { @@ -49,11 +50,8 @@ public TextSearch(IVsTextLines buffer) _asyncJobs = new Queue(); - _comparsion = StringComparison.InvariantCultureIgnoreCase; - if (AddinSettings.Instance.CaseSensitiveSearch) - { - _comparsion = StringComparison.InvariantCulture; - } + _caseSensitiveSearch = AddinSettings.Instance.CaseSensitiveSearch; + _searchWholeWordsOnly = AddinSettings.Instance.SearchWholeWordsOnly; } /// @@ -63,49 +61,77 @@ public TextSearch(IVsTextLines buffer) private TextOccurences SearchOccurrencesInText(string text, string value, int searchStart, int searchEnd) { var positions = new TreapBuilder(); - + /* Preprocessing */ - int valueLength = value.Length; - var badChars = new Dictionary(valueLength); - + int textLength = text.Length; + int valueLength = value.Length; + var badChars = new Dictionary(valueLength); + for (int i = 0; i < valueLength; i++) { - int key = value[i]; - badChars[key] = valueLength - i; + char c = value[i]; + + if (_caseSensitiveSearch) + badChars[c] = valueLength - i; + else + { + badChars[char.ToLower(c)] = valueLength - i; + badChars[char.ToUpper(c)] = valueLength - i; + } } /* Searching */ - searchEnd = Math.Min(searchEnd, text.Length) - (valueLength - 1); + var comparsion = (_caseSensitiveSearch ? + StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase); + + searchEnd = Math.Min(searchEnd, text.Length) - (valueLength - 1); for (int i = searchStart; i < searchEnd; ) { - if (text.Substring(i, valueLength).StartsWith(value, _comparsion)) - positions.Add(i); + if (text.Substring(i, valueLength).StartsWith(value, comparsion)) + { + if (!_searchWholeWordsOnly) + { + positions.Add(i); + } + else + { + if ( + (i - 1 < 0 || !char.IsLetterOrDigit(text[i - 1])) + && (i + valueLength >= textLength || !char.IsLetterOrDigit(text[i + valueLength])) + ) + positions.Add(i); + } + } - if (i + valueLength >= searchEnd) - break; + if (i + valueLength >= searchEnd) + break; - int key = text[i + valueLength]; - if (badChars.ContainsKey(key)) - i += badChars[key]; - else - i += valueLength + 1; + int key = text[i + valueLength]; + if (badChars.ContainsKey(key)) + i += badChars[key]; + else + i += valueLength + 1; } return new TextOccurences(value, positions); } - public TextOccurences SearchOccurrences(string value, int searchStart, int searchEnd) + public TextOccurences SearchOccurrences(string value, int searchStart, int searchEnd) { if (!string.IsNullOrEmpty(value)) { - string text = _buffer.GetText(); - if (!string.IsNullOrEmpty(text)) + //Disabled searching of multi line text + if (!value.Contains('\n')) { - int length = value.Length; - - if (searchEnd >= searchStart && length > 0) + string text = _buffer.GetText(); + if (!string.IsNullOrEmpty(text)) { - return SearchOccurrencesInText(text, value, searchStart, searchEnd); + int length = value.Length; + + if (searchEnd >= searchStart && length > 0) + { + return SearchOccurrencesInText(text, value, searchStart, searchEnd); + } } } } @@ -115,7 +141,7 @@ public TextOccurences SearchOccurrences(string value, int searchStart, int searc #region Delayed searching - public void SearchOccurrencesDelayed(string value, int searchStart, int searchEnd) + public void SearchOccurrencesDelayed(string value, int searchStart, int searchEnd) { _searchTimer.Stop(); @@ -124,8 +150,8 @@ public void SearchOccurrencesDelayed(string value, int searchStart, int searchEn lock (_delayedSearchSyncLock) { _delayedJob.Value = value; - _delayedJob.SearchStart = searchStart; - _delayedJob.SearchEnd = searchEnd; + _delayedJob.SearchStart = searchStart; + _delayedJob.SearchEnd = searchEnd; } _searchTimer.Start(); } @@ -135,23 +161,23 @@ private void SearchTimer_Elapsed(object sender, ElapsedEventArgs e) { string value; int searchStart; - int searchEnd; + int searchEnd; lock (_delayedSearchSyncLock) { value = _delayedJob.Value; searchStart = _delayedJob.SearchStart; - searchEnd = _delayedJob.SearchEnd; + searchEnd = _delayedJob.SearchEnd; } - SearchOccurrencesAsync(value, searchStart, searchEnd); + SearchOccurrencesAsync(value, searchStart, searchEnd); } #endregion #region Async searching - public void SearchOccurrencesAsync(string value, int searchStart, int searchEnd) + public void SearchOccurrencesAsync(string value, int searchStart, int searchEnd) { lock (_asyncJobsSyncRoot) { @@ -191,7 +217,7 @@ private void SearchThreadWorker(object stateInfo) EventHandler evt = SearchCompleted; if (evt != null) { - evt(this, new SearchCompletedEventArgs(occurences)); + evt(this, new SearchCompletedEventArgs(occurences)); } } }