/// <summary> /// Takes a configuration and a description of a using directive list, and /// produces two things: a list containing just the directives (with the /// items representing blank lines removed), and a description of the /// correct order and grouping for these items for the given configuration. /// </summary> /// <param name="configuration">The configuration that will determine the /// correct order and grouping.</param> /// <param name="items">A list of using directives and the blank lines /// interspersed therein. (To simplify processing, this may be null to /// represent the absence of any using directives.)</param> /// <param name="imports">The 'flattened' list (just the using directives, /// with any blank lines stripped out) will be written to this argument, /// unless <c>items</c> is null, in which case this will be set to null.</param> /// <param name="requiredOrderByGroups">The correct ordering and spacing /// for the using directives (as determined by the configuration) will be /// written to this argument (unless <c>items</c> is null, in which case /// this will be set to null).</param> /// <remarks> /// The correct order and spacing is represented as a list of lists. Each /// nested list represents a group of usings, where each group should be /// separated by a blank line. /// </remarks> public static void FlattenImportsAndDetermineOrderAndSpacing( OrderUsingsConfiguration configuration, List<UsingDirectiveOrSpace> items, out List<UsingDirective> imports, out List<List<UsingDirective>> requiredOrderByGroups) { imports = null; requiredOrderByGroups = null; if (items != null) { imports = items .Where(i => !i.IsBlankLine) .Select(i => i.Directive) .ToList(); requiredOrderByGroups = OrderAndSpacingGenerator.DetermineOrderAndSpacing(imports, configuration); } }
/// <summary> /// Fixes the order of the using directives in a given file or namespace block /// to match the specified configuration. /// </summary> /// <param name="holder">The file or namespace block in which to fix the order /// of using directives (if any are present).</param> /// <param name="configuration">The configuration determining the correct order.</param> public static void FixOrder( ICSharpTypeAndNamespaceHolderDeclaration holder, OrderUsingsConfiguration configuration) { // The reordering proceeds one item at a time, so we just keep reapplying it // until there's nothing left to do. // To avoid hanging VS in the event that an error in the logic causes the // sequence of modifications not to terminate, we ensure we don't try to // apply more changes than there are using directives. int tries = 0; int itemCount = 0; while (tries == 0 || tries <= itemCount) { List<UsingDirectiveOrSpace> items = ImportReader.ReadImports(holder); List<UsingDirective> imports; List<List<UsingDirective>> requiredOrderByGroups; ImportInspector.FlattenImportsAndDetermineOrderAndSpacing( configuration, items, out imports, out requiredOrderByGroups); if (requiredOrderByGroups == null) { break; } itemCount = imports.Count; Relocation nextChange = ImportInspector.GetNextUsingToMove(requiredOrderByGroups, imports); if (nextChange != null) { IUsingDirective toMove = holder.Imports[nextChange.From]; IUsingDirective before = holder.Imports[nextChange.To]; holder.RemoveImport(toMove); holder.AddImportBefore(toMove, before); tries += 1; } else { break; } } }
/// <summary> /// Initializes a <see cref="UsingOrderHighlighting"/>. /// </summary> /// <param name="typeAndNamespaceHolder">The file or namespace block that contains /// the import list being highlighted.</param> /// <param name="config">The configuration that was active when we determined that /// the import list does not match the requirements.</param> internal UsingOrderHighlighting( ICSharpTypeAndNamespaceHolderDeclaration typeAndNamespaceHolder, OrderUsingsConfiguration config) : base(typeAndNamespaceHolder, config) { }
/// <summary> /// Initializes a <see cref="OrderUsingsDaemonStageProcess"/>. /// </summary> /// <param name="process">The process object supplied by R# for this work.</param> /// <param name="file">The file to process.</param> /// <param name="config">The order and spacing configuration to use.</param> public OrderUsingsDaemonStageProcess(IDaemonProcess process, ICSharpFile file, OrderUsingsConfiguration config) { _file = file; _config = config; DaemonProcess = process; }
/// <summary> /// Fixes the spacing of the using directives in a given file or namespace block /// to match the specified configuration. (The directives must already be in /// the correct order.) /// </summary> /// <param name="holder">The file or namespace block in which to fix the spacing /// of using directives (if any are present).</param> /// <param name="configuration">The configuration determining the correct spacing.</param> public static void FixSpacing( ICSharpTypeAndNamespaceHolderDeclaration holder, OrderUsingsConfiguration configuration) { // The reordering proceeds one item at a time, so we just keep reapplying it // until there's nothing left to do. // To avoid hanging VS in the event that an error in the logic causes the // sequence of modifications not to terminate, we ensure we don't try to // apply more changes than there are either using directives or blank // lines in the usings list. int tries = 0; int itemCount = 0; while (tries == 0 || tries <= itemCount) { List<UsingDirectiveOrSpace> items = ImportReader.ReadImports(holder); if (items == null) { return; } itemCount = items.Count; List<UsingDirective> imports; List<List<UsingDirective>> requiredOrderByGroups; ImportInspector.FlattenImportsAndDetermineOrderAndSpacing( configuration, items, out imports, out requiredOrderByGroups); SpaceChange nextChange = ImportInspector.GetNextSpacingModification(requiredOrderByGroups, items); if (nextChange != null) { IUsingDirective usingBeforeSpace = holder.Imports[nextChange.Index - 1]; if (nextChange.ShouldInsert) { using (WriteLockCookie.Create()) { var newLineText = new StringBuffer("\r\n"); LeafElementBase newLine = TreeElementFactory.CreateLeafElement( CSharpTokenType.NEW_LINE, newLineText, 0, newLineText.Length); LowLevelModificationUtil.AddChildAfter(usingBeforeSpace, newLine); } } else { var syb = usingBeforeSpace.NextSibling; for (; syb != null && !(syb is IUsingDirective); syb = syb.NextSibling) { if (syb.NodeType == CSharpTokenType.NEW_LINE) { LowLevelModificationUtil.DeleteChild(syb); } } } } else { break; } tries += 1; } }
/// <summary> /// Initializes a <see cref="BaseHighlighting"/>. /// </summary> /// <param name="typeAndNamespaceHolder">The file or namespace block that contains /// the import list being highlighted.</param> /// <param name="config">The configuration that was active when we determined that /// the import list does not match the requirements.</param> internal BaseHighlighting( ICSharpTypeAndNamespaceHolderDeclaration typeAndNamespaceHolder, OrderUsingsConfiguration config) { _config = config; _typeAndNamespaceHolder = typeAndNamespaceHolder; }
/// <summary> /// Recursively walk namespace declaration blocks, cleaning any import lists they /// contain. /// </summary> /// <param name="namespaceDeclarationNodes">The namespace declaration blocks /// to check.</param> /// <param name="configuration">The configuration defining the correct order /// and spacing.</param> private void WalkNamespaceDeclarations( TreeNodeCollection<ICSharpNamespaceDeclaration> namespaceDeclarationNodes, OrderUsingsConfiguration configuration) { foreach (var ns in namespaceDeclarationNodes) { CleanUsings(ns, configuration); WalkNamespaceDeclarations(ns.NamespaceDeclarations, configuration); } }
/// <summary> /// Fixes the order and spacing for the using blocks in a file or namespace declaration block. /// </summary> /// <param name="holder">The file or namespace declaration blocks to check.</param> /// <param name="configuration">The configuration defining the correct order /// and spacing.</param> private void CleanUsings( ICSharpTypeAndNamespaceHolderDeclaration holder, OrderUsingsConfiguration configuration) { Fixes.FixOrder(holder, configuration); Fixes.FixSpacing(holder, configuration); }