Browse Source

Implemented PermanentAnchor (TextAnchor inside SharpDevelop that works even when a file is closed)

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5027 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 16 years ago
parent
commit
c9218dfa30
  1. 23
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs
  2. 6
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
  3. 21
      src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchResultNode.cs
  4. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  5. 3
      src/Main/Base/Project/Src/Editor/ITextAnchor.cs
  6. 276
      src/Main/Base/Project/Src/Editor/PermanentAnchor.cs
  7. 12
      src/Main/Base/Project/Src/TextEditor/Bookmarks/BookmarkManager.cs
  8. 3
      src/Main/Base/Project/Src/TextEditor/Bookmarks/Pad/BookmarkPad.cs
  9. 9
      src/Main/Base/Project/Src/TextEditor/Bookmarks/SDBookmark.cs
  10. 1
      src/Main/Core/Project/ICSharpCode.Core.csproj
  11. 106
      src/Main/Core/Project/Src/Services/FileUtility/FileName.cs

23
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs

@ -78,7 +78,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -78,7 +78,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
codeEditor.Load(stream);
// we set the file name after loading because this will place the fold markers etc.
codeEditor.FileName = file.FileName;
codeEditor.FileName = FileName.Create(file.FileName);
BookmarksAttach();
} finally {
isLoading = false;
@ -89,14 +89,17 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -89,14 +89,17 @@ namespace ICSharpCode.AvalonEdit.AddIn
{
base.OnFileNameChanged(file);
if (file == PrimaryFile) {
if (!string.IsNullOrEmpty(codeEditor.FileName))
ParserService.ClearParseInformation(codeEditor.FileName);
FileName oldFileName = codeEditor.FileName;
FileName newFileName = FileName.Create(file.FileName);
codeEditor.FileName = file.FileName;
if (!string.IsNullOrEmpty(oldFileName))
ParserService.ClearParseInformation(oldFileName);
codeEditor.FileName = newFileName;
ParserService.BeginParse(file.FileName, codeEditor.DocumentAdapter);
BookmarksNotifyNameChange(file.FileName);
BookmarksNotifyNameChange(oldFileName, newFileName);
}
}
@ -121,10 +124,16 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -121,10 +124,16 @@ namespace ICSharpCode.AvalonEdit.AddIn
}
BookmarkManager.Added += BookmarkManager_Added;
BookmarkManager.Removed += BookmarkManager_Removed;
PermanentAnchorService.AttachDocument(codeEditor.FileName, codeEditor.DocumentAdapter);
}
void BookmarksDetach()
{
if (codeEditor.FileName != null) {
PermanentAnchorService.DetachDocument(codeEditor.FileName, codeEditor.DocumentAdapter);
}
BookmarkManager.Added -= BookmarkManager_Added;
BookmarkManager.Removed -= BookmarkManager_Removed;
foreach (SDBookmark bookmark in codeEditor.IconBarManager.Bookmarks.OfType<SDBookmark>()) {
@ -151,8 +160,10 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -151,8 +160,10 @@ namespace ICSharpCode.AvalonEdit.AddIn
}
}
void BookmarksNotifyNameChange(string newFileName)
void BookmarksNotifyNameChange(FileName oldFileName, FileName newFileName)
{
PermanentAnchorService.RenameDocument(oldFileName, newFileName, codeEditor.DocumentAdapter);
foreach (SDBookmark bookmark in codeEditor.IconBarManager.Bookmarks.OfType<SDBookmark>()) {
bookmark.FileName = newFileName;
}

6
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs

@ -105,9 +105,9 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -105,9 +105,9 @@ namespace ICSharpCode.AvalonEdit.AddIn
get { return iconBarManager; }
}
string fileName;
FileName fileName;
public string FileName {
public FileName FileName {
get { return fileName; }
set {
if (fileName != value) {
@ -171,7 +171,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -171,7 +171,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
textView.Services.AddService(typeof(CodeEditor), this);
textEditor.Background = Brushes.White;
textEditor.FontFamily = new FontFamily("Consolas");
textEditor.FontFamily = new FontFamily("Consolas, Courier New");
textEditor.FontSize = 13;
textEditor.ShowLineNumbers = true;
textEditor.TextArea.TextEntering += TextAreaTextEntering;

21
src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchResultNode.cs

@ -28,7 +28,7 @@ namespace SearchAndReplace @@ -28,7 +28,7 @@ namespace SearchAndReplace
static readonly FontFamily resultLineFamily = new FontFamily("Consolas, Courier New");
SearchResultMatch result;
int lineNumber, column;
PermanentAnchor anchor;
HighlightedInlineBuilder inlineBuilder;
public SearchResultNode(SearchResultMatch result)
@ -37,8 +37,10 @@ namespace SearchAndReplace @@ -37,8 +37,10 @@ namespace SearchAndReplace
IDocument document = result.CreateDocument();
var startPosition = result.GetStartPosition(document);
this.lineNumber = startPosition.Line;
this.column = startPosition.Column;
int lineNumber = startPosition.Line;
int column = startPosition.Column;
this.anchor = new PermanentAnchor(FileName.Create(result.FileName), lineNumber, column);
anchor.SurviveDeletion = true;
if (lineNumber >= 1 && lineNumber <= document.TotalNumberOfLines) {
IDocumentLine matchedLine = document.GetLine(lineNumber);
@ -87,10 +89,12 @@ namespace SearchAndReplace @@ -87,10 +89,12 @@ namespace SearchAndReplace
protected override object CreateText()
{
LoggingService.Debug("Creating text for search result (" + lineNumber + ", " + column + ") ");
var location = anchor.Location;
LoggingService.Debug("Creating text for search result (" + location.Line + ", " + location.Column + ") ");
TextBlock textBlock = new TextBlock();
textBlock.Inlines.Add("(" + lineNumber + ", " + column + ")\t");
textBlock.Inlines.Add("(" + location.Line + ", " + location.Column + ")\t");
string displayText = result.DisplayText;
if (displayText != null) {
@ -103,7 +107,7 @@ namespace SearchAndReplace @@ -103,7 +107,7 @@ namespace SearchAndReplace
textBlock.Inlines.Add(
new Run {
Text = StringParser.Parse("\t${res:MainWindow.Windows.SearchResultPanel.In} ")
+ Path.GetFileName(result.FileName) + "(" + Path.GetDirectoryName(result.FileName) +")",
+ Path.GetFileName(anchor.FileName) + "(" + Path.GetDirectoryName(anchor.FileName) +")",
FontStyle = FontStyles.Italic
});
}
@ -112,13 +116,14 @@ namespace SearchAndReplace @@ -112,13 +116,14 @@ namespace SearchAndReplace
public override void ActivateItem()
{
ITextEditorProvider provider = FileService.JumpToFilePosition(result.FileName, lineNumber, column) as ITextEditorProvider;
var location = anchor.Location;
ITextEditorProvider provider = FileService.JumpToFilePosition(anchor.FileName, location.Line, location.Column) as ITextEditorProvider;
if (provider != null) {
ITextMarkerService markerService = provider.TextEditor.GetService(typeof(ITextMarkerService)) as ITextMarkerService;
if (markerService != null) {
ITextMarker marker = null;
try {
marker = markerService.Create(provider.TextEditor.Document.PositionToOffset(lineNumber, column), result.Length);
marker = markerService.Create(provider.TextEditor.Document.PositionToOffset(location.Line, location.Column), result.Length);
} catch (ArgumentOutOfRangeException) {
// can happen if lineNumber/column is after the end of the document; or if
// result.Length is too long

1
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -107,6 +107,7 @@ @@ -107,6 +107,7 @@
<Compile Include="Src\Editor\IDocumentLine.cs" />
<Compile Include="Src\Editor\IFileDocumentProvider.cs" />
<Compile Include="Src\Editor\IFormattingStrategy.cs" />
<Compile Include="Src\Editor\PermanentAnchor.cs" />
<Compile Include="Src\Editor\ISyntaxHighlighter.cs" />
<Compile Include="Src\Editor\ITextAnchor.cs" />
<Compile Include="Src\Editor\ITextAreaToolTipProvider.cs">

3
src/Main/Base/Project/Src/Editor/ITextAnchor.cs

@ -10,6 +10,9 @@ using System; @@ -10,6 +10,9 @@ using System;
namespace ICSharpCode.SharpDevelop.Editor
{
/// <summary>
/// Represents an anchored location inside an <see cref="IDocument"/>.
/// </summary>
public interface ITextAnchor
{
/// <summary>

276
src/Main/Base/Project/Src/Editor/PermanentAnchor.cs

@ -0,0 +1,276 @@ @@ -0,0 +1,276 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using ICSharpCode.Core;
using ICSharpCode.NRefactory;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.TextEditor.Util;
namespace ICSharpCode.SharpDevelop.Editor
{
/// <summary>
/// A permanent anchor that works even when a file is closed and later reopened.
/// </summary>
public sealed class PermanentAnchor : ITextAnchor
{
IDocument currentDocument;
ITextAnchor baseAnchor;
FileName fileName;
int line, column;
bool isDeleted;
bool surviveDeletion;
AnchorMovementType movementType = AnchorMovementType.BeforeInsertion;
public PermanentAnchor(FileName fileName, int line, int column)
{
if (fileName == null)
throw new ArgumentNullException("fileName");
if (line < 1)
throw new ArgumentOutOfRangeException("line");
if (column < 1)
throw new ArgumentOutOfRangeException("column");
Gui.WorkbenchSingleton.AssertMainThread();
this.fileName = fileName;
this.line = line;
this.column = column;
PermanentAnchorService.AddAnchor(this);
}
internal void AttachTo(IDocument document)
{
if (isDeleted)
return;
Debug.Assert(currentDocument == null && document != null);
this.currentDocument = document;
line = Math.Min(line, document.TotalNumberOfLines);
column = Math.Min(column, document.GetLine(line).Length + 1);
baseAnchor = document.CreateAnchor(document.PositionToOffset(line, column));
baseAnchor.MovementType = movementType;
baseAnchor.SurviveDeletion = surviveDeletion;
baseAnchor.Deleted += baseAnchor_Deleted;
}
internal void Detach()
{
if (isDeleted)
return;
Debug.Assert(currentDocument != null);
Location loc = baseAnchor.Location;
line = loc.Line;
column = loc.Column;
baseAnchor = null;
currentDocument = null;
}
internal void SetFileName(FileName newName)
{
this.fileName = newName;
}
void baseAnchor_Deleted(object sender, EventArgs e)
{
isDeleted = true;
baseAnchor = null;
currentDocument = null;
if (Deleted != null)
Deleted(this, e);
}
/// <summary>
/// Gets the file name of the anchor.
/// </summary>
public FileName FileName {
get { return fileName; }
}
/// <inheritdoc/>
public event EventHandler Deleted;
/// <inheritdoc/>
public Location Location {
get {
if (isDeleted)
throw new InvalidOperationException();
if (baseAnchor != null)
return baseAnchor.Location;
else
return new Location(column, line);
}
}
/// <summary>
/// Gets the editor to which this anchor currently belongs; or null if the file is not opened in any text editor.
/// </summary>
public IDocument CurrentDocument {
get { return currentDocument; }
}
/// <summary>
/// Gets the offset.
/// Warning: this method is only available while the document anchor is attached to a text editor, otherwise
/// it will throw an InvalidOperationException.
/// </summary>
public int Offset {
get {
if (baseAnchor != null)
return baseAnchor.Offset;
else
throw new InvalidOperationException();
}
}
/// <inheritdoc/>
public AnchorMovementType MovementType {
get { return movementType; }
set {
movementType = value;
if (baseAnchor != null)
baseAnchor.MovementType = value;
}
}
/// <inheritdoc/>
public bool SurviveDeletion {
get { return surviveDeletion; }
set {
surviveDeletion = value;
if (baseAnchor != null)
baseAnchor.SurviveDeletion = value;
}
}
/// <inheritdoc/>
public bool IsDeleted {
get { return isDeleted; }
}
/// <inheritdoc/>
public int Line {
get {
if (baseAnchor != null)
return baseAnchor.Line;
else
return line;
}
}
/// <inheritdoc/>
public int Column {
get {
if (baseAnchor != null)
return baseAnchor.Column;
else
return line;
}
}
}
public static class PermanentAnchorService
{
static WeakCollection<PermanentAnchor> permanentAnchors = new WeakCollection<PermanentAnchor>();
static Dictionary<FileName, IDocument> openDocuments = new Dictionary<FileName, IDocument>();
internal static void AddAnchor(PermanentAnchor anchor)
{
permanentAnchors.Add(anchor);
IDocument doc;
if (openDocuments.TryGetValue(anchor.FileName, out doc))
anchor.AttachTo(doc);
}
/// <summary>
/// Tells detached permanent anchors to attach to the specified text editor.
/// </summary>
public static void AttachDocument(FileName fileName, IDocument document)
{
if (fileName == null)
throw new ArgumentNullException("fileName");
if (document == null)
throw new ArgumentNullException("document");
Gui.WorkbenchSingleton.AssertMainThread();
// there may be multiple documents with the same file name - in that case, only attach to one of them
if (!openDocuments.ContainsKey(fileName)) {
openDocuments.Add(fileName, document);
foreach (PermanentAnchor anchor in permanentAnchors) {
if (anchor.CurrentDocument == null && anchor.FileName == fileName) {
anchor.AttachTo(document);
}
}
}
}
/// <summary>
/// Tells detached permanent anchors to attach to the specified text editor.
/// </summary>
public static void DetachDocument(FileName fileName, IDocument document)
{
if (fileName == null)
throw new ArgumentNullException("fileName");
if (document == null)
throw new ArgumentNullException("document");
Gui.WorkbenchSingleton.AssertMainThread();
IDocument actualDocument;
if (openDocuments.TryGetValue(fileName, out actualDocument)) {
// test whether we're detaching the correct document
if (document == actualDocument) {
openDocuments.Remove(fileName);
foreach (PermanentAnchor anchor in permanentAnchors) {
if (anchor.CurrentDocument == document) {
anchor.Detach();
}
}
}
}
}
/// <summary>
/// Informs the PermanentAnchorService when the file name of a document has changed.
/// </summary>
public static void RenameDocument(FileName oldFileName, FileName newFileName, IDocument document)
{
if (oldFileName == null)
throw new ArgumentNullException("oldFileName");
if (newFileName == null)
throw new ArgumentNullException("newFileName");
if (document == null)
throw new ArgumentNullException("document");
Gui.WorkbenchSingleton.AssertMainThread();
IDocument actualDocument;
if (openDocuments.TryGetValue(oldFileName, out actualDocument)) {
// test whether we're detaching the correct document
if (document == actualDocument) {
if (openDocuments.ContainsKey(newFileName)) {
// new file name already taken? just detach the old stuff
DetachDocument(oldFileName, document);
} else {
openDocuments.Remove(oldFileName);
openDocuments.Add(newFileName, document);
foreach (PermanentAnchor anchor in permanentAnchors) {
if (anchor.CurrentDocument == document) {
anchor.SetFileName(newFileName);
}
}
}
}
}
}
}
}

12
src/Main/Base/Project/Src/TextEditor/Bookmarks/BookmarkManager.cs

@ -26,13 +26,15 @@ namespace ICSharpCode.SharpDevelop.Bookmarks @@ -26,13 +26,15 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
}
}
public static List<SDBookmark> GetBookmarks(string fileName)
public static List<SDBookmark> GetBookmarks(FileName fileName)
{
if (fileName == null)
throw new ArgumentNullException("fileName");
List<SDBookmark> marks = new List<SDBookmark>();
foreach (SDBookmark mark in bookmarks) {
if (mark.FileName == null) continue;
if (FileUtility.IsEqualFileName(mark.FileName, fileName)) {
if (fileName == mark.FileName) {
marks.Add(mark);
}
}
@ -57,7 +59,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks @@ -57,7 +59,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
return false;
if (a.GetType() != b.GetType())
return false;
if (!FileUtility.IsEqualFileName(a.FileName, b.FileName))
if (a.FileName != b.FileName)
return false;
return a.LineNumber == b.LineNumber;
}
@ -111,7 +113,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks @@ -111,7 +113,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
Predicate<SDBookmark> canToggle,
Func<Location, SDBookmark> bookmarkFactory)
{
foreach (SDBookmark bookmark in GetBookmarks(editor.FileName)) {
foreach (SDBookmark bookmark in GetBookmarks(new FileName(editor.FileName))) {
if (canToggle(bookmark) && bookmark.LineNumber == line) {
BookmarkManager.RemoveMark(bookmark);
return;

3
src/Main/Base/Project/Src/TextEditor/Bookmarks/Pad/BookmarkPad.cs

@ -9,6 +9,7 @@ using System; @@ -9,6 +9,7 @@ using System;
using System.Collections.Generic;
using System.Windows.Forms;
using ICSharpCode.Core;
using ICSharpCode.Core.WinForms;
using ICSharpCode.SharpDevelop.Gui;
@ -38,7 +39,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks @@ -38,7 +39,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
Panel myPanel = new Panel();
ExtTreeView bookmarkTreeView = new ExtTreeView();
Dictionary<string, BookmarkFolderNode> fileNodes = new Dictionary<string, BookmarkFolderNode>();
Dictionary<FileName, BookmarkFolderNode> fileNodes = new Dictionary<FileName, BookmarkFolderNode>();
public override object Control {
get {

9
src/Main/Base/Project/Src/TextEditor/Bookmarks/SDBookmark.cs

@ -5,9 +5,10 @@ @@ -5,9 +5,10 @@
// <version>$Revision$</version>
// </file>
using ICSharpCode.NRefactory;
using System;
using System.ComponentModel;
using ICSharpCode.Core;
using ICSharpCode.NRefactory;
namespace ICSharpCode.SharpDevelop.Bookmarks
{
@ -19,12 +20,12 @@ namespace ICSharpCode.SharpDevelop.Bookmarks @@ -19,12 +20,12 @@ namespace ICSharpCode.SharpDevelop.Bookmarks
{
public SDBookmark(string fileName, Location location) : base(location)
{
this.fileName = fileName;
this.fileName = FileName.Create(fileName);
}
string fileName;
FileName fileName;
public string FileName {
public FileName FileName {
get {
return fileName;
}

1
src/Main/Core/Project/ICSharpCode.Core.csproj

@ -88,6 +88,7 @@ @@ -88,6 +88,7 @@
<Compile Include="Src\Services\AnalyticsMonitor\AnalyticsMonitorService.cs" />
<Compile Include="Src\Services\AnalyticsMonitor\IAnalyticsMonitor.cs" />
<Compile Include="Src\Services\ApplicationStateInfoService.cs" />
<Compile Include="Src\Services\FileUtility\FileName.cs" />
<Compile Include="Src\Services\FileUtility\FileNameEventHandler.cs" />
<Compile Include="Src\Services\FileUtility\FileUtility.cs" />
<Compile Include="Src\Services\FileUtility\FileUtility.Minimal.cs" />

106
src/Main/Core/Project/Src/Services/FileUtility/FileName.cs

@ -0,0 +1,106 @@ @@ -0,0 +1,106 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
namespace ICSharpCode.Core
{
/// <summary>
/// Represents a directory path or filename.
/// The equality operator is overloaded to compare for path equality (case insensitive, normalizing paths with '..\')
/// </summary>
public sealed class FileName : IEquatable<FileName>
{
readonly string normalizedFileName;
public FileName(string fileName)
{
if (fileName == null)
throw new ArgumentNullException("fileName");
this.normalizedFileName = FileUtility.NormalizePath(fileName);
}
public static FileName Create(string fileName)
{
if (fileName != null)
return new FileName(fileName);
else
return null;
}
public static implicit operator string(FileName fileName)
{
if (fileName != null)
return fileName.normalizedFileName;
else
return null;
}
public override string ToString()
{
return normalizedFileName;
}
#region Equals and GetHashCode implementation
public override bool Equals(object obj)
{
return Equals(obj as FileName);
}
public bool Equals(FileName other)
{
if (other != null)
return string.Equals(normalizedFileName, other.normalizedFileName, StringComparison.OrdinalIgnoreCase);
else
return false;
}
public override int GetHashCode()
{
return StringComparer.OrdinalIgnoreCase.GetHashCode(normalizedFileName);
}
public static bool operator ==(FileName left, FileName right)
{
if (ReferenceEquals(left, right))
return true;
if (ReferenceEquals(left, null) || ReferenceEquals(right, null))
return false;
return left.Equals(right);
}
public static bool operator !=(FileName left, FileName right)
{
return !(left == right);
}
[ObsoleteAttribute("Warning: comparing FileName with string results in case-sensitive comparison")]
public static bool operator ==(FileName left, string right)
{
return (string)left == right;
}
[ObsoleteAttribute("Warning: comparing FileName with string results in case-sensitive comparison")]
public static bool operator !=(FileName left, string right)
{
return (string)left != right;
}
[ObsoleteAttribute("Warning: comparing FileName with string results in case-sensitive comparison")]
public static bool operator ==(string left, FileName right)
{
return left == (string)right;
}
[ObsoleteAttribute("Warning: comparing FileName with string results in case-sensitive comparison")]
public static bool operator !=(string left, FileName right)
{
return left != (string)right;
}
#endregion
}
}
Loading…
Cancel
Save