public static CodeBlock CalculateIndentation(string code, int offset) { if (offset >= code.Length) { offset = code.Length - 1; } CodeBlock block = null; var parserEndLocation = DocumentHelper.OffsetToLocation(code, offset); var lex = new Lexer(new StringReader(code)); lex.NextToken(); DToken t = null; DToken la = null; bool isTheoreticEOF = false; while (!lex.IsEOF) { lex.NextToken(); t = lex.CurrentToken; la = lex.LookAhead; if (la.line == 4) { } isTheoreticEOF = la.Location >= parserEndLocation; // Ensure one token after the caret offset becomes parsed if (t != null && t.Location >= parserEndLocation) { break; } // Handle case: or default: occurences if (t != null && (t.Kind == DTokens.Case || t.Kind == DTokens.Default)) { if (block != null && block.IsNonClampBlock) { block = block.Parent; } // On e.g. case myEnum.A: if (la.Kind != DTokens.Colon) { // To prevent further issues, skip the expression var psr = new DParser(lex); psr.AssignExpression(); // FIXME: What if cursor is somewhere between case and ':'?? } // lex.LookAhead should be ':' now if (lex.LookAhead.EndLocation >= parserEndLocation) { break; } else { block = new CodeBlock { InitialToken = DTokens.Case, StartLocation = t.EndLocation, Parent = block } }; } // If in a single-statement scope, unindent by 1 if semicolon found /* * Note: On multiple single-sub-statemented statements: for instance * if(..) * if(...) * if(...) * foo(); * // No indentation anymore! */ else if (block != null && (block.IsSingleLineIndentation) && la.Kind == DTokens.Semicolon) { block = block.Parent; } // New block is opened by (,[,{ else if ( !isTheoreticEOF && (la.Kind == DTokens.OpenParenthesis || la.Kind == DTokens.OpenSquareBracket || la.Kind == DTokens.OpenCurlyBrace)) { block = new CodeBlock { LastPreBlockIdentifier = t, InitialToken = la.Kind, StartLocation = la.Location, Parent = block }; } // Open block is closed by ),],} else if (block != null && ( la.Kind == DTokens.CloseParenthesis || la.Kind == DTokens.CloseSquareBracket || la.Kind == DTokens.CloseCurlyBrace)) { // If EOF reached, only 'decrement' indentation if code line consists of the closing bracket only // --> Return immediately if there's been another token on the same line if (isTheoreticEOF && t.line == la.line) { return(block); } // Statements that contain only one sub-statement -> indent by 1 if (((block.InitialToken == DTokens.OpenParenthesis && la.Kind == DTokens.CloseParenthesis && IsPreStatementToken(block.LastPreBlockIdentifier.Kind)) || la.Kind == DTokens.Do) // 'Do'-Statements allow single statements inside && lex.Peek().Kind != DTokens.OpenCurlyBrace /* Ensure that no block statement follows */) { block = new CodeBlock { LastPreBlockIdentifier = t, IsSingleLineIndentation = true, StartLocation = t.Location, Parent = block.Parent }; } /* * Do unindent if the watched code is NOT about to end OR if * the next token is a '}' (which normally means the end of a class body/block statement etc.) * AND if no line-break was entered (so unindent the finalizing '}' but not the block's statements) */ else if (!isTheoreticEOF || (la.Kind == DTokens.CloseCurlyBrace && la.line == parserEndLocation.Line)) { /* * On "case:" or "default:" blocks (so mostly in switch blocks), * skip back to the 'switch' scope. * * switch(..) * { * default: * case ..: * // There is indentation now! * // (On following lines, case blocks are still active) * } // Decreased Indentation; case blocks discarded */ if (la.Kind == DTokens.CloseCurlyBrace) { while (block != null && block.IsNonClampBlock) { block = block.Parent; } } if (block != null) { block = block.Parent; } } } } return(block); }
public CodeBlock CalculateIndentation(TextReader code, int line) { block = null; Lexer = new Lexer(code); maxLine = line; Lexer.NextToken(); DToken lastToken = null; while (!Lexer.IsEOF) { if (t != null && la.Line > t.Line && t.Line < maxLine) { RemoveNextLineUnindentBlocks(); } lastToken = t; Lexer.NextToken(); if (IsEOF) { if (la.Line > maxLine || Lexer.IsEOF) { lastLineIndent = null; } if (t.Line > maxLine) { break; } } /* * if(..) * for(...) * while(...) * foo(); * // No indentation anymore! */ if (t.Kind == DTokens.Comma || t.Kind == DTokens.Semicolon && maxLine > t.Line && la.Line > t.Line) { if (block == null) { continue; } if (block.Reason == CodeBlock.IndentReason.UnfinishedStatement) { PopBlock(); } while ( block != null && block.Reason == CodeBlock.IndentReason.SingleLineStatement && !IsSemicolonContainingStatement) { PopBlock(); } } // (,[,{ else if (t.Kind == DTokens.OpenParenthesis || t.Kind == DTokens.OpenSquareBracket || t.Kind == DTokens.OpenCurlyBrace) { var tBlock = block; if (block != null && ( block.Reason == CodeBlock.IndentReason.SingleLineStatement || block.Reason == CodeBlock.IndentReason.UnfinishedStatement)) { PopBlock(); } PushBlock(tBlock).BlockStartToken = t.Kind; } // ),],} else if (t.Kind == DTokens.CloseParenthesis || t.Kind == DTokens.CloseSquareBracket || t.Kind == DTokens.CloseCurlyBrace) { if (t.Kind == DTokens.CloseCurlyBrace) { while (block != null && !block.IsClampBlock) { PopBlock(); } /* * If the last token was on this line OR if it's eof but on the following line, * decrement indent on next line only. */ if (lastToken != null && lastToken.Line == t.Line && block != null) { block.PopOnNextLine = true; } else { PopBlock(); } } else { while (block != null && !block.IsClampBlock) { PopBlock(); } if (lastLineIndent == null && (block == null || block.StartLocation.Line < t.Line)) { lastLineIndent = block; } if (t.Kind == DTokens.CloseParenthesis && block != null && block.BlockStartToken == DTokens.OpenParenthesis && la.Kind != DTokens.OpenCurlyBrace) { block = block.previousBlock; continue; } else { PopBlock(); } if (t.Kind == DTokens.CloseParenthesis && block != null && block.BlockStartToken == DTokens.OpenParenthesis) { if (la.Kind == DTokens.OpenCurlyBrace && la.Line > t.Line) { PopBlock(); } else if (block != null && block.LastPreBlockIdentifier != null && IsPreStatementToken(block.LastPreBlockIdentifier.Kind)) { block = block.previousBlock; } } } } else if ((DParser.IsAttributeSpecifier(t.Kind, la.Kind) && la.Kind == DTokens.Colon) || t.Kind == DTokens.Case || t.Kind == DTokens.Default) { while (block != null && block.BlockStartToken != DTokens.OpenCurlyBrace) { PopBlock(); } PushBlock().Reason = CodeBlock.IndentReason.StatementLabel; HadCaseStatementBegin = true; } else if (t.Kind == DTokens.Colon) { if (HadCaseStatementBegin) { while (block != null && block.Reason != CodeBlock.IndentReason.StatementLabel) { PopBlock(); } HadCaseStatementBegin = false; } } // Don't indent these in front of function bodies else if (t.Kind == DTokens.In || t.Kind == DTokens.Out || t.Kind == DTokens.Body) { if (block != null && block.Reason == CodeBlock.IndentReason.UnfinishedStatement) { PopBlock(); } } else if (block == null || block.Reason != CodeBlock.IndentReason.UnfinishedStatement && block.Reason != CodeBlock.IndentReason.SingleLineStatement) { PushBlock().Reason = CodeBlock.IndentReason.UnfinishedStatement; } } if (t != null && la.Line > t.Line) { RemoveNextLineUnindentBlocks(); } return(lastLineIndent ?? block); }