diff --git a/src/plugins/cpptools/cppcodecompletion.cpp b/src/plugins/cpptools/cppcodecompletion.cpp
index 733671fa51507155aaaf152adef324cbc51f471c..9ea8d987dc17d1060eb16193bb659a328b951d86 100644
--- a/src/plugins/cpptools/cppcodecompletion.cpp
+++ b/src/plugins/cpptools/cppcodecompletion.cpp
@@ -1723,7 +1723,33 @@ QList<TextEditor::CompletionItem> CppCodeCompletion::getCompletions()
     return completionItems;
 }
 
-void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
+bool CppCodeCompletion::typedCharCompletes(const TextEditor::CompletionItem &item, QChar typedChar)
+{
+    if (item.data.canConvert<QString>()) // snippet
+        return false;
+
+    if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT)
+        return typedChar == QLatin1Char('(')
+                || typedChar == QLatin1Char(',');
+
+    if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL)
+        return typedChar == QLatin1Char('/')
+                && item.text.endsWith(QLatin1Char('/'));
+
+    if (item.data.value<Symbol *>())
+        return typedChar == QLatin1Char(':')
+                || typedChar == QLatin1Char(';')
+                || typedChar == QLatin1Char('.')
+                || typedChar == QLatin1Char(',')
+                || typedChar == QLatin1Char('(');
+
+    if (item.data.canConvert<CompleteFunctionDeclaration>())
+        return typedChar == QLatin1Char('(');
+
+    return false;
+}
+
+void CppCodeCompletion::complete(const TextEditor::CompletionItem &item, QChar typedChar)
 {
     Symbol *symbol = 0;
 
@@ -1749,10 +1775,15 @@ void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
     if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
         toInsert = item.text;
         extraChars += QLatin1Char(')');
+
+        if (typedChar == QLatin1Char('(')) // Eat the opening parenthesis
+            typedChar = QChar();
     } else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL) {
         toInsert = item.text;
         if (!toInsert.endsWith(QLatin1Char('/')))
             extraChars += QLatin1Char((m_completionOperator == T_ANGLE_STRING_LITERAL) ? '>' : '"');
+        else if (typedChar == QLatin1Char('/')) // Eat the slash
+            typedChar = QChar();
     } else {
         toInsert = item.text;
 
@@ -1768,7 +1799,7 @@ void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
                 if (! function->hasReturnType() && (function->identity() && !function->identity()->isDestructorNameId())) {
                     // Don't insert any magic, since the user might have just wanted to select the class
 
-                } else if (function->templateParameterCount() != 0) {
+                } else if (function->templateParameterCount() != 0 && typedChar != QLatin1Char('(')) {
                     // If there are no arguments, then we need the template specification
                     if (function->argumentCount() == 0) {
                         extraChars += QLatin1Char('<');
@@ -1777,32 +1808,50 @@ void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
                     if (completionSettings().m_spaceAfterFunctionName)
                         extraChars += QLatin1Char(' ');
                     extraChars += QLatin1Char('(');
+                    if (typedChar == QLatin1Char('('))
+                        typedChar = QChar();
 
                     // If the function doesn't return anything, automatically place the semicolon,
                     // unless we're doing a scope completion (then it might be function definition).
-                    bool endWithSemicolon = function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON;
+                    const QChar characterAtCursor = m_editor->characterAt(m_editor->position());
+                    bool endWithSemicolon = typedChar == QLatin1Char(';')
+                            || (function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON);
+                    const QChar semicolon = typedChar.isNull() ? QLatin1Char(';') : typedChar;
+
+                    if (endWithSemicolon && characterAtCursor == semicolon) {
+                        endWithSemicolon = false;
+                        typedChar = QChar();
+                    }
 
                     // If the function takes no arguments, automatically place the closing parenthesis
                     if (item.duplicateCount == 0 && ! function->hasArguments()) {
                         extraChars += QLatin1Char(')');
-                        if (endWithSemicolon)
-                            extraChars += QLatin1Char(';');
+                        if (endWithSemicolon) {
+                            extraChars += semicolon;
+                            typedChar = QChar();
+                        }
                     } else if (autoParenthesesEnabled) {
                         const QChar lookAhead = m_editor->characterAt(m_editor->position() + 1);
                         if (MatchingText::shouldInsertMatchingText(lookAhead)) {
                             extraChars += QLatin1Char(')');
                             --cursorOffset;
                             if (endWithSemicolon) {
-                                extraChars += QLatin1Char(';');
+                                extraChars += semicolon;
                                 --cursorOffset;
+                                typedChar = QChar();
                             }
                         }
+                        // TODO: When an opening parenthesis exists, the "semicolon" should really be
+                        // inserted after the matching closing parenthesis.
                     }
                 }
             }
         }
 
         if (autoInsertBrackets && item.data.canConvert<CompleteFunctionDeclaration>()) {
+            if (typedChar == QLatin1Char('('))
+                typedChar = QChar();
+
             // everything from the closing parenthesis on are extra chars, to
             // make sure an auto-inserted ")" gets replaced by ") const" if necessary
             int closingParen = toInsert.lastIndexOf(QLatin1Char(')'));
@@ -1811,6 +1860,13 @@ void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
         }
     }
 
