PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/IronPython_Main/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/CompletionAnalysis.cs

#
C# | 302 lines | 225 code | 44 blank | 33 comment | 35 complexity | 69dac54548f3d167cfadccc13f56f73b MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, CPL-1.0, CC-BY-SA-3.0, BSD-3-Clause, ISC, AGPL-3.0, LGPL-2.1, Apache-2.0
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. * ***************************************************************************/
  14. using System;
  15. using System.Collections.Generic;
  16. using Microsoft.IronPythonTools.Internal;
  17. using Microsoft.IronStudio;
  18. using Microsoft.PyAnalysis;
  19. using Microsoft.VisualStudio.Language.Intellisense;
  20. using Microsoft.VisualStudio.Text;
  21. using Microsoft.VisualStudio.Text.Classification;
  22. using System.Diagnostics;
  23. namespace Microsoft.IronPythonTools.Intellisense {
  24. /// <summary>
  25. /// Provides various completion services after the text around the current location has been
  26. /// processed. The completion services are specific to the current context
  27. /// </summary>
  28. public class CompletionAnalysis {
  29. private readonly string _text;
  30. protected readonly int _pos;
  31. private readonly ITrackingSpan _span;
  32. private readonly ITextBuffer _textBuffer;
  33. internal const Int64 TooMuchTime = 50;
  34. protected static Stopwatch _stopwatch = MakeStopWatch();
  35. internal static CompletionAnalysis EmptyCompletionContext = new CompletionAnalysis(String.Empty, 0, null, null);
  36. internal CompletionAnalysis(string text, int pos, ITrackingSpan span, ITextBuffer textBuffer) {
  37. _text = text ?? String.Empty;
  38. _pos = pos;
  39. _span = span;
  40. _textBuffer = textBuffer;
  41. }
  42. #if FALSE
  43. /// <summary>
  44. /// Gets a CompletionContext providing a list of possible members the user can dot through.
  45. /// </summary>
  46. public static CompletionContext GetMemberCompletionContext(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span) {
  47. ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, buffer, span);
  48. var loc = parser.Span.GetSpan(parser.Snapshot.Version);
  49. var line = parser.Snapshot.GetLineFromPosition(loc.Start);
  50. var lineStart = line.Start;
  51. var textLen = loc.End - lineStart.Position;
  52. if (textLen <= 0) {
  53. // Ctrl-Space on an empty line, we just want to get global vars
  54. return new NormalCompletionContext(String.Empty, loc.Start, parser.Snapshot, parser.Span, parser.Buffer, 0);
  55. }
  56. return TrySpecialCompletions(parser, loc) ??
  57. GetNormalCompletionContext(parser, loc);
  58. }
  59. /// <summary>
  60. /// Gets a CompletionContext for the expression at the provided span. If the span is in
  61. /// part of an identifier then the expression is extended to complete the identifier.
  62. /// </summary>
  63. public static CompletionContext GetExpressionContext(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span) {
  64. ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, buffer, span);
  65. var loc = parser.Span.GetSpan(parser.Snapshot.Version);
  66. var exprRange = parser.GetExpressionRange();
  67. if (exprRange == null) {
  68. return EmptyCompletionContext;
  69. }
  70. // extend right for any partial expression the user is hovering on, for example:
  71. // "x.Baz" where the user is hovering over the B in baz we want the complete
  72. // expression.
  73. var text = exprRange.Value.GetText();
  74. var endingLine = exprRange.Value.End.GetContainingLine();
  75. var endText = snapshot.GetText(exprRange.Value.End.Position, endingLine.End.Position - exprRange.Value.End.Position);
  76. for (int i = 0; i < endText.Length; i++) {
  77. if (!Char.IsLetterOrDigit(endText[i]) && endText[i] != '_') {
  78. text += endText.Substring(0, i);
  79. break;
  80. }
  81. }
  82. var applicableSpan = parser.Snapshot.CreateTrackingSpan(
  83. exprRange.Value.Span,
  84. SpanTrackingMode.EdgeExclusive
  85. );
  86. return new NormalCompletionContext(
  87. text,
  88. loc.Start,
  89. parser.Snapshot,
  90. applicableSpan,
  91. parser.Buffer,
  92. -1
  93. );
  94. }
  95. public static CompletionContext GetSignatureContext(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span) {
  96. ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, buffer, span);
  97. var loc = parser.Span.GetSpan(parser.Snapshot.Version);
  98. int paramIndex;
  99. var exprRange = parser.GetExpressionRange(1, out paramIndex);
  100. if (exprRange == null) {
  101. return EmptyCompletionContext;
  102. }
  103. var text = exprRange.Value.GetText();
  104. var applicableSpan = parser.Snapshot.CreateTrackingSpan(
  105. exprRange.Value.Span,
  106. SpanTrackingMode.EdgeExclusive
  107. );
  108. return new NormalCompletionContext(
  109. text,
  110. loc.Start,
  111. parser.Snapshot,
  112. applicableSpan,
  113. parser.Buffer,
  114. paramIndex
  115. );
  116. }
  117. #endif
  118. public ITextBuffer TextBuffer {
  119. get {
  120. return _textBuffer;
  121. }
  122. }
  123. public string Text {
  124. get {
  125. return _text;
  126. }
  127. }
  128. public ITrackingSpan Span {
  129. get {
  130. return _span;
  131. }
  132. }
  133. #if FALSE
  134. public virtual int ParameterIndex {
  135. get { return 0; }
  136. }
  137. #endif
  138. public virtual CompletionSet GetCompletions(IGlyphService glyphService) {
  139. return null;
  140. }
  141. #if FALSE
  142. public virtual ISignature[] GetSignatures() {
  143. return new ISignature[0];
  144. }
  145. public virtual string GetQuickInfo() {
  146. return null;
  147. }
  148. public virtual IEnumerable<VariableResult> GetVariableInfo() {
  149. yield break;
  150. }
  151. private static CompletionContext GetNormalCompletionContext(ReverseExpressionParser parser, Span loc) {
  152. var exprRange = parser.GetExpressionRange();
  153. if (exprRange == null) {
  154. return EmptyCompletionContext;
  155. }
  156. var text = exprRange.Value.GetText();
  157. var applicableSpan = parser.Snapshot.CreateTrackingSpan(
  158. exprRange.Value.Span,
  159. SpanTrackingMode.EdgeExclusive
  160. );
  161. return new NormalCompletionContext(
  162. text,
  163. loc.Start,
  164. parser.Snapshot,
  165. applicableSpan,
  166. parser.Buffer,
  167. -1
  168. );
  169. }
  170. private static CompletionContext TrySpecialCompletions(ReverseExpressionParser parser, Span loc) {
  171. if (parser.Tokens.Count > 0) {
  172. // Check for context-sensitive intellisense
  173. var lastClass = parser.Tokens[parser.Tokens.Count - 1];
  174. if (lastClass.ClassificationType == parser.Classifier.Provider.Comment) {
  175. // No completions in comments
  176. return EmptyCompletionContext;
  177. } else if (lastClass.ClassificationType == parser.Classifier.Provider.StringLiteral) {
  178. // String completion
  179. return new StringLiteralCompletionContext(lastClass.Span.GetText(), loc.Start, parser.Span, parser.Buffer);
  180. }
  181. // Import completions
  182. var first = parser.Tokens[0];
  183. if (IsKeyword(first, "import")) {
  184. return ImportCompletionContext.Make(first, lastClass, loc, parser.Snapshot, parser.Span, parser.Buffer, IsSpaceCompletion(parser, loc));
  185. } else if (IsKeyword(first, "from")) {
  186. return FromImportCompletionContext.Make(parser.Tokens, first, loc, parser.Snapshot, parser.Span, parser.Buffer, IsSpaceCompletion(parser, loc));
  187. }
  188. return null;
  189. }
  190. return EmptyCompletionContext;
  191. }
  192. private static bool IsSpaceCompletion(ReverseExpressionParser parser, Span loc) {
  193. var keySpan = new SnapshotSpan(parser.Snapshot, loc.Start - 1, 1);
  194. return (keySpan.GetText() == " ");
  195. }
  196. #endif
  197. internal static bool IsKeyword(ClassificationSpan token, string keyword) {
  198. return token.ClassificationType.Classification == "keyword" && token.Span.GetText() == keyword;
  199. }
  200. internal static Completion PythonCompletion(IGlyphService service, MemberResult memberResult) {
  201. StandardGlyphGroup group = memberResult.MemberType.ToGlyphGroup();
  202. var icon = new IconDescription(group, StandardGlyphItem.GlyphItemPublic);
  203. var result = new LazyCompletion(memberResult.Name, () => memberResult.Completion, () => memberResult.ToolTip, service.GetGlyph(group, StandardGlyphItem.GlyphItemPublic));
  204. result.Properties.AddProperty(typeof(IconDescription), icon);
  205. return result;
  206. }
  207. internal static Completion PythonCompletion(IGlyphService service, string name, string tooltip, StandardGlyphGroup group) {
  208. var icon = new IconDescription(group, StandardGlyphItem.GlyphItemPublic);
  209. var result = new LazyCompletion(name, () => name, () => tooltip, service.GetGlyph(group, StandardGlyphItem.GlyphItemPublic));
  210. result.Properties.AddProperty(typeof(IconDescription), icon);
  211. return result;
  212. }
  213. internal ModuleAnalysis GetAnalysisEntry() {
  214. return ((IPythonProjectEntry)TextBuffer.GetAnalysis()).Analysis;
  215. }
  216. private static Stopwatch MakeStopWatch() {
  217. var res = new Stopwatch();
  218. res.Start();
  219. return res;
  220. }
  221. public virtual Completion[] GetModules(IGlyphService glyphService, string text) {
  222. var analysis = GetAnalysisEntry();
  223. var path = text.Split('.');
  224. if (path.Length > 0) {
  225. // path = path[:-1]
  226. var newPath = new string[path.Length - 1];
  227. Array.Copy(path, newPath, path.Length - 1);
  228. path = newPath;
  229. }
  230. MemberResult[] modules = new MemberResult[0];
  231. if (path.Length == 0) {
  232. if (analysis != null) {
  233. modules = analysis.ProjectState.GetModules();
  234. }
  235. #if REPL
  236. var repl = Intellisense.GetRepl(_textBuffer);
  237. if (repl != null) {
  238. modules = Intellisense.MergeMembers(modules, repl.GetModules());
  239. }
  240. #endif
  241. } else {
  242. if (analysis != null) {
  243. modules = analysis.ProjectState.GetModuleMembers(path);
  244. }
  245. }
  246. var sortedAndFiltered = NormalCompletionAnalysis.FilterCompletions(modules, text, (x, y) => x.StartsWith(y));
  247. Array.Sort(sortedAndFiltered, NormalCompletionAnalysis.ModuleSort);
  248. var result = new Completion[sortedAndFiltered.Length];
  249. for (int i = 0; i < sortedAndFiltered.Length; i++) {
  250. result[i] = PythonCompletion(glyphService, sortedAndFiltered[i]);
  251. }
  252. return result;
  253. }
  254. public override string ToString() {
  255. return String.Format("CompletionContext({0}): {1} @{2}", GetType().Name, Text, _pos);
  256. }
  257. }
  258. }