123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- using System;
- using System.Collections.Generic;
- using System.Text.RegularExpressions;
-
-
- namespace Unity.Burst.Editor
- {
- internal struct SearchCriteria
- {
- internal string filter;
- internal bool isCaseSensitive;
- internal bool isWholeWords;
- internal bool isRegex;
-
- internal SearchCriteria(string keyword, bool caseSensitive, bool wholeWord, bool regex)
- {
- filter = keyword;
- isCaseSensitive = caseSensitive;
- isWholeWords = wholeWord;
- isRegex = regex;
- }
-
- internal bool Equals(SearchCriteria obj) =>
- filter == obj.filter && isCaseSensitive == obj.isCaseSensitive && isWholeWords == obj.isWholeWords && isRegex == obj.isRegex;
-
- public override bool Equals(object obj) =>
- obj is SearchCriteria other && Equals(other);
-
- public override int GetHashCode() => base.GetHashCode();
- }
-
- internal static class BurstStringSearch
- {
- /// <summary>
- /// Gets index of line end in given string, both absolute and relative to start of line.
- /// </summary>
- /// <param name="str">String to search in.</param>
- /// <param name="line">Line to get end index of.</param>
- /// <returns>(absolute line end index of string, line end index relative to line start).</returns>
- /// <exception cref="ArgumentOutOfRangeException">
- /// Argument must be greater than 0 and less than or equal to number of lines in
- /// <paramref name="str" />.
- /// </exception>
- internal static (int total, int relative) GetEndIndexOfPlainLine (string str, int line)
- {
- var lastIdx = -1;
- var newIdx = -1;
-
- for (var i = 0; i <= line; i++)
- {
- lastIdx = newIdx;
- newIdx = str.IndexOf('\n', lastIdx + 1);
-
- if (newIdx == -1 && i < line)
- {
- throw new ArgumentOutOfRangeException(nameof(line),
- "Argument must be greater than 0 and less than or equal to number of lines in str.");
- }
- }
- lastIdx++;
- return newIdx != -1 ? (newIdx, newIdx - lastIdx) : (str.Length - 1, str.Length - 1 - lastIdx);
- }
-
- /// <summary>
- /// Gets index of line end in given string, both absolute and relative to start of line.
- /// Adjusts the index so color tags are not included in relative index.
- /// </summary>
- /// <param name="str">String to search in.</param>
- /// <param name="line">Line to find end of in string.</param>
- /// <returns>(absolute line end index of string, line end index relative to line start adjusted for color tags).</returns>
- internal static (int total, int relative) GetEndIndexOfColoredLine(string str, int line)
- {
- var (total, relative) = GetEndIndexOfPlainLine(str, line);
- return RemoveColorTagFromIdx(str, total, relative);
- }
-
- /// <summary>
- /// Adjusts index of color tags on line.
- /// </summary>
- /// <remarks>Assumes that <see cref="tidx"/> is index of something not a color tag.</remarks>
- /// <param name="str">String containing the indexes.</param>
- /// <param name="tidx">Total index of line end.</param>
- /// <param name="ridx">Relative index of line end.</param>
- /// <returns>(<see cref="tidx"/>, <see cref="ridx"/>) adjusted for color tags on line.</returns>
- private static (int total, int relative) RemoveColorTagFromIdx(string str, int tidx, int ridx)
- {
- var lineStartIdx = tidx - ridx;
- var colorTagFiller = 0;
-
- var tmp = str.LastIndexOf("</color", tidx);
- var lastWasStart = true;
- var colorTagStart = str.LastIndexOf("<color=", tidx);
-
- if (tmp > colorTagStart)
- {
- // color tag end was closest
- lastWasStart = false;
- colorTagStart = tmp;
- }
-
- while (colorTagStart != -1 && colorTagStart >= lineStartIdx)
- {
- var colorTagEnd = str.IndexOf('>', colorTagStart);
- // +1 as the index is zero based.
- colorTagFiller += colorTagEnd - colorTagStart + 1;
-
- if (lastWasStart)
- {
- colorTagStart = str.LastIndexOf("</color", colorTagStart);
- lastWasStart = false;
- }
- else
- {
- colorTagStart = str.LastIndexOf("<color=", colorTagStart);
- lastWasStart = true;
- }
- }
- return (tidx - colorTagFiller, ridx - colorTagFiller);
- }
-
- /// <summary>
- /// Finds the zero indexed line number of given <see cref="matchIdx"/>.
- /// </summary>
- /// <param name="str">String to search in.</param>
- /// <param name="matchIdx">Index to find line number of.</param>
- /// <returns>Line number of given index in string.</returns>
- internal static int FindLineNr(string str, int matchIdx)
- {
- var lineNr = 0;
- var idxn = str.IndexOf('\n');
-
- while (idxn != -1 && idxn < matchIdx)
- {
- lineNr++;
- idxn = str.IndexOf('\n', idxn + 1);
- }
-
- return lineNr;
- }
-
- /// <summary>
- /// Finds first match of <see cref="criteria"/> in given string.
- /// </summary>
- /// <param name="str">String to search in.</param>
- /// <param name="criteria">Search options.</param>
- /// <param name="regx">Used when <see cref="criteria"/> specifies regex search.</param>
- /// <param name="startIdx">Index to start the search at.</param>
- /// <returns>(start index of match, length of match)</returns>
- internal static (int idx, int length) FindMatch(string str, SearchCriteria criteria, Regex regx, int startIdx = 0)
- {
- var idx = -1;
- var len = 0;
-
- if (criteria.isRegex)
- {
- // regex will have the appropriate options in it if isCaseSensitive or/and isWholeWords is true.
- var res = regx.Match(str, startIdx);
-
- if (res.Success) (idx, len) = (res.Index, res.Length);
- }
- else if (criteria.isWholeWords)
- {
- (idx, len) = (IndexOfWholeWord(str, startIdx, criteria.filter, criteria.isCaseSensitive
- ? StringComparison.InvariantCulture
- : StringComparison.InvariantCultureIgnoreCase), criteria.filter.Length);
- }
- else
- {
- unsafe
- {
- fixed (char* source = str)
- {
- fixed (char* target = criteria.filter)
- {
- (idx, len) = (
- IndexOfCustom(source, str.Length, target, criteria.filter.Length, startIdx, criteria.isCaseSensitive)
- , criteria.filter.Length);
- }
- }
- }
- }
-
- return (idx, len);
- }
-
- internal static List<(int idx, int length)> FindAllMatches(string str, SearchCriteria criteria, Regex regx,
- int startIdx = 0)
- {
- var retVal = new List<(int, int)>();
-
- if (criteria.isRegex)
- {
- var res = regx.Matches(str, startIdx);
-
- foreach (Match match in res)
- {
- retVal.Add((match.Index, match.Length));
- }
- }
- else if (criteria.isWholeWords)
- {
- retVal.AddRange(IndexOfWholeWordAll(str, startIdx, criteria.filter,
- criteria.isCaseSensitive
- ? StringComparison.InvariantCulture
- : StringComparison.CurrentCultureIgnoreCase));
- }
- else
- {
- unsafe
- {
- fixed (char* source = str)
- {
- fixed (char* target = criteria.filter)
- {
- retVal.AddRange(FindAllIndices(source, str.Length, target, criteria.filter.Length, startIdx, criteria.isCaseSensitive));
- }
- }
- }
- }
-
- return retVal;
- }
-
- private static char ToUpper(char c) => c - 97U > 25U ? c : (char)(c - 32U);
-
- private static unsafe int ScanForFilterInsensitive(char* str, char* filter, int flen, int i)
- {
- int j = 0;
- while (j < flen && ToUpper(str[i + j]) == ToUpper(filter[j]))
- {
- j++;
- }
- return j;
- }
-
- private static unsafe int ScanForFilter(char* str, char* filter, int flen, int i)
- {
- int j = 0;
- while (j < flen && str[i + j] == filter[j])
- {
- j++;
- }
- return j;
- }
-
- private static unsafe List<(int, int)> FindAllIndices(char* str, int len, char* filter, int flen, int startIdx, bool caseSensitive)
- {
- var retVal = new List<(int,int)>();
- if (len < flen) { return retVal; }
-
- int stop = len - flen;
- if (caseSensitive)
- {
- for (int i = startIdx; i < stop; i++)
- {
- if (ScanForFilter(str, filter, flen, i) == flen)
- {
- retVal.Add((i, flen));
- i += flen - 1;
- }
- }
- }
- else
- {
- for (int i = startIdx; i < stop; i++)
- {
- if (ScanForFilterInsensitive(str, filter, flen, i) == flen)
- {
- retVal.Add((i, flen));
- i += flen-1;
- }
- }
- }
- return retVal;
- }
-
-
- /// <summary>
- /// Finds index of first occurence of <see cref="filter"/> in <see cref="str"/>.
- /// </summary>
- /// <param name="str">String to search through</param>
- /// <param name="len">Length of <see cref="str"/></param>
- /// <param name="filter">Needle to find</param>
- /// <param name="flen">Lenght of <see cref="filter"/></param>
- /// <param name="startIdx">Index to start search from</param>
- /// <param name="caseSensitive">Whether search ignore casing</param>
- /// <returns>index of first match or -1</returns>
- private static unsafe int IndexOfCustom(char* str, int len, char* filter, int flen, int startIdx, bool caseSensitive)
- {
- if (len < flen) { return -1; }
-
- int stop = len - flen;
- if (caseSensitive)
- {
- for (int i = startIdx; i < stop; i++)
- {
- if (ScanForFilter(str, filter, flen, i) == flen)
- {
- return i;
- }
- }
- }
- else
- {
- for (int i = startIdx; i < stop; i++)
- {
- if (ScanForFilterInsensitive(str, filter, flen, i) == flen)
- {
- return i;
- }
- }
- }
-
- return -1;
- }
-
- /// <summary>
- /// Finds index of <see cref="filter"/> matching for whole words.
- /// </summary>
- /// <param name="str">String to search in.</param>
- /// <param name="startIdx">Index to start search from.</param>
- /// <param name="filter">Key to search for.</param>
- /// <param name="opt">Options for string comparison.</param>
- /// <returns>Index of match or -1.</returns>
- private static int IndexOfWholeWord(string str, int startIdx, string filter, StringComparison opt)
- {
- const string wholeWordMatch = @"\w";
-
- var j = startIdx;
- var filterLen = filter.Length;
- var strLen = str.Length;
- while (j < strLen && (j = str.IndexOf(filter, j, opt)) >= 0)
- {
- var noPrior = true;
- if (j != 0)
- {
- var frontBorder = str[j - 1];
- noPrior = !Regex.IsMatch(frontBorder.ToString(), wholeWordMatch);
- }
-
- var noAfter = true;
- if (j + filterLen != strLen)
- {
- var endBorder = str[j + filterLen];
- noAfter = !Regex.IsMatch(endBorder.ToString(), wholeWordMatch);
- }
-
- if (noPrior && noAfter) return j;
-
- j++;
- }
- return -1;
- }
-
-
- private static List<(int idx, int len)> IndexOfWholeWordAll(string str, int startIdx, string filter, StringComparison opt)
- {
- const string wholeWordMatch = @"\w";
- var retVal = new List<(int, int)>();
-
- var j = startIdx;
- var filterLen = filter.Length;
- var strLen = str.Length;
- while (j < strLen && (j = str.IndexOf(filter, j, opt)) >= 0)
- {
- var noPrior = true;
- if (j != 0)
- {
- var frontBorder = str[j - 1];
- noPrior = !Regex.IsMatch(frontBorder.ToString(), wholeWordMatch);
- }
-
- var noAfter = true;
- if (j + filterLen != strLen)
- {
- var endBorder = str[j + filterLen];
- noAfter = !Regex.IsMatch(endBorder.ToString(), wholeWordMatch);
- }
-
- if (noPrior && noAfter)
- {
- retVal.Add((j, filterLen));
- j += filterLen - 1;
- }
-
- j++;
- }
- return retVal;
- }
- }
- }
|