From ccf71cc67e35673cdfd741cdc6cf0cc05e138ec5 Mon Sep 17 00:00:00 2001 From: JohnnyBravo75 Date: Thu, 24 Jul 2014 22:03:22 +0200 Subject: [PATCH] C# Outline Pad A c# outline pad, based on the syntax tree --- .../CSharpBinding/Project/CSharpBinding.addin | 11 + .../Project/CSharpBinding.csproj | 9 + .../Project/Src/CSharpTextEditorExtension.cs | 29 + .../Project/Src/OutlinePad/AstNodeHelper.cs | 524 ++++++++++++++++++ .../Src/OutlinePad/CSharpOutlineCommands.cs | 128 +++++ .../OutlinePad/CSharpOutlineContentHost.xaml | 13 + .../CSharpOutlineContentHost.xaml.cs | 352 ++++++++++++ .../Src/OutlinePad/CSharpOutlineNode.cs | 169 ++++++ .../Src/OutlinePad/ExtensionMethods.cs | 51 ++ .../Highlighting/HighlightingColorizer.cs | 3 + .../ICSharpCode.TreeView/SharpTreeNode.cs | 8 + 11 files changed, 1297 insertions(+) create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/AstNodeHelper.cs create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineCommands.cs create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineContentHost.xaml create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineContentHost.xaml.cs create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineNode.cs create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/ExtensionMethods.cs diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin index b70e3a2dcf..221dddc0f8 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin @@ -33,6 +33,17 @@ + + + + + diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj index 970be27a88..00a094c92b 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj @@ -115,6 +115,13 @@ CSharpProjectFormattingOptions.xaml Code + + + + CSharpOutlineContentHost.xaml + + + @@ -264,6 +271,7 @@ + @@ -274,6 +282,7 @@ + \ No newline at end of file diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpTextEditorExtension.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpTextEditorExtension.cs index 0851efb6a9..93a886b242 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpTextEditorExtension.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpTextEditorExtension.cs @@ -24,10 +24,14 @@ using System.Reflection; using ICSharpCode.AvalonEdit; using CSharpBinding.FormattingStrategy; using CSharpBinding.Refactoring; +using CSharpBinding.OutlinePad; using ICSharpCode.Core; using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Refactoring; +using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.AvalonEdit.Rendering; + namespace CSharpBinding { public class CSharpTextEditorExtension : ITextEditorExtension @@ -46,6 +50,8 @@ namespace CSharpBinding TextEditorOptions originalEditorOptions; + CSharpOutlineContentHost contentHost; + public void Attach(ITextEditor editor) { this.editor = editor; @@ -75,6 +81,17 @@ namespace CSharpBinding // Set TextEditor's options to same object originalEditorOptions = textEditor.Options; textEditor.Options = options.TextEditorOptions; + + // add the outline pad + var textView = textEditor.TextArea.TextView; + if (textView != null) { + if (SD.Workbench != null) { + // add the CSharpOutlineContentHost, which manages the tree view + contentHost = new CSharpOutlineContentHost(editor); + textView.Services.AddService(typeof(IOutlineContentHost), contentHost); + } + textView.Services.AddService(typeof(CSharpTextEditorExtension), this); + } } } @@ -83,13 +100,25 @@ namespace CSharpBinding var textEditor = editor.GetService(); if (textEditor != null) { var textView = textEditor.TextArea.TextView; + // Unregister our ITextEditorOptions instance from editor var optionsService = textView.GetService(); if ((optionsService != null) && (optionsService == options)) textView.Services.RemoveService(typeof(ITextEditorOptions)); + // Reset TextEditor options, too? if ((textEditor.Options != null) && (textEditor.Options == options.TextEditorOptions)) textEditor.Options = originalEditorOptions; + + // remove the outline pad + if (textView != null) { + if (contentHost != null) { + textView.Services.RemoveService(typeof(IOutlineContentHost)); + contentHost.Dispose(); + contentHost = null; + } + textView.Services.RemoveService(typeof(CSharpTextEditorExtension)); + } } codeManipulation.Dispose(); diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/AstNodeHelper.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/AstNodeHelper.cs new file mode 100644 index 0000000000..c193578637 --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/AstNodeHelper.cs @@ -0,0 +1,524 @@ +// 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. + +namespace CSharpBinding.OutlinePad +{ + 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; + + 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 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 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.ElementName = "****** " + 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.ElementName = "*********************"; + 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.ElementName = 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.ElementName = typeNode.Name; + if (colorizeNode) + node.ForegroundBrush = (classHighLighting.Foreground as SimpleHighlightingBrush).GetBrush(null); +// node.Weight = classHighLighting.FontWeight != null ? classHighLighting.FontWeight.Value : FontWeights.Normal; + node.IsExpanded = true; + + 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.ElementName = 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.ElementName = typeNode.Name; + if (colorizeNode) + node.ForegroundBrush = (classHighLighting.Foreground as SimpleHighlightingBrush).GetBrush(null); + node.IsExpanded = true; + + 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.ElementName = typeNode.Name + + typeNode.LChevronToken + + GetTypeParameterDeclsAsString(typeNode.TypeParameters) + + typeNode.RChevronToken; + if (colorizeNode) + node.ForegroundBrush = (classHighLighting.Foreground as SimpleHighlightingBrush).GetBrush(null); +// node.Weight = classHighLighting.FontWeight != null ? classHighLighting.FontWeight.Value : FontWeights.Normal; + node.IsExpanded = true; + + 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.ElementName = (varInitializer != null) + ? varInitializer.Name + : ""; + 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.ElementName = typeNode.Name; + if (colorizeNode) + node.ForegroundBrush = (interfaceHighLighting.Foreground as SimpleHighlightingBrush).GetBrush(null); + node.IsExpanded = true; + 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.ElementName = operatorNode.OperatorToken + + " " + operatorNode.OperatorTypeToken; + + if (showExtendedInfos) { + node.ElementName += " " + operatorNode.LParToken + + GetParameterDeclsAsString(operatorNode.Parameters) + + operatorNode.RParToken; + } + if (colorizeNode) + node.ForegroundBrush = Brushes.Black; + + 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.ElementName = methodNode.Name; + + node.ElementName += " " + 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.ElementName = 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.ElementName = propertyNode.Name; + if (colorizeNode) + node.ForegroundBrush = Brushes.Black; + if (showExtendedInfos) { + node.ElementName += " " + 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.ElementName = indexerNode.ReturnType.ToString() + " " + indexerNode.ThisToken.ToString() + + indexerNode.LBracketToken + + GetParameterDeclsAsString(indexerNode.Parameters) + + indexerNode.RBracketToken; + if (colorizeNode) + node.ForegroundBrush = Brushes.Black; + if (showExtendedInfos) { + node.ElementName += " " + 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.ElementName = 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.ElementName = constructorNode.Name; + if (colorizeNode) + node.ForegroundBrush = Brushes.Black; + + node.ElementName += " " + + 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.ElementName = Path.GetFileName(((SyntaxTree)dataNode).FileName); + node.IconName = "C#.File.FullFile"; + node.IsExpanded = true; + node.Weight = FontWeights.Bold; + if (colorizeNode) + node.ForegroundBrush = Brushes.Black; + } + } +} diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineCommands.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineCommands.cs new file mode 100644 index 0000000000..4815cc9211 --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineCommands.cs @@ -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. + +namespace CSharpBinding.OutlinePad +{ + using System; + using ICSharpCode.SharpDevelop; + using ICSharpCode.AvalonEdit.Folding; + using System.Linq; + + /// + /// RemoveNodeCommand. + /// + 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); + } + } + + /// + /// SelectRegionCommand. + /// + 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); + } + } + + /// + /// HandleFoldingCommand + /// + 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; + } + } +} diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineContentHost.xaml b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineContentHost.xaml new file mode 100644 index 0000000000..bda20483ed --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineContentHost.xaml @@ -0,0 +1,13 @@ + + + \ No newline at end of file diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineContentHost.xaml.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineContentHost.xaml.cs new file mode 100644 index 0000000000..ae470525c2 --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineContentHost.xaml.cs @@ -0,0 +1,352 @@ +// 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 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; +using System.Windows.Threading; +using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.AvalonEdit.Folding; + +namespace CSharpBinding.OutlinePad +{ + /// + /// Interaction logic for CSharpOutlineContentHost.xaml + /// + public partial class CSharpOutlineContentHost : DockPanel, IOutlineContentHost, IDisposable + { + ITextEditor editor; + DispatcherTimer updateTreeTimer = new DispatcherTimer(); + DispatcherTimer scrollToNodeTimer = new DispatcherTimer(); + SyntaxTree syntaxTree; + TextLocation? lastCaretLocation; + CSharpOutlineNode selectedNode = null; + static double refreshDelayInMilliSec = 1000; + static bool optionSelectActiveTreeNode = true; + static 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(refreshDelayInMilliSec); + this.updateTreeTimer.Tick += this.UpdateTreeTimer_Tick; + + this.scrollToNodeTimer.Interval = TimeSpan.FromMilliseconds(200); + 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; + // same line, mostly in the same region, no update needed (there is a small inaccuracy, when entering a method/member) +// if (this.lastCaretLocation.HasValue && this.lastCaretLocation.Value.Line == this.editor.Caret.Location.Line) +// 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) { + +// if (AstNodeHelper.IsRegionStart(dataChildren[i])) { +// var regionNode = new CSharpOutlineNode(); +// SetNodeInfos(regionNode, node, dataChildren[i]); +// node.Children.Add(regionNode); +// node = regionNode; +// continue; +// } +// if (AstNodeHelper.IsRegionEnd(dataChildren[i])) { +// node = node.Parent; +// continue; +// } + + node.Children.Add(BuildNode(node, dataChildren[i])); + + } else if (i >= dataCount) { + while (node.Children.Count > dataCount) + node.Children.RemoveAt(dataCount); + + } else { + 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) { +// if (AstNodeHelper.IsRegionStart(child)) { +// var regionNode = new CSharpOutlineNode(); +// SetNodeInfos(regionNode, node, child); +// node.Children.Add(regionNode); +// node = regionNode; +// continue; +// } +// if (AstNodeHelper.IsRegionEnd(child)) { +// node = node.Parent; +// continue; +// } + + 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; + } + } + + + } + +} diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineNode.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineNode.cs new file mode 100644 index 0000000000..ef4224e37b --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/CSharpOutlineNode.cs @@ -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.NRefactory.TypeSystem; +using ICSharpCode.Core.Presentation; +using System.Windows.Controls; +using ICSharpCode.AvalonEdit.Folding; + +namespace CSharpBinding.OutlinePad +{ + class CSharpOutlineNode : SharpTreeNode + { + string elementName, name, iconName; + + public string ElementName { + get { return elementName; } + set { + this.elementName = value; + this.RaisePropertyChanged("Text"); + } + } + + public string Name { + get { return name; } + set { + 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 { + iconName = value; + this.RaisePropertyChanged("Icon"); + } + } + + public string GetSourceText() { + if (StartMarker.IsDeleted || EndMarker.IsDeleted) + return ""; + + return Editor.Document.GetText(StartMarker.Offset, EndMarker.Offset - StartMarker.Offset); + } + + public override bool CanDelete(SharpTreeNode[] nodes) { + return nodes.OfType().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()) { + CSharpNode.DeleteCore(); + } + } + + void DeleteCore() + { + Editor.Document.Remove(StartMarker.Offset, EndMarker.Offset - StartMarker.Offset); + } + + public override object Text { + get { return (!string.IsNullOrEmpty(Name) ? ElementName + " (" + Name + ")" : ElementName); } + } + + 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 { + weight = value; + RaisePropertyChanged("FontWeight"); + } + } + FontStyle style = FontStyles.Normal; + public FontStyle Style { + get { + return style; + } + set { + 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); + } + } +} diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/ExtensionMethods.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/ExtensionMethods.cs new file mode 100644 index 0000000000..0b62999986 --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/OutlinePad/ExtensionMethods.cs @@ -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. + +namespace CSharpBinding.OutlinePad +{ + using System; + using ICSharpCode.NRefactory; + + /// + /// Description of TextLocationExtensions. + /// + 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); + } + } +} diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs index 68a625745e..fef10c4f68 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs @@ -330,6 +330,9 @@ namespace ICSharpCode.AvalonEdit.Highlighting // so it will always invalidate the next visual line when a folded line is constructed // and the highlighting stack has changed. + if (fromLineNumber > textView.Document.LineCount || toLineNumber > textView.Document.LineCount) + return; + if (fromLineNumber == toLineNumber) { textView.Redraw(textView.Document.GetLineByNumber(fromLineNumber)); } else { diff --git a/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNode.cs b/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNode.cs index 84426dd7e5..be50c48cff 100644 --- a/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNode.cs +++ b/src/Libraries/SharpTreeView/ICSharpCode.TreeView/SharpTreeNode.cs @@ -136,6 +136,14 @@ namespace ICSharpCode.TreeView get { return Parent != null ? Parent.Level + 1 : 0; } } + public virtual FontWeight FontWeight { + get { return FontWeights.Normal; } + } + + public virtual FontStyle FontStyle { + get { return FontStyles.Normal; } + } + public bool IsRoot { get { return Parent == null; }