Browse Source

Improved C# code completion (typing constraints on generic classes).

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@2664 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 18 years ago
parent
commit
136722e9b1
  1. 111
      src/Main/Base/Test/CSharpExpressionFinderTests.cs
  2. 146
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/ExpressionFinder.cs
  3. 9
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ExpressionContext.cs
  4. 13
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryResolver.cs

111
src/Main/Base/Test/CSharpExpressionFinderTests.cs

@ -224,7 +224,7 @@ class Main {
} }
[Test] [Test]
public void IdentifierAfterByLessThan() public void IdentifierAfterLessThan()
{ {
FindFull(program3, "oo1)", "foo1", ExpressionContext.Default); FindFull(program3, "oo1)", "foo1", ExpressionContext.Default);
} }
@ -236,7 +236,7 @@ class Main {
} }
[Test] [Test]
public void IdentifierAfterByGreaterThan() public void IdentifierAfterGreaterThan()
{ {
FindFull(program3, "oo2)", "foo2", ExpressionContext.Default); FindFull(program3, "oo2)", "foo2", ExpressionContext.Default);
} }
@ -350,6 +350,113 @@ class Main {
FindFull(program, "taticMethod", "StaticMethod( 2)", ExpressionContext.Default); FindFull(program, "taticMethod", "StaticMethod( 2)", ExpressionContext.Default);
FindExpr(program, " 3", null, ExpressionContext.Default); FindExpr(program, " 3", null, ExpressionContext.Default);
} }
[Test]
public void ExpressionContextInConstructorDeclaration()
{
const string program = @"using System; using System.Text;
class Main {
public Main() : base(arg) {
body;
} }";
FindFull(program, "base", "base(arg)", ExpressionContext.BaseConstructorCall);
FindFull(program, "body", "body", ExpressionContext.MethodBody);
FindFull(program, "arg", "arg", ExpressionContext.Default);
}
[Test]
public void FieldInGenericClass()
{
const string program = @"using System;
class MyList<T> where T : IComparable<T> {
List<T> ";
ExpressionResult result = ef.FindExpression(program, program.Length);
Assert.AreEqual("List<T> ", result.Expression);
Assert.AreEqual(ExpressionContext.IdentifierExpected.ToString(), result.Context.ToString());
}
[Test]
public void MultipleFields()
{
const string program = @"using System;
class MyClass {
List<T> field1, ";
ExpressionResult result = ef.FindExpression(program, program.Length);
Assert.AreEqual(null, result.Expression);
Assert.AreEqual(ExpressionContext.IdentifierExpected.ToString(), result.Context.ToString());
}
[Test]
public void MultipleFieldsWithInitializers()
{
const string program = @"using System;
class MyClass {
int field1 = 1, ";
ExpressionResult result = ef.FindExpression(program, program.Length);
Assert.AreEqual(null, result.Expression);
Assert.AreEqual(ExpressionContext.IdentifierExpected.ToString(), result.Context.ToString());
}
[Test]
public void GenericClassDeclaration()
{
const string program = @"using System;
class List<";
ExpressionResult result = ef.FindExpression(program, program.Length);
Assert.AreEqual(null, result.Expression);
Assert.AreEqual(ExpressionContext.IdentifierExpected.ToString(), result.Context.ToString());
}
[Test]
public void GenericClassDeclaration2()
{
const string program = @"using System;
class Dictionary<K, ";
ExpressionResult result = ef.FindExpression(program, program.Length);
Assert.AreEqual(null, result.Expression);
Assert.AreEqual(ExpressionContext.IdentifierExpected.ToString(), result.Context.ToString());
}
[Test]
public void GenericClassDeclaration3()
{
const string program = @"using System;
class List<T> ";
ExpressionResult result = ef.FindExpression(program, program.Length);
Assert.AreEqual("List<T> ", result.Expression);
Assert.AreEqual(ExpressionContext.ConstraintsStart.ToString(), result.Context.ToString());
}
[Test]
public void GenericClassDeclaration4()
{
const string program = @"using System;
class List<T> where ";
ExpressionResult result = ef.FindExpression(program, program.Length);
Assert.AreEqual("where ", result.Expression);
Assert.AreEqual(ExpressionContext.Constraints.ToString(), result.Context.ToString());
}
[Test]
public void GenericClassDeclaration5()
{
const string program = @"using System;
class List<T> where T : ";
ExpressionResult result = ef.FindExpression(program, program.Length);
Assert.AreEqual(null, result.Expression);
Assert.AreEqual(ExpressionContext.Constraints.ToString(), result.Context.ToString());
}
[Test]
public void GenericClassDeclaration6()
{
const string program = @"using System;
class List<T> where T : class, ";
ExpressionResult result = ef.FindExpression(program, program.Length);
Assert.AreEqual(null, result.Expression);
Assert.AreEqual(ExpressionContext.Constraints.ToString(), result.Context.ToString());
}
} }
} }

