Browse Source

Added support for code folding of global python methods defined outside of a class.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@4495 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Matt Ward 16 years ago
parent
commit
778c760b8d
  1. 69
      src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonAstWalker.cs
  2. 205
      src/AddIns/BackendBindings/Python/PythonBinding/Test/Parsing/ParseMethodsWithNoClassTestFixture.cs
  3. 1
      src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj

69
src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonAstWalker.cs

@ -13,6 +13,7 @@ using System.IO; @@ -13,6 +13,7 @@ using System.IO;
using ICSharpCode.SharpDevelop.Dom;
using IronPython.Compiler;
using IronPython.Compiler.Ast;
using IronPython.Runtime;
namespace ICSharpCode.PythonBinding
{
@ -23,6 +24,7 @@ namespace ICSharpCode.PythonBinding @@ -23,6 +24,7 @@ namespace ICSharpCode.PythonBinding
{
DefaultCompilationUnit compilationUnit;
DefaultClass currentClass;
DefaultClass globalClass;
string ns;
/// <summary>
@ -77,22 +79,30 @@ namespace ICSharpCode.PythonBinding @@ -77,22 +79,30 @@ namespace ICSharpCode.PythonBinding
/// </summary>
public override bool Walk(FunctionDefinition node)
{
if (currentClass != null) {
string methodName = node.Name.ToString();
DomRegion bodyRegion = GetBodyRegion(node.Body, node.Header);
DomRegion region = GetMethodRegion(node);
DefaultMethod method;
if (methodName == "__init__") {
method = new Constructor(ModifierEnum.Public, region, bodyRegion, currentClass);
} else {
method = new DefaultMethod(methodName, new DefaultReturnType(currentClass), ModifierEnum.Public, region, bodyRegion, currentClass);
}
foreach (IParameter parameter in ConvertParameters(node.Parameters)) {
method.Parameters.Add(parameter);
}
currentClass.Methods.Add(method);
bool ignoreFirstMethodParameter = true;
IClass c = currentClass;
if (currentClass == null) {
// Walking a global method.
CreateGlobalClass();
c = globalClass;
ignoreFirstMethodParameter = false;
}
// Create method.
string methodName = node.Name.ToString();
DomRegion bodyRegion = GetBodyRegion(node.Body, node.Header);
DomRegion region = GetMethodRegion(node);
DefaultMethod method;
if (methodName == "__init__") {
method = new Constructor(ModifierEnum.Public, region, bodyRegion, c);
} else {
method = new DefaultMethod(methodName, new DefaultReturnType(c), ModifierEnum.Public, region, bodyRegion, c);
}
foreach (IParameter parameter in ConvertParameters(node.Parameters, ignoreFirstMethodParameter)) {
method.Parameters.Add(parameter);
}
c.Methods.Add(method);
return true;
}
@ -175,16 +185,22 @@ namespace ICSharpCode.PythonBinding @@ -175,16 +185,22 @@ namespace ICSharpCode.PythonBinding
/// <summary>
/// Converts from Python AST expressions to parameters.
/// </summary>
IParameter[] ConvertParameters(Parameter[] parameters)
/// <remarks>If the parameters belong to a class method then the first
/// "self" parameter can be ignored.</remarks>
IParameter[] ConvertParameters(Parameter[] parameters, bool ignoreFirstParameter)
{
List<IParameter> convertedTarameters = new List<IParameter>();
List<IParameter> convertedParameters = new List<IParameter>();
// Ignore first parameter since this is the "self" parameter.
for (int i = 1; i < parameters.Length; ++i) {
int startingIndex = 0;
if (ignoreFirstParameter) {
startingIndex = 1;
}
for (int i = startingIndex; i < parameters.Length; ++i) {
DefaultParameter parameter = new DefaultParameter(parameters[i].Name.ToString(), null, new DomRegion());
convertedTarameters.Add(parameter);
convertedParameters.Add(parameter);
}
return convertedTarameters.ToArray();
return convertedParameters.ToArray();
}
@ -195,5 +211,16 @@ namespace ICSharpCode.PythonBinding @@ -195,5 +211,16 @@ namespace ICSharpCode.PythonBinding
{
return String.Concat(ns, ".", classDef.Name.ToString());
}
/// <summary>
/// Creates the dummy class that is used to hold global methods.
/// </summary>
void CreateGlobalClass()
{
if (globalClass == null) {
globalClass = new DefaultClass(compilationUnit, ns);
compilationUnit.Classes.Add(globalClass);
}
}
}
}

205
src/AddIns/BackendBindings/Python/PythonBinding/Test/Parsing/ParseMethodsWithNoClassTestFixture.cs

@ -0,0 +1,205 @@ @@ -0,0 +1,205 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using ICSharpCode.PythonBinding;
using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.TextEditor.Document;
using NUnit.Framework;
using PythonBinding.Tests;
namespace PythonBinding.Tests.Parsing
{
/// <summary>
/// Support folding when no classes are defined.
/// </summary>
[TestFixture]
public class ParseMethodsWithNoClassTestFixture
{
ICompilationUnit compilationUnit;
FoldingRegion fooFoldingRegion;
FoldingRegion barFoldingRegion;
FoldMarker fooMethodMarker;
FoldMarker barMethodMarker;
IClass globalClass;
IMethod fooMethod;
IMethod barMethod;
[TestFixtureSetUp]
public void SetUpFixture()
{
string python = "def foo():\r\n" +
"\tpass\r\n" +
"\r\n" +
"def bar(i):\r\n" +
"\tpass";
DefaultProjectContent projectContent = new DefaultProjectContent();
PythonParser parser = new PythonParser();
compilationUnit = parser.Parse(projectContent, @"C:\test.py", python);
if (compilationUnit.FoldingRegions.Count > 1) {
fooFoldingRegion = compilationUnit.FoldingRegions[0];
barFoldingRegion = compilationUnit.FoldingRegions[1];
}
if (compilationUnit.Classes.Count > 0) {
globalClass = compilationUnit.Classes[0];
if (globalClass.Methods.Count > 1) {
fooMethod = globalClass.Methods[0];
barMethod = globalClass.Methods[1];
}
}
// Get folds.
ParserFoldingStrategy foldingStrategy = new ParserFoldingStrategy();
ParseInformation parseInfo = new ParseInformation();
parseInfo.SetCompilationUnit(compilationUnit);
DocumentFactory docFactory = new DocumentFactory();
IDocument doc = docFactory.CreateDocument();
doc.TextContent = python;
List<FoldMarker> markers = foldingStrategy.GenerateFoldMarkers(doc, @"C:\Temp\test.py", parseInfo);
if (markers.Count > 1) {
fooMethodMarker = markers[0];
barMethodMarker = markers[1];
}
}
[Test]
public void OneClass()
{
Assert.AreEqual(1, compilationUnit.Classes.Count);
}
[Test]
public void GlobalClassName()
{
Assert.AreEqual("test", globalClass.Name);
}
[Test]
public void GlobalClassHasTwoMethods()
{
Assert.AreEqual(2, globalClass.Methods.Count);
}
[Test]
public void FooMethodName()
{
Assert.AreEqual("foo", fooMethod.Name);
}
[Test]
public void BarMethodName()
{
Assert.AreEqual("bar", barMethod.Name);
}
[Test]
public void FooMethodDefaultReturnType()
{
Assert.AreEqual(globalClass, fooMethod.ReturnType.GetUnderlyingClass());
}
[Test]
public void BarMethodDefaultReturnType()
{
Assert.AreEqual(globalClass, barMethod.ReturnType.GetUnderlyingClass());
}
[Test]
public void FooMethodDeclaringType()
{
Assert.AreEqual(globalClass, fooMethod.DeclaringType);
}
[Test]
public void FooMethodBodyRegion()
{
int startLine = 1;
int startColumn = 11;
int endLine = 2;
int endColumn = 6;
DomRegion region = new DomRegion(startLine, startColumn, endLine, endColumn);
Assert.AreEqual(region.ToString(), fooMethod.BodyRegion.ToString());
}
/// <summary>
/// The method region needs to extend up just after the colon. It does not include the body.
/// </summary>
[Test]
public void FooMethodRegion()
{
int startLine = 1;
int startColumn = 1;
int endLine = 1;
int endColumn = 11;
DomRegion region = new DomRegion(startLine, startColumn, endLine, endColumn);
Assert.AreEqual(region.ToString(), fooMethod.Region.ToString());
}
[Test]
public void BarMethodBodyRegion()
{
int startLine = 4;
int startColumn = 12;
int endLine = 5;
int endColumn = 6;
DomRegion region = new DomRegion(startLine, startColumn, endLine, endColumn);
Assert.AreEqual(region.ToString(), barMethod.BodyRegion.ToString());
}
/// <summary>
/// The method region needs to extend up just after the colon. It does not include the body.
/// </summary>
[Test]
public void BarMethodRegion()
{
int startLine = 4;
int startColumn = 1;
int endLine = 4;
int endColumn = 12;
DomRegion region = new DomRegion(startLine, startColumn, endLine, endColumn);
Assert.AreEqual(region.ToString(), barMethod.Region.ToString());
}
[Test]
public void BarMethodHasOneParameter()
{
Assert.AreEqual(1, barMethod.Parameters.Count);
}
[Test]
public void FooMethodFoldMarkerInnerText()
{
Assert.AreEqual("\r\n\tpass", fooMethodMarker.InnerText);
}
[Test]
public void BarMethodFoldMarkerInnerText()
{
Assert.AreEqual("\r\n\tpass", barMethodMarker.InnerText);
}
[Test]
public void FooMethodCollapsedFoldText()
{
Assert.AreEqual("...", fooMethodMarker.FoldText);
}
[Test]
public void BarMethodCollapsedFoldText()
{
Assert.AreEqual("...", barMethodMarker.FoldText);
}
}
}

1
src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj

@ -276,6 +276,7 @@ @@ -276,6 +276,7 @@
<Compile Include="Parsing\ParseClassWithMethodTestFixture.cs" />
<Compile Include="Parsing\ParseImportTestFixture.cs" />
<Compile Include="Parsing\ParseInvalidPythonCodeTestFixture.cs" />
<Compile Include="Parsing\ParseMethodsWithNoClassTestFixture.cs" />
<Compile Include="Parsing\ParserTestFixture.cs" />
<Compile Include="Parsing\ParseSingleClassTestFixture.cs" />
<Compile Include="Parsing\InvalidClassTestFixture.cs" />

Loading…
Cancel
Save