+    // Append an unhandled typed character, adjusting cursor offset when it had been adjusted before
+    if (!typedChar.isNull()) {
+        extraChars += typedChar;
+        if (cursorOffset != 0)
+            --cursorOffset;
+    }
+
     // Avoid inserting characters that are already there
     for (int i = 0; i < extraChars.length(); ++i) {
         const QChar a = extraChars.at(i);
@@ -1836,7 +1892,7 @@ bool CppCodeCompletion::partiallyComplete(const QList<TextEditor::CompletionItem
     if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
         return false;
     } else if (completionItems.count() == 1) {
-        complete(completionItems.first());
+        complete(completionItems.first(), QChar());
         return true;
     } else if (m_completionOperator != T_LPAREN) {
         return TextEditor::ICompletionCollector::partiallyComplete(completionItems);
diff --git a/src/plugins/cpptools/cppcodecompletion.h b/src/plugins/cpptools/cppcodecompletion.h
index 513b038407a969fa04edfd87d69389c97112f8fc..406d4a2f872cfe0fce51e942fde1f81491529b01 100644
--- a/src/plugins/cpptools/cppcodecompletion.h
+++ b/src/plugins/cpptools/cppcodecompletion.h
@@ -78,7 +78,8 @@ public:
     int startCompletion(TextEditor::ITextEditable *editor);
     void completions(QList<TextEditor::CompletionItem> *completions);
 
-    void complete(const TextEditor::CompletionItem &item);
+    bool typedCharCompletes(const TextEditor::CompletionItem &item, QChar typedChar);
+    void complete(const TextEditor::CompletionItem &item, QChar typedChar);
     bool partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems);
     void cleanup();
 
diff --git a/src/plugins/qmljseditor/qmljscodecompletion.cpp b/src/plugins/qmljseditor/qmljscodecompletion.cpp
index 1bd3c77a52fc4fd020ff7170400a0c226a72ac9a..5f28d8acac37444e04447a8494f8aaeeb2e5e534 100644
--- a/src/plugins/qmljseditor/qmljscodecompletion.cpp
+++ b/src/plugins/qmljseditor/qmljscodecompletion.cpp
@@ -878,8 +878,19 @@ void CodeCompletion::completions(QList<TextEditor::CompletionItem> *completions)
     }
 }
 
