~halega/+junk/sharpdevelop

« back to all changes in this revision

Viewing changes to src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs

  • Committer: sk
  • Date: 2011-09-10 05:17:57 UTC
  • Revision ID: halega@halega.com-20110910051757-qfouz1llya9m6boy
4.1.0.7915 Release Candidate 1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
 
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
 
3
 
 
4
using System;
 
5
using ICSharpCode.Core;
 
6
using ICSharpCode.NRefactory;
 
7
using ICSharpCode.NRefactory.Visitors;
 
8
using ICSharpCode.SharpDevelop;
 
9
using ICSharpCode.SharpDevelop.Dom;
 
10
using ICSharpCode.SharpDevelop.Dom.CSharp;
 
11
using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver;
 
12
using ICSharpCode.SharpDevelop.Dom.Refactoring;
 
13
using ICSharpCode.SharpDevelop.Editor;
 
14
using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
 
15
using AST = ICSharpCode.NRefactory.Ast;
 
16
 
 
17
namespace CSharpBinding
 
18
{
 
19
        public class CSharpCompletionBinding : NRefactoryCodeCompletionBinding
 
20
        {
 
21
                public CSharpCompletionBinding() : base(SupportedLanguage.CSharp)
 
22
                {
 
23
                }
 
24
                
 
25
                static CSharpExpressionFinder CreateExpressionFinder(string fileName)
 
26
                {
 
27
                        return new CSharpExpressionFinder(ParserService.GetParseInformation(fileName));
 
28
                }
 
29
                
 
30
                public override CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch)
 
31
                {
 
32
                        CSharpExpressionFinder ef = CreateExpressionFinder(editor.FileName);
 
33
                        int cursor = editor.Caret.Offset;
 
34
                        if (ch == '[') {
 
35
                                var line = editor.Document.GetLineForOffset(cursor);
 
36
                                /* TODO: AVALONEDIT Reimplement this
 
37
                                if (TextUtilities.FindPrevWordStart(editor.ActiveTextAreaControl.Document, cursor) <= line.Offset) {
 
38
                                        // [ is first character on the line
 
39
                                        // -> Attribute completion
 
40
                                        editor.ShowCompletionWindow(new AttributesDataProvider(ParserService.CurrentProjectContent), ch);
 
41
                                        return true;
 
42
                                }*/
 
43
                        } else if (ch == ',' && CodeCompletionOptions.InsightRefreshOnComma && CodeCompletionOptions.InsightEnabled) {
 
44
                                IInsightWindow insightWindow;
 
45
                                if (insightHandler.InsightRefreshOnComma(editor, ch, out insightWindow)) {
 
46
                                        return CodeCompletionKeyPressResult.Completed;
 
47
                                }
 
48
                        } else if(ch == '=') {
 
49
                                var curLine = editor.Document.GetLineForOffset(cursor);
 
50
                                string documentText = editor.Document.Text;
 
51
                                int position = editor.Caret.Offset - 2;
 
52
                                
 
53
                                if (position > 0 && (documentText[position + 1] == '+')) {
 
54
                                        ExpressionResult result = ef.FindFullExpression(documentText, position);
 
55
                                        
 
56
                                        if(result.Expression != null) {
 
57
                                                ResolveResult resolveResult = ParserService.Resolve(result, editor.Caret.Line, editor.Caret.Column, editor.FileName, documentText);
 
58
                                                if (resolveResult != null && resolveResult.ResolvedType != null) {
 
59
                                                        IClass underlyingClass = resolveResult.ResolvedType.GetUnderlyingClass();
 
60
                                                        if (underlyingClass != null && underlyingClass.IsTypeInInheritanceTree(ParserService.CurrentProjectContent.GetClass("System.MulticastDelegate", 0))) {
 
61
                                                                EventHandlerCompletionItemProvider eventHandlerProvider = new EventHandlerCompletionItemProvider(result.Expression, resolveResult);
 
62
                                                                eventHandlerProvider.ShowCompletion(editor);
 
63
                                                                return CodeCompletionKeyPressResult.Completed;
 
64
                                                        }
 
65
                                                }
 
66
                                        }
 
67
                                } else if (position > 0) {
 
68
                                        ExpressionResult result = ef.FindFullExpression(documentText, position);
 
69
                                        
 
70
                                        if(result.Expression != null) {
 
71
                                                ResolveResult resolveResult = ParserService.Resolve(result, editor.Caret.Line, editor.Caret.Column, editor.FileName, documentText);
 
72
                                                if (resolveResult != null && resolveResult.ResolvedType != null) {
 
73
                                                        if (ProvideContextCompletion(editor, resolveResult.ResolvedType, ch)) {
 
74
                                                                return CodeCompletionKeyPressResult.Completed;
 
75
                                                        }
 
76
                                                }
 
77
                                        }
 
78
                                }
 
79
                        } else if (ch == '.') {
 
80
                                new CSharpCodeCompletionDataProvider().ShowCompletion(editor);
 
81
                                return CodeCompletionKeyPressResult.Completed;
 
82
                        } else if (ch == '>') {
 
83
                                if (IsInComment(editor)) return CodeCompletionKeyPressResult.None;
 
84
                                char prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' ';
 
85
                                if (prevChar == '-') {
 
86
                                        new PointerArrowCompletionDataProvider().ShowCompletion(editor);
 
87
                                        
 
88
                                        return CodeCompletionKeyPressResult.Completed;
 
89
                                }
 
90
                        }
 
91
                        
 
92
                        if (char.IsLetter(ch) && CodeCompletionOptions.CompleteWhenTyping) {
 
93
                                if (editor.SelectionLength > 0) {
 
94
                                        // allow code completion when overwriting an identifier
 
95
                                        int endOffset = editor.SelectionStart + editor.SelectionLength;
 
96
                                        // but block code completion when overwriting only part of an identifier
 
97
                                        if (endOffset < editor.Document.TextLength && char.IsLetterOrDigit(editor.Document.GetCharAt(endOffset)))
 
98
                                                return CodeCompletionKeyPressResult.None;
 
99
                                        
 
100
                                        editor.Document.Remove(editor.SelectionStart, editor.SelectionLength);
 
101
                                        // Read caret position again after removal - this is required because the document might change in other
 
102
                                        // locations, too (e.g. bound elements in snippets).
 
103
                                        cursor = editor.Caret.Offset;
 
104
                                }
 
105
                                char prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' ';
 
106
                                bool afterUnderscore = prevChar == '_';
 
107
                                if (afterUnderscore) {
 
108
                                        cursor--;
 
109
                                        prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' ';
 
110
                                }
 
111
                                if (!char.IsLetterOrDigit(prevChar) && prevChar != '.' && !IsInComment(editor)) {
 
112
                                        ExpressionResult result = ef.FindExpression(editor.Document.Text, cursor);
 
113
                                        LoggingService.Debug("CC: Beginning to type a word, result=" + result);
 
114
                                        if (result.Context != ExpressionContext.IdentifierExpected) {
 
115
                                                var ctrlSpaceProvider = new NRefactoryCtrlSpaceCompletionItemProvider(LanguageProperties.CSharp, result.Context);
 
116
                                                ctrlSpaceProvider.ShowTemplates = true;
 
117
                                                ctrlSpaceProvider.AllowCompleteExistingExpression = afterUnderscore;
 
118
                                                ctrlSpaceProvider.ShowCompletion(editor);
 
119
                                                return CodeCompletionKeyPressResult.CompletedIncludeKeyInCompletion;
 
120
                                        }
 
121
                                }
 
122
                        }
 
123
                        
 
124
                        return base.HandleKeyPress(editor, ch);
 
125
                }
 
126
                
 
127
                class CSharpCodeCompletionDataProvider : DotCodeCompletionItemProvider
 
128
                {
 
129
                        public override ResolveResult Resolve(ITextEditor editor, ExpressionResult expressionResult)
 
130
                        {
 
131
                                // bypass ParserService.Resolve and set resolver.LimitMethodExtractionUntilCaretLine
 
132
                                ParseInformation parseInfo = ParserService.GetParseInformation(editor.FileName);
 
133
                                NRefactoryResolver resolver = new NRefactoryResolver(LanguageProperties.CSharp);
 
134
                                resolver.LimitMethodExtractionUntilLine = editor.Caret.Line;
 
135
                                return resolver.Resolve(expressionResult, parseInfo, editor.Document.Text);
 
136
                        }
 
137
                }
 
138
                
 
139
                class PointerArrowCompletionDataProvider : DotCodeCompletionItemProvider
 
140
                {
 
141
                        public override ResolveResult Resolve(ITextEditor editor, ExpressionResult expressionResult)
 
142
                        {
 
143
                                ResolveResult rr = base.Resolve(editor, expressionResult);
 
144
                                if (rr != null && rr.ResolvedType != null) {
 
145
                                        PointerReturnType prt = rr.ResolvedType.CastToDecoratingReturnType<PointerReturnType>();
 
146
                                        if (prt != null)
 
147
                                                return new ResolveResult(rr.CallingClass, rr.CallingMember, prt.BaseType);
 
148
                                }
 
149
                                return null;
 
150
                        }
 
151
                        
 
152
                        public override ExpressionResult GetExpression(ITextEditor editor)
 
153
                        {
 
154
                                // - 1 because the "-" is already inserted (the ">" is about to be inserted)
 
155
                                return GetExpressionFromOffset(editor, editor.Caret.Offset - 1);
 
156
                        }
 
157
                }
 
158
                
 
159
                bool IsInComment(ITextEditor editor)
 
160
                {
 
161
                        CSharpExpressionFinder ef = CreateExpressionFinder(editor.FileName);
 
162
                        int cursor = editor.Caret.Offset - 1;
 
163
                        return ef.FilterComments(editor.Document.GetText(0, cursor + 1), ref cursor) == null;
 
164
                }
 
165
                
 
166
                public override bool HandleKeyword(ITextEditor editor, string word)
 
167
                {
 
168
                        switch (word) {
 
169
                                case "using":
 
170
                                        if (IsInComment(editor)) return false;
 
171
                                        
 
172
                                        ParseInformation parseInfo = ParserService.GetParseInformation(editor.FileName);
 
173
                                        if (parseInfo != null) {
 
174
                                                IClass innerMostClass = parseInfo.CompilationUnit.GetInnermostClass(editor.Caret.Line, editor.Caret.Column);
 
175
                                                if (innerMostClass == null) {
 
176
                                                        new NRefactoryCtrlSpaceCompletionItemProvider(LanguageProperties.CSharp, ExpressionContext.Namespace).ShowCompletion(editor);
 
177
                                                        return true;
 
178
                                                }
 
179
                                        }
 
180
                                        break;
 
181
                                case "as":
 
182
                                case "is":
 
183
                                        if (IsInComment(editor)) return false;
 
184
                                        new NRefactoryCtrlSpaceCompletionItemProvider(LanguageProperties.CSharp, ExpressionContext.Type).ShowCompletion(editor);
 
185
                                        return true;
 
186
                                case "override":
 
187
                                        if (IsInComment(editor)) return false;
 
188
                                        new OverrideCompletionItemProvider().ShowCompletion(editor);
 
189
                                        return true;
 
190
                                case "new":
 
191
                                        return ShowNewCompletion(editor);
 
192
                                case "case":
 
193
                                        if (IsInComment(editor)) return false;
 
194
                                        return DoCaseCompletion(editor);
 
195
                                case "return":
 
196
                                        if (IsInComment(editor)) return false;
 
197
                                        IMember m = GetCurrentMember(editor);
 
198
                                        if (m != null) {
 
199
                                                return ProvideContextCompletion(editor, m.ReturnType, ' ');
 
200
                                        }
 
201
                                        break;
 
202
                        }
 
203
                        return base.HandleKeyword(editor, word);
 
204
                }
 
205
                
 
206
                bool ShowNewCompletion(ITextEditor editor)
 
207
                {
 
208
                        CSharpExpressionFinder ef = CreateExpressionFinder(editor.FileName);
 
209
                        int cursor = editor.Caret.Offset;
 
210
                        string documentToCursor = editor.Document.GetText(0, cursor);
 
211
                        ExpressionResult expressionResult = ef.FindExpression(documentToCursor, cursor);
 
212
                        
 
213
                        LoggingService.Debug("ShowNewCompletion: expression is " + expressionResult);
 
214
                        if (expressionResult.Context.IsObjectCreation) {
 
215
                                var currentLine = editor.Document.GetLineForOffset(cursor);
 
216
                                string lineText = editor.Document.GetText(currentLine.Offset, cursor - currentLine.Offset);
 
217
                                // when the new follows an assignment, improve code-completion by detecting the
 
218
                                // type of the variable that is assigned to
 
219
                                if (lineText.Replace(" ", "").EndsWith("=new")) {
 
220
                                        int pos = lineText.LastIndexOf('=');
 
221
                                        ExpressionContext context = FindExactContextForNewCompletion(editor, documentToCursor,
 
222
                                                                                                     currentLine, pos);
 
223
                                        if (context != null)
 
224
                                                expressionResult.Context = context;
 
225
                                }
 
226
                                new NRefactoryCtrlSpaceCompletionItemProvider(LanguageProperties.CSharp, expressionResult.Context).ShowCompletion(editor);
 
227
                                return true;
 
228
                        }
 
229
                        return false;
 
230
                }
 
231
                
 
232
                ExpressionContext FindExactContextForNewCompletion(ITextEditor editor, string documentToCursor,
 
233
                                                                   IDocumentLine currentLine, int pos)
 
234
                {
 
235
                        CSharpExpressionFinder ef = CreateExpressionFinder(editor.FileName);
 
236
                        // find expression on left hand side of the assignment
 
237
                        ExpressionResult lhsExpr = ef.FindExpression(documentToCursor, currentLine.Offset + pos);
 
238
                        if (lhsExpr.Expression != null) {
 
239
                                ResolveResult rr = ParserService.Resolve(lhsExpr, currentLine.LineNumber, pos, editor.FileName, editor.Document.Text);
 
240
                                if (rr != null && rr.ResolvedType != null) {
 
241
                                        ExpressionContext context;
 
242
                                        IClass c;
 
243
                                        if (rr.ResolvedType.IsArrayReturnType) {
 
244
                                                // when creating an array, all classes deriving from the array's element type are allowed
 
245
                                                IReturnType elementType = rr.ResolvedType.CastToArrayReturnType().ArrayElementType;
 
246
                                                c = elementType != null ? elementType.GetUnderlyingClass() : null;
 
247
                                                context = ExpressionContext.TypeDerivingFrom(elementType, false);
 
248
                                        } else {
 
249
                                                // when creating a normal instance, all non-abstract classes deriving from the type
 
250
                                                // are allowed
 
251
                                                c = rr.ResolvedType.GetUnderlyingClass();
 
252
                                                context = ExpressionContext.TypeDerivingFrom(rr.ResolvedType, true);
 
253
                                        }
 
254
                                        if (c != null && context.ShowEntry(c)) {
 
255
                                                // Try to suggest an entry (List<int> a = new => suggest List<int>).
 
256
                                                
 
257
                                                string suggestedClassName = LanguageProperties.CSharp.CodeGenerator.GenerateCode(
 
258
                                                        CodeGenerator.ConvertType(
 
259
                                                                rr.ResolvedType,
 
260
                                                                new ClassFinder(ParserService.GetParseInformation(editor.FileName), editor.Caret.Line, editor.Caret.Column)
 
261
                                                        ), "");
 
262
                                                if (suggestedClassName != c.Name) {
 
263
                                                        // create a special code completion item that completes also the type arguments
 
264
                                                        context.SuggestedItem = new SuggestedCodeCompletionItem(c, suggestedClassName);
 
265
                                                } else {
 
266
                                                        context.SuggestedItem = new CodeCompletionItem(c);
 
267
                                                }
 
268
                                        }
 
269
                                        return context;
 
270
                                }
 
271
                        }
 
272
                        return null;
 
273
                }
 
274
                
 
275
                #region "case"-keyword completion
 
276
                bool DoCaseCompletion(ITextEditor editor)
 
277
                {
 
278
                        ITextEditorCaret caret = editor.Caret;
 
279
                        NRefactoryResolver r = new NRefactoryResolver(LanguageProperties.CSharp);
 
280
                        if (r.Initialize(ParserService.GetParseInformation(editor.FileName), caret.Line, caret.Column)) {
 
281
                                AST.INode currentMember = r.ParseCurrentMember(editor.Document.Text);
 
282
                                if (currentMember != null) {
 
283
                                        CaseCompletionSwitchFinder ccsf = new CaseCompletionSwitchFinder(caret.Line, caret.Column);
 
284
                                        currentMember.AcceptVisitor(ccsf, null);
 
285
                                        if (ccsf.bestStatement != null) {
 
286
                                                r.RunLookupTableVisitor(currentMember);
 
287
                                                ResolveResult rr = r.ResolveInternal(ccsf.bestStatement.SwitchExpression, ExpressionContext.Default);
 
288
                                                if (rr != null && rr.ResolvedType != null) {
 
289
                                                        return ProvideContextCompletion(editor, rr.ResolvedType, ' ');
 
290
                                                }
 
291
                                        }
 
292
                                }
 
293
                        }
 
294
                        return false;
 
295
                }
 
296
                
 
297
                private class CaseCompletionSwitchFinder : AbstractAstVisitor
 
298
                {
 
299
                        Location caretLocation;
 
300
                        internal AST.SwitchStatement bestStatement;
 
301
                        
 
302
                        public CaseCompletionSwitchFinder(int caretLine, int caretColumn)
 
303
                        {
 
304
                                caretLocation = new Location(caretColumn, caretLine);
 
305
                        }
 
306
                        
 
307
                        public override object VisitSwitchStatement(AST.SwitchStatement switchStatement, object data)
 
308
                        {
 
309
                                if (switchStatement.StartLocation < caretLocation && caretLocation < switchStatement.EndLocation) {
 
310
                                        bestStatement = switchStatement;
 
311
                                }
 
312
                                return base.VisitSwitchStatement(switchStatement, data);
 
313
                        }
 
314
                }
 
315
                #endregion
 
316
        }
 
317
}