2
// TextEditorResolverProvider.cs
5
// Mike Krüger <mkrueger@novell.com>
7
// Copyright (c) 2010 Novell, Inc (http://www.novell.com)
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:
16
// The above copyright notice and this permission notice shall be included in
17
// all copies or substantial portions of the Software.
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
27
using MonoDevelop.Ide.Gui.Content;
28
using MonoDevelop.Core;
29
using MonoDevelop.Projects.Dom.Parser;
30
using MonoDevelop.Projects.Dom;
31
using Mono.TextEditor;
33
using MonoDevelop.Ide.Gui;
34
using MonoDevelop.Projects.Dom.Output;
35
using MonoDevelop.Ide;
37
namespace MonoDevelop.CSharp.Resolver
39
public class TextEditorResolverProvider : ITextEditorResolverProvider
41
#region ITextEditorResolverProvider implementation
43
public MonoDevelop.Projects.Dom.ResolveResult GetLanguageItem (ProjectDom dom, Mono.TextEditor.TextEditorData data, int offset)
45
string fileName = data.Document.FileName;
47
IParser parser = ProjectDomService.GetParser (fileName, data.Document.MimeType);
51
MonoDevelop.Ide.Gui.Document doc = IdeApp.Workbench.ActiveDocument;
55
IResolver resolver = parser.CreateResolver (dom, doc, fileName);
56
IExpressionFinder expressionFinder = parser.CreateExpressionFinder (dom);
57
if (resolver == null || expressionFinder == null)
60
string txt = data.Document.Text;
62
while (wordEnd < txt.Length && (Char.IsLetterOrDigit (txt[wordEnd]) || txt[wordEnd] == '_'))
65
ExpressionResult expressionResult = expressionFinder.FindExpression (txt, wordEnd);
66
if (expressionResult == null)
68
ResolveResult resolveResult;
69
DocumentLocation loc = data.Document.OffsetToLocation (offset);
70
string savedExpression = null;
72
// special handling for 'var' "keyword"
73
if (expressionResult.ExpressionContext == ExpressionContext.IdentifierExpected && expressionResult.Expression != null && expressionResult.Expression.Trim () == "var") {
74
int endOffset = data.Document.LocationToOffset (expressionResult.Region.End.Line - 1, expressionResult.Region.End.Column - 1);
75
StringBuilder identifer = new StringBuilder ();
76
for (int i = endOffset; i >= 0 && i < data.Document.Length; i++) {
77
char ch = data.Document.GetCharAt (i);
78
if (Char.IsWhiteSpace (ch))
82
if (Char.IsLetterOrDigit (ch) || ch =='_') {
83
identifer.Append (ch);
89
if (identifer.Length > 0) {
90
expressionResult.Expression = identifer.ToString ();
91
resolveResult = resolver.Resolve (expressionResult, new DomLocation (loc.Line + 1, loc.Column + 1));
92
if (resolveResult != null) {
93
resolveResult = new MemberResolveResult (dom.GetType (resolveResult.ResolvedType));
99
if (expressionResult.ExpressionContext == ExpressionContext.Attribute) {
100
savedExpression = expressionResult.Expression;
101
expressionResult.Expression += "Attribute";
102
expressionResult.ExpressionContext = ExpressionContext.ObjectCreation;
104
resolveResult = resolver.Resolve (expressionResult, new DomLocation (loc.Line + 1, loc.Column + 1));
106
if (savedExpression != null && resolveResult == null) {
107
expressionResult.Expression = savedExpression;
108
resolveResult = resolver.Resolve (expressionResult, new DomLocation (loc.Line + 1, loc.Column + 1));
110
// Search for possible generic parameters.
111
// if (this.resolveResult == null || this.resolveResult.ResolvedType == null || String.IsNullOrEmpty (this.resolveResult.ResolvedType.Name)) {
112
if (!expressionResult.Region.IsEmpty) {
113
int j = data.Document.LocationToOffset (expressionResult.Region.End.Line - 1, expressionResult.Region.End.Column - 1);
115
for (int i = j; i >= 0 && i < data.Document.Length; i++) {
116
char ch = data.Document.GetCharAt (i);
117
if (Char.IsWhiteSpace (ch))
121
} else if (ch == '>') {
124
expressionResult.Expression += data.Document.GetTextBetween (j, i + 1);
125
expressionResult.ExpressionContext = ExpressionContext.ObjectCreation;
126
resolveResult = resolver.Resolve (expressionResult, new DomLocation (loc.Line + 1, loc.Column + 1));
136
// To resolve method overloads the full expression must be parsed.
137
// ex.: Overload (1)/ Overload("one") - parsing "Overload" gives just a MethodResolveResult
138
// and for constructor initializers it's tried too to to resolve constructor overloads.
139
if (resolveResult is ThisResolveResult ||
140
resolveResult is BaseResolveResult ||
141
resolveResult is MethodResolveResult && ((MethodResolveResult)resolveResult).Methods.Count > 1) {
142
// put the search offset at the end of the invocation to be able to find the full expression
143
// the resolver finds it itself if spaces are between the method name and the argument opening parentheses.
144
while (wordEnd < txt.Length - 1 && Char.IsWhiteSpace (txt[wordEnd]))
146
if (txt[wordEnd] == '(') {
147
int matchingBracket = data.Document.GetMatchingBracketOffset (wordEnd);
148
if (matchingBracket > 0)
149
wordEnd = matchingBracket;
151
//Console.WriteLine (expressionFinder.FindFullExpression (txt, wordEnd));
152
ResolveResult possibleResult = resolver.Resolve (expressionFinder.FindFullExpression (txt, wordEnd), new DomLocation (loc.Line + 1, loc.Column + 1)) ?? resolveResult;
153
//Console.WriteLine ("possi:" + resolver.Resolve (expressionFinder.FindFullExpression (txt, wordEnd), new DomLocation (loc.Line + 1, loc.Column + 1)));
154
if (possibleResult is MethodResolveResult)
155
resolveResult = possibleResult;
157
return resolveResult;
160
public MonoDevelop.Projects.Dom.ResolveResult GetLanguageItem (ProjectDom dom, Mono.TextEditor.TextEditorData data, int offset, string expression)
162
string fileName = data.Document.FileName;
163
MonoDevelop.Ide.Gui.Document doc = IdeApp.Workbench.ActiveDocument;
167
IParser parser = ProjectDomService.GetParser (fileName, data.Document.MimeType);
171
IResolver resolver = parser.CreateResolver (dom, doc, fileName);
172
IExpressionFinder expressionFinder = parser.CreateExpressionFinder (dom);
173
if (resolver == null || expressionFinder == null)
175
string txt = data.Document.Text;
176
int wordEnd = offset;
177
while (wordEnd < txt.Length && (Char.IsLetterOrDigit (txt[wordEnd]) || txt[wordEnd] == '_'))
179
ExpressionResult expressionResult = new ExpressionResult (expression);
180
expressionResult.ExpressionContext = ExpressionContext.MethodBody;
182
DocumentLocation loc = data.Document.OffsetToLocation (offset);
183
string savedExpression = null;
184
ResolveResult resolveResult;
186
if (expressionResult.ExpressionContext == ExpressionContext.Attribute) {
187
savedExpression = expressionResult.Expression;
188
expressionResult.Expression += "Attribute";
189
expressionResult.ExpressionContext = ExpressionContext.ObjectCreation;
191
resolveResult = resolver.Resolve (expressionResult, new DomLocation (loc.Line + 1, loc.Column + 1));
192
if (savedExpression != null && resolveResult == null) {
193
expressionResult.Expression = savedExpression;
194
resolveResult = resolver.Resolve (expressionResult, new DomLocation (loc.Line + 1, loc.Column + 1));
196
// Search for possible generic parameters.
197
// if (this.resolveResult == null || this.resolveResult.ResolvedType == null || String.IsNullOrEmpty (this.resolveResult.ResolvedType.Name)) {
198
int j = data.Document.LocationToOffset (expressionResult.Region.End.Line - 1, expressionResult.Region.End.Column - 1);
200
for (int i = j; i >= 0 && i < data.Document.Length; i++) {
201
char ch = data.Document.GetCharAt (i);
202
if (Char.IsWhiteSpace (ch))
206
} else if (ch == '>') {
209
expressionResult.Expression += data.Document.GetTextBetween (j, i + 1);
210
expressionResult.ExpressionContext = ExpressionContext.ObjectCreation;
211
resolveResult = resolver.Resolve (expressionResult, new DomLocation (loc.Line + 1, loc.Column + 1));
221
// To resolve method overloads the full expression must be parsed.
222
// ex.: Overload (1)/ Overload("one") - parsing "Overload" gives just a MethodResolveResult
223
if (resolveResult is MethodResolveResult)
224
resolveResult = resolver.Resolve (expressionFinder.FindFullExpression (txt, wordEnd), new DomLocation (loc.Line + 1, loc.Column + 1)) ?? resolveResult;
225
return resolveResult;
229
static string paramStr = GettextCatalog.GetString ("Parameter");
230
static string localStr = GettextCatalog.GetString ("Local variable");
231
static string fieldStr = GettextCatalog.GetString ("Field");
232
static string propertyStr = GettextCatalog.GetString ("Property");
233
static string methodStr = GettextCatalog.GetString ("Method");
234
static string typeStr = GettextCatalog.GetString ("Type");
235
static string namespaceStr = GettextCatalog.GetString ("Namespace");
237
public string CreateTooltip (ProjectDom dom, ICompilationUnit unit, MonoDevelop.Projects.Dom.ResolveResult result, string errorInformations, Ambience ambience, Gdk.ModifierType modifierState)
239
OutputSettings settings = new OutputSettings (OutputFlags.ClassBrowserEntries | OutputFlags.IncludeParameterName | OutputFlags.IncludeKeywords | OutputFlags.IncludeMarkup | OutputFlags.UseFullName);
240
if ((Gdk.ModifierType.ShiftMask & modifierState) == Gdk.ModifierType.ShiftMask) {
241
settings.EmitNameCallback = delegate(INode domVisitable, ref string outString) {
242
// crop used namespaces.
245
foreach (IUsing u in unit.Usings) {
246
foreach (string ns in u.Namespaces) {
247
if (outString.StartsWith (ns + ".")) {
248
len = Math.Max (len, ns.Length + 1);
252
string newName = outString.Substring (len);
254
// check if there is a name clash.
255
if (dom.GetType (newName) != null)
257
foreach (IUsing u in unit.Usings) {
258
foreach (string ns in u.Namespaces) {
259
if (dom.GetType (ns + "." + newName) != null)
264
if (len > 0 && count == 1)
270
// Approximate value for usual case
271
StringBuilder s = new StringBuilder (150);
273
if (result != null) {
274
if (result is AggregatedResolveResult)
275
result = ((AggregatedResolveResult)result).PrimaryResult;
276
if (result is ParameterResolveResult) {
277
s.Append ("<small><i>");
279
s.Append ("</i></small>\n");
280
s.Append (ambience.GetString (((ParameterResolveResult)result).Parameter, settings));
281
} else if (result is LocalVariableResolveResult) {
282
s.Append ("<small><i>");
284
s.Append ("</i></small>\n");
285
s.Append (ambience.GetString (((LocalVariableResolveResult)result).ResolvedType, settings));
287
s.Append (((LocalVariableResolveResult)result).LocalVariable.Name);
288
} else if (result is UnresolvedMemberResolveResult) {
289
s.Append (String.Format (GettextCatalog.GetString ("Unresolved member '{0}'"), ((UnresolvedMemberResolveResult)result).MemberName));
290
} else if (result is MethodResolveResult) {
291
MethodResolveResult mrr = (MethodResolveResult)result;
292
s.Append("<small><i>");
294
s.Append("</i></small>\n");
295
s.Append(ambience.GetString(mrr.MostLikelyMethod, settings));
296
if (mrr.Methods.Count > 1) {
297
int overloadCount = mrr.Methods.Count - 1;
298
s.Append(string.Format(GettextCatalog.GetPluralString(" (+{0} overload)", " (+{0} overloads)", overloadCount), overloadCount));
300
doc = AmbienceService.GetDocumentationSummary(((MethodResolveResult)result).MostLikelyMethod);
301
} else if (result is MemberResolveResult) {
302
IMember member = ((MemberResolveResult)result).ResolvedMember;
303
if (member == null) {
304
IReturnType returnType = ((MemberResolveResult)result).ResolvedType;
305
if (returnType != null) {
306
IType type = dom.GetType (returnType);
308
s.Append ("<small><i>");
310
s.Append ("</i></small>\n");
311
s.Append (ambience.GetString (type, settings));
312
doc = AmbienceService.GetDocumentationSummary (type);
316
if (member is IField) {
317
s.Append ("<small><i>");
319
s.Append ("</i></small>\n");
320
} else if (member is IProperty) {
321
s.Append ("<small><i>");
322
s.Append (propertyStr);
323
s.Append ("</i></small>\n");
325
s.Append (ambience.GetString (member, settings));
326
doc = AmbienceService.GetDocumentationSummary (member);
328
} else if (result is NamespaceResolveResult) {
329
s.Append ("<small><i>");
330
s.Append (namespaceStr);
331
s.Append ("</i></small>\n");
332
s.Append (ambience.GetString (new Namespace (((NamespaceResolveResult)result).Namespace), settings));
334
s.Append (ambience.GetString (result.ResolvedType, settings));
338
if (!string.IsNullOrEmpty (doc)) {
339
s.Append ("\n<small>");
340
s.Append (AmbienceService.GetDocumentationMarkup ( "<summary>" + doc + "</summary>"));
341
s.Append ("</small>");
345
if (!string.IsNullOrEmpty (errorInformations)) {
348
s.Append ("<small>");
349
s.Append (errorInformations);
350
s.Append ("</small>");
352
return s.ToString ();