2010-01-07 16:23:43 +01:00
|
|
|
/**************************************************************************
|
|
|
|
**
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
**
|
2010-03-05 11:25:49 +01:00
|
|
|
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
2010-01-07 16:23:43 +01:00
|
|
|
**
|
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
|
|
**
|
|
|
|
** Commercial Usage
|
|
|
|
**
|
|
|
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
|
|
|
** accordance with the Qt Commercial License Agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
** a written agreement between you and Nokia.
|
|
|
|
**
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
**
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
** General Public License version 2.1 as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
**
|
|
|
|
** If you are unsure which license is appropriate for your use, please
|
|
|
|
** contact the sales department at http://qt.nokia.com/contact.
|
|
|
|
**
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
This file is a self-contained interactive indenter for Qt Script.
|
|
|
|
|
|
|
|
The general problem of indenting a program is ill posed. On
|
|
|
|
the one hand, an indenter has to analyze programs written in a
|
|
|
|
free-form formal language that is best described in terms of
|
|
|
|
tokens, not characters, not lines. On the other hand, indentation
|
|
|
|
applies to lines and white space characters matter, and otherwise
|
|
|
|
the programs to indent are formally invalid in general, as they
|
|
|
|
are begin edited.
|
|
|
|
|
|
|
|
The approach taken here works line by line. We receive a program
|
|
|
|
consisting of N lines or more, and we want to compute the
|
|
|
|
indentation appropriate for the Nth line. Lines beyond the Nth
|
|
|
|
lines are of no concern to us, so for simplicity we pretend the
|
|
|
|
program has exactly N lines and we call the Nth line the "bottom
|
|
|
|
line". Typically, we have to indent the bottom line when it's
|
|
|
|
still empty, so we concentrate our analysis on the N - 1 lines
|
|
|
|
that precede.
|
|
|
|
|
|
|
|
By inspecting the (N - 1)-th line, the (N - 2)-th line, ...
|
|
|
|
backwards, we determine the kind of the bottom line and indent it
|
|
|
|
accordingly.
|
|
|
|
|
|
|
|
* The bottom line is a comment line. See
|
|
|
|
bottomLineStartsInCComment() and
|
|
|
|
indentWhenBottomLineStartsInCComment().
|
|
|
|
* The bottom line is a continuation line. See isContinuationLine()
|
|
|
|
and indentForContinuationLine().
|
|
|
|
* The bottom line is a standalone line. See
|
|
|
|
indentForStandaloneLine().
|
|
|
|
|
|
|
|
Certain tokens that influence the indentation, notably braces,
|
|
|
|
are looked for in the lines. This is done by simple string
|
|
|
|
comparison, without a real tokenizer. Confusing constructs such
|
|
|
|
as comments and string literals are removed beforehand.
|
|
|
|
*/
|
|
|
|
|
2010-01-18 16:15:23 +01:00
|
|
|
#include <qmljs/qmljsindenter.h>
|
|
|
|
#include <qmljs/qmljsscanner.h>
|
2010-01-18 14:12:04 +01:00
|
|
|
|
2010-01-08 14:55:11 +01:00
|
|
|
#include <QtDebug>
|
2010-01-07 16:23:43 +01:00
|
|
|
|
2010-01-18 14:12:04 +01:00
|
|
|
using namespace QmlJS;
|
2010-01-07 16:23:43 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
The indenter avoids getting stuck in almost infinite loops by
|
|
|
|
imposing arbitrary limits on the number of lines it analyzes when
|
|
|
|
looking for a construct.
|
|
|
|
|
|
|
|
For example, the indenter never considers more than BigRoof lines
|
|
|
|
backwards when looking for the start of a C-style comment.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
const int QmlJSIndenter::SmallRoof = 40;
|
|
|
|
const int QmlJSIndenter::BigRoof = 400;
|
2010-01-07 16:23:43 +01:00
|
|
|
|
2010-01-18 16:15:23 +01:00
|
|
|
QmlJSIndenter::QmlJSIndenter()
|
2010-01-29 15:01:49 +01:00
|
|
|
: braceX(QRegExp(QLatin1String("^\\s*\\}\\s*(?:else|catch)\\b")))
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
The indenter supports a few parameters:
|
|
|
|
|
|
|
|
* ppHardwareTabSize is the size of a '\t' in your favorite editor.
|
|
|
|
* ppIndentSize is the size of an indentation, or software tab
|
|
|
|
size.
|
|
|
|
* ppContinuationIndentSize is the extra indent for a continuation
|
|
|
|
line, when there is nothing to align against on the previous
|
|
|
|
line.
|
|
|
|
* ppCommentOffset is the indentation within a C-style comment,
|
|
|
|
when it cannot be picked up.
|
|
|
|
*/
|
|
|
|
|
|
|
|
ppHardwareTabSize = 8;
|
|
|
|
ppIndentSize = 4;
|
|
|
|
ppContinuationIndentSize = 8;
|
|
|
|
ppCommentOffset = 2;
|
|
|
|
|
|
|
|
/*
|
|
|
|
The "linizer" is a group of functions and variables to iterate
|
|
|
|
through the source code of the program to indent. The program is
|
|
|
|
given as a list of strings, with the bottom line being the line
|
|
|
|
to indent. The actual program might contain extra lines, but
|
|
|
|
those are uninteresting and not passed over to us.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// shorthands
|
|
|
|
yyLine = 0;
|
|
|
|
yyBraceDepth = 0;
|
|
|
|
yyLeftBraceFollows = 0;
|
|
|
|
}
|
|
|
|
|
2010-01-18 16:15:23 +01:00
|
|
|
QmlJSIndenter::~QmlJSIndenter()
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2010-01-18 16:15:23 +01:00
|
|
|
void QmlJSIndenter::setTabSize(int size)
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
ppHardwareTabSize = size;
|
|
|
|
}
|
|
|
|
|
2010-01-18 16:15:23 +01:00
|
|
|
void QmlJSIndenter::setIndentSize(int size)
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
ppIndentSize = size;
|
|
|
|
ppContinuationIndentSize = 2 * size;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Returns the first non-space character in the string t, or
|
|
|
|
QChar() if the string is made only of white space.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
QChar QmlJSIndenter::firstNonWhiteSpace(const QString &t) const
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
while (i < t.length()) {
|
|
|
|
if (!t.at(i).isSpace())
|
|
|
|
return t.at(i);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
return QChar();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Returns true if string t is made only of white space; otherwise
|
|
|
|
returns false.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
bool QmlJSIndenter::isOnlyWhiteSpace(const QString &t) const
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
return firstNonWhiteSpace(t).isNull();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Assuming string t is a line, returns the column number of a given
|
|
|
|
index. Column numbers and index are identical for strings that don't
|
|
|
|
contain '\t's.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
int QmlJSIndenter::columnForIndex(const QString &t, int index) const
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
int col = 0;
|
|
|
|
if (index > t.length())
|
|
|
|
index = t.length();
|
|
|
|
|
|
|
|
for (int i = 0; i < index; i++) {
|
|
|
|
if (t.at(i) == QLatin1Char('\t')) {
|
|
|
|
col = ((col / ppHardwareTabSize) + 1) * ppHardwareTabSize;
|
|
|
|
} else {
|
|
|
|
col++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return col;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Returns the indentation size of string t.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
int QmlJSIndenter::indentOfLine(const QString &t) const
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
return columnForIndex(t, t.indexOf(firstNonWhiteSpace(t)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Replaces t[k] by ch, unless t[k] is '\t'. Tab characters are better
|
|
|
|
left alone since they break the "index equals column" rule. No
|
|
|
|
provisions are taken against '\n' or '\r', which shouldn't occur in
|
|
|
|
t anyway.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
void QmlJSIndenter::eraseChar(QString &t, int k, QChar ch) const
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
if (t.at(k) != QLatin1Char('\t'))
|
|
|
|
t[k] = ch;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Removes some nefast constructs from a code line and returns the
|
|
|
|
resulting line.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
QString QmlJSIndenter::trimmedCodeLine(const QString &t)
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
2010-02-15 12:56:03 +01:00
|
|
|
Scanner scanner;
|
2010-01-07 16:23:43 +01:00
|
|
|
|
2010-01-14 15:35:00 +01:00
|
|
|
QTextBlock currentLine = yyLinizerState.iter;
|
|
|
|
int startState = qMax(0, currentLine.previous().userState()) & 0xff;
|
2010-01-07 16:23:43 +01:00
|
|
|
|
2010-01-14 15:35:00 +01:00
|
|
|
yyLinizerState.tokens = scanner(t, startState);
|
2010-01-14 12:01:51 +01:00
|
|
|
QString trimmed;
|
|
|
|
int previousTokenEnd = 0;
|
2010-01-19 12:54:17 +01:00
|
|
|
foreach (const Token &token, yyLinizerState.tokens) {
|
2010-01-14 12:01:51 +01:00
|
|
|
trimmed.append(t.midRef(previousTokenEnd, token.begin() - previousTokenEnd));
|
2010-01-07 16:23:43 +01:00
|
|
|
|
2010-01-19 12:54:17 +01:00
|
|
|
if (token.is(Token::String)) {
|
2010-01-14 12:01:51 +01:00
|
|
|
for (int i = 0; i < token.length; ++i)
|
|
|
|
trimmed.append(QLatin1Char('X'));
|
2010-01-07 16:23:43 +01:00
|
|
|
|
2010-01-19 12:54:17 +01:00
|
|
|
} else if (token.is(Token::Comment)) {
|
2010-01-15 12:54:17 +01:00
|
|
|
for (int i = 0; i < token.length; ++i)
|
2010-01-14 15:35:00 +01:00
|
|
|
trimmed.append(QLatin1Char(' '));
|
|
|
|
|
2010-01-14 12:01:51 +01:00
|
|
|
} else {
|
2010-01-19 13:57:59 +01:00
|
|
|
trimmed.append(tokenText(token));
|
2010-01-14 12:01:51 +01:00
|
|
|
}
|
2010-01-08 14:55:11 +01:00
|
|
|
|
2010-01-14 12:01:51 +01:00
|
|
|
previousTokenEnd = token.end();
|
|
|
|
}
|
2010-01-07 16:23:43 +01:00
|
|
|
|
2010-01-14 12:01:51 +01:00
|
|
|
int index = yyLinizerState.tokens.size() - 1;
|
|
|
|
for (; index != -1; --index) {
|
2010-01-19 12:54:17 +01:00
|
|
|
const Token &token = yyLinizerState.tokens.at(index);
|
|
|
|
if (token.isNot(Token::Comment))
|
2010-01-14 12:01:51 +01:00
|
|
|
break;
|
|
|
|
}
|
2010-01-07 16:23:43 +01:00
|
|
|
|
2010-01-14 12:01:51 +01:00
|
|
|
bool isBinding = false;
|
2010-01-19 12:54:17 +01:00
|
|
|
foreach (const Token &token, yyLinizerState.tokens) {
|
|
|
|
if (token.is(Token::Colon)) {
|
2010-01-14 12:01:51 +01:00
|
|
|
isBinding = true;
|
|
|
|
break;
|
2010-01-07 16:23:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-14 12:01:51 +01:00
|
|
|
if (index != -1) {
|
2010-01-19 12:54:17 +01:00
|
|
|
const Token &last = yyLinizerState.tokens.at(index);
|
2010-01-26 11:58:20 +01:00
|
|
|
bool needSemicolon = false;
|
2010-01-14 12:01:51 +01:00
|
|
|
|
|
|
|
switch (last.kind) {
|
2010-01-19 12:54:17 +01:00
|
|
|
case Token::LeftParenthesis:
|
|
|
|
case Token::LeftBrace:
|
|
|
|
case Token::Semicolon:
|
2010-01-28 13:12:52 +01:00
|
|
|
case Token::Delimiter:
|
2010-01-13 14:25:52 +01:00
|
|
|
break;
|
|
|
|
|
2010-01-19 12:54:17 +01:00
|
|
|
case Token::RightParenthesis:
|
|
|
|
case Token::RightBrace:
|
2010-01-14 12:01:51 +01:00
|
|
|
if (isBinding)
|
2010-01-26 11:58:20 +01:00
|
|
|
needSemicolon = true;
|
2010-01-14 12:01:51 +01:00
|
|
|
break;
|
2010-01-13 14:25:52 +01:00
|
|
|
|
2010-01-26 12:56:05 +01:00
|
|
|
case Token::String:
|
|
|
|
case Token::Number:
|
2010-01-19 12:54:17 +01:00
|
|
|
case Token::Colon:
|
2010-01-29 11:31:41 +01:00
|
|
|
case Token::Comma:
|
2010-01-19 12:54:17 +01:00
|
|
|
case Token::LeftBracket:
|
|
|
|
case Token::RightBracket:
|
2010-01-26 11:58:20 +01:00
|
|
|
needSemicolon = true;
|
2010-01-14 12:01:51 +01:00
|
|
|
break;
|
|
|
|
|
2010-01-19 12:54:17 +01:00
|
|
|
case Token::Identifier:
|
2010-01-29 11:31:41 +01:00
|
|
|
needSemicolon = true;
|
|
|
|
break;
|
|
|
|
|
2010-01-19 12:54:17 +01:00
|
|
|
case Token::Keyword:
|
2010-01-19 13:57:59 +01:00
|
|
|
if (tokenText(last) != QLatin1String("else"))
|
2010-01-26 11:58:20 +01:00
|
|
|
needSemicolon = true;
|
2010-01-13 14:25:52 +01:00
|
|
|
break;
|
2010-01-08 14:55:11 +01:00
|
|
|
|
2010-01-14 12:01:51 +01:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
} // end of switch
|
2010-01-26 11:58:20 +01:00
|
|
|
|
|
|
|
if (needSemicolon) {
|
|
|
|
const Token sc(trimmed.size(), 1, Token::Semicolon);
|
|
|
|
yyLinizerState.tokens.append(sc);
|
|
|
|
trimmed.append(QLatin1Char(';'));
|
|
|
|
}
|
2010-01-14 12:01:51 +01:00
|
|
|
}
|
2010-01-13 14:25:52 +01:00
|
|
|
|
2010-01-07 16:23:43 +01:00
|
|
|
return trimmed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Returns '(' if the last parenthesis is opening, ')' if it is
|
|
|
|
closing, and QChar() if there are no parentheses in t.
|
|
|
|
*/
|
2010-01-19 13:57:59 +01:00
|
|
|
QChar QmlJSIndenter::lastParen() const
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
2010-01-19 13:57:59 +01:00
|
|
|
for (int index = yyLinizerState.tokens.size() - 1; index != -1; --index) {
|
|
|
|
const Token &token = yyLinizerState.tokens.at(index);
|
|
|
|
|
|
|
|
if (token.is(Token::LeftParenthesis))
|
|
|
|
return QChar('(');
|
|
|
|
|
|
|
|
else if (token.is(Token::RightParenthesis))
|
|
|
|
return QChar(')');
|
2010-01-07 16:23:43 +01:00
|
|
|
}
|
2010-01-19 13:57:59 +01:00
|
|
|
|
2010-01-07 16:23:43 +01:00
|
|
|
return QChar();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Returns true if typedIn the same as okayCh or is null; otherwise
|
|
|
|
returns false.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
bool QmlJSIndenter::okay(QChar typedIn, QChar okayCh) const
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
return typedIn == QChar() || typedIn == okayCh;
|
|
|
|
}
|
|
|
|
|
2010-01-19 12:54:17 +01:00
|
|
|
Token QmlJSIndenter::lastToken() const
|
2010-01-19 12:50:03 +01:00
|
|
|
{
|
|
|
|
for (int index = yyLinizerState.tokens.size() - 1; index != -1; --index) {
|
2010-01-19 12:54:17 +01:00
|
|
|
const Token &token = yyLinizerState.tokens.at(index);
|
2010-01-19 12:50:03 +01:00
|
|
|
|
2010-01-19 12:54:17 +01:00
|
|
|
if (token.isNot(Token::Comment))
|
2010-01-19 12:50:03 +01:00
|
|
|
return token;
|
|
|
|
}
|
|
|
|
|
2010-01-19 12:54:17 +01:00
|
|
|
return Token();
|
2010-01-19 12:50:03 +01:00
|
|
|
}
|
|
|
|
|
2010-01-19 12:58:55 +01:00
|
|
|
QStringRef QmlJSIndenter::tokenText(const Token &token) const
|
|
|
|
{
|
|
|
|
return yyLinizerState.line.midRef(token.offset, token.length);
|
|
|
|
}
|
|
|
|
|
2010-01-07 16:23:43 +01:00
|
|
|
/*
|
|
|
|
Saves and restores the state of the global linizer. This enables
|
|
|
|
backtracking.
|
|
|
|
*/
|
|
|
|
#define YY_SAVE() LinizerState savedState = yyLinizerState
|
|
|
|
#define YY_RESTORE() yyLinizerState = savedState
|
|
|
|
|
|
|
|
/*
|
|
|
|
Advances to the previous line in yyProgram and update yyLine
|
|
|
|
accordingly. yyLine is cleaned from comments and other damageable
|
|
|
|
constructs. Empty lines are skipped.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
bool QmlJSIndenter::readLine()
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
int k;
|
|
|
|
|
|
|
|
yyLinizerState.leftBraceFollows =
|
|
|
|
(firstNonWhiteSpace(yyLinizerState.line) == QLatin1Char('{'));
|
|
|
|
|
|
|
|
do {
|
2010-01-08 14:55:11 +01:00
|
|
|
if (yyLinizerState.iter == yyProgram.firstBlock()) {
|
2010-01-07 16:23:43 +01:00
|
|
|
yyLinizerState.line.clear();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-01-08 14:55:11 +01:00
|
|
|
yyLinizerState.iter = yyLinizerState.iter.previous();
|
|
|
|
yyLinizerState.line = yyLinizerState.iter.text();
|
2010-01-07 16:23:43 +01:00
|
|
|
|
|
|
|
yyLinizerState.line = trimmedCodeLine(yyLinizerState.line);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Remove trailing spaces.
|
|
|
|
*/
|
|
|
|
k = yyLinizerState.line.length();
|
|
|
|
while (k > 0 && yyLinizerState.line.at(k - 1).isSpace())
|
|
|
|
k--;
|
|
|
|
yyLinizerState.line.truncate(k);
|
|
|
|
|
|
|
|
/*
|
|
|
|
'}' increment the brace depth and '{' decrements it and not
|
|
|
|
the other way around, as we are parsing backwards.
|
|
|
|
*/
|
|
|
|
yyLinizerState.braceDepth +=
|
|
|
|
yyLinizerState.line.count('}') -
|
|
|
|
yyLinizerState.line.count('{');
|
|
|
|
|
|
|
|
/*
|
|
|
|
We use a dirty trick for
|
|
|
|
|
|
|
|
} else ...
|
|
|
|
|
|
|
|
We don't count the '}' yet, so that it's more or less
|
|
|
|
equivalent to the friendly construct
|
|
|
|
|
|
|
|
}
|
|
|
|
else ...
|
|
|
|
*/
|
|
|
|
if (yyLinizerState.pendingRightBrace)
|
|
|
|
yyLinizerState.braceDepth++;
|
|
|
|
yyLinizerState.pendingRightBrace =
|
|
|
|
(yyLinizerState.line.indexOf(braceX) == 0);
|
|
|
|
if (yyLinizerState.pendingRightBrace)
|
|
|
|
yyLinizerState.braceDepth--;
|
|
|
|
} while (yyLinizerState.line.isEmpty());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Resets the linizer to its initial state, with yyLine containing the
|
|
|
|
line above the bottom line of the program.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
void QmlJSIndenter::startLinizer()
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
yyLinizerState.braceDepth = 0;
|
|
|
|
yyLinizerState.pendingRightBrace = false;
|
|
|
|
|
|
|
|
yyLine = &yyLinizerState.line;
|
|
|
|
yyBraceDepth = &yyLinizerState.braceDepth;
|
|
|
|
yyLeftBraceFollows = &yyLinizerState.leftBraceFollows;
|
|
|
|
|
2010-01-08 14:55:11 +01:00
|
|
|
yyLinizerState.iter = yyProgram.lastBlock();
|
|
|
|
yyLinizerState.iter = yyLinizerState.iter.previous();
|
|
|
|
yyLinizerState.line = yyLinizerState.iter.text();
|
2010-01-07 16:23:43 +01:00
|
|
|
readLine();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Returns true if the start of the bottom line of yyProgram (and
|
|
|
|
potentially the whole line) is part of a C-style comment;
|
|
|
|
otherwise returns false.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
bool QmlJSIndenter::bottomLineStartsInMultilineComment()
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
2010-01-14 15:35:00 +01:00
|
|
|
QTextBlock currentLine = yyProgram.lastBlock().previous();
|
|
|
|
QTextBlock previousLine = currentLine.previous();
|
2010-01-07 16:23:43 +01:00
|
|
|
|
2010-01-14 15:35:00 +01:00
|
|
|
int startState = qMax(0, previousLine.userState()) & 0xff;
|
|
|
|
if (startState > 0)
|
|
|
|
return true;
|
2010-01-07 16:23:43 +01:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Returns the recommended indent for the bottom line of yyProgram
|
|
|
|
assuming that it starts in a C-style comment, a condition that is
|
|
|
|
tested elsewhere.
|
|
|
|
|
|
|
|
Essentially, we're trying to align against some text on the
|
|
|
|
previous line.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
int QmlJSIndenter::indentWhenBottomLineStartsInMultiLineComment()
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
2010-01-15 11:57:04 +01:00
|
|
|
QTextBlock block = yyProgram.lastBlock().previous();
|
|
|
|
QString blockText;
|
|
|
|
|
|
|
|
for (; block.isValid(); block = block.previous()) {
|
|
|
|
blockText = block.text();
|
|
|
|
|
|
|
|
if (! isOnlyWhiteSpace(blockText))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-01-15 12:54:17 +01:00
|
|
|
return indentOfLine(blockText);
|
2010-01-07 16:23:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
A function called match...() modifies the linizer state. If it
|
|
|
|
returns true, yyLine is the top line of the matched construct;
|
|
|
|
otherwise, the linizer is left in an unknown state.
|
|
|
|
|
|
|
|
A function called is...() keeps the linizer state intact.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
Returns true if the current line (and upwards) forms a braceless
|
|
|
|
control statement; otherwise returns false.
|
|
|
|
|
|
|
|
The first line of the following example is a "braceless control
|
|
|
|
statement":
|
|
|
|
|
|
|
|
if (x)
|
|
|
|
y;
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
bool QmlJSIndenter::matchBracelessControlStatement()
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
int delimDepth = 0;
|
|
|
|
|
2010-01-18 18:31:17 +01:00
|
|
|
if (! yyLinizerState.tokens.isEmpty()) {
|
2010-01-19 12:54:17 +01:00
|
|
|
Token tk = lastToken();
|
2010-01-07 16:23:43 +01:00
|
|
|
|
2010-01-29 11:31:41 +01:00
|
|
|
if (tk.is(Token::Keyword) && tokenText(tk) == QLatin1String("else"))
|
2010-01-18 18:31:17 +01:00
|
|
|
return true;
|
|
|
|
|
2010-01-19 12:54:17 +01:00
|
|
|
else if (tk.isNot(Token::RightParenthesis))
|
2010-01-18 18:31:17 +01:00
|
|
|
return false;
|
|
|
|
}
|
2010-01-07 16:23:43 +01:00
|
|
|
|
|
|
|
for (int i = 0; i < SmallRoof; i++) {
|
2010-01-18 18:31:17 +01:00
|
|
|
for (int tokenIndex = yyLinizerState.tokens.size() - 1; tokenIndex != -1; --tokenIndex) {
|
2010-01-19 12:54:17 +01:00
|
|
|
const Token &token = yyLinizerState.tokens.at(tokenIndex);
|
2010-01-18 18:31:17 +01:00
|
|
|
|
|
|
|
switch (token.kind) {
|
|
|
|
default:
|
2010-01-07 16:23:43 +01:00
|
|
|
break;
|
2010-01-18 18:31:17 +01:00
|
|
|
|
2010-01-19 12:54:17 +01:00
|
|
|
case Token::Comment:
|
2010-01-19 12:50:03 +01:00
|
|
|
// skip comments
|
|
|
|
break;
|
|
|
|
|
2010-01-19 12:54:17 +01:00
|
|
|
case Token::RightParenthesis:
|
2010-01-18 18:31:17 +01:00
|
|
|
++delimDepth;
|
|
|
|
break;
|
|
|
|
|
2010-01-19 12:54:17 +01:00
|
|
|
case Token::LeftBrace:
|
|
|
|
case Token::RightBrace:
|
|
|
|
case Token::Semicolon:
|
2010-01-18 18:31:17 +01:00
|
|
|
/*
|
|
|
|
We met a statement separator, but not where we
|
|
|
|
expected it. What follows is probably a weird
|
|
|
|
continuation line. Be careful with ';' in for,
|
|
|
|
though.
|
|
|
|
*/
|
2010-01-19 12:54:17 +01:00
|
|
|
if (token.kind != Token::Semicolon || delimDepth == 0)
|
2010-01-18 18:31:17 +01:00
|
|
|
return false;
|
2010-01-19 13:57:59 +01:00
|
|
|
break;
|
2010-01-18 18:31:17 +01:00
|
|
|
|
|
|
|
|
2010-01-19 12:54:17 +01:00
|
|
|
case Token::LeftParenthesis:
|
2010-01-18 18:31:17 +01:00
|
|
|
--delimDepth;
|
|
|
|
|
|
|
|
if (delimDepth == 0 && tokenIndex > 0) {
|
2010-01-19 12:54:17 +01:00
|
|
|
const Token &tk = yyLinizerState.tokens.at(tokenIndex - 1);
|
2010-01-18 18:31:17 +01:00
|
|
|
|
2010-01-29 11:31:41 +01:00
|
|
|
if (tk.is(Token::Keyword)) {
|
2010-01-19 13:57:59 +01:00
|
|
|
const QStringRef text = tokenText(tk);
|
2010-01-18 18:31:17 +01:00
|
|
|
|
2010-01-07 16:23:43 +01:00
|
|
|
/*
|
|
|
|
We have
|
|
|
|
|
2010-01-18 18:31:17 +01:00
|
|
|
if-like (x)
|
2010-01-07 16:23:43 +01:00
|
|
|
y
|
|
|
|
|
|
|
|
"if (x)" is not part of the statement
|
|
|
|
"y".
|
|
|
|
*/
|
2010-01-18 18:31:17 +01:00
|
|
|
|
|
|
|
|
2010-01-19 13:57:59 +01:00
|
|
|
if (tk.length == 5 && text == QLatin1String("catch"))
|
2010-01-18 18:31:17 +01:00
|
|
|
return true;
|
|
|
|
|
2010-01-19 13:57:59 +01:00
|
|
|
else if (tk.length == 2 && text == QLatin1String("do"))
|
2010-01-18 18:31:17 +01:00
|
|
|
return true;
|
|
|
|
|
2010-01-19 13:57:59 +01:00
|
|
|
else if (tk.length == 3 && text == QLatin1String("for"))
|
2010-01-18 18:31:17 +01:00
|
|
|
return true;
|
|
|
|
|
2010-01-19 13:57:59 +01:00
|
|
|
else if (tk.length == 2 && text == QLatin1String("if"))
|
2010-01-18 18:31:17 +01:00
|
|
|
return true;
|
|
|
|
|
2010-01-19 13:57:59 +01:00
|
|
|
else if (tk.length == 5 && text == QLatin1String("while"))
|
2010-01-18 18:31:17 +01:00
|
|
|
return true;
|
|
|
|
|
2010-01-19 13:57:59 +01:00
|
|
|
else if (tk.length == 4 && text == QLatin1String("with"))
|
2010-01-18 18:31:17 +01:00
|
|
|
return true;
|
2010-01-07 16:23:43 +01:00
|
|
|
}
|
|
|
|
}
|
2010-01-18 18:31:17 +01:00
|
|
|
|
2010-01-07 16:23:43 +01:00
|
|
|
if (delimDepth == -1) {
|
|
|
|
/*
|
|
|
|
We have
|
|
|
|
|
|
|
|
if ((1 +
|
|
|
|
2)
|
|
|
|
|
|
|
|
and not
|
|
|
|
|
|
|
|
if (1 +
|
|
|
|
2)
|
|
|
|
*/
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
2010-01-18 18:31:17 +01:00
|
|
|
|
|
|
|
} // end of switch
|
2010-01-07 16:23:43 +01:00
|
|
|
}
|
|
|
|
|
2010-01-18 18:31:17 +01:00
|
|
|
if (! readLine())
|
2010-01-07 16:23:43 +01:00
|
|
|
break;
|
|
|
|
}
|
2010-01-18 18:31:17 +01:00
|
|
|
|
2010-01-07 16:23:43 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Returns true if yyLine is an unfinished line; otherwise returns
|
|
|
|
false.
|
|
|
|
|
|
|
|
In many places we'll use the terms "standalone line", "unfinished
|
|
|
|
line" and "continuation line". The meaning of these should be
|
|
|
|
evident from this code example:
|
|
|
|
|
|
|
|
a = b; // standalone line
|
|
|
|
c = d + // unfinished line
|
|
|
|
e + // unfinished continuation line
|
|
|
|
f + // unfinished continuation line
|
|
|
|
g; // continuation line
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
bool QmlJSIndenter::isUnfinishedLine()
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
bool unf = false;
|
|
|
|
|
|
|
|
YY_SAVE();
|
|
|
|
|
|
|
|
if (yyLine->isEmpty())
|
|
|
|
return false;
|
|
|
|
|
2010-01-19 13:57:59 +01:00
|
|
|
const QChar lastCh = yyLine->at(yyLine->length() - 1);
|
|
|
|
|
2010-01-14 11:01:20 +01:00
|
|
|
if (QString::fromLatin1("{};").indexOf(lastCh) == -1) {
|
2010-01-07 16:23:43 +01:00
|
|
|
/*
|
2010-01-14 11:01:20 +01:00
|
|
|
It doesn't end with ';' or similar. If it's not an "if (x)", it must be an unfinished line.
|
2010-01-07 16:23:43 +01:00
|
|
|
*/
|
2010-01-13 14:41:52 +01:00
|
|
|
unf = ! matchBracelessControlStatement();
|
2010-01-14 11:01:20 +01:00
|
|
|
|
2010-01-13 14:41:52 +01:00
|
|
|
if (unf && lastCh == QLatin1Char(')'))
|
|
|
|
unf = false;
|
|
|
|
|
2010-01-07 16:23:43 +01:00
|
|
|
} else if (lastCh == QLatin1Char(';')) {
|
2010-01-19 13:57:59 +01:00
|
|
|
if (lastParen() == QLatin1Char('(')) {
|
2010-01-07 16:23:43 +01:00
|
|
|
/*
|
|
|
|
Exception:
|
|
|
|
|
|
|
|
for (int i = 1; i < 10;
|
|
|
|
*/
|
|
|
|
unf = true;
|
|
|
|
} else if (readLine() && yyLine->endsWith(QLatin1String(";")) &&
|
2010-01-19 13:57:59 +01:00
|
|
|
lastParen() == QLatin1Char('(')) {
|
2010-01-07 16:23:43 +01:00
|
|
|
/*
|
|
|
|
Exception:
|
|
|
|
|
|
|
|
for (int i = 1;
|
|
|
|
i < 10;
|
|
|
|
*/
|
|
|
|
unf = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
YY_RESTORE();
|
|
|
|
return unf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Returns true if yyLine is a continuation line; otherwise returns
|
|
|
|
false.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
bool QmlJSIndenter::isContinuationLine()
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
bool cont = false;
|
|
|
|
|
|
|
|
YY_SAVE();
|
|
|
|
if (readLine())
|
|
|
|
cont = isUnfinishedLine();
|
|
|
|
YY_RESTORE();
|
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Returns the recommended indent for the bottom line of yyProgram,
|
|
|
|
assuming it's a continuation line.
|
|
|
|
|
|
|
|
We're trying to align the continuation line against some parenthesis
|
|
|
|
or other bracked left opened on a previous line, or some interesting
|
|
|
|
operator such as '='.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
int QmlJSIndenter::indentForContinuationLine()
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
int braceDepth = 0;
|
|
|
|
int delimDepth = 0;
|
|
|
|
|
|
|
|
bool leftBraceFollowed = *yyLeftBraceFollows;
|
|
|
|
|
|
|
|
for (int i = 0; i < SmallRoof; i++) {
|
|
|
|
int hook = -1;
|
|
|
|
|
|
|
|
int j = yyLine->length();
|
|
|
|
while (j > 0 && hook < 0) {
|
|
|
|
j--;
|
|
|
|
QChar ch = yyLine->at(j);
|
|
|
|
|
|
|
|
switch (ch.unicode()) {
|
|
|
|
case ')':
|
|
|
|
delimDepth++;
|
|
|
|
break;
|
2010-01-08 14:55:11 +01:00
|
|
|
case ']':
|
|
|
|
braceDepth++;
|
|
|
|
break;
|
2010-01-07 16:23:43 +01:00
|
|
|
case '}':
|
|
|
|
braceDepth++;
|
|
|
|
break;
|
|
|
|
case '(':
|
|
|
|
delimDepth--;
|
|
|
|
/*
|
|
|
|
An unclosed delimiter is a good place to align at,
|
|
|
|
at least for some styles (including Qt's).
|
|
|
|
*/
|
|
|
|
if (delimDepth == -1)
|
|
|
|
hook = j;
|
|
|
|
break;
|
2010-01-08 14:55:11 +01:00
|
|
|
|
|
|
|
case '[':
|
|
|
|
braceDepth--;
|
|
|
|
/*
|
|
|
|
An unclosed delimiter is a good place to align at,
|
|
|
|
at least for some styles (including Qt's).
|
|
|
|
*/
|
|
|
|
if (braceDepth == -1)
|
|
|
|
hook = j;
|
|
|
|
break;
|
2010-01-07 16:23:43 +01:00
|
|
|
case '{':
|
|
|
|
braceDepth--;
|
|
|
|
/*
|
|
|
|
A left brace followed by other stuff on the same
|
|
|
|
line is typically for an enum or an initializer.
|
|
|
|
Such a brace must be treated just like the other
|
|
|
|
delimiters.
|
|
|
|
*/
|
|
|
|
if (braceDepth == -1) {
|
|
|
|
if (j < yyLine->length() - 1) {
|
|
|
|
hook = j;
|
|
|
|
} else {
|
|
|
|
return 0; // shouldn't happen
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '=':
|
|
|
|
/*
|
|
|
|
An equal sign is a very natural alignment hook
|
|
|
|
because it's usually the operator with the lowest
|
|
|
|
precedence in statements it appears in. Case in
|
|
|
|
point:
|
|
|
|
|
|
|
|
int x = 1 +
|
|
|
|
2;
|
|
|
|
|
|
|
|
However, we have to beware of constructs such as
|
|
|
|
default arguments and explicit enum constant
|
|
|
|
values:
|
|
|
|
|
|
|
|
void foo(int x = 0,
|
|
|
|
int y = 0);
|
|
|
|
|
|
|
|
And not
|
|
|
|
|
|
|
|
void foo(int x = 0,
|
|
|
|
int y = 0);
|
|
|
|
|
|
|
|
These constructs are caracterized by a ',' at the
|
|
|
|
end of the unfinished lines or by unbalanced
|
|
|
|
parentheses.
|
|
|
|
*/
|
|
|
|
Q_ASSERT(j - 1 >= 0);
|
|
|
|
|
|
|
|
if (QString::fromLatin1("!=<>").indexOf(yyLine->at(j - 1)) == -1 &&
|
|
|
|
j + 1 < yyLine->length() && yyLine->at(j + 1) != '=') {
|
|
|
|
if (braceDepth == 0 && delimDepth == 0 &&
|
|
|
|
j < yyLine->length() - 1 &&
|
2010-01-26 12:56:05 +01:00
|
|
|
!yyLine->endsWith(QLatin1Char(',')) &&
|
|
|
|
(yyLine->contains(QLatin1Char('(')) == yyLine->contains(QLatin1Char(')'))))
|
2010-01-07 16:23:43 +01:00
|
|
|
hook = j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hook >= 0) {
|
|
|
|
/*
|
|
|
|
Yes, we have a delimiter or an operator to align
|
|
|
|
against! We don't really align against it, but rather
|
|
|
|
against the following token, if any. In this example,
|
|
|
|
the following token is "11":
|
|
|
|
|
|
|
|
int x = (11 +
|
|
|
|
2);
|
|
|
|
|
|
|
|
If there is no such token, we use a continuation indent:
|
|
|
|
|
|
|
|
static QRegExp foo(QString(
|
|
|
|
"foo foo foo foo foo foo foo foo foo"));
|
|
|
|
*/
|
|
|
|
hook++;
|
|
|
|
while (hook < yyLine->length()) {
|
|
|
|
if (!yyLine->at(hook).isSpace())
|
|
|
|
return columnForIndex(*yyLine, hook);
|
|
|
|
hook++;
|
|
|
|
}
|
|
|
|
return indentOfLine(*yyLine) + ppContinuationIndentSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (braceDepth != 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
The line's delimiters are balanced. It looks like a
|
|
|
|
continuation line or something.
|
|
|
|
*/
|
|
|
|
if (delimDepth == 0) {
|
|
|
|
if (leftBraceFollowed) {
|
|
|
|
/*
|
|
|
|
We have
|
|
|
|
|
|
|
|
int main()
|
|
|
|
{
|
|
|
|
|
|
|
|
or
|
|
|
|
|
|
|
|
Bar::Bar()
|
|
|
|
: Foo(x)
|
|
|
|
{
|
|
|
|
|
|
|
|
The "{" should be flush left.
|
|
|
|
*/
|
|
|
|
if (!isContinuationLine())
|
|
|
|
return indentOfLine(*yyLine);
|
|
|
|
} else if (isContinuationLine() || yyLine->endsWith(QLatin1String(","))) {
|
|
|
|
/*
|
|
|
|
We have
|
|
|
|
|
|
|
|
x = a +
|
|
|
|
b +
|
|
|
|
c;
|
|
|
|
|
|
|
|
or
|
|
|
|
|
|
|
|
int t[] = {
|
|
|
|
1, 2, 3,
|
|
|
|
4, 5, 6
|
|
|
|
|
|
|
|
The "c;" should fall right under the "b +", and the
|
|
|
|
"4, 5, 6" right under the "1, 2, 3,".
|
|
|
|
*/
|
|
|
|
return indentOfLine(*yyLine);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
We have
|
|
|
|
|
|
|
|
stream << 1 +
|
|
|
|
2;
|
|
|
|
|
|
|
|
We could, but we don't, try to analyze which
|
|
|
|
operator has precedence over which and so on, to
|
|
|
|
obtain the excellent result
|
|
|
|
|
|
|
|
stream << 1 +
|
|
|
|
2;
|
|
|
|
|
|
|
|
We do have a special trick above for the assignment
|
|
|
|
operator above, though.
|
|
|
|
*/
|
|
|
|
return indentOfLine(*yyLine) + ppContinuationIndentSize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!readLine())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Returns the recommended indent for the bottom line of yyProgram if
|
|
|
|
that line is standalone (or should be indented likewise).
|
|
|
|
|
|
|
|
Indenting a standalone line is tricky, mostly because of braceless
|
|
|
|
control statements. Grossly, we are looking backwards for a special
|
|
|
|
line, a "hook line", that we can use as a starting point to indent,
|
|
|
|
and then modify the indentation level according to the braces met
|
|
|
|
along the way to that hook.
|
|
|
|
|
|
|
|
Let's consider a few examples. In all cases, we want to indent the
|
|
|
|
bottom line.
|
|
|
|
|
|
|
|
Example 1:
|
|
|
|
|
|
|
|
x = 1;
|
|
|
|
y = 2;
|
|
|
|
|
|
|
|
The hook line is "x = 1;". We met 0 opening braces and 0 closing
|
|
|
|
braces. Therefore, "y = 2;" inherits the indent of "x = 1;".
|
|
|
|
|
|
|
|
Example 2:
|
|
|
|
|
|
|
|
if (x) {
|
|
|
|
y;
|
|
|
|
|
|
|
|
The hook line is "if (x) {". No matter what precedes it, "y;" has
|
|
|
|
to be indented one level deeper than the hook line, since we met one
|
|
|
|
opening brace along the way.
|
|
|
|
|
|
|
|
Example 3:
|
|
|
|
|
|
|
|
if (a)
|
|
|
|
while (b) {
|
|
|
|
c;
|
|
|
|
}
|
|
|
|
d;
|
|
|
|
|
|
|
|
To indent "d;" correctly, we have to go as far as the "if (a)".
|
|
|
|
Compare with
|
|
|
|
|
|
|
|
if (a) {
|
|
|
|
while (b) {
|
|
|
|
c;
|
|
|
|
}
|
|
|
|
d;
|
|
|
|
|
|
|
|
Still, we're striving to go back as little as possible to
|
|
|
|
accommodate people with irregular indentation schemes. A hook line
|
|
|
|
near at hand is much more reliable than a remote one.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
int QmlJSIndenter::indentForStandaloneLine()
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
for (int i = 0; i < SmallRoof; i++) {
|
|
|
|
if (!*yyLeftBraceFollows) {
|
|
|
|
YY_SAVE();
|
|
|
|
|
|
|
|
if (matchBracelessControlStatement()) {
|
|
|
|
/*
|
|
|
|
The situation is this, and we want to indent "z;":
|
|
|
|
|
|
|
|
if (x &&
|
|
|
|
y)
|
|
|
|
z;
|
|
|
|
|
|
|
|
yyLine is "if (x &&".
|
|
|
|
*/
|
|
|
|
return indentOfLine(*yyLine) + ppIndentSize;
|
|
|
|
}
|
|
|
|
YY_RESTORE();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (yyLine->endsWith(QLatin1Char(';')) || yyLine->contains(QLatin1Char('{'))) {
|
|
|
|
/*
|
|
|
|
The situation is possibly this, and we want to indent
|
|
|
|
"z;":
|
|
|
|
|
|
|
|
while (x)
|
|
|
|
y;
|
|
|
|
z;
|
|
|
|
|
|
|
|
We return the indent of "while (x)". In place of "y;",
|
|
|
|
any arbitrarily complex compound statement can appear.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (*yyBraceDepth > 0) {
|
|
|
|
do {
|
|
|
|
if (!readLine())
|
|
|
|
break;
|
|
|
|
} while (*yyBraceDepth > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
LinizerState hookState;
|
|
|
|
|
|
|
|
while (isContinuationLine())
|
|
|
|
readLine();
|
|
|
|
hookState = yyLinizerState;
|
|
|
|
|
|
|
|
readLine();
|
|
|
|
if (*yyBraceDepth <= 0) {
|
|
|
|
do {
|
|
|
|
if (!matchBracelessControlStatement())
|
|
|
|
break;
|
|
|
|
hookState = yyLinizerState;
|
|
|
|
} while (readLine());
|
|
|
|
}
|
|
|
|
|
|
|
|
yyLinizerState = hookState;
|
|
|
|
|
|
|
|
while (isContinuationLine())
|
|
|
|
readLine();
|
|
|
|
|
|
|
|
/*
|
|
|
|
Never trust lines containing only '{' or '}', as some
|
|
|
|
people (Richard M. Stallman) format them weirdly.
|
|
|
|
*/
|
|
|
|
if (yyLine->trimmed().length() > 1)
|
|
|
|
return indentOfLine(*yyLine) - *yyBraceDepth * ppIndentSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!readLine())
|
|
|
|
return -*yyBraceDepth * ppIndentSize;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Returns the recommended indent for the bottom line of program.
|
|
|
|
Unless null, typedIn stores the character of yyProgram that
|
|
|
|
triggered reindentation.
|
|
|
|
|
|
|
|
This function works better if typedIn is set properly; it is
|
|
|
|
slightly more conservative if typedIn is completely wild, and
|
|
|
|
slighly more liberal if typedIn is always null. The user might be
|
|
|
|
annoyed by the liberal behavior.
|
|
|
|
*/
|
2010-01-18 16:15:23 +01:00
|
|
|
int QmlJSIndenter::indentForBottomLine(QTextBlock begin, QTextBlock end, QChar typedIn)
|
2010-01-07 16:23:43 +01:00
|
|
|
{
|
|
|
|
if (begin == end)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
yyProgram = Program(begin, end);
|
|
|
|
startLinizer();
|
|
|
|
|
2010-01-08 14:55:11 +01:00
|
|
|
const QTextBlock last = end.previous();
|
2010-01-07 16:23:43 +01:00
|
|
|
|
2010-01-08 14:55:11 +01:00
|
|
|
QString bottomLine = last.text();
|
2010-01-07 16:23:43 +01:00
|
|
|
QChar firstCh = firstNonWhiteSpace(bottomLine);
|
2010-01-08 14:55:11 +01:00
|
|
|
int indent = 0;
|
2010-01-07 16:23:43 +01:00
|
|
|
|
2010-01-14 15:35:00 +01:00
|
|
|
if (bottomLineStartsInMultilineComment()) {
|
2010-01-07 16:23:43 +01:00
|
|
|
/*
|
|
|
|
The bottom line starts in a C-style comment. Indent it
|
|
|
|
smartly, unless the user has already played around with it,
|
|
|
|
in which case it's better to leave her stuff alone.
|
|
|
|
*/
|
|
|
|
if (isOnlyWhiteSpace(bottomLine)) {
|
2010-01-14 15:35:00 +01:00
|
|
|
indent = indentWhenBottomLineStartsInMultiLineComment();
|
2010-01-07 16:23:43 +01:00
|
|
|
} else {
|
|
|
|
indent = indentOfLine(bottomLine);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (isUnfinishedLine()) {
|
|
|
|
indent = indentForContinuationLine();
|
|
|
|
} else {
|
|
|
|
indent = indentForStandaloneLine();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (okay(typedIn, QLatin1Char('}')) && firstCh == QLatin1Char('}')) {
|
|
|
|
/*
|
|
|
|
A closing brace is one level more to the left than the
|
|
|
|
code it follows.
|
|
|
|
*/
|
|
|
|
indent -= ppIndentSize;
|
|
|
|
} else if (okay(typedIn, QLatin1Char(':'))) {
|
|
|
|
QRegExp caseLabel(
|
2010-01-08 14:55:11 +01:00
|
|
|
QLatin1String("\\s*(?:case\\b(?:[^:]|::)+"
|
|
|
|
"|(?:default)\\s*"
|
|
|
|
")?:.*"));
|
2010-01-07 16:23:43 +01:00
|
|
|
|
|
|
|
if (caseLabel.exactMatch(bottomLine)) {
|
|
|
|
/*
|
|
|
|
Move a case label (or the ':' in front of a
|
|
|
|
constructor initialization list) one level to the
|
|
|
|
left, but only if the user did not play around with
|
|
|
|
it yet. Some users have exotic tastes in the
|
|
|
|
matter, and most users probably are not patient
|
|
|
|
enough to wait for the final ':' to format their
|
|
|
|
code properly.
|
|
|
|
|
|
|
|
We don't attempt the same for goto labels, as the
|
|
|
|
user is probably the middle of "foo::bar". (Who
|
|
|
|
uses goto, anyway?)
|
|
|
|
*/
|
|
|
|
if (indentOfLine(bottomLine) <= indent)
|
|
|
|
indent -= ppIndentSize;
|
|
|
|
else
|
|
|
|
indent = indentOfLine(bottomLine);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return qMax(0, indent);
|
|
|
|
}
|
2010-01-19 12:50:03 +01:00
|
|
|
|