11 changed files with 486 additions and 48 deletions
@ -0,0 +1,72 @@ |
|||||||
|
// 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 System.IO; |
||||||
|
using ICSharpCode.Core; |
||||||
|
|
||||||
|
namespace ICSharpCode.SharpDevelop.Workbench |
||||||
|
{ |
||||||
|
sealed class BinaryFileModelProvider : IFileModelProvider<IBinaryModel> |
||||||
|
{ |
||||||
|
sealed class OnDiskBinaryModel : IBinaryModel |
||||||
|
{ |
||||||
|
internal readonly OpenedFile file; |
||||||
|
|
||||||
|
public OnDiskBinaryModel(OpenedFile file) |
||||||
|
{ |
||||||
|
this.file = file; |
||||||
|
} |
||||||
|
|
||||||
|
public Stream OpenRead() |
||||||
|
{ |
||||||
|
return SD.FileSystem.OpenRead(file.FileName); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public IBinaryModel Load(OpenedFile file) |
||||||
|
{ |
||||||
|
return new OnDiskBinaryModel(file); |
||||||
|
} |
||||||
|
|
||||||
|
public void Save(OpenedFile file, IBinaryModel model) |
||||||
|
{ |
||||||
|
SaveCopyAs(file, model, file.FileName); |
||||||
|
file.ReplaceModel(this, model, ReplaceModelMode.SetAsValid); // remove dirty flag
|
||||||
|
} |
||||||
|
|
||||||
|
public void SaveCopyAs(OpenedFile file, IBinaryModel model, FileName outputFileName) |
||||||
|
{ |
||||||
|
var onDisk = model as OnDiskBinaryModel; |
||||||
|
if (onDisk != null) { |
||||||
|
// We can just copy the file (but avoid copying to itself)
|
||||||
|
if (onDisk.file.FileName != outputFileName) { |
||||||
|
SD.FileSystem.CopyFile(onDisk.file.FileName, outputFileName); |
||||||
|
} |
||||||
|
} else { |
||||||
|
using (var inputStream = model.OpenRead()) { |
||||||
|
using (var outputStream = SD.FileSystem.OpenWrite(outputFileName)) { |
||||||
|
inputStream.CopyTo(outputStream); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool CanLoadFrom<U>(IFileModelProvider<U> otherProvider) where U : class |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public void NotifyRename(OpenedFile file, IBinaryModel model, FileName oldName, FileName newName) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public void NotifyStale(OpenedFile file, IBinaryModel model) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public void NotifyUnloaded(OpenedFile file, IBinaryModel model) |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
// 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 System.Text; |
||||||
|
|
||||||
|
namespace ICSharpCode.SharpDevelop.Workbench |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Extra information about a TextDocument that was loaded from an OpenedFile.
|
||||||
|
/// </summary>
|
||||||
|
public class DocumentFileModelInfo |
||||||
|
{ |
||||||
|
public DocumentFileModelInfo() |
||||||
|
{ |
||||||
|
this.Encoding = SD.FileService.DefaultFileEncoding; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether the model is stale.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsStale { get; internal set; } |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the encoding of the model.
|
||||||
|
/// </summary>
|
||||||
|
public Encoding Encoding { get; set; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
// 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 System.IO; |
||||||
|
using ICSharpCode.AvalonEdit.Document; |
||||||
|
|
||||||
|
namespace ICSharpCode.SharpDevelop.Workbench |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Pre-defined file models.
|
||||||
|
/// </summary>
|
||||||
|
public static class FileModels |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// The binary file model provider.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly IFileModelProvider<IBinaryModel> Binary = new BinaryFileModelProvider(); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The text document file model provider.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly IFileModelProvider<TextDocument> TextDocument = new TextDocumentFileModelProvider(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the binary contents of a file.
|
||||||
|
/// </summary>
|
||||||
|
public interface IBinaryModel |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Creates a stream that reads from the binary model.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A readable and seekable stream.</returns>
|
||||||
|
/// <exception cref="IOException">Error opening the file.</exception>
|
||||||
|
Stream OpenRead(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
// 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.Core; |
||||||
|
|
||||||
|
namespace ICSharpCode.SharpDevelop.Workbench |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Interface that deals with loading and saving OpenedFile model instances.
|
||||||
|
/// Pre-defined model providers are available in the <see cref="FileModels"/> class.
|
||||||
|
/// Custom IFileModelProvider implementations may be used to add support for additional types of models.
|
||||||
|
/// </summary>
|
||||||
|
public interface IFileModelProvider<T> where T : class |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Loads a file model from the OpenedFile.
|
||||||
|
/// Most implementations of this method will request a lower-level model (e.g. FileModels.Binary) from the opened file
|
||||||
|
/// and construct a higher-level representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="System.IO.IOException">Error loading the file.</exception>
|
||||||
|
/// <exception cref="FormatException">Cannot construct the model because the underyling data is in an invalid format.</exception>
|
||||||
|
T Load(OpenedFile file); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves a file model.
|
||||||
|
/// The file should be saved to a location where a subsequent Load() operation can load from.
|
||||||
|
/// Usually, this means saving to a lower-level model (e.g. a TextDocument model is saved by creating a binary model).
|
||||||
|
///
|
||||||
|
/// After the Save() method completes successfully, the specified model may no longer be marked as dirty. For models saving to
|
||||||
|
/// lower-level models, this is done by marking that lower-level model as dirty.
|
||||||
|
/// In other cases, the provider may have to explicitly remove the dirty flag from the model.
|
||||||
|
/// </summary>
|
||||||
|
void Save(OpenedFile file, T model); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves a file model to disk. This method may by-pass lower-level models (such as FileModels.Binary) and directly write to disk.
|
||||||
|
/// Note that this method is supposed to save a copy of the file, without resetting the dirty flag (think "Save Copy As...").
|
||||||
|
/// However, transferring the dirty flag to a lower-level model is allowed.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="System.IO.IOException">Error saving the file.</exception>
|
||||||
|
void SaveCopyAs(OpenedFile file, T model, FileName outputFileName); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether this provider can load from the specified other provider.
|
||||||
|
/// It is important that CanLoadFrom() is transitive!
|
||||||
|
/// For a file model provider that loads from a text document model, the implementation should look like this:
|
||||||
|
/// <code>
|
||||||
|
/// return otherProvider == FileModels.TextDocument || FileModels.TextDocument.CanLoadFrom(otherProvider);
|
||||||
|
/// </code>
|
||||||
|
/// </summary>
|
||||||
|
bool CanLoadFrom<U>(IFileModelProvider<U> otherProvider) where U : class; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Notifies the provider that a file was renamed.
|
||||||
|
/// If the file name is already available as part of the model, the provider is responsible for updating the model.
|
||||||
|
/// </summary>
|
||||||
|
void NotifyRename(OpenedFile file, T model, FileName oldName, FileName newName); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Notifies the provider that a model was marked as stale.
|
||||||
|
/// </summary>
|
||||||
|
void NotifyStale(OpenedFile file, T model); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Notifies a model that it became unloaded.
|
||||||
|
/// This can happen due to:
|
||||||
|
/// <list type="items"></list>
|
||||||
|
/// <item>an explicit <see cref="OpenedFile.UnloadModel"/> call</item>
|
||||||
|
/// <item>a <see cref="OpenedFile.ReplaceModel"/> call replacing this model with another</item>
|
||||||
|
/// <item>a <see cref="OpenedFile.GetModel"/> call replacing a stale model with another</item>
|
||||||
|
/// <item>the OpenedFile getting disposed</item>
|
||||||
|
/// </summary>
|
||||||
|
void NotifyUnloaded(OpenedFile file, T model); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,89 @@ |
|||||||
|
// 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 System.Collections.Generic; |
||||||
|
using System.ComponentModel.Design; |
||||||
|
using System.Diagnostics; |
||||||
|
using System.IO; |
||||||
|
using System.Text; |
||||||
|
using ICSharpCode.AvalonEdit.Document; |
||||||
|
using ICSharpCode.AvalonEdit.Utils; |
||||||
|
using ICSharpCode.Core; |
||||||
|
using ICSharpCode.SharpDevelop.Widgets.MyersDiff; |
||||||
|
|
||||||
|
namespace ICSharpCode.SharpDevelop.Workbench |
||||||
|
{ |
||||||
|
sealed class TextDocumentFileModelProvider : IFileModelProvider<TextDocument> |
||||||
|
{ |
||||||
|
public TextDocument Load(OpenedFile file) |
||||||
|
{ |
||||||
|
TextDocument document = file.GetModel(this, GetModelOptions.AllowStale | GetModelOptions.DoNotLoad); |
||||||
|
var info = document != null ? document.GetFileModelInfo() : new DocumentFileModelInfo(); |
||||||
|
string textContent; |
||||||
|
using (Stream stream = file.GetModel(FileModels.Binary).OpenRead()) { |
||||||
|
using (StreamReader reader = FileReader.OpenStream(stream, SD.FileService.DefaultFileEncoding)) { |
||||||
|
textContent = reader.ReadToEnd(); |
||||||
|
info.Encoding = reader.CurrentEncoding; |
||||||
|
} |
||||||
|
} |
||||||
|
if (document != null) { |
||||||
|
// Reload document
|
||||||
|
var diff = new MyersDiffAlgorithm(new StringSequence(document.Text), new StringSequence(textContent)); |
||||||
|
document.Replace(0, document.TextLength, textContent, ToOffsetChangeMap(diff.GetEdits())); |
||||||
|
document.UndoStack.ClearAll(); |
||||||
|
info.IsStale = false; |
||||||
|
return document; |
||||||
|
} else { |
||||||
|
document = new TextDocument(textContent); |
||||||
|
document.GetRequiredService<IServiceContainer>().AddService(typeof(DocumentFileModelInfo), info); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
OffsetChangeMap ToOffsetChangeMap(IEnumerable<Edit> edits) |
||||||
|
{ |
||||||
|
var map = new OffsetChangeMap(); |
||||||
|
int diff = 0; |
||||||
|
foreach (var edit in edits) { |
||||||
|
Debug.Assert(edit.EditType != ChangeType.None && edit.EditType != ChangeType.Unsaved); |
||||||
|
int offset = edit.BeginA + diff; |
||||||
|
int removalLength = edit.EndA - edit.BeginA; |
||||||
|
int insertionLength = edit.EndB - edit.BeginB; |
||||||
|
|
||||||
|
diff += (insertionLength - removalLength); |
||||||
|
map.Add(new OffsetChangeMapEntry(offset, removalLength, insertionLength)); |
||||||
|
} |
||||||
|
return map; |
||||||
|
} |
||||||
|
|
||||||
|
public void Save(OpenedFile file, TextDocument model) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public void SaveCopyAs(OpenedFile file, TextDocument model, FileName outputFileName) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
bool IFileModelProvider<TextDocument>.CanLoadFrom<U>(IFileModelProvider<U> otherProvider) |
||||||
|
{ |
||||||
|
return otherProvider == FileModels.Binary || FileModels.Binary.CanLoadFrom(otherProvider); |
||||||
|
} |
||||||
|
|
||||||
|
public void NotifyRename(OpenedFile file, TextDocument model, FileName oldName, FileName newName) |
||||||
|
{ |
||||||
|
model.FileName = newName; |
||||||
|
} |
||||||
|
|
||||||
|
public void NotifyStale(OpenedFile file, TextDocument model) |
||||||
|
{ |
||||||
|
model.GetFileModelInfo().IsStale = true; |
||||||
|
} |
||||||
|
|
||||||
|
public void NotifyUnloaded(OpenedFile file, TextDocument model) |
||||||
|
{ |
||||||
|
model.GetFileModelInfo().IsStale = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue