7 changed files with 192 additions and 428 deletions
@ -1,313 +1,33 @@
@@ -1,313 +1,33 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
/* |
||||
using System; |
||||
using ICSharpCode.Core; |
||||
using ICSharpCode.NRefactory; |
||||
using ICSharpCode.NRefactory.Visitors; |
||||
using ICSharpCode.SharpDevelop; |
||||
using ICSharpCode.SharpDevelop.Dom; |
||||
using ICSharpCode.SharpDevelop.Dom.CSharp; |
||||
using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver; |
||||
using ICSharpCode.SharpDevelop.Dom.Refactoring; |
||||
using ICSharpCode.NRefactory.Completion; |
||||
using ICSharpCode.NRefactory.CSharp.Completion; |
||||
using ICSharpCode.SharpDevelop.Editor; |
||||
using ICSharpCode.SharpDevelop.Editor.CodeCompletion; |
||||
using AST = ICSharpCode.NRefactory.Ast; |
||||
|
||||
namespace CSharpBinding |
||||
{ |
||||
public class CSharpCompletionBinding : NRefactoryCodeCompletionBinding |
||||
public class CSharpCompletionBinding : ICodeCompletionBinding |
||||
{ |
||||
public CSharpCompletionBinding() : base(SupportedLanguage.CSharp) |
||||
public CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) |
||||
{ |
||||
return CodeCompletionKeyPressResult.None; |
||||
} |
||||
|
||||
static CSharpExpressionFinder CreateExpressionFinder(string fileName) |
||||
public bool HandleKeyPressed(ITextEditor editor, char ch) |
||||
{ |
||||
return new CSharpExpressionFinder(ParserService.GetParseInformation(fileName)); |
||||
if (ch == '.') |
||||
return CtrlSpace(editor); |
||||
else |
||||
return false; |
||||
} |
||||
|
||||
public override CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) |
||||
public bool CtrlSpace(ITextEditor editor) |
||||
{ |
||||
CSharpExpressionFinder ef = CreateExpressionFinder(editor.FileName); |
||||
int cursor = editor.Caret.Offset; |
||||
if (ch == '[') { |
||||
var line = editor.Document.GetLineForOffset(cursor); |
||||
|
||||
} else if (ch == ',' && CodeCompletionOptions.InsightRefreshOnComma && CodeCompletionOptions.InsightEnabled) { |
||||
IInsightWindow insightWindow; |
||||
if (insightHandler.InsightRefreshOnComma(editor, ch, out insightWindow)) { |
||||
return CodeCompletionKeyPressResult.Completed; |
||||
} |
||||
} else if(ch == '=') { |
||||
var curLine = editor.Document.GetLineForOffset(cursor); |
||||
string documentText = editor.Document.Text; |
||||
int position = editor.Caret.Offset - 2; |
||||
|
||||
if (position > 0 && (documentText[position + 1] == '+')) { |
||||
ExpressionResult result = ef.FindFullExpression(documentText, position); |
||||
|
||||
if(result.Expression != null) { |
||||
ResolveResult resolveResult = ParserService.Resolve(result, editor.Caret.Line, editor.Caret.Column, editor.FileName, documentText); |
||||
if (resolveResult != null && resolveResult.ResolvedType != null) { |
||||
IClass underlyingClass = resolveResult.ResolvedType.GetUnderlyingClass(); |
||||
if (underlyingClass != null && underlyingClass.IsTypeInInheritanceTree(ParserService.CurrentProjectContent.GetClass("System.MulticastDelegate", 0))) { |
||||
EventHandlerCompletionItemProvider eventHandlerProvider = new EventHandlerCompletionItemProvider(result.Expression, resolveResult); |
||||
eventHandlerProvider.ShowCompletion(editor); |
||||
return CodeCompletionKeyPressResult.Completed; |
||||
} |
||||
} |
||||
} |
||||
} else if (position > 0) { |
||||
ExpressionResult result = ef.FindFullExpression(documentText, position); |
||||
|
||||
if(result.Expression != null) { |
||||
ResolveResult resolveResult = ParserService.Resolve(result, editor.Caret.Line, editor.Caret.Column, editor.FileName, documentText); |
||||
if (resolveResult != null && resolveResult.ResolvedType != null) { |
||||
if (ProvideContextCompletion(editor, resolveResult.ResolvedType, ch)) { |
||||
return CodeCompletionKeyPressResult.Completed; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} else if (ch == '.') { |
||||
new CSharpCodeCompletionDataProvider().ShowCompletion(editor); |
||||
return CodeCompletionKeyPressResult.Completed; |
||||
} else if (ch == '>') { |
||||
if (IsInComment(editor)) return CodeCompletionKeyPressResult.None; |
||||
char prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' '; |
||||
if (prevChar == '-') { |
||||
new PointerArrowCompletionDataProvider().ShowCompletion(editor); |
||||
|
||||
return CodeCompletionKeyPressResult.Completed; |
||||
} |
||||
} |
||||
|
||||
if (char.IsLetter(ch) && CodeCompletionOptions.CompleteWhenTyping) { |
||||
if (editor.SelectionLength > 0) { |
||||
// allow code completion when overwriting an identifier
|
||||
int endOffset = editor.SelectionStart + editor.SelectionLength; |
||||
// but block code completion when overwriting only part of an identifier
|
||||
if (endOffset < editor.Document.TextLength && char.IsLetterOrDigit(editor.Document.GetCharAt(endOffset))) |
||||
return CodeCompletionKeyPressResult.None; |
||||
|
||||
editor.Document.Remove(editor.SelectionStart, editor.SelectionLength); |
||||
// Read caret position again after removal - this is required because the document might change in other
|
||||
// locations, too (e.g. bound elements in snippets).
|
||||
cursor = editor.Caret.Offset; |
||||
} |
||||
char prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' '; |
||||
bool afterUnderscore = prevChar == '_'; |
||||
if (afterUnderscore) { |
||||
cursor--; |
||||
prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' '; |
||||
} |
||||
if (!char.IsLetterOrDigit(prevChar) && prevChar != '.' && !IsInComment(editor)) { |
||||
ExpressionResult result = ef.FindExpression(editor.Document.Text, cursor); |
||||
LoggingService.Debug("CC: Beginning to type a word, result=" + result); |
||||
if (result.Context != ExpressionContext.IdentifierExpected) { |
||||
var ctrlSpaceProvider = new NRefactoryCtrlSpaceCompletionItemProvider(LanguageProperties.CSharp, result.Context); |
||||
ctrlSpaceProvider.ShowTemplates = true; |
||||
ctrlSpaceProvider.AllowCompleteExistingExpression = afterUnderscore; |
||||
ctrlSpaceProvider.ShowCompletion(editor); |
||||
return CodeCompletionKeyPressResult.CompletedIncludeKeyInCompletion; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return base.HandleKeyPress(editor, ch); |
||||
} |
||||
|
||||
class CSharpCodeCompletionDataProvider : DotCodeCompletionItemProvider |
||||
{ |
||||
public override ResolveResult Resolve(ITextEditor editor, ExpressionResult expressionResult) |
||||
{ |
||||
// bypass ParserService.Resolve and set resolver.LimitMethodExtractionUntilCaretLine
|
||||
ParseInformation parseInfo = ParserService.GetParseInformation(editor.FileName); |
||||
NRefactoryResolver resolver = new NRefactoryResolver(LanguageProperties.CSharp); |
||||
resolver.LimitMethodExtractionUntilLine = editor.Caret.Line; |
||||
return resolver.Resolve(expressionResult, parseInfo, editor.Document.Text); |
||||
} |
||||
} |
||||
|
||||
class PointerArrowCompletionDataProvider : DotCodeCompletionItemProvider |
||||
{ |
||||
public override ResolveResult Resolve(ITextEditor editor, ExpressionResult expressionResult) |
||||
{ |
||||
ResolveResult rr = base.Resolve(editor, expressionResult); |
||||
if (rr != null && rr.ResolvedType != null) { |
||||
PointerReturnType prt = rr.ResolvedType.CastToDecoratingReturnType<PointerReturnType>(); |
||||
if (prt != null) |
||||
return new ResolveResult(rr.CallingClass, rr.CallingMember, prt.BaseType); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public override ExpressionResult GetExpression(ITextEditor editor) |
||||
{ |
||||
// - 1 because the "-" is already inserted (the ">" is about to be inserted)
|
||||
return GetExpressionFromOffset(editor, editor.Caret.Offset - 1); |
||||
} |
||||
} |
||||
|
||||
bool IsInComment(ITextEditor editor) |
||||
{ |
||||
CSharpExpressionFinder ef = CreateExpressionFinder(editor.FileName); |
||||
int cursor = editor.Caret.Offset - 1; |
||||
return ef.FilterComments(editor.Document.GetText(0, cursor + 1), ref cursor) == null; |
||||
} |
||||
|
||||
public override bool HandleKeyword(ITextEditor editor, string word) |
||||
{ |
||||
switch (word) { |
||||
case "using": |
||||
if (IsInComment(editor)) return false; |
||||
|
||||
ParseInformation parseInfo = ParserService.GetParseInformation(editor.FileName); |
||||
if (parseInfo != null) { |
||||
IClass innerMostClass = parseInfo.CompilationUnit.GetInnermostClass(editor.Caret.Line, editor.Caret.Column); |
||||
if (innerMostClass == null) { |
||||
new NRefactoryCtrlSpaceCompletionItemProvider(LanguageProperties.CSharp, ExpressionContext.Namespace).ShowCompletion(editor); |
||||
return true; |
||||
} |
||||
} |
||||
break; |
||||
case "as": |
||||
case "is": |
||||
if (IsInComment(editor)) return false; |
||||
new NRefactoryCtrlSpaceCompletionItemProvider(LanguageProperties.CSharp, ExpressionContext.Type).ShowCompletion(editor); |
||||
return true; |
||||
case "override": |
||||
if (IsInComment(editor)) return false; |
||||
new OverrideCompletionItemProvider().ShowCompletion(editor); |
||||
return true; |
||||
case "new": |
||||
return ShowNewCompletion(editor); |
||||
case "case": |
||||
if (IsInComment(editor)) return false; |
||||
return DoCaseCompletion(editor); |
||||
case "return": |
||||
if (IsInComment(editor)) return false; |
||||
IMember m = GetCurrentMember(editor); |
||||
if (m != null) { |
||||
return ProvideContextCompletion(editor, m.ReturnType, ' '); |
||||
} |
||||
break; |
||||
} |
||||
return base.HandleKeyword(editor, word); |
||||
} |
||||
|
||||
bool ShowNewCompletion(ITextEditor editor) |
||||
{ |
||||
CSharpExpressionFinder ef = CreateExpressionFinder(editor.FileName); |
||||
int cursor = editor.Caret.Offset; |
||||
string documentToCursor = editor.Document.GetText(0, cursor); |
||||
ExpressionResult expressionResult = ef.FindExpression(documentToCursor, cursor); |
||||
|
||||
LoggingService.Debug("ShowNewCompletion: expression is " + expressionResult); |
||||
if (expressionResult.Context.IsObjectCreation) { |
||||
var currentLine = editor.Document.GetLineForOffset(cursor); |
||||
string lineText = editor.Document.GetText(currentLine.Offset, cursor - currentLine.Offset); |
||||
// when the new follows an assignment, improve code-completion by detecting the
|
||||
// type of the variable that is assigned to
|
||||
if (lineText.Replace(" ", "").EndsWith("=new")) { |
||||
int pos = lineText.LastIndexOf('='); |
||||
ExpressionContext context = FindExactContextForNewCompletion(editor, documentToCursor, |
||||
currentLine, pos); |
||||
if (context != null) |
||||
expressionResult.Context = context; |
||||
} |
||||
new NRefactoryCtrlSpaceCompletionItemProvider(LanguageProperties.CSharp, expressionResult.Context).ShowCompletion(editor); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
ExpressionContext FindExactContextForNewCompletion(ITextEditor editor, string documentToCursor, |
||||
IDocumentLine currentLine, int pos) |
||||
{ |
||||
CSharpExpressionFinder ef = CreateExpressionFinder(editor.FileName); |
||||
// find expression on left hand side of the assignment
|
||||
ExpressionResult lhsExpr = ef.FindExpression(documentToCursor, currentLine.Offset + pos); |
||||
if (lhsExpr.Expression != null) { |
||||
ResolveResult rr = ParserService.Resolve(lhsExpr, currentLine.LineNumber, pos, editor.FileName, editor.Document.Text); |
||||
if (rr != null && rr.ResolvedType != null) { |
||||
ExpressionContext context; |
||||
IClass c; |
||||
if (rr.ResolvedType.IsArrayReturnType) { |
||||
// when creating an array, all classes deriving from the array's element type are allowed
|
||||
IReturnType elementType = rr.ResolvedType.CastToArrayReturnType().ArrayElementType; |
||||
c = elementType != null ? elementType.GetUnderlyingClass() : null; |
||||
context = ExpressionContext.TypeDerivingFrom(elementType, false); |
||||
} else { |
||||
// when creating a normal instance, all non-abstract classes deriving from the type
|
||||
// are allowed
|
||||
c = rr.ResolvedType.GetUnderlyingClass(); |
||||
context = ExpressionContext.TypeDerivingFrom(rr.ResolvedType, true); |
||||
} |
||||
if (c != null && context.ShowEntry(c)) { |
||||
// Try to suggest an entry (List<int> a = new => suggest List<int>).
|
||||
|
||||
string suggestedClassName = LanguageProperties.CSharp.CodeGenerator.GenerateCode( |
||||
CodeGenerator.ConvertType( |
||||
rr.ResolvedType, |
||||
new ClassFinder(ParserService.GetParseInformation(editor.FileName), editor.Caret.Line, editor.Caret.Column) |
||||
), ""); |
||||
if (suggestedClassName != c.Name) { |
||||
// create a special code completion item that completes also the type arguments
|
||||
context.SuggestedItem = new SuggestedCodeCompletionItem(c, suggestedClassName); |
||||
} else { |
||||
context.SuggestedItem = new CodeCompletionItem(c); |
||||
} |
||||
} |
||||
return context; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
#region "case"-keyword completion
|
||||
bool DoCaseCompletion(ITextEditor editor) |
||||
{ |
||||
ITextEditorCaret caret = editor.Caret; |
||||
NRefactoryResolver r = new NRefactoryResolver(LanguageProperties.CSharp); |
||||
if (r.Initialize(ParserService.GetParseInformation(editor.FileName), caret.Line, caret.Column)) { |
||||
AST.INode currentMember = r.ParseCurrentMember(editor.Document.Text); |
||||
if (currentMember != null) { |
||||
CaseCompletionSwitchFinder ccsf = new CaseCompletionSwitchFinder(caret.Line, caret.Column); |
||||
currentMember.AcceptVisitor(ccsf, null); |
||||
if (ccsf.bestStatement != null) { |
||||
r.RunLookupTableVisitor(currentMember); |
||||
ResolveResult rr = r.ResolveInternal(ccsf.bestStatement.SwitchExpression, ExpressionContext.Default); |
||||
if (rr != null && rr.ResolvedType != null) { |
||||
return ProvideContextCompletion(editor, rr.ResolvedType, ' '); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
private class CaseCompletionSwitchFinder : AbstractAstVisitor |
||||
{ |
||||
Location caretLocation; |
||||
internal AST.SwitchStatement bestStatement; |
||||
|
||||
public CaseCompletionSwitchFinder(int caretLine, int caretColumn) |
||||
{ |
||||
caretLocation = new Location(caretColumn, caretLine); |
||||
} |
||||
|
||||
public override object VisitSwitchStatement(AST.SwitchStatement switchStatement, object data) |
||||
{ |
||||
if (switchStatement.StartLocation < caretLocation && caretLocation < switchStatement.EndLocation) { |
||||
bestStatement = switchStatement; |
||||
} |
||||
return base.VisitSwitchStatement(switchStatement, data); |
||||
} |
||||
} |
||||
#endregion
|
||||
} |
||||
} |
||||
*/ |
||||
|
||||
|
Loading…
Reference in new issue