1// Copyright (C) 2021 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#ifndef QQMLDOMLINEWRITER_P
5#define QQMLDOMLINEWRITER_P
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include "qqmldom_global.h"
19#include "qqmldomstringdumper_p.h"
20
21#include <QtQml/private/qqmljssourcelocation_p.h>
22#include <QtCore/QObject>
23#include <QtCore/QAtomicInt>
24#include <QtCore/QMap>
25#include <functional>
26
27QT_BEGIN_NAMESPACE
28namespace QQmlJS {
29namespace Dom {
30
31class IndentInfo
32{
33public:
34 QStringView string;
35 QStringView trailingString;
36 int nNewlines = 0;
37 int column = 0;
38
39 IndentInfo(QStringView line, int tabSize, int initialColumn = 0)
40 {
41 string = line;
42 int fixup = 0;
43 if (initialColumn < 0) // we do not want % of negative numbers
44 fixup = (-initialColumn + tabSize - 1) / tabSize * tabSize;
45 column = initialColumn + fixup;
46 const QChar tab = QLatin1Char('\t');
47 int iStart = 0;
48 int len = line.size();
49 for (int i = 0; i < len; i++) {
50 if (line[i] == tab)
51 column = ((column / tabSize) + 1) * tabSize;
52 else if (line[i] == QLatin1Char('\n')
53 || (line[i] == QLatin1Char('\r')
54 && (i + 1 == len || line[i + 1] != QLatin1Char('\n')))) {
55 iStart = i + 1;
56 ++nNewlines;
57 column = 0;
58 } else if (!line[i].isLowSurrogate())
59 column++;
60 }
61 column -= fixup;
62 trailingString = line.mid(pos: iStart);
63 }
64};
65
66class QMLDOM_EXPORT FormatOptions
67{
68public:
69 int tabSize = 4;
70 int indentSize = 4;
71 bool useTabs = false;
72};
73
74class QMLDOM_EXPORT LineWriterOptions
75{
76 Q_GADGET
77public:
78 enum class LineEndings { Unix, Windows, OldMacOs };
79 Q_ENUM(LineEndings)
80 enum class TrailingSpace { Preserve, Remove };
81 Q_ENUM(TrailingSpace)
82 enum class Update { None = 0, Expressions = 0x1, Locations = 0x2, All = 0x3, Default = All };
83 Q_ENUM(Update)
84 Q_DECLARE_FLAGS(Updates, Update)
85 enum class AttributesSequence { Normalize, Preserve };
86 Q_ENUM(AttributesSequence)
87
88 int maxLineLength = -1;
89 int strongMaxLineExtra = 20;
90 int minContentLength = 10;
91 LineEndings lineEndings = LineEndings::Unix;
92 TrailingSpace codeTrailingSpace = TrailingSpace::Remove;
93 TrailingSpace commentTrailingSpace = TrailingSpace::Remove;
94 TrailingSpace stringTrailingSpace = TrailingSpace::Preserve;
95 FormatOptions formatOptions;
96 Updates updateOptions = Update::Default;
97 AttributesSequence attributesSequence = AttributesSequence::Normalize;
98 bool objectsSpacing = false;
99 bool functionsSpacing = false;
100};
101Q_DECLARE_OPERATORS_FOR_FLAGS(LineWriterOptions::Updates)
102
103using PendingSourceLocationId = QAtomicInt;
104class LineWriter;
105
106class QMLDOM_EXPORT PendingSourceLocation
107{
108 Q_GADGET
109public:
110 quint32 utf16Start() const;
111 quint32 utf16End() const;
112 void changeAtOffset(quint32 offset, qint32 change, qint32 colChange, qint32 lineChange);
113 void commit();
114 PendingSourceLocationId id;
115 SourceLocation value;
116 SourceLocation *toUpdate = nullptr;
117 std::function<void(SourceLocation)> updater = nullptr;
118 bool open = true;
119};
120
121class QMLDOM_EXPORT LineWriter
122{
123 Q_GADGET
124public:
125 enum class TextAddType {
126 Normal,
127 Extra,
128 Newline,
129 NewlineSplit,
130 NewlineExtra,
131 PartialCommit,
132 Eof
133 };
134
135 LineWriter(SinkF innerSink, QString fileName,
136 const LineWriterOptions &options = LineWriterOptions(), int lineNr = 0,
137 int columnNr = 0, int utf16Offset = 0, QString currentLine = QString());
138 std::function<void(QStringView)> sink()
139 {
140 return [this](QStringView s) { this->write(v: s); };
141 }
142
143 virtual ~LineWriter() { }
144
145 QList<SinkF> innerSinks() { return m_innerSinks; }
146 void addInnerSink(SinkF s) { m_innerSinks.append(t: s); }
147 LineWriter &ensureNewline(int nNewlines = 1, TextAddType t = TextAddType::Extra);
148 LineWriter &ensureSpace(TextAddType t = TextAddType::Extra);
149 LineWriter &ensureSpace(QStringView space, TextAddType t = TextAddType::Extra);
150
151 LineWriter &newline()
152 {
153 write(v: u"\n");
154 return *this;
155 }
156 LineWriter &space()
157 {
158 write(v: u" ");
159 return *this;
160 }
161 LineWriter &write(QStringView v, TextAddType tType = TextAddType::Normal);
162 LineWriter &write(QStringView v, SourceLocation *toUpdate)
163 {
164 auto pLoc = startSourceLocation(toUpdate);
165 write(v);
166 endSourceLocation(pLoc);
167 return *this;
168 }
169 void commitLine(QString eol, TextAddType t = TextAddType::Normal, int untilChar = -1);
170 void flush();
171 void eof(bool ensureNewline = true);
172 SourceLocation committedLocation() const;
173 PendingSourceLocationId startSourceLocation(SourceLocation *);
174 PendingSourceLocationId startSourceLocation(std::function<void(SourceLocation)>);
175 void endSourceLocation(PendingSourceLocationId);
176 quint32 counter() const { return m_counter; }
177 int addTextAddCallback(std::function<bool(LineWriter &, TextAddType)> callback);
178 bool removeTextAddCallback(int i) { return m_textAddCallbacks.remove(key: i); }
179 int addNewlinesAutospacerCallback(int nLines);
180 void handleTrailingSpace(LineWriterOptions::TrailingSpace s);
181 void setLineIndent(int indentAmount);
182 QString fileName() const { return m_fileName; }
183 const QString &currentLine() const { return m_currentLine; }
184 const LineWriterOptions &options() const { return m_options; }
185 virtual void lineChanged() { }
186 virtual void reindentAndSplit(QString eol, bool eof = false);
187 virtual void willCommit() { }
188
189private:
190 Q_DISABLE_COPY_MOVE(LineWriter)
191protected:
192 void changeAtOffset(quint32 offset, qint32 change, qint32 colChange, qint32 lineChange);
193 QString eolToWrite() const;
194 SourceLocation currentSourceLocation() const;
195 int column(int localIndex);
196 void textAddCallback(TextAddType t);
197
198 QList<SinkF> m_innerSinks;
199 QString m_fileName;
200 int m_lineNr = 0;
201 int m_columnNr = 0; // columnNr (starts at 0) of committed data
202 int m_lineUtf16Offset = 0; // utf16 offset since last newline (what is typically stores as
203 // SourceLocation::startColumn
204 int m_currentColumnNr = 0; // current columnNr (starts at 0)
205 int m_utf16Offset = 0; // utf16 offset since start for committed data
206 QString m_currentLine;
207 LineWriterOptions m_options;
208 PendingSourceLocationId m_lastSourceLocationId;
209 QMap<PendingSourceLocationId, PendingSourceLocation> m_pendingSourceLocations;
210 QAtomicInt m_lastCallbackId;
211 QMap<int, std::function<bool(LineWriter &, TextAddType)>> m_textAddCallbacks;
212 quint32 m_counter = 0;
213 quint32 m_committedEmptyLines = 0x7FFFFFFF;
214 bool m_reindent = true;
215};
216
217} // namespace Dom
218} // namespace QQmlJS
219QT_END_NAMESPACE
220#endif
221

source code of qtdeclarative/src/qmldom/qqmldomlinewriter_p.h