#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

314 lines
13 KiB

// 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.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
using AST = ICSharpCode.NRefactory.Ast;
namespace CSharpBinding
{
public class CSharpCompletionBinding : NRefactoryCodeCompletionBinding
{
public CSharpCompletionBinding() : base(SupportedLanguage.CSharp)
{
}
static CSharpExpressionFinder CreateExpressionFinder(string fileName)
{
return new CSharpExpressionFinder(ParserService.GetParseInformation(fileName));
}
public override CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch)
{
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)) {
insightHandler.HighlightParameter(insightWindow, -1); // disable highlighting
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
}
}
*/