~halega/+junk/sharpdevelop

« back to all changes in this revision

Viewing changes to src/Main/Base/Project/Src/Editor/PermanentAnchor.cs

  • Committer: sk
  • Date: 2011-09-10 05:17:57 UTC
  • Revision ID: halega@halega.com-20110910051757-qfouz1llya9m6boy
4.1.0.7915 Release Candidate 1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
 
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
 
3
 
 
4
using System;
 
5
using System.Collections.Generic;
 
6
using System.Diagnostics;
 
7
 
 
8
using ICSharpCode.Core;
 
9
using ICSharpCode.NRefactory;
 
10
using ICSharpCode.SharpDevelop.Dom;
 
11
using ICSharpCode.SharpDevelop.Util;
 
12
 
 
13
namespace ICSharpCode.SharpDevelop.Editor
 
14
{
 
15
        /// <summary>
 
16
        /// A permanent anchor that works even when a file is closed and later reopened.
 
17
        /// </summary>
 
18
        public sealed class PermanentAnchor : ITextAnchor
 
19
        {
 
20
                IDocument currentDocument;
 
21
                ITextAnchor baseAnchor;
 
22
                
 
23
                FileName fileName;
 
24
                int line, column;
 
25
                bool isDeleted;
 
26
                bool surviveDeletion;
 
27
                AnchorMovementType movementType = AnchorMovementType.BeforeInsertion;
 
28
                
 
29
                public PermanentAnchor(FileName fileName, int line, int column)
 
30
                {
 
31
                        if (fileName == null)
 
32
                                throw new ArgumentNullException("fileName");
 
33
                        if (line < 1)
 
34
                                throw new ArgumentOutOfRangeException("line");
 
35
                        if (column < 1)
 
36
                                throw new ArgumentOutOfRangeException("column");
 
37
                        
 
38
                        Gui.WorkbenchSingleton.AssertMainThread();
 
39
                        
 
40
                        this.fileName = fileName;
 
41
                        this.line = line;
 
42
                        this.column = column;
 
43
                        PermanentAnchorService.AddAnchor(this);
 
44
                }
 
45
                
 
46
                internal void AttachTo(IDocument document)
 
47
                {
 
48
                        if (isDeleted)
 
49
                                return;
 
50
                        Debug.Assert(currentDocument == null && document != null);
 
51
                        this.currentDocument = document;
 
52
                        line = Math.Min(line, document.TotalNumberOfLines);
 
53
                        column = Math.Min(column, document.GetLine(line).Length + 1);
 
54
                        baseAnchor = document.CreateAnchor(document.PositionToOffset(line, column));
 
55
                        baseAnchor.MovementType = movementType;
 
56
                        baseAnchor.SurviveDeletion = surviveDeletion;
 
57
                        baseAnchor.Deleted += baseAnchor_Deleted;
 
58
                }
 
59
 
 
60
                internal void Detach()
 
61
                {
 
62
                        if (isDeleted)
 
63
                                return;
 
64
                        Debug.Assert(currentDocument != null);
 
65
                        
 
66
                        Location loc = baseAnchor.Location;
 
67
                        line = loc.Line;
 
68
                        column = loc.Column;
 
69
                        
 
70
                        baseAnchor = null;
 
71
                        currentDocument = null;
 
72
                }
 
73
                
 
74
                internal void SetFileName(FileName newName)
 
75
                {
 
76
                        this.fileName = newName;
 
77
                }
 
78
                
 
79
                internal void FileDeleted()
 
80
                {
 
81
                        Debug.Assert(currentDocument == null);
 
82
                        if (!surviveDeletion) {
 
83
                                baseAnchor_Deleted(null, EventArgs.Empty);
 
84
                        }
 
85
                }
 
86
                
 
87
                void baseAnchor_Deleted(object sender, EventArgs e)
 
88
                {
 
89
                        isDeleted = true;
 
90
                        baseAnchor = null;
 
91
                        currentDocument = null;
 
92
                        
 
93
                        if (Deleted != null)
 
94
                                Deleted(this, e);
 
95
                }
 
96
                
 
97
                /// <summary>
 
98
                /// Gets the file name of the anchor.
 
99
                /// </summary>
 
100
                public FileName FileName {
 
101
                        get { return fileName; }
 
102
                }
 
103
                
 
104
                /// <inheritdoc/>
 
105
                public event EventHandler Deleted;
 
106
                
 
107
                /// <inheritdoc/>
 
108
                public Location Location {
 
109
                        get {
 
110
                                if (isDeleted)
 
111
                                        throw new InvalidOperationException();
 
112
                                if (baseAnchor != null)
 
113
                                        return baseAnchor.Location;
 
114
                                else
 
115
                                        return new Location(column, line);
 
116
                        }
 
117
                }
 
118
                
 
119
                /// <summary>
 
120
                /// Gets the editor to which this anchor currently belongs; or null if the file is not opened in any text editor.
 
121
                /// </summary>
 
122
                public IDocument CurrentDocument {
 
123
                        get { return currentDocument; }
 
124
                }
 
125
                
 
126
                /// <summary>
 
127
                /// Gets the offset.
 
128
                /// Warning: this method is only available while the document anchor is attached to a text editor, otherwise
 
129
                /// it will throw an InvalidOperationException.
 
130
                /// </summary>
 
131
                public int Offset {
 
132
                        get {
 
133
                                if (baseAnchor != null)
 
134
                                        return baseAnchor.Offset;
 
135
                                else
 
136
                                        throw new InvalidOperationException();
 
137
                        }
 
138
                }
 
139
                
 
140
                /// <inheritdoc/>
 
141
                public AnchorMovementType MovementType {
 
142
                        get { return movementType; }
 
143
                        set {
 
144
                                movementType = value;
 
145
                                if (baseAnchor != null)
 
146
                                        baseAnchor.MovementType = value;
 
147
                        }
 
148
                }
 
149
                
 
150
                /// <inheritdoc/>
 
151
                public bool SurviveDeletion {
 
152
                        get { return surviveDeletion; }
 
153
                        set {
 
154
                                surviveDeletion = value;
 
155
                                if (baseAnchor != null)
 
156
                                        baseAnchor.SurviveDeletion = value;
 
157
                        }
 
158
                }
 
159
                
 
160
                /// <inheritdoc/>
 
161
                public bool IsDeleted {
 
162
                        get { return isDeleted; }
 
163
                }
 
164
                
 
165
                /// <inheritdoc/>
 
166
                public int Line {
 
167
                        get {
 
168
                                if (baseAnchor != null)
 
169
                                        return baseAnchor.Line;
 
170
                                else
 
171
                                        return line;
 
172
                        }
 
173
                }
 
174
                
 
175
                /// <inheritdoc/>
 
176
                public int Column {
 
177
                        get {
 
178
                                if (baseAnchor != null)
 
179
                                        return baseAnchor.Column;
 
180
                                else
 
181
                                        return column;
 
182
                        }
 
183
                }
 
184
        }
 
185
        
 
186
        public static class PermanentAnchorService
 
187
        {
 
188
                static WeakCollection<PermanentAnchor> permanentAnchors = new WeakCollection<PermanentAnchor>();
 
189
                static Dictionary<FileName, IDocument> openDocuments = new Dictionary<FileName, IDocument>();
 
190
                
 
191
                internal static void AddAnchor(PermanentAnchor anchor)
 
192
                {
 
193
                        permanentAnchors.Add(anchor);
 
194
                        
 
195
                        IDocument doc;
 
196
                        if (openDocuments.TryGetValue(anchor.FileName, out doc))
 
197
                                anchor.AttachTo(doc);
 
198
                }
 
199
                
 
200
                /// <summary>
 
201
                /// Renames anchors without document.
 
202
                /// </summary>
 
203
                internal static void FileRenamed(FileRenameEventArgs e)
 
204
                {
 
205
                        FileName sourceFile = new FileName(e.SourceFile);
 
206
                        FileName targetFile = new FileName(e.TargetFile);
 
207
                        
 
208
                        foreach (PermanentAnchor anchor in permanentAnchors) {
 
209
                                if (anchor.CurrentDocument == null) {
 
210
                                        if (e.IsDirectory) {
 
211
                                                if (FileUtility.IsBaseDirectory(e.SourceFile, anchor.FileName)) {
 
212
                                                        anchor.SetFileName(new FileName(FileUtility.RenameBaseDirectory(anchor.FileName, e.SourceFile, e.TargetFile)));
 
213
                                                }
 
214
                                        } else {
 
215
                                                if (anchor.FileName == sourceFile)
 
216
                                                        anchor.SetFileName(targetFile);
 
217
                                        }
 
218
                                }
 
219
                        }
 
220
                }
 
221
                
 
222
                /// <summary>
 
223
                /// Deletes anchors without document.
 
224
                /// </summary>
 
225
                internal static void FileDeleted(FileEventArgs e)
 
226
                {
 
227
                        FileName fileName = new FileName(e.FileName);
 
228
                        foreach (PermanentAnchor anchor in permanentAnchors) {
 
229
                                if (anchor.CurrentDocument == null) {
 
230
                                        if (e.IsDirectory) {
 
231
                                                if (FileUtility.IsBaseDirectory(fileName, anchor.FileName))
 
232
                                                        anchor.FileDeleted();
 
233
                                        } else {
 
234
                                                if (fileName == anchor.FileName)
 
235
                                                        anchor.FileDeleted();
 
236
                                        }
 
237
                                }
 
238
                        }
 
239
                }
 
240
                
 
241
                /// <summary>
 
242
                /// Tells detached permanent anchors to attach to the specified text editor.
 
243
                /// </summary>
 
244
                public static void AttachDocument(FileName fileName, IDocument document)
 
245
                {
 
246
                        if (fileName == null)
 
247
                                throw new ArgumentNullException("fileName");
 
248
                        if (document == null)
 
249
                                throw new ArgumentNullException("document");
 
250
                        Gui.WorkbenchSingleton.AssertMainThread();
 
251
                        
 
252
                        // there may be multiple documents with the same file name - in that case, only attach to one of them
 
253
                        if (!openDocuments.ContainsKey(fileName)) {
 
254
                                openDocuments.Add(fileName, document);
 
255
                                foreach (PermanentAnchor anchor in permanentAnchors) {
 
256
                                        if (anchor.CurrentDocument == null && anchor.FileName == fileName) {
 
257
                                                anchor.AttachTo(document);
 
258
                                        }
 
259
                                }
 
260
                        }
 
261
                }
 
262
                
 
263
                /// <summary>
 
264
                /// Tells attached permanent anchors to detach from the specified text editor.
 
265
                /// </summary>
 
266
                public static void DetachDocument(FileName fileName, IDocument document)
 
267
                {
 
268
                        if (fileName == null)
 
269
                                throw new ArgumentNullException("fileName");
 
270
                        if (document == null)
 
271
                                throw new ArgumentNullException("document");
 
272
                        Gui.WorkbenchSingleton.AssertMainThread();
 
273
                        
 
274
                        IDocument actualDocument;
 
275
                        if (openDocuments.TryGetValue(fileName, out actualDocument)) {
 
276
                                // test whether we're detaching the correct document
 
277
                                if (document == actualDocument) {
 
278
                                        openDocuments.Remove(fileName);
 
279
                                        foreach (PermanentAnchor anchor in permanentAnchors) {
 
280
                                                if (anchor.CurrentDocument == document) {
 
281
                                                        anchor.Detach();
 
282
                                                }
 
283
                                        }
 
284
                                }
 
285
                        }
 
286
                }
 
287
                
 
288
                /// <summary>
 
289
                /// Informs the PermanentAnchorService when the file name of a document has changed.
 
290
                /// </summary>
 
291
                public static void RenameDocument(FileName oldFileName, FileName newFileName, IDocument document)
 
292
                {
 
293
                        if (oldFileName == null)
 
294
                                throw new ArgumentNullException("oldFileName");
 
295
                        if (newFileName == null)
 
296
                                throw new ArgumentNullException("newFileName");
 
297
                        if (document == null)
 
298
                                throw new ArgumentNullException("document");
 
299
                        Gui.WorkbenchSingleton.AssertMainThread();
 
300
                        
 
301
                        IDocument actualDocument;
 
302
                        if (openDocuments.TryGetValue(oldFileName, out actualDocument)) {
 
303
                                // test whether we're detaching the correct document
 
304
                                if (document == actualDocument) {
 
305
                                        if (openDocuments.ContainsKey(newFileName)) {
 
306
                                                // new file name already taken? just detach the old stuff
 
307
                                                DetachDocument(oldFileName, document);
 
308
                                        } else {
 
309
                                                openDocuments.Remove(oldFileName);
 
310
                                                openDocuments.Add(newFileName, document);
 
311
                                                foreach (PermanentAnchor anchor in permanentAnchors) {
 
312
                                                        if (anchor.CurrentDocument == document) {
 
313
                                                                anchor.SetFileName(newFileName);
 
314
                                                        }
 
315
                                                }
 
316
                                        }
 
317
                                }
 
318
                        }
 
319
                }
 
320
        }
 
321
}