PageRenderTime 34ms CodeModel.GetById 12ms app.highlight 17ms RepoModel.GetById 1ms 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
  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
 15using System;
 16using System.Collections.Generic;
 17using Microsoft.IronPythonTools.Internal;
 18using Microsoft.IronStudio;
 19using Microsoft.PyAnalysis;
 20using Microsoft.VisualStudio.Language.Intellisense;
 21using Microsoft.VisualStudio.Text;
 22using Microsoft.VisualStudio.Text.Classification;
 23using System.Diagnostics;
 24
 25namespace Microsoft.IronPythonTools.Intellisense {
 26    /// <summary>
 27    /// Provides various completion services after the text around the current location has been
 28    /// processed. The completion services are specific to the current context
 29    /// </summary>
 30    public class CompletionAnalysis {
 31        private readonly string _text;
 32        protected readonly int _pos;
 33        private readonly ITrackingSpan _span;
 34        private readonly ITextBuffer _textBuffer;
 35        internal const Int64 TooMuchTime = 50;
 36        protected static Stopwatch _stopwatch = MakeStopWatch();
 37
 38        internal static CompletionAnalysis EmptyCompletionContext = new CompletionAnalysis(String.Empty, 0, null, null);
 39
 40        internal CompletionAnalysis(string text, int pos, ITrackingSpan span, ITextBuffer textBuffer) {
 41            _text = text ?? String.Empty;
 42            _pos = pos;
 43            _span = span;
 44            _textBuffer = textBuffer;
 45        }
 46
 47#if FALSE
 48        /// <summary>
 49        /// Gets a CompletionContext providing a list of possible members the user can dot through.
 50        /// </summary>
 51        public static CompletionContext GetMemberCompletionContext(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span) {
 52            ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, buffer, span);
 53
 54            var loc = parser.Span.GetSpan(parser.Snapshot.Version);
 55            var line = parser.Snapshot.GetLineFromPosition(loc.Start);
 56            var lineStart = line.Start;
 57
 58            var textLen = loc.End - lineStart.Position;
 59            if (textLen <= 0) {
 60                // Ctrl-Space on an empty line, we just want to get global vars
 61                return new NormalCompletionContext(String.Empty, loc.Start, parser.Snapshot, parser.Span, parser.Buffer, 0);
 62            }
 63
 64            return TrySpecialCompletions(parser, loc) ??
 65                   GetNormalCompletionContext(parser, loc);
 66        }
 67
 68        /// <summary>
 69        /// Gets a CompletionContext for the expression at the provided span.  If the span is in
 70        /// part of an identifier then the expression is extended to complete the identifier.
 71        /// </summary>
 72        public static CompletionContext GetExpressionContext(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span) {
 73            ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, buffer, span);
 74
 75            var loc = parser.Span.GetSpan(parser.Snapshot.Version);
 76            var exprRange = parser.GetExpressionRange();
 77            if (exprRange == null) {
 78                return EmptyCompletionContext;
 79            }
 80
 81            // extend right for any partial expression the user is hovering on, for example:
 82            // "x.Baz" where the user is hovering over the B in baz we want the complete
 83            // expression.
 84            var text = exprRange.Value.GetText();
 85            var endingLine = exprRange.Value.End.GetContainingLine();
 86            var endText = snapshot.GetText(exprRange.Value.End.Position, endingLine.End.Position - exprRange.Value.End.Position);
 87            for (int i = 0; i < endText.Length; i++) {
 88                if (!Char.IsLetterOrDigit(endText[i]) && endText[i] != '_') {
 89                    text += endText.Substring(0, i);
 90                    break;
 91                }
 92            }
 93
 94            var applicableSpan = parser.Snapshot.CreateTrackingSpan(
 95                exprRange.Value.Span,
 96                SpanTrackingMode.EdgeExclusive
 97            );
 98
 99            return new NormalCompletionContext(
100                text,
101                loc.Start,
102                parser.Snapshot,
103                applicableSpan,
104                parser.Buffer,
105                -1
106            );
107        }
108
109        public static CompletionContext GetSignatureContext(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span) {
110            ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, buffer, span);
111
112            var loc = parser.Span.GetSpan(parser.Snapshot.Version);
113
114            int paramIndex;
115            var exprRange = parser.GetExpressionRange(1, out paramIndex);
116            if (exprRange == null) {
117                return EmptyCompletionContext;
118            }
119            var text = exprRange.Value.GetText();
120
121            var applicableSpan = parser.Snapshot.CreateTrackingSpan(
122                exprRange.Value.Span,
123                SpanTrackingMode.EdgeExclusive
124            );
125
126            return new NormalCompletionContext(
127                text,
128                loc.Start,
129                parser.Snapshot,
130                applicableSpan,
131                parser.Buffer,
132                paramIndex
133            );
134        }
135
136#endif
137        public ITextBuffer TextBuffer {
138            get {
139                return _textBuffer;
140            }
141        }
142
143        public string Text {
144            get {
145                return _text;
146            }
147        }
148
149        public ITrackingSpan Span {
150            get {
151                return _span;
152            }
153        }
154#if FALSE
155        public virtual int ParameterIndex {
156            get { return 0; }
157        }
158#endif
159        public virtual CompletionSet GetCompletions(IGlyphService glyphService) {
160            return null;
161        }
162
163#if FALSE
164        public virtual ISignature[] GetSignatures() {
165            return new ISignature[0];
166        }
167
168        public virtual string GetQuickInfo() {
169            return null;
170        }
171
172        public virtual IEnumerable<VariableResult> GetVariableInfo() {
173            yield break;
174        }
175
176        private static CompletionContext GetNormalCompletionContext(ReverseExpressionParser parser, Span loc) {
177            var exprRange = parser.GetExpressionRange();
178            if (exprRange == null) {
179                return EmptyCompletionContext;
180            }
181            var text = exprRange.Value.GetText();
182
183            var applicableSpan = parser.Snapshot.CreateTrackingSpan(
184                exprRange.Value.Span,
185                SpanTrackingMode.EdgeExclusive
186            );
187
188            return new NormalCompletionContext(
189                text,
190                loc.Start,
191                parser.Snapshot,
192                applicableSpan,
193                parser.Buffer,
194                -1
195            );
196        }
197
198        private static CompletionContext TrySpecialCompletions(ReverseExpressionParser parser, Span loc) {
199            if (parser.Tokens.Count > 0) {
200                // Check for context-sensitive intellisense
201                var lastClass = parser.Tokens[parser.Tokens.Count - 1];
202
203                if (lastClass.ClassificationType == parser.Classifier.Provider.Comment) {
204                    // No completions in comments
205                    return EmptyCompletionContext;
206                } else if (lastClass.ClassificationType == parser.Classifier.Provider.StringLiteral) {
207                    // String completion
208                    return new StringLiteralCompletionContext(lastClass.Span.GetText(), loc.Start, parser.Span, parser.Buffer);
209                }
210
211                // Import completions
212                var first = parser.Tokens[0];
213                if (IsKeyword(first, "import")) {
214                    return ImportCompletionContext.Make(first, lastClass, loc, parser.Snapshot, parser.Span, parser.Buffer, IsSpaceCompletion(parser, loc));
215                } else if (IsKeyword(first, "from")) {
216                    return FromImportCompletionContext.Make(parser.Tokens, first, loc, parser.Snapshot, parser.Span, parser.Buffer, IsSpaceCompletion(parser, loc));
217                }
218                return null;
219            }
220
221            return EmptyCompletionContext;
222        }
223
224        private static bool IsSpaceCompletion(ReverseExpressionParser parser, Span loc) {
225            var keySpan = new SnapshotSpan(parser.Snapshot, loc.Start - 1, 1);
226            return (keySpan.GetText() == " ");
227        }
228#endif
229        internal static bool IsKeyword(ClassificationSpan token, string keyword) {
230            return token.ClassificationType.Classification == "keyword" && token.Span.GetText() == keyword;
231        }
232
233        internal static Completion PythonCompletion(IGlyphService service, MemberResult memberResult) {
234            StandardGlyphGroup group = memberResult.MemberType.ToGlyphGroup();
235            var icon = new IconDescription(group, StandardGlyphItem.GlyphItemPublic);
236
237            var result = new LazyCompletion(memberResult.Name, () => memberResult.Completion, () => memberResult.ToolTip, service.GetGlyph(group, StandardGlyphItem.GlyphItemPublic));
238            result.Properties.AddProperty(typeof(IconDescription), icon);
239            return result;
240        }
241
242        internal static Completion PythonCompletion(IGlyphService service, string name, string tooltip, StandardGlyphGroup group) {
243            var icon = new IconDescription(group, StandardGlyphItem.GlyphItemPublic);
244
245            var result = new LazyCompletion(name, () => name, () => tooltip, service.GetGlyph(group, StandardGlyphItem.GlyphItemPublic));
246            result.Properties.AddProperty(typeof(IconDescription), icon);
247            return result;
248        }
249
250        internal ModuleAnalysis GetAnalysisEntry() {
251            return ((IPythonProjectEntry)TextBuffer.GetAnalysis()).Analysis;
252        }
253
254        private static Stopwatch MakeStopWatch() {
255            var res = new Stopwatch();
256            res.Start();
257            return res;
258        }
259
260        public virtual Completion[] GetModules(IGlyphService glyphService, string text) {
261            var analysis = GetAnalysisEntry();
262            var path = text.Split('.');
263            if (path.Length > 0) {
264                // path = path[:-1]
265                var newPath = new string[path.Length - 1];
266                Array.Copy(path, newPath, path.Length - 1);
267                path = newPath;
268            }
269
270            MemberResult[] modules = new MemberResult[0];
271            if (path.Length == 0) {
272                if (analysis != null) {
273                    modules = analysis.ProjectState.GetModules();
274                }
275
276#if REPL
277                var repl = Intellisense.GetRepl(_textBuffer);
278                if (repl != null) {
279                    modules = Intellisense.MergeMembers(modules, repl.GetModules());
280                }
281#endif
282            } else {
283                if (analysis != null) {
284                    modules = analysis.ProjectState.GetModuleMembers(path);
285                }
286            }
287
288            var sortedAndFiltered = NormalCompletionAnalysis.FilterCompletions(modules, text, (x, y) => x.StartsWith(y));
289            Array.Sort(sortedAndFiltered, NormalCompletionAnalysis.ModuleSort);
290
291            var result = new Completion[sortedAndFiltered.Length];
292            for (int i = 0; i < sortedAndFiltered.Length; i++) {
293                result[i] = PythonCompletion(glyphService, sortedAndFiltered[i]);
294            }
295            return result;
296        }
297
298        public override string ToString() {
299            return String.Format("CompletionContext({0}): {1} @{2}", GetType().Name, Text, _pos);
300        }
301    }
302}