24collectNestedIds(
const QQmlLSUtils::ItemLocation &item)
27 item.domItem.goToFile(item.domItem.canonicalFilePath()).ownerAs<QQmlJS::Dom::QmlFile>();
28 const QString code = filePtr ? filePtr->code() : QString();
30 QList<std::pair<QString, QQmlJS::SourceLocation>> innerIds;
31 FileLocations::visitTree(
33 [&code, &innerIds](
const auto &,
const FileLocations::Tree &t) ->
bool {
34 const auto idNameLoc = t->info().regions[FileLocationRegion::IdNameRegion];
35 if (idNameLoc.isValid()) {
36 innerIds.append({ code.mid(idNameLoc.begin(), idNameLoc.length), idNameLoc });
45 CodeActions codeActions;
47 for (
const Diagnostic &diagnostic : diagnostics) {
48 if (!diagnostic.data.has_value())
51 const auto &data = diagnostic.data.value();
53 int version = data[u"version"].toInt();
54 QJsonArray suggestions = data[u"suggestions"].toArray();
56 QList<WorkspaceEdit::DocumentChange> edits;
58 for (
const QJsonValue &suggestion : std::as_const(suggestions)) {
59 message += suggestion[u"message"_s].toString() + u'\n';
60 const auto &documentEdits = suggestion[u"documentEdits"_s].toArray();
61 TextDocumentEdit textDocEdit;
62 for (
const auto &documentEdit : documentEdits) {
66 static_cast<
unsigned>(documentEdit[u"lspBeginLine"].toDouble()),
67 static_cast<
unsigned>(documentEdit[u"lspBeginCharacter"].toDouble()) },
68 Position{
static_cast<
unsigned>(documentEdit[u"lspEndLine"].toDouble()),
69 static_cast<
unsigned>(documentEdit[u"lspEndCharacter"].toDouble()) }
72 textEdit.newText = documentEdit[u"replacement"_s].toString().toUtf8();
73 QString filename = documentEdit[u"filename"_s].toString();
74 textDocEdit.textDocument = { { filename.toUtf8() }, version };
75 textDocEdit.edits.append(textEdit);
77 edits.append(textDocEdit);
81 edit.documentChanges = edits;
85 action.kind = CodeActionKind::QuickFix;
87 action.title = message.toUtf8();
89 codeActions.append(action);
115 const QString componentOpen = QString::fromLatin1(
"Component {\n"
118 const QString componentClose = QString::fromLatin1(
"\n}\n");
119 return { TextEdit{ { itemRange.start, itemRange.start }, componentOpen.toUtf8() },
120 TextEdit{ { itemRange.end, itemRange.end }, componentClose.toUtf8() } };
137 const QLatin1StringView nestedIdPrefix(
"inner_");
138 QString idAliases = QString::fromLatin1(
"\n");
139 for (
auto &id : nestedIds) {
140 idAliases += QString::fromLatin1(
"property alias %1: %2%1\n").arg(id.first, nestedIdPrefix);
145 const auto posAfterLBrace =
146 Position{
static_cast<
unsigned int>(
static_cast<
int>(openingBrace.startLine - 1)),
147 static_cast<
unsigned int>(
148 static_cast<
int>(openingBrace.startColumn)) };
150 edits.append(TextEdit{ { posAfterLBrace, posAfterLBrace }, idAliases.toUtf8() });
153 for (
auto &id : nestedIds) {
155 Position{
static_cast<
unsigned int>(
static_cast<
int>(id.second.startLine - 1)),
156 static_cast<
unsigned int>(
static_cast<
int>(id.second.startColumn - 1)) };
157 edits.append(TextEdit{ { idPos, idPos }, nestedIdPrefix.toUtf8() });
164 const auto generateId = [&item](
const QString &base) -> QString {
165 const auto ids = item.domItem.component().field(Fields::ids).keys();
166 if (!ids.contains(base)) {
170 for (; extraNumber < ids.size(); ++extraNumber) {
171 QString id = base + QString::number(extraNumber);
172 if (!ids.contains(id)) {
176 return base + QString::number(extraNumber);
179 const auto objId = item.domItem.idStr();
180 const auto objName = objId.isEmpty() ? item.domItem.name() : objId;
181 const QString componentId = generateId(QLatin1StringView(
"component_") + objName);
182 const QString loaderId = generateId(QLatin1StringView(
"loader_") + objName);
184 const auto itemRange = QQmlLSUtils::qmlLocationToLspLocation(
185 QQmlLSUtils::Location::tryFrom(item.domItem.canonicalFilePath(),
186 item.fileLocation->info().fullRegion, item.domItem)
187 .value_or(QQmlLSUtils::Location{}));
189 QList<std::pair<QString, QQmlJS::SourceLocation>> nestedIds = collectNestedIds(item);
190 if (!objId.isEmpty()) {
193 Q_ASSERT(nestedIds.front().first == objId);
194 nestedIds.removeFirst();
198 edits << todoComment(itemRange.start, loaderId, objId, nestedIds)
199 << exposeNestedIds(item.fileLocation->info().regions[FileLocationRegion::LeftBraceRegion],
201 << wrapIntoComponent(itemRange, componentId)
202 << addLoader(itemRange.end, loaderId, componentId);
207 const QQmlLSUtils::ItemLocation &item)
209 if (item.domItem.internalKind() != DomType::QmlObject) {
213 if (item.domItem == item.domItem.component().field(Fields::objects).index(0)) {
217 if (item.domItem.canonicalPath().last() == Path::fromField(Fields::value)) {
222 TextDocumentEdit textDocEdit;
223 textDocEdit.textDocument = { textDocument, {} };
224 textDocEdit.edits = wrapInLoaderTextEdits(item);
227 edit.documentChanges = { textDocEdit };
230 action.kind = CodeActionKind::RefactorRewrite;
231 action.title =
"Wrap Component in Loader";
258 CodeActions codeActions;
259 codeActions.append(quickfixes(request->m_parameters.context.diagnostics));
264 auto itemsFound = itemsForRequest(request, request->m_parameters.range.end);
265 if (std::holds_alternative<QQmlLSUtils::ErrorMessage>(itemsFound)) {
266 qCWarning(lsCodeActionSupport) << std::get<QQmlLSUtils::ErrorMessage>(itemsFound).message;
267 }
else if (std::holds_alternative<QList<QQmlLSUtils::ItemLocation>>(itemsFound)) {
268 QQmlLSUtils::ItemLocation &item =
269 std::get<QList<QQmlLSUtils::ItemLocation>>(itemsFound).front();
271 codeActions.append(refactorings(request->m_parameters.textDocument, item));
274 request->m_response.sendResponse(codeActions);
static TextEdit todoComment(const Position &pos, const QString &loaderId, const QString &maybeId, const QList< std::pair< QString, QQmlJS::SourceLocation > > &nestedIds)