-void CodeCompletion::complete(const TextEditor::CompletionItem &item)
+bool CodeCompletion::typedCharCompletes(const TextEditor::CompletionItem &item, QChar typedChar)
 {
+    if (item.data.canConvert<QString>()) // snippet
+        return false;
+
+    return (item.text.endsWith(QLatin1String(": ")) && typedChar == QLatin1Char(':'))
+            || (item.text.endsWith(QLatin1Char('.')) && typedChar == QLatin1Char('.'));
+}
+
+void CodeCompletion::complete(const TextEditor::CompletionItem &item, QChar typedChar)
+{
+    Q_UNUSED(typedChar) // Currently always included in the completion item when used
+
     QString toInsert = item.text;
 
     if (QmlJSTextEditor *edit = qobject_cast<QmlJSTextEditor *>(m_editor->widget())) {
@@ -895,7 +906,7 @@ void CodeCompletion::complete(const TextEditor::CompletionItem &item)
     QString replacableChars;
     if (toInsert.endsWith(QLatin1String(": ")))
         replacableChars = QLatin1String(": ");
-    else if (toInsert.endsWith(QLatin1String(".")))
+    else if (toInsert.endsWith(QLatin1Char('.')))
         replacableChars = QLatin1String(".");
 
     int replacedLength = 0;
@@ -924,7 +935,7 @@ bool CodeCompletion::partiallyComplete(const QList<TextEditor::CompletionItem> &
         const TextEditor::CompletionItem item = completionItems.first();
 
         if (!item.data.canConvert<QString>()) {
-            complete(item);
+            complete(item, QChar());
             return true;
         }
     }
diff --git a/src/plugins/qmljseditor/qmljscodecompletion.h b/src/plugins/qmljseditor/qmljscodecompletion.h
index 0cae652ad2cbab19d6548716f63801386a0ae56e..f435badf24dddce3aa939af05f3950301a3e789e 100644
--- a/src/plugins/qmljseditor/qmljscodecompletion.h
+++ b/src/plugins/qmljseditor/qmljscodecompletion.h
@@ -68,7 +68,8 @@ public:
     virtual bool triggersCompletion(TextEditor::ITextEditable *editor);
     virtual int startCompletion(TextEditor::ITextEditable *editor);
     virtual void completions(QList<TextEditor::CompletionItem> *completions);
-    virtual void complete(const TextEditor::CompletionItem &item);
+    virtual bool typedCharCompletes(const TextEditor::CompletionItem &item, QChar typedChar);
+    virtual void complete(const TextEditor::CompletionItem &item, QChar typedChar);
     virtual bool partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems);
     virtual QList<TextEditor::CompletionItem> getCompletions();
 
diff --git a/src/plugins/texteditor/completionsupport.cpp b/src/plugins/texteditor/completionsupport.cpp
index 3b06580a7412204c41d85ab5eacdd752172f314c..bd6cc6c12ec9227307259a095d9da6ddabc4b6ce 100644
--- a/src/plugins/texteditor/completionsupport.cpp
+++ b/src/plugins/texteditor/completionsupport.cpp
@@ -67,7 +67,7 @@ CompletionSupport::CompletionSupport()
 
 void CompletionSupport::performCompletion(const CompletionItem &item)
 {
-    item.collector->complete(item);
+    item.collector->complete(item, m_completionList->typedChar());
     m_checkCompletionTrigger = true;
 }
 
diff --git a/src/plugins/texteditor/completionwidget.cpp b/src/plugins/texteditor/completionwidget.cpp
index c8a6eae08a47d0056f3ee31ae0fb07daea225102..5b005c17b07f16e141b6e2047c92e0f001829ca6 100644
--- a/src/plugins/texteditor/completionwidget.cpp
+++ b/src/plugins/texteditor/completionwidget.cpp
@@ -199,6 +199,11 @@ void CompletionWidget::showCompletions(int startPos)
     setFocus();
 }
 
+QChar CompletionWidget::typedChar() const
+{
+    return m_completionListView->m_typedChar;
+}
+
 void CompletionWidget::updatePositionAndSize(int startPos)
 {
     // Determine size by calculating the space of the visible items
@@ -415,6 +420,16 @@ bool CompletionListView::event(QEvent *e)
         }
 
         if (forwardKeys && ! m_quickFix) {
+            if (ke->text().length() == 1 && currentIndex().isValid() && qApp->focusWidget() == this) {
+                QChar typedChar = ke->text().at(0);
+                const CompletionItem &item = m_model->itemAt(currentIndex());
+                if (item.collector->typedCharCompletes(item, typedChar)) {
+                    m_typedChar = typedChar;
+                    m_completionWidget->closeList(currentIndex());
+                    return true;
+                }
+            }
+
             m_blockFocusOut = true;
             QApplication::sendEvent(m_editorWidget, e);
             m_blockFocusOut = false;
diff --git a/src/plugins/texteditor/completionwidget.h b/src/plugins/texteditor/completionwidget.h
index ece02904b115bb125910bc3149f903bcb697e378..813fd89f3071842b5246ebcd0bc76b507323dd75 100644
--- a/src/plugins/texteditor/completionwidget.h
+++ b/src/plugins/texteditor/completionwidget.h
@@ -61,6 +61,8 @@ public:
     void setCompletionItems(const QList<TextEditor::CompletionItem> &completionitems);
     void showCompletions(int startPos);
 
+    QChar typedChar() const;
+
 signals:
     void itemSelected(const TextEditor::CompletionItem &item);
     void completionListClosed();
@@ -115,6 +117,7 @@ private:
     CompletionSupport *m_support;
     QPointer<CompletionInfoFrame> m_infoFrame;
     QTimer m_infoTimer;
+    QChar m_typedChar;
 };
 
 } // namespace Internal
