1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmldomindentinglinewriter_p.h"
5
6#include <QtCore/QCoreApplication>
7#include <QtCore/QRegularExpression>
8
9QT_BEGIN_NAMESPACE
10namespace QQmlJS {
11namespace Dom {
12
13FormatPartialStatus &IndentingLineWriter::fStatus()
14{
15 if (!m_fStatusValid) {
16 m_fStatus = formatCodeLine(line: m_currentLine, options: m_options.formatOptions, initialStatus: m_preCachedStatus);
17 m_fStatusValid = true;
18 }
19 return m_fStatus;
20}
21
22void IndentingLineWriter::willCommit()
23{
24 m_preCachedStatus = fStatus().currentStatus;
25}
26
27void IndentingLineWriter::reindentAndSplit(QString eol, bool eof)
28{
29 bool shouldReindent = m_reindent;
30indentAgain:
31 // maybe re-indent
32 if (shouldReindent && m_columnNr == 0) {
33 setLineIndent(fStatus().indentLine());
34 }
35 if (!eol.isEmpty() || eof) {
36 LineWriterOptions::TrailingSpace trailingSpace;
37 if (!m_currentLine.isEmpty() && m_currentLine.trimmed().isEmpty()) {
38 // space only line
39 const Scanner::State &oldState = m_preCachedStatus.lexerState;
40 if (oldState.isMultilineComment())
41 trailingSpace = m_options.commentTrailingSpace;
42 else if (oldState.isMultiline())
43 trailingSpace = m_options.stringTrailingSpace;
44 else
45 trailingSpace = m_options.codeTrailingSpace;
46 // in the LSP we will probably want to treat is specially if it is the line with the
47 // cursor, of if indentation of it is requested
48 } else {
49 const Scanner::State &currentState = fStatus().currentStatus.lexerState;
50 if (currentState.isMultilineComment()) {
51 trailingSpace = m_options.commentTrailingSpace;
52 } else if (currentState.isMultiline()) {
53 trailingSpace = m_options.stringTrailingSpace;
54 } else {
55 const int kind =
56 (fStatus().lineTokens.isEmpty() ? Lexer::T_EOL
57 : fStatus().lineTokens.last().lexKind);
58 if (Token::lexKindIsComment(kind)) {
59 // a // comment...
60 trailingSpace = m_options.commentTrailingSpace;
61 Q_ASSERT(fStatus().currentStatus.state().type
62 != FormatTextStatus::StateType::MultilineCommentCont
63 && fStatus().currentStatus.state().type
64 != FormatTextStatus::StateType::
65 MultilineCommentStart); // these should have been
66 // handled above
67 } else {
68 trailingSpace = m_options.codeTrailingSpace;
69 }
70 }
71 }
72 handleTrailingSpace(s: trailingSpace);
73 }
74 // maybe split long line
75 if (m_options.maxLineLength > 0 && m_currentLine.size() > m_options.maxLineLength) {
76 int possibleSplit = -1;
77 if (fStatus().lineTokens.size() > 1) {
78 // {}[] should already be handled (handle also here?)
79 int minLen = 0;
80 while (minLen < m_currentLine.size() && m_currentLine.at(i: minLen).isSpace())
81 ++minLen;
82 minLen = column(localIndex: minLen) + m_options.minContentLength;
83 int maxLen = qMax(a: minLen + m_options.strongMaxLineExtra, b: m_options.maxLineLength);
84 std::array<QSet<int>, 2> splitSequence(
85 { QSet<int>({ // try split after ',','||','&&'
86 QQmlJSGrammar::T_COMMA, QQmlJSGrammar::T_AND_AND,
87 QQmlJSGrammar::T_OR_OR }),
88 QSet<int>({ // try split after '('
89 QQmlJSGrammar::T_LPAREN }) });
90 // try split after other binary operators?
91 int minSplit = m_currentLine.size();
92 for (const QSet<int> &splitOnToken : splitSequence) {
93 for (int iToken = 0; iToken < fStatus().tokenCount(); ++iToken) {
94 const Token t = fStatus().tokenAt(idx: iToken);
95 int tCol = column(localIndex: t.end());
96 if (splitOnToken.contains(t.lexKind) && tCol > minLen) {
97 if (tCol <= maxLen && possibleSplit < t.end())
98 possibleSplit = t.end();
99 if (t.end() < minSplit)
100 minSplit = t.end();
101 }
102 }
103 if (possibleSplit > 0)
104 break;
105 }
106 if (possibleSplit == -1 && minSplit + 4 < m_currentLine.size())
107 possibleSplit = minSplit;
108 if (possibleSplit > 0) {
109 lineChanged();
110 quint32 oChange = eolToWrite().size();
111 changeAtOffset(offset: m_utf16Offset + possibleSplit, change: oChange, colChange: 0,
112 lineChange: 0); // line & col change updated in commitLine
113 commitLine(eol: eolToWrite(), t: TextAddType::NewlineSplit, untilChar: possibleSplit);
114 shouldReindent = true;
115 goto indentAgain;
116 }
117 }
118 }
119 // maybe write out
120 if (!eol.isEmpty() || eof)
121 commitLine(eol);
122}
123
124} // namespace Dom
125} // namespace QQmlJS
126QT_END_NAMESPACE
127

source code of qtdeclarative/src/qmldom/qqmldomindentinglinewriter.cpp