File indexing completed on 2024-06-09 05:44:31
0001 /* 0002 SPDX-FileCopyrightText: 2022 Waqar Ahmed <waqar.17a@gmail.com> 0003 SPDX-License-Identifier: LGPL-2.0-or-later 0004 */ 0005 #pragma once 0006 0007 #include <QJsonObject> 0008 #include <QPointer> 0009 #include <QProcess> 0010 0011 #include <KLocalizedString> 0012 #include <KTextEditor/Document> 0013 0014 #include "FormatApply.h" 0015 #include <ktexteditor_utils.h> 0016 0017 struct PatchLine; 0018 0019 class AbstractFormatter : public QObject 0020 { 0021 Q_OBJECT 0022 public: 0023 AbstractFormatter(const QJsonObject &obj, KTextEditor::Document *parent) 0024 : QObject(parent) 0025 , originalText(parent->text()) 0026 , m_doc(parent) 0027 , m_globalConfig(obj) 0028 { 0029 } 0030 0031 virtual ~AbstractFormatter() 0032 { 0033 if (m_procHandle && m_procHandle->state() != QProcess::NotRunning) { 0034 m_procHandle->disconnect(this); 0035 m_procHandle->kill(); 0036 m_procHandle->waitForFinished(); 0037 } 0038 } 0039 0040 virtual void run(KTextEditor::Document *doc); 0041 0042 void setCursorPosition(KTextEditor::Cursor c) 0043 { 0044 m_pos = c; 0045 } 0046 0047 bool formatOnSaveEnabled(bool defaultValue) const 0048 { 0049 return m_globalConfig.value(name()).toObject().value(QLatin1String("formatOnSave")).toBool(defaultValue); 0050 } 0051 0052 QString cmdline() const 0053 { 0054 if (m_procHandle) { 0055 return m_procHandle->program() + QLatin1String(" ") + m_procHandle->arguments().join(QLatin1String(" ")); 0056 } 0057 return {}; 0058 } 0059 0060 const QString originalText; 0061 0062 protected: 0063 struct RunOutput { 0064 int exitCode = -1; 0065 QByteArray out; 0066 QByteArray err; 0067 }; 0068 0069 virtual void onResultReady(const RunOutput &o); 0070 0071 virtual bool supportsStdin() const 0072 { 0073 return false; 0074 } 0075 0076 virtual QStringList args(KTextEditor::Document *doc) const = 0; 0077 virtual QString name() const = 0; 0078 virtual QString workingDir() const 0079 { 0080 return {}; 0081 } 0082 0083 virtual QProcessEnvironment env() 0084 { 0085 return QProcessEnvironment::systemEnvironment(); 0086 } 0087 0088 QPointer<KTextEditor::Document> m_doc; 0089 QJsonObject m_config; 0090 QPointer<QProcess> m_procHandle; 0091 KTextEditor::Cursor m_pos; 0092 0093 private: 0094 QByteArray textForStdin() const 0095 { 0096 return originalText.toUtf8(); 0097 } 0098 0099 const QJsonObject m_globalConfig; 0100 0101 Q_SIGNALS: 0102 void textFormatted(AbstractFormatter *formatter, KTextEditor::Document *doc, const QByteArray &text, int offset = -1); 0103 void textFormattedPatch(KTextEditor::Document *doc, const std::vector<PatchLine> &); 0104 void error(const QString &error); 0105 }; 0106 0107 class ClangFormat : public AbstractFormatter 0108 { 0109 public: 0110 using AbstractFormatter::AbstractFormatter; 0111 QString name() const override 0112 { 0113 return QStringLiteral("clang-format"); 0114 } 0115 0116 QStringList args(KTextEditor::Document *doc) const override; 0117 void onResultReady(const RunOutput &o) override; 0118 0119 bool supportsStdin() const override 0120 { 0121 return true; 0122 } 0123 0124 private: 0125 bool m_withCursor = false; 0126 }; 0127 0128 class DartFormat : public AbstractFormatter 0129 { 0130 public: 0131 using AbstractFormatter::AbstractFormatter; 0132 QString name() const override 0133 { 0134 return QStringLiteral("dart"); 0135 } 0136 0137 QStringList args(KTextEditor::Document *doc) const override 0138 { 0139 return {QStringLiteral("format"), 0140 QStringLiteral("--output=show"), 0141 QStringLiteral("--summary=none"), 0142 QStringLiteral("--set-exit-if-changed"), 0143 doc->url().toDisplayString(QUrl::PreferLocalFile)}; 0144 } 0145 0146 private: 0147 void onResultReady(const RunOutput &out) override; 0148 }; 0149 0150 class JsonJqFormat : public AbstractFormatter 0151 { 0152 public: 0153 using AbstractFormatter::AbstractFormatter; 0154 QString name() const override 0155 { 0156 return QStringLiteral("jq"); 0157 } 0158 0159 QStringList args(KTextEditor::Document *doc) const override 0160 { 0161 // Reuse doc's indent 0162 bool ok = false; 0163 int width = doc->configValue(QStringLiteral("indent-width")).toInt(&ok); 0164 if (!ok) { 0165 width = 4; 0166 } 0167 return { 0168 QStringLiteral("."), 0169 QStringLiteral("--indent"), 0170 QString::number(width), 0171 QStringLiteral("-M"), // no color 0172 }; 0173 } 0174 0175 private: 0176 bool supportsStdin() const override 0177 { 0178 return true; 0179 } 0180 }; 0181 0182 class PrettierFormat : public AbstractFormatter 0183 { 0184 public: 0185 using AbstractFormatter::AbstractFormatter; 0186 0187 QString name() const override 0188 { 0189 return QStringLiteral("prettier"); 0190 } 0191 0192 void run(KTextEditor::Document *) override; 0193 0194 QStringList args(KTextEditor::Document *doc) const override 0195 { 0196 return {QStringLiteral("--no-color"), doc->url().toDisplayString(QUrl::PreferLocalFile)}; 0197 } 0198 0199 QString workingDir() const override 0200 { 0201 return Utils::projectBaseDirForDocument(m_doc); 0202 } 0203 0204 private: 0205 void onReadyReadOut(); 0206 void onReadyReadErr(); 0207 0208 void setupNode(); 0209 static inline QPointer<QTemporaryFile> s_tempFile = nullptr; 0210 static inline QPointer<QProcess> s_nodeProcess = nullptr; 0211 RunOutput m_runOutput; 0212 }; 0213 0214 class RustFormat : public AbstractFormatter 0215 { 0216 public: 0217 using AbstractFormatter::AbstractFormatter; 0218 QString name() const override 0219 { 0220 return QStringLiteral("rustfmt"); 0221 } 0222 0223 QStringList args(KTextEditor::Document *) const override 0224 { 0225 return {QStringLiteral("--color=never"), QStringLiteral("--emit=stdout")}; 0226 } 0227 0228 private: 0229 bool supportsStdin() const override 0230 { 0231 return true; 0232 } 0233 0234 void onResultReady(const RunOutput &out) override; 0235 }; 0236 0237 class XmlLintFormat : public AbstractFormatter 0238 { 0239 public: 0240 using AbstractFormatter::AbstractFormatter; 0241 QString name() const override 0242 { 0243 return QStringLiteral("xmllint"); 0244 } 0245 0246 QStringList args(KTextEditor::Document *) const override 0247 { 0248 return {QStringLiteral("--format"), QStringLiteral("-")}; 0249 } 0250 0251 QProcessEnvironment env() override 0252 { 0253 auto environment = AbstractFormatter::env(); 0254 auto ciface = m_doc; 0255 0256 // Reuse doc's indent 0257 bool ok = false; 0258 int width = ciface->configValue(QStringLiteral("indent-width")).toInt(&ok); 0259 if (!ok) { 0260 return environment; 0261 } 0262 bool spaces = ciface->configValue(QStringLiteral("replace-tabs")).toBool(); 0263 QString indent; 0264 if (spaces) { 0265 indent = QString(width, QLatin1Char(' ')); 0266 } else { 0267 indent = QStringLiteral("\t"); 0268 } 0269 0270 environment.insert(QStringLiteral("XMLLINT_INDENT"), indent); 0271 return environment; 0272 } 0273 0274 bool supportsStdin() const override 0275 { 0276 return true; 0277 } 0278 }; 0279 0280 class GoFormat : public AbstractFormatter 0281 { 0282 public: 0283 using AbstractFormatter::AbstractFormatter; 0284 QString name() const override 0285 { 0286 return QStringLiteral("gofmt"); 0287 } 0288 0289 QStringList args(KTextEditor::Document *) const override 0290 { 0291 return {QStringLiteral("-d")}; 0292 } 0293 0294 private: 0295 bool supportsStdin() const override 0296 { 0297 return true; 0298 } 0299 0300 void onResultReady(const RunOutput &out) override; 0301 }; 0302 0303 class ZigFormat : public AbstractFormatter 0304 { 0305 public: 0306 using AbstractFormatter::AbstractFormatter; 0307 QString name() const override 0308 { 0309 return QStringLiteral("zig"); 0310 } 0311 0312 QStringList args(KTextEditor::Document *) const override 0313 { 0314 return {QStringLiteral("fmt"), QStringLiteral("--stdin")}; 0315 } 0316 0317 private: 0318 bool supportsStdin() const override 0319 { 0320 return true; 0321 } 0322 }; 0323 0324 class CMakeFormat : public AbstractFormatter 0325 { 0326 public: 0327 using AbstractFormatter::AbstractFormatter; 0328 QString name() const override 0329 { 0330 return QStringLiteral("cmake-format"); 0331 } 0332 0333 QStringList args(KTextEditor::Document *) const override 0334 { 0335 return {m_doc->url().toLocalFile()}; 0336 } 0337 }; 0338 0339 class AutoPep8Format : public AbstractFormatter 0340 { 0341 public: 0342 using AbstractFormatter::AbstractFormatter; 0343 QString name() const override 0344 { 0345 return QStringLiteral("autopep8"); 0346 } 0347 0348 QStringList args(KTextEditor::Document *) const override 0349 { 0350 return {m_doc->url().toLocalFile()}; 0351 } 0352 };