21 changed files with 1651 additions and 151 deletions
@ -0,0 +1,16 @@ |
|||||||
|
<Properties> |
||||||
|
<MonoDevelop.Ide.Workspace ActiveConfiguration="Debug" /> |
||||||
|
<MonoDevelop.Ide.Workbench ActiveDocument="src\Libraries\NRefactory\ICSharpCode.NRefactory.CSharp\IndentEngine\IndentState.cs"> |
||||||
|
<Files> |
||||||
|
<File FileName="src\AddIns\BackendBindings\CSharpBinding\Project\Src\OutlinePad\AstNodeHelper.cs" Line="35" Column="51" /> |
||||||
|
<File FileName="src\AddIns\BackendBindings\CSharpBinding\Project\Src\OutlinePad\CSharpOutlineContentHost.xaml.cs" Line="49" Column="3" /> |
||||||
|
<File FileName="src\AddIns\BackendBindings\CSharpBinding\Project\Src\OutlinePad\CSharpOutlineContentHost.xaml" Line="8" Column="8" /> |
||||||
|
<File FileName="src\AddIns\BackendBindings\CSharpBinding\Project\Src\OutlinePad\CSharpOutlineNode.cs" Line="121" Column="5" /> |
||||||
|
<File FileName="src\Libraries\NRefactory\ICSharpCode.NRefactory.CSharp\IndentEngine\IndentState.cs" Line="1703" Column="5" /> |
||||||
|
</Files> |
||||||
|
</MonoDevelop.Ide.Workbench> |
||||||
|
<MonoDevelop.Ide.DebuggingService.Breakpoints> |
||||||
|
<BreakpointStore /> |
||||||
|
</MonoDevelop.Ide.DebuggingService.Breakpoints> |
||||||
|
<MonoDevelop.Ide.DebuggingService.PinnedWatches /> |
||||||
|
</Properties> |
@ -0,0 +1,544 @@ |
|||||||
|
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.IO; |
||||||
|
using System.Linq; |
||||||
|
using System.Windows; |
||||||
|
using System.Windows.Media; |
||||||
|
using System.Text; |
||||||
|
using ICSharpCode.NRefactory.CSharp; |
||||||
|
using ICSharpCode.AvalonEdit.Highlighting; |
||||||
|
using System.Collections.Generic; |
||||||
|
|
||||||
|
namespace CSharpBinding.OutlinePad |
||||||
|
{ |
||||||
|
internal static class AstNodeHelper { |
||||||
|
|
||||||
|
static bool showExtendedInfos = true; |
||||||
|
static bool colorizeNode = false; |
||||||
|
static HighlightingColor nameSpaceHighLighting, regionHighLighting, classHighLighting, methodHighLighting, interfaceHighLighting; |
||||||
|
|
||||||
|
static AstNodeHelper() { |
||||||
|
var highlightingDefinition = HighlightingManager.Instance.GetDefinition("C#"); |
||||||
|
|
||||||
|
nameSpaceHighLighting = highlightingDefinition.NamedHighlightingColors.First( x => x.Name == "NamespaceKeywords"); |
||||||
|
regionHighLighting = highlightingDefinition.NamedHighlightingColors.First( x => x.Name == "Preprocessor"); |
||||||
|
classHighLighting = highlightingDefinition.NamedHighlightingColors.First( x => x.Name == "ReferenceTypeKeywords"); |
||||||
|
interfaceHighLighting = highlightingDefinition.NamedHighlightingColors.First( x => x.Name == "ReferenceTypeKeywords"); |
||||||
|
methodHighLighting = highlightingDefinition.NamedHighlightingColors.First( x => x.Name == "MethodCall"); |
||||||
|
} |
||||||
|
|
||||||
|
private static Modifiers GetAccessModifier(AstNode node) { |
||||||
|
var accessModifier = node.Children.FirstOrDefault(x => x is CSharpModifierToken && |
||||||
|
((x as CSharpModifierToken).Modifier == Modifiers.Public || |
||||||
|
(x as CSharpModifierToken).Modifier == Modifiers.Protected || |
||||||
|
(x as CSharpModifierToken).Modifier == Modifiers.Private || |
||||||
|
(x as CSharpModifierToken).Modifier == Modifiers.Internal )) as CSharpModifierToken; |
||||||
|
|
||||||
|
// special case: All members in an interface are public, although they have no access modifier
|
||||||
|
if (accessModifier == null && IsInterface(node.Parent)) { |
||||||
|
return Modifiers.Public; |
||||||
|
} |
||||||
|
return accessModifier == null |
||||||
|
? Modifiers.None |
||||||
|
: accessModifier.Modifier; |
||||||
|
} |
||||||
|
|
||||||
|
private static string GetParameterDeclsAsString(AstNodeCollection<ParameterDeclaration> parameterDecls) { |
||||||
|
var parameterString = new StringBuilder(); |
||||||
|
int current = 0; |
||||||
|
foreach (var paramDecl in parameterDecls) { |
||||||
|
if (paramDecl.ParameterModifier != ParameterModifier.None) { |
||||||
|
parameterString.Append(paramDecl.ParameterModifier.ToString().ToLower()); |
||||||
|
parameterString.Append(" "); |
||||||
|
} |
||||||
|
parameterString.Append(paramDecl.Type.ToString()); |
||||||
|
parameterString.Append(" "); |
||||||
|
parameterString.Append(paramDecl.Name); |
||||||
|
|
||||||
|
if (current < (parameterDecls.Count - 1)) { |
||||||
|
parameterString.Append(", "); |
||||||
|
} |
||||||
|
current++; |
||||||
|
} |
||||||
|
|
||||||
|
return parameterString.ToString(); |
||||||
|
} |
||||||
|
|
||||||
|
private static string GetTypeParameterDeclsAsString(AstNodeCollection<TypeParameterDeclaration> parameterDecls) { |
||||||
|
var parameterString = new StringBuilder(); |
||||||
|
int current = 0; |
||||||
|
foreach (var paramDecl in parameterDecls) { |
||||||
|
parameterString.Append(paramDecl.NameToken.ToString()); |
||||||
|
|
||||||
|
if (current < (parameterDecls.Count - 1)) { |
||||||
|
parameterString.Append(", "); |
||||||
|
} |
||||||
|
current++; |
||||||
|
} |
||||||
|
|
||||||
|
return parameterString.ToString(); |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsAllowedNode(AstNode node) { |
||||||
|
return (IsRegionStart(node) |
||||||
|
|| IsRegionEnd(node) |
||||||
|
|| IsClass(node) |
||||||
|
|| IsInterface(node) |
||||||
|
|| IsMethod(node) |
||||||
|
|| IsField(node) |
||||||
|
|| IsProperty(node) |
||||||
|
|| IsNameSpace(node) |
||||||
|
|| IsConstructor(node) |
||||||
|
|| IsEvent(node) |
||||||
|
|| IsDelegate(node) |
||||||
|
|| IsIndexer(node) |
||||||
|
|| IsEnum(node) |
||||||
|
|| IsEnumMember(node) |
||||||
|
|| IsStruct(node) |
||||||
|
|| IsOperator(node) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsRegionStart(AstNode node) { |
||||||
|
return (node is PreProcessorDirective && (node as PreProcessorDirective).Type == PreProcessorDirectiveType.Region); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetRegionStartInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
var preProcessorNode = (PreProcessorDirective)dataNode; |
||||||
|
node.Name = "*****" + preProcessorNode.Argument + "****"; |
||||||
|
node.ForegroundBrush = (regionHighLighting.Foreground as SimpleHighlightingBrush).GetBrush(null); |
||||||
|
// node.Weight = regionHighLighting.FontWeight != null ? regionHighLighting.FontWeight.Value : FontWeights.Normal;
|
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsRegionEnd(AstNode node) { |
||||||
|
return (node is PreProcessorDirective && (node as PreProcessorDirective).Type == PreProcessorDirectiveType.Endregion); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetRegionEndInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
var preProcessorNode = (PreProcessorDirective)dataNode; |
||||||
|
node.Name = "********************"; |
||||||
|
node.ForegroundBrush = (regionHighLighting.Foreground as SimpleHighlightingBrush).GetBrush(null); |
||||||
|
// node.Weight = regionHighLighting.FontWeight != null ? regionHighLighting.FontWeight.Value : FontWeights.Normal;
|
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsDelegate(AstNode node) { |
||||||
|
return (node is DelegateDeclaration); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetDelegateNodeInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
var typeNode = (DelegateDeclaration)dataNode; |
||||||
|
node.Name = typeNode.Name; |
||||||
|
if (colorizeNode) |
||||||
|
node.ForegroundBrush = Brushes.Black; |
||||||
|
// node.Weight = classHighLighting.FontWeight != null ? classHighLighting.FontWeight.Value : FontWeights.Normal;
|
||||||
|
|
||||||
|
switch(GetAccessModifier(dataNode)) { |
||||||
|
case Modifiers.Public: |
||||||
|
node.IconName = "Icons.16x16.Delegate"; |
||||||
|
break; |
||||||
|
case Modifiers.Protected: |
||||||
|
node.IconName = "Icons.16x16.ProtectedDelegate"; |
||||||
|
break; |
||||||
|
case Modifiers.Private: |
||||||
|
node.IconName = "Icons.16x16.PrivateDelegate"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
node.IconName = "Icons.16x16.InternalDelegate"; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsStruct(AstNode node) { |
||||||
|
return (node is TypeDeclaration && (node as TypeDeclaration).ClassType == ClassType.Struct); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetStructNodeInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
var typeNode = (TypeDeclaration)dataNode; |
||||||
|
node.Name = typeNode.Name; |
||||||
|
node.IsExpanded = true; |
||||||
|
|
||||||
|
if (colorizeNode) |
||||||
|
node.ForegroundBrush = (classHighLighting.Foreground as SimpleHighlightingBrush).GetBrush(null); |
||||||
|
// node.Weight = classHighLighting.FontWeight != null ? classHighLighting.FontWeight.Value : FontWeights.Normal;
|
||||||
|
|
||||||
|
switch(GetAccessModifier(dataNode)) { |
||||||
|
case Modifiers.Public: |
||||||
|
node.IconName = "Icons.16x16.Struct"; |
||||||
|
break; |
||||||
|
case Modifiers.Protected: |
||||||
|
node.IconName = "Icons.16x16.ProtectedStruct"; |
||||||
|
break; |
||||||
|
case Modifiers.Private: |
||||||
|
node.IconName = "Icons.16x16.PrivateStruct"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
node.IconName = "Icons.16x16.InternalStruct"; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsEnumMember(AstNode node) { |
||||||
|
return (node is EnumMemberDeclaration); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetEnumMemberNodeInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
var enumMemberNode = (EnumMemberDeclaration)dataNode; |
||||||
|
node.Name = enumMemberNode.Name; |
||||||
|
|
||||||
|
if (colorizeNode) |
||||||
|
node.ForegroundBrush = Brushes.Black; |
||||||
|
node.IconName = "Icons.16x16.Enum"; |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsEnum(AstNode node) { |
||||||
|
return (node is TypeDeclaration && (node as TypeDeclaration).ClassType == ClassType.Enum); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetEnumNodeInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
var typeNode = (TypeDeclaration)dataNode; |
||||||
|
node.Name = typeNode.Name; |
||||||
|
node.IsExpanded = true; |
||||||
|
|
||||||
|
if (colorizeNode) |
||||||
|
node.ForegroundBrush = (classHighLighting.Foreground as SimpleHighlightingBrush).GetBrush(null); |
||||||
|
|
||||||
|
switch(GetAccessModifier(dataNode)) { |
||||||
|
case Modifiers.Public: |
||||||
|
node.IconName = "Icons.16x16.Enum"; |
||||||
|
break; |
||||||
|
case Modifiers.Protected: |
||||||
|
node.IconName = "Icons.16x16.ProtectedEnum"; |
||||||
|
break; |
||||||
|
case Modifiers.Private: |
||||||
|
node.IconName = "Icons.16x16.PrivateEnum"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
node.IconName = "Icons.16x16.InternalEnum"; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsClass(AstNode node) { |
||||||
|
return (node is TypeDeclaration && (node as TypeDeclaration).ClassType == ClassType.Class); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetClassNodeInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
var typeNode = (TypeDeclaration)dataNode; |
||||||
|
node.Name = typeNode.Name |
||||||
|
+ typeNode.LChevronToken |
||||||
|
+ GetTypeParameterDeclsAsString(typeNode.TypeParameters) |
||||||
|
+ typeNode.RChevronToken; |
||||||
|
node.IsExpanded = true; |
||||||
|
|
||||||
|
if (colorizeNode) |
||||||
|
node.ForegroundBrush = (classHighLighting.Foreground as SimpleHighlightingBrush).GetBrush(null); |
||||||
|
// node.Weight = classHighLighting.FontWeight != null ? classHighLighting.FontWeight.Value : FontWeights.Normal;
|
||||||
|
|
||||||
|
switch(GetAccessModifier(dataNode)) { |
||||||
|
case Modifiers.Public: |
||||||
|
node.IconName = "Icons.16x16.Class"; |
||||||
|
break; |
||||||
|
case Modifiers.Protected: |
||||||
|
node.IconName = "Icons.16x16.ProtectedClass"; |
||||||
|
break; |
||||||
|
case Modifiers.Private: |
||||||
|
node.IconName = "Icons.16x16.PrivateClass"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
node.IconName = "Icons.16x16.InternalClass"; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsEvent(AstNode node) { |
||||||
|
return (node is EventDeclaration); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetEventNodeInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
var eventNode = (EventDeclaration)dataNode; |
||||||
|
var varInitializer = eventNode.Children.FirstOrDefault(x => x is VariableInitializer) as VariableInitializer; |
||||||
|
node.Name = (varInitializer != null) |
||||||
|
? varInitializer.Name |
||||||
|
: "<unknown>"; |
||||||
|
if (colorizeNode) |
||||||
|
node.ForegroundBrush = Brushes.Black; |
||||||
|
|
||||||
|
switch(GetAccessModifier(dataNode)) { |
||||||
|
case Modifiers.Public: |
||||||
|
node.IconName = "Icons.16x16.Event"; |
||||||
|
break; |
||||||
|
case Modifiers.Protected: |
||||||
|
node.IconName = "Icons.16x16.ProtectedEvent"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
node.IconName = "Icons.16x16.PrivateEvent"; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsInterface(AstNode node) { |
||||||
|
return (node is TypeDeclaration && (node as TypeDeclaration).ClassType == ClassType.Interface); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetInterfaceNodeInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
var typeNode = (TypeDeclaration)dataNode; |
||||||
|
node.Name = typeNode.Name; |
||||||
|
node.IsExpanded = true; |
||||||
|
|
||||||
|
if (colorizeNode) |
||||||
|
node.ForegroundBrush = (interfaceHighLighting.Foreground as SimpleHighlightingBrush).GetBrush(null); |
||||||
|
|
||||||
|
switch(GetAccessModifier(dataNode)) { |
||||||
|
case Modifiers.Public: |
||||||
|
node.IconName = "Icons.16x16.Interface"; |
||||||
|
break; |
||||||
|
case Modifiers.Protected: |
||||||
|
node.IconName = "Icons.16x16.ProtectedInterface"; |
||||||
|
break; |
||||||
|
case Modifiers.Private: |
||||||
|
node.IconName = "Icons.16x16.PrivateInterface"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
node.IconName = "Icons.16x16.InternalInterface"; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsOperator(AstNode node) { |
||||||
|
return (node is OperatorDeclaration); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetOperatorNodeInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
var operatorNode = (OperatorDeclaration)dataNode; |
||||||
|
node.Name = operatorNode.OperatorToken |
||||||
|
+ " " |
||||||
|
+ operatorNode.OperatorTypeToken; |
||||||
|
|
||||||
|
if (colorizeNode) |
||||||
|
node.ForegroundBrush = Brushes.Black; |
||||||
|
|
||||||
|
if (showExtendedInfos) { |
||||||
|
node.Name += " " |
||||||
|
+ operatorNode.LParToken |
||||||
|
+ GetParameterDeclsAsString(operatorNode.Parameters) |
||||||
|
+ operatorNode.RParToken; |
||||||
|
} |
||||||
|
switch(GetAccessModifier(dataNode)) { |
||||||
|
case Modifiers.Public: |
||||||
|
node.IconName = "Icons.16x16.Method"; |
||||||
|
break; |
||||||
|
case Modifiers.Protected: |
||||||
|
node.IconName = "Icons.16x16.ProtectedMethod"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
node.IconName = "Icons.16x16.PrivateMethod"; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsMethod(AstNode node) { |
||||||
|
return (node is MethodDeclaration); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetMethodNodeInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
var methodNode = (MethodDeclaration)dataNode; |
||||||
|
node.Name = methodNode.Name; |
||||||
|
|
||||||
|
node.Name += " " |
||||||
|
+ methodNode.LParToken |
||||||
|
+ (showExtendedInfos ? GetParameterDeclsAsString(methodNode.Parameters) : "") |
||||||
|
+ methodNode.RParToken; |
||||||
|
|
||||||
|
if (colorizeNode) |
||||||
|
node.ForegroundBrush = (methodHighLighting.Foreground as SimpleHighlightingBrush).GetBrush(null); |
||||||
|
// node.Weight = methodHighLighting.FontWeight != null ? methodHighLighting.FontWeight.Value : FontWeights.Normal;
|
||||||
|
|
||||||
|
switch(GetAccessModifier(dataNode)) { |
||||||
|
case Modifiers.Public: |
||||||
|
node.IconName = "Icons.16x16.Method"; |
||||||
|
break; |
||||||
|
case Modifiers.Protected: |
||||||
|
node.IconName = "Icons.16x16.ProtectedMethod"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
node.IconName = "Icons.16x16.PrivateMethod"; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsField(AstNode node) { |
||||||
|
return (node is FieldDeclaration); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetFieldNodeInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
|
||||||
|
var fieldNode = (FieldDeclaration)dataNode; |
||||||
|
var fieldName = new StringBuilder(); |
||||||
|
int current = 0; |
||||||
|
foreach (var varInitializer in fieldNode.Variables) { |
||||||
|
fieldName.Append(varInitializer.Name); |
||||||
|
|
||||||
|
if (current < (fieldNode.Variables.Count - 1)) { |
||||||
|
fieldName.Append(", "); |
||||||
|
} |
||||||
|
current++; |
||||||
|
} |
||||||
|
|
||||||
|
node.Name = fieldName.ToString(); |
||||||
|
|
||||||
|
if (colorizeNode) |
||||||
|
node.ForegroundBrush = Brushes.Black; |
||||||
|
|
||||||
|
switch(GetAccessModifier(dataNode)) { |
||||||
|
case Modifiers.Public: |
||||||
|
node.IconName = "Icons.16x16.Field"; |
||||||
|
break; |
||||||
|
case Modifiers.Protected: |
||||||
|
node.IconName = "Icons.16x16.ProtectedField"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
node.IconName = "Icons.16x16.PrivateField"; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsProperty(AstNode node) { |
||||||
|
return (node is PropertyDeclaration); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetPropertyNodeInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
var propertyNode = (PropertyDeclaration)dataNode; |
||||||
|
node.Name = propertyNode.Name; |
||||||
|
|
||||||
|
if (colorizeNode) |
||||||
|
node.ForegroundBrush = Brushes.Black; |
||||||
|
|
||||||
|
if (showExtendedInfos) { |
||||||
|
node.Name += " " + propertyNode.LBraceToken |
||||||
|
+ " " + propertyNode.Getter.Keyword + (!string.IsNullOrEmpty(propertyNode.Getter.Keyword.ToString()) ? ";" : "") |
||||||
|
+ " " + propertyNode.Setter.Keyword + (!string.IsNullOrEmpty(propertyNode.Setter.Keyword.ToString()) ? ";" : "") |
||||||
|
+ " " + propertyNode.RBraceToken; |
||||||
|
} |
||||||
|
switch(GetAccessModifier(dataNode)) { |
||||||
|
case Modifiers.Public: |
||||||
|
node.IconName = "Icons.16x16.Property"; |
||||||
|
break; |
||||||
|
case Modifiers.Protected: |
||||||
|
node.IconName = "Icons.16x16.ProtectedProperty"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
node.IconName = "Icons.16x16.PrivateProperty"; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsIndexer(AstNode node) { |
||||||
|
return (node is IndexerDeclaration); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetIndexerNodeInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
var indexerNode = (IndexerDeclaration)dataNode; |
||||||
|
node.Name = indexerNode.ReturnType.ToString() |
||||||
|
+ " " |
||||||
|
+ indexerNode.ThisToken.ToString() |
||||||
|
+ indexerNode.LBracketToken |
||||||
|
+ GetParameterDeclsAsString(indexerNode.Parameters) |
||||||
|
+ indexerNode.RBracketToken; |
||||||
|
if (colorizeNode) |
||||||
|
node.ForegroundBrush = Brushes.Black; |
||||||
|
|
||||||
|
if (showExtendedInfos) { |
||||||
|
node.Name += " " + indexerNode.LBraceToken |
||||||
|
+ " " + indexerNode.Getter.Keyword + (!string.IsNullOrEmpty(indexerNode.Getter.Keyword.ToString()) ? ";" : "") |
||||||
|
+ " " + indexerNode.Setter.Keyword + (!string.IsNullOrEmpty(indexerNode.Setter.Keyword.ToString()) ? ";" : "") |
||||||
|
+ " " + indexerNode.RBraceToken; |
||||||
|
} |
||||||
|
switch(GetAccessModifier(dataNode)) { |
||||||
|
case Modifiers.Public: |
||||||
|
node.IconName = "Icons.16x16.Property"; |
||||||
|
break; |
||||||
|
case Modifiers.Protected: |
||||||
|
node.IconName = "Icons.16x16.ProtectedProperty"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
node.IconName = "Icons.16x16.PrivateProperty"; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsNameSpace(AstNode node) { |
||||||
|
return (node is NamespaceDeclaration); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetNameSpaceNodeInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
var nameSpaceNode = (NamespaceDeclaration)dataNode; |
||||||
|
node.Name = nameSpaceNode.Name; |
||||||
|
node.IconName = "Icons.16x16.NameSpace"; |
||||||
|
node.IsExpanded = true; |
||||||
|
|
||||||
|
if (colorizeNode) |
||||||
|
node.ForegroundBrush = (nameSpaceHighLighting.Foreground as SimpleHighlightingBrush).GetBrush(null); |
||||||
|
// node.Weight = nameSpaceHighLighting.FontWeight != null ? nameSpaceHighLighting.FontWeight.Value : FontWeights.Normal;
|
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsConstructor(AstNode node) { |
||||||
|
return (node is ConstructorDeclaration); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetConstructorNodeInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
var constructorNode = (ConstructorDeclaration)dataNode; |
||||||
|
node.Name = constructorNode.Name; |
||||||
|
|
||||||
|
if (colorizeNode) |
||||||
|
node.ForegroundBrush = Brushes.Black; |
||||||
|
|
||||||
|
node.Name += " " |
||||||
|
+ constructorNode.LParToken |
||||||
|
+ (showExtendedInfos ? GetParameterDeclsAsString(constructorNode.Parameters) : "") |
||||||
|
+ constructorNode.RParToken; |
||||||
|
|
||||||
|
switch(GetAccessModifier(dataNode)) { |
||||||
|
case Modifiers.Public: |
||||||
|
node.IconName = "Icons.16x16.Method"; |
||||||
|
break; |
||||||
|
case Modifiers.Protected: |
||||||
|
node.IconName = "Icons.16x16.ProtectedMethod"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
node.IconName = "Icons.16x16.PrivateMethod"; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsDocument(AstNode node) { |
||||||
|
return (node is SyntaxTree); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void SetDocumentNodeInfos(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
node.Name = Path.GetFileName(((SyntaxTree)dataNode).FileName); |
||||||
|
node.IconName = "C#.File.FullFile"; |
||||||
|
node.IsExpanded = true; |
||||||
|
node.Weight = FontWeights.Bold; |
||||||
|
|
||||||
|
if (colorizeNode) |
||||||
|
node.ForegroundBrush = Brushes.Black; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,128 @@ |
|||||||
|
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using ICSharpCode.SharpDevelop; |
||||||
|
using ICSharpCode.AvalonEdit.Folding; |
||||||
|
using System.Linq; |
||||||
|
|
||||||
|
namespace CSharpBinding.OutlinePad |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// RemoveNodeCommand.
|
||||||
|
/// </summary>
|
||||||
|
public class RemoveNodeCommand : SimpleCommand |
||||||
|
{ |
||||||
|
public override bool CanExecute(object parameter) { |
||||||
|
var node = parameter as CSharpOutlineNode; |
||||||
|
if (node == null) |
||||||
|
return false; |
||||||
|
if (node.StartMarker == null || node.EndMarker == null) |
||||||
|
return false; |
||||||
|
if (node.StartMarker.IsDeleted || node.EndMarker.IsDeleted) |
||||||
|
return false; |
||||||
|
if (node.EndMarker.Offset == 0) |
||||||
|
return false; |
||||||
|
if (node.EndMarker.Offset < node.StartMarker.Offset) |
||||||
|
return false; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override void Execute(object parameter) { |
||||||
|
var node = parameter as CSharpOutlineNode; |
||||||
|
node.Editor.Document.Remove(node.StartMarker.Offset, node.EndMarker.Offset - node.StartMarker.Offset); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SelectRegionCommand.
|
||||||
|
/// </summary>
|
||||||
|
public class SelectRegionCommand : SimpleCommand |
||||||
|
{ |
||||||
|
public override bool CanExecute(object parameter) { |
||||||
|
var node = parameter as CSharpOutlineNode; |
||||||
|
if (node == null) |
||||||
|
return false; |
||||||
|
if (node.StartMarker == null || node.EndMarker == null) |
||||||
|
return false; |
||||||
|
if (node.StartMarker.IsDeleted || node.EndMarker.IsDeleted) |
||||||
|
return false; |
||||||
|
if (node.EndMarker.Offset == 0) |
||||||
|
return false; |
||||||
|
if (node.EndMarker.Offset < node.StartMarker.Offset) |
||||||
|
return false; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override void Execute(object parameter) { |
||||||
|
var node = parameter as CSharpOutlineNode; |
||||||
|
node.Editor.Select(node.StartMarker.Offset, node.EndMarker.Offset - node.StartMarker.Offset); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HandleFoldingCommand
|
||||||
|
/// </summary>
|
||||||
|
public class HandleFoldingCommand : SimpleCommand |
||||||
|
{ |
||||||
|
public override bool CanExecute(object parameter) { |
||||||
|
var node = parameter as CSharpOutlineNode; |
||||||
|
if (node == null) |
||||||
|
return false; |
||||||
|
if (node.StartMarker == null || node.EndMarker == null) |
||||||
|
return false; |
||||||
|
if (node.StartMarker.IsDeleted || node.EndMarker.IsDeleted) |
||||||
|
return false; |
||||||
|
if (node.EndMarker.Offset == 0) |
||||||
|
return false; |
||||||
|
if (node.EndMarker.Offset < node.StartMarker.Offset) |
||||||
|
return false; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override void Execute(object parameter) { |
||||||
|
var node = parameter as CSharpOutlineNode; |
||||||
|
|
||||||
|
if (node == null || node.Editor == null) |
||||||
|
return; |
||||||
|
|
||||||
|
FoldingManager foldingManager = node.Editor.GetService(typeof(FoldingManager)) as FoldingManager; |
||||||
|
|
||||||
|
if (foldingManager == null) |
||||||
|
return; |
||||||
|
|
||||||
|
// The endline is, where the first child starts. The folding has to be
|
||||||
|
// between the StartMarker.Line of the current node and the StartMarker.Line of the first child.
|
||||||
|
int endLine = node.StartMarker.Line; |
||||||
|
var firstChild = node.Children.FirstOrDefault(); |
||||||
|
if (firstChild != null) { |
||||||
|
endLine = (firstChild as CSharpOutlineNode).StartMarker.Line; |
||||||
|
} |
||||||
|
|
||||||
|
FoldingSection folding = foldingManager.GetNextFolding(node.StartMarker.Offset); |
||||||
|
|
||||||
|
if (folding == null) |
||||||
|
return; |
||||||
|
// is next folding below the endline, the it belongs to a other node
|
||||||
|
if (node.Editor.Document.GetLineForOffset(folding.StartOffset).LineNumber >= endLine) |
||||||
|
return; |
||||||
|
|
||||||
|
folding.IsFolded = !node.IsExpanded; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
<DockPanel x:Class="CSharpBinding.OutlinePad.CSharpOutlineContentHost" |
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||||
|
xmlns:treeview="http://icsharpcode.net/sharpdevelop/treeview" |
||||||
|
> |
||||||
|
<treeview:SharpTreeView |
||||||
|
x:Name="treeView" |
||||||
|
AllowDrop="True" |
||||||
|
AllowDropOrder="True" |
||||||
|
MouseDoubleClick="TreeViewMouseDoubleClick" |
||||||
|
MouseLeftButtonUp="TreeView_MouseLeftButtonUp" |
||||||
|
/> |
||||||
|
</DockPanel> |
@ -0,0 +1,323 @@ |
|||||||
|
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Linq; |
||||||
|
using System.Windows.Controls; |
||||||
|
using System.Windows.Input; |
||||||
|
using System.Windows.Threading; |
||||||
|
using ICSharpCode.Core; |
||||||
|
using ICSharpCode.NRefactory; |
||||||
|
using ICSharpCode.NRefactory.CSharp; |
||||||
|
using ICSharpCode.SharpDevelop; |
||||||
|
using ICSharpCode.SharpDevelop.Editor; |
||||||
|
using ICSharpCode.SharpDevelop.Gui; |
||||||
|
using ICSharpCode.SharpDevelop.Parser; |
||||||
|
using CSharpBinding.Parser; |
||||||
|
|
||||||
|
namespace CSharpBinding.OutlinePad |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for CSharpOutlineContentHost.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class CSharpOutlineContentHost : DockPanel, IOutlineContentHost, IDisposable |
||||||
|
{ |
||||||
|
ITextEditor editor; |
||||||
|
DispatcherTimer updateTreeTimer = new DispatcherTimer(); |
||||||
|
const double updateDelayMilliseconds = 1000; |
||||||
|
DispatcherTimer scrollToNodeTimer = new DispatcherTimer(); |
||||||
|
const double scrollDelayMilliseconds = 200; |
||||||
|
SyntaxTree syntaxTree; |
||||||
|
TextLocation? lastCaretLocation; |
||||||
|
CSharpOutlineNode selectedNode = null; |
||||||
|
const bool optionSelectActiveTreeNode = true; |
||||||
|
const bool optionSelectRange = false; |
||||||
|
|
||||||
|
public CSharpOutlineContentHost(ITextEditor editor) { |
||||||
|
this.editor = editor; |
||||||
|
this.editor.Caret.LocationChanged += CaretLocationChanged; |
||||||
|
|
||||||
|
InitializeComponent(); |
||||||
|
|
||||||
|
SD.ParserService.ParseInformationUpdated += ParseInfoUpdated; |
||||||
|
|
||||||
|
this.updateTreeTimer.Interval = TimeSpan.FromMilliseconds(updateDelayMilliseconds); |
||||||
|
this.updateTreeTimer.Tick += this.UpdateTreeTimer_Tick; |
||||||
|
|
||||||
|
this.scrollToNodeTimer.Interval = TimeSpan.FromMilliseconds(scrollDelayMilliseconds); |
||||||
|
this.scrollToNodeTimer.Tick += this.ScrollToNodeTimer_Tick; |
||||||
|
} |
||||||
|
|
||||||
|
void ParseInfoUpdated(object sender, ParseInformationEventArgs e) { |
||||||
|
if (this.editor == null || !FileUtility.IsEqualFileName(this.editor.FileName, e.FileName)) |
||||||
|
return; |
||||||
|
|
||||||
|
var parseInfo = e.NewParseInformation as CSharpFullParseInformation; |
||||||
|
if (parseInfo != null && parseInfo.SyntaxTree != null) { |
||||||
|
this.updateTreeTimer.Stop(); |
||||||
|
this.syntaxTree = parseInfo.SyntaxTree; |
||||||
|
this.updateTreeTimer.Start(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void CaretLocationChanged(object sender, EventArgs e) |
||||||
|
{ |
||||||
|
SelectActiveTreeNode(); |
||||||
|
} |
||||||
|
|
||||||
|
void SelectActiveTreeNode() { |
||||||
|
if (!optionSelectActiveTreeNode) |
||||||
|
return; |
||||||
|
// prevent unnecessary looping, when both CaretLocationChanged and ParseUpdateChanged are fired.
|
||||||
|
if (this.lastCaretLocation.HasValue && this.lastCaretLocation == this.editor.Caret.Location) |
||||||
|
return; |
||||||
|
|
||||||
|
this.lastCaretLocation = this.editor.Caret.Location; |
||||||
|
selectedNode = null; |
||||||
|
FindNodeFromLocation(this.editor.Caret.Location, treeView.Root as CSharpOutlineNode); |
||||||
|
if (selectedNode != null && treeView.SelectedItem != selectedNode) { |
||||||
|
treeView.SelectedItem = selectedNode; |
||||||
|
|
||||||
|
if (!scrollToNodeTimer.IsEnabled) { |
||||||
|
scrollToNodeTimer.Start(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool IsRangeInside(TextLocation outerStartLocation, TextLocation outerEndLocation, |
||||||
|
TextLocation innerStartLocation, TextLocation innerEndLocation) { |
||||||
|
if (outerStartLocation.IsEmpty || outerStartLocation.IsInfinite() || |
||||||
|
outerEndLocation.IsEmpty || outerEndLocation.IsInfinite() || |
||||||
|
innerStartLocation.IsEmpty || innerStartLocation.IsInfinite() || |
||||||
|
innerEndLocation.IsEmpty || innerEndLocation.IsInfinite()) |
||||||
|
return false; |
||||||
|
|
||||||
|
const int virtualLineLength = 200; |
||||||
|
var outerRange = (outerEndLocation.Line - outerStartLocation.Line) * virtualLineLength - outerStartLocation.Column + outerEndLocation.Column; |
||||||
|
var innerRange = (innerEndLocation.Line - innerStartLocation.Line) * virtualLineLength - innerStartLocation.Column + innerEndLocation.Column; |
||||||
|
return innerRange < outerRange; |
||||||
|
} |
||||||
|
|
||||||
|
void FindNodeFromLocation(TextLocation location, CSharpOutlineNode node) { |
||||||
|
if (node == null) |
||||||
|
return; |
||||||
|
if (node.StartMarker.IsDeleted || node.EndMarker.IsDeleted) |
||||||
|
return; |
||||||
|
|
||||||
|
if (location.IsInside(node.StartMarker.Location, node.EndMarker.Location) |
||||||
|
&& (selectedNode == null || IsRangeInside(selectedNode.AstNodeItem.StartLocation, selectedNode.AstNodeItem.EndLocation, |
||||||
|
node.AstNodeItem.StartLocation, node.AstNodeItem.EndLocation))) { |
||||||
|
selectedNode = node; |
||||||
|
} |
||||||
|
|
||||||
|
foreach(var child in node.Children) { |
||||||
|
FindNodeFromLocation(location, child as CSharpOutlineNode); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void UpdateTreeTimer_Tick(Object sender, EventArgs args) { |
||||||
|
this.updateTreeTimer.Stop(); |
||||||
|
this.UpdateTree(this.syntaxTree); |
||||||
|
this.SelectActiveTreeNode(); |
||||||
|
} |
||||||
|
|
||||||
|
void ScrollToNodeTimer_Tick(Object sender, EventArgs args) { |
||||||
|
this.scrollToNodeTimer.Stop(); |
||||||
|
if (selectedNode != null) { |
||||||
|
treeView.ScrollIntoView(selectedNode); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void UpdateTree(AstNode syntaxTree) { |
||||||
|
if (syntaxTree == null) |
||||||
|
return; |
||||||
|
|
||||||
|
if (treeView.Root == null) { |
||||||
|
treeView.Root = new CSharpOutlineNode(); |
||||||
|
SetNodeInfos(treeView.Root as CSharpOutlineNode, null, syntaxTree); |
||||||
|
} |
||||||
|
|
||||||
|
this.UpdateNode(treeView.Root as CSharpOutlineNode, syntaxTree); |
||||||
|
} |
||||||
|
|
||||||
|
void UpdateNode(CSharpOutlineNode node, AstNode dataNode) { |
||||||
|
if (dataNode == null || node == null) |
||||||
|
return; |
||||||
|
|
||||||
|
SetNodeInfos(node, null, dataNode); |
||||||
|
|
||||||
|
// Filter the children, for only the needed/wanted nodes
|
||||||
|
var dataChildren = dataNode.Children.Where(childNode => AstNodeHelper.IsAllowedNode(childNode)).ToList(); |
||||||
|
|
||||||
|
int childrenCount = node.Children.Count; |
||||||
|
int dataCount = dataChildren.Count; |
||||||
|
|
||||||
|
for (int i = 0; i < Math.Max(childrenCount, dataCount); i++) { |
||||||
|
if (i >= childrenCount) { |
||||||
|
node.Children.Add(BuildNode(node, dataChildren[i])); |
||||||
|
|
||||||
|
} else if (i >= dataCount) { |
||||||
|
while (node.Children.Count > dataCount) |
||||||
|
node.Children.RemoveAt(dataCount); |
||||||
|
|
||||||
|
} else { |
||||||
|
// Create new node, the old node doesn´t update (seems to be a bug)
|
||||||
|
node.Children[i] = new CSharpOutlineNode(); |
||||||
|
UpdateNode(node.Children[i] as CSharpOutlineNode, dataChildren[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
CSharpOutlineNode BuildNode(CSharpOutlineNode parentNode, AstNode dataNode) { |
||||||
|
|
||||||
|
var node = new CSharpOutlineNode(); |
||||||
|
SetNodeInfos(node, parentNode, dataNode); |
||||||
|
|
||||||
|
// Filter the children, for only the needed/wanted nodes
|
||||||
|
var dataChildren = dataNode.Children.Where(v => AstNodeHelper.IsAllowedNode(v)).ToList(); |
||||||
|
foreach (var child in dataChildren) { |
||||||
|
node.Children.Add(BuildNode(node, child)); |
||||||
|
} |
||||||
|
return node; |
||||||
|
} |
||||||
|
|
||||||
|
void SetNodeInfos(CSharpOutlineNode node, CSharpOutlineNode parentNode, AstNode dataNode) { |
||||||
|
|
||||||
|
int startOffset = 0; |
||||||
|
int textLength = Math.Max(editor.Document.TextLength - 1,0); |
||||||
|
if (!dataNode.StartLocation.IsValid() || dataNode.StartLocation.Line > editor.Document.LineCount) |
||||||
|
startOffset = 0; |
||||||
|
else |
||||||
|
startOffset = editor.Document.GetOffset(dataNode.StartLocation); |
||||||
|
|
||||||
|
int endOffset = 0; |
||||||
|
if (!dataNode.EndLocation.IsValid() || dataNode.EndLocation.Line > editor.Document.LineCount) |
||||||
|
endOffset = textLength; |
||||||
|
else |
||||||
|
endOffset = editor.Document.GetOffset(dataNode.EndLocation); |
||||||
|
|
||||||
|
node.AstNodeItem = dataNode; |
||||||
|
node.StartMarker = editor.Document.CreateAnchor(MinMax(startOffset, 0, textLength)); |
||||||
|
node.EndMarker = editor.Document.CreateAnchor(MinMax(endOffset, 0, textLength)); |
||||||
|
node.Editor = editor; |
||||||
|
node.Parent = parentNode; |
||||||
|
|
||||||
|
if (AstNodeHelper.IsDocument(dataNode)) |
||||||
|
AstNodeHelper.SetDocumentNodeInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsNameSpace(dataNode)) |
||||||
|
AstNodeHelper.SetNameSpaceNodeInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsRegionStart(dataNode)) |
||||||
|
AstNodeHelper.SetRegionStartInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsRegionEnd(dataNode)) |
||||||
|
AstNodeHelper.SetRegionEndInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsClass(dataNode)) |
||||||
|
AstNodeHelper.SetClassNodeInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsInterface(dataNode)) |
||||||
|
AstNodeHelper.SetInterfaceNodeInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsConstructor(dataNode)) |
||||||
|
AstNodeHelper.SetConstructorNodeInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsField(dataNode)) |
||||||
|
AstNodeHelper.SetFieldNodeInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsProperty(dataNode)) |
||||||
|
AstNodeHelper.SetPropertyNodeInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsMethod(dataNode)) |
||||||
|
AstNodeHelper.SetMethodNodeInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsEvent(dataNode)) |
||||||
|
AstNodeHelper.SetEventNodeInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsDelegate(dataNode)) |
||||||
|
AstNodeHelper.SetDelegateNodeInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsIndexer(dataNode)) |
||||||
|
AstNodeHelper.SetIndexerNodeInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsEnum(dataNode)) |
||||||
|
AstNodeHelper.SetEnumNodeInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsEnumMember(dataNode)) |
||||||
|
AstNodeHelper.SetEnumMemberNodeInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsStruct(dataNode)) |
||||||
|
AstNodeHelper.SetStructNodeInfos(node, dataNode); |
||||||
|
|
||||||
|
if (AstNodeHelper.IsOperator(dataNode)) |
||||||
|
AstNodeHelper.SetOperatorNodeInfos(node, dataNode); |
||||||
|
} |
||||||
|
|
||||||
|
static int MinMax(int value, int lower, int upper) { |
||||||
|
return Math.Min(Math.Max(value, lower), upper); |
||||||
|
} |
||||||
|
|
||||||
|
void TreeView_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) |
||||||
|
{ |
||||||
|
var node = treeView.SelectedItem as CSharpOutlineNode; |
||||||
|
if (node == null) |
||||||
|
return; |
||||||
|
|
||||||
|
if (optionSelectRange) |
||||||
|
editor.Select(node.StartMarker.Offset, node.EndMarker.Offset - node.StartMarker.Offset); |
||||||
|
FileService.JumpToFilePosition(this.editor.FileName, node.AstNodeItem.StartLocation.Line, node.AstNodeItem.StartLocation.Column); |
||||||
|
} |
||||||
|
|
||||||
|
void TreeViewMouseDoubleClick(object sender, MouseButtonEventArgs e) { |
||||||
|
} |
||||||
|
|
||||||
|
public object OutlineContent { |
||||||
|
get { return this; } |
||||||
|
} |
||||||
|
|
||||||
|
public void Dispose() { |
||||||
|
SD.ParserService.ParseInformationUpdated -= ParseInfoUpdated; |
||||||
|
|
||||||
|
if (this.editor != null) { |
||||||
|
if (this.editor.Caret != null) |
||||||
|
this.editor.Caret.LocationChanged -= CaretLocationChanged; |
||||||
|
this.editor = null; |
||||||
|
} |
||||||
|
|
||||||
|
this.syntaxTree = null; |
||||||
|
this.lastCaretLocation = null; |
||||||
|
this.selectedNode = null; |
||||||
|
|
||||||
|
if (this.updateTreeTimer != null) { |
||||||
|
this.updateTreeTimer.Stop(); |
||||||
|
this.updateTreeTimer.Tick -= this.UpdateTreeTimer_Tick; |
||||||
|
this.updateTreeTimer = null; |
||||||
|
} |
||||||
|
|
||||||
|
if (this.scrollToNodeTimer != null) { |
||||||
|
this.scrollToNodeTimer.Stop(); |
||||||
|
this.scrollToNodeTimer.Tick -= this.ScrollToNodeTimer_Tick; |
||||||
|
this.scrollToNodeTimer = null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,169 @@ |
|||||||
|
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Linq; |
||||||
|
using System.Windows; |
||||||
|
using System.Windows.Media; |
||||||
|
using ICSharpCode.NRefactory.Editor; |
||||||
|
using ICSharpCode.SharpDevelop; |
||||||
|
using ICSharpCode.SharpDevelop.Editor; |
||||||
|
using ICSharpCode.TreeView; |
||||||
|
using ICSharpCode.NRefactory.CSharp; |
||||||
|
using ICSharpCode.Core.Presentation; |
||||||
|
using System.Windows.Controls; |
||||||
|
|
||||||
|
namespace CSharpBinding.OutlinePad |
||||||
|
{ |
||||||
|
class CSharpOutlineNode : SharpTreeNode |
||||||
|
{ |
||||||
|
string name, iconName; |
||||||
|
|
||||||
|
public string Name { |
||||||
|
get { return name; } |
||||||
|
set { |
||||||
|
if (this.name != value) { |
||||||
|
this.name = value; |
||||||
|
this.RaisePropertyChanged("Text"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override object ToolTip { |
||||||
|
get { return this.GetSourceText(); } |
||||||
|
} |
||||||
|
|
||||||
|
public ITextAnchor StartMarker { get; set; } |
||||||
|
public ITextAnchor EndMarker { get; set; } |
||||||
|
public ITextEditor Editor { get; set; } |
||||||
|
public AstNode AstNodeItem { get; set; } |
||||||
|
public new CSharpOutlineNode Parent { get; set; } |
||||||
|
|
||||||
|
public string IconName { |
||||||
|
get { return iconName; } |
||||||
|
set { |
||||||
|
if (iconName != value) { |
||||||
|
iconName = value; |
||||||
|
this.RaisePropertyChanged("Icon"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public string GetSourceText() { |
||||||
|
if (StartMarker.IsDeleted || EndMarker.IsDeleted) |
||||||
|
return string.Empty; |
||||||
|
|
||||||
|
return Editor.Document.GetText(StartMarker.Offset, EndMarker.Offset - StartMarker.Offset); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool CanDelete(SharpTreeNode[] nodes) { |
||||||
|
return nodes.OfType<CSharpOutlineNode>().All(n => n.Parent != null); |
||||||
|
} |
||||||
|
|
||||||
|
public override void Delete(SharpTreeNode[] nodes) { |
||||||
|
DeleteWithoutConfirmation(nodes); |
||||||
|
} |
||||||
|
|
||||||
|
public override void DeleteWithoutConfirmation(SharpTreeNode[] nodes) { |
||||||
|
foreach (CSharpOutlineNode CSharpNode in nodes.OfType<CSharpOutlineNode>()) { |
||||||
|
CSharpNode.DeleteCore(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void DeleteCore() |
||||||
|
{ |
||||||
|
Editor.Document.Remove(StartMarker.Offset, EndMarker.Offset - StartMarker.Offset); |
||||||
|
} |
||||||
|
|
||||||
|
public override object Text { |
||||||
|
get { return Name; } |
||||||
|
} |
||||||
|
|
||||||
|
public override object Icon { |
||||||
|
get { return !string.IsNullOrEmpty(this.IconName) |
||||||
|
? SD.ResourceService.GetImageSource(this.IconName) |
||||||
|
: null; } |
||||||
|
} |
||||||
|
|
||||||
|
public override Brush Foreground { |
||||||
|
get { return foregroundBrush ?? SystemColors.WindowTextBrush; } |
||||||
|
} |
||||||
|
|
||||||
|
Brush foregroundBrush; |
||||||
|
public Brush ForegroundBrush { |
||||||
|
get { |
||||||
|
return foregroundBrush; |
||||||
|
} |
||||||
|
set { |
||||||
|
foregroundBrush = value; |
||||||
|
RaisePropertyChanged("Foreground"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
FontWeight weight = FontWeights.Normal; |
||||||
|
public FontWeight Weight { |
||||||
|
get { |
||||||
|
return weight; |
||||||
|
} |
||||||
|
set { |
||||||
|
if (weight != value) { |
||||||
|
weight = value; |
||||||
|
RaisePropertyChanged("FontWeight"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
FontStyle style = FontStyles.Normal; |
||||||
|
public FontStyle Style { |
||||||
|
get { |
||||||
|
return style; |
||||||
|
} |
||||||
|
set { |
||||||
|
if (style != value) { |
||||||
|
style = value; |
||||||
|
RaisePropertyChanged("FontStyle"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override FontWeight FontWeight { |
||||||
|
get { return Weight; } |
||||||
|
} |
||||||
|
|
||||||
|
public override FontStyle FontStyle { |
||||||
|
get { return Style; } |
||||||
|
} |
||||||
|
|
||||||
|
public override void ShowContextMenu(ContextMenuEventArgs e) |
||||||
|
{ |
||||||
|
MenuService.ShowContextMenu(null, this, "/SharpDevelop/Pads/OutlinePad/ContextMenu/NodeActions"); |
||||||
|
} |
||||||
|
|
||||||
|
protected override void OnExpanding() { |
||||||
|
var cmd = new HandleFoldingCommand(); |
||||||
|
if (cmd.CanExecute(this)) |
||||||
|
cmd.Execute(this); |
||||||
|
} |
||||||
|
|
||||||
|
protected override void OnCollapsing() { |
||||||
|
var cmd = new HandleFoldingCommand(); |
||||||
|
if (cmd.CanExecute(this)) |
||||||
|
cmd.Execute(this); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using ICSharpCode.NRefactory; |
||||||
|
|
||||||
|
namespace CSharpBinding.OutlinePad |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Description of TextLocationExtensions.
|
||||||
|
/// </summary>
|
||||||
|
static class TextLocationExtensions { |
||||||
|
|
||||||
|
public static bool IsInfinite(this TextLocation location) { |
||||||
|
return location == null || location.Line == int.MaxValue || location.Column == int.MaxValue; |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsValid(this TextLocation location) { |
||||||
|
if (location.IsEmpty) |
||||||
|
return false; |
||||||
|
if (location.IsInfinite()) |
||||||
|
return false; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsInside(this TextLocation location, TextLocation startLocation, TextLocation endLocation) { |
||||||
|
if (location.IsEmpty) |
||||||
|
return false; |
||||||
|
|
||||||
|
return location.Line >= startLocation.Line && |
||||||
|
(location.Line <= endLocation.Line || endLocation.Line == -1) && |
||||||
|
(location.Line != startLocation.Line || location.Column >= startLocation.Column) && |
||||||
|
(location.Line != endLocation.Line || location.Column <= endLocation.Column); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using ICSharpCode.NRefactory; |
||||||
|
|
||||||
|
namespace ICSharpCode.XamlBinding |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Description of TextLocationExtensions.
|
||||||
|
/// </summary>
|
||||||
|
static class TextLocationExtensions { |
||||||
|
|
||||||
|
public static bool IsInfinite(this TextLocation location) { |
||||||
|
return location == null || location.Line == int.MaxValue || location.Column == int.MaxValue; |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsValid(this TextLocation location) { |
||||||
|
if (location.IsEmpty) |
||||||
|
return false; |
||||||
|
if (location.IsInfinite()) |
||||||
|
return false; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsInside(this TextLocation location, TextLocation startLocation, TextLocation endLocation) { |
||||||
|
if (location.IsEmpty) |
||||||
|
return false; |
||||||
|
|
||||||
|
return location.Line >= startLocation.Line && |
||||||
|
(location.Line <= endLocation.Line || endLocation.Line == -1) && |
||||||
|
(location.Line != startLocation.Line || location.Column >= startLocation.Column) && |
||||||
|
(location.Line != endLocation.Line || location.Column <= endLocation.Column); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,263 @@ |
|||||||
|
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.IO; |
||||||
|
using System.Linq; |
||||||
|
using System.Windows.Controls; |
||||||
|
using System.Windows.Forms; |
||||||
|
using System.Windows.Input; |
||||||
|
using System.Windows.Threading; |
||||||
|
using ICSharpCode.Core; |
||||||
|
using ICSharpCode.NRefactory.Xml; |
||||||
|
using ICSharpCode.SharpDevelop; |
||||||
|
using ICSharpCode.SharpDevelop.Editor; |
||||||
|
using ICSharpCode.SharpDevelop.Gui; |
||||||
|
using ICSharpCode.SharpDevelop.Parser; |
||||||
|
using ICSharpCode.NRefactory; |
||||||
|
|
||||||
|
|
||||||
|
namespace ICSharpCode.XamlBinding |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for XamlOutlineContentHost.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class XamlOutlineContentHost : DockPanel, IOutlineContentHost, IDisposable |
||||||
|
{ |
||||||
|
ITextEditor editor; |
||||||
|
DispatcherTimer updateTreeTimer = new DispatcherTimer(); |
||||||
|
const double updateDelayMilliseconds = 500; |
||||||
|
DispatcherTimer scrollToNodeTimer = new DispatcherTimer(); |
||||||
|
const double scrollDelayMilliseconds = 200; |
||||||
|
AXmlDocument document; |
||||||
|
TextLocation? lastCaretLocation; |
||||||
|
XamlOutlineNode selectedNode = null; |
||||||
|
const bool optionSelectActiveTreeNode = true; |
||||||
|
const bool optionSelectRange = false; |
||||||
|
|
||||||
|
public XamlOutlineContentHost(ITextEditor editor) |
||||||
|
{ |
||||||
|
this.editor = editor; |
||||||
|
this.editor.Caret.LocationChanged += CaretLocationChanged; |
||||||
|
|
||||||
|
InitializeComponent(); |
||||||
|
|
||||||
|
SD.ParserService.ParseInformationUpdated += ParseInfoUpdated; |
||||||
|
|
||||||
|
this.updateTreeTimer.Interval = TimeSpan.FromMilliseconds(updateDelayMilliseconds); |
||||||
|
this.updateTreeTimer.Tick += this.UpdateTreeTimer_Tick; |
||||||
|
|
||||||
|
this.scrollToNodeTimer.Interval = TimeSpan.FromMilliseconds(scrollDelayMilliseconds); |
||||||
|
this.scrollToNodeTimer.Tick += this.ScrollToNodeTimer_Tick; |
||||||
|
} |
||||||
|
|
||||||
|
void ParseInfoUpdated(object sender, ParseInformationEventArgs e) |
||||||
|
{ |
||||||
|
if (this.editor == null || !FileUtility.IsEqualFileName(this.editor.FileName, e.FileName)) |
||||||
|
return; |
||||||
|
|
||||||
|
var parseInfo = e.NewParseInformation as XamlFullParseInformation; |
||||||
|
if (parseInfo != null && parseInfo.Document != null) { |
||||||
|
this.updateTreeTimer.Stop(); |
||||||
|
this.document = parseInfo.Document; |
||||||
|
this.updateTreeTimer.Start(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void CaretLocationChanged(object sender, EventArgs e) |
||||||
|
{ |
||||||
|
SelectActiveTreeNode(); |
||||||
|
} |
||||||
|
|
||||||
|
void SelectActiveTreeNode() { |
||||||
|
if (!optionSelectActiveTreeNode) |
||||||
|
return; |
||||||
|
// prevent unnecessary looping, when both CaretLocationChanged and ParseUpdateChanged are fired.
|
||||||
|
if (this.lastCaretLocation.HasValue && this.lastCaretLocation == this.editor.Caret.Location) |
||||||
|
return; |
||||||
|
|
||||||
|
this.lastCaretLocation = this.editor.Caret.Location; |
||||||
|
selectedNode = null; |
||||||
|
FindNodeFromLocation(this.editor.Caret.Location, treeView.Root as XamlOutlineNode); |
||||||
|
if (selectedNode != null && treeView.SelectedItem != selectedNode) { |
||||||
|
treeView.SelectedItem = selectedNode; |
||||||
|
if (!scrollToNodeTimer.IsEnabled) { |
||||||
|
scrollToNodeTimer.Start(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool IsRangeInside(TextLocation outerStartLocation, TextLocation outerEndLocation, |
||||||
|
TextLocation innerStartLocation, TextLocation innerEndLocation) { |
||||||
|
if (outerStartLocation.IsEmpty || outerStartLocation.IsInfinite() || |
||||||
|
outerEndLocation.IsEmpty || outerEndLocation.IsInfinite() || |
||||||
|
innerStartLocation.IsEmpty || innerStartLocation.IsInfinite() || |
||||||
|
innerEndLocation.IsEmpty || innerEndLocation.IsInfinite()) |
||||||
|
return false; |
||||||
|
|
||||||
|
const int virtualLineLength = 200; |
||||||
|
var outerRange = (outerEndLocation.Line - outerStartLocation.Line) * virtualLineLength - outerStartLocation.Column + outerEndLocation.Column; |
||||||
|
var innerRange = (innerEndLocation.Line - innerStartLocation.Line) * virtualLineLength - innerStartLocation.Column + innerEndLocation.Column; |
||||||
|
return innerRange < outerRange; |
||||||
|
} |
||||||
|
|
||||||
|
void FindNodeFromLocation(TextLocation location, XamlOutlineNode node) { |
||||||
|
if (node == null) |
||||||
|
return; |
||||||
|
if (node.StartMarker.IsDeleted || node.EndMarker.IsDeleted) |
||||||
|
return; |
||||||
|
|
||||||
|
if (location.IsInside(node.StartMarker.Location, node.EndMarker.Location) |
||||||
|
&& (selectedNode == null || IsRangeInside(selectedNode.StartLocation, selectedNode.EndLocation, |
||||||
|
node.StartLocation, node.EndLocation))) { |
||||||
|
selectedNode = node; |
||||||
|
} |
||||||
|
|
||||||
|
foreach(var child in node.Children) { |
||||||
|
FindNodeFromLocation(location, child as XamlOutlineNode); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void UpdateTreeTimer_Tick(Object sender, EventArgs args) { |
||||||
|
this.updateTreeTimer.Stop(); |
||||||
|
this.UpdateTree(this.document); |
||||||
|
this.SelectActiveTreeNode(); |
||||||
|
} |
||||||
|
|
||||||
|
void ScrollToNodeTimer_Tick(Object sender, EventArgs args) { |
||||||
|
this.scrollToNodeTimer.Stop(); |
||||||
|
if (selectedNode != null) { |
||||||
|
treeView.ScrollIntoView(selectedNode); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void UpdateTree(AXmlDocument root) |
||||||
|
{ |
||||||
|
if (treeView.Root == null) { |
||||||
|
treeView.Root = new XamlOutlineNode { |
||||||
|
ElementName = "Document Root", |
||||||
|
Name = Path.GetFileName(editor.FileName), |
||||||
|
Editor = editor |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
UpdateNode(treeView.Root as XamlOutlineNode, root); |
||||||
|
} |
||||||
|
|
||||||
|
void UpdateNode(XamlOutlineNode node, AXmlObject dataNode) |
||||||
|
{ |
||||||
|
if (dataNode == null || node == null) |
||||||
|
return; |
||||||
|
int textLength = Math.Max(editor.Document.TextLength - 1,0); |
||||||
|
if (dataNode is AXmlElement) { |
||||||
|
var item = (AXmlElement)dataNode; |
||||||
|
node.Name = item.GetAttributeValue("Name") ?? item.GetAttributeValue(XamlConst.XamlNamespace, "Name"); |
||||||
|
node.ElementName = item.Name; |
||||||
|
} |
||||||
|
node.StartMarker = editor.Document.CreateAnchor(Utils.MinMax(dataNode.StartOffset, 0, textLength)); |
||||||
|
node.EndMarker = editor.Document.CreateAnchor(Utils.MinMax(dataNode.EndOffset, 0, textLength)); |
||||||
|
node.StartLocation = new TextLocation(node.StartMarker.Line, node.StartMarker.Column); |
||||||
|
node.EndLocation = new TextLocation(node.EndMarker.Line, node.EndMarker.Column); |
||||||
|
|
||||||
|
var dataChildren = dataNode.Children.OfType<AXmlElement>().ToList(); |
||||||
|
|
||||||
|
int childrenCount = node.Children.Count; |
||||||
|
int dataCount = dataChildren.Count; |
||||||
|
|
||||||
|
for (int i = 0; i < Math.Max(childrenCount, dataCount); i++) { |
||||||
|
if (i >= childrenCount) { |
||||||
|
node.Children.Add(BuildNode(dataChildren[i])); |
||||||
|
} else if (i >= dataCount) { |
||||||
|
while (node.Children.Count > dataCount) |
||||||
|
node.Children.RemoveAt(dataCount); |
||||||
|
} else { |
||||||
|
UpdateNode(node.Children[i] as XamlOutlineNode, dataChildren[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
XamlOutlineNode BuildNode(AXmlElement item) |
||||||
|
{ |
||||||
|
XamlOutlineNode node = new XamlOutlineNode { |
||||||
|
Name = item.GetAttributeValue("Name") ?? item.GetAttributeValue(XamlConst.XamlNamespace, "Name"), |
||||||
|
ElementName = item.Name, |
||||||
|
StartMarker = editor.Document.CreateAnchor(Utils.MinMax(item.StartOffset, 0, editor.Document.TextLength - 1)), |
||||||
|
EndMarker = editor.Document.CreateAnchor(Utils.MinMax(item.EndOffset, 0, editor.Document.TextLength - 1)), |
||||||
|
Editor = editor, |
||||||
|
XmlNodeItem = item |
||||||
|
}; |
||||||
|
|
||||||
|
node.StartLocation = new TextLocation(node.StartMarker.Line, node.StartMarker.Column); |
||||||
|
node.EndLocation = new TextLocation(node.EndMarker.Line, node.EndMarker.Column); |
||||||
|
node.IsExpanded = true; |
||||||
|
|
||||||
|
foreach (var child in item.Children.OfType<AXmlElement>()) |
||||||
|
node.Children.Add(BuildNode(child)); |
||||||
|
|
||||||
|
return node; |
||||||
|
} |
||||||
|
|
||||||
|
void TreeView_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) |
||||||
|
{ |
||||||
|
var node = treeView.SelectedItem as XamlOutlineNode; |
||||||
|
if (node == null) |
||||||
|
return; |
||||||
|
|
||||||
|
if (optionSelectRange) |
||||||
|
editor.Select(node.StartMarker.Offset, node.EndMarker.Offset - node.StartMarker.Offset); |
||||||
|
FileService.JumpToFilePosition(this.editor.FileName, node.StartMarker.Line, node.StartMarker.Column); |
||||||
|
} |
||||||
|
|
||||||
|
void TreeViewMouseDoubleClick(object sender, MouseButtonEventArgs e) |
||||||
|
{ |
||||||
|
// XamlOutlineNode node = treeView.SelectedItem as XamlOutlineNode;
|
||||||
|
// if (node == null) return;
|
||||||
|
// editor.Select(node.StartMarker.Offset, node.EndMarker.Offset - node.StartMarker.Offset);
|
||||||
|
} |
||||||
|
|
||||||
|
public object OutlineContent { |
||||||
|
get { return this; } |
||||||
|
} |
||||||
|
|
||||||
|
public void Dispose() { |
||||||
|
SD.ParserService.ParseInformationUpdated -= ParseInfoUpdated; |
||||||
|
|
||||||
|
if (this.editor != null) { |
||||||
|
if (this.editor.Caret != null) |
||||||
|
this.editor.Caret.LocationChanged -= CaretLocationChanged; |
||||||
|
this.editor = null; |
||||||
|
} |
||||||
|
|
||||||
|
this.document = null; |
||||||
|
this.lastCaretLocation = null; |
||||||
|
this.selectedNode = null; |
||||||
|
|
||||||
|
if (this.updateTreeTimer != null) { |
||||||
|
this.updateTreeTimer.Stop(); |
||||||
|
this.updateTreeTimer.Tick -= this.UpdateTreeTimer_Tick; |
||||||
|
this.updateTreeTimer = null; |
||||||
|
} |
||||||
|
|
||||||
|
if (this.scrollToNodeTimer != null) { |
||||||
|
this.scrollToNodeTimer.Stop(); |
||||||
|
this.scrollToNodeTimer.Tick -= this.ScrollToNodeTimer_Tick; |
||||||
|
this.scrollToNodeTimer = null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,134 +0,0 @@ |
|||||||
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
// software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
||||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
||||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
// substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
||||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
using System; |
|
||||||
using System.IO; |
|
||||||
using System.Linq; |
|
||||||
using System.Windows.Controls; |
|
||||||
using System.Windows.Input; |
|
||||||
|
|
||||||
using ICSharpCode.Core; |
|
||||||
using ICSharpCode.NRefactory.Xml; |
|
||||||
using ICSharpCode.SharpDevelop; |
|
||||||
using ICSharpCode.SharpDevelop.Editor; |
|
||||||
using ICSharpCode.SharpDevelop.Gui; |
|
||||||
using ICSharpCode.SharpDevelop.Parser; |
|
||||||
|
|
||||||
namespace ICSharpCode.XamlBinding |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Interaction logic for XamlOutlineContentHost.xaml
|
|
||||||
/// </summary>
|
|
||||||
public partial class XamlOutlineContentHost : DockPanel, IOutlineContentHost, IDisposable |
|
||||||
{ |
|
||||||
ITextEditor editor; |
|
||||||
|
|
||||||
public XamlOutlineContentHost(ITextEditor editor) |
|
||||||
{ |
|
||||||
this.editor = editor; |
|
||||||
|
|
||||||
InitializeComponent(); |
|
||||||
|
|
||||||
SD.ParserService.ParseInformationUpdated += ParseInfoUpdated; |
|
||||||
} |
|
||||||
|
|
||||||
void ParseInfoUpdated(object sender, ParseInformationEventArgs e) |
|
||||||
{ |
|
||||||
if (this.editor == null || !FileUtility.IsEqualFileName(this.editor.FileName, e.FileName)) |
|
||||||
return; |
|
||||||
|
|
||||||
var parseInfo = e.NewParseInformation as XamlFullParseInformation; |
|
||||||
if (parseInfo != null && parseInfo.Document != null) |
|
||||||
UpdateTree(parseInfo.Document); |
|
||||||
} |
|
||||||
|
|
||||||
void UpdateTree(AXmlDocument root) |
|
||||||
{ |
|
||||||
if (treeView.Root == null) { |
|
||||||
treeView.Root = new XamlOutlineNode { |
|
||||||
ElementName = "Document Root", |
|
||||||
Name = Path.GetFileName(editor.FileName), |
|
||||||
Editor = editor |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
UpdateNode(treeView.Root as XamlOutlineNode, root); |
|
||||||
} |
|
||||||
|
|
||||||
void UpdateNode(XamlOutlineNode node, AXmlObject dataNode) |
|
||||||
{ |
|
||||||
if (dataNode == null || node == null) |
|
||||||
return; |
|
||||||
if (dataNode is AXmlElement) { |
|
||||||
var item = (AXmlElement)dataNode; |
|
||||||
node.Name = item.GetAttributeValue("Name") ?? item.GetAttributeValue(XamlConst.XamlNamespace, "Name"); |
|
||||||
node.ElementName = item.Name; |
|
||||||
} |
|
||||||
node.Marker = editor.Document.CreateAnchor(Utils.MinMax(dataNode.StartOffset, 0, editor.Document.TextLength)); |
|
||||||
node.EndMarker = editor.Document.CreateAnchor(Utils.MinMax(dataNode.EndOffset, 0, editor.Document.TextLength)); |
|
||||||
|
|
||||||
var dataChildren = dataNode.Children.OfType<AXmlElement>().ToList(); |
|
||||||
|
|
||||||
int childrenCount = node.Children.Count; |
|
||||||
int dataCount = dataChildren.Count; |
|
||||||
|
|
||||||
for (int i = 0; i < Math.Max(childrenCount, dataCount); i++) { |
|
||||||
if (i >= childrenCount) { |
|
||||||
node.Children.Add(BuildNode(dataChildren[i])); |
|
||||||
} else if (i >= dataCount) { |
|
||||||
while (node.Children.Count > dataCount) |
|
||||||
node.Children.RemoveAt(dataCount); |
|
||||||
} else { |
|
||||||
UpdateNode(node.Children[i] as XamlOutlineNode, dataChildren[i]); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
XamlOutlineNode BuildNode(AXmlElement item) |
|
||||||
{ |
|
||||||
XamlOutlineNode node = new XamlOutlineNode { |
|
||||||
Name = item.GetAttributeValue("Name") ?? item.GetAttributeValue(XamlConst.XamlNamespace, "Name"), |
|
||||||
ElementName = item.Name, |
|
||||||
Marker = editor.Document.CreateAnchor(Utils.MinMax(item.StartOffset, 0, editor.Document.TextLength - 1)), |
|
||||||
EndMarker = editor.Document.CreateAnchor(Utils.MinMax(item.EndOffset, 0, editor.Document.TextLength - 1)), |
|
||||||
Editor = editor |
|
||||||
}; |
|
||||||
|
|
||||||
foreach (var child in item.Children.OfType<AXmlElement>()) |
|
||||||
node.Children.Add(BuildNode(child)); |
|
||||||
|
|
||||||
return node; |
|
||||||
} |
|
||||||
|
|
||||||
void TreeViewMouseDoubleClick(object sender, MouseButtonEventArgs e) |
|
||||||
{ |
|
||||||
XamlOutlineNode node = treeView.SelectedItem as XamlOutlineNode; |
|
||||||
if (node == null) return; |
|
||||||
editor.Select(node.Marker.Offset, node.EndMarker.Offset - node.Marker.Offset); |
|
||||||
} |
|
||||||
|
|
||||||
public object OutlineContent { |
|
||||||
get { return this; } |
|
||||||
} |
|
||||||
|
|
||||||
public void Dispose() |
|
||||||
{ |
|
||||||
SD.ParserService.ParseInformationUpdated -= ParseInfoUpdated; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue