public AbsoluteCodeLine(string code, IIndenterSettings settings, AbsoluteCodeLine previous) { _settings = settings; Previous = previous; if (code.EndsWith(StupidLineEnding)) { _code = code.Substring(0, code.Length - StupidLineEnding.Length); _stupidLineEnding = true; } else { _code = code; } Original = code; _escaper = new StringLiteralAndBracketEscaper(_code); _code = _escaper.EscapedString; ExtractLineNumber(); ExtractEndOfLineComment(); _segments = _code.Split(new[] { ": " }, StringSplitOptions.None); }
//The splitNamed parameter is a straight up hack for fixing https://github.com/rubberduck-vba/Rubberduck/issues/2402 private int FunctionAlign(string line, bool splitNamed) { line = new StringLiteralAndBracketEscaper(line).EscapedString; var stackPos = _alignment.Count; for (var index = StartIgnoreRegex.Match(line).Length + 1; index <= line.Length; index++) { var character = line.Substring(index - 1, 1); switch (character) { case "\a": case "\x2": break; case "(": //Start of another function => remember this position _alignment.Push(new AlignmentToken(AlignmentTokenType.Function, index, ++_nestingDepth)); _alignment.Push(new AlignmentToken(AlignmentTokenType.Parameter, index + 1, _nestingDepth)); break; case ")": //Function finished => Remove back to the previous open bracket while (_alignment.Any()) { var finished = _alignment.Count == stackPos + 1; var token = _alignment.Pop(); if (token.Type == AlignmentTokenType.Function && token.NestingDepth == _nestingDepth - 1) { _alignment.Push(token); finished = true; } if (finished) { _nestingDepth--; break; } } break; case " ": if (index + 3 < line.Length && line.Substring(index - 1, 3).Equals(" = ")) { //Space before an = sign => remember it to align to later if (!_alignment.Any(a => a.Type == AlignmentTokenType.Equals || a.Type == AlignmentTokenType.Variable)) { _alignment.Push(new AlignmentToken(AlignmentTokenType.Equals, index + 2, _nestingDepth)); } } else if (!_alignment.Any() && index < line.Length - 2) { //Space after a name before the end of the line => remember it for later _alignment.Push(new AlignmentToken(AlignmentTokenType.Variable, index, _nestingDepth)); } else if (index > 5 && line.Substring(index - 6, 6).Equals(" Then ")) { //Clear the collection if we find a Then in an If...Then and set the //indenting to align with the bit after the "If " while (_alignment.Count > 1) { _alignment.Pop(); } } break; case ",": //Start of a new parameter => remember it to align to _alignment.Push(new AlignmentToken(AlignmentTokenType.Parameter, index + 1, _nestingDepth)); break; case ":": if (line.Substring(index - 1, 2).Equals(":=")) { //A named paremeter => remember to align to after the name _alignment.Push(new AlignmentToken(AlignmentTokenType.Parameter, index + 3, _nestingDepth)); } else if (line.Substring(index, 2).Equals(": ")) { //A new line section, so clear the brackets _alignment.Clear(); index++; } break; } } //If we end with a comma or a named parameter, get rid of all other comma alignments if (line.EndsWith(", _") || line.EndsWith(":= _") || splitNamed) { while (_alignment.Any() && _alignment.Peek().Type == AlignmentTokenType.Parameter) { _alignment.Pop(); } } else if (line.EndsWith("( _")) //If we end with a "( _", remove it and the space alignment after it' { _alignment.Pop(); _alignment.Pop(); } var output = 0; var fallback = 0; //Get the position of the unmatched bracket and align to that foreach (var align in _alignment.Reverse()) { switch (align.Type) { case AlignmentTokenType.Parameter: output = align.Position - 1; break; case AlignmentTokenType.Function: case AlignmentTokenType.Equals: output = align.Position; break; default: fallback = align.Position - 1; break; } } if (fallback == 0 || fallback >= line.Length - 1) { fallback = !_alignment.Any() ? (_settings.IndentSpaces * 2) : 0; } else { fallback = fallback + 1; } return(output > 0 ? output : fallback); }