/// <summary> /// Called after key was changed - adds undo unit and performs refactoring of code /// </summary> public void KeyRenamed(ResXStringGridRow row, string newKey) { string oldKey = row.Status == KEY_STATUS.ERROR ? null : row.DataSourceItem.Name; GridRenameKeyUndoUnit unit = new GridRenameKeyUndoUnit(row, editorControl, oldKey, newKey); editorControl.Editor.AddUndoUnit(unit); if (VisualLocalizerPackage.Instance.DTE.Solution.ContainsProjectItem(editorControl.Editor.ProjectItem.InternalProjectItem)) { // obtain ResX project item ResXProjectItem resxItem = editorControl.Editor.ProjectItem; resxItem.ResolveNamespaceClass(resxItem.InternalProjectItem.ContainingProject.GetResXItemsAround(false, true)); if (row.ConflictItems.Count == 0 && resxItem != null && !resxItem.IsCultureSpecific() && !string.IsNullOrEmpty(newKey)) { int errors = 0; int count = row.CodeReferences.Count; // set new key row.CodeReferences.ForEach((item) => { item.KeyAfterRename = newKey.CreateIdentifier(resxItem.DesignerLanguage); }); // run the replacer try { editorControl.ReferenceCounterThreadSuspended = true; BatchReferenceReplacer replacer = new BatchReferenceReplacer(); replacer.Inline(row.CodeReferences, true, ref errors); } finally { editorControl.ReferenceCounterThreadSuspended = false; } VLOutputWindow.VisualLocalizerPane.WriteLine("Renamed {0} key references in code, {1} errors", count, errors); } } }
/// <summary> /// Removes all data from specified ResX file /// </summary> private void ClearFile(ResXProjectItem target) { try { target.Load(); target.Data.Clear(); target.Flush(); target.Unload(); } catch (Exception) { } }
/// <summary> /// Returns number of string resources in specified ResX file /// </summary> private int GetResourcesCountIn(ResXProjectItem item) { item.Load(); int result = item.GetAllStringReferences(false).Count; item.Unload(); return(result); }
/// <summary> /// Initializes list of ResX items /// </summary> private Dictionary <string, ResXProjectItem> InitResxItems(string[] targetFiles, Project containingProject) { Dictionary <string, ResXProjectItem> d = new Dictionary <string, ResXProjectItem>(); for (int i = 0; i < targetFiles.Length; i++) { ProjectItem pitem = Agent.GetDTE().Solution.FindProjectItem(targetFiles[i]); d.Add(targetFiles[i], ResXProjectItem.ConvertToResXItem(pitem, containingProject)); } return(d); }
/// <summary> /// Adds a new undo unit to the undo stack, representing the "move to resources" action. /// Text replacement and adding new using block is already in the undo stack - /// these items are removed and merged into one atomic action. /// </summary> /// <param name="key">Resource file key</param> /// <param name="value">Resource value</param> /// <param name="resXProjectItem">Destination ResX project item</param> /// <param name="addNamespace">Whether new using block has been added</param> /// <returns>True, if original undo units has been successfully removed from the undo stack</returns> private bool CreateMoveToResourcesUndoUnit(string key, string value, ResXProjectItem resXProjectItem, bool addNamespace, bool removeConst) { bool unitsRemoved = false; int unitsToRemoveCount = (addNamespace && removeConst) ? 3 : (addNamespace || removeConst ? 2 : 1); List <IOleUndoUnit> units = undoManager.RemoveTopFromUndoStack(unitsToRemoveCount); unitsRemoved = true; MoveToResourcesUndoUnit newUnit = new MoveToResourcesUndoUnit(key, value, resXProjectItem); newUnit.AppendUnits.AddRange(units); undoManager.Add(newUnit); return(unitsRemoved); }
public MoveToResourcesUndoUnit(string key, string value, ResXProjectItem resxItem) { if (key == null) { throw new ArgumentNullException("key"); } if (resxItem == null) { throw new ArgumentNullException("resxItem"); } this.Key = key; this.Item = resxItem; this.Value = value; }
public MoveToResourcesOverwriteUndoUnit(string key, string oldValue, string newValue, ResXProjectItem resxItem) { if (key == null) { throw new ArgumentNullException("key"); } if (resxItem == null) { throw new ArgumentNullException("resxItem"); } this.Key = key; this.Item = resxItem; this.OldValue = oldValue; this.NewValue = newValue; }
/// <summary> /// Sets destination file of selected rows to given ResX file /// </summary> private void SetDestinationOfSelected(ResXProjectItem item) { try { if (item == null) { throw new ArgumentNullException("item"); } foreach (DataGridViewKeyValueRow <CodeStringResultItem> row in SelectedRows) { row.Cells[DestinationColumnName].Value = item.ToString(); Validate(row); } } catch (Exception ex) { VLOutputWindow.VisualLocalizerPane.WriteException(ex); VisualLocalizer.Library.Components.MessageBox.ShowException(ex); } }
/// <summary> /// Returns list of references to resource within given block of code /// </summary> /// <param name="projectItem">Project item where code belongs</param> /// <param name="text">Text to search</param> /// <param name="currentIndex">Position of the code block in its source file - column</param> /// <param name="currentLine">Position of the code block in its source file - line</param> /// <param name="currentOffset">Position of the code block in its source file - char offset</param> /// <param name="Trie">Trie consisting of searched references</param> /// <param name="usedNamespaces">Namespaces affecting the block of code</param> /// <param name="isWithinLocFalse">True if code block is decorated with [Localizable(false)]</param> /// <param name="project">Project in which the code belongs</param> /// <param name="prefferedResXItem"></param> /// <returns></returns> public List <T> LookForReferences(ProjectItem projectItem, string text, int currentIndex, int currentLine, int currentOffset, Trie <CodeReferenceTrieElement> Trie, NamespacesList usedNamespaces, bool isWithinLocFalse, Project project, ResXProjectItem prefferedResXItem) { lock (syncRoot) { this.SourceItem = projectItem; this.text = text; this.CurrentIndex = currentIndex; this.CurrentLine = currentLine; this.CurrentAbsoluteOffset = currentOffset; this.Trie = Trie; this.UsedNamespaces = usedNamespaces; this.IsWithinLocFalse = isWithinLocFalse; this.Project = project; this.prefferedResXItem = prefferedResXItem; this.OriginalAbsoluteOffset = this.CurrentAbsoluteOffset; this.OriginalLine = this.CurrentLine; this.OriginalIndex = this.CurrentIndex; return(LookForReferences()); } }
/// <summary> /// Loads data from specified ResX file /// </summary> public override void LoadFile(string path) { base.LoadFile(path); ResXResourceReader reader = null; try { // initialize corresponding project item instance ProjectItem item = VisualLocalizerPackage.Instance.DTE.Solution.FindProjectItem(FileName); ProjectItem = ResXProjectItem.ConvertToResXItem(item, item.ContainingProject); Dictionary <string, ResXDataNode> data = new Dictionary <string, ResXDataNode>(); reader = new ResXResourceReader(path); reader.UseResXDataNodes = true; reader.BasePath = Path.GetDirectoryName(path); foreach (DictionaryEntry pair in reader) { data.Add(pair.Key.ToString(), pair.Value as ResXDataNode); } // display data in GUI UIControl.SetData(data); VLOutputWindow.VisualLocalizerPane.WriteLine("Opened file \"{0}\"", path); } catch (Exception ex) { VLOutputWindow.VisualLocalizerPane.WriteException(ex); VisualLocalizer.Library.Components.MessageBox.ShowException(ex); throw; } finally { if (reader != null) { reader.Close(); } } }
/// <summary> /// Returns list of references to resource within given block of code /// </summary> /// <param name="projectItem">Project item where code belongs</param> /// <param name="text">Text to search</param> /// <param name="startPoint">Position of the text in its source file</param> /// <param name="Trie">Trie consisting of searched references</param> /// <param name="usedNamespaces">Namespaces affecting the block of code</param> /// <param name="isWithinLocFalse">True if code block is decorated with [Localizable(false)]</param> /// <param name="project">Project in which the code belongs</param> /// <param name="prefferedResXItem"></param> public List <T> LookForReferences(ProjectItem projectItem, string text, TextPoint startPoint, Trie <CodeReferenceTrieElement> Trie, NamespacesList usedNamespaces, bool isWithinLocFalse, Project project, ResXProjectItem prefferedResXItem) { return(LookForReferences(projectItem, text, startPoint.LineCharOffset - 1, startPoint.Line, startPoint.AbsoluteCharOffset + startPoint.Line - 2, Trie, usedNamespaces, isWithinLocFalse, project, prefferedResXItem)); }
/// <summary> /// Returns list of ResX files in given project and all referenced projects /// </summary> /// <param name="project">Base project to search</param> /// <param name="includeInternal">True if ResX files with "internal" designer classes should be included in the search</param> /// <param name="includeReadonly">True if readonly files should be included in the search</param> public static List <ResXProjectItem> GetResXItemsAround(this Project project, bool includeInternal, bool includeReadonly) { if (project == null) { throw new ArgumentNullException("project"); } // get files from base project List <ProjectItem> items = project.GetFiles(ProjectEx.IsItemResX, true, includeReadonly); // convert them to ResX items List <ResXProjectItem> resxItems = new List <ResXProjectItem>(); items.ForEach((i) => { ResXProjectItem resxItem = ResXProjectItem.ConvertToResXItem(i, project); if (!resxItem.MarkedInternalInReferencedProject || includeInternal) { resxItems.Add(resxItem); } }); // sort the ResX items // - culture-neutral files first // - ResX items from base project first (others according to names) // - project-default ResX file first (Propertires/Resources.resx) // - not-dependant ResX files first (to eliminate Form.cs/Form.resx) resxItems.Sort(new Comparison <ResXProjectItem>((a, b) => { bool isAneutral = !a.IsCultureSpecific(); bool isBneutral = !b.IsCultureSpecific(); bool isAProjectDefault = a.IsProjectDefault(project); bool isBProjectDefault = b.IsProjectDefault(project); bool isADepOnAny = a.InternalProjectItem.GetIsDependent(); bool isBDepOnAny = b.InternalProjectItem.GetIsDependent(); if (isAneutral == isBneutral) { if (a.InternalProjectItem.ContainingProject == project && b.InternalProjectItem.ContainingProject == project) { if (isAProjectDefault == isBProjectDefault) { if (isADepOnAny == isBDepOnAny) { return(a.InternalProjectItem.Name.CompareTo(b.InternalProjectItem.Name)); } else { return(isBDepOnAny ? -1 : 1); } } else { return(isBProjectDefault ? 1 : -1); } } else { if (a.InternalProjectItem.ContainingProject == project) { return(-1); } else if (b.InternalProjectItem.ContainingProject == project) { return(1); } else { return(a.InternalProjectItem.Name.CompareTo(b.InternalProjectItem.Name)); } } } else { return(isBneutral ? 1 : -1); } })); // resolve namespace and class of the designer files resxItems.ForEach((item) => { item.ResolveNamespaceClass(resxItems); }); return(resxItems); }
/// <summary> /// Executed on every change in window form /// </summary> private void ValidateData() { bool existsFile = comboBox.Items.Count > 0; // there's at least one possible destination file string errorText = null; bool ok = true; if (!existsFile) // no destination file { ok = false; errorText = "Project does not contain any useable resource files"; } else { ResXProjectItem item = comboBox.SelectedItem as ResXProjectItem; if (!item.IsLoaded) { item.Load(); VLDocumentViewsManager.SetFileReadonly(item.InternalProjectItem.GetFullPath(), true); } resultItem.DestinationItem = item; bool isKeyEmpty = string.IsNullOrEmpty(keyBox.Text); bool isValidIdentifier = keyBox.Text.IsValidIdentifier(resultItem.Language); bool hasOwnDesigner = (item.DesignerItem != null || item.HasImplicitDesignerFile) && !item.IsCultureSpecific(); bool identifierErrorExists = false; // determine whether current key name is valid switch (SettingsObject.Instance.BadKeyNamePolicy) { case BAD_KEY_NAME_POLICY.IGNORE_COMPLETELY: identifierErrorExists = isKeyEmpty; // only empty keys are invalid break; case BAD_KEY_NAME_POLICY.IGNORE_ON_NO_DESIGNER: identifierErrorExists = isKeyEmpty || (!isValidIdentifier && hasOwnDesigner); // empty keys and invalid identifiers in ResX files with their own designer file break; case BAD_KEY_NAME_POLICY.WARN_ALWAYS: identifierErrorExists = isKeyEmpty || !isValidIdentifier; // empty keys and invalid identifiers break; } if (!identifierErrorExists) // identifier ok - check for key name conflicts { keyConflict = item.GetKeyConflictType(keyBox.Text, valueBox.Text, true); Color backColor = Color.White; switch (keyConflict) { case CONTAINS_KEY_RESULT.EXISTS_WITH_SAME_VALUE: // key already exists and has the same value - ok backColor = EXISTING_KEY_COLOR; break; case CONTAINS_KEY_RESULT.EXISTS_WITH_DIFF_VALUE: // key exists with different value - error errorText = "Key is already present and has different value"; existingValueBox.Text = item.GetString(keyBox.Text); backColor = ERROR_COLOR; break; case CONTAINS_KEY_RESULT.DOESNT_EXIST: // key doesn't exists - ok backColor = Color.White; break; } overwriteButton.Visible = keyConflict == CONTAINS_KEY_RESULT.EXISTS_WITH_DIFF_VALUE; inlineButton.Visible = keyConflict == CONTAINS_KEY_RESULT.EXISTS_WITH_DIFF_VALUE; existingValueBox.Visible = keyConflict == CONTAINS_KEY_RESULT.EXISTS_WITH_DIFF_VALUE; existingLabel.Visible = keyConflict == CONTAINS_KEY_RESULT.EXISTS_WITH_DIFF_VALUE; keyBox.BackColor = backColor; valueBox.BackColor = backColor; } else { errorText = "Key is not a valid identifier"; keyBox.BackColor = ERROR_COLOR; valueBox.BackColor = Color.White; } ok = !identifierErrorExists && !overwriteButton.Visible; referenceText.ClassPart = item.Class; referenceText.KeyPart = keyBox.Text; if (string.IsNullOrEmpty(item.Namespace)) // no namespace was found in designer file - error { ok = false; errorText = "Cannot reference resources in this file, missing namespace"; } else { if (!usingBox.Checked || resultItem.MustUseFullName) // force using full reference { referenceText.NamespacePart = item.Namespace; } else { referenceText.NamespacePart = null; } } referenceLabel.Text = resultItem.GetReferenceText(referenceText); } okButton.Enabled = ok; if (ok) { errorLabel.Text = string.Empty; } else { errorLabel.Text = errorText; } }
/// <summary> /// Initializes "batch move to resources" tool window and grid /// </summary> /// <param name="resxItems">List of possible destination items</param> /// <param name="items">Result items</param> /// <param name="fullName">True if "use full name" policy should be applied</param> /// <param name="mark">True if "mark with VL_NO_LOC" policy should be applied</param> /// <param name="resxCounts">Number of resources determined to be moved to each ResX file</param> /// <param name="sourceItemCounts">Number of resource items for each source code file</param> /// <param name="expectedToBeMarked">Number of resources that are expected to be marked with VL_NO_LOC</param> /// <returns></returns> private BatchMoveToResourcesToolWindow_Accessor InitBatchToolWindow(List <ResXProjectItem> resxItems, List <CodeStringResultItem> items, bool fullName, bool mark, out Dictionary <ResXProjectItem, int> resxCounts, out Dictionary <ProjectItem, int> sourceItemCounts, out int expectedToBeMarked) { DTE2 dte = Agent.GetDTE(); expectedToBeMarked = 0; sourceItemCounts = new Dictionary <ProjectItem, int>(); // init window BatchMoveToResourcesToolWindow_Accessor window = new BatchMoveToResourcesToolWindow_Accessor(new PrivateObject(new BatchMoveToResourcesToolWindow())); window.SetData(items); // init the policies window.currentNamespacePolicy = window.NAMESPACE_POLICY_ITEMS[fullName ? 1 : 0]; window.currentRememberOption = window.REMEMBER_OPTIONS[mark ? 1 : 0]; BatchMoveToResourcesToolGrid grid = ((BatchMoveToResourcesToolGrid)window.panel.ToolGrid.Target); int x = 0; Random rnd = new Random(); resxCounts = new Dictionary <ResXProjectItem, int>(); // check/uncheck random rows foreach (DataGridViewKeyValueRow <CodeStringResultItem> row in grid.Rows) { bool check = rnd.Next(2) == 0; if (check) { // set unique key row.Cells[grid.KeyColumnName].Value = string.Format("xx{0}", x); // select random destination item ResXProjectItem destResX = resxItems[rnd.Next(resxItems.Count)]; row.Cells[grid.DestinationColumnName].Value = destResX.ToString(); if (!resxCounts.ContainsKey(destResX)) { resxCounts.Add(destResX, 0); } resxCounts[destResX]++; if (!sourceItemCounts.ContainsKey(row.DataSourceItem.SourceItem)) { sourceItemCounts.Add(row.DataSourceItem.SourceItem, 0); } sourceItemCounts[row.DataSourceItem.SourceItem]++; } else { AspNetStringResultItem aitem = row.DataSourceItem as AspNetStringResultItem; if (((row.DataSourceItem is CSharpStringResultItem) || (aitem != null && aitem.ComesFromCodeBlock && aitem.Language == LANGUAGE.CSHARP))) { if (mark && !row.DataSourceItem.IsMarkedWithUnlocalizableComment) { if (!sourceItemCounts.ContainsKey(row.DataSourceItem.SourceItem)) { sourceItemCounts.Add(row.DataSourceItem.SourceItem, 0); } sourceItemCounts[row.DataSourceItem.SourceItem]++; } expectedToBeMarked++; } } row.Cells[grid.CheckBoxColumnName].Value = check; window.panel.ToolGrid.Validate(row); if (check) { Assert.IsTrue(string.IsNullOrEmpty(row.ErrorText), row.ErrorText); } x++; } // randomly sort the grid grid.Sort(grid.Columns[rnd.Next(grid.Columns.Count)], rnd.Next(2) == 0 ? System.ComponentModel.ListSortDirection.Ascending : System.ComponentModel.ListSortDirection.Descending); return(window); }
/// <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); } }
/// <summary> /// Runs this command, filling Results with references to resources in given file /// </summary> /// <param name="editorInstance">Instance of the ResX editor issuing the search</param> /// <param name="projects">List of referenced projects (are included in the search)</param> /// <param name="trie">Trie created from resource names</param> /// <param name="prefferedResXItem">Original ResX project item - used when culture-neutral vs. culture-specific differences are handled</param> /// <param name="isInitial">True if this search is the first after opening the resource file and therefore files with no code-model should be force opened</param> public void Process(ResXEditor editorInstance, List <Project> projects, Trie <CodeReferenceTrieElement> trie, ResXProjectItem prefferedResXItem, bool isInitial) { this.trie = trie; this.prefferedResXItem = prefferedResXItem; this.isInitial = isInitial; this.editorInstance = editorInstance; searchedProjectItems.Clear(); generatedProjectItems.Clear(); Results = new List <CodeReferenceResultItem>(); foreach (Project project in projects) { Process(project, false); } codeUsingsCache.Clear(); }
/// <summary> /// Returns list of references to resource within given block of code /// </summary> /// <param name="projectItem">Project item where code belongs</param> /// <param name="text">Text to search</param> /// <param name="blockSpan">Position of the text in its source file</param> /// <param name="Trie">Trie consisting of searched references</param> /// <param name="usedNamespaces">Namespaces affecting the block of code</param> /// <param name="project">Project in which the code belongs</param> /// <param name="prefferedResXItem"></param> /// <returns></returns> public List <T> LookForReferences(ProjectItem projectItem, string text, BlockSpan blockSpan, Trie <CodeReferenceTrieElement> Trie, NamespacesList usedNamespaces, Project project, ResXProjectItem prefferedResXItem) { return(LookForReferences(projectItem, text, blockSpan.StartIndex - 1, blockSpan.StartLine, blockSpan.AbsoluteCharOffset, Trie, usedNamespaces, false, project, prefferedResXItem)); }
/// <summary> /// Validates given row and updates its error messages /// </summary> protected override void Validate(DataGridViewKeyValueRow <CodeStringResultItem> row) { object dest = row.Cells[DestinationColumnName].Value; bool existsSameValue = false; if (dest == null) // no destination file was selected { row.ErrorMessages.Add(NoDestinationFileError); } else { row.ErrorMessages.Remove(NoDestinationFileError); ResXProjectItem resxItem = resxItemsCache[dest.ToString()]; if (!resxItem.IsLoaded) { resxItem.Load(); // load the ResX file VLDocumentViewsManager.SetFileReadonly(resxItem.InternalProjectItem.GetFullPath(), true); // lock it loadedItems.Add(resxItem); } string key = row.Key; string value = row.Value; CONTAINS_KEY_RESULT keyConflict = resxItem.GetKeyConflictType(key, value, true); // get conflict type switch (keyConflict) { case CONTAINS_KEY_RESULT.EXISTS_WITH_SAME_VALUE: row.ErrorMessages.Remove(DuplicateKeyError); existsSameValue = true; break; case CONTAINS_KEY_RESULT.EXISTS_WITH_DIFF_VALUE: row.ErrorMessages.Add(DuplicateKeyError); break; case CONTAINS_KEY_RESULT.DOESNT_EXIST: row.ErrorMessages.Remove(DuplicateKeyError); break; } string originalValue = (string)row.Cells[KeyColumnName].Tag; ((DestinationKeyValueConflictResolver)ConflictResolver).TryAdd(originalValue, key, row, resxItem, row.DataSourceItem.Language); if (originalValue == null) { row.Cells[KeyColumnName].Tag = key; } } row.UpdateErrorSetDisplay(); // update error messages if (row.ErrorMessages.Count == 0) { if (existsSameValue) // set background color according to conflict type { row.DefaultCellStyle.BackColor = ExistingKeySameValueColor; } else { row.DefaultCellStyle.BackColor = Color.White; } } }