PageRenderTime 58ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs

http://github.com/icsharpcode/NRefactory
C# | 907 lines | 818 code | 50 blank | 39 comment | 255 complexity | 6031f604aee0d4719d09c66d44621503 MD5 | raw file
  1. //
  2. // CSharpCompletionEngineBase.cs
  3. //
  4. // Author:
  5. // Mike Krüger <mkrueger@xamarin.com>
  6. //
  7. // Copyright (c) 2011 Xamarin Inc. (http://xamarin.com)
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in
  17. // all copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. // THE SOFTWARE.
  26. using System;
  27. using System.Collections.Generic;
  28. using System.Linq;
  29. using System.Text;
  30. using ICSharpCode.NRefactory.CSharp.Resolver;
  31. using ICSharpCode.NRefactory.Editor;
  32. using ICSharpCode.NRefactory.TypeSystem;
  33. using ICSharpCode.NRefactory.Semantics;
  34. using ICSharpCode.NRefactory.TypeSystem.Implementation;
  35. using ICSharpCode.NRefactory.CSharp.TypeSystem;
  36. namespace ICSharpCode.NRefactory.CSharp.Completion
  37. {
  38. /// <summary>
  39. /// Acts as a common base between code completion and parameter completion.
  40. /// </summary>
  41. public class CSharpCompletionEngineBase
  42. {
  43. protected IDocument document;
  44. protected int offset;
  45. protected TextLocation location;
  46. protected IUnresolvedTypeDefinition currentType;
  47. protected IUnresolvedMember currentMember;
  48. #region Input properties
  49. public CSharpTypeResolveContext ctx { get; private set; }
  50. public IProjectContent ProjectContent { get; private set; }
  51. ICompilation compilation;
  52. protected ICompilation Compilation {
  53. get {
  54. if (compilation == null)
  55. compilation = ProjectContent.Resolve (ctx).Compilation;
  56. return compilation;
  57. }
  58. }
  59. Version languageVersion = new Version (5, 0);
  60. public Version LanguageVersion {
  61. get {
  62. return languageVersion;
  63. }
  64. set {
  65. languageVersion = value;
  66. }
  67. }
  68. #endregion
  69. protected CSharpCompletionEngineBase(IProjectContent content, ICompletionContextProvider completionContextProvider, CSharpTypeResolveContext ctx)
  70. {
  71. if (content == null)
  72. throw new ArgumentNullException("content");
  73. if (ctx == null)
  74. throw new ArgumentNullException("ctx");
  75. if (completionContextProvider == null)
  76. throw new ArgumentNullException("completionContextProvider");
  77. this.ProjectContent = content;
  78. this.CompletionContextProvider = completionContextProvider;
  79. this.ctx = ctx;
  80. }
  81. public ICompletionContextProvider CompletionContextProvider {
  82. get;
  83. private set;
  84. }
  85. public void SetOffset (int offset)
  86. {
  87. Reset ();
  88. this.offset = offset;
  89. this.location = document.GetLocation (offset);
  90. CompletionContextProvider.GetCurrentMembers (offset, out currentType, out currentMember);
  91. }
  92. public bool GetParameterCompletionCommandOffset (out int cpos)
  93. {
  94. // Start calculating the parameter offset from the beginning of the
  95. // current member, instead of the beginning of the file.
  96. cpos = offset - 1;
  97. var mem = currentMember;
  98. if (mem == null || (mem is IType) || IsInsideCommentStringOrDirective ()) {
  99. return false;
  100. }
  101. int startPos = document.GetOffset (mem.Region.BeginLine, mem.Region.BeginColumn);
  102. int parenDepth = 0;
  103. int chevronDepth = 0;
  104. Stack<int> indexStack = new Stack<int> ();
  105. while (cpos > startPos) {
  106. char c = document.GetCharAt (cpos);
  107. if (c == ')') {
  108. parenDepth++;
  109. }
  110. if (c == '>') {
  111. chevronDepth++;
  112. }
  113. if (c == '}') {
  114. if (indexStack.Count > 0) {
  115. parenDepth = indexStack.Pop ();
  116. } else {
  117. parenDepth = 0;
  118. }
  119. chevronDepth = 0;
  120. }
  121. if (indexStack.Count == 0 && (parenDepth == 0 && c == '(' || chevronDepth == 0 && c == '<')) {
  122. int p = GetCurrentParameterIndex (startPos, cpos + 1);
  123. if (p != -1) {
  124. cpos++;
  125. return true;
  126. } else {
  127. return false;
  128. }
  129. }
  130. if (c == '(') {
  131. parenDepth--;
  132. }
  133. if (c == '<') {
  134. chevronDepth--;
  135. }
  136. if (c == '{') {
  137. indexStack.Push (parenDepth);
  138. chevronDepth = 0;
  139. }
  140. cpos--;
  141. }
  142. return false;
  143. }
  144. public int GetCurrentParameterIndex(int triggerOffset, int endOffset)
  145. {
  146. List<string> list;
  147. return GetCurrentParameterIndex (triggerOffset, endOffset, out list);
  148. }
  149. public int GetCurrentParameterIndex (int triggerOffset, int endOffset, out List<string> usedNamedParameters)
  150. {
  151. usedNamedParameters =new List<string> ();
  152. var parameter = new Stack<int> ();
  153. var bracketStack = new Stack<Stack<int>> ();
  154. bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false;
  155. var word = new StringBuilder ();
  156. bool foundCharAfterOpenBracket = false;
  157. for (int i = triggerOffset; i < endOffset; i++) {
  158. char ch = document.GetCharAt (i);
  159. char nextCh = i + 1 < document.TextLength ? document.GetCharAt (i + 1) : '\0';
  160. if (ch == ':') {
  161. usedNamedParameters.Add (word.ToString ());
  162. word.Length = 0;
  163. } else if (char.IsLetterOrDigit (ch) || ch =='_') {
  164. word.Append (ch);
  165. } else if (char.IsWhiteSpace (ch)) {
  166. } else {
  167. word.Length = 0;
  168. }
  169. if (!char.IsWhiteSpace(ch) && parameter.Count > 0)
  170. foundCharAfterOpenBracket = true;
  171. switch (ch) {
  172. case '{':
  173. if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
  174. break;
  175. }
  176. bracketStack.Push (parameter);
  177. parameter = new Stack<int> ();
  178. break;
  179. case '[':
  180. case '(':
  181. if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
  182. break;
  183. }
  184. parameter.Push (0);
  185. break;
  186. case '}':
  187. if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
  188. break;
  189. }
  190. if (bracketStack.Count > 0) {
  191. parameter = bracketStack.Pop ();
  192. } else {
  193. return -1;
  194. }
  195. break;
  196. case ']':
  197. case ')':
  198. if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
  199. break;
  200. }
  201. if (parameter.Count > 0) {
  202. parameter.Pop ();
  203. } else {
  204. return -1;
  205. }
  206. break;
  207. case '<':
  208. if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
  209. break;
  210. }
  211. parameter.Push (0);
  212. break;
  213. case '=':
  214. if (nextCh == '>') {
  215. i++;
  216. continue;
  217. }
  218. break;
  219. case '>':
  220. if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
  221. break;
  222. }
  223. if (parameter.Count > 0) {
  224. parameter.Pop ();
  225. }
  226. break;
  227. case ',':
  228. if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
  229. break;
  230. }
  231. if (parameter.Count > 0) {
  232. parameter.Push (parameter.Pop () + 1);
  233. }
  234. break;
  235. case '/':
  236. if (inString || inChar || inVerbatimString) {
  237. break;
  238. }
  239. if (nextCh == '/') {
  240. i++;
  241. inSingleComment = true;
  242. }
  243. if (nextCh == '*') {
  244. inMultiLineComment = true;
  245. }
  246. break;
  247. case '*':
  248. if (inString || inChar || inVerbatimString || inSingleComment) {
  249. break;
  250. }
  251. if (nextCh == '/') {
  252. i++;
  253. inMultiLineComment = false;
  254. }
  255. break;
  256. case '@':
  257. if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
  258. break;
  259. }
  260. if (nextCh == '"') {
  261. i++;
  262. inVerbatimString = true;
  263. }
  264. break;
  265. case '\\':
  266. if (inString || inChar) {
  267. i++;
  268. }
  269. break;
  270. case '"':
  271. if (inSingleComment || inMultiLineComment || inChar) {
  272. break;
  273. }
  274. if (inVerbatimString) {
  275. if (nextCh == '"') {
  276. i++;
  277. break;
  278. }
  279. inVerbatimString = false;
  280. break;
  281. }
  282. inString = !inString;
  283. break;
  284. case '\'':
  285. if (inSingleComment || inMultiLineComment || inString || inVerbatimString) {
  286. break;
  287. }
  288. inChar = !inChar;
  289. break;
  290. default:
  291. if (NewLine.IsNewLine(ch)) {
  292. inSingleComment = false;
  293. inString = false;
  294. inChar = false;
  295. }
  296. break;
  297. }
  298. }
  299. if (parameter.Count != 1 || bracketStack.Count > 0) {
  300. return -1;
  301. }
  302. if (!foundCharAfterOpenBracket)
  303. return 0;
  304. return parameter.Pop() + 1;
  305. }
  306. #region Context helper methods
  307. public class MiniLexer
  308. {
  309. readonly string text;
  310. public bool IsFistNonWs = true;
  311. public bool IsInSingleComment = false;
  312. public bool IsInString = false;
  313. public bool IsInVerbatimString = false;
  314. public bool IsInChar = false;
  315. public bool IsInMultiLineComment = false;
  316. public bool IsInPreprocessorDirective = false;
  317. public MiniLexer(string text)
  318. {
  319. this.text = text;
  320. }
  321. /// <summary>
  322. /// Parsing all text and calling act delegate on almost every character.
  323. /// Skipping begining of comments, begining of verbatim strings and escaped characters.
  324. /// </summary>
  325. /// <param name="act">Return true to abort parsing. Integer argument represent offset in text.</param>
  326. /// <returns>True if aborted.</returns>
  327. public bool Parse(Func<char, int, bool> act = null)
  328. {
  329. return Parse(0, text.Length, act);
  330. }
  331. /// <summary>
  332. /// Parsing text from start to start+length and calling act delegate on almost every character.
  333. /// Skipping begining of comments, begining of verbatim strings and escaped characters.
  334. /// </summary>
  335. /// <param name="start">Start offset.</param>
  336. /// <param name="length">Lenght to parse.</param>
  337. /// <param name="act">Return true to abort parsing. Integer argument represent offset in text.</param>
  338. /// <returns>True if aborted.</returns>
  339. public bool Parse(int start, int length, Func<char, int, bool> act = null)
  340. {
  341. for (int i = start; i < length; i++) {
  342. char ch = text [i];
  343. char nextCh = i + 1 < text.Length ? text [i + 1] : '\0';
  344. switch (ch) {
  345. case '#':
  346. if (IsFistNonWs)
  347. IsInPreprocessorDirective = true;
  348. break;
  349. case '/':
  350. if (IsInString || IsInChar || IsInVerbatimString || IsInSingleComment || IsInMultiLineComment)
  351. break;
  352. if (nextCh == '/') {
  353. i++;
  354. IsInSingleComment = true;
  355. IsInPreprocessorDirective = false;
  356. }
  357. if (nextCh == '*' && !IsInPreprocessorDirective) {
  358. IsInMultiLineComment = true;
  359. i++;
  360. }
  361. break;
  362. case '*':
  363. if (IsInString || IsInChar || IsInVerbatimString || IsInSingleComment)
  364. break;
  365. if (nextCh == '/') {
  366. i++;
  367. IsInMultiLineComment = false;
  368. }
  369. break;
  370. case '@':
  371. if (IsInString || IsInChar || IsInVerbatimString || IsInSingleComment || IsInMultiLineComment)
  372. break;
  373. if (nextCh == '"') {
  374. i++;
  375. IsInVerbatimString = true;
  376. }
  377. break;
  378. case '\n':
  379. case '\r':
  380. IsInSingleComment = false;
  381. IsInString = false;
  382. IsInChar = false;
  383. IsFistNonWs = true;
  384. IsInPreprocessorDirective = false;
  385. break;
  386. case '\\':
  387. if (IsInString || IsInChar)
  388. i++;
  389. break;
  390. case '"':
  391. if (IsInSingleComment || IsInMultiLineComment || IsInChar)
  392. break;
  393. if (IsInVerbatimString) {
  394. if (nextCh == '"') {
  395. i++;
  396. break;
  397. }
  398. IsInVerbatimString = false;
  399. break;
  400. }
  401. IsInString = !IsInString;
  402. break;
  403. case '\'':
  404. if (IsInSingleComment || IsInMultiLineComment || IsInString || IsInVerbatimString)
  405. break;
  406. IsInChar = !IsInChar;
  407. break;
  408. }
  409. if (act != null)
  410. if (act (ch, i))
  411. return true;
  412. IsFistNonWs &= ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
  413. }
  414. return false;
  415. }
  416. }
  417. protected bool IsInsideCommentStringOrDirective(int offset)
  418. {
  419. var lexer = new MiniLexer(document.Text);
  420. lexer.Parse(0, offset);
  421. return
  422. lexer.IsInSingleComment ||
  423. lexer.IsInString ||
  424. lexer.IsInVerbatimString ||
  425. lexer.IsInChar ||
  426. lexer.IsInMultiLineComment ||
  427. lexer.IsInPreprocessorDirective;
  428. }
  429. protected bool IsInsideCommentStringOrDirective()
  430. {
  431. var text = GetMemberTextToCaret();
  432. var lexer = new MiniLexer(text.Item1);
  433. lexer.Parse();
  434. return
  435. lexer.IsInSingleComment ||
  436. lexer.IsInString ||
  437. lexer.IsInVerbatimString ||
  438. lexer.IsInChar ||
  439. lexer.IsInMultiLineComment ||
  440. lexer.IsInPreprocessorDirective;
  441. }
  442. protected bool IsInsideDocComment ()
  443. {
  444. var text = GetMemberTextToCaret ();
  445. bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false;
  446. bool singleLineIsDoc = false;
  447. for (int i = 0; i < text.Item1.Length - 1; i++) {
  448. char ch = text.Item1 [i];
  449. char nextCh = text.Item1 [i + 1];
  450. switch (ch) {
  451. case '/':
  452. if (inString || inChar || inVerbatimString)
  453. break;
  454. if (nextCh == '/') {
  455. i++;
  456. inSingleComment = true;
  457. singleLineIsDoc = i + 1 < text.Item1.Length && text.Item1 [i + 1] == '/';
  458. if (singleLineIsDoc) {
  459. i++;
  460. }
  461. }
  462. if (nextCh == '*')
  463. inMultiLineComment = true;
  464. break;
  465. case '*':
  466. if (inString || inChar || inVerbatimString || inSingleComment)
  467. break;
  468. if (nextCh == '/') {
  469. i++;
  470. inMultiLineComment = false;
  471. }
  472. break;
  473. case '@':
  474. if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment)
  475. break;
  476. if (nextCh == '"') {
  477. i++;
  478. inVerbatimString = true;
  479. }
  480. break;
  481. case '\n':
  482. case '\r':
  483. inSingleComment = false;
  484. inString = false;
  485. inChar = false;
  486. break;
  487. case '\\':
  488. if (inString || inChar)
  489. i++;
  490. break;
  491. case '"':
  492. if (inSingleComment || inMultiLineComment || inChar)
  493. break;
  494. if (inVerbatimString) {
  495. if (nextCh == '"') {
  496. i++;
  497. break;
  498. }
  499. inVerbatimString = false;
  500. break;
  501. }
  502. inString = !inString;
  503. break;
  504. case '\'':
  505. if (inSingleComment || inMultiLineComment || inString || inVerbatimString)
  506. break;
  507. inChar = !inChar;
  508. break;
  509. }
  510. }
  511. return inSingleComment && singleLineIsDoc;
  512. }
  513. protected CSharpResolver GetState ()
  514. {
  515. return new CSharpResolver (ctx);
  516. /*var state = new CSharpResolver (ctx);
  517. state.CurrentMember = currentMember;
  518. state.CurrentTypeDefinition = currentType;
  519. state.CurrentUsingScope = CSharpUnresolvedFile.GetUsingScope (location);
  520. if (state.CurrentMember != null) {
  521. var node = Unit.GetNodeAt (location);
  522. if (node == null)
  523. return state;
  524. var navigator = new NodeListResolveVisitorNavigator (new[] { node });
  525. var visitor = new ResolveVisitor (state, CSharpUnresolvedFile, navigator);
  526. Unit.AcceptVisitor (visitor, null);
  527. try {
  528. var newState = visitor.GetResolverStateBefore (node);
  529. if (newState != null)
  530. state = newState;
  531. } catch (Exception) {
  532. }
  533. }
  534. return state;*/
  535. }
  536. #endregion
  537. #region Basic parsing/resolving functions
  538. static Stack<Tuple<char, int>> GetBracketStack (string memberText)
  539. {
  540. var bracketStack = new Stack<Tuple<char, int>> ();
  541. bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false;
  542. for (int i = 0; i < memberText.Length; i++) {
  543. char ch = memberText [i];
  544. char nextCh = i + 1 < memberText.Length ? memberText [i + 1] : '\0';
  545. switch (ch) {
  546. case '(':
  547. case '[':
  548. case '{':
  549. if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment)
  550. break;
  551. bracketStack.Push (Tuple.Create (ch, i));
  552. break;
  553. case ')':
  554. case ']':
  555. case '}':
  556. if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment)
  557. break;
  558. if (bracketStack.Count > 0)
  559. bracketStack.Pop ();
  560. break;
  561. case '/':
  562. if (inString || inChar || inVerbatimString)
  563. break;
  564. if (nextCh == '/') {
  565. i++;
  566. inSingleComment = true;
  567. }
  568. if (nextCh == '*')
  569. inMultiLineComment = true;
  570. break;
  571. case '*':
  572. if (inString || inChar || inVerbatimString || inSingleComment)
  573. break;
  574. if (nextCh == '/') {
  575. i++;
  576. inMultiLineComment = false;
  577. }
  578. break;
  579. case '@':
  580. if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment)
  581. break;
  582. if (nextCh == '"') {
  583. i++;
  584. inVerbatimString = true;
  585. }
  586. break;
  587. case '\\':
  588. if (inString || inChar)
  589. i++;
  590. break;
  591. case '"':
  592. if (inSingleComment || inMultiLineComment || inChar)
  593. break;
  594. if (inVerbatimString) {
  595. if (nextCh == '"') {
  596. i++;
  597. break;
  598. }
  599. inVerbatimString = false;
  600. break;
  601. }
  602. inString = !inString;
  603. break;
  604. case '\'':
  605. if (inSingleComment || inMultiLineComment || inString || inVerbatimString)
  606. break;
  607. inChar = !inChar;
  608. break;
  609. default :
  610. if (NewLine.IsNewLine(ch)) {
  611. inSingleComment = false;
  612. inString = false;
  613. inChar = false;
  614. }
  615. break;
  616. }
  617. }
  618. return bracketStack;
  619. }
  620. public static void AppendMissingClosingBrackets (StringBuilder wrapper, bool appendSemicolon)
  621. {
  622. var memberText = wrapper.ToString();
  623. var bracketStack = GetBracketStack(memberText);
  624. bool didAppendSemicolon = !appendSemicolon;
  625. //char lastBracket = '\0';
  626. while (bracketStack.Count > 0) {
  627. var t = bracketStack.Pop ();
  628. switch (t.Item1) {
  629. case '(':
  630. wrapper.Append (')');
  631. if (appendSemicolon)
  632. didAppendSemicolon = false;
  633. //lastBracket = ')';
  634. break;
  635. case '[':
  636. wrapper.Append (']');
  637. if (appendSemicolon)
  638. didAppendSemicolon = false;
  639. //lastBracket = ']';
  640. break;
  641. case '<':
  642. wrapper.Append ('>');
  643. if (appendSemicolon)
  644. didAppendSemicolon = false;
  645. //lastBracket = '>';
  646. break;
  647. case '{':
  648. int o = t.Item2 - 1;
  649. if (!didAppendSemicolon) {
  650. didAppendSemicolon = true;
  651. wrapper.Append (';');
  652. }
  653. bool didAppendCatch = false;
  654. while (o >= "try".Length) {
  655. char ch = memberText [o];
  656. if (!char.IsWhiteSpace (ch)) {
  657. if (ch == 'y' && memberText [o - 1] == 'r' && memberText [o - 2] == 't' && (o - 3 < 0 || !char.IsLetterOrDigit(memberText [o - 3]))) {
  658. wrapper.Append ("} catch {}");
  659. didAppendCatch = true;
  660. }
  661. break;
  662. }
  663. o--;
  664. }
  665. if (!didAppendCatch)
  666. wrapper.Append ('}');
  667. break;
  668. }
  669. }
  670. if (!didAppendSemicolon)
  671. wrapper.Append (';');
  672. }
  673. protected StringBuilder CreateWrapper(string continuation, bool appendSemicolon, string afterContinuation, string memberText, TextLocation memberLocation, ref int closingBrackets, ref int generatedLines)
  674. {
  675. var wrapper = new StringBuilder();
  676. bool wrapInClass = memberLocation != new TextLocation(1, 1);
  677. if (wrapInClass) {
  678. wrapper.Append("class Stub {");
  679. wrapper.AppendLine();
  680. closingBrackets++;
  681. generatedLines++;
  682. }
  683. wrapper.Append(memberText);
  684. wrapper.Append(continuation);
  685. AppendMissingClosingBrackets(wrapper, appendSemicolon);
  686. wrapper.Append(afterContinuation);
  687. if (closingBrackets > 0) {
  688. wrapper.Append(new string('}', closingBrackets));
  689. }
  690. return wrapper;
  691. }
  692. protected SyntaxTree ParseStub(string continuation, bool appendSemicolon = true, string afterContinuation = null)
  693. {
  694. var mt = GetMemberTextToCaret();
  695. if (mt == null) {
  696. return null;
  697. }
  698. string memberText = mt.Item1;
  699. var memberLocation = mt.Item2;
  700. int closingBrackets = 1;
  701. int generatedLines = 0;
  702. var wrapper = CreateWrapper(continuation, appendSemicolon, afterContinuation, memberText, memberLocation, ref closingBrackets, ref generatedLines);
  703. var parser = new CSharpParser ();
  704. foreach (var sym in CompletionContextProvider.ConditionalSymbols)
  705. parser.CompilerSettings.ConditionalSymbols.Add (sym);
  706. parser.InitialLocation = new TextLocation(memberLocation.Line - generatedLines, 1);
  707. var result = parser.Parse(wrapper.ToString ());
  708. return result;
  709. }
  710. protected virtual void Reset ()
  711. {
  712. memberText = null;
  713. }
  714. Tuple<string, TextLocation> memberText;
  715. protected Tuple<string, TextLocation> GetMemberTextToCaret()
  716. {
  717. if (memberText == null)
  718. memberText = CompletionContextProvider.GetMemberTextToCaret(offset, currentType, currentMember);
  719. return memberText;
  720. }
  721. protected ExpressionResult GetInvocationBeforeCursor(bool afterBracket)
  722. {
  723. SyntaxTree baseUnit;
  724. baseUnit = ParseStub("a", false);
  725. var section = baseUnit.GetNodeAt<AttributeSection>(location.Line, location.Column - 2);
  726. var attr = section != null ? section.Attributes.LastOrDefault() : null;
  727. if (attr != null) {
  728. return new ExpressionResult((AstNode)attr, baseUnit);
  729. }
  730. //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin;
  731. var mref = baseUnit.GetNodeAt(location.Line, location.Column - 1, n => n is InvocationExpression || n is ObjectCreateExpression);
  732. AstNode expr = null;
  733. if (mref is InvocationExpression) {
  734. expr = ((InvocationExpression)mref).Target;
  735. } else if (mref is ObjectCreateExpression) {
  736. expr = mref;
  737. } else {
  738. baseUnit = ParseStub(")};", false);
  739. mref = baseUnit.GetNodeAt(location.Line, location.Column - 1, n => n is InvocationExpression || n is ObjectCreateExpression);
  740. if (mref is InvocationExpression) {
  741. expr = ((InvocationExpression)mref).Target;
  742. } else if (mref is ObjectCreateExpression) {
  743. expr = mref;
  744. }
  745. }
  746. if (expr == null) {
  747. // work around for missing ';' bug in mcs:
  748. baseUnit = ParseStub("a", true);
  749. section = baseUnit.GetNodeAt<AttributeSection>(location.Line, location.Column - 2);
  750. attr = section != null ? section.Attributes.LastOrDefault() : null;
  751. if (attr != null) {
  752. return new ExpressionResult((AstNode)attr, baseUnit);
  753. }
  754. //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin;
  755. mref = baseUnit.GetNodeAt(location.Line, location.Column - 1, n => n is InvocationExpression || n is ObjectCreateExpression);
  756. expr = null;
  757. if (mref is InvocationExpression) {
  758. expr = ((InvocationExpression)mref).Target;
  759. } else if (mref is ObjectCreateExpression) {
  760. expr = mref;
  761. }
  762. }
  763. if (expr == null) {
  764. return null;
  765. }
  766. return new ExpressionResult ((AstNode)expr, baseUnit);
  767. }
  768. public class ExpressionResult
  769. {
  770. public AstNode Node { get; private set; }
  771. public SyntaxTree Unit { get; private set; }
  772. public ExpressionResult (AstNode item2, SyntaxTree item3)
  773. {
  774. this.Node = item2;
  775. this.Unit = item3;
  776. }
  777. public override string ToString ()
  778. {
  779. return string.Format ("[ExpressionResult: Node={0}, Unit={1}]", Node, Unit);
  780. }
  781. }
  782. protected ExpressionResolveResult ResolveExpression (ExpressionResult tuple)
  783. {
  784. return ResolveExpression (tuple.Node);
  785. }
  786. protected class ExpressionResolveResult
  787. {
  788. public ResolveResult Result { get; set; }
  789. public CSharpResolver Resolver { get; set; }
  790. public CSharpAstResolver AstResolver { get; set; }
  791. public ExpressionResolveResult(ResolveResult item1, CSharpResolver item2, CSharpAstResolver item3)
  792. {
  793. this.Result = item1;
  794. this.Resolver = item2;
  795. this.AstResolver = item3;
  796. }
  797. }
  798. protected ExpressionResolveResult ResolveExpression(AstNode expr)
  799. {
  800. if (expr == null) {
  801. return null;
  802. }
  803. AstNode resolveNode;
  804. if (expr is Expression || expr is AstType) {
  805. resolveNode = expr;
  806. } else if (expr is VariableDeclarationStatement) {
  807. resolveNode = ((VariableDeclarationStatement)expr).Type;
  808. } else {
  809. resolveNode = expr;
  810. }
  811. try {
  812. var root = expr.AncestorsAndSelf.FirstOrDefault(n => n is EntityDeclaration || n is SyntaxTree);
  813. if (root == null) {
  814. return null;
  815. }
  816. var curState = GetState();
  817. // current member needs to be in the setter because of the 'value' parameter
  818. if (root is Accessor) {
  819. var prop = curState.CurrentMember as IProperty;
  820. if (prop != null && prop.CanSet && (root.Role == IndexerDeclaration.SetterRole || root.Role == PropertyDeclaration.SetterRole))
  821. curState = curState.WithCurrentMember(prop.Setter);
  822. }
  823. // Rood should be the 'body' - otherwise the state -> current member isn't correct.
  824. var body = root.Children.FirstOrDefault(r => r.Role == Roles.Body);
  825. if (body != null && body.Contains(expr.StartLocation))
  826. root = body;
  827. var csResolver = CompletionContextProvider.GetResolver (curState, root);
  828. var result = csResolver.Resolve(resolveNode);
  829. var state = csResolver.GetResolverStateBefore(resolveNode);
  830. if (state.CurrentMember == null)
  831. state = state.WithCurrentMember(curState.CurrentMember);
  832. if (state.CurrentTypeDefinition == null)
  833. state = state.WithCurrentTypeDefinition(curState.CurrentTypeDefinition);
  834. if (state.CurrentUsingScope == null)
  835. state = state.WithCurrentUsingScope(curState.CurrentUsingScope);
  836. return new ExpressionResolveResult(result, state, csResolver);
  837. } catch (Exception e) {
  838. Console.WriteLine(e);
  839. return null;
  840. }
  841. }
  842. #endregion
  843. }
  844. }