File indexing completed on 2024-05-12 04:39:12

0001 /*
0002     SPDX-FileCopyrightText: 2018, 2019 Amish K. Naidu <amhndu@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "headerguardassistant.h"
0008 #include "util/clangutils.h"
0009 #include "util/clangtypes.h"
0010 
0011 #include <language/codegen/documentchangeset.h>
0012 #include <language/codegen/coderepresentation.h>
0013 #include <language/duchain/duchainlock.h>
0014 
0015 #include <QDir>
0016 #include <QRegularExpression>
0017 
0018 #include <algorithm>
0019 
0020 
0021 enum class GuardType
0022 {
0023     Pragma,
0024     Macro
0025 };
0026 
0027 class AddHeaderGuardAction
0028     : public KDevelop::IAssistantAction
0029 {
0030     Q_OBJECT
0031 
0032 public:
0033     AddHeaderGuardAction(const GuardType type, const int startLine, const KDevelop::IndexedString& path)
0034         : m_type(type)
0035         , m_startLine(startLine)
0036         , m_path(path)
0037     {
0038     }
0039 
0040     virtual ~AddHeaderGuardAction() override = default;
0041 
0042     QString description() const override
0043     {
0044         switch (m_type) {
0045             case GuardType::Pragma: return i18n("Add #pragma once");
0046             case GuardType::Macro: return i18n("Add macro-based #ifndef/#define/#endif header guard");
0047         }
0048         return {};
0049     }
0050 
0051     void execute() override
0052     {
0053         KDevelop::DocumentChangeSet changes;
0054         switch (m_type) {
0055             case GuardType::Pragma:
0056             {
0057                 KDevelop::DocumentChange change(m_path, KTextEditor::Range(m_startLine, 0, m_startLine, 0), QString(),
0058                                                 QStringLiteral("#pragma once\n\n"));
0059                 changes.addChange(change);
0060                 break;
0061             }
0062             case GuardType::Macro:
0063             {
0064                 const QString macro = m_path.toUrl()
0065                         .fileName(QUrl::PrettyDecoded)
0066                         .replace(QRegularExpression(QStringLiteral("[^a-zA-Z0-9]")), QStringLiteral(" "))
0067                         .simplified()
0068                         .toUpper()
0069                         .replace(QLatin1Char(' '), QLatin1Char('_'))
0070                         .append(QLatin1String("_INCLUDED"));
0071 
0072                 const auto representation = KDevelop::createCodeRepresentation(m_path);
0073                 const auto lastLine = representation->lines() - 1;
0074                 const auto lastColumn = representation->line(lastLine).length();
0075 
0076                 // Add the #endif change before so that it applies correctly in case lastLine == m_startline
0077                 changes.addChange(KDevelop::DocumentChange(m_path,
0078                                                            KTextEditor::Range(lastLine, lastColumn, lastLine, lastColumn),
0079                                                            QString(),
0080                                                            QStringLiteral("\n#endif // %1").arg(macro)));
0081                 changes.addChange(KDevelop::DocumentChange(m_path,
0082                                                            KTextEditor::Range(m_startLine, 0, m_startLine, 0),
0083                                                            QString(),
0084                                                            QStringLiteral("#ifndef %1\n#define %1\n\n").arg(macro)));
0085                 break;
0086             }
0087         }
0088 
0089         KDevelop::DUChainReadLocker lock;
0090         changes.setReplacementPolicy(KDevelop::DocumentChangeSet::WarnOnFailedChange);
0091         changes.applyAllChanges();
0092         emit executed(this);
0093     }
0094 
0095 private:
0096     const GuardType m_type;
0097     const int m_startLine;
0098     const KDevelop::IndexedString m_path;
0099 };
0100 
0101 HeaderGuardAssistant::HeaderGuardAssistant(const CXTranslationUnit unit, const CXFile file)
0102     : m_line(std::max(ClangUtils::skipTopCommentBlock(unit, file), 1u) - 1) // skip license etc
0103     , m_path(QDir(ClangString(clang_getFileName(file)).toString()).canonicalPath())
0104 {
0105 }
0106 
0107 QString HeaderGuardAssistant::title() const
0108 {
0109     return QStringLiteral("Fix-Header");
0110 }
0111 
0112 void HeaderGuardAssistant::createActions()
0113 {
0114     addAction(KDevelop::IAssistantAction::Ptr{new AddHeaderGuardAction(GuardType::Pragma, m_line, m_path)});
0115     addAction(KDevelop::IAssistantAction::Ptr{new AddHeaderGuardAction(GuardType::Macro, m_line, m_path)});
0116 }
0117 
0118 #include "headerguardassistant.moc"
0119 #include "moc_headerguardassistant.cpp"