/// <summary> /// Searches the given file for the given search text. /// </summary> /// <param name="file">FileInfo object</param> /// <param name="searchSpec">ISearchSpec interface value</param> /// <param name="ex">Exception holder if error occurs</param> /// <returns>Hitobject containing grep results, null if on error</returns> /// <history> /// [Curtis_Beard] 07/28/2006 Created /// [Curtis_Beard] 05/25/2007 ADD: support for Exception object /// [Curtis_Beard] 03/31/2015 CHG: rework Grep/Matches /// </history> public MatchResult Grep(FileInfo file, ISearchSpec searchSpec, ref Exception ex) { // initialize Exception object to null ex = null; if (IsAvailable && IsUsable) { try { if (file.Exists) { //const int MARGINSIZE = 4; int count = 0; MatchResult match = null; int prevLine = 0; int prevPage = 0; //string _spacer = new string(' ', MARGINSIZE); //string _contextSpacer = string.Empty; //if (searchSpec.ContextLines > 0) //{ // _contextSpacer = new string(' ', MARGINSIZE); // _spacer = _contextSpacer.Substring(_contextSpacer.Length - MARGINSIZE - 2) + "> "; //} //else // _spacer = new string(' ', MARGINSIZE); // Open a given Word document as readonly // Note: Word 2003+ requires write mode since reading mode doesn't allow use of home/end keys to select text) object appversion = __WordApplication.GetType().InvokeMember("Version", BindingFlags.GetProperty, null, __WordApplication, null); double version = 0; double.TryParse(appversion.ToString(), out version); bool useReadOnly = version >= 12.00 ? false : true; object wordDocument = OpenDocument(file.FullName, useReadOnly); // Get Selection Property __WordSelection = __WordApplication.GetType().InvokeMember("Selection", BindingFlags.GetProperty, null, __WordApplication, null); // create range and find objects object range = GetProperty(wordDocument, "Content"); object find = GetProperty(range, "Find"); // setup find RunRoutine(find, "ClearFormatting", null); SetProperty(find, "Forward", true); SetProperty(find, "Text", searchSpec.SearchText); SetProperty(find, "MatchWholeWord", searchSpec.UseWholeWordMatching); SetProperty(find, "MatchCase", searchSpec.UseCaseSensitivity); // start find FindExecute(find); // keep finding text while ((bool)GetProperty(find, "Found") == true) { count += 1; if (count == 1) { // create hit object match = new MatchResult(file); } // since a hit was found and only displaying file names, quickly exit if (searchSpec.ReturnOnlyFileNames) break; // retrieve find information int start = (int)GetProperty(range, "Start"); int colNum = (int)Information(range, WdInformation.wdFirstCharacterColumnNumber); int lineNum = (int)Information(range, WdInformation.wdFirstCharacterLineNumber); int pageNum = (int)Information(range, WdInformation.wdActiveEndPageNumber); string line = GetFindTextLine(start); // don't add a hit if on same line if (!(prevLine == lineNum && prevPage == pageNum)) { // check for line numbers //if (searchSpec.IncludeLineNumbers) //{ // // setup line header // _spacer = "(" + string.Format("{0},{1}", lineNum, pageNum); // if (_spacer.Length <= 5) // { // _spacer = _spacer + new string(' ', 6 - _spacer.Length); // } // _spacer = _spacer + ") "; // //_contextSpacer = "(" + new string(' ', _spacer.Length - 3) + ") "; //} // remove any odd characters from the text line = RemoveSpecialCharacters(line); // add context lines before // if (__contextLines > 0){ // For i As int = __contextLines To 1 Step -1 // SetProperty(__WordSelection, "Start", start) // SelectionMoveUp(WdUnits.wdLine, i, WdMovementType.wdMove) // Dim cxt As string = GetFindTextLine() // cxt = RemoveSpecialCharacters(cxt) // if (Not HitExists(cxt, hit)){ // hit.Add(_contextSpacer & cxt & NEW_LINE, lineNum - i, 1) // End If // Next // End If // add line MatchResultLine matchLine = new MatchResultLine() { HasMatch = true, ColumnNumber = colNum, LineNumber = lineNum, Line = line }; var lineMatches = libAstroGrep.Grep.RetrieveLineMatches(line, searchSpec); match.SetHitCount(lineMatches.Count); matchLine.Matches = lineMatches; match.Matches.Add(matchLine); //match.Add(_spacer, line, lineNum, colNum); // add context lines after // if (__contextLines > 0){ // For i As int = 1 To __contextLines // SetProperty(__WordSelection, "Start", start) // SelectionMoveDown(WdUnits.wdLine, i, WdMovementType.wdMove) // Dim cxt As string = GetFindTextLine() // cxt = RemoveSpecialCharacters(cxt) // if (Not HitExists(cxt, hit)){ // hit.Add(_contextSpacer & cxt & NEW_LINE, lineNum + i, 1) // End If // Next // End If } //match.SetHitCount(); prevLine = lineNum; prevPage = pageNum; // find again FindExecute(find); } ReleaseSelection(); CloseDocument(wordDocument); return match; } else { string msg = string.Format("File does not exist: {0}", file.FullName); ex = new Exception(msg); Trace(msg); } } catch (Exception mainEx) { ex = mainEx; Trace(mainEx.ToString()); } } else { ex = new Exception("Plugin not available or usable."); Trace("Plugin not available or usable."); } return null; }
/// <summary> /// Search the given file. /// </summary> /// <param name="SourceFile">FileInfo object to be searched</param> private void SearchFile(FileInfo SourceFile) { try { // skip any files that are filtered out FilterItem filterItem = null; string filterValue = string.Empty; if (ShouldFilterOut(SourceFile, FileFilterSpec, out filterItem, out filterValue)) { OnFileFiltered(SourceFile, filterItem, filterValue); } else if (string.IsNullOrEmpty(SearchSpec.SearchText)) { // return a 'file hit' if the search text is empty var match = new MatchResult(SourceFile) { Index = MatchResults.Count }; var matchLine = new MatchResultLine(); match.Matches.Add(matchLine); MatchResults.Add(match); OnFileHit(SourceFile, match.Index); } else { SearchFileContents(SourceFile); } } catch (ThreadAbortException) { UnloadPlugins(); } catch (Exception ex) { OnSearchError(SourceFile, ex); } }
/// <summary> /// Search a given file for the searchText. /// </summary> /// <param name="file">FileInfo object for file to search for searchText</param> /// <history> /// [Curtis_Beard] 09/08/2005 Created /// [Curtis_Beard] 11/21/2005 ADD: update hit count when actual line added /// [Curtis_Beard] 12/02/2005 CHG: use SearchingFile instead of StatusMessage /// [Curtis_Beard] 04/21/2006 CHG: use a regular expression match collection to get /// correct count of hits in a line when using RegEx /// [Curtis_Beard] 07/03/2006 FIX: 1500174, use a FileStream to open the files readonly /// [Curtis_Beard] 07/07/2006 FIX: 1512029, RegEx use Case Sensitivity and WholeWords, /// also use different whole word matching regex /// [Curtis_Beard] 07/26/2006 ADD: 1512026, column position /// [Curtis_Beard] 07/26/2006 FIX: 1530023, retrieve file with correct encoding /// [Curtis_Beard] 09/12/2006 CHG: Converted to C# /// [Curtis_Beard] 09/28/2006 FIX: check for any plugins before looping through them /// [Curtis_Beard] 05/18/2006 FIX: 1723815, use correct whole word matching regex /// [Curtis_Beard] 06/26/2007 FIX: correctly detect plugin extension support /// [Curtis_Beard] 06/26/2007 FIX: 1779270, increase array size holding context lines /// [Curtis_Beard] 10/09/2012 FIX: don't overwrite position when getting context lines /// [Curtis_Beard] 10/12/2012 FIX: get correct position when using whole word option /// [Curtis_Beard] 10/12/2012 CHG: 32, implement a hit count filter /// [Curtis_Beard] 10/31/2012 CHG: renamed to SearchFileContents, remove parameter searchText /// [Curtis_Beard] 08/19/2014 FIX: 57, escape search text when whole word is enabled but not regular expressions /// [Curtis_Beard] 10/27/2014 CHG: 85, remove leading white space, remove use of newline so each line is in hit object /// [Curtis_Beard] 02/09/2015 CHG: 92, support for specific file encodings /// [Curtis_Beard] 03/05/2015 FIX: 64/35, if whole word doesn't pass our check but does pass regex, make it fail. Code cleanup. /// [Curtis_Beard] 04/02/2015 CHG: remove line number logic and always include line number in MatchResultLine. /// [Curtis_Beard] 05/18/2015 FIX: 72, don't grab file sample when detect encoding option is turned off. /// [Curtis_Beard] 05/18/2015 FIX: 69, use same stream to detect encoding and grep contents /// [Curtis_Beard] 05/26/2015 FIX: 69, add performance setting for file detection /// [Curtis_Beard] 06/02/2015 FIX: 75, use sample size from performance setting /// [theblackbunny] 06/25/2015 FIX: 39, remove context lines that intersect with each other in different MatchResults /// </history> private void SearchFileContents(FileInfo file) { // Raise SearchFile Event OnSearchingFile(file); FileStream _stream = null; StreamReader _reader = null; int _lineNumber = 0; MatchResult match = null; Regex _regularExp; MatchCollection _regularExpCol = null; bool _hitOccurred = false; bool _fileNameDisplayed = false; int _maxContextLines = 0; var _context = new string[11]; int _contextIndex = -1; int _lastHit = 0; int userFilterCount = 0; try { #region Plugin Processing if (Plugins != null) { for (int i = 0; i < Plugins.Count; i++) { // find a valid plugin for this file type if (Plugins[i].Enabled && Plugins[i].Plugin.IsAvailable) { // detect if plugin supports extension bool isFound = Plugins[i].Plugin.IsFileSupported(file); // if extension not supported try another plugin if (!isFound) continue; Exception pluginEx = null; // load plugin and perform grep if (Plugins[i].Plugin.Load()) { OnSearchingFileByPlugin(Plugins[i].Plugin.Name); match = Plugins[i].Plugin.Grep(file, SearchSpec, ref pluginEx); } else { OnSearchError(file, new Exception(string.Format("Plugin {0} failed to load.", Plugins[i].Plugin.Name))); } Plugins[i].Plugin.Unload(); // if the plugin processed successfully if (pluginEx == null) { // check for a hit if (match != null) { match.FromPlugin = true; // only perform is not using negation if (!SearchSpec.UseNegation) { if (DoesPassHitCountCheck(match)) { match.Index = MatchResults.Count; MatchResults.Add(match); OnFileHit(file, match.Index); if (SearchSpec.ReturnOnlyFileNames) match.SetHitCount(); OnLineHit(match, match.Index); } } } else if (SearchSpec.UseNegation) { // no hit but using negation so create one match = new MatchResult(file) { Index = MatchResults.Count, FromPlugin = true }; MatchResults.Add(match); OnFileHit(file, match.Index); } } else { // the plugin had an error OnSearchError(file, pluginEx); } return; } } } #endregion // open stream to file to use in encoding detection if enabled and in grep logic _stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite); #region Encoding Detection string usedEncoder = string.Empty; System.Text.Encoding encoding = null; // // User specified file encoding // FileEncoding fileEncoding = SearchSpec.FileEncodings != null && SearchSpec.FileEncodings.Count > 0 ? (from f in SearchSpec.FileEncodings where f.FilePath.Equals(file.FullName, StringComparison.InvariantCultureIgnoreCase) && f.Enabled select f).ToList().FirstOrDefault() : null; if (fileEncoding != null) { usedEncoder = "User"; encoding = fileEncoding.Encoding; } else { // // Detect file encoding if enabled // if (SearchSpec.EncodingDetectionOptions.DetectFileEncoding) { // encoding cache check var key = file.FullName; if (SearchSpec.EncodingDetectionOptions.UseEncodingCache && EncodingCache.Instance.ContainsKey(key)) { var value = EncodingCache.Instance.GetItem(key); usedEncoder = value.DetectorName; encoding = System.Text.Encoding.GetEncoding(value.CodePage); } else { byte[] sampleBytes; //Check if can read first try { int sampleSize = EncodingOptions.GetSampleSizeByPerformance(SearchSpec.EncodingDetectionOptions != null ? SearchSpec.EncodingDetectionOptions.PerformanceSetting : EncodingOptions.Performance.Default); sampleBytes = EncodingTools.ReadFileContentSample(_stream, sampleSize); } catch (Exception ex) { // can't read file for sample bytes OnSearchError(file, ex); return; } // detect encoding based on user set performance level that determines what detectors are used encoding = EncodingDetector.Detect(sampleBytes, out usedEncoder, EncodingOptions.GetEncodingDetectorOptionsByPerformance(SearchSpec.EncodingDetectionOptions != null ? SearchSpec.EncodingDetectionOptions.PerformanceSetting : EncodingOptions.Performance.Default), System.Text.Encoding.Default); // add to cache if enabled if (encoding != null && SearchSpec.EncodingDetectionOptions.UseEncodingCache) { var value = new EncodingCacheItem() { CodePage = encoding.CodePage, DetectorName = usedEncoder }; EncodingCache.Instance.SetItem(key, value); } } } else { // Use original encoding method before detect encoding option availalbe usedEncoder = "Default"; encoding = System.Text.Encoding.Default; } } if (encoding == null) { // Could not detect file encoding OnSearchError(file, new Exception("Could not detect file encoding.")); return; } OnFileEncodingDetected(file, encoding, usedEncoder); // process all encoding detectors and display results to output window //var values = EncodingDetector.DetectAll(sampleBytes); //if (values.Count > 0) //{ // System.Diagnostics.Debug.WriteLine(string.Format("File: {0}", file.FullName)); // foreach (var value in values) // { // System.Diagnostics.Debug.WriteLine(string.Format("Encoding: {0} ({1})", value.Encoding != null ? value.Encoding.EncodingName : "None", value.Option.ToString())); // } // System.Diagnostics.Debug.WriteLine(Environment.NewLine); //} #endregion // could have read some data for the encoding check, seek back to start of file if (_stream.CanSeek) { _stream.Seek(0, SeekOrigin.Begin); } _reader = new StreamReader(_stream, encoding); _maxContextLines = SearchSpec.ContextLines + 1; do { string textLine = _reader.ReadLine(); if (textLine == null) break; else { _lineNumber += 1; int _posInStr = -1; if (SearchSpec.UseRegularExpressions) { if (textLine.Length > 0) { string pattern = string.Format("{0}{1}{0}", SearchSpec.UseWholeWordMatching ? "\\b" : string.Empty, SearchSpec.SearchText); RegexOptions options = SearchSpec.UseCaseSensitivity ? RegexOptions.None : RegexOptions.IgnoreCase; _regularExp = new Regex(pattern, options); _regularExpCol = _regularExp.Matches(textLine); if (_regularExpCol.Count > 0) { if (SearchSpec.UseNegation) { _hitOccurred = true; } _posInStr = 1; } } } else { // If we are looking for whole worlds only, perform the check. if (SearchSpec.UseWholeWordMatching) { _regularExp = new Regex("\\b" + Regex.Escape(SearchSpec.SearchText) + "\\b", SearchSpec.UseCaseSensitivity ? RegexOptions.None : RegexOptions.IgnoreCase); // if match is found, also check against our internal line hit count method to be sure they are in sync Match mtc = _regularExp.Match(textLine); if (mtc != null && mtc.Success && RetrieveLineMatches(textLine, SearchSpec).Count > 0) { if (SearchSpec.UseNegation) { _hitOccurred = true; } _posInStr = mtc.Index; } } else { _posInStr = textLine.IndexOf(SearchSpec.SearchText, SearchSpec.UseCaseSensitivity ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); if (SearchSpec.UseNegation && _posInStr > -1) { _hitOccurred = true; } } } //******************************************* // We found an occurrence of our search text. //******************************************* if (_posInStr > -1) { //since we have a hit, check to see if negation is checked if (SearchSpec.UseNegation) break; // create new hit and add to collection if (match == null) { match = new MatchResult(file) { Index = MatchResults.Count, DetectedEncoding = encoding }; MatchResults.Add(match); } // don't show until passes count check if (!_fileNameDisplayed && DoesPassHitCountCheck(match)) { OnFileHit(file, match.Index); _fileNameDisplayed = true; } // If we are only showing filenames, go to the next file. if (SearchSpec.ReturnOnlyFileNames) { if (!_fileNameDisplayed) { OnFileHit(file, match.Index); _fileNameDisplayed = true; } //notify that at least 1 hit is in file match.SetHitCount(); OnLineHit(match, match.Index); break; } // Display context lines if applicable. if (SearchSpec.ContextLines > 0 && _lastHit <= 0) { if (match.Matches.Count > 0 && _lastHit < -_maxContextLines) { // Insert a blank space before the context lines. var matchLine = new MatchResultLine() { Line = string.Empty, LineNumber = -1 }; match.Matches.Add(matchLine); int _pos = match.Matches.Count - 1; if (DoesPassHitCountCheck(match)) { OnLineHit(match, _pos); } } // Display preceding n context lines before the hit. int tempContextLines = SearchSpec.ContextLines; // But only output the context lines which are not part of the previous context if(_lastHit >= -_maxContextLines) { tempContextLines = -_lastHit; } // Roll back the context index to get the first context line that needs to be displayed _contextIndex = _contextIndex - tempContextLines; if(_contextIndex < 0) { _contextIndex += _maxContextLines; } for (int tempPosInStr = tempContextLines; tempPosInStr >= 1; tempPosInStr--) { _contextIndex = _contextIndex + 1; if (_contextIndex >= _maxContextLines) _contextIndex = 0; // If there is a match in the first one or two lines, // the entire preceeding context may not be available. if (_lineNumber > tempPosInStr) { // Add the context line. var matchLine = new MatchResultLine() { Line = _context[_contextIndex], LineNumber = _lineNumber - tempPosInStr }; match.Matches.Add(matchLine); int _pos = match.Matches.Count - 1; if (DoesPassHitCountCheck(match)) { OnLineHit(match, _pos); } } } } _lastHit = SearchSpec.ContextLines; // // Add the actual "hit". // var matchLineFound = new MatchResultLine() { Line = textLine, LineNumber = _lineNumber, HasMatch = true }; if (SearchSpec.UseRegularExpressions) { _posInStr = _regularExpCol[0].Index; match.SetHitCount(_regularExpCol.Count); foreach (Match regExMatch in _regularExpCol) { matchLineFound.Matches.Add(new MatchResultLineMatch(regExMatch.Index, regExMatch.Length)); } } else { var lineMatches = RetrieveLineMatches(textLine, SearchSpec); match.SetHitCount(lineMatches.Count); matchLineFound.Matches = lineMatches; } matchLineFound.ColumnNumber = _posInStr + 1; match.Matches.Add(matchLineFound); int _index = match.Matches.Count - 1; if (DoesPassHitCountCheck(match)) { OnLineHit(match, _index); } } else if (SearchSpec.ContextLines > 0) { if(_lastHit > 0) { //*************************************************** // We didn't find a hit, but since lastHit is > 0, we // need to display this context line. //*************************************************** var matchLine = new MatchResultLine() { Line = textLine, LineNumber = _lineNumber }; match.Matches.Add(matchLine); int _index = match.Matches.Count - 1; if (DoesPassHitCountCheck(match)) { OnLineHit(match, _index); } } if(_lastHit >= -_maxContextLines) { //***************************************************** // We continue keeping track of the number of potential // context lines since the last displayed context line // until we pass (-_maxContextLines). //***************************************************** _lastHit -= 1; } } // Found a hit or not. // If we are showing context lines, keep the last n+1 lines. if (SearchSpec.ContextLines > 0) { _contextIndex += 1; if (_contextIndex >= _maxContextLines) _contextIndex = 0; _context[_contextIndex] = textLine; } } } while (true); // send event file/line hit if we haven't yet but it should be if (!_fileNameDisplayed && match != null && DoesPassHitCountCheck(match)) { // need to display it OnFileHit(file, match.Index); OnLineHit(match, match.Index); } // send event for file filtered if it fails the file hit count filter if (!SearchSpec.UseNegation && !SearchSpec.ReturnOnlyFileNames && match != null && !DoesPassHitCountCheck(match)) { // remove from grep collection only if // not negation // not filenames only // actually have a hit // doesn't pass the hit count filter MatchResults.RemoveAt(MatchResults.Count - 1); string filterValue = match.HitCount.ToString(); FilterItem filterItem = new FilterItem(new FilterType(FilterType.Categories.File, FilterType.SubCategories.MinimumHitCount), userFilterCount.ToString(), FilterType.ValueOptions.None, false, true); OnFileFiltered(file, filterItem, filterValue); } // // Check for no hits through out the file // if (SearchSpec.UseNegation && _hitOccurred == false) { //add the file to the hit list if (!_fileNameDisplayed) { match = new MatchResult(file) { Index = MatchResults.Count, DetectedEncoding = encoding }; MatchResults.Add(match); OnFileHit(file, match.Index); } } } finally { if (_reader != null) _reader.Close(); if (_stream != null) _stream.Close(); } }
/// <summary> /// Raise line hit event. /// </summary> /// <param name="match">MatchResult containing line hit</param> /// <param name="index">Index to line</param> /// <history> /// [Curtis_Beard] 05/25/2007 Created /// </history> protected virtual void OnLineHit(MatchResult match, int index) { if (LineHit != null) { LineHit(match, index); } }
/// <summary> /// Determines if current match count passes the file hit count filter. /// </summary> /// <param name="hit">Current MatchResult</param> /// <returns>true if match count valid, false if not</returns> /// <history> /// [Curtis_Beard] 10/12/2012 Created: 32, implement file hit count /// </history> private bool DoesPassHitCountCheck(MatchResult match) { if (userFilterCount <= 0) return true; if (userFilterCount > 0 && (match != null && match.HitCount >= userFilterCount)) return true; return false; }
/// <summary> /// A line has been detected to contain the searching text /// </summary> /// <param name="match">The MatchResult that contains the line</param> /// <param name="index">The position in the HitObject's line collection</param> /// <history> /// [Curtis_Beard] 11/04/2005 Created /// </history> private void ReceiveLineHit(MatchResult match, int index) { UpdateHitCount(match); CalculateTotalCount(); }
/// <summary> /// Updates the count column (Thread safe) /// </summary> /// <param name="match">MatchResult that contains updated information</param> /// <history> /// [Curtis_Beard] 11/21/2005 Created /// </history> private void UpdateHitCount(MatchResult match) { // Makes this a thread safe operation if (lstFileNames.InvokeRequired) { UpdateHitCountCallBack del = new UpdateHitCountCallBack(UpdateHitCount); lstFileNames.Invoke(del, new object[1] { match }); return; } // find correct item to update foreach (ListViewItem item in lstFileNames.Items) { if (int.Parse(item.SubItems[Constants.COLUMN_INDEX_GREP_INDEX].Text) == match.Index) { item.SubItems[Constants.COLUMN_INDEX_COUNT].Text = match.HitCount.ToString(); break; } } }
/// <summary> /// Highlight the searched text in the results /// </summary> /// <param name="match">MatchResult containing results</param> /// <history> /// [Curtis_Beard] 01/27/2005 Created /// [Curtis_Beard] 04/12/2005 FIX: 1180741, Don't capitalize hit line /// [Curtis_Beard] 11/18/2005 ADD: custom highlight colors /// [Curtis_Beard] 12/06/2005 CHG: call WholeWordOnly from Grep class /// [Curtis_Beard] 04/21/2006 CHG: highlight regular expression searches /// [Curtis_Beard] 09/28/2006 FIX: use grep object for settings instead of gui items /// [Ed_Jakubowski] 05/20/2009 CHG: Skip highlight if hitCount = 0 /// [Curtis_Beard] 01/24/2012 CHG: allow back color use again since using .Net v2+ /// [Curtis_Beard] 10/27/2014 CHG: 85, remove leading white space, add newline for display, fix windows sounds for empty text /// [Curtis_Beard] 11/26/2014 FIX: don't highlight found text that is part of the spacer text /// [Curtis_Beard] 02/24/2015 CHG: remove hit line restriction until proper fix for long loading /// [Curtis_Beard] 03/04/2015 CHG: move standard code to function to cleanup this function. /// [Curtis_Beard] 03/05/2015 FIX: 64/35, clear text field before anything to not have left over content. /// [Curtis_Beard] 04/08/2015 CHG: 61, change from RichTextBox to AvalonEdit /// [Curtis_Beard] 07/06/2015 FIX: 78, display empty preview area when result is null or no matches /// </history> private void ProcessMatchForDisplay(MatchResult match) { if (match == null || match.Matches.Count == 0) { txtHits.Clear(); txtHits.SyntaxHighlighting = null; txtHits.LineNumbers = null; return; } txtHits.Clear(); txtHits.SyntaxHighlighting = null; txtHits.ScrollToHome(); for (int i = txtHits.TextArea.TextView.LineTransformers.Count - 1; i >= 0; i--) { if (txtHits.TextArea.TextView.LineTransformers[i] is ResultHighlighter) txtHits.TextArea.TextView.LineTransformers.RemoveAt(i); else if (txtHits.TextArea.TextView.LineTransformers[i] is AllResultHighlighter) txtHits.TextArea.TextView.LineTransformers.RemoveAt(i); } var foreground = Convertors.ConvertStringToSolidColorBrush(Core.GeneralSettings.HighlightForeColor); var background = Convertors.ConvertStringToSolidColorBrush(Core.GeneralSettings.HighlightBackColor); var nonForeground = Convertors.ConvertStringToSolidColorBrush(Core.GeneralSettings.ResultsContextForeColor); txtHits.TextArea.TextView.LineTransformers.Add(new ResultHighlighter(match, RemoveWhiteSpaceMenuItem.Checked) { MatchForeground = foreground, MatchBackground = background, NonMatchForeground = nonForeground }); StringBuilder builder = new StringBuilder(); var lineNumbers = new List<LineNumber>(); var path = match.File.FullName; int max = match.Matches.Count; for (int i = 0; i < max; i++) { string line = match.Matches[i].Line; if (RemoveWhiteSpaceMenuItem.Checked) { line = line.TrimStart(); } builder.AppendLine(line); lineNumbers.Add(new LineNumber() { Number = match.Matches[i].LineNumber, HasMatch = match.Matches[i].HasMatch, FileFullName = match.Matches[i].LineNumber > -1 || match.FromPlugin ? path : string.Empty, ColumnNumber = match.Matches[i].ColumnNumber }); } // the last result will have a hanging newline, so remove it. if (builder.Length > 0) { builder.Remove(builder.Length - Environment.NewLine.Length, Environment.NewLine.Length); } txtHits.LineNumbers = lineNumbers; txtHits.Text = builder.ToString(); }
/// <summary> /// Displays the entire selected file and uses a syntax highlighter when available for that file extension. /// </summary> /// <param name="match">Currently selected MatchResult</param> /// <history> /// [Curtis_Beard] 04/08/2015 CHG: 20/21, display entire file and use syntax highlighter /// [Curtis_Beard] 05/18/2015 FIX: 71, use language text for message. /// </history> private void ProcessFileForDisplay(MatchResult match) { if (match == null || match.Matches.Count == 0) { txtHits.Clear(); txtHits.LineNumbers = null; return; } if ((match.File.Length > 1024000 || FilterItem.IsBinaryFile(match.File)) && MessageBox.Show(this, Language.GetGenericText("ResultsPreviewLargeBinaryFile"), ProductInformation.ApplicationName, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == System.Windows.Forms.DialogResult.No) { ProcessMatchForDisplay(match); // display just the results then and not the whole file. return; } txtHits.Clear(); txtHits.LineNumbers = null; txtHits.ScrollToHome(); var def = ICSharpCode.AvalonEdit.Highlighting.HighlightingManager.Instance.GetDefinitionByExtension(match.File.Extension); txtHits.SyntaxHighlighting = def; for (int i = txtHits.TextArea.TextView.LineTransformers.Count - 1; i >= 0; i--) { if (txtHits.TextArea.TextView.LineTransformers[i] is ResultHighlighter) txtHits.TextArea.TextView.LineTransformers.RemoveAt(i); else if (txtHits.TextArea.TextView.LineTransformers[i] is AllResultHighlighter) txtHits.TextArea.TextView.LineTransformers.RemoveAt(i); } var foreground = Convertors.ConvertStringToSolidColorBrush(Core.GeneralSettings.HighlightForeColor); var background = Convertors.ConvertStringToSolidColorBrush(Core.GeneralSettings.HighlightBackColor); var nonForeground = Convertors.ConvertStringToSolidColorBrush(Core.GeneralSettings.ResultsContextForeColor); txtHits.TextArea.TextView.LineTransformers.Add(new ResultHighlighter(match, false, true) { MatchForeground = foreground, MatchBackground = background, NonMatchForeground = nonForeground }); // convert MatchResultLine to LineNumber List<LineNumber> lineNumbers = new List<LineNumber>(); foreach (MatchResultLine matchLine in match.Matches) { lineNumbers.Add(new LineNumber() { ColumnNumber = matchLine.ColumnNumber, Number = matchLine.LineNumber, HasMatch = matchLine.HasMatch, FileFullName = match.File.FullName }); } txtHits.LineNumbers = lineNumbers; txtHits.Encoding = match.DetectedEncoding; txtHits.Load(match.File.FullName); }
/// <summary> /// Creates an instance of this class. /// </summary> /// <param name="match">The current MatchResult</param> /// <param name="removeWhiteSpace">Determines if leading white space was removed</param> /// <param name="showingFullFile">Determines if showing full file contents or just matches</param> /// <history> /// [Curtis_Beard] 04/08/2015 ADD: switch from Rich Text Box to AvalonEdit /// </history> public ResultHighlighter(MatchResult match, bool removeWhiteSpace, bool showingFullFile) : this(match, removeWhiteSpace) { this.showingFullFile = showingFullFile; }
/// <summary> /// Creates an instance of this class. /// </summary> /// <param name="match">The current MatchResult</param> /// <param name="removeWhiteSpace">Determines if leading white space was removed</param> /// <history> /// [Curtis_Beard] 04/08/2015 ADD: switch from Rich Text Box to AvalonEdit /// </history> public ResultHighlighter(MatchResult match, bool removeWhiteSpace) { this.match = match; this.removeWhiteSpace = removeWhiteSpace; }
/// <summary> /// Searches the given file for the given search text. /// </summary> /// <param name="file">FileInfo object</param> /// <param name="searchSpec">ISearchSpec interface value</param> /// <param name="ex">Exception holder if error occurs</param> /// <returns>Hitobject containing grep results, null if on error</returns> /// <history> /// [Curtis_Beard] 10/17/2012 Created /// [Curtis_Beard] 03/31/2015 CHG: rework Grep/Matches /// </history> public MatchResult Grep(FileInfo file, ISearchSpec searchSpec, ref Exception ex) { // initialize Exception object to null ex = null; MatchResult match = null; if (Parser.IsParseable(file.FullName)) { string fileContent = Parser.Parse(file.FullName); if (!string.IsNullOrEmpty(fileContent)) { string[] lines = fileContent.Split(new char[] { '\n', '\r' }); for (int i = 0; i < lines.Length; i++) { string line = lines[i]; int posInStr = -1; Regex reg = null; MatchCollection regCol = null; if (searchSpec.UseRegularExpressions) { string pattern = string.Format("{0}{1}{0}", searchSpec.UseWholeWordMatching ? "\\b" : string.Empty, searchSpec.SearchText); RegexOptions options = searchSpec.UseCaseSensitivity ? RegexOptions.None : RegexOptions.IgnoreCase; reg = new Regex(pattern, options); regCol = reg.Matches(line); if (regCol.Count > 0) { posInStr = 1; } } else { // If we are looking for whole worlds only, perform the check. if (searchSpec.UseWholeWordMatching) { reg = new Regex("\\b" + Regex.Escape(searchSpec.SearchText) + "\\b", searchSpec.UseCaseSensitivity ? RegexOptions.None : RegexOptions.IgnoreCase); // if match is found, also check against our internal line hit count method to be sure they are in sync Match mtc = reg.Match(line); if (mtc != null && mtc.Success && libAstroGrep.Grep.RetrieveLineMatches(line, searchSpec).Count > 0) { posInStr = mtc.Index; } } else { posInStr = line.IndexOf(searchSpec.SearchText, searchSpec.UseCaseSensitivity ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); } } if (posInStr > -1) { if (match == null) { match = new MatchResult(file); // found hit in file so just return if (searchSpec.ReturnOnlyFileNames) { break; } } var matchLineFound = new MatchResultLine() { Line = line, LineNumber = -1, HasMatch = true }; if (searchSpec.UseRegularExpressions) { posInStr = regCol[0].Index; match.SetHitCount(regCol.Count); foreach (Match regExMatch in regCol) { matchLineFound.Matches.Add(new MatchResultLineMatch(regExMatch.Index, regExMatch.Length)); } } else { var lineMatches = libAstroGrep.Grep.RetrieveLineMatches(line, searchSpec); match.SetHitCount(lineMatches.Count); matchLineFound.Matches = lineMatches; } matchLineFound.ColumnNumber = 1; match.Matches.Add(matchLineFound); } } } } return match; }