/// <summary> /// Checks active document whether it can be searched /// </summary> protected void CheckActiveDocument() { Document currentDocument = VisualLocalizerPackage.Instance.DTE.ActiveDocument; if (currentDocument == null) { throw new Exception("No selected document"); } if (currentDocument.ProjectItem == null) { throw new Exception("Selected document has no corresponding Project Item."); } if (currentDocument.ProjectItem.ContainingProject == null) { throw new Exception("Selected document is not a part of any Project."); } if (RDTManager.IsFileReadonly(currentDocument.FullName) || VLDocumentViewsManager.IsFileLocked(currentDocument.FullName)) { throw new Exception("Cannot perform this operation - active document is readonly"); } if (VisualLocalizerPackage.Instance.DTE.Solution.FindProjectItem(currentDocument.FullName) == null) { throw new Exception("Selected document is not a part of an open Solution."); } }
/// <summary> /// Recursively searches given ProjectItems and returns list of project items satisfying given condition /// </summary> private static List <ProjectItem> GetFilesOf(ProjectItems items, Predicate <ProjectItem> test, bool includeReadonly) { List <ProjectItem> list = new List <ProjectItem>(); if (items != null) { foreach (ProjectItem item in items) { if (test == null || test(item)) { if (includeReadonly || !RDTManager.IsFileReadonly(item.GetFullPath())) { list.Add(item); } } else { if (item.ProjectItems != null && item.ProjectItems.Count > 0) { list.AddRange(GetFilesOf(item.ProjectItems, test, includeReadonly)); } } } } return(list); }
/// <summary> /// Set opened-state of the specified files /// </summary> /// <param name="testFiles"></param> /// <param name="shouldBeOpened">True if files should be opened, false otherwise</param> protected void SetFilesOpened(string[] testFiles, bool shouldBeOpened) { foreach (string sourcePath in testFiles) { if (!shouldBeOpened && RDTManager.IsFileOpen(sourcePath)) { var win = VsShellUtilities.GetWindowObject(VLDocumentViewsManager.GetWindowFrameForFile(sourcePath, false)); // close the window win.Detach(); win.Close(vsSaveChanges.vsSaveChangesNo); } if (shouldBeOpened) { Window win = null; // open the file and activate the window if (!RDTManager.IsFileOpen(sourcePath)) { win = Agent.GetDTE().OpenFile(null, sourcePath); } else { win = VsShellUtilities.GetWindowObject(VLDocumentViewsManager.GetWindowFrameForFile(sourcePath, true)); } Assert.IsNotNull(win, "Window cannot be opened " + sourcePath); win.Visible = true; win.Activate(); } } }
/// <summary> /// Explores given file, using parent batch command's methods as callbacks /// </summary> /// <param name="parentCommand"></param> /// <param name="projectItem"></param> /// <param name="maxLine">Line where parser should stop</param> /// <param name="maxIndex">Column where parser should stop</param> public void Explore(AbstractBatchCommand parentCommand, ProjectItem projectItem, int maxLine, int maxIndex) { if (parentCommand == null) { throw new ArgumentNullException("parentCommand"); } if (projectItem == null) { throw new ArgumentNullException("projectItem"); } lock (syncObject) { fullPath = projectItem.GetFullPath(); if (string.IsNullOrEmpty(fullPath)) { throw new Exception("Cannot process item " + projectItem.Name); } this.parentCommand = parentCommand; this.declaredNamespaces.Clear(); this.ClassFileName = Path.GetFileNameWithoutExtension(fullPath); this.projectItem = projectItem; this.openedElements.Clear(); // initialize type resolver if (parentCommand is BatchMoveCommand) { webConfig = WebConfig.Get(projectItem, VisualLocalizerPackage.Instance.DTE.Solution); } else { webConfig = null; } fileText = null; if (RDTManager.IsFileOpen(fullPath)) // file is open { var textLines = VLDocumentViewsManager.GetTextLinesForFile(fullPath, false); // get text buffer if (textLines == null) { return; } int lastLine, lastLineIndex; int hr = textLines.GetLastLineIndex(out lastLine, out lastLineIndex); Marshal.ThrowExceptionForHR(hr); hr = textLines.GetLineText(0, 0, lastLine, lastLineIndex, out fileText); // get plain text Marshal.ThrowExceptionForHR(hr); } else // file is closed - read it from disk { fileText = File.ReadAllText(fullPath); } Parser parser = new Parser(fileText, this, maxLine, maxIndex); // run ASP .NET parser parser.Process(); } }
/// <summary> /// Modifes lock on the document. This lock persists if document is closed and reopened. /// </summary> /// <param name="path">File path</param> /// <param name="setreadonly">True to set document readonly</param> public static void SetFileReadonly(string path, bool setreadonly) { if (path == null) { throw new ArgumentNullException("path"); } if (RDTManager.IsFileOpen(path)) // file is open { IVsWindowFrame frame = DocumentViewsManager.GetWindowFrameForFile(path, false); object docData; int hr = frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocData, out docData); // get document buffer Marshal.ThrowExceptionForHR(hr); if (docData is ResXEditor) // file is opened in ResXEditor { (docData as ResXEditor).ReadOnly = setreadonly; // use custom method to set it readonly } else // file is opened in VS editor { Document document = DTE.Documents.Item(path); document.ReadOnly = setreadonly; } // add/remove the document from locked documents list if (setreadonly) { if (!lockedDocuments.Contains(path)) { lockedDocuments.Add(path); } } else { lockedDocuments.Remove(path); } } else // file is closed { if (setreadonly) // add file to waiting documents set { if (!lockedDocumentsWaiting.Contains(path)) { lockedDocumentsWaiting.Add(path); } } else // remove the file from waiting documents { lockedDocumentsWaiting.Remove(path); } } }
/// <summary> /// Treats given ProjectItem as a VB code file, using VBCodeExplorer to examine the file. LookInVB method is called as a callback, /// given plain methods text. /// </summary> protected override void ProcessVB(ProjectItem projectItem, Predicate <CodeElement> exploreable, bool verbose) { if (isInitial || editorInstance.UIControl.sourceFilesThatNeedUpdate.Contains(projectItem.GetFullPath().ToLower())) { base.ProcessVB(projectItem, exploreable, verbose); } else { bool fileOpened; FileCodeModel2 codeModel = projectItem.GetCodeModel(false, false, out fileOpened); if (codeModel == null && !RDTManager.IsFileOpen(projectItem.GetFullPath())) { editorInstance.UIControl.RegisterAsStaticReferenceSource(projectItem); return; } if (codeModel == null) { if (verbose) { VLOutputWindow.VisualLocalizerPane.WriteLine("\tCannot process {0}, file code model does not exist.", projectItem.Name); } return; } if (verbose) { VLOutputWindow.VisualLocalizerPane.WriteLine("\tProcessing {0}", projectItem.Name); } currentlyProcessedItem = projectItem; try { VBCodeExplorer.Instance.Explore(this, exploreable, codeModel); } catch (COMException ex) { if (ex.ErrorCode == -2147483638) { VLOutputWindow.VisualLocalizerPane.WriteLine("\tError occured during processing {0} - the file is not yet compiled.", projectItem.Name); } else { throw; } } currentlyProcessedItem = null; } editorInstance.UIControl.sourceFilesThatNeedUpdate.Remove(projectItem.GetFullPath().ToLower()); }
/// <summary> /// Restores files from backups - copy the backuped file back, overwriting the current. The backuped file is deleted. /// </summary> protected void RestoreBackups(Dictionary <string, string> backups) { foreach (var pair in backups) { if (RDTManager.IsFileOpen(pair.Key)) { RDTManager.SetIgnoreFileChanges(pair.Key, true); File.Copy(pair.Value, pair.Key, true); File.Delete(pair.Value); RDTManager.SilentlyReloadFile(pair.Key); RDTManager.SetIgnoreFileChanges(pair.Key, false); } else { File.Copy(pair.Value, pair.Key, true); File.Delete(pair.Value); } } }
/// <summary> /// Returns code model for given item /// </summary> public static FileCodeModel2 GetCodeModel(this ProjectItem item, bool throwOnNull, bool openFileIfNecessary, out bool fileOpened) { if (item == null) { throw new ArgumentNullException("item"); } fileOpened = false; if (item.FileCodeModel == null && !RDTManager.IsFileOpen(item.GetFullPath()) && openFileIfNecessary) { item.Open(EnvDTE.Constants.vsext_vk_Code); fileOpened = true; } if (item.FileCodeModel == null && throwOnNull) { throw new InvalidOperationException("FileCodeModel for " + item.Name + " cannot be obtained. Try recompiling the file."); } return((FileCodeModel2)item.FileCodeModel); }
/// <summary> /// Flushes current Data. If the file is closed, it means write data to disk; otherwise corresponding file buffer is modified. /// </summary> public void Flush() { if (!dataChangedInBatchMode && IsInBatchMode) { return; } string path = InternalProjectItem.GetFullPath(); if (RDTManager.IsFileOpen(path)) { VLDocumentViewsManager.SaveDataToBuffer(data, path); // modify in-memory buffer of this file } else { // write data to disk ResXResourceWriter writer = null; try { writer = new ResXResourceWriter(path); writer.BasePath = Path.GetDirectoryName(path); foreach (var pair in data) { writer.AddResource(pair.Value); } writer.Generate(); } finally { if (writer != null) { writer.Close(); } } } // regenerate designer item if (DesignerItem != null) { RDTManager.SilentlyModifyFile(DesignerItem.GetFullPath(), (string p) => { RunCustomTool(); }); } }
/// <summary> /// Search given ProjectItem, using predicate to determine whether a code element should be explored (used when processing selection) /// </summary> /// <param name="projectItem">Item to search</param> /// <param name="exploreable">Predicate returning true, if given code element should be searched for result items</param> /// <param name="verbose"></param> protected virtual void Process(ProjectItem projectItem, Predicate <CodeElement> exploreable, bool verbose) { if (searchedProjectItems.Contains(projectItem)) { return; } searchedProjectItems.Add(projectItem); invisibleWindowsAuthor = GetType(); if (VLDocumentViewsManager.IsFileLocked(projectItem.GetFullPath()) || RDTManager.IsFileReadonly(projectItem.GetFullPath())) { if (verbose) { VLOutputWindow.VisualLocalizerPane.WriteLine("\tSkipping {0} - document is readonly", projectItem.Name); } } else { try { switch (projectItem.GetFileType()) { case FILETYPE.CSHARP: ProcessCSharp(projectItem, exploreable, verbose); break; case FILETYPE.ASPX: ProcessAspNet(projectItem, verbose); break; case FILETYPE.VB: ProcessVB(projectItem, exploreable, verbose); break; default: break; // do nothing if file type is not known } } catch (Exception ex) { if (verbose) { VLOutputWindow.VisualLocalizerPane.WriteLine("\tException occured while processing " + projectItem.Name); VLOutputWindow.VisualLocalizerPane.WriteException(ex); } } } }
/// <summary> /// Loads file's content into memory /// </summary> public void Load() { if (IsLoaded) { return; } string path = InternalProjectItem.GetFullPath(); if (RDTManager.IsFileOpen(path)) // file is open - read from VS buffer { VLDocumentViewsManager.LoadDataFromBuffer(ref data, path); } else // file is closed - read from disk { ResXResourceReader reader = null; try { data = new Dictionary <string, ResXDataNode>(); reader = new ResXResourceReader(path); reader.BasePath = Path.GetDirectoryName(path); reader.UseResXDataNodes = true; foreach (DictionaryEntry entry in reader) { data.Add(entry.Key.ToString().ToLower(), entry.Value as ResXDataNode); } } finally { if (reader != null) { reader.Close(); } } } IsLoaded = true; }
public void Move(List <CodeStringResultItem> dataList, ref int errorRows) { // sort according to position dataList.Sort(new ResultItemsPositionCompararer <CodeStringResultItem>()); for (int i = dataList.Count - 1; i >= 0; i--) { try { // initialization of data CodeStringResultItem resultItem = dataList[i]; string path = resultItem.SourceItem.GetFullPath(); ReferenceString referenceText = null; bool addUsingBlock = false; CONTAINS_KEY_RESULT keyConflict = CONTAINS_KEY_RESULT.DOESNT_EXIST; if (resultItem.MoveThisItem) // row was checked in the toolwindow { Validate(resultItem); // check that key, value and destination item was specifed and that row has no errors if (!resultItem.DestinationItem.IsLoaded) { resultItem.DestinationItem.Load(); } if (!loadedResxItems.Contains(resultItem.DestinationItem)) { loadedResxItems.Add(resultItem.DestinationItem); } // check if such item already exists in destination file keyConflict = resultItem.DestinationItem.GetKeyConflictType(resultItem.Key, resultItem.Value, true); if (keyConflict == CONTAINS_KEY_RESULT.EXISTS_WITH_DIFF_VALUE) { throw new InvalidOperationException(string.Format("Key \"{0}\" already exists with different value.", resultItem.Key)); } resultItem.Key = resultItem.DestinationItem.GetRealKey(resultItem.Key); // if key already exists, return its name (solves case-sensitivity problems) NamespacesList usedNamespaces = GetUsedNamespacesFor(resultItem); if (UseFullName || resultItem.MustUseFullName || (resultItem.Language == LANGUAGE.VB && resultItem.DestinationItem.IsProjectDefault(resultItem.SourceItem.ContainingProject))) // reference will contain namespace { referenceText = new ReferenceString(resultItem.DestinationItem.Namespace, resultItem.DestinationItem.Class, resultItem.Key); addUsingBlock = false; // no using block will be added } else { // use resolver whether it is ok to add using block addUsingBlock = usedNamespaces.ResolveNewElement(resultItem.DestinationItem.Namespace, resultItem.DestinationItem.Class, resultItem.Key, resultItem.SourceItem.ContainingProject, out referenceText); } if (addUsingBlock) // new using block will be added { if (!usedNamespacesCache.ContainsKey(resultItem.SourceItem)) { usedNamespacesCache.Add(resultItem.SourceItem, new NamespacesList()); } foreach (var pair in usedNamespacesCache) { if (!pair.Value.ContainsNamespace(resultItem.DestinationItem.Namespace)) { pair.Value.Add(resultItem.DestinationItem.Namespace, null, true); } } } } if (RDTManager.IsFileOpen(path) && RDTManager.IsFileVisible(path)) // file is open { if (resultItem.MoveThisItem || (MarkUncheckedStringsWithComment && !resultItem.IsMarkedWithUnlocalizableComment)) // string literal in text will be modified (referenced or marked with comment) { if (!buffersCache.ContainsKey(path)) { // load file's buffer IVsTextLines textLines = DocumentViewsManager.GetTextLinesForFile(path, false); buffersCache.Add(path, textLines); IOleUndoManager m; // get file's undo manager int hr = textLines.GetUndoManager(out m); Marshal.ThrowExceptionForHR(hr); undoManagersCache.Add(path, m); } } if (resultItem.MoveThisItem) { // perform the text replacement MoveToResource(buffersCache[path], resultItem, referenceText); if (resultItem.IsConst && resultItem.CodeModelSource is CodeVariable2) { CodeVariable2 codeVar = (CodeVariable2)resultItem.CodeModelSource; codeVar.ConstKind = vsCMConstKind.vsCMConstKindNone; } if (addUsingBlock) { // add using block to the source file int beforeLines, afterLines; buffersCache[path].GetLineCount(out beforeLines); resultItem.AddUsingBlock(buffersCache[path]); buffersCache[path].GetLineCount(out afterLines); int diff = afterLines - beforeLines; // because of the previous step, it is necessary to adjust position of all not-yet referenced result items for (int j = i; j >= 0; j--) { var item = dataList[j]; if (item.SourceItem == resultItem.SourceItem) { TextSpan ts = new TextSpan(); ts.iEndIndex = item.ReplaceSpan.iEndIndex; ts.iEndLine = item.ReplaceSpan.iEndLine + diff; ts.iStartIndex = item.ReplaceSpan.iStartIndex; ts.iStartLine = item.ReplaceSpan.iStartLine + diff; item.ReplaceSpan = ts; } } } // previous step (replace and possibly new using block) caused undo unit to be added - remove it int unitsToRemoveCount = (resultItem.IsConst && addUsingBlock ? 3 : (resultItem.IsConst || addUsingBlock ? 2 : 1)); List <IOleUndoUnit> units = undoManagersCache[path].RemoveTopFromUndoStack(unitsToRemoveCount); // and add custom undo unit AbstractUndoUnit newUnit = null; if (keyConflict == CONTAINS_KEY_RESULT.DOESNT_EXIST) { newUnit = new MoveToResourcesUndoUnit(resultItem.Key, resultItem.Value, resultItem.DestinationItem); } else if (keyConflict == CONTAINS_KEY_RESULT.EXISTS_WITH_SAME_VALUE) { newUnit = new MoveToResourcesReferenceUndoUnit(resultItem.Key); } newUnit.AppendUnits.AddRange(units); undoManagersCache[path].Add(newUnit); } else if (MarkUncheckedStringsWithComment && !resultItem.IsMarkedWithUnlocalizableComment) // string literal should be marked with comment { AspNetStringResultItem aitem = resultItem as AspNetStringResultItem; // this operation is only possible if string literal comes from C# code if (resultItem is CSharpStringResultItem || (aitem != null && aitem.ComesFromCodeBlock && aitem.Language == LANGUAGE.CSHARP)) { // add the comment int c = MarkAsNoLoc(buffersCache[path], resultItem); // add undo unit List <IOleUndoUnit> units = undoManagersCache[path].RemoveTopFromUndoStack(1); MarkAsNotLocalizedStringUndoUnit newUnit = new MarkAsNotLocalizedStringUndoUnit(resultItem.Value); newUnit.AppendUnits.AddRange(units); undoManagersCache[path].Add(newUnit); } } } else // file is closed // same as with open file, only operating with text, not buffers { if (resultItem.MoveThisItem || (MarkUncheckedStringsWithComment && !resultItem.IsMarkedWithUnlocalizableComment)) // string literal will be modified // load file's text into the cache { if (!filesCache.ContainsKey(path)) { filesCache.Add(path, new StringBuilder(File.ReadAllText(path))); } } if (resultItem.IsConst && resultItem.CodeModelSource is CodeVariable2) { CodeVariable2 codeVar = (CodeVariable2)resultItem.CodeModelSource; fieldsToRemoveConst.Add(codeVar); } if (resultItem.MoveThisItem) { StringBuilder b = filesCache[path]; // perform the replacement string insertText = resultItem.GetReferenceText(referenceText); b.Remove(resultItem.AbsoluteCharOffset, resultItem.AbsoluteCharLength); b.Insert(resultItem.AbsoluteCharOffset, insertText); if (addUsingBlock) { // add using block if (!newUsingsPlan.ContainsKey(path)) { newUsingsPlan.Add(path, new List <string>()); } newUsingsPlan[path].Add(resultItem.DestinationItem.Namespace); } } else if (MarkUncheckedStringsWithComment && !resultItem.IsMarkedWithUnlocalizableComment) { AspNetStringResultItem aitem = resultItem as AspNetStringResultItem; if (resultItem is CSharpStringResultItem || (aitem != null && aitem.ComesFromCodeBlock && aitem.Language == LANGUAGE.CSHARP)) { StringBuilder b = filesCache[path]; b.Insert(resultItem.AbsoluteCharOffset, resultItem.NoLocalizationComment); } } } if (resultItem.MoveThisItem && keyConflict == CONTAINS_KEY_RESULT.DOESNT_EXIST) { if (!resultItem.DestinationItem.IsInBatchMode) { resultItem.DestinationItem.BeginBatch(); } // add the key to the ResX file resultItem.DestinationItem.AddString(resultItem.Key, resultItem.Value); } } catch (Exception ex) { errorRows++; VLOutputWindow.VisualLocalizerPane.WriteException(ex); } } // add using blocks to closed files texts foreach (var pair in newUsingsPlan) { foreach (string nmspc in pair.Value) { AddUsingBlockTo(pair.Key, nmspc); } } // flush closed files texts foreach (var pair in filesCache) { if (RDTManager.IsFileOpen(pair.Key)) { RDTManager.SetIgnoreFileChanges(pair.Key, true); File.WriteAllText(pair.Key, pair.Value.ToString()); RDTManager.SetIgnoreFileChanges(pair.Key, false); RDTManager.SilentlyReloadFile(pair.Key); } else { File.WriteAllText(pair.Key, pair.Value.ToString()); } } // remove 'const' modifier from fields in closed files HashSet <ProjectItem> itemsToSave = new HashSet <ProjectItem>(); foreach (CodeVariable2 codeVar in fieldsToRemoveConst) { codeVar.ConstKind = vsCMConstKind.vsCMConstKindNone; itemsToSave.Add(codeVar.ProjectItem); } foreach (ProjectItem item in itemsToSave) { item.Save(null); } foreach (ResXProjectItem item in loadedResxItems) { if (item.IsInBatchMode) { item.EndBatch(); } item.Unload(); } if (errorRows > 0) { throw new Exception("Error occured while processing some rows - see Output window for details."); } }
/// <summary> /// If the given item is ResX file, adds it to the list of ResX files /// </summary> private void SearchForResxFiles(ProjectItem item, List <GlobalTranslateProjectItem> resxFiles) { if (searchedProjectItems.Contains(item)) { return; } SearchForResxFiles(item.ProjectItems, resxFiles); if (item.IsItemResX()) { GlobalTranslateProjectItem r = new GlobalTranslateProjectItem(item); r.Checked = false; r.Readonly = VLDocumentViewsManager.IsFileLocked(item.GetFullPath()) || RDTManager.IsFileReadonly(item.GetFullPath()); resxFiles.Add(r); } }
/// <summary> /// Add string data from given ResX file to the list of data for translation /// </summary> private void AddDataForTranslation(GlobalTranslateProjectItem item, List <AbstractTranslateInfoItem> data) { string path = item.ProjectItem.GetFullPath(); if (RDTManager.IsFileOpen(path)) // file is open { object docData = VLDocumentViewsManager.GetDocData(path); // get document buffer if (docData is ResXEditor) // document is opened in ResX editor -> use custom method to get string data { ResXEditor editor = (ResXEditor)docData; editor.UIControl.AddForTranslation(data); } else // document is opened in original VS editor { IVsTextLines lines = VLDocumentViewsManager.GetTextLinesForFile(path, false); string text = VLDocumentViewsManager.GetTextFrom(lines); // get plain text of ResX file ResXResourceReader reader = null; BufferTranslateInfoItem prev = null; BufferTranslateInfoItem first = null; try { reader = ResXResourceReader.FromFileContents(text); reader.UseResXDataNodes = true; // add all string resources to the list // items are linked like a linked-list, allowing ApplyTranslation to work foreach (DictionaryEntry entry in reader) { ResXDataNode node = (entry.Value as ResXDataNode); if (node.HasValue <string>()) { BufferTranslateInfoItem translateItem = new BufferTranslateInfoItem(); translateItem.ResourceKey = entry.Key.ToString(); translateItem.Value = node.GetValue <string>(); translateItem.Filename = path; translateItem.Applied = false; translateItem.GlobalTranslateItem = item; translateItem.Prev = prev; translateItem.IVsTextLines = lines; data.Add(translateItem); prev = translateItem; if (first == null) { first = translateItem; } } else { item.NonStringData.Add(node); } } if (first != null) { first.Prev = prev; } } finally { if (reader != null) { reader.Close(); } } } } else // file is closed { ResXProjectItem resxItem = ResXProjectItem.ConvertToResXItem(item.ProjectItem, item.ProjectItem.ContainingProject); resxItem.Load(); loadedResxItems.Add(resxItem); // add string data from ResX file resxItem.AddAllStringReferencesUnique(data); } }
public void Inline(List <CodeReferenceResultItem> dataList, bool externalChange, ref int errorRows) { // sort according to position dataList.Sort(new ResultItemsPositionCompararer <CodeReferenceResultItem>()); // start with the last items - not necessary to adjust position of many items after replace for (int i = dataList.Count - 1; i >= 0; i--) { try { CodeReferenceResultItem resultItem = dataList[i]; if (resultItem.MoveThisItem) // the item was checked in the toolwindow grid { int absoluteStartIndex, absoluteLength; // get text that replaces the result item string text = GetReplaceString(resultItem); // get position information about block to replace TextSpan inlineSpan = GetInlineReplaceSpan(resultItem, out absoluteStartIndex, out absoluteLength); string path = resultItem.SourceItem.GetFullPath(); if (RDTManager.IsFileOpen(path) && RDTManager.IsFileVisible(path)) // file is open { if (!buffersCache.ContainsKey(path)) // file's buffer is not yet loaded // load buffer { IVsTextLines textLines = DocumentViewsManager.GetTextLinesForFile(path, false); buffersCache.Add(path, textLines); IOleUndoManager m; // load undo manager int hr = textLines.GetUndoManager(out m); Marshal.ThrowExceptionForHR(hr); undoManagersCache.Add(path, m); } // replace the result item with the new text int h = buffersCache[path].ReplaceLines(inlineSpan.iStartLine, inlineSpan.iStartIndex, inlineSpan.iEndLine, inlineSpan.iEndIndex, Marshal.StringToBSTR(text), text.Length, new TextSpan[] { inlineSpan }); Marshal.ThrowExceptionForHR(h); // previous step caused undo unit to be added - remove it List <IOleUndoUnit> units = undoManagersCache[path].RemoveTopFromUndoStack(1); // and add custom undo unit which includes whole operation AbstractUndoUnit newUnit = GetUndoUnit(resultItem, externalChange); newUnit.AppendUnits.AddRange(units); undoManagersCache[path].Add(newUnit); } else { if (!filesCache.ContainsKey(path)) // file is not yet loaded // load the file and save it in cache { filesCache.Add(path, new StringBuilder(File.ReadAllText(path))); } StringBuilder b = filesCache[path]; // replace the text b = b.Remove(absoluteStartIndex, absoluteLength); b = b.Insert(absoluteStartIndex, text); filesCache[path] = b; } } } catch (Exception ex) { errorRows++; VLOutputWindow.VisualLocalizerPane.WriteException(ex); } } foreach (var pair in filesCache) { if (RDTManager.IsFileOpen(pair.Key)) { RDTManager.SetIgnoreFileChanges(pair.Key, true); File.WriteAllText(pair.Key, pair.Value.ToString()); RDTManager.SetIgnoreFileChanges(pair.Key, false); RDTManager.SilentlyReloadFile(pair.Key); } else { File.WriteAllText(pair.Key, pair.Value.ToString()); } } if (errorRows > 0) { throw new Exception("Error occured while processing some rows - see Output window for details."); } }