/VsIntegration/Nemerle.VisualStudio/LanguageService/BracketFinder.cs

http://github.com/xxVisorxx/nemerle · C# · 347 lines · 248 code · 65 blank · 34 comment · 87 complexity · 359e9676c9baeb0fc027de725fbb5df4 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using Microsoft.VisualStudio.TextManager.Interop;
  6. using Nemerle.Completion2;
  7. using Nemerle.Compiler;
  8. using Microsoft.VisualStudio;
  9. namespace Nemerle.VisualStudio.LanguageService
  10. {
  11. /// <summary>
  12. /// Helper class which match paired token (brackets, brace, etc.)
  13. /// </summary>
  14. class BracketFinder
  15. {
  16. #region Fields
  17. public readonly ScanTokenInfo StartBraceInfo;
  18. private readonly IVsTextColorState ColorState;
  19. private readonly NemerleSource Source;
  20. private readonly int StartLine;
  21. private readonly ScanLexer Lex;
  22. private readonly NemerleScanner Scanner;
  23. #endregion
  24. public BracketFinder(NemerleSource source, int startLine,
  25. int startCol, NemerleScanner scanner, IVsTextColorState colorState)
  26. {
  27. #region Init fields
  28. Scanner = scanner;
  29. Source = source;
  30. StartLine = startLine;
  31. Lex = scanner.GetNewLexer();
  32. Lex.SetFileName(source.GetFilePath());
  33. ColorState = colorState;
  34. _lineCount = source.GetLineCount();
  35. var line = startLine - 1;
  36. _buffer = new string[1] { source.GetText(line, 0, line, source.GetLineLength(line)) };
  37. _startBufferLine = line;
  38. #endregion
  39. #region 2. Determine that it is a paired token. 3. Determine paired token.
  40. // Get tokens of line under text carret into dynamic array.
  41. List<ScanTokenInfo> lineToks = GetLineTokens(startLine, true);
  42. // Find index of token which located under text carret.
  43. int index = FindIndex(lineToks, x => x.Token.Location.Contains(startLine, startCol));
  44. if (index < 0)
  45. return;
  46. // If index is corret get corresponding token.
  47. ScanTokenInfo startBraceInfo = lineToks[index];
  48. // Remember it, if token have paired token.
  49. if (IsPairedToken(startBraceInfo.Token))
  50. StartBraceInfo = startBraceInfo;
  51. else
  52. {
  53. // otherwise try get right-hand token...
  54. startBraceInfo = RightHand(lineToks, index);
  55. // Remember it, if token have paired token.
  56. if (IsPairedToken(startBraceInfo.Token))
  57. StartBraceInfo = startBraceInfo;
  58. }
  59. #endregion
  60. }
  61. public ScanTokenInfo FindMatchBraceInfo()
  62. {
  63. if (StartBraceInfo == null)
  64. return null;
  65. // 4. Find paired token in the source file.
  66. Token tok = StartBraceInfo.Token;
  67. Predicate<Token> isStartBrace = GetMatchBracePredicate(tok);
  68. Predicate<Token> isEndBrace = GetMatchBracePredicate(GetPairedToken(tok));
  69. int nestedLevel = 1;
  70. foreach (ScanTokenInfo tokInfo in GetTokenIter())
  71. {
  72. if (isEndBrace(tokInfo.Token))
  73. nestedLevel--;
  74. else if (isStartBrace(tokInfo.Token))
  75. nestedLevel++;
  76. if (nestedLevel == 0)
  77. return tokInfo; // Match found!
  78. }
  79. return null; // Match not found.
  80. }
  81. #region GetTokenIter()
  82. /// <summary>
  83. /// Return iterator which allow iterate throw tokens of curent
  84. /// source file. Iteration process start from token behind current
  85. /// token and go forward or backward depending on type of start token.
  86. /// If start token is open token (for example, "(" or "{") iteration
  87. /// execute forward. If start token is close token (for example, ")"
  88. /// or "]") iteration execute backward.
  89. /// </summary>
  90. private IEnumerable<ScanTokenInfo> GetTokenIter()
  91. {
  92. bool isScanForward = IsOpenToken(StartBraceInfo.Token);
  93. int startLine = StartLine;
  94. // 1. Для первой строки находим токен и начиная от него сканируем вперед или назад.
  95. List<ScanTokenInfo> tokInfs = new List<ScanTokenInfo>(
  96. GetLineTokens(startLine, isScanForward));
  97. int tokIndex = FindIndex(tokInfs, delegate(ScanTokenInfo ti)
  98. { return ti.Token.Location == StartBraceInfo.Token.Location; });
  99. if (tokIndex < 0)
  100. throw new Exception("tokInfs.IndexOf(startBraceInfo)");
  101. // Scan first line from "start bracket" index.
  102. for (int i = tokIndex + 1; i < tokInfs.Count; i++)
  103. yield return tokInfs[i];
  104. if (isScanForward) // Scan next lines.
  105. for (int i = startLine + 1, count = _lineCount; i <= count; i++)
  106. foreach (ScanTokenInfo tokInf in GetLineTokens(i, true))
  107. yield return tokInf;
  108. else // Scan previous lines.
  109. for (int i = startLine - 1; i > 0; i--)
  110. foreach (ScanTokenInfo tokInf in GetLineTokens(i, false))
  111. yield return tokInf;
  112. }
  113. #endregion
  114. #region GetLineTokens()
  115. const int BufferLen = 100;
  116. string[] _buffer;
  117. int _startBufferLine;
  118. int _lineCount;
  119. /// <summary>
  120. /// Get tokens of specified line.
  121. /// </summary>
  122. /// <param name="line">Line number which tokens it is necessary retrieve</param>
  123. /// <param name="isForward">Direction of iteration (true is forward)</param>
  124. /// <returns>Token list</returns>
  125. List<ScanTokenInfo> GetLineTokens(int nline, bool isForward)
  126. {
  127. int line = nline - 1;
  128. ScanState scanState = GetLineState(nline);
  129. string lineText;
  130. int index = line - _startBufferLine;
  131. // читаем буферами по BufferLen стрк, чтобы ускорить работу
  132. if (index < 0 || index >= _buffer.Length)
  133. {
  134. int start, end;
  135. if (isForward)
  136. {
  137. end = Math.Min(_lineCount - 1, line + BufferLen);
  138. start = line;
  139. }
  140. else
  141. {
  142. start = Math.Max(0, line - BufferLen);
  143. end = line;
  144. }
  145. var str = Source.GetText(start, 0, end, Source.GetLineLength(end));
  146. _buffer = str.Split(new string[] { "\r\n", "\n", "\r" }, StringSplitOptions.None);
  147. _startBufferLine = start;
  148. index = line - _startBufferLine;
  149. }
  150. lineText = _buffer[index];
  151. Scanner.SetSource(lineText, 0);
  152. Lex.SetLine(nline, lineText, 0, null, null);
  153. List<ScanTokenInfo> lst = new List<ScanTokenInfo>();
  154. foreach (ScanTokenInfo var in GetLineTokens(Lex, scanState))
  155. lst.Add(var);
  156. if (!isForward)
  157. lst.Reverse();
  158. return lst;
  159. }
  160. /// <summary>Return colorer lexer state for specified line.</summary>
  161. private ScanState GetLineState(int line)
  162. {
  163. int state;
  164. ErrorHandler.ThrowOnFailure(
  165. ColorState.GetColorStateAtStartOfLine(line, out state));
  166. return (ScanState)state;
  167. }
  168. /// <summary>
  169. /// Implementation of<c>GetLineTokens</c>(). Don't use this method directly!
  170. /// </summary>
  171. IEnumerable<ScanTokenInfo> GetLineTokens(ScanLexer lex, ScanState scanState)
  172. {
  173. ScanTokenInfo info = lex.GetToken(scanState);
  174. scanState = info.State;
  175. while (!info.IsEndOfLine)
  176. {
  177. yield return info;
  178. info = lex.GetToken(scanState);
  179. scanState = info.State;
  180. }
  181. }
  182. #endregion
  183. #region Paired token identification functions
  184. public static bool IsOpenToken(Token token)
  185. {
  186. if (token is Token.BeginBrace) return true;
  187. if (token is Token.BeginQuote) return true;
  188. if (token is Token.BeginRound) return true;
  189. if (token is Token.BeginSquare) return true;
  190. if (token is Token.EndBrace) return false;
  191. if (token is Token.EndQuote) return false;
  192. if (token is Token.EndRound) return false;
  193. if (token is Token.EndSquare) return false;
  194. Token.Keyword kwd = token as Token.Keyword;
  195. if (kwd != null)
  196. {
  197. if (kwd.name == "if") return true;
  198. if (kwd.name == "else") return false;
  199. }
  200. throw new ArgumentException(string.Format("The token '{0}' not a brace!", token), "token");
  201. }
  202. private static bool IsPairedToken(Token token)
  203. {
  204. if (token is Token.BeginBrace || token is Token.BeginQuote
  205. || token is Token.BeginRound || token is Token.BeginSquare
  206. || token is Token.EndBrace || token is Token.EndQuote
  207. || token is Token.EndRound || token is Token.EndSquare
  208. )
  209. return true;
  210. var kwd = token as Token.Keyword;
  211. if (kwd != null && (kwd.name == "if" || kwd.name == "else"))
  212. return true;
  213. return false;
  214. }
  215. /// <summary>
  216. /// Return predicate function which check it argument is same token .
  217. /// </summary>
  218. private static Predicate<Token> GetMatchBracePredicate(Token token)
  219. {
  220. if (token is Token.BeginBrace) return t => t is Token.BeginBrace;
  221. if (token is Token.BeginQuote) return t => t is Token.BeginQuote;
  222. if (token is Token.BeginRound) return t => t is Token.BeginRound;
  223. if (token is Token.BeginSquare) return t => t is Token.BeginSquare;
  224. if (token is Token.EndBrace) return t => t is Token.EndBrace;
  225. if (token is Token.EndQuote) return t => t is Token.EndQuote;
  226. if (token is Token.EndRound) return t => t is Token.EndRound;
  227. if (token is Token.EndSquare) return t => t is Token.EndSquare;
  228. Token.Keyword kwd = token as Token.Keyword;
  229. if (kwd != null)
  230. {
  231. if (kwd.name == "if")
  232. return delegate(Token t)
  233. {
  234. Token.Keyword kwd1 = t as Token.Keyword;
  235. return kwd1 != null && kwd1.name == "if";
  236. };
  237. if (kwd.name == "else")
  238. return delegate(Token t)
  239. {
  240. Token.Keyword kwd1 = t as Token.Keyword;
  241. return kwd1 != null && kwd1.name == "else";
  242. };
  243. }
  244. return null;
  245. }
  246. private static Token GetPairedToken(Token token)
  247. {
  248. if (token is Token.BeginBrace) return new Token.EndBrace(true);
  249. if (token is Token.BeginQuote) return new Token.EndQuote();
  250. if (token is Token.BeginRound) return new Token.EndRound();
  251. if (token is Token.BeginSquare) return new Token.EndSquare();
  252. if (token is Token.EndBrace) return new Token.BeginBrace(true);
  253. if (token is Token.EndQuote) return new Token.BeginQuote();
  254. if (token is Token.EndRound) return new Token.BeginRound();
  255. if (token is Token.EndSquare) return new Token.BeginSquare();
  256. Token.Keyword kwd = token as Token.Keyword;
  257. if (kwd != null)
  258. {
  259. if (kwd.name == "if") return new Token.Keyword("else");
  260. if (kwd.name == "else") return new Token.Keyword("if");
  261. }
  262. return null;
  263. }
  264. #endregion
  265. #region Utils
  266. static T RightHand<T>(IList<T> lst, int index) where T : new()
  267. {
  268. int nextIndex = index + 1;
  269. if (nextIndex >= lst.Count)
  270. return new T();
  271. return lst[nextIndex];
  272. }
  273. static int FindIndex<T>(IList<T> lst, Predicate<T> predicate) where T : class
  274. {
  275. for (int i = 0; i < lst.Count; i++)
  276. if (predicate(lst[i]))
  277. return i;
  278. return -1;
  279. }
  280. #endregion
  281. }
  282. }