diff --git a/src/plugins/texteditor/icompletioncollector.h b/src/plugins/texteditor/icompletioncollector.h
index 354e45c75db9d3567a8954be57f91fcdb6ae353e..dc8549635b7a00d929485c319b564586ad18b560 100644
--- a/src/plugins/texteditor/icompletioncollector.h
+++ b/src/plugins/texteditor/icompletioncollector.h
@@ -111,9 +111,19 @@ public:
      */
     virtual void completions(QList<CompletionItem> *completions) = 0;
 
-    /* This method should complete the given completion item.
+    /**
+     * This method should return true when the given typed character should cause
+     * the selected completion item to be completed.
      */
-    virtual void complete(const CompletionItem &item) = 0;
+    virtual bool typedCharCompletes(const CompletionItem &item, QChar typedChar) = 0;
+
+    /**
+     * This method should complete the given completion item.
+     *
+     * \param typedChar Non-null when completion was triggered by typing a
+     *                  character. Possible values depend on typedCharCompletes()
+     */
+    virtual void complete(const CompletionItem &item, QChar typedChar) = 0;
 
     /* This method gives the completion collector a chance to partially complete
      * based on a set of items. The general use case is to complete the common
@@ -153,6 +163,17 @@ public:
     IQuickFixCollector(QObject *parent = 0) : ICompletionCollector(parent) {}
     virtual ~IQuickFixCollector() {}
 
+    virtual bool typedCharCompletes(const CompletionItem &, QChar)
+    { return false; }
+
+    virtual void fix(const TextEditor::CompletionItem &item) = 0;
+
+    virtual void complete(const CompletionItem &item, QChar typedChar)
+    {
+        Q_UNUSED(typedChar)
+        fix(item);
+    }
+
     virtual bool triggersCompletion(TextEditor::ITextEditable *)
     { return false; }
 
diff --git a/src/plugins/texteditor/quickfix.cpp b/src/plugins/texteditor/quickfix.cpp
index 9918ad4f5b4d192b983437de001e89b90db76a6a..8e5e024750e9c305d19f9b16e61002b09cd2d59f 100644
--- a/src/plugins/texteditor/quickfix.cpp
+++ b/src/plugins/texteditor/quickfix.cpp
@@ -171,7 +171,7 @@ void QuickFixCollector::completions(QList<TextEditor::CompletionItem> *quickFixI
     }
 }
 
-void QuickFixCollector::complete(const TextEditor::CompletionItem &item)
+void QuickFixCollector::fix(const TextEditor::CompletionItem &item)
 {
     const int index = item.data.toInt();
 
diff --git a/src/plugins/texteditor/quickfix.h b/src/plugins/texteditor/quickfix.h
index fd731c6c5a1533bf93b0832eeb4acc85ca7268d8..2ad179e0ac9d19e5420ba572b1381b334bb36fad 100644
--- a/src/plugins/texteditor/quickfix.h
+++ b/src/plugins/texteditor/quickfix.h
@@ -111,7 +111,7 @@ public:
     virtual bool triggersCompletion(TextEditor::ITextEditable *editor);
     virtual int startCompletion(TextEditor::ITextEditable *editor);
     virtual void completions(QList<TextEditor::CompletionItem> *completions);
-    virtual void complete(const TextEditor::CompletionItem &item);
+    virtual void fix(const TextEditor::CompletionItem &item);
     virtual void cleanup();
 
     virtual TextEditor::QuickFixState *initializeCompletion(TextEditor::ITextEditable *editable) = 0;