146
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/ExpressionFinder.cs

@ -54,6 +54,7 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
Event, Event,
Statements, Statements,
Expression, Expression,
TypeParameterDecl,
Popped Popped
} }
@ -80,7 +81,7 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
/// </summary> /// </summary>
FieldDecl, FieldDecl,
/// <summary> /// <summary>
/// parsing a method delcaration (Interface+TypeDecl) /// parsing a method declaration (Interface+TypeDecl)
/// </summary> /// </summary>
MethodDecl, MethodDecl,
/// <summary> /// <summary>
@ -90,7 +91,11 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
/// <summary> /// <summary>
/// Between class/struct/enum keyword and body of the type declaration /// Between class/struct/enum keyword and body of the type declaration
/// </summary> /// </summary>
TypeDecl TypeDecl,
/// <summary>
/// Between "where" and start of the generic method/class
/// </summary>
Constraints
} }
/// <summary> /// <summary>
@ -101,7 +106,8 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
{ {
internal Frame parent; internal Frame parent;
internal FrameType type; internal FrameType type;
internal FrameType childType; internal FrameType parenthesisChildType;
internal FrameType curlyChildType;
internal FrameState state; internal FrameState state;
internal char bracketType; internal char bracketType;
internal ExpressionContext context; internal ExpressionContext context;
@ -109,7 +115,7 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
internal bool InExpressionMode { internal bool InExpressionMode {
get { get {
return type == FrameType.Statements return type == FrameType.Statements
|| type == FrameType.Expression || type == FrameType.Expression
|| state == FrameState.Initializer; || state == FrameState.Initializer;
} }
@ -128,13 +134,15 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
internal void SetDefaultContext() internal void SetDefaultContext()
{ {
if (state == FrameState.InheritanceList) { if (state == FrameState.InheritanceList) {
if (childType == FrameType.Enum) { if (curlyChildType == FrameType.Enum) {
SetContext(ExpressionContext.EnumBaseType); SetContext(ExpressionContext.EnumBaseType);
} else if (childType == FrameType.Interface) { } else if (curlyChildType == FrameType.Interface) {
SetContext(ExpressionContext.Interface); SetContext(ExpressionContext.Interface);
} else { } else {
SetContext(ExpressionContext.InheritableType); SetContext(ExpressionContext.InheritableType);
} }
} else if (state == FrameState.Constraints) {
SetContext(ExpressionContext.Constraints);
} else { } else {
switch (type) { switch (type) {
case FrameType.Global: case FrameType.Global:
@ -144,6 +152,7 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
SetContext(ExpressionContext.TypeDeclaration); SetContext(ExpressionContext.TypeDeclaration);
break; break;
case FrameType.Enum: case FrameType.Enum:
case FrameType.TypeParameterDecl:
SetContext(ExpressionContext.IdentifierExpected); SetContext(ExpressionContext.IdentifierExpected);
break; break;
case FrameType.Interface: case FrameType.Interface:
@ -174,37 +183,49 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
internal Location lastExpressionStart; internal Location lastExpressionStart;
public Frame(Frame parent) public Frame() : this(null, '\0') {}
public Frame(Frame parent, char bracketType)
{ {
this.parent = parent; this.parent = parent;
this.bracketType = bracketType;
if (parent != null) { if (parent != null) {
this.type = parent.childType; if (bracketType == '{') {
this.type = parent.curlyChildType;
} else if (bracketType == '(') {
this.type = parent.parenthesisChildType;
} else {
this.type = parent.type;
}
} }
ResetChildType(); ResetCurlyChildType();
ResetParenthesisChildType();
SetDefaultContext(); SetDefaultContext();
} }
public void ResetChildType() public void ResetCurlyChildType()
{ {
if (parent != null) { if (parent != null) {
switch (this.type) { switch (this.type) {
case FrameType.Property: case FrameType.Property:
case FrameType.Event: case FrameType.Event:
this.childType = FrameType.Statements; this.curlyChildType = FrameType.Statements;
break; break;
case FrameType.TypeDecl:
if (state == FrameState.Initializer) {
this.childType = FrameType.Expression;
break;
} else {
goto default;
}
default: default:
this.childType = this.type; this.curlyChildType = this.type;
break; break;
} }
} else { } else {
this.childType = this.type; this.curlyChildType = this.type;
}
}
public void ResetParenthesisChildType()
{
if (state == FrameState.Initializer) {
this.parenthesisChildType = FrameType.Expression;
} else {
this.parenthesisChildType = this.type;
} }
} }
} }
@ -229,7 +250,7 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
targetPosition = new Location(offset - lineOffsets[lineOffsets.Count - 1] + 1, lineOffsets.Count); targetPosition = new Location(offset - lineOffsets[lineOffsets.Count - 1] + 1, lineOffsets.Count);
} }
frame = new Frame(null); frame = new Frame();
lastToken = Tokens.EOF; lastToken = Tokens.EOF;
} }
@ -284,9 +305,8 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
switch (token.kind) { switch (token.kind) {
case Tokens.OpenCurlyBrace: case Tokens.OpenCurlyBrace:
frame.lastExpressionStart = Location.Empty; frame.lastExpressionStart = Location.Empty;
frame = new Frame(frame); frame = new Frame(frame, '{');
frame.parent.ResetChildType(); frame.parent.ResetCurlyChildType();
frame.bracketType = '{';
break; break;
case Tokens.CloseCurlyBrace: case Tokens.CloseCurlyBrace:
while (frame.parent != null) { while (frame.parent != null) {
@ -304,9 +324,8 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
case Tokens.OpenSquareBracket: case Tokens.OpenSquareBracket:
if (frame.lastExpressionStart.IsEmpty && token.kind == Tokens.OpenParenthesis) if (frame.lastExpressionStart.IsEmpty && token.kind == Tokens.OpenParenthesis)
frame.lastExpressionStart = token.Location; frame.lastExpressionStart = token.Location;
frame = new Frame(frame); frame = new Frame(frame, '(');
frame.parent.ResetChildType(); frame.parent.ResetParenthesisChildType();
frame.bracketType = '(';
break; break;
case Tokens.CloseParenthesis: case Tokens.CloseParenthesis:
case Tokens.CloseSquareBracket: case Tokens.CloseSquareBracket:
@ -317,10 +336,15 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
break; break;
case Tokens.LessThan: case Tokens.LessThan:
if (Tokens.ValidInsideTypeName[lastToken]) { if (Tokens.ValidInsideTypeName[lastToken]) {
frame = new Frame(frame); frame = new Frame(frame, '<');
frame.bracketType = '<'; if (frame.parent.InExpressionMode) {
if (frame.parent.type == FrameType.Statements || frame.parent.type == FrameType.Expression) {
frame.SetContext(ExpressionContext.Default); frame.SetContext(ExpressionContext.Default);
} else if ((frame.parent.state == FrameState.TypeDecl || frame.parent.state == FrameState.MethodDecl)
&& frame.parent.context == ExpressionContext.IdentifierExpected)
{
frame.type = FrameType.TypeParameterDecl;
frame.SetContext(ExpressionContext.IdentifierExpected);
frame.parent.SetContext(ExpressionContext.ConstraintsStart);
} else { } else {
frame.SetContext(ExpressionContext.Type); frame.SetContext(ExpressionContext.Type);
} }
@ -386,7 +410,7 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
} else if (frame.type == FrameType.TypeDecl) { } else if (frame.type == FrameType.TypeDecl) {
frame.SetContext(ExpressionContext.Default); frame.SetContext(ExpressionContext.Default);
frame.state = FrameState.Initializer; frame.state = FrameState.Initializer;
frame.ResetChildType(); frame.ResetParenthesisChildType();
break; break;
} else { } else {
goto default; goto default;
@ -394,50 +418,62 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
case Tokens.Colon: case Tokens.Colon:
if (frame.state == FrameState.MethodDecl && lastToken == Tokens.CloseParenthesis) { if (frame.state == FrameState.MethodDecl && lastToken == Tokens.CloseParenthesis) {
frame.SetContext(ExpressionContext.BaseConstructorCall); frame.SetContext(ExpressionContext.BaseConstructorCall);
//frame.childType = FrameType.Expression; frame.parenthesisChildType = FrameType.Expression;
} else { } else {
if (frame.childType == FrameType.TypeDecl || frame.childType == FrameType.Interface || frame.childType == FrameType.Enum) { if (frame.curlyChildType == FrameType.TypeDecl || frame.curlyChildType == FrameType.Interface || frame.curlyChildType == FrameType.Enum) {
frame.state = FrameState.InheritanceList; if (frame.state != FrameState.Constraints) {
frame.SetDefaultContext(); frame.state = FrameState.InheritanceList;
frame.SetDefaultContext();
}
} }
} }
break; break;
case Tokens.Class: case Tokens.Class:
case Tokens.Struct: case Tokens.Struct:
if (frame.type == FrameType.Global || frame.type == FrameType.TypeDecl) { if (frame.type == FrameType.Global || frame.type == FrameType.TypeDecl) {
frame.state = FrameState.TypeDecl; if (frame.state != FrameState.Constraints) {
frame.childType = FrameType.TypeDecl; frame.state = FrameState.TypeDecl;
frame.SetContext(ExpressionContext.IdentifierExpected); frame.curlyChildType = FrameType.TypeDecl;
frame.SetContext(ExpressionContext.IdentifierExpected);
}
} }
break; break;
case Tokens.Interface: case Tokens.Interface:
if (frame.type == FrameType.Global || frame.type == FrameType.TypeDecl) { if (frame.type == FrameType.Global || frame.type == FrameType.TypeDecl) {
frame.state = FrameState.TypeDecl; frame.state = FrameState.TypeDecl;
frame.childType = FrameType.Interface; frame.curlyChildType = FrameType.Interface;
frame.SetContext(ExpressionContext.IdentifierExpected); frame.SetContext(ExpressionContext.IdentifierExpected);
} }
break; break;
case Tokens.Enum: case Tokens.Enum:
if (frame.type == FrameType.Global || frame.type == FrameType.TypeDecl) { if (frame.type == FrameType.Global || frame.type == FrameType.TypeDecl) {
frame.state = FrameState.TypeDecl; frame.state = FrameState.TypeDecl;
frame.childType = FrameType.Enum; frame.curlyChildType = FrameType.Enum;
frame.SetContext(ExpressionContext.IdentifierExpected); frame.SetContext(ExpressionContext.IdentifierExpected);
} }
break; break;
case Tokens.Delegate: case Tokens.Delegate:
if (frame.type == FrameType.Global || frame.type == FrameType.TypeDecl) { if (frame.type == FrameType.Global || frame.type == FrameType.TypeDecl) {
frame.childType = FrameType.ParameterList; frame.parenthesisChildType = FrameType.ParameterList;
frame.SetContext(ExpressionContext.Type); frame.SetContext(ExpressionContext.Type);
} }
break; break;
case Tokens.Event: case Tokens.Event:
frame.SetContext(ExpressionContext.DelegateType); frame.SetContext(ExpressionContext.DelegateType);
frame.childType = FrameType.Event; frame.curlyChildType = FrameType.Event;
frame.state = FrameState.EventDecl; frame.state = FrameState.EventDecl;
break; break;
case Tokens.Comma: case Tokens.Comma:
frame.lastExpressionStart = Location.Empty; if (frame.state == FrameState.FieldDecl || frame.state == FrameState.Initializer) {
frame.SetDefaultContext(); frame.state = FrameState.FieldDecl;
frame.SetContext(ExpressionContext.IdentifierExpected);
}
break;
case Tokens.Where:
if (!frame.InExpressionMode && (frame.type == FrameType.Global || frame.type == FrameType.TypeDecl)) {
frame.state = FrameState.Constraints;
frame.SetDefaultContext();
}
break; break;
case Tokens.CloseCurlyBrace: case Tokens.CloseCurlyBrace:
case Tokens.Semicolon: case Tokens.Semicolon:
@ -449,14 +485,14 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
frame.type = FrameType.ParameterList; frame.type = FrameType.ParameterList;
frame.SetContext(ExpressionContext.FirstParameterType); frame.SetContext(ExpressionContext.FirstParameterType);
frame.parent.state = FrameState.MethodDecl; frame.parent.state = FrameState.MethodDecl;
frame.parent.childType = FrameType.Statements; frame.parent.curlyChildType = FrameType.Statements;
} }
break; break;
case Tokens.If: case Tokens.If:
case Tokens.While: case Tokens.While:
case Tokens.Switch: case Tokens.Switch:
if (frame.type == FrameType.Statements) { if (frame.type == FrameType.Statements) {
frame.childType = FrameType.Expression; frame.parenthesisChildType = FrameType.Expression;
} }
break; break;
default: default:
@ -464,7 +500,7 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
if (frame.type == FrameType.Interface || frame.type == FrameType.TypeDecl) { if (frame.type == FrameType.Interface || frame.type == FrameType.TypeDecl) {
if (frame.state == FrameState.Normal) { if (frame.state == FrameState.Normal) {
frame.state = FrameState.FieldDecl; frame.state = FrameState.FieldDecl;
frame.childType = FrameType.Property; frame.curlyChildType = FrameType.Property;
} }
if (!(frame.state == FrameState.Initializer && frame.context.IsObjectCreation)) { if (!(frame.state == FrameState.Initializer && frame.context.IsObjectCreation)) {
frame.SetContext(ExpressionContext.IdentifierExpected); frame.SetContext(ExpressionContext.IdentifierExpected);
@ -491,9 +527,9 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
/// </summary> /// </summary>
public ExpressionResult FindFullExpressionInTypeDeclaration(string text, int offset) public ExpressionResult FindFullExpressionInTypeDeclaration(string text, int offset)
{ {
Frame root = new Frame(null); Frame root = new Frame();
root.childType = FrameType.TypeDecl; root.curlyChildType = FrameType.TypeDecl;
Frame typeDecl = new Frame(root); Frame typeDecl = new Frame(root, '{');
return FindFullExpression(text, offset, typeDecl); return FindFullExpression(text, offset, typeDecl);
} }
@ -503,11 +539,11 @@ namespace ICSharpCode.SharpDevelop.Dom.CSharp
/// </summary> /// </summary>
public ExpressionResult FindFullExpressionInMethod(string text, int offset) public ExpressionResult FindFullExpressionInMethod(string text, int offset)
{ {
Frame root = new Frame(null); Frame root = new Frame();
root.childType = FrameType.TypeDecl; root.curlyChildType = FrameType.TypeDecl;
Frame typeDecl = new Frame(root); Frame typeDecl = new Frame(root, '{');
typeDecl.childType = FrameType.Statements; typeDecl.curlyChildType = FrameType.Statements;
Frame methodBody = new Frame(typeDecl); Frame methodBody = new Frame(typeDecl, '{');
return FindFullExpression(text, offset, methodBody); return FindFullExpression(text, offset, methodBody);
} }

9
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ExpressionContext.cs

@ -92,6 +92,15 @@ namespace ICSharpCode.SharpDevelop.Dom
/// <example>void Main () { *expr* }</example> /// <example>void Main () { *expr* }</example>
public static ExpressionContext MethodBody = new DefaultExpressionContext("MethodBody"); public static ExpressionContext MethodBody = new DefaultExpressionContext("MethodBody");
/// <summary>The context is after the type parameters of a type/method declaration.
/// The only valid keyword is "where"</summary>
/// <example>class &lt;T&gt; *expr*</example>
public static ExpressionContext ConstraintsStart = new DefaultExpressionContext("ConstraintsStart");
/// <summary>The context is after the 'where' of a constraints list.</summary>
/// <example>class &lt;T&gt; where *expr*</example>
public static ExpressionContext Constraints = new NonStaticTypeExpressionContext("Constraints", false);
/// <summary>The context is the body of a type declaration.</summary> /// <summary>The context is the body of a type declaration.</summary>
public static ExpressionContext TypeDeclaration = new NonStaticTypeExpressionContext("TypeDeclaration", true); public static ExpressionContext TypeDeclaration = new NonStaticTypeExpressionContext("TypeDeclaration", true);

13
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryResolver.cs

@ -1142,6 +1142,19 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
} else if (context == ExpressionContext.BaseConstructorCall) { } else if (context == ExpressionContext.BaseConstructorCall) {
result.Add("this"); result.Add("this");
result.Add("base"); result.Add("base");
} else if (context == ExpressionContext.ConstraintsStart) {
result.Add("where");
} else if (context == ExpressionContext.Constraints) {
result.Add("where");
result.Add("new");
result.Add("struct");
result.Add("class");
AddCSharpPrimitiveTypes(result);
CtrlSpaceInternal(result, fileContent);
} else if (context == ExpressionContext.InheritableType) {
result.Add("where"); // the inheritance list can be followed by constraints
AddCSharpPrimitiveTypes(result);
CtrlSpaceInternal(result, fileContent);
} else if (context == ExpressionContext.PropertyDeclaration) { } else if (context == ExpressionContext.PropertyDeclaration) {
AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.InPropertyDeclaration); AddCSharpKeywords(result, NR.Parser.CSharp.Tokens.InPropertyDeclaration);
} else if (context == ExpressionContext.EventDeclaration) { } else if (context == ExpressionContext.EventDeclaration) {

Loading…
Cancel
Save