public static TResult Extension <TSource, TResult>(this TSource source, ExtensionSelector <TSource, TResult> selector, Action <TResult> callback = null) where TSource : IExtensible <TSource> where TResult : IExtension <TSource> { var extensions = (ExtensionCollection <TSource>)Extensions.GetValue(source, (x) => new ExtensionCollection <TSource>()); var extension = (IExtension <TSource>)null; if (false == extensions.TryGetValue(typeof(TResult), out extension)) { extension = (TResult)Activator.CreateInstance(typeof(TResult), source); extensions[typeof(TResult)] = extension; } callback?.Invoke((TResult)extension); return((TResult)extension); }
public ReloadResult Reload(EditOptions options) { if (_isDisposed) { throw new ObjectDisposedException(nameof(ITextDocument)); } if (_raisingDirtyStateChangedEvent || _raisingFileActionChangedEvent) { throw new InvalidOperationException(); } Encoding newEncoding; var beforeSnapshot = _textBuffer.CurrentSnapshot; bool characterSubstitutionsOccurred = false; try { _reloadingFile = true; // Load the file and read the contents to the text buffer long fileSize; using (var stream = TextDocumentFactoryService.OpenFile(_filePath, out _lastModifiedTimeUtc, out fileSize)) { var detectors = ExtensionSelector.SelectMatchingExtensions(_textDocumentFactoryService.OrderedEncodingDetectors, _textBuffer.ContentType); if (_explicitEncoding) { // If the user explicitly chose their encoding, we want to respect it. newEncoding = this.Encoding; } else { newEncoding = EncodedStreamReader.DetectEncoding(stream, detectors, _textDocumentFactoryService.GuardedOperations); } if (newEncoding == null && _attemptUtf8Detection) { try { var detectorEncoding = new ExtendedCharacterDetector(); ReloadBufferFromStream(stream, fileSize, options, detectorEncoding); if (detectorEncoding.DecodedExtendedCharacters) { // Valid UTF-8 but has bytes that are not merely ASCII. newEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); } else { // Valid UTF8 but no extended characters, so it looks like valid ASCII. // However, we don't use ASCII here because of the following scenario: // The user with a non-English US system encoding opens a code file that happens to contain ASCII-only contents // Therefore we'll just use their system encoding. newEncoding = Encoding.Default; } } catch (DecoderFallbackException) { // Not valid UTF-8. // Proceed to the next if block to try the system's default codepage. // For example, this occurs when you have extended characters like € in a UTF-8 file or ANSI file. // We reset the stream so we can continue loading with the default system encoding. Debug.Assert(newEncoding == null); Debug.Assert(beforeSnapshot.Version.Next == null); stream.Position = 0; } } // If all else didn't work, use system's default encoding. if (newEncoding == null) { newEncoding = Encoding.Default; } //If there is no "Next" version of the original snapshot, we have not successfully reloaded the document if (beforeSnapshot.Version.Next == null) { //We use this fall back detector to observe whether or not character substitutions //occur while we're reading the stream var fallbackDetector = new FallbackDetector(newEncoding.DecoderFallback); var modifiedEncoding = (Encoding)newEncoding.Clone(); modifiedEncoding.DecoderFallback = fallbackDetector; Debug.Assert(stream.Position == 0); ReloadBufferFromStream(stream, fileSize, options, modifiedEncoding); if (fallbackDetector.FallbackOccurred) { characterSubstitutionsOccurred = fallbackDetector.FallbackOccurred; } } } } finally { _reloadingFile = false; } //The snapshot on a reload will change even if the contents of the before & after files are identical (differences will simply find an //empty set of changes) so this test is a measure of whether of not the reload succeeded. if (beforeSnapshot.Version.Next != null) { // Update status // set the "clean" reiterated version number to the reiterated version number of the version immediately // after the before snapshot (which is the state of the buffer after loading the document but before any // subsequent edits made in the text buffer changed events). _cleanReiteratedVersion = beforeSnapshot.Version.Next.ReiteratedVersionNumber; // TODO: the following event really should be queued up through the buffer group so that it comes before // the text changed event (and any subsequent text changed event invoked from an event handler) RaiseFileActionChangedEvent(_lastModifiedTimeUtc, FileActionTypes.ContentLoadedFromDisk, _filePath); this.Encoding = newEncoding; return(characterSubstitutionsOccurred ? ReloadResult.SucceededWithCharacterSubstitutions : ReloadResult.Succeeded); } else { return(ReloadResult.Aborted); } }
public ITextDocument CreateAndLoadTextDocument(string filePath, IContentType contentType, bool attemptUtf8Detection, out bool characterSubstitutionsOccurred) { if (filePath == null) { throw new ArgumentNullException(nameof(filePath)); } if (contentType == null) { throw new ArgumentNullException(nameof(contentType)); } characterSubstitutionsOccurred = false; Encoding chosenEncoding = null; ITextBuffer buffer = null; DateTime lastModified; long fileSize; // select matching detectors without instantiating any var detectors = ExtensionSelector.SelectMatchingExtensions(OrderedEncodingDetectors, contentType); using (Stream stream = OpenFile(filePath, out lastModified, out fileSize)) { // First, look for a byte order marker and let the encoding detecters // suggest encodings. chosenEncoding = EncodedStreamReader.DetectEncoding(stream, detectors, GuardedOperations); // If that didn't produce a result, tentatively try to open as UTF 8. if (chosenEncoding == null && attemptUtf8Detection) { try { var detectorEncoding = new ExtendedCharacterDetector(); using (StreamReader reader = new EncodedStreamReader.NonStreamClosingStreamReader(stream, detectorEncoding, false)) { buffer = ((ITextBufferFactoryService2)BufferFactoryService).CreateTextBuffer(reader, contentType, fileSize, filePath); characterSubstitutionsOccurred = false; } if (detectorEncoding.DecodedExtendedCharacters) { // Valid UTF-8 but has bytes that are not merely ASCII. chosenEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); } else { // Valid UTF8 but no extended characters, so it's valid ASCII. // We don't use ASCII here because of the following scenario: // The user with a non-ENU system encoding opens a code file with ASCII-only contents chosenEncoding = DefaultEncoding; } } catch (DecoderFallbackException) { // Not valid UTF-8. // Proceed to the next if block to try the system's default codepage. Debug.Assert(buffer == null); buffer = null; stream.Position = 0; } } Debug.Assert(buffer == null || chosenEncoding != null); // If all else didn't work, use system's default encoding. if (chosenEncoding == null) { chosenEncoding = DefaultEncoding; } if (buffer == null) { var fallbackDetector = new FallbackDetector(chosenEncoding.DecoderFallback); var modifiedEncoding = (Encoding)chosenEncoding.Clone(); modifiedEncoding.DecoderFallback = fallbackDetector; Debug.Assert(stream.Position == 0); using (StreamReader reader = new EncodedStreamReader.NonStreamClosingStreamReader(stream, modifiedEncoding, detectEncodingFromByteOrderMarks: false)) { Debug.Assert(chosenEncoding.CodePage == reader.CurrentEncoding.CodePage); buffer = ((ITextBufferFactoryService2)BufferFactoryService).CreateTextBuffer(reader, contentType, fileSize, filePath); } characterSubstitutionsOccurred = fallbackDetector.FallbackOccurred; } } TextDocument textDocument = new TextDocument(buffer, filePath, lastModified, this, chosenEncoding, attemptUtf8Detection: attemptUtf8Detection); RaiseTextDocumentCreated(textDocument); return(textDocument); }