From 6e16bab3190432610c0db7a03afae337689348cb Mon Sep 17 00:00:00 2001 From: Matt Ward Date: Sun, 6 Feb 2011 14:23:48 +0000 Subject: [PATCH 01/11] Fix null reference exception when opening a WiX dialog in the designer. --- .../Project/Src/DesignerViewContent.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs index 8c103d1316..f0a3ec5635 100644 --- a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs +++ b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs @@ -12,11 +12,11 @@ using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; - using ICSharpCode.Core; using ICSharpCode.FormsDesigner.Services; using ICSharpCode.FormsDesigner.UndoRedo; using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Gui; @@ -277,7 +277,7 @@ namespace ICSharpCode.FormsDesigner serviceContainer.AddService(typeof(DesignerOptionService), new SharpDevelopDesignerOptionService()); serviceContainer.AddService(typeof(ITypeDiscoveryService), new TypeDiscoveryService()); serviceContainer.AddService(typeof(MemberRelationshipService), new DefaultMemberRelationshipService()); - serviceContainer.AddService(typeof(ProjectResourceService), new ProjectResourceService(ParserService.GetParseInformation(this.DesignerCodeFile.FileName).CompilationUnit.ProjectContent)); + serviceContainer.AddService(typeof(ProjectResourceService), CreateProjectResourceService()); // Provide the ImageResourceEditor for all Image and Icon properties this.addedTypeDescriptionProviders.Add(typeof(Image), TypeDescriptor.AddAttributes(typeof(Image), new EditorAttribute(typeof(ImageResourceEditor), typeof(System.Drawing.Design.UITypeEditor)))); @@ -329,6 +329,21 @@ namespace ICSharpCode.FormsDesigner LoggingService.Info("Form Designer: END INITIALIZE"); } + ProjectResourceService CreateProjectResourceService() + { + IProjectContent projectContent = GetProjectContentForFile(); + return new ProjectResourceService(projectContent); + } + + IProjectContent GetProjectContentForFile() + { + ParseInformation parseInfo = ParserService.GetParseInformation(this.DesignerCodeFile.FileName); + if (parseInfo != null) { + return parseInfo.CompilationUnit.ProjectContent; + } + return DefaultProjectContent.DummyProjectContent; + } + bool hasUnmergedChanges; void MakeDirty() From 90e3b939a367dbb760d8ae1c33dacea3599d99a0 Mon Sep 17 00:00:00 2001 From: Matt Ward Date: Sun, 6 Feb 2011 22:01:58 +0000 Subject: [PATCH 02/11] Fix null reference exception when dragging controls from the toolbox to a WiX dialog when a project has not been opened. --- .../FormsDesigner/Project/Src/ToolboxProvider.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/ToolboxProvider.cs b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/ToolboxProvider.cs index aff68b55ea..84db6cd3e7 100644 --- a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/ToolboxProvider.cs +++ b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/ToolboxProvider.cs @@ -218,6 +218,10 @@ namespace ICSharpCode.FormsDesigner static IProject FindProjectContainingType(string type) { IProject currentProject = ProjectService.CurrentProject; + if (currentProject == null) { + return null; + } + foreach (IProject project in ProjectService.OpenSolution.Projects) { if (project != currentProject) { IProjectContent projectContent = ParserService.GetProjectContent(project); From 783fc0024e140cb8d41ff40b7f17016a04005102 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 8 Feb 2011 19:23:21 +0100 Subject: [PATCH 03/11] VisualLine.GetVisualColumnFloor bugfix: for positions after the end of the line; return the line's end column. --- .../ICSharpCode.AvalonEdit/Rendering/VisualLine.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs index 47744a5942..2e4136b41a 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs @@ -53,7 +53,7 @@ namespace ICSharpCode.AvalonEdit.Rendering /// Gets the start offset of the VisualLine inside the document. /// This is equivalent to FirstDocumentLine.Offset. /// - public int StartOffset { + public int StartOffset { get { return FirstDocumentLine.Offset; } @@ -323,6 +323,12 @@ namespace ICSharpCode.AvalonEdit.Rendering public int GetVisualColumnFloor(Point point) { TextLine textLine = GetTextLineByVisualYPosition(point.Y); + if (point.X > textLine.WidthIncludingTrailingWhitespace) { + // GetCharacterHitFromDistance returns a hit with FirstCharacterIndex=last character inline + // and TrailingLength=1 when clicking behind the line, so the floor function needs to handle this case + // specially end return the line's end column instead. + return GetTextLineVisualStartColumn(textLine) + textLine.Length; + } CharacterHit ch = textLine.GetCharacterHitFromDistance(point.X); return ch.FirstCharacterIndex; } From f2f61d6c5e2f29c51b5bdcf8b607df6cb447dcee Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 11 Feb 2011 23:37:58 +0100 Subject: [PATCH 04/11] Fixed bug in window bound restore code (introduced in 1230d8a3920da8f2050c39e8ffc3b3f9c5a2f2b2) --- src/Main/Base/Project/Src/Gui/Workbench/WpfWorkbench.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Main/Base/Project/Src/Gui/Workbench/WpfWorkbench.cs b/src/Main/Base/Project/Src/Gui/Workbench/WpfWorkbench.cs index 18197d439c..d7f7de022a 100644 --- a/src/Main/Base/Project/Src/Gui/Workbench/WpfWorkbench.cs +++ b/src/Main/Base/Project/Src/Gui/Workbench/WpfWorkbench.cs @@ -559,7 +559,8 @@ namespace ICSharpCode.SharpDevelop.Gui this.Top = bounds.Top; this.Width = bounds.Width; this.Height = bounds.Height; - this.WindowState = memento.Get("WindowState", System.Windows.WindowState.Maximized); + lastNonMinimizedWindowState = memento.Get("WindowState", System.Windows.WindowState.Maximized); + this.WindowState = lastNonMinimizedWindowState; } protected override void OnClosing(CancelEventArgs e) From 5280d731ce3473e5df5c8da04c2825627b8778a8 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 11 Feb 2011 23:47:49 +0100 Subject: [PATCH 05/11] Deactivate code completion when non-default input handler (e.g. incremental search) is used. --- src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs index 57c364d3d6..c235712264 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs @@ -401,6 +401,10 @@ namespace ICSharpCode.AvalonEdit.AddIn if (!CodeCompletionOptions.EnableCodeCompletion) return; + TextArea textArea = GetTextEditorFromSender(sender).TextArea; + if (textArea.ActiveInputHandler != textArea.DefaultInputHandler) + return; // deactivate CC for non-default input handlers + ITextEditor adapter = GetAdapterFromSender(sender); foreach (char c in e.Text) { From 540b66cc3723d8df875882190a16bfa897ee78ce Mon Sep 17 00:00:00 2001 From: Matt Ward Date: Tue, 15 Feb 2011 19:43:46 +0000 Subject: [PATCH 06/11] Ignore FileLoadException thrown when attempting to load an unverifiable assembly. --- .../Project/Src/Services/TypeResolutionService.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Services/TypeResolutionService.cs b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Services/TypeResolutionService.cs index 04389441a6..889eeea69c 100644 --- a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Services/TypeResolutionService.cs +++ b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Services/TypeResolutionService.cs @@ -264,6 +264,10 @@ namespace ICSharpCode.FormsDesigner.Services */ asm = Assembly.LoadFile(tempPath); MarkFileToDeleteOnReboot(tempPath); + } else if (e.Message.Contains("HRESULT: 0x80131019")) { + LoggingService.Debug(e.Message); + LoggingService.Debug("Attempting to load unverifiable assembly. Ignoring."); + return null; } else { throw; // don't ignore other load errors } From dcc21eeb99bcb5661192d23fd76f588e4e9552d0 Mon Sep 17 00:00:00 2001 From: Matt Ward Date: Sun, 27 Feb 2011 14:25:18 +0000 Subject: [PATCH 07/11] Fix ArgumentOutOfRangeException when searching for references on array indexer. --- .../Project/Src/LanguageProperties.cs | 5 +- .../ICSharpCode.SharpDevelop.Dom.Tests.csproj | 2 + .../IndexBeforeTextFinderTests.cs | 70 +++++++++++++++++++ .../IndexBeforeTextFinderHelper.cs | 21 ++++++ 4 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/IndexBeforeTextFinderTests.cs create mode 100644 src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/NUnitHelpers/IndexBeforeTextFinderHelper.cs diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs index b6ee46075a..be382bb3c0 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/LanguageProperties.cs @@ -532,11 +532,10 @@ namespace ICSharpCode.SharpDevelop.Dom public override TextFinderMatch Find(string inputText, int startPosition) { int pos = inputText.IndexOf(searchText, startPosition); - if (pos >= 0) { + if (pos > 0) { return new TextFinderMatch(pos, searchText.Length, pos - 1); - } else { - return TextFinderMatch.Empty; } + return TextFinderMatch.Empty; } } #endregion diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/ICSharpCode.SharpDevelop.Dom.Tests.csproj b/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/ICSharpCode.SharpDevelop.Dom.Tests.csproj index 6b5a6788de..ae4d307071 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/ICSharpCode.SharpDevelop.Dom.Tests.csproj +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/ICSharpCode.SharpDevelop.Dom.Tests.csproj @@ -52,8 +52,10 @@ + + diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/IndexBeforeTextFinderTests.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/IndexBeforeTextFinderTests.cs new file mode 100644 index 0000000000..fafacb14c8 --- /dev/null +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/IndexBeforeTextFinderTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.SharpDevelop.Dom; +using ICSharpCode.SharpDevelop.Dom.Refactoring; +using ICSharpCode.SharpDevelop.Dom.Tests.NUnitHelpers; +using NUnit.Framework; + +namespace ICSharpCode.SharpDevelop.Dom.Tests +{ + [TestFixture] + public class IndexBeforeTextFinderTests + { + TextFinder textFinder; + IndexBeforeTextFinderHelper helper; + + void CreateIndexBeforeTextFinderWithSearchTextOf(string searchText) + { + helper = new IndexBeforeTextFinderHelper(); + textFinder = helper.CreateIndexBeforeTextFinder(searchText); + } + + void AssertTextFindMatchesAreEqual(TextFinderMatch expectedMatch, TextFinderMatch actualMatch) + { + string expectedMatchAsString = GetTextFinderMatchAsString(expectedMatch); + string actualMatchAsString = GetTextFinderMatchAsString(actualMatch); + Assert.AreEqual(expectedMatchAsString, actualMatchAsString); + } + + string GetTextFinderMatchAsString(TextFinderMatch match) + { + return String.Format( + "Position: {0}, Length: {1}, ResolvePosition: {2}", + match.Position, + match.Length, + match.ResolvePosition); + } + + [Test] + public void Find_SearchingForSquareBracketCharacterAndInputTextHasNoSquareBracketCharacter_ReturnsEmptyTextFinderMatch() + { + CreateIndexBeforeTextFinderWithSearchTextOf("["); + TextFinderMatch match = textFinder.Find("abc", 0); + + AssertTextFindMatchesAreEqual(TextFinderMatch.Empty, match); + } + + [Test] + public void Find_SearchingForSquareBracketCharacterAndInputTextHasSquareBracketAtPositionOne_ReturnsTextFinderMatchForPositionOne() + { + CreateIndexBeforeTextFinderWithSearchTextOf("["); + TextFinderMatch match = textFinder.Find("a[0]", 0); + + TextFinderMatch expectedMatch = + new TextFinderMatch(position: 1, length: 1, resolvePosition: 0); + + AssertTextFindMatchesAreEqual(expectedMatch, match); + } + + [Test] + public void Find_SearchingForSquareBracketCharacterAndInputTextHasSquareBracketCharacterAtPositionZero_ReturnsEmptyTextFinderMatch() + { + CreateIndexBeforeTextFinderWithSearchTextOf("["); + TextFinderMatch match = textFinder.Find("[assembly: AssemblyCulture(\"\")]", 0); + + AssertTextFindMatchesAreEqual(TextFinderMatch.Empty, match); + } + } +} diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/NUnitHelpers/IndexBeforeTextFinderHelper.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/NUnitHelpers/IndexBeforeTextFinderHelper.cs new file mode 100644 index 0000000000..e7e966c4fc --- /dev/null +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Tests/ICSharpCode.SharpDevelop.Dom.Tests/NUnitHelpers/IndexBeforeTextFinderHelper.cs @@ -0,0 +1,21 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.SharpDevelop.Dom.Refactoring; + +namespace ICSharpCode.SharpDevelop.Dom.Tests.NUnitHelpers +{ + public class IndexBeforeTextFinderHelper : LanguageProperties + { + public IndexBeforeTextFinderHelper() + : base(StringComparer.Ordinal) + { + } + + public TextFinder CreateIndexBeforeTextFinder(string searchText) + { + return new IndexBeforeTextFinder(searchText); + } + } +} From b173684de0e2dbc3d66070b869ded532731be822 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 22 Feb 2011 13:59:05 +0100 Subject: [PATCH 08/11] Remove out-of-date documentation comment. --- .../ICSharpCode.AvalonEdit/Rendering/IBackgroundRenderer.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/IBackgroundRenderer.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/IBackgroundRenderer.cs index f8728f0a3b..cb9d87243e 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/IBackgroundRenderer.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/IBackgroundRenderer.cs @@ -11,9 +11,6 @@ namespace ICSharpCode.AvalonEdit.Rendering /// You can use background renderers to draw non-interactive elements on the TextView /// without introducing new UIElements. /// - /// Background renderer will draw only if their associated known - /// layer chooses to draw them. For example, background renderers in the caret - /// layer will be invisible when the caret is hidden. public interface IBackgroundRenderer { /// From 34da9b3fadaa2f7e8666e16fc51af2316bf35786 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 4 Mar 2011 17:26:43 +0100 Subject: [PATCH 09/11] Code completion for generic types - insert only e.g. "List" instead of "List". This is a backport of Martin's commit b47a93ab1eaca954d614b486ea346d42bc53d3fe to 4.0. --- .../Project/Src/CSharpCompletionBinding.cs | 27 +------ .../Project/ICSharpCode.SharpDevelop.csproj | 2 +- .../CodeCompletionItemProvider.cs | 79 ++++++++++--------- .../Services/ParserService/ParserService.cs | 12 +-- .../ContextActionsHelper.cs | 0 5 files changed, 53 insertions(+), 67 deletions(-) rename src/Main/Base/Project/Src/Services/RefactoringService/{ => ContextActions}/ContextActionsHelper.cs (100%) diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs index 2044291750..640e829325 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs @@ -261,10 +261,10 @@ namespace CSharpBinding new ClassFinder(ParserService.GetParseInformation(editor.FileName), editor.Caret.Line, editor.Caret.Column) ), ""); if (suggestedClassName != c.Name) { - // create an IClass instance that includes the type arguments in its name - context.SuggestedItem = new RenamedClass(c, suggestedClassName); + // create a special code completion item that completes also the type arguments + context.SuggestedItem = new SuggestedCodeCompletionItem(c, suggestedClassName); } else { - context.SuggestedItem = c; + context.SuggestedItem = new CodeCompletionItem(c); } } return context; @@ -273,27 +273,6 @@ namespace CSharpBinding return null; } - /// - /// A class that copies the properties important for the code completion display from another class, - /// but provides its own Name implementation. - /// Unlike the AbstractEntity.Name implementation, here 'Name' may include the namespace or type arguments. - /// - sealed class RenamedClass : DefaultClass, IClass - { - string newName; - - public RenamedClass(IClass c, string newName) : base(c.CompilationUnit, c.ClassType, c.Modifiers, c.Region, c.DeclaringType) - { - this.newName = newName; - CopyDocumentationFrom(c); - this.FullyQualifiedName = c.FullyQualifiedName; - } - - string ICompletionEntry.Name { - get { return newName; } - } - } - #region "case"-keyword completion bool DoCaseCompletion(ITextEditor editor) { diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index 15768dfd9b..a86f959798 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -350,6 +350,7 @@ Code + @@ -361,7 +362,6 @@ - diff --git a/src/Main/Base/Project/Src/Editor/CodeCompletion/CodeCompletionItemProvider.cs b/src/Main/Base/Project/Src/Editor/CodeCompletion/CodeCompletionItemProvider.cs index c91bf39aa3..d1d1964238 100644 --- a/src/Main/Base/Project/Src/Editor/CodeCompletion/CodeCompletionItemProvider.cs +++ b/src/Main/Base/Project/Src/Editor/CodeCompletion/CodeCompletionItemProvider.cs @@ -154,18 +154,13 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion if (method != null && codeItem != null) { methodItems[method.Name] = codeItem; } - if (o.Equals(context.SuggestedItem)) - result.SuggestedItem = item; } } - if (context.SuggestedItem != null) { - if (result.SuggestedItem == null) { - result.SuggestedItem = CreateCompletionItem(context.SuggestedItem, context); - if (result.SuggestedItem != null) { - result.Items.Insert(0, result.SuggestedItem); - } - } + // Suggested entry (List a = new => suggest List). + if (context.SuggestedItem is SuggestedCodeCompletionItem) { + result.SuggestedItem = (SuggestedCodeCompletionItem)context.SuggestedItem; + result.Items.Insert(0, result.SuggestedItem); } return result; } @@ -238,6 +233,7 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion description = ambience.Convert(entity); this.Image = ClassBrowserIconService.GetIcon(entity); this.Overloads = 1; + this.InsertGenericArguments = false; this.Priority = CodeCompletionDataUsageCache.GetPriority(entity.DotNetName, true); } @@ -252,6 +248,12 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion public IImage Image { get; set; } + /// + /// If true, will insert Text including generic arguments (e.g. List<T> or List<string>). + /// Otherwise will insert Text without generic arguments (e.g. List). + /// + public bool InsertGenericArguments { get; set; } + protected void MarkAsUsed() { CodeCompletionDataUsageCache.IncrementUsage(entity.DotNetName); @@ -288,8 +290,7 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion addUsing = !IsKnownName(nameResult); } - //insertedText = StripGenericArgument(insertedText, context); - InsertText(context, insertedText); + InsertTextStripGenericArguments(context, insertedText, this.InsertGenericArguments); if (addUsing && nameResult != null && nameResult.CallingClass != null) { var cu = nameResult.CallingClass.CompilationUnit; @@ -298,15 +299,28 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion } } else { // Something else than a class or Extension method is being inserted - just insert text - //insertedText = StripGenericArgument(insertedText, context); - InsertText(context, insertedText); + InsertTextStripGenericArguments(context, insertedText, this.InsertGenericArguments); } } + /// + /// Inserts the given text. Strips generic arguments from it if specified. + /// + static void InsertTextStripGenericArguments(CompletionContext context, string insertedText, bool stringGenericArguments) + { + if (!stringGenericArguments) { + // FIXME CodeCompletionItem should contain IReturnType and decide whether to INCLUDE generic arguments + // or not, not strip them. + insertedText = StripGenericArguments(insertedText, context); + } + context.Editor.Document.Replace(context.StartOffset, context.Length, insertedText); + context.EndOffset = context.StartOffset + insertedText.Length; + } + /// /// Turns e.g. "List<T>" into "List<" /// - string StripGenericArgument(string itemText, CompletionContext context) + static string StripGenericArguments(string itemText, CompletionContext context) { if (context == null || context.Editor == null || context.Editor.Language == null || context.Editor.Language.Properties != LanguageProperties.CSharp) @@ -315,33 +329,12 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion int pos = itemText.LastIndexOf('<'); if (pos == -1) return itemText; - int insertLen = pos + 1; - if (context.CompletionChar == '<') { - // don't insert '<' twice if user typed '<' - insertLen -= 1; - } + int insertLen = pos; itemText = itemText.Substring(0, insertLen); } return itemText; } - bool IsReferenceTo(ResolveResult nameResult, IClass selectedClass) - { - // CC list contains RenamedClass instances which are kind of hacky: - // their name is e.g. "List" or "int[]", but they do not have any generic arguments, - // so IsReferenceTo fails bc it compares generic argument count. - // This compares just name and ignores generic arguments. - return nameResult.IsReferenceTo(selectedClass) || - (nameResult.ResolvedType.IsConstructedReturnType && - nameResult.ResolvedType.FullyQualifiedName == selectedClass.FullyQualifiedName); - } - - void InsertText(CompletionContext context, string insertedText) - { - context.Editor.Document.Replace(context.StartOffset, context.Length, insertedText); - context.EndOffset = context.StartOffset + insertedText.Length; - } - IClass GetClassOrExtensionMethodClass(IEntity selectedEntity) { var selectedClass = selectedEntity as IClass; @@ -514,4 +507,18 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion } #endregion } + + /// + /// CodeCompletionItem that inserts also generic arguments. + /// Used only when suggesting items in CC (e.g. List<int> a = new => suggest List<int>). + /// + public class SuggestedCodeCompletionItem : CodeCompletionItem + { + public SuggestedCodeCompletionItem(IEntity entity, string nameWithSpecifiedGenericArguments) + : base(entity) + { + this.Text = nameWithSpecifiedGenericArguments; + this.InsertGenericArguments = true; + } + } } diff --git a/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs b/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs index a172c0f72d..433a9ab7b8 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs +++ b/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs @@ -276,6 +276,12 @@ namespace ICSharpCode.SharpDevelop return null; } + public static ResolveResult Resolve(int offset, IDocument document, string fileName) + { + var position = document.OffsetToPosition(offset); + return Resolve(position.Line, position.Column, document, fileName); + } + public static ExpressionResult FindFullExpression(int caretLine, int caretColumn, IDocument document, string fileName) { IExpressionFinder expressionFinder = GetExpressionFinder(fileName); @@ -285,12 +291,6 @@ namespace ICSharpCode.SharpDevelop return ExpressionResult.Empty; return expressionFinder.FindFullExpression(document.Text, document.PositionToOffset(caretLine, caretColumn)); } - - public static ResolveResult Resolve(int offset, IDocument document, string fileName) - { - var position = document.OffsetToPosition(offset); - return Resolve(position.Line, position.Column, document, fileName); - } #endregion #region GetParseableFileContent diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActionsHelper.cs b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsHelper.cs similarity index 100% rename from src/Main/Base/Project/Src/Services/RefactoringService/ContextActionsHelper.cs rename to src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsHelper.cs From 441fd154889c8bc0fa9befecdb18539fcbdc3934 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 8 Mar 2011 01:34:46 +0100 Subject: [PATCH 10/11] Fixed bug in EditingCommandHandler.Cut that allowed deleting read-only text. --- .../ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs index e5d383273d..15a9e9c37c 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs @@ -300,7 +300,10 @@ namespace ICSharpCode.AvalonEdit.Editing if (textArea.Selection.IsEmpty && textArea.Options.CutCopyWholeLine) { DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line); CopyWholeLine(textArea, currentLine); - textArea.Document.Remove(currentLine.Offset, currentLine.TotalLength); + ISegment[] segmentsToDelete = textArea.GetDeletableSegments(new SimpleSegment(currentLine.Offset, currentLine.TotalLength)); + for (int i = segmentsToDelete.Length - 1; i >= 0; i--) { + textArea.Document.Remove(segmentsToDelete[i]); + } } else { CopySelectedText(textArea); textArea.RemoveSelectedText(); From b7b12d310e2021338c3e12e9926ecf380a44e471 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 8 Mar 2011 15:21:20 +0100 Subject: [PATCH 11/11] Reduce memory usage when dealing with long lines and word-wrapping. --- .../ICSharpCode.AvalonEdit.csproj | 1 + .../Rendering/ITextRunConstructionContext.cs | 12 ++ .../Rendering/LinkElementGenerator.cs | 14 +-- .../SingleCharacterElementGenerator.cs | 7 +- .../Rendering/VisualLineText.cs | 8 +- .../Rendering/VisualLineTextSource.cs | 15 +++ .../Utils/StringSegment.cs | 107 ++++++++++++++++++ 7 files changed, 146 insertions(+), 18 deletions(-) create mode 100644 src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/StringSegment.cs diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj index d572f6c8b0..ac84b63f8b 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj @@ -342,6 +342,7 @@ + diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/ITextRunConstructionContext.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/ITextRunConstructionContext.cs index 83a2184540..f0fd8d1be0 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/ITextRunConstructionContext.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/ITextRunConstructionContext.cs @@ -4,6 +4,7 @@ using System; using System.Windows.Media.TextFormatting; using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.AvalonEdit.Utils; namespace ICSharpCode.AvalonEdit.Rendering { @@ -31,5 +32,16 @@ namespace ICSharpCode.AvalonEdit.Rendering /// Gets the global text run properties. /// TextRunProperties GlobalTextRunProperties { get; } + + /// + /// Gets a piece of text from the document. + /// + /// + /// This method is allowed to return a larger string than requested. + /// It does this by returning a that describes the requested segment within the returned string. + /// This method should be the preferred text access method in the text transformation pipeline, as it can avoid repeatedly allocating string instances + /// for text within the same line. + /// + StringSegment GetText(int offset, int length); } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/LinkElementGenerator.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/LinkElementGenerator.cs index 0040decd12..70d59358ae 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/LinkElementGenerator.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/LinkElementGenerator.cs @@ -2,16 +2,8 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; -using System.Diagnostics; using System.Text.RegularExpressions; -using System.Windows; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.TextFormatting; - -using ICSharpCode.AvalonEdit.Document; -using System.Windows.Navigation; +using ICSharpCode.AvalonEdit.Utils; namespace ICSharpCode.AvalonEdit.Rendering { @@ -68,8 +60,8 @@ namespace ICSharpCode.AvalonEdit.Rendering Match GetMatch(int startOffset) { int endOffset = CurrentContext.VisualLine.LastDocumentLine.EndOffset; - string relevantText = CurrentContext.Document.GetText(startOffset, endOffset - startOffset); - return linkRegex.Match(relevantText); + StringSegment relevantText = CurrentContext.GetText(startOffset, endOffset - startOffset); + return linkRegex.Match(relevantText.Text, relevantText.Offset, relevantText.Count); } /// diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/SingleCharacterElementGenerator.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/SingleCharacterElementGenerator.cs index 3d26e7c9b9..4ff5def317 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/SingleCharacterElementGenerator.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/SingleCharacterElementGenerator.cs @@ -60,10 +60,11 @@ namespace ICSharpCode.AvalonEdit.Rendering public override int GetFirstInterestedOffset(int startOffset) { DocumentLine endLine = CurrentContext.VisualLine.LastDocumentLine; - string relevantText = CurrentContext.Document.GetText(startOffset, endLine.EndOffset - startOffset); + StringSegment relevantText = CurrentContext.GetText(startOffset, endLine.EndOffset - startOffset); - for (int i = 0; i < relevantText.Length; i++) { - char c = relevantText[i]; + int endPos = relevantText.Offset + relevantText.Count; + for (int i = relevantText.Offset; i < endPos; i++) { + char c = relevantText.Text[i]; switch (c) { case ' ': if (ShowSpaces) diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineText.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineText.cs index 3cefe1e264..f5d7ae19ed 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineText.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineText.cs @@ -53,8 +53,8 @@ namespace ICSharpCode.AvalonEdit.Rendering throw new ArgumentNullException("context"); int relativeOffset = startVisualColumn - VisualColumn; - string text = context.Document.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset + relativeOffset, DocumentLength - relativeOffset); - return new TextCharacters(text, 0, text.Length, this.TextRunProperties); + StringSegment text = context.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset + relativeOffset, DocumentLength - relativeOffset); + return new TextCharacters(text.Text, text.Offset, text.Count, this.TextRunProperties); } /// @@ -71,8 +71,8 @@ namespace ICSharpCode.AvalonEdit.Rendering throw new ArgumentNullException("context"); int relativeOffset = visualColumnLimit - VisualColumn; - string text = context.Document.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset, relativeOffset); - CharacterBufferRange range = new CharacterBufferRange(text, 0, text.Length); + StringSegment text = context.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset, relativeOffset); + CharacterBufferRange range = new CharacterBufferRange(text.Text, text.Offset, text.Count); return new TextSpan(range.Length, new CultureSpecificCharacterBufferRange(this.TextRunProperties.CultureInfo, range)); } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineTextSource.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineTextSource.cs index eea016446f..375a09bf9e 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineTextSource.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineTextSource.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Windows.Media.TextFormatting; using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.AvalonEdit.Utils; namespace ICSharpCode.AvalonEdit.Rendering { @@ -83,5 +84,19 @@ namespace ICSharpCode.AvalonEdit.Rendering { throw new NotSupportedException(); } + + string cachedString; + int cachedStringOffset; + + public StringSegment GetText(int offset, int length) + { + if (cachedString != null) { + if (offset >= cachedStringOffset && offset + length <= cachedStringOffset + cachedString.Length) { + return new StringSegment(cachedString, offset - cachedStringOffset, length); + } + } + cachedStringOffset = offset; + return new StringSegment(cachedString = this.Document.GetText(offset, length)); + } } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/StringSegment.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/StringSegment.cs new file mode 100644 index 0000000000..8a39ea874f --- /dev/null +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/StringSegment.cs @@ -0,0 +1,107 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.AvalonEdit.Utils +{ + /// + /// Represents a string with a segment. + /// Similar to System.ArraySegment<T>, but for strings instead of arrays. + /// + public struct StringSegment : IEquatable + { + readonly string text; + readonly int offset; + readonly int count; + + /// + /// Creates a new StringSegment. + /// + public StringSegment(string text, int offset, int count) + { + if (text == null) + throw new ArgumentNullException("text"); + if (offset < 0 || offset > text.Length) + throw new ArgumentOutOfRangeException("offset"); + if (offset + count > text.Length) + throw new ArgumentOutOfRangeException("count"); + this.text = text; + this.offset = offset; + this.count = count; + } + + /// + /// Creates a new StringSegment. + /// + public StringSegment(string text) + { + if (text == null) + throw new ArgumentNullException("text"); + this.text = text; + this.offset = 0; + this.count = text.Length; + } + + /// + /// Gets the string used for this segment. + /// + public string Text { + get { return text; } + } + + /// + /// Gets the start offset of the segment with the text. + /// + public int Offset { + get { return offset; } + } + + /// + /// Gets the length of the segment. + /// + public int Count { + get { return count; } + } + + #region Equals and GetHashCode implementation + /// + public override bool Equals(object obj) + { + if (obj is StringSegment) + return Equals((StringSegment)obj); // use Equals method below + else + return false; + } + + /// + public bool Equals(StringSegment other) + { + // add comparisions for all members here + return object.ReferenceEquals(this.text, other.text) && offset == other.offset && count == other.count; + } + + /// + public override int GetHashCode() + { + return text.GetHashCode() ^ offset ^ count; + } + + /// + /// Equality operator. + /// + public static bool operator ==(StringSegment left, StringSegment right) + { + return left.Equals(right); + } + + /// + /// Inequality operator. + /// + public static bool operator !=(StringSegment left, StringSegment right) + { + return !left.Equals(right); + } + #endregion + } +}