TextEditor: Make threshold for automatic completion configurable

Fixes: QTCREATORBUG-19920
Change-Id: Id6be79485b1fb8c2ab4cce2706816949ae5e217b
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Kandeler
2020-08-03 16:19:19 +02:00
parent 7576ead1dd
commit 760d91665f
11 changed files with 214 additions and 136 deletions

View File

@@ -43,6 +43,7 @@
#include <texteditor/codeassist/functionhintproposal.h>
#include <texteditor/codeassist/genericproposal.h>
#include <texteditor/codeassist/ifunctionhintproposalmodel.h>
#include <texteditor/texteditorsettings.h>
#include <cplusplus/BackwardsScanner.h>
#include <cplusplus/ExpressionUnderCursor.h>
@@ -405,11 +406,11 @@ bool ClangCompletionAssistProcessor::accepts() const
return true;
} else {
// Trigger completion after three characters of a name have been typed, when not editing an existing name
// Trigger completion after n characters of a name have been typed, when not editing an existing name
QChar characterUnderCursor = m_interface->characterAt(pos);
if (!characterUnderCursor.isLetterOrNumber() && characterUnderCursor != QLatin1Char('_')) {
const int startOfName = findStartOfName(pos);
if (pos - startOfName >= 3) {
if (pos - startOfName >= TextEditorSettings::completionSettings().m_characterThreshold) {
const QChar firstCharacter = m_interface->characterAt(startOfName);
if (firstCharacter.isLetter() || firstCharacter == QLatin1Char('_')) {
// Finally check that we're not inside a comment or string (code copied from startOfOperator)

View File

@@ -851,12 +851,12 @@ bool InternalCppCompletionAssistProcessor::accepts() const
return true;
} else {
// Trigger completion after three characters of a name have been typed, when not editing an existing name
// Trigger completion after n characters of a name have been typed, when not editing an existing name
QChar characterUnderCursor = m_interface->characterAt(pos);
if (!isValidIdentifierChar(characterUnderCursor)) {
const int startOfName = findStartOfName(pos);
if (pos - startOfName >= 3) {
if (pos - startOfName >= TextEditorSettings::completionSettings().m_characterThreshold) {
const QChar firstCharacter = m_interface->characterAt(startOfName);
if (isValidFirstIdentifierChar(firstCharacter)) {
// Finally check that we're not inside a comment or string (code copied from startOfOperator)

View File

@@ -40,6 +40,7 @@
#include <texteditor/codeassist/genericproposalmodel.h>
#include <texteditor/codeassist/genericproposal.h>
#include <texteditor/codeassist/functionhintproposal.h>
#include <texteditor/texteditorsettings.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/Icons.h>
@@ -528,7 +529,8 @@ bool GlslCompletionAssistProcessor::acceptsIdleEditor() const
++pos;
const QString word = m_interface->textAt(pos, cursorPosition - pos);
if (word.length() > 2 && checkStartOfIdentifier(word)) {
if (word.length() >= TextEditorSettings::completionSettings().m_characterThreshold
&& checkStartOfIdentifier(word)) {
for (auto character : word) {
if (!isIdentifierChar(character))
return false;

View File

@@ -34,6 +34,7 @@
#include <texteditor/codeassist/iassistprocessor.h>
#include <texteditor/codeassist/genericproposal.h>
#include <texteditor/codeassist/genericproposalmodel.h>
#include <texteditor/texteditorsettings.h>
#include <utils/algorithm.h>
#include <utils/textutils.h>
#include <utils/utilsicons.h>
@@ -314,13 +315,13 @@ IAssistProposal *LanguageClientCompletionAssistProcessor::perform(const AssistIn
QTC_ASSERT(m_client, return nullptr);
m_pos = interface->position();
if (interface->reason() == IdleEditor) {
// Trigger an automatic completion request only when we are on a word with more than 2 "identifier" character
// Trigger an automatic completion request only when we are on a word with at least n "identifier" characters
const QRegularExpression regexp("[_a-zA-Z0-9]+");
auto hasMatch = [&regexp](const QString &txt) { return regexp.match(txt).hasMatch(); };
int delta = 0;
while (m_pos - delta > 0 && hasMatch(interface->textAt(m_pos - delta - 1, delta + 1)))
++delta;
if (delta < 3)
if (delta < TextEditorSettings::completionSettings().m_characterThreshold)
return nullptr;
if (m_client->documentUpdatePostponed(interface->fileName())) {
m_postponedUpdateConnection

View File

@@ -891,7 +891,8 @@ bool QmlJSCompletionAssistProcessor::acceptsIdleEditor() const
++startPos;
const QString &word = m_interface->textAt(startPos, cursorPos - startPos);
if (word.length() > 2 && isIdentifierChar(word.at(0), true)) {
if (word.length() >= TextEditorSettings::completionSettings().m_characterThreshold
&& isIdentifierChar(word.at(0), true)) {
for (int i = 1; i < word.length(); ++i) {
if (!isIdentifierChar(word.at(i)))
return false;

View File

@@ -31,6 +31,8 @@
#include "genericproposalmodel.h"
#include "iassistprocessor.h"
#include "../snippets/snippetassistcollector.h"
#include "../completionsettings.h"
#include "../texteditorsettings.h"
#include <utils/algorithm.h>
#include <utils/runextensions.h>
@@ -127,9 +129,11 @@ IAssistProposal *DocumentContentCompletionProcessor::perform(const AssistInterfa
if (interface->reason() == IdleEditor) {
QChar characterUnderCursor = interface->characterAt(interface->position());
if (characterUnderCursor.isLetterOrNumber() || length < 3)
if (characterUnderCursor.isLetterOrNumber()
|| length < TextEditorSettings::completionSettings().m_characterThreshold) {
return nullptr;
}
}
const QString wordUnderCursor = interface->textAt(pos, length);
const QString text = interface->textDocument()->toPlainText();

View File

@@ -195,7 +195,8 @@ IAssistProposal *KeywordsCompletionAssistProcessor::perform(const AssistInterfac
if (interface->reason() == IdleEditor) {
QChar characterUnderCursor = interface->characterAt(interface->position());
if (characterUnderCursor.isLetterOrNumber() || interface->position() - startPosition < 3) {
if (characterUnderCursor.isLetterOrNumber() || interface->position() - startPosition
< TextEditorSettings::completionSettings().m_characterThreshold) {
QList<AssistProposalItemInterface *> items;
if (m_dynamicCompletionFunction)
m_dynamicCompletionFunction(interface, &items, startPosition);

View File

@@ -31,6 +31,7 @@ static const char settingsGroup[] = "CppTools/Completion";
static const char caseSensitivityKey[] = "CaseSensitivity";
static const char completionTriggerKey[] = "CompletionTrigger";
static const char automaticProposalTimeoutKey[] = "AutomaticProposalTimeout";
static const char characterThresholdKey[] = "CharacterThreshold";
static const char autoInsertBracesKey[] = "AutoInsertBraces";
static const char surroundingAutoBracketsKey[] = "SurroundingAutoBrackets";
static const char autoInsertQuotesKey[] = "AutoInsertQuotes";
@@ -52,6 +53,7 @@ void CompletionSettings::toSettings(QSettings *s) const
s->setValue(caseSensitivityKey, (int) m_caseSensitivity);
s->setValue(completionTriggerKey, (int) m_completionTrigger);
s->setValue(automaticProposalTimeoutKey, m_automaticProposalTimeoutInMs);
s->setValue(characterThresholdKey, m_characterThreshold);
s->setValue(autoInsertBracesKey, m_autoInsertBrackets);
s->setValue(surroundingAutoBracketsKey, m_surroundingAutoBrackets);
s->setValue(autoInsertQuotesKey, m_autoInsertQuotes);
@@ -78,6 +80,8 @@ void CompletionSettings::fromSettings(QSettings *s)
s->value(completionTriggerKey, m_completionTrigger).toInt();
m_automaticProposalTimeoutInMs =
s->value(automaticProposalTimeoutKey, m_automaticProposalTimeoutInMs).toInt();
m_characterThreshold =
s->value(characterThresholdKey, m_characterThreshold).toInt();
m_autoInsertBrackets =
s->value(autoInsertBracesKey, m_autoInsertBrackets).toBool();
m_surroundingAutoBrackets =
@@ -110,6 +114,7 @@ bool CompletionSettings::equals(const CompletionSettings &cs) const
return m_caseSensitivity == cs.m_caseSensitivity
&& m_completionTrigger == cs.m_completionTrigger
&& m_automaticProposalTimeoutInMs == cs.m_automaticProposalTimeoutInMs
&& m_characterThreshold == cs.m_characterThreshold
&& m_autoInsertBrackets == cs.m_autoInsertBrackets
&& m_surroundingAutoBrackets == cs.m_surroundingAutoBrackets
&& m_autoInsertQuotes == cs.m_autoInsertQuotes

View File

@@ -59,6 +59,7 @@ public:
CaseSensitivity m_caseSensitivity = CaseInsensitive;
CompletionTrigger m_completionTrigger = AutomaticCompletion;
int m_automaticProposalTimeoutInMs = 400;
int m_characterThreshold = 3;
bool m_autoInsertBrackets = true;
bool m_surroundingAutoBrackets = true;
bool m_autoInsertQuotes = true;

View File

@@ -100,6 +100,7 @@ CompletionSettingsPageWidget::CompletionSettingsPageWidget(CompletionSettingsPag
m_ui.completionTrigger->setCurrentIndex(completionTriggerIndex);
m_ui.automaticProposalTimeoutSpinBox
->setValue(m_owner->m_completionSettings.m_automaticProposalTimeoutInMs);
m_ui.thresholdSpinBox->setValue(m_owner->m_completionSettings.m_characterThreshold);
m_ui.insertBrackets->setChecked(m_owner->m_completionSettings.m_autoInsertBrackets);
m_ui.surroundBrackets->setChecked(m_owner->m_completionSettings.m_surroundingAutoBrackets);
m_ui.insertQuotes->setChecked(m_owner->m_completionSettings.m_autoInsertQuotes);
@@ -173,6 +174,7 @@ void CompletionSettingsPageWidget::settingsFromUi(CompletionSettings &completion
completion.m_completionTrigger = completionTrigger();
completion.m_automaticProposalTimeoutInMs
= m_ui.automaticProposalTimeoutSpinBox->value();
completion.m_characterThreshold = m_ui.thresholdSpinBox->value();
completion.m_autoInsertBrackets = m_ui.insertBrackets->isChecked();
completion.m_surroundingAutoBrackets = m_ui.surroundBrackets->isChecked();
completion.m_autoInsertQuotes = m_ui.insertQuotes->isChecked();

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>551</width>
<height>507</height>
<width>823</width>
<height>756</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
@@ -16,27 +16,9 @@
<property name="title">
<string>Behavior</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="completionTriggerLabel">
<property name="text">
<string>Activate completion:</string>
</property>
</widget>
</item>
<item row="0" column="2" colspan="4">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>70</width>
<height>24</height>
</size>
</property>
</spacer>
</item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="caseSensitivityLabel">
<property name="text">
@@ -48,6 +30,8 @@
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="caseSensitivity">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@@ -72,7 +56,31 @@
</item>
</widget>
</item>
<item row="1" column="1" colspan="2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="completionTriggerLabel">
<property name="text">
<string>Activate completion:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QComboBox" name="completionTrigger">
<item>
<property name="text">
@@ -91,14 +99,31 @@
</item>
</widget>
</item>
<item row="1" column="3">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="automaticProposalTimeoutLabel">
<property name="text">
<string>Timeout in ms:</string>
</property>
</widget>
</item>
<item row="1" column="4">
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QSpinBox" name="automaticProposalTimeoutSpinBox">
<property name="maximum">
<number>2000</number>
@@ -111,7 +136,7 @@
</property>
</widget>
</item>
<item row="1" column="5">
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@@ -119,12 +144,45 @@
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>24</height>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="thresholdLabel">
<property name="text">
<string>Character threshold:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QSpinBox" name="thresholdSpinBox">
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="partiallyComplete">
<property name="toolTip">
<string>Inserts the common prefix of available completion items.</string>
@@ -137,7 +195,7 @@
</property>
</widget>
</item>
<item row="3" column="0">
<item row="5" column="0">
<widget class="QCheckBox" name="autoSplitStrings">
<property name="toolTip">
<string>Splits a string into two lines by adding an end quote at the cursor position when you press Enter and a start quote to the next line, before the rest of the string.
@@ -150,6 +208,8 @@ In addition, Shift+Enter inserts an escape character at the cursor position and
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>