1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "cppcodestylesettingspage.h"
27 
28 #include "cppcodestylepreferences.h"
29 #include "cppcodestylesnippets.h"
30 #include "cpppointerdeclarationformatter.h"
31 #include "cppqtstyleindenter.h"
32 #include "cpptoolsconstants.h"
33 #include "cpptoolssettings.h"
34 #include <ui_cppcodestylesettingspage.h>
35 
36 #include <coreplugin/icore.h>
37 #include <cppeditor/cppeditorconstants.h>
38 #include <texteditor/codestyleeditor.h>
39 #include <texteditor/fontsettings.h>
40 #include <texteditor/icodestylepreferencesfactory.h>
41 #include <texteditor/textdocument.h>
42 #include <texteditor/displaysettings.h>
43 #include <texteditor/snippets/snippetprovider.h>
44 #include <texteditor/texteditorsettings.h>
45 
46 #include <cplusplus/Overview.h>
47 #include <cplusplus/pp.h>
48 
49 #include <extensionsystem/pluginmanager.h>
50 
51 #include <QTextBlock>
52 #include <QTextStream>
53 
54 using namespace TextEditor;
55 
56 namespace CppTools {
57 
58 namespace Internal {
59 
applyRefactorings(QTextDocument * textDocument,TextEditorWidget * editor,const CppCodeStyleSettings & settings)60 static void applyRefactorings(QTextDocument *textDocument, TextEditorWidget *editor,
61                               const CppCodeStyleSettings &settings)
62 {
63     // Preprocess source
64     Environment env;
65     Preprocessor preprocess(nullptr, &env);
66     const QByteArray preprocessedSource
67         = preprocess.run(QLatin1String("<no-file>"), textDocument->toPlainText());
68 
69     Document::Ptr cppDocument = Document::create(QLatin1String("<no-file>"));
70     cppDocument->setUtf8Source(preprocessedSource);
71     cppDocument->parse(Document::ParseTranlationUnit);
72     cppDocument->check();
73 
74     CppRefactoringFilePtr cppRefactoringFile = CppRefactoringChanges::file(editor, cppDocument);
75 
76     // Run the formatter
77     Overview overview;
78     overview.showReturnTypes = true;
79     overview.starBindFlags = {};
80 
81     if (settings.bindStarToIdentifier)
82         overview.starBindFlags |= Overview::BindToIdentifier;
83     if (settings.bindStarToTypeName)
84         overview.starBindFlags |= Overview::BindToTypeName;
85     if (settings.bindStarToLeftSpecifier)
86         overview.starBindFlags |= Overview::BindToLeftSpecifier;
87     if (settings.bindStarToRightSpecifier)
88         overview.starBindFlags |= Overview::BindToRightSpecifier;
89 
90     PointerDeclarationFormatter formatter(cppRefactoringFile, overview);
91     Utils::ChangeSet change = formatter.format(cppDocument->translationUnit()->ast());
92 
93     // Apply change
94     QTextCursor cursor(textDocument);
95     change.apply(&cursor);
96 }
97 
98 // ------------------ CppCodeStyleSettingsWidget
99 
CppCodeStylePreferencesWidget(QWidget * parent)100 CppCodeStylePreferencesWidget::CppCodeStylePreferencesWidget(QWidget *parent)
101     : QWidget(parent),
102       m_ui(new Ui::CppCodeStyleSettingsPage)
103 {
104     m_ui->setupUi(this);
105     m_ui->categoryTab->setProperty("_q_custom_style_disabled", true);
106 
107     m_previews << m_ui->previewTextEditGeneral << m_ui->previewTextEditContent
108                << m_ui->previewTextEditBraces << m_ui->previewTextEditSwitch
109                << m_ui->previewTextEditPadding << m_ui->previewTextEditPointerReferences;
110     for (int i = 0; i < m_previews.size(); ++i)
111         m_previews[i]->setPlainText(QLatin1String(Constants::DEFAULT_CODE_STYLE_SNIPPETS[i]));
112 
113     decorateEditors(TextEditorSettings::fontSettings());
114     connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged,
115             this, &CppCodeStylePreferencesWidget::decorateEditors);
116 
117     setVisualizeWhitespace(true);
118 
119     connect(m_ui->tabSettingsWidget, &TabSettingsWidget::settingsChanged,
120             this, &CppCodeStylePreferencesWidget::slotTabSettingsChanged);
121     connect(m_ui->indentBlockBraces, &QCheckBox::toggled,
122             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
123     connect(m_ui->indentBlockBody, &QCheckBox::toggled,
124             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
125     connect(m_ui->indentClassBraces, &QCheckBox::toggled,
126             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
127     connect(m_ui->indentNamespaceBraces, &QCheckBox::toggled,
128             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
129     connect(m_ui->indentEnumBraces, &QCheckBox::toggled,
130             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
131     connect(m_ui->indentNamespaceBody, &QCheckBox::toggled,
132             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
133     connect(m_ui->indentSwitchLabels, &QCheckBox::toggled,
134             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
135     connect(m_ui->indentCaseStatements, &QCheckBox::toggled,
136             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
137     connect(m_ui->indentCaseBlocks, &QCheckBox::toggled,
138             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
139     connect(m_ui->indentCaseBreak, &QCheckBox::toggled,
140             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
141     connect(m_ui->indentAccessSpecifiers, &QCheckBox::toggled,
142             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
143     connect(m_ui->indentDeclarationsRelativeToAccessSpecifiers, &QCheckBox::toggled,
144             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
145     connect(m_ui->indentFunctionBody, &QCheckBox::toggled,
146             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
147     connect(m_ui->indentFunctionBraces, &QCheckBox::toggled,
148             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
149     connect(m_ui->extraPaddingConditions, &QCheckBox::toggled,
150             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
151     connect(m_ui->alignAssignments, &QCheckBox::toggled,
152             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
153     connect(m_ui->bindStarToIdentifier, &QCheckBox::toggled,
154             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
155     connect(m_ui->bindStarToTypeName, &QCheckBox::toggled,
156             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
157     connect(m_ui->bindStarToLeftSpecifier, &QCheckBox::toggled,
158             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
159     connect(m_ui->bindStarToRightSpecifier, &QCheckBox::toggled,
160             this, &CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged);
161 
162     m_ui->categoryTab->setCurrentIndex(0);
163 }
164 
~CppCodeStylePreferencesWidget()165 CppCodeStylePreferencesWidget::~CppCodeStylePreferencesWidget()
166 {
167     delete m_ui;
168 }
169 
setCodeStyle(CppTools::CppCodeStylePreferences * codeStylePreferences)170 void CppCodeStylePreferencesWidget::setCodeStyle(CppTools::CppCodeStylePreferences *codeStylePreferences)
171 {
172     // code preferences
173     m_preferences = codeStylePreferences;
174 
175     connect(m_preferences, &CppCodeStylePreferences::currentTabSettingsChanged,
176             this, &CppCodeStylePreferencesWidget::setTabSettings);
177     connect(m_preferences, &CppCodeStylePreferences::currentCodeStyleSettingsChanged,
178             this, [this](const CppTools::CppCodeStyleSettings &codeStyleSettings) {
179         setCodeStyleSettings(codeStyleSettings);
180     });
181 
182     connect(m_preferences, &ICodeStylePreferences::currentPreferencesChanged,
183             this, [this](TextEditor::ICodeStylePreferences *currentPreferences) {
184         slotCurrentPreferencesChanged(currentPreferences);
185     });
186 
187     setTabSettings(m_preferences->tabSettings());
188     setCodeStyleSettings(m_preferences->codeStyleSettings(), false);
189     slotCurrentPreferencesChanged(m_preferences->currentPreferences(), false);
190 
191     updatePreview();
192 }
193 
cppCodeStyleSettings() const194 CppCodeStyleSettings CppCodeStylePreferencesWidget::cppCodeStyleSettings() const
195 {
196     CppCodeStyleSettings set;
197 
198     set.indentBlockBraces = m_ui->indentBlockBraces->isChecked();
199     set.indentBlockBody = m_ui->indentBlockBody->isChecked();
200     set.indentClassBraces = m_ui->indentClassBraces->isChecked();
201     set.indentEnumBraces = m_ui->indentEnumBraces->isChecked();
202     set.indentNamespaceBraces = m_ui->indentNamespaceBraces->isChecked();
203     set.indentNamespaceBody = m_ui->indentNamespaceBody->isChecked();
204     set.indentAccessSpecifiers = m_ui->indentAccessSpecifiers->isChecked();
205     set.indentDeclarationsRelativeToAccessSpecifiers = m_ui->indentDeclarationsRelativeToAccessSpecifiers->isChecked();
206     set.indentFunctionBody = m_ui->indentFunctionBody->isChecked();
207     set.indentFunctionBraces = m_ui->indentFunctionBraces->isChecked();
208     set.indentSwitchLabels = m_ui->indentSwitchLabels->isChecked();
209     set.indentStatementsRelativeToSwitchLabels = m_ui->indentCaseStatements->isChecked();
210     set.indentBlocksRelativeToSwitchLabels = m_ui->indentCaseBlocks->isChecked();
211     set.indentControlFlowRelativeToSwitchLabels = m_ui->indentCaseBreak->isChecked();
212     set.bindStarToIdentifier = m_ui->bindStarToIdentifier->isChecked();
213     set.bindStarToTypeName = m_ui->bindStarToTypeName->isChecked();
214     set.bindStarToLeftSpecifier = m_ui->bindStarToLeftSpecifier->isChecked();
215     set.bindStarToRightSpecifier = m_ui->bindStarToRightSpecifier->isChecked();
216     set.extraPaddingForConditionsIfConfusingAlign = m_ui->extraPaddingConditions->isChecked();
217     set.alignAssignments = m_ui->alignAssignments->isChecked();
218 
219     return set;
220 }
221 
setTabSettings(const TabSettings & settings)222 void CppCodeStylePreferencesWidget::setTabSettings(const TabSettings &settings)
223 {
224     m_ui->tabSettingsWidget->setTabSettings(settings);
225 }
226 
setCodeStyleSettings(const CppCodeStyleSettings & s,bool preview)227 void CppCodeStylePreferencesWidget::setCodeStyleSettings(const CppCodeStyleSettings &s, bool preview)
228 {
229     const bool wasBlocked = m_blockUpdates;
230     m_blockUpdates = true;
231     m_ui->indentBlockBraces->setChecked(s.indentBlockBraces);
232     m_ui->indentBlockBody->setChecked(s.indentBlockBody);
233     m_ui->indentClassBraces->setChecked(s.indentClassBraces);
234     m_ui->indentEnumBraces->setChecked(s.indentEnumBraces);
235     m_ui->indentNamespaceBraces->setChecked(s.indentNamespaceBraces);
236     m_ui->indentNamespaceBody->setChecked(s.indentNamespaceBody);
237     m_ui->indentAccessSpecifiers->setChecked(s.indentAccessSpecifiers);
238     m_ui->indentDeclarationsRelativeToAccessSpecifiers->setChecked(s.indentDeclarationsRelativeToAccessSpecifiers);
239     m_ui->indentFunctionBody->setChecked(s.indentFunctionBody);
240     m_ui->indentFunctionBraces->setChecked(s.indentFunctionBraces);
241     m_ui->indentSwitchLabels->setChecked(s.indentSwitchLabels);
242     m_ui->indentCaseStatements->setChecked(s.indentStatementsRelativeToSwitchLabels);
243     m_ui->indentCaseBlocks->setChecked(s.indentBlocksRelativeToSwitchLabels);
244     m_ui->indentCaseBreak->setChecked(s.indentControlFlowRelativeToSwitchLabels);
245     m_ui->bindStarToIdentifier->setChecked(s.bindStarToIdentifier);
246     m_ui->bindStarToTypeName->setChecked(s.bindStarToTypeName);
247     m_ui->bindStarToLeftSpecifier->setChecked(s.bindStarToLeftSpecifier);
248     m_ui->bindStarToRightSpecifier->setChecked(s.bindStarToRightSpecifier);
249     m_ui->extraPaddingConditions->setChecked(s.extraPaddingForConditionsIfConfusingAlign);
250     m_ui->alignAssignments->setChecked(s.alignAssignments);
251     m_blockUpdates = wasBlocked;
252     if (preview)
253         updatePreview();
254 }
255 
slotCurrentPreferencesChanged(ICodeStylePreferences * preferences,bool preview)256 void CppCodeStylePreferencesWidget::slotCurrentPreferencesChanged(ICodeStylePreferences *preferences, bool preview)
257 {
258     const bool enable = !preferences->isReadOnly() && !m_preferences->currentDelegate();
259     m_ui->tabSettingsWidget->setEnabled(enable);
260     m_ui->contentGroupBox->setEnabled(enable);
261     m_ui->bracesGroupBox->setEnabled(enable);
262     m_ui->switchGroupBox->setEnabled(enable);
263     m_ui->alignmentGroupBox->setEnabled(enable);
264     m_ui->pointerReferencesGroupBox->setEnabled(enable);
265     if (preview)
266         updatePreview();
267 }
268 
slotCodeStyleSettingsChanged()269 void CppCodeStylePreferencesWidget::slotCodeStyleSettingsChanged()
270 {
271     if (m_blockUpdates)
272         return;
273 
274     if (m_preferences) {
275         auto current = qobject_cast<CppCodeStylePreferences *>(m_preferences->currentPreferences());
276         if (current)
277             current->setCodeStyleSettings(cppCodeStyleSettings());
278     }
279 
280     updatePreview();
281 }
282 
slotTabSettingsChanged(const TabSettings & settings)283 void CppCodeStylePreferencesWidget::slotTabSettingsChanged(const TabSettings &settings)
284 {
285     if (m_blockUpdates)
286         return;
287 
288     if (m_preferences) {
289         auto current = qobject_cast<CppCodeStylePreferences *>(m_preferences->currentPreferences());
290         if (current)
291             current->setTabSettings(settings);
292     }
293 
294     updatePreview();
295 }
296 
updatePreview()297 void CppCodeStylePreferencesWidget::updatePreview()
298 {
299     CppCodeStylePreferences *cppCodeStylePreferences = m_preferences
300             ? m_preferences
301             : CppToolsSettings::instance()->cppCodeStyle();
302     const CppCodeStyleSettings ccss = cppCodeStylePreferences->currentCodeStyleSettings();
303     const TabSettings ts = cppCodeStylePreferences->currentTabSettings();
304     QtStyleCodeFormatter formatter(ts, ccss);
305     foreach (SnippetEditorWidget *preview, m_previews) {
306         preview->textDocument()->setTabSettings(ts);
307         preview->setCodeStyle(cppCodeStylePreferences);
308 
309         QTextDocument *doc = preview->document();
310         formatter.invalidateCache(doc);
311 
312         QTextBlock block = doc->firstBlock();
313         QTextCursor tc = preview->textCursor();
314         tc.beginEditBlock();
315         while (block.isValid()) {
316             preview->textDocument()->indenter()->indentBlock(block, QChar::Null, ts);
317 
318             block = block.next();
319         }
320         applyRefactorings(doc, preview, ccss);
321         tc.endEditBlock();
322     }
323 }
324 
decorateEditors(const FontSettings & fontSettings)325 void CppCodeStylePreferencesWidget::decorateEditors(const FontSettings &fontSettings)
326 {
327     foreach (SnippetEditorWidget *editor, m_previews) {
328         editor->textDocument()->setFontSettings(fontSettings);
329         SnippetProvider::decorateEditor(editor, CppEditor::Constants::CPP_SNIPPETS_GROUP_ID);
330     }
331 }
332 
setVisualizeWhitespace(bool on)333 void CppCodeStylePreferencesWidget::setVisualizeWhitespace(bool on)
334 {
335     foreach (SnippetEditorWidget *editor, m_previews) {
336         DisplaySettings displaySettings = editor->displaySettings();
337         displaySettings.m_visualizeWhitespace = on;
338         editor->setDisplaySettings(displaySettings);
339     }
340 }
341 
342 
343 // ------------------ CppCodeStyleSettingsPage
344 
CppCodeStyleSettingsPage()345 CppCodeStyleSettingsPage::CppCodeStyleSettingsPage()
346 {
347     setId(Constants::CPP_CODE_STYLE_SETTINGS_ID);
348     setDisplayName(QCoreApplication::translate("CppTools", Constants::CPP_CODE_STYLE_SETTINGS_NAME));
349     setCategory(Constants::CPP_SETTINGS_CATEGORY);
350 }
351 
widget()352 QWidget *CppCodeStyleSettingsPage::widget()
353 {
354     if (!m_widget) {
355         CppCodeStylePreferences *originalCodeStylePreferences = CppToolsSettings::instance()
356                                                                     ->cppCodeStyle();
357         m_pageCppCodeStylePreferences = new CppCodeStylePreferences();
358         m_pageCppCodeStylePreferences->setDelegatingPool(
359             originalCodeStylePreferences->delegatingPool());
360         m_pageCppCodeStylePreferences->setCodeStyleSettings(
361             originalCodeStylePreferences->codeStyleSettings());
362         m_pageCppCodeStylePreferences->setCurrentDelegate(
363             originalCodeStylePreferences->currentDelegate());
364         // we set id so that it won't be possible to set delegate to the original prefs
365         m_pageCppCodeStylePreferences->setId(originalCodeStylePreferences->id());
366         m_widget = TextEditorSettings::codeStyleFactory(CppTools::Constants::CPP_SETTINGS_ID)
367                        ->createCodeStyleEditor(m_pageCppCodeStylePreferences);
368     }
369     return m_widget;
370 }
371 
apply()372 void CppCodeStyleSettingsPage::apply()
373 {
374     if (m_widget) {
375         QSettings *s = Core::ICore::settings();
376 
377         CppCodeStylePreferences *originalCppCodeStylePreferences = CppToolsSettings::instance()->cppCodeStyle();
378         if (originalCppCodeStylePreferences->codeStyleSettings() != m_pageCppCodeStylePreferences->codeStyleSettings()) {
379             originalCppCodeStylePreferences->setCodeStyleSettings(m_pageCppCodeStylePreferences->codeStyleSettings());
380             originalCppCodeStylePreferences->toSettings(QLatin1String(CppTools::Constants::CPP_SETTINGS_ID), s);
381         }
382         if (originalCppCodeStylePreferences->tabSettings() != m_pageCppCodeStylePreferences->tabSettings()) {
383             originalCppCodeStylePreferences->setTabSettings(m_pageCppCodeStylePreferences->tabSettings());
384             originalCppCodeStylePreferences->toSettings(QLatin1String(CppTools::Constants::CPP_SETTINGS_ID), s);
385         }
386         if (originalCppCodeStylePreferences->currentDelegate() != m_pageCppCodeStylePreferences->currentDelegate()) {
387             originalCppCodeStylePreferences->setCurrentDelegate(m_pageCppCodeStylePreferences->currentDelegate());
388             originalCppCodeStylePreferences->toSettings(QLatin1String(CppTools::Constants::CPP_SETTINGS_ID), s);
389         }
390 
391         m_widget->apply();
392     }
393 }
394 
finish()395 void CppCodeStyleSettingsPage::finish()
396 {
397     delete m_widget;
398 }
399 
400 } // namespace Internal
401 } // namespace CppTools
402