forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/4.8'
Conflicts: src/plugins/debugger/debuggeritem.cpp tests/unit/unittest/unittest.pro Change-Id: Id2e4e9c2bc87b2556d7c2845aea3fe2fa11b630b
This commit is contained in:
@@ -255,13 +255,9 @@ http://llvm.org/docs/GettingStarted.html#git-mirror:
|
||||
|
||||
1. Clone LLVM and checkout a suitable branch
|
||||
|
||||
git clone -b release_60-based https://code.qt.io/clang/llvm
|
||||
git clone -b release_70-based --recursive https://code.qt.io/clang/llvm
|
||||
|
||||
2. Clone Clang into llvm/tools/clang and checkout a suitable branch
|
||||
|
||||
git clone -b release_60-based https://code.qt.io/clang/clang llvm/tools/clang
|
||||
|
||||
3. Build and install LLVM/Clang
|
||||
2. Build and install LLVM/Clang
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
Qt Creator version 4.7.2 contains bug fixes.
|
||||
|
||||
The most important changes are listed in this document. For a complete
|
||||
list of changes, see the Git log for the Qt Creator sources that
|
||||
you can check out from the public Git repository. For example:
|
||||
|
||||
git clone git://code.qt.io/qt-creator/qt-creator.git
|
||||
git log --cherry-pick --pretty=oneline origin/v4.7.1..v4.7.2
|
||||
|
||||
Editing
|
||||
|
||||
* Fixed that collapsed text no longer showed up in tooltip (QTCREATORBUG-21040)
|
||||
* Fixed crash with generic text completion (QTCREATORBUG-21192)
|
||||
|
||||
C++ Support
|
||||
|
||||
* Fixed wrong value of `__cplusplus` define (QTCREATORBUG-20884)
|
||||
* Clang Code Model
|
||||
* Fixed possible crash in `Follow Symbol Under Cursor`
|
||||
* Fixed crash when using `Select Block Up/Down` with lambda
|
||||
(QTCREATORBUG-20994)
|
||||
|
||||
Debugging
|
||||
|
||||
* CDB
|
||||
* Fixed pretty printing of `std::vector` without Python (QTCREATORBUG-21074)
|
||||
|
||||
Platform Specific
|
||||
|
||||
Windows
|
||||
|
||||
* Fixed saving of files when another application blocks atomic save operation
|
||||
(QTCREATORBUG-7668)
|
||||
|
||||
Remote Linux
|
||||
|
||||
* Fixed superfluous empty lines in application output (QTCREATORBUG-19367)
|
||||
|
||||
Credits for these changes go to:
|
||||
David Schulz
|
||||
Eike Ziller
|
||||
Friedemann Kleint
|
||||
Hannes Domani
|
||||
Ivan Donchevskii
|
||||
Jonathan Liu
|
||||
Kai Köhne
|
||||
Nikolai Kosjar
|
||||
Sergey Belyashov
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -33,7 +33,7 @@
|
||||
\contentspage index.html
|
||||
\previouspage creator-heob.html
|
||||
\page creator-cpu-usage-analyzer.html
|
||||
\nextpage creator-autotest.html
|
||||
\nextpage creator-cppcheck.html
|
||||
|
||||
\title Analyzing CPU Usage
|
||||
|
||||
|
||||
@@ -81,6 +81,11 @@
|
||||
desktop applications with the Performance Analyzer (commercial only)
|
||||
that integrates the Linux Perf tool.
|
||||
|
||||
\li \l{Analyzing Code with Cppcheck}
|
||||
|
||||
You can use the experimental Cppcheck plugin to detect undefined
|
||||
behavior and dangerous coding constructs.
|
||||
|
||||
\endlist
|
||||
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the Qt Creator documentation.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Free Documentation License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Free
|
||||
** Documentation License version 1.3 as published by the Free Software
|
||||
** Foundation and appearing in the file included in the packaging of
|
||||
** this file. Please review the following information to ensure
|
||||
** the GNU Free Documentation License version 1.3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
/*!
|
||||
\contentspage index.html
|
||||
\previouspage creator-cpu-usage-analyzer.html
|
||||
\page creator-cppcheck.html
|
||||
\nextpage creator-autotest.html
|
||||
|
||||
\title Analyzing Code with Cppcheck
|
||||
|
||||
\l{http://cppcheck.sourceforge.net/}{Cppcheck} is a static analysis tool
|
||||
that detects errors in C++ code. Static analysis is performed on the source
|
||||
code without actually executing the application.
|
||||
|
||||
The experimental Cppcheck Diagnostics plugin integrates diagnostics
|
||||
that are generated by the Cppcheck tool into the C++ editor.
|
||||
|
||||
To enable running Cppcheck automatically on currently open files:
|
||||
|
||||
\list 1
|
||||
\li Select \uicontrol Help > \uicontrol {About Plugins} >
|
||||
\uicontrol {Code Analyzer} > \uicontrol Cppcheck to enable the
|
||||
plugin.
|
||||
\li Restart \QC to load the plugin.
|
||||
\li Select \uicontrol Tools > \uicontrol Options > \uicontrol Analyzer
|
||||
> \uicontrol Cppcheck to specify settings for running Cppcheck.
|
||||
\image qtcreator-cppcheck-options.png "Cppcheck options"
|
||||
\li In the \uicontrol Binary field, enter the path to the Cppcheck
|
||||
executable file.
|
||||
\li In the \uicontrol Checks group, select the checks to perform on
|
||||
currently open files.
|
||||
\note By default, Cppcheck uses multiple threads to perform checks.
|
||||
Selecting the \uicontrol {Unused functions} option disables the
|
||||
default behavior.
|
||||
\li In the \uicontrol {Custom arguments} field, enter additional
|
||||
arguments for running Cppcheck. The arguments might be shadowed
|
||||
by automatically generated ones. To avoid possible conflicts in
|
||||
configuration, select the \uicontrol {Show raw output} check box
|
||||
to see the final arguments.
|
||||
\li In the \uicontrol {Ignored file patterns} field, enter a filter
|
||||
for ignoring files that match the pattern (wildcard). You can enter
|
||||
multiple patterns separated by commas. Even though Cppcheck is not
|
||||
run on files that match the provided patterns, they might be
|
||||
implicitly checked if other files include them.
|
||||
\li Select the \uicontrol {Inconclusive errors} check box to also
|
||||
mark possible false positives.
|
||||
\li Select the \uicontrol {Check all define combinations} check box to
|
||||
check all define combinations. Enabling this option can significantly
|
||||
slow down analysis, but might help to find more issues.
|
||||
\li Select the \uicontrol {Add include paths} check box to pass the
|
||||
current project's include paths to Cppcheck. Enabling this option
|
||||
slows down checks on big projects, but can help Cppcheck to find
|
||||
missing includes.
|
||||
\li Select the \uicontrol {Calculate additional arguments} check box to
|
||||
calculate additional arguments based on current project's settings
|
||||
(such as the language used and standard version) and pass them to
|
||||
Cppcheck.
|
||||
\endlist
|
||||
|
||||
\QC automatically runs Cppcheck on currently opened documents and displays
|
||||
results via text marks or annotations.
|
||||
*/
|
||||
@@ -41,7 +41,9 @@
|
||||
|
||||
\title Coding
|
||||
|
||||
\if defined(qtcreator)
|
||||
\image creator_coding.png
|
||||
\endif
|
||||
|
||||
\list
|
||||
|
||||
@@ -89,6 +91,13 @@
|
||||
|
||||
\list
|
||||
|
||||
\li \l{Using Language Servers}
|
||||
|
||||
The experimental language client provides code completion,
|
||||
highlighting of the symbol under cursor, and jumping to the symbol
|
||||
definition for other programming languages besides C++. In addition,
|
||||
it integrates diagnostics from the language server.
|
||||
|
||||
\li \l{Editing MIME Types}
|
||||
|
||||
\QC uses the MIME type of a file to determine which mode and editor
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
\contentspage index.html
|
||||
\previouspage creator-editor-options-text.html
|
||||
\page creator-editor-fakevim.html
|
||||
\nextpage creator-mime-types.html
|
||||
\nextpage creator-language-servers.html
|
||||
|
||||
\title Using FakeVim Mode
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the Qt Creator documentation.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Free Documentation License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Free
|
||||
** Documentation License version 1.3 as published by the Free Software
|
||||
** Foundation and appearing in the file included in the packaging of
|
||||
** this file. Please review the following information to ensure
|
||||
** the GNU Free Documentation License version 1.3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
/*!
|
||||
\contentspage index.html
|
||||
\previouspage creator-editor-fakevim.html
|
||||
\page creator-language-servers.html
|
||||
\nextpage creator-mime-types.html
|
||||
|
||||
\title Using Language Servers
|
||||
|
||||
For several programming languages, a \e {language server} is available
|
||||
that provides information about the code to IDEs as long as they support
|
||||
communication via the language server protocol (LSP). This enables the
|
||||
IDE to provide code completion, highlighting of the symbol under cursor,
|
||||
and jumping to the symbol definition, as well as to integrate diagnostics
|
||||
from the language server.
|
||||
|
||||
By providing a client for the language server protocol, \QC can support
|
||||
the above features for several other programming languages besides C++.
|
||||
However, the experimental client does support language servers that
|
||||
require special handling.
|
||||
|
||||
\QC uses the \l{https://www.iana.org/assignments/media-types/media-types.xhtml}
|
||||
{MIME type} of the file to determine which language server to start when you
|
||||
open a file for editing. Add new MIME types as filters for language servers.
|
||||
For more information about how \QC uses MIME types, see
|
||||
\l {Editing MIME Types}.
|
||||
|
||||
The experimental language service client has been mostly tested with Python.
|
||||
If problems arise when you try some other language, please select
|
||||
\uicontrol Help > \uicontrol {Report Bug} to report them in the Qt Bug
|
||||
Tracker. The reports should include \QC console output with the environment
|
||||
variable \c {QT_LOGGING_RULES=qtc.languageclient.*=true} set.
|
||||
|
||||
To use a language server:
|
||||
|
||||
\list 1
|
||||
|
||||
\li Enable the language server client by selecting \uicontrol Help >
|
||||
\uicontrol {About Plugins} > \uicontrol {Other Languages} >
|
||||
\uicontrol {Language Client} (or \uicontrol {Qt Creator} >
|
||||
\uicontrol {About Plugins} > \uicontrol {Other Languages} >
|
||||
\uicontrol {Language Client} on \macos).
|
||||
\li Restart \QC to load the language client plugin.
|
||||
\li Select \uicontrol Tools > \uicontrol Options >
|
||||
\uicontrol {Language Client} > \uicontrol Add (or
|
||||
\uicontrol {Qt Creator} > \uicontrol {Language Client} >
|
||||
\uicontrol Add on \macos) to add language servers.
|
||||
\li In the \uicontrol Name field, enter a name for the language server.
|
||||
\li Select the \uicontrol Enabled check box.
|
||||
\li In the \uicontrol {MIME Type} field, enter the filter for files that
|
||||
the lanaguage server will handle. When you start typing the filter
|
||||
name, a list of predefined filters appears where you can select a
|
||||
filter.
|
||||
\li In the \uicontrol Executable field, enter the path to the language
|
||||
server executable.
|
||||
\li In the \uicontrol Arguments field, enter any required command line
|
||||
arguments.
|
||||
\endlist
|
||||
*/
|
||||
@@ -31,16 +31,16 @@
|
||||
|
||||
/*!
|
||||
\contentspage index.html
|
||||
\previouspage creator-editor-fakevim.html
|
||||
\previouspage creator-language-servers.html
|
||||
\page creator-mime-types.html
|
||||
\nextpage creator-modeling.html
|
||||
|
||||
\title Editing MIME Types
|
||||
|
||||
\QC uses the \l{http://en.wikipedia.org/wiki/Internet_media_type}
|
||||
{Internet media type} (MIME type) of the file to determine which mode and
|
||||
editor to use for opening the file. For example, \QC opens C++ source and
|
||||
header files in the C++ editor, and Qt widget based UI files (.ui) in \QD.
|
||||
\QC uses the \l{https://www.iana.org/assignments/media-types/media-types.xhtml}
|
||||
{MIME type} of the file to determine which mode and editor to use for
|
||||
opening the file. For example, \QC opens C++ source and header files in
|
||||
the C++ editor, and Qt widget based UI files (.ui) in \QD.
|
||||
|
||||
To identify the MIME type of a file, \QC uses matching by pattern and
|
||||
matching by contents. First, \QC looks at the filename to check whether it
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
/*!
|
||||
\contentspage index.html
|
||||
\previouspage creator-cpu-usage-analyzer.html
|
||||
\previouspage creator-cppcheck.html
|
||||
\page creator-autotest.html
|
||||
\nextpage creator-advanced.html
|
||||
|
||||
|
||||
@@ -141,6 +141,7 @@
|
||||
\li \l{Specifying Text Editor Settings}
|
||||
\li \l{Using FakeVim Mode}
|
||||
\endlist
|
||||
\li \l{Using Language Servers}
|
||||
\li \l{Editing MIME Types}
|
||||
\li \l{Modeling}
|
||||
\li \l{Editing State Charts}
|
||||
@@ -191,6 +192,7 @@
|
||||
\li \l{Using Clang Tools}
|
||||
\li \l{Detecting Memory Leaks with Heob}
|
||||
\li \l{Analyzing CPU Usage}
|
||||
\li \l{Analyzing Code with Cppcheck}
|
||||
\endlist
|
||||
\li \l{Running Autotests}
|
||||
\endlist
|
||||
|
||||
@@ -107,24 +107,24 @@ function formattingLibs(llvmConfig, qtcFunctions, targetOS)
|
||||
var libs = []
|
||||
if (qtcFunctions.versionIsAtLeast(clangVersion, MinimumLLVMVersion)) {
|
||||
if (qtcFunctions.versionIsAtLeast(clangVersion, "7.0.0")) {
|
||||
libs.concat([
|
||||
libs.push(
|
||||
"clangFormat",
|
||||
"clangToolingInclusions",
|
||||
"clangToolingCore",
|
||||
"clangRewrite",
|
||||
"clangLex",
|
||||
"clangBasic",
|
||||
]);
|
||||
"clangBasic"
|
||||
);
|
||||
} else {
|
||||
libs.concat([
|
||||
libs.push(
|
||||
"clangFormat",
|
||||
"clangToolingCore",
|
||||
"clangRewrite",
|
||||
"clangLex",
|
||||
"clangBasic",
|
||||
]);
|
||||
"clangBasic"
|
||||
);
|
||||
}
|
||||
libs.concat(extraLibraries(llvmConfig, targetOS));
|
||||
libs = libs.concat(extraLibraries(llvmConfig, targetOS));
|
||||
}
|
||||
|
||||
return libs;
|
||||
|
||||
@@ -230,7 +230,10 @@ class Dumper(DumperBase):
|
||||
return None
|
||||
|
||||
def parseAndEvaluate(self, exp):
|
||||
return self.fromNativeValue(cdbext.parseAndEvaluate(exp))
|
||||
return self.fromNativeValue(self.nativeParseAndEvaluate(exp))
|
||||
|
||||
def nativeParseAndEvaluate(self, exp):
|
||||
return cdbext.parseAndEvaluate(exp)
|
||||
|
||||
def isWindowsTarget(self):
|
||||
return True
|
||||
|
||||
@@ -337,6 +337,10 @@ class DumperBase:
|
||||
#warn('EXPANDED INAMES: %s' % self.expandedINames)
|
||||
#warn('WATCHERS: %s' % self.watchers)
|
||||
|
||||
def resetPerStepCaches(self):
|
||||
self.perStepCache = {}
|
||||
pass
|
||||
|
||||
def resetCaches(self):
|
||||
# This is a cache mapping from 'type name' to 'display alternatives'.
|
||||
self.qqFormats = { 'QVariant (QVariantMap)' : mapForms() }
|
||||
@@ -355,6 +359,14 @@ class DumperBase:
|
||||
# to not be QObject derived, it contains a 0 value.
|
||||
self.knownStaticMetaObjects = {}
|
||||
|
||||
# A dictionary to serve as a per debugging step cache.
|
||||
# Cleared on each step over / into / continue.
|
||||
self.perStepCache = {}
|
||||
|
||||
# A dictionary to serve as a general cache throughout the whole
|
||||
# debug session.
|
||||
self.generalCache = {}
|
||||
|
||||
self.counts = {}
|
||||
self.structPatternCache = {}
|
||||
self.pretimings = {}
|
||||
@@ -3670,6 +3682,9 @@ class DumperBase:
|
||||
error('wrong')
|
||||
return bytes(struct.pack(self.packCode + self.ptrCode(), address))
|
||||
|
||||
def fromPointerData(self, bytes_value):
|
||||
return struct.unpack(self.packCode + self.ptrCode(), bytes_value)
|
||||
|
||||
def createPointerValue(self, targetAddress, targetTypish):
|
||||
if not isinstance(targetTypish, self.Type) and not isinstance(targetTypish, str):
|
||||
error('Expected type in createPointerValue(), got %s'
|
||||
|
||||
@@ -708,14 +708,18 @@ class Dumper(DumperBase):
|
||||
self.reportResult(self.output)
|
||||
|
||||
def parseAndEvaluate(self, exp):
|
||||
val = self.nativeParseAndEvaluate(exp)
|
||||
return None if val is None else self.fromNativeValue(val)
|
||||
|
||||
def nativeParseAndEvaluate(self, exp):
|
||||
#warn('EVALUATE "%s"' % exp)
|
||||
try:
|
||||
val = gdb.parse_and_eval(exp)
|
||||
return val
|
||||
except RuntimeError as error:
|
||||
if self.passExceptions:
|
||||
warn("Cannot evaluate '%s': %s" % (exp, error))
|
||||
return None
|
||||
return self.fromNativeValue(val)
|
||||
|
||||
def callHelper(self, rettype, value, function, args):
|
||||
# args is a tuple.
|
||||
|
||||
@@ -1127,6 +1127,13 @@ class Dumper(DumperBase):
|
||||
|
||||
self.setVariableFetchingOptions(args)
|
||||
|
||||
# Reset certain caches whenever a step over / into / continue
|
||||
# happens.
|
||||
# FIXME: Caches are currently also cleared if currently
|
||||
# selected frame is changed, that shouldn't happen.
|
||||
if not self.partialVariable:
|
||||
self.resetPerStepCaches()
|
||||
|
||||
anyModule = self.target.GetModuleAtIndex(0)
|
||||
anySymbol = anyModule.GetSymbolAtIndex(0)
|
||||
self.fakeAddress = int(anySymbol.GetStartAddress())
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
############################################################################
|
||||
|
||||
from dumper import *
|
||||
import re
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
@@ -351,6 +352,171 @@ def qdump__WTF__String(d, value):
|
||||
d.putCharArrayHelper(bufferPtr, stringLength, charSize)
|
||||
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
# Python
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
def get_python_interpreter_major_version(d):
|
||||
key = 'python_interpreter_major_version'
|
||||
if key in d.generalCache:
|
||||
return d.generalCache[key]
|
||||
|
||||
e = "(char*)Py_GetVersion()"
|
||||
result = d.nativeParseAndEvaluate(e)
|
||||
result_str = str(result)
|
||||
matches = re.search(r'(\d+?)\.(\d+?)\.(\d+?)', result_str)
|
||||
if matches:
|
||||
result_str = matches.group(1)
|
||||
d.generalCache[key] = result_str
|
||||
return result_str
|
||||
|
||||
|
||||
def is_python_3(d):
|
||||
return get_python_interpreter_major_version(d) == '3'
|
||||
|
||||
|
||||
def repr_cache_decorator(namespace):
|
||||
def real_decorator(func_to_decorate):
|
||||
def wrapper(d, address):
|
||||
if namespace in d.perStepCache and address in d.perStepCache[namespace]:
|
||||
return d.perStepCache[namespace][address]
|
||||
|
||||
if namespace not in d.perStepCache:
|
||||
d.perStepCache[namespace] = {}
|
||||
|
||||
if address == 0:
|
||||
result_str = d.perStepCache[namespace][address] = "<nullptr>"
|
||||
return result_str
|
||||
|
||||
result = func_to_decorate(d, address)
|
||||
d.perStepCache[namespace][address] = result
|
||||
return result
|
||||
return wrapper
|
||||
return real_decorator
|
||||
|
||||
|
||||
@repr_cache_decorator('py_object')
|
||||
def get_py_object_repr_helper(d, address):
|
||||
# The code below is a long way to evaluate:
|
||||
# ((PyBytesObject *)PyUnicode_AsEncodedString(PyObject_Repr(
|
||||
# (PyObject*){}), \"utf-8\", \"backslashreplace\"))->ob_sval"
|
||||
# But with proper object cleanup.
|
||||
|
||||
e_decref = "Py_DecRef((PyObject *){})"
|
||||
|
||||
e = "PyObject_Repr((PyObject*){})"
|
||||
repr_object_value = d.parseAndEvaluate(e.format(address))
|
||||
repr_object_address = d.fromPointerData(repr_object_value.ldata)[0]
|
||||
|
||||
if is_python_3(d):
|
||||
# Try to get a UTF-8 encoded string from the repr object.
|
||||
e = "PyUnicode_AsEncodedString((PyObject*){}, \"utf-8\", \"backslashreplace\")"
|
||||
string_object_value = d.parseAndEvaluate(e.format(repr_object_address))
|
||||
string_object_address = d.fromPointerData(string_object_value.ldata)[0]
|
||||
|
||||
e = "(char*)(((PyBytesObject *){})->ob_sval)"
|
||||
result = d.nativeParseAndEvaluate(e.format(string_object_address))
|
||||
|
||||
# It's important to stringify the result before any other evaluations happen.
|
||||
result_str = str(result)
|
||||
|
||||
# Clean up.
|
||||
d.nativeParseAndEvaluate(e_decref.format(string_object_address))
|
||||
|
||||
else:
|
||||
# Retrieve non-unicode string.
|
||||
e = "(char*)(PyString_AsString((PyObject*){}))"
|
||||
result = d.nativeParseAndEvaluate(e.format(repr_object_address))
|
||||
|
||||
# It's important to stringify the result before any other evaluations happen.
|
||||
result_str = str(result)
|
||||
|
||||
# Do some string stripping.
|
||||
# FIXME when using cdb engine.
|
||||
matches = re.search(r'.+?"(.+)"$', result_str)
|
||||
if matches:
|
||||
result_str = matches.group(1)
|
||||
|
||||
# Clean up.
|
||||
d.nativeParseAndEvaluate(e_decref.format(repr_object_address))
|
||||
|
||||
return result_str
|
||||
|
||||
|
||||
@repr_cache_decorator('py_object_type')
|
||||
def get_py_object_type(d, object_address):
|
||||
e = "((PyObject *){})->ob_type"
|
||||
type_value = d.parseAndEvaluate(e.format(object_address))
|
||||
type_address = d.fromPointerData(type_value.ldata)[0]
|
||||
type_repr = get_py_object_repr_helper(d, type_address)
|
||||
return type_repr
|
||||
|
||||
|
||||
@repr_cache_decorator('py_object_meta_type')
|
||||
def get_py_object_meta_type(d, object_address):
|
||||
# The python3 object layout has a few more indirections.
|
||||
if is_python_3(d):
|
||||
e = "((PyObject *){})->ob_type->ob_base->ob_base->ob_type"
|
||||
else:
|
||||
e = "((PyObject *){})->ob_type->ob_type"
|
||||
type_value = d.parseAndEvaluate(e.format(object_address))
|
||||
type_address = d.fromPointerData(type_value.ldata)[0]
|
||||
type_repr = get_py_object_repr_helper(d, type_address)
|
||||
return type_repr
|
||||
|
||||
|
||||
@repr_cache_decorator('py_object_base_class')
|
||||
def get_py_object_base_class(d, object_address):
|
||||
e = "((PyObject *){})->ob_type->tp_base"
|
||||
base_value = d.parseAndEvaluate(e.format(object_address))
|
||||
base_address = d.fromPointerData(base_value.ldata)[0]
|
||||
base_repr = get_py_object_repr_helper(d, base_address)
|
||||
return base_repr
|
||||
|
||||
|
||||
def get_py_object_repr(d, value):
|
||||
address = value.address()
|
||||
repr_available = False
|
||||
try:
|
||||
result = get_py_object_repr_helper(d, address)
|
||||
d.putValue(d.hexencode(result), encoding='utf8')
|
||||
repr_available = True
|
||||
except:
|
||||
d.putEmptyValue()
|
||||
|
||||
def sub_item(name, functor, address):
|
||||
with SubItem(d, '[{}]'.format(name)):
|
||||
sub_value = functor(d, address)
|
||||
d.putValue(d.hexencode(sub_value), encoding='utf8')
|
||||
|
||||
d.putNumChild(1)
|
||||
if d.isExpanded():
|
||||
with Children(d):
|
||||
if repr_available:
|
||||
sub_item('class', get_py_object_type, address)
|
||||
sub_item('super class', get_py_object_base_class, address)
|
||||
sub_item('meta type', get_py_object_meta_type, address)
|
||||
d.putFields(value)
|
||||
|
||||
|
||||
def qdump__PyTypeObject(d, value):
|
||||
get_py_object_repr(d, value)
|
||||
|
||||
|
||||
def qdump___typeobject(d, value):
|
||||
get_py_object_repr(d, value)
|
||||
|
||||
|
||||
def qdump__PyObject(d, value):
|
||||
get_py_object_repr(d, value)
|
||||
|
||||
|
||||
def qdump__PyVarObject(d, value):
|
||||
get_py_object_repr(d, value)
|
||||
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
# Internal test
|
||||
|
||||
@@ -195,7 +195,7 @@ void QuickItemNodeInstance::doComponentComplete()
|
||||
|
||||
QmlPrivateGate::disableTextCursor(quickItem());
|
||||
|
||||
DesignerSupport::emitComponentCompleteSignalForAttachedProperty(quickItem());
|
||||
QmlPrivateGate::emitComponentComplete(quickItem());
|
||||
|
||||
QQmlProperty contentItemProperty(quickItem(), "contentItem", engine());
|
||||
if (contentItemProperty.isValid())
|
||||
|
||||
@@ -85,6 +85,7 @@ public:
|
||||
void setPropertyBinding(QObject *object, QQmlContext *context, const PropertyName &propertyName, const QString &expression);
|
||||
void keepBindingFromGettingDeleted(QObject *object, QQmlContext *context, const PropertyName &propertyName);
|
||||
|
||||
void emitComponentComplete(QObject *item);
|
||||
void doComponentCompleteRecursive(QObject *object, NodeInstanceServer *nodeInstanceServer);
|
||||
|
||||
bool objectWasDeleted(QObject *object);
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
#include <private/qquickdesignersupportproperties_p.h>
|
||||
#include <private/qquickdesignersupportpropertychanges_p.h>
|
||||
#include <private/qquickdesignersupportstates_p.h>
|
||||
#include <private/qqmldata_p.h>
|
||||
#include <private/qqmlcomponentattached_p.h>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
@@ -216,6 +218,24 @@ void setPropertyBinding(QObject *object, QQmlContext *context, const PropertyNam
|
||||
QQuickDesignerSupportProperties::setPropertyBinding(object, context, propertyName, expression);
|
||||
}
|
||||
|
||||
void emitComponentComplete(QObject *item)
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
QQmlData *data = QQmlData::get(item);
|
||||
if (data && data->context) {
|
||||
QQmlComponentAttached *componentAttached = data->context->componentAttached;
|
||||
while (componentAttached) {
|
||||
if (componentAttached->parent())
|
||||
if (componentAttached->parent() == item)
|
||||
emit componentAttached->completed();
|
||||
|
||||
componentAttached = componentAttached->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void doComponentCompleteRecursive(QObject *object, NodeInstanceServer *nodeInstanceServer)
|
||||
{
|
||||
if (object) {
|
||||
@@ -224,6 +244,8 @@ void doComponentCompleteRecursive(QObject *object, NodeInstanceServer *nodeInsta
|
||||
if (item && DesignerSupport::isComponentComplete(item))
|
||||
return;
|
||||
|
||||
if (!nodeInstanceServer->hasInstanceForObject(item))
|
||||
emitComponentComplete(object);
|
||||
QList<QObject*> childList = object->children();
|
||||
|
||||
if (item) {
|
||||
|
||||
@@ -77,7 +77,7 @@ static QPair<QByteArray, QByteArray> splitHeaderFieldLine(
|
||||
return {headerFieldLine.mid(0, assignmentIndex),
|
||||
headerFieldLine.mid(assignmentIndex + fieldSeparatorLength)};
|
||||
}
|
||||
parseError = BaseMessage::tr("Unexpected header line \"%1\"")
|
||||
parseError = BaseMessage::tr("Unexpected header line \"%1\".")
|
||||
.arg(QLatin1String(headerFieldLine));
|
||||
return {};
|
||||
}
|
||||
@@ -97,8 +97,8 @@ static void parseContentType(BaseMessage &message, QByteArray contentType, QStri
|
||||
if (equalindex > 0)
|
||||
codec = QTextCodec::codecForName(charset);
|
||||
if (!codec) {
|
||||
parseError = BaseMessage::tr("Cannot decode content with \"%1\" "
|
||||
"falling back to \"%2\"")
|
||||
parseError = BaseMessage::tr("Cannot decode content with \"%1\". "
|
||||
"Falling back to \"%2\".")
|
||||
.arg(QLatin1String(charset),
|
||||
QLatin1String(defaultCharset));
|
||||
}
|
||||
@@ -113,7 +113,7 @@ static void parseContentLength(BaseMessage &message, QByteArray contentLength, Q
|
||||
bool ok = true;
|
||||
message.contentLength = QString::fromLatin1(contentLength).toInt(&ok);
|
||||
if (!ok) {
|
||||
parseError = BaseMessage::tr("Expected an integer in \"%1\", but got \"%2\"")
|
||||
parseError = BaseMessage::tr("Expected an integer in \"%1\", but got \"%2\".")
|
||||
.arg(contentLengthFieldName, QLatin1String(contentLength));
|
||||
}
|
||||
}
|
||||
@@ -146,7 +146,7 @@ void BaseMessage::parse(QBuffer *data, QString &parseError, BaseMessage &message
|
||||
} else if (headerFieldName == contentTypeFieldName) {
|
||||
parseContentType(message, headerFieldValue, parseError);
|
||||
} else {
|
||||
parseError = tr("Unexpected header field \"%1\" in \"%2\"")
|
||||
parseError = tr("Unexpected header field \"%1\" in \"%2\".")
|
||||
.arg(QLatin1String(headerFieldName),
|
||||
QLatin1String(headerFieldLine));
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ QString JsonObject::valueTypeString(QJsonValue::Type type)
|
||||
|
||||
QString JsonObject::errorString(QJsonValue::Type expected, QJsonValue::Type actual)
|
||||
{
|
||||
return tr("Expected Type %1 but value contained %2")
|
||||
return tr("Expected type %1 but value contained %2")
|
||||
.arg(valueTypeString(expected), valueTypeString(actual));
|
||||
}
|
||||
|
||||
|
||||
@@ -141,9 +141,9 @@ QJsonObject JsonRpcMessageHandler::toJsonObject(const QByteArray &_content,
|
||||
if (doc.isObject())
|
||||
return doc.object();
|
||||
if (doc.isNull())
|
||||
parseError = tr("Could not parse Json message '%1'").arg(error.errorString());
|
||||
parseError = tr("Could not parse JSON message \"%1\".").arg(error.errorString());
|
||||
else
|
||||
parseError = tr("Expected Json object, but got a json '%1'").arg(docTypeName(doc));
|
||||
parseError = tr("Expected a JSON object, but got a JSON \"%1\".").arg(docTypeName(doc));
|
||||
return QJsonObject();
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ public:
|
||||
}
|
||||
if (errorMessage)
|
||||
*errorMessage = QCoreApplication::translate("LanguageServerProtocol::Notification",
|
||||
"No parameters in '%1'").arg(method());
|
||||
"No parameters in \"%1\".").arg(method());
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -274,7 +274,7 @@ public:
|
||||
return true;
|
||||
if (errorMessage) {
|
||||
*errorMessage = QCoreApplication::translate("LanguageServerProtocol::Request",
|
||||
"No id set in '%1'").arg(this->method());
|
||||
"No ID set in \"%1\".").arg(this->method());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -331,7 +331,7 @@ bool MarkedString::isValid(QStringList *errorHierarchy) const
|
||||
*errorHierarchy << QCoreApplication::translate(
|
||||
"LanguageServerProtocol::MarkedString",
|
||||
"MarkedString should be either MarkedLanguageString, "
|
||||
"MarkupContent or QList<MarkedLanguageString>");
|
||||
"MarkupContent, or QList<MarkedLanguageString>.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -356,7 +356,7 @@ bool DocumentFormattingProperty::isValid(QStringList *error) const
|
||||
if (error) {
|
||||
*error << QCoreApplication::translate(
|
||||
"LanguageServerProtocol::MarkedString",
|
||||
"DocumentFormattingProperty should be either bool, double or QString");
|
||||
"DocumentFormattingProperty should be either bool, double, or QString.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ bool MarkupOrString::isValid(QStringList *error) const
|
||||
return true;
|
||||
if (error) {
|
||||
*error << QCoreApplication::translate("LanguageServerProtocoll::MarkupOrString",
|
||||
"Expected a string or MarkupContent in MarkupOrString");
|
||||
"Expected a string or MarkupContent in MarkupOrString.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -140,5 +140,20 @@ QTextCursor wordStartCursor(const QTextCursor &textCursor)
|
||||
return cursor;
|
||||
}
|
||||
|
||||
int utf8NthLineOffset(const QTextDocument *textDocument, const QByteArray &buffer, int line)
|
||||
{
|
||||
if (textDocument->characterCount() == buffer.size() + 1)
|
||||
return textDocument->findBlockByNumber(line - 1).position();
|
||||
|
||||
int pos = 0;
|
||||
for (int count = 0; count < line - 1; ++count) {
|
||||
pos = buffer.indexOf('\n', pos);
|
||||
if (pos == -1)
|
||||
return -1;
|
||||
++pos;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
} // Text
|
||||
} // Utils
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "utils_global.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QTextCursor>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QTextDocument)
|
||||
QT_FORWARD_DECLARE_CLASS(QTextCursor)
|
||||
@@ -37,7 +36,7 @@ QT_FORWARD_DECLARE_CLASS(QTextCursor)
|
||||
namespace Utils {
|
||||
namespace Text {
|
||||
|
||||
// line is 1-based, column is 0-based
|
||||
// line is 1-based, column is 1-based
|
||||
QTCREATOR_UTILS_EXPORT bool convertPosition(const QTextDocument *document,
|
||||
int pos,
|
||||
int *line, int *column);
|
||||
@@ -55,42 +54,9 @@ QTCREATOR_UTILS_EXPORT QTextCursor flippedCursor(const QTextCursor &cursor);
|
||||
|
||||
QTCREATOR_UTILS_EXPORT QTextCursor wordStartCursor(const QTextCursor &cursor);
|
||||
|
||||
template <class CharacterProvider>
|
||||
void moveToPrevChar(CharacterProvider &provider, QTextCursor &cursor)
|
||||
{
|
||||
cursor.movePosition(QTextCursor::PreviousCharacter);
|
||||
while (provider.characterAt(cursor.position()).isSpace())
|
||||
cursor.movePosition(QTextCursor::PreviousCharacter);
|
||||
}
|
||||
|
||||
template <class CharacterProvider>
|
||||
bool matchPreviousWord(CharacterProvider &provider, QTextCursor cursor, QString pattern)
|
||||
{
|
||||
cursor.movePosition(QTextCursor::PreviousWord);
|
||||
while (provider.characterAt(cursor.position()) == ':')
|
||||
cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 2);
|
||||
|
||||
int previousWordStart = cursor.position();
|
||||
cursor.movePosition(QTextCursor::NextWord);
|
||||
moveToPrevChar(provider, cursor);
|
||||
QString toMatch = provider.textAt(previousWordStart, cursor.position() - previousWordStart + 1);
|
||||
|
||||
pattern = pattern.simplified();
|
||||
while (!pattern.isEmpty() && pattern.endsWith(toMatch)) {
|
||||
pattern.chop(toMatch.length());
|
||||
if (pattern.endsWith(' '))
|
||||
pattern.chop(1);
|
||||
if (!pattern.isEmpty()) {
|
||||
cursor.movePosition(QTextCursor::StartOfWord);
|
||||
cursor.movePosition(QTextCursor::PreviousWord);
|
||||
previousWordStart = cursor.position();
|
||||
cursor.movePosition(QTextCursor::NextWord);
|
||||
moveToPrevChar(provider, cursor);
|
||||
toMatch = provider.textAt(previousWordStart, cursor.position() - previousWordStart + 1);
|
||||
}
|
||||
}
|
||||
return pattern.isEmpty();
|
||||
}
|
||||
QTCREATOR_UTILS_EXPORT int utf8NthLineOffset(const QTextDocument *textDocument,
|
||||
const QByteArray &buffer,
|
||||
int line);
|
||||
|
||||
} // Text
|
||||
} // Utils
|
||||
|
||||
@@ -451,6 +451,8 @@ AndroidToolChainFactory::autodetectToolChainsForNdk(const FileName &ndkPath,
|
||||
QList<AndroidToolChain *> toolChainBundle;
|
||||
for (Core::Id lang : {ProjectExplorer::Constants::CXX_LANGUAGE_ID, ProjectExplorer::Constants::C_LANGUAGE_ID}) {
|
||||
FileName compilerPath = AndroidConfigurations::currentConfig().gccPath(abi, lang, version);
|
||||
if (!compilerPath.exists())
|
||||
continue;
|
||||
|
||||
AndroidToolChain *tc = findToolChain(compilerPath, lang, alreadyKnown);
|
||||
if (!tc || tc->originalTargetTriple().isEmpty()) {
|
||||
@@ -464,7 +466,8 @@ AndroidToolChainFactory::autodetectToolChainsForNdk(const FileName &ndkPath,
|
||||
toolChainBundle.append(tc);
|
||||
}
|
||||
|
||||
QTC_ASSERT(!toolChainBundle.isEmpty(), continue);
|
||||
if (toolChainBundle.isEmpty())
|
||||
continue;
|
||||
|
||||
auto it = newestToolChainForArch.constFind(abi);
|
||||
if (it == newestToolChainForArch.constEnd())
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
#include <projectexplorer/session.h>
|
||||
#include <projectexplorer/target.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
#include <texteditor/textdocument.h>
|
||||
#include <utils/textutils.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
@@ -261,11 +262,22 @@ void AutotestPlugin::onRunFileTriggered()
|
||||
runner->prepareToRunTests(TestRunMode::Run);
|
||||
}
|
||||
|
||||
static QList<TestConfiguration *> testItemsToTestConfigurations(const QList<TestTreeItem *> &items,
|
||||
TestRunMode mode)
|
||||
{
|
||||
QList<TestConfiguration *> configs;
|
||||
for (const TestTreeItem * item : items) {
|
||||
if (TestConfiguration *currentConfig = item->asConfiguration(mode))
|
||||
configs << currentConfig;
|
||||
}
|
||||
return configs;
|
||||
}
|
||||
|
||||
void AutotestPlugin::onRunUnderCursorTriggered(TestRunMode mode)
|
||||
{
|
||||
QTextCursor cursor = Utils::Text::wordStartCursor(
|
||||
TextEditor::BaseTextEditor::currentTextEditor()->editorWidget()->textCursor());
|
||||
cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
||||
TextEditor::BaseTextEditor *currentEditor = TextEditor::BaseTextEditor::currentTextEditor();
|
||||
QTextCursor cursor = currentEditor->editorWidget()->textCursor();
|
||||
cursor.select(QTextCursor::WordUnderCursor);
|
||||
const QString text = cursor.selectedText();
|
||||
if (text.isEmpty())
|
||||
return; // Do not trigger when no name under cursor
|
||||
@@ -274,11 +286,15 @@ void AutotestPlugin::onRunUnderCursorTriggered(TestRunMode mode)
|
||||
if (testsItems.isEmpty())
|
||||
return; // Wrong location triggered
|
||||
|
||||
QList<TestConfiguration *> testsToRun;
|
||||
for (const TestTreeItem * item : testsItems){
|
||||
if (TestConfiguration *cfg = item->asConfiguration(mode))
|
||||
testsToRun << cfg;
|
||||
}
|
||||
// check whether we have been triggered on a test function definition
|
||||
const uint line = uint(currentEditor->currentLine());
|
||||
const QString &filePath = currentEditor->textDocument()->filePath().toString();
|
||||
const QList<TestTreeItem *> filteredItems = Utils::filtered(testsItems, [&](TestTreeItem *it){
|
||||
return it->line() == line && it->filePath() == filePath;
|
||||
});
|
||||
|
||||
const QList<TestConfiguration *> testsToRun = testItemsToTestConfigurations(
|
||||
filteredItems.size() == 1 ? filteredItems : testsItems, mode);
|
||||
|
||||
if (testsToRun.isEmpty()) {
|
||||
MessageManager::write(tr("Selected test was not found (%1).").arg(text), MessageManager::Flash);
|
||||
|
||||
@@ -198,9 +198,9 @@ void ClangAssistProposalItem::apply(TextDocumentManipulatorInterface &manipulato
|
||||
QTextCursor cursor = manipulator.textCursorAt(basePosition);
|
||||
|
||||
bool abandonParen = false;
|
||||
if (::Utils::Text::matchPreviousWord(manipulator, cursor, "&")) {
|
||||
::Utils::Text::moveToPrevChar(manipulator, cursor);
|
||||
::Utils::Text::moveToPrevChar(manipulator, cursor);
|
||||
if (Utils::Text::matchPreviousWord(manipulator, cursor, "&")) {
|
||||
Utils::Text::moveToPreviousWord(manipulator, cursor);
|
||||
Utils::Text::moveToPreviousChar(manipulator, cursor);
|
||||
const QChar prevChar = manipulator.characterAt(cursor.position());
|
||||
cursor.setPosition(basePosition);
|
||||
abandonParen = QString("(;,{}").contains(prevChar);
|
||||
@@ -211,7 +211,7 @@ void ClangAssistProposalItem::apply(TextDocumentManipulatorInterface &manipulato
|
||||
if (!abandonParen && ccr.completionKind == CodeCompletion::FunctionDefinitionCompletionKind) {
|
||||
const CodeCompletionChunk resultType = ccr.chunks.first();
|
||||
if (resultType.kind == CodeCompletionChunk::ResultType) {
|
||||
if (::Utils::Text::matchPreviousWord(manipulator, cursor, resultType.text.toString())) {
|
||||
if (Utils::Text::matchPreviousWord(manipulator, cursor, resultType.text.toString())) {
|
||||
extraCharacters += methodDefinitionParameters(ccr.chunks);
|
||||
// To skip the next block.
|
||||
abandonParen = true;
|
||||
|
||||
@@ -207,9 +207,26 @@ bool BackendCommunicator::isNotWaitingForCompletion() const
|
||||
return !m_receiver.isExpectingCompletionsMessage();
|
||||
}
|
||||
|
||||
void BackendCommunicator::setBackendJobsPostponed(bool postponed)
|
||||
{
|
||||
if (postponed) {
|
||||
if (!m_postponeBackendJobs)
|
||||
documentVisibilityChanged(Utf8String(), {});
|
||||
++m_postponeBackendJobs;
|
||||
} else {
|
||||
if (QTC_GUARD(m_postponeBackendJobs > 0))
|
||||
--m_postponeBackendJobs;
|
||||
if (!m_postponeBackendJobs)
|
||||
documentVisibilityChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void BackendCommunicator::documentVisibilityChanged(const Utf8String ¤tEditorFilePath,
|
||||
const Utf8StringVector &visibleEditorsFilePaths)
|
||||
{
|
||||
if (m_postponeBackendJobs)
|
||||
return;
|
||||
|
||||
const DocumentVisibilityChangedMessage message(currentEditorFilePath, visibleEditorsFilePaths);
|
||||
m_sender->documentVisibilityChanged(message);
|
||||
}
|
||||
@@ -459,9 +476,14 @@ void BackendCommunicator::initializeBackendWithCurrentData()
|
||||
|
||||
void BackendCommunicator::documentsOpened(const FileContainers &fileContainers)
|
||||
{
|
||||
const DocumentsOpenedMessage message(fileContainers,
|
||||
currentCppEditorDocumentFilePath(),
|
||||
visibleCppEditorDocumentsFilePaths());
|
||||
Utf8String currentDocument;
|
||||
Utf8StringVector visibleDocuments;
|
||||
if (!m_postponeBackendJobs) {
|
||||
currentDocument = currentCppEditorDocumentFilePath();
|
||||
visibleDocuments = visibleCppEditorDocumentsFilePaths();
|
||||
}
|
||||
|
||||
const DocumentsOpenedMessage message(fileContainers, currentDocument, visibleDocuments);
|
||||
m_sender->documentsOpened(message);
|
||||
}
|
||||
|
||||
|
||||
@@ -108,6 +108,8 @@ public:
|
||||
void updateChangeContentStartPosition(const QString &filePath, int position);
|
||||
bool isNotWaitingForCompletion() const;
|
||||
|
||||
void setBackendJobsPostponed(bool postponed);
|
||||
|
||||
private:
|
||||
void initializeBackend();
|
||||
void initializeBackendWithCurrentData();
|
||||
@@ -134,6 +136,7 @@ private:
|
||||
QTimer m_backendStartTimeOut;
|
||||
QScopedPointer<ClangBackEnd::ClangCodeModelServerInterface> m_sender;
|
||||
int m_connectedCount = 0;
|
||||
int m_postponeBackendJobs = 1; // Initial application state is inactive, so no jobs should be run.
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -98,9 +98,9 @@ static void addFunctionOverloadAssistProposalItem(QList<AssistProposalItemInterf
|
||||
cursor.movePosition(QTextCursor::StartOfWord);
|
||||
|
||||
const ClangBackEnd::CodeCompletionChunk resultType = codeCompletion.chunks.first();
|
||||
if (::Utils::Text::matchPreviousWord(*interface->textEditorWidget(),
|
||||
cursor,
|
||||
resultType.text.toString())) {
|
||||
if (Utils::Text::matchPreviousWord(*interface->textEditorWidget(),
|
||||
cursor,
|
||||
resultType.text.toString())) {
|
||||
// Function definition completion - do not merge completions together.
|
||||
addAssistProposalItem(items, codeCompletion, name);
|
||||
} else {
|
||||
@@ -445,6 +445,12 @@ bool ClangCompletionAssistProcessor::completeInclude(const QTextCursor &cursor)
|
||||
completeIncludePath(realPath, suffixes);
|
||||
}
|
||||
|
||||
auto includesCompare = [](AssistProposalItemInterface *first,
|
||||
AssistProposalItemInterface *second) {
|
||||
return first->text() < second->text();
|
||||
};
|
||||
std::sort(m_completions.begin(), m_completions.end(), includesCompare);
|
||||
|
||||
return !m_completions.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QApplication>
|
||||
#include <QMenu>
|
||||
#include <QTextBlock>
|
||||
|
||||
@@ -75,6 +75,8 @@ ModelManagerSupportClang::ModelManagerSupportClang()
|
||||
QTC_CHECK(!m_instance);
|
||||
m_instance = this;
|
||||
|
||||
QApplication::instance()->installEventFilter(this);
|
||||
|
||||
CppTools::CppModelManager::instance()->setCurrentDocumentFilter(
|
||||
std::make_unique<ClangCurrentDocumentFilter>());
|
||||
|
||||
@@ -138,6 +140,11 @@ std::unique_ptr<CppTools::AbstractOverviewModel> ModelManagerSupportClang::creat
|
||||
return std::make_unique<OverviewModel>();
|
||||
}
|
||||
|
||||
void ModelManagerSupportClang::setBackendJobsPostponed(bool postponed)
|
||||
{
|
||||
m_communicator.setBackendJobsPostponed(postponed);
|
||||
}
|
||||
|
||||
CppTools::BaseEditorDocumentProcessor *ModelManagerSupportClang::createEditorDocumentProcessor(
|
||||
TextEditor::TextDocument *baseTextDocument)
|
||||
{
|
||||
@@ -211,6 +218,20 @@ void ModelManagerSupportClang::connectToWidgetsMarkContextMenuRequested(QWidget
|
||||
}
|
||||
}
|
||||
|
||||
bool ModelManagerSupportClang::eventFilter(QObject *obj, QEvent *e)
|
||||
{
|
||||
if (obj == QApplication::instance() && e->type() == QEvent::ApplicationStateChange) {
|
||||
switch (QApplication::applicationState()) {
|
||||
case Qt::ApplicationInactive: setBackendJobsPostponed(true); break;
|
||||
case Qt::ApplicationActive: setBackendJobsPostponed(false); break;
|
||||
default:
|
||||
QTC_CHECK(false && "Unexpected Qt::ApplicationState");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ModelManagerSupportClang::onEditorOpened(Core::IEditor *editor)
|
||||
{
|
||||
QTC_ASSERT(editor, return);
|
||||
|
||||
@@ -72,6 +72,7 @@ public:
|
||||
CppTools::FollowSymbolInterface &followSymbolInterface() override;
|
||||
CppTools::RefactoringEngineInterface &refactoringEngineInterface() override;
|
||||
std::unique_ptr<CppTools::AbstractOverviewModel> createOverviewModel() override;
|
||||
void setBackendJobsPostponed(bool postponed) override;
|
||||
|
||||
BackendCommunicator &communicator();
|
||||
QString dummyUiHeaderOnDiskDirPath() const;
|
||||
@@ -82,6 +83,8 @@ public:
|
||||
static ModelManagerSupportClang *instance();
|
||||
|
||||
private:
|
||||
bool eventFilter(QObject *obj, QEvent *e) override;
|
||||
|
||||
void onEditorOpened(Core::IEditor *editor);
|
||||
void onEditorClosed(const QList<Core::IEditor *> &editors);
|
||||
void onCurrentEditorChanged(Core::IEditor *newCurrent);
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
#include <cpptools/projectpart.h>
|
||||
|
||||
#include <QTextCursor>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTextBlock;
|
||||
QT_END_NAMESPACE
|
||||
@@ -66,5 +68,53 @@ QString diagnosticCategoryPrefixRemoved(const QString &text);
|
||||
|
||||
void generateCompilationDB(::Utils::FileName projectDir, CppTools::ProjectInfo projectInfo);
|
||||
|
||||
namespace Text {
|
||||
|
||||
template <class CharacterProvider>
|
||||
void moveToPreviousChar(CharacterProvider &provider, QTextCursor &cursor)
|
||||
{
|
||||
cursor.movePosition(QTextCursor::PreviousCharacter);
|
||||
while (provider.characterAt(cursor.position()).isSpace())
|
||||
cursor.movePosition(QTextCursor::PreviousCharacter);
|
||||
}
|
||||
|
||||
template <class CharacterProvider>
|
||||
void moveToPreviousWord(CharacterProvider &provider, QTextCursor &cursor)
|
||||
{
|
||||
cursor.movePosition(QTextCursor::PreviousWord);
|
||||
while (provider.characterAt(cursor.position()) == ':')
|
||||
cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 2);
|
||||
}
|
||||
|
||||
template <class CharacterProvider>
|
||||
bool matchPreviousWord(CharacterProvider &provider, QTextCursor cursor, QString pattern)
|
||||
{
|
||||
cursor.movePosition(QTextCursor::PreviousWord);
|
||||
while (provider.characterAt(cursor.position()) == ':')
|
||||
cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 2);
|
||||
|
||||
int previousWordStart = cursor.position();
|
||||
cursor.movePosition(QTextCursor::NextWord);
|
||||
moveToPreviousChar(provider, cursor);
|
||||
QString toMatch = provider.textAt(previousWordStart, cursor.position() - previousWordStart + 1);
|
||||
|
||||
pattern = pattern.simplified();
|
||||
while (!pattern.isEmpty() && pattern.endsWith(toMatch)) {
|
||||
pattern.chop(toMatch.length());
|
||||
if (pattern.endsWith(' '))
|
||||
pattern.chop(1);
|
||||
if (!pattern.isEmpty()) {
|
||||
cursor.movePosition(QTextCursor::StartOfWord);
|
||||
cursor.movePosition(QTextCursor::PreviousWord);
|
||||
previousWordStart = cursor.position();
|
||||
cursor.movePosition(QTextCursor::NextWord);
|
||||
moveToPreviousChar(provider, cursor);
|
||||
toMatch = provider.textAt(previousWordStart, cursor.position() - previousWordStart + 1);
|
||||
}
|
||||
}
|
||||
return pattern.isEmpty();
|
||||
}
|
||||
|
||||
} // namespace Text
|
||||
} // namespace Utils
|
||||
} // namespace Clang
|
||||
|
||||
@@ -24,7 +24,8 @@ HEADERS = \
|
||||
clangformatconfigwidget.h \
|
||||
clangformatindenter.h \
|
||||
clangformatplugin.h \
|
||||
clangformatconstants.h
|
||||
clangformatconstants.h \
|
||||
clangformatutils.h
|
||||
|
||||
FORMS += \
|
||||
clangformatconfigwidget.ui
|
||||
|
||||
@@ -27,10 +27,11 @@ QtcPlugin {
|
||||
"clangformatconfigwidget.cpp",
|
||||
"clangformatconfigwidget.h",
|
||||
"clangformatconfigwidget.ui",
|
||||
"clangformatconstants.h",
|
||||
"clangformatindenter.cpp",
|
||||
"clangformatindenter.h",
|
||||
"clangformatplugin.cpp",
|
||||
"clangformatplugin.h",
|
||||
"clangformatconstants.h",
|
||||
"clangformatutils.h",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
|
||||
#include "clangformatconfigwidget.h"
|
||||
|
||||
#include "clangformatutils.h"
|
||||
#include "ui_clangformatconfigwidget.h"
|
||||
|
||||
#include <clang/Format/Format.h>
|
||||
@@ -40,23 +42,6 @@
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace ClangFormat {
|
||||
namespace Internal {
|
||||
|
||||
static void createGlobalClangFormatFileIfNeeded(const QString &settingsDir)
|
||||
{
|
||||
const QString fileName = settingsDir + "/.clang-format";
|
||||
if (QFile::exists(fileName))
|
||||
return;
|
||||
|
||||
QFile file(fileName);
|
||||
if (!file.open(QFile::WriteOnly))
|
||||
return;
|
||||
|
||||
const clang::format::FormatStyle defaultStyle = clang::format::getLLVMStyle();
|
||||
const std::string configuration = clang::format::configurationAsText(defaultStyle);
|
||||
file.write(configuration.c_str());
|
||||
file.close();
|
||||
}
|
||||
|
||||
static void readTable(QTableWidget *table, std::istringstream &stream)
|
||||
{
|
||||
@@ -137,50 +122,75 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(ProjectExplorer::Project *proje
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
std::string testFilePath;
|
||||
initialize();
|
||||
}
|
||||
|
||||
void ClangFormatConfigWidget::initialize()
|
||||
{
|
||||
m_ui->projectHasClangFormat->show();
|
||||
m_ui->clangFormatOptionsTable->show();
|
||||
m_ui->applyButton->show();
|
||||
|
||||
if (m_project && !m_project->projectDirectory().appendPath(".clang-format").exists()) {
|
||||
m_ui->projectHasClangFormat->setText("No .clang-format file for the project");
|
||||
m_ui->projectHasClangFormat->setText(tr("No .clang-format file for the project."));
|
||||
m_ui->clangFormatOptionsTable->hide();
|
||||
m_ui->applyButton->hide();
|
||||
|
||||
connect(m_ui->createFileButton, &QPushButton::clicked,
|
||||
this, [this]() {
|
||||
createStyleFileIfNeeded(m_project->projectDirectory());
|
||||
initialize();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
m_ui->createFileButton->hide();
|
||||
|
||||
std::string testFilePath;
|
||||
if (m_project) {
|
||||
m_ui->projectHasClangFormat->hide();
|
||||
testFilePath = m_project->projectDirectory().appendPath("t.cpp").toString().toStdString();
|
||||
connect(m_ui->applyButton, &QPushButton::clicked, this, &ClangFormatConfigWidget::apply);
|
||||
} else {
|
||||
const Project *currentProject = SessionManager::startupProject();
|
||||
if (!currentProject
|
||||
|| !currentProject->projectDirectory().appendPath(".clang-format").exists()) {
|
||||
m_ui->projectHasClangFormat->hide();
|
||||
} else {
|
||||
m_ui->projectHasClangFormat->setText(
|
||||
tr(" Current project has its own .clang-format file "
|
||||
"and can be configured in Projects > Clang Format."));
|
||||
}
|
||||
const QString settingsDir = Core::ICore::userResourcePath();
|
||||
createGlobalClangFormatFileIfNeeded(settingsDir);
|
||||
createStyleFileIfNeeded(Utils::FileName::fromString(settingsDir));
|
||||
testFilePath = settingsDir.toStdString() + "/t.cpp";
|
||||
m_ui->applyButton->hide();
|
||||
}
|
||||
|
||||
fillTable(testFilePath);
|
||||
}
|
||||
|
||||
void ClangFormatConfigWidget::fillTable(const std::string &testFilePath)
|
||||
{
|
||||
llvm::Expected<clang::format::FormatStyle> formatStyle =
|
||||
clang::format::getStyle("file", testFilePath, "LLVM", "");
|
||||
if (!formatStyle)
|
||||
return;
|
||||
|
||||
const std::string configText = clang::format::configurationAsText(*formatStyle);
|
||||
std::istringstream stream(configText);
|
||||
|
||||
readTable(m_ui->clangFormatOptionsTable, stream);
|
||||
|
||||
if (m_project) {
|
||||
m_ui->projectHasClangFormat->hide();
|
||||
return;
|
||||
clang::format::getStyle("file", testFilePath, "LLVM");
|
||||
std::string configText;
|
||||
bool brokenConfig = false;
|
||||
if (!formatStyle) {
|
||||
handleAllErrors(formatStyle.takeError(), [](const llvm::ErrorInfoBase &) {
|
||||
// do nothing
|
||||
});
|
||||
configText = clang::format::configurationAsText(clang::format::getLLVMStyle());
|
||||
brokenConfig = true;
|
||||
} else {
|
||||
configText = clang::format::configurationAsText(*formatStyle);
|
||||
}
|
||||
|
||||
const Project *currentProject = SessionManager::startupProject();
|
||||
if (!currentProject || !currentProject->projectDirectory().appendPath(".clang-format").exists())
|
||||
m_ui->projectHasClangFormat->hide();
|
||||
std::istringstream stream(configText);
|
||||
readTable(m_ui->clangFormatOptionsTable, stream);
|
||||
if (brokenConfig)
|
||||
apply();
|
||||
|
||||
connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
|
||||
this, [this](ProjectExplorer::Project *project) {
|
||||
if (project && project->projectDirectory().appendPath(".clang-format").exists())
|
||||
m_ui->projectHasClangFormat->show();
|
||||
else
|
||||
m_ui->projectHasClangFormat->hide();
|
||||
});
|
||||
}
|
||||
|
||||
ClangFormatConfigWidget::~ClangFormatConfigWidget() = default;
|
||||
@@ -201,5 +211,4 @@ void ClangFormatConfigWidget::apply()
|
||||
file.close();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangFormat
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
namespace ProjectExplorer { class Project; }
|
||||
|
||||
namespace ClangFormat {
|
||||
namespace Internal {
|
||||
|
||||
namespace Ui {
|
||||
class ClangFormatConfigWidget;
|
||||
@@ -49,9 +48,11 @@ public:
|
||||
void apply();
|
||||
|
||||
private:
|
||||
void initialize();
|
||||
void fillTable(const std::string &testFilePath);
|
||||
|
||||
ProjectExplorer::Project *m_project;
|
||||
std::unique_ptr<Ui::ClangFormatConfigWidget> m_ui;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangFormat
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ClangFormat::Internal::ClangFormatConfigWidget</class>
|
||||
<widget class="QWidget" name="ClangFormat::Internal::ClangFormatConfigWidget">
|
||||
<class>ClangFormat::ClangFormatConfigWidget</class>
|
||||
<widget class="QWidget" name="ClangFormat::ClangFormatConfigWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
@@ -29,7 +29,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="projectHasClangFormat">
|
||||
<property name="text">
|
||||
<string> Current project has its own .clang-format file and can be configured in Projects -> ClangFormat.</string>
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -38,6 +38,13 @@
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="createFileButton">
|
||||
<property name="text">
|
||||
<string>Create Clang Format Configuration File</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="applyButton">
|
||||
<property name="text">
|
||||
|
||||
@@ -25,16 +25,21 @@
|
||||
|
||||
#include "clangformatindenter.h"
|
||||
|
||||
#include "clangformatutils.h"
|
||||
|
||||
#include <clang/Format/Format.h>
|
||||
#include <clang/Tooling/Core/Replacement.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <cpptools/cppmodelmanager.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/session.h>
|
||||
#include <texteditor/textdocument.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/textutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <llvm/Config/llvm-config.h>
|
||||
|
||||
@@ -42,6 +47,8 @@
|
||||
#include <QFileInfo>
|
||||
#include <QTextBlock>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
using namespace clang;
|
||||
using namespace format;
|
||||
using namespace llvm;
|
||||
@@ -50,67 +57,35 @@ using namespace ProjectExplorer;
|
||||
using namespace TextEditor;
|
||||
|
||||
namespace ClangFormat {
|
||||
namespace Internal {
|
||||
|
||||
namespace {
|
||||
|
||||
void adjustFormatStyleForLineBreak(format::FormatStyle &style,
|
||||
int length,
|
||||
int prevBlockSize,
|
||||
bool prevBlockEndsWithPunctuation)
|
||||
void adjustFormatStyleForLineBreak(format::FormatStyle &style)
|
||||
{
|
||||
if (length > 0)
|
||||
style.ColumnLimit = prevBlockSize;
|
||||
style.AlwaysBreakBeforeMultilineStrings = true;
|
||||
#if LLVM_VERSION_MAJOR >= 7
|
||||
style.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_Yes;
|
||||
#else
|
||||
style.AlwaysBreakTemplateDeclarations = true;
|
||||
style.DisableFormat = false;
|
||||
style.ColumnLimit = 0;
|
||||
#ifdef KEEP_LINE_BREAKS_FOR_NON_EMPTY_LINES_BACKPORTED
|
||||
style.KeepLineBreaksForNonEmptyLines = true;
|
||||
#endif
|
||||
|
||||
style.AllowAllParametersOfDeclarationOnNextLine = true;
|
||||
style.AllowShortBlocksOnASingleLine = true;
|
||||
style.AllowShortCaseLabelsOnASingleLine = true;
|
||||
style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty;
|
||||
style.AllowShortIfStatementsOnASingleLine = true;
|
||||
style.AllowShortLoopsOnASingleLine = true;
|
||||
if (prevBlockEndsWithPunctuation) {
|
||||
style.BreakBeforeBinaryOperators = FormatStyle::BOS_None;
|
||||
style.BreakBeforeTernaryOperators = false;
|
||||
style.BreakConstructorInitializers = FormatStyle::BCIS_AfterColon;
|
||||
} else {
|
||||
style.BreakBeforeBinaryOperators = FormatStyle::BOS_All;
|
||||
style.BreakBeforeTernaryOperators = true;
|
||||
style.BreakConstructorInitializers = FormatStyle::BCIS_BeforeComma;
|
||||
}
|
||||
style.MaxEmptyLinesToKeep = 2;
|
||||
}
|
||||
|
||||
Replacements filteredReplacements(const Replacements &replacements,
|
||||
unsigned int offset,
|
||||
unsigned int lengthForFilter,
|
||||
int extraOffsetToAdd,
|
||||
int prevBlockLength)
|
||||
int offset,
|
||||
int lengthForFilter,
|
||||
int extraOffsetToAdd)
|
||||
{
|
||||
Replacements filtered;
|
||||
for (const Replacement &replacement : replacements) {
|
||||
unsigned int replacementOffset = replacement.getOffset();
|
||||
int replacementOffset = static_cast<int>(replacement.getOffset());
|
||||
if (replacementOffset > offset + lengthForFilter)
|
||||
break;
|
||||
|
||||
if (offset > static_cast<unsigned int>(prevBlockLength)
|
||||
&& replacementOffset < offset - static_cast<unsigned int>(prevBlockLength))
|
||||
continue;
|
||||
|
||||
if (lengthForFilter == 0 && replacement.getReplacementText().find('\n') == std::string::npos
|
||||
&& filtered.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (replacementOffset + 1 >= offset)
|
||||
replacementOffset += static_cast<unsigned int>(extraOffsetToAdd);
|
||||
replacementOffset += extraOffsetToAdd;
|
||||
|
||||
Error error = filtered.add(Replacement(replacement.getFilePath(),
|
||||
replacementOffset,
|
||||
static_cast<unsigned int>(replacementOffset),
|
||||
replacement.getLength(),
|
||||
replacement.getReplacementText()));
|
||||
// Throws if error is not checked.
|
||||
@@ -120,147 +95,58 @@ Replacements filteredReplacements(const Replacements &replacements,
|
||||
return filtered;
|
||||
}
|
||||
|
||||
std::string assumedFilePath()
|
||||
Utils::FileName styleConfigPath()
|
||||
{
|
||||
const Project *project = SessionManager::startupProject();
|
||||
if (project && project->projectDirectory().appendPath(".clang-format").exists())
|
||||
return project->projectDirectory().appendPath("test.cpp").toString().toStdString();
|
||||
return project->projectDirectory();
|
||||
|
||||
return QString(Core::ICore::userResourcePath() + "/test.cpp").toStdString();
|
||||
return Utils::FileName::fromString(Core::ICore::userResourcePath());
|
||||
}
|
||||
|
||||
FormatStyle formatStyle()
|
||||
FormatStyle formatStyle(Utils::FileName styleConfigPath)
|
||||
{
|
||||
Expected<FormatStyle> style = format::getStyle("file", assumedFilePath(), "none", "");
|
||||
createStyleFileIfNeeded(styleConfigPath);
|
||||
|
||||
Expected<FormatStyle> style = format::getStyle(
|
||||
"file", styleConfigPath.appendPath("test.cpp").toString().toStdString(), "LLVM");
|
||||
if (style)
|
||||
return *style;
|
||||
return FormatStyle();
|
||||
|
||||
handleAllErrors(style.takeError(), [](const ErrorInfoBase &) {
|
||||
// do nothing
|
||||
});
|
||||
|
||||
return format::getLLVMStyle();
|
||||
}
|
||||
|
||||
Replacements replacements(const std::string &buffer,
|
||||
unsigned int offset,
|
||||
unsigned int length,
|
||||
bool blockFormatting = false,
|
||||
const QChar &typedChar = QChar::Null,
|
||||
int extraOffsetToAdd = 0,
|
||||
int prevBlockLength = 1,
|
||||
bool prevBlockEndsWithPunctuation = false)
|
||||
{
|
||||
FormatStyle style = formatStyle();
|
||||
|
||||
if (blockFormatting && typedChar == QChar::Null)
|
||||
adjustFormatStyleForLineBreak(style, length, prevBlockLength, prevBlockEndsWithPunctuation);
|
||||
|
||||
std::vector<Range> ranges{{offset, length}};
|
||||
FormattingAttemptStatus status;
|
||||
|
||||
Replacements replacements = reformat(style, buffer, ranges, assumedFilePath(), &status);
|
||||
|
||||
if (!status.FormatComplete)
|
||||
Replacements();
|
||||
|
||||
unsigned int lengthForFilter = 0;
|
||||
if (!blockFormatting)
|
||||
lengthForFilter = length;
|
||||
|
||||
return filteredReplacements(replacements,
|
||||
offset,
|
||||
lengthForFilter,
|
||||
extraOffsetToAdd,
|
||||
prevBlockLength);
|
||||
}
|
||||
|
||||
void applyReplacements(QTextDocument *doc,
|
||||
const std::string &stdStrBuffer,
|
||||
const tooling::Replacements &replacements,
|
||||
int totalShift)
|
||||
{
|
||||
if (replacements.empty())
|
||||
return;
|
||||
|
||||
QTextCursor editCursor(doc);
|
||||
int fullOffsetDiff = 0;
|
||||
for (const Replacement &replacement : replacements) {
|
||||
const int utf16Offset
|
||||
= QString::fromStdString(stdStrBuffer.substr(0, replacement.getOffset())).length()
|
||||
+ totalShift + fullOffsetDiff;
|
||||
const int utf16Length = QString::fromStdString(stdStrBuffer.substr(replacement.getOffset(),
|
||||
replacement.getLength()))
|
||||
.length();
|
||||
const QString replacementText = QString::fromStdString(replacement.getReplacementText());
|
||||
|
||||
editCursor.beginEditBlock();
|
||||
editCursor.setPosition(utf16Offset);
|
||||
editCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, utf16Length);
|
||||
editCursor.removeSelectedText();
|
||||
editCursor.insertText(replacementText);
|
||||
editCursor.endEditBlock();
|
||||
fullOffsetDiff += replacementText.length() - utf16Length;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns offset shift.
|
||||
int modifyToIndentEmptyLines(QString &buffer, int &offset, int &length, const QTextBlock &block)
|
||||
{
|
||||
//This extra text works for the most cases.
|
||||
QString extraText("a;");
|
||||
|
||||
const QString blockText = block.text().trimmed();
|
||||
// Search for previous character
|
||||
QTextBlock prevBlock = block.previous();
|
||||
while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty())
|
||||
prevBlock = prevBlock.previous();
|
||||
if (prevBlock.text().endsWith(','))
|
||||
extraText = "int a,";
|
||||
|
||||
const bool closingParenBlock = blockText.startsWith(')');
|
||||
if (closingParenBlock) {
|
||||
if (prevBlock.text().endsWith(','))
|
||||
extraText = "int a";
|
||||
else
|
||||
extraText = "&& a";
|
||||
}
|
||||
|
||||
if (length == 0 || closingParenBlock) {
|
||||
length += extraText.length();
|
||||
buffer.insert(offset, extraText);
|
||||
}
|
||||
|
||||
if (blockText.startsWith('}')) {
|
||||
buffer.insert(offset - 1, extraText);
|
||||
offset += extraText.size();
|
||||
return extraText.size();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns first non-empty block (searches from current block backwards).
|
||||
QTextBlock clearFirstNonEmptyBlockFromExtraSpaces(const QTextBlock ¤tBlock)
|
||||
void trimFirstNonEmptyBlock(const QTextBlock ¤tBlock)
|
||||
{
|
||||
QTextBlock prevBlock = currentBlock.previous();
|
||||
while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty())
|
||||
prevBlock = prevBlock.previous();
|
||||
|
||||
if (prevBlock.text().trimmed().isEmpty())
|
||||
return prevBlock;
|
||||
return;
|
||||
|
||||
const QString initialText = prevBlock.text();
|
||||
if (!initialText.at(initialText.length() - 1).isSpace())
|
||||
return prevBlock;
|
||||
if (!initialText.at(initialText.size() - 1).isSpace())
|
||||
return;
|
||||
|
||||
int extraSpaceCount = 1;
|
||||
for (int i = initialText.size() - 2; i >= 0; --i) {
|
||||
if (!initialText.at(i).isSpace())
|
||||
break;
|
||||
++extraSpaceCount;
|
||||
}
|
||||
|
||||
QTextCursor cursor(prevBlock);
|
||||
cursor.beginEditBlock();
|
||||
cursor.movePosition(QTextCursor::EndOfBlock);
|
||||
cursor.movePosition(QTextCursor::Left);
|
||||
|
||||
while (cursor.positionInBlock() >= 0 && initialText.at(cursor.positionInBlock()).isSpace())
|
||||
cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
|
||||
if (cursor.hasSelection())
|
||||
cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
||||
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor,
|
||||
initialText.size() - extraSpaceCount);
|
||||
cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, extraSpaceCount);
|
||||
cursor.removeSelectedText();
|
||||
cursor.endEditBlock();
|
||||
return prevBlock;
|
||||
}
|
||||
|
||||
// Returns the total langth of previous lines with pure whitespace.
|
||||
@@ -276,42 +162,175 @@ int previousEmptyLinesLength(const QTextBlock ¤tBlock)
|
||||
return length;
|
||||
}
|
||||
|
||||
static constexpr const int MinCharactersBeforeCurrentInBuffer = 200;
|
||||
static constexpr const int MaxCharactersBeforeCurrentInBuffer = 500;
|
||||
|
||||
int startOfIndentationBuffer(const QString &buffer, int start)
|
||||
void modifyToIndentEmptyLines(QByteArray &buffer, int &offset, int &length, const QTextBlock &block)
|
||||
{
|
||||
if (start < MaxCharactersBeforeCurrentInBuffer)
|
||||
return 0;
|
||||
const QString blockText = block.text().trimmed();
|
||||
const bool closingParenBlock = blockText.startsWith(')');
|
||||
if (length != 0 && !closingParenBlock)
|
||||
return;
|
||||
|
||||
auto it = buffer.cbegin() + (start - MinCharactersBeforeCurrentInBuffer);
|
||||
for (; it != buffer.cbegin() + (start - MaxCharactersBeforeCurrentInBuffer); --it) {
|
||||
if (*it == '{') {
|
||||
// Find the start of it's line.
|
||||
for (auto inner = it;
|
||||
inner != buffer.cbegin() + (start - MaxCharactersBeforeCurrentInBuffer);
|
||||
--inner) {
|
||||
if (*inner == '\n')
|
||||
return inner + 1 - buffer.cbegin();
|
||||
}
|
||||
break;
|
||||
}
|
||||
//This extra text works for the most cases.
|
||||
QByteArray extraText("a;");
|
||||
|
||||
// Search for previous character
|
||||
QTextBlock prevBlock = block.previous();
|
||||
while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty())
|
||||
prevBlock = prevBlock.previous();
|
||||
if (prevBlock.text().endsWith(','))
|
||||
extraText = "int a,";
|
||||
|
||||
if (closingParenBlock) {
|
||||
if (prevBlock.text().endsWith(','))
|
||||
extraText = "int a";
|
||||
else
|
||||
extraText = "&& a";
|
||||
}
|
||||
|
||||
return it - buffer.cbegin();
|
||||
length += extraText.length();
|
||||
buffer.insert(offset, extraText);
|
||||
}
|
||||
|
||||
int nextEndingScopePosition(const QString &buffer, int start)
|
||||
{
|
||||
if (start >= buffer.size() - 1)
|
||||
return buffer.size() - 1;
|
||||
static const int kMaxLinesFromCurrentBlock = 200;
|
||||
|
||||
for (auto it = buffer.cbegin() + (start + 1); it != buffer.cend(); ++it) {
|
||||
if (*it == '}')
|
||||
return it - buffer.cbegin();
|
||||
Replacements replacements(QByteArray buffer,
|
||||
int utf8Offset,
|
||||
int utf8Length,
|
||||
const QTextBlock *block = nullptr,
|
||||
const QChar &typedChar = QChar::Null)
|
||||
{
|
||||
Utils::FileName stylePath = styleConfigPath();
|
||||
FormatStyle style = formatStyle(stylePath);
|
||||
|
||||
int extraOffset = 0;
|
||||
if (block) {
|
||||
if (block->blockNumber() > kMaxLinesFromCurrentBlock) {
|
||||
extraOffset = Utils::Text::utf8NthLineOffset(
|
||||
block->document(), buffer, block->blockNumber() - kMaxLinesFromCurrentBlock);
|
||||
}
|
||||
buffer = buffer.mid(extraOffset,
|
||||
std::min(buffer.size(), utf8Offset + kMaxLinesFromCurrentBlock)
|
||||
- extraOffset);
|
||||
utf8Offset -= extraOffset;
|
||||
|
||||
const int emptySpaceLength = previousEmptyLinesLength(*block);
|
||||
utf8Offset -= emptySpaceLength;
|
||||
buffer.remove(utf8Offset, emptySpaceLength);
|
||||
|
||||
extraOffset += emptySpaceLength;
|
||||
|
||||
adjustFormatStyleForLineBreak(style);
|
||||
if (typedChar == QChar::Null)
|
||||
modifyToIndentEmptyLines(buffer, utf8Offset, utf8Length, *block);
|
||||
}
|
||||
|
||||
return buffer.size() - 1;
|
||||
std::vector<Range> ranges{{static_cast<unsigned int>(utf8Offset),
|
||||
static_cast<unsigned int>(utf8Length)}};
|
||||
FormattingAttemptStatus status;
|
||||
|
||||
const std::string assumedFilePath
|
||||
= stylePath.appendPath("test.cpp").toString().toStdString();
|
||||
Replacements replacements = reformat(style, buffer.data(), ranges, assumedFilePath, &status);
|
||||
|
||||
if (!status.FormatComplete)
|
||||
Replacements();
|
||||
|
||||
int lengthForFilter = 0;
|
||||
if (block == nullptr)
|
||||
lengthForFilter = utf8Length;
|
||||
|
||||
return filteredReplacements(replacements,
|
||||
utf8Offset,
|
||||
lengthForFilter,
|
||||
extraOffset);
|
||||
}
|
||||
|
||||
Utils::LineColumn utf16LineColumn(const QTextBlock &block,
|
||||
int blockOffsetUtf8,
|
||||
const QByteArray &utf8Buffer,
|
||||
int utf8Offset)
|
||||
{
|
||||
if (utf8Offset < blockOffsetUtf8 - 1)
|
||||
return Utils::LineColumn();
|
||||
|
||||
if (utf8Offset == blockOffsetUtf8 - 1) {
|
||||
const int lineStart = utf8Buffer.lastIndexOf('\n', utf8Offset - 1) + 1;
|
||||
const QByteArray lineText = utf8Buffer.mid(lineStart, utf8Offset - lineStart);
|
||||
return Utils::LineColumn(block.blockNumber(), QString::fromUtf8(lineText).size() + 1);
|
||||
}
|
||||
|
||||
int pos = blockOffsetUtf8;
|
||||
int prevPos = pos;
|
||||
int line = block.blockNumber(); // Start with previous line.
|
||||
while (pos != -1 && pos <= utf8Offset) {
|
||||
// Find the first pos which comes after offset and take the previous line.
|
||||
++line;
|
||||
prevPos = pos;
|
||||
pos = utf8Buffer.indexOf('\n', pos);
|
||||
if (pos != -1)
|
||||
++pos;
|
||||
}
|
||||
|
||||
const QByteArray lineText = utf8Buffer.mid(prevPos, utf8Offset - prevPos);
|
||||
return Utils::LineColumn(line, QString::fromUtf8(lineText).size() + 1);
|
||||
}
|
||||
|
||||
tooling::Replacements utf16Replacements(const QTextBlock &block,
|
||||
int blockOffsetUtf8,
|
||||
const QByteArray &utf8Buffer,
|
||||
const tooling::Replacements &replacements)
|
||||
{
|
||||
tooling::Replacements convertedReplacements;
|
||||
for (const Replacement &replacement : replacements) {
|
||||
const Utils::LineColumn lineColUtf16 = utf16LineColumn(
|
||||
block, blockOffsetUtf8, utf8Buffer, static_cast<int>(replacement.getOffset()));
|
||||
if (!lineColUtf16.isValid())
|
||||
continue;
|
||||
const int utf16Offset = Utils::Text::positionInText(block.document(),
|
||||
lineColUtf16.line,
|
||||
lineColUtf16.column);
|
||||
const int utf16Length = QString::fromUtf8(
|
||||
utf8Buffer.mid(static_cast<int>(replacement.getOffset()),
|
||||
static_cast<int>(replacement.getLength()))).size();
|
||||
Error error = convertedReplacements.add(
|
||||
Replacement(replacement.getFilePath(),
|
||||
static_cast<unsigned int>(utf16Offset),
|
||||
static_cast<unsigned int>(utf16Length),
|
||||
replacement.getReplacementText()));
|
||||
// Throws if error is not checked.
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
|
||||
return convertedReplacements;
|
||||
}
|
||||
|
||||
void applyReplacements(const QTextBlock &block,
|
||||
int blockOffsetUtf8,
|
||||
const QByteArray &utf8Buffer,
|
||||
const tooling::Replacements &replacements)
|
||||
{
|
||||
if (replacements.empty())
|
||||
return;
|
||||
|
||||
tooling::Replacements convertedReplacements = utf16Replacements(block,
|
||||
blockOffsetUtf8,
|
||||
utf8Buffer,
|
||||
replacements);
|
||||
|
||||
int fullOffsetShift = 0;
|
||||
QTextCursor editCursor(block);
|
||||
for (const Replacement &replacement : convertedReplacements) {
|
||||
const QString replacementString = QString::fromStdString(replacement.getReplacementText());
|
||||
const int replacementLength = static_cast<int>(replacement.getLength());
|
||||
editCursor.beginEditBlock();
|
||||
editCursor.setPosition(static_cast<int>(replacement.getOffset()) + fullOffsetShift);
|
||||
editCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor,
|
||||
replacementLength);
|
||||
editCursor.removeSelectedText();
|
||||
editCursor.insertText(replacementString);
|
||||
editCursor.endEditBlock();
|
||||
fullOffsetShift += replacementString.length() - replacementLength;
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
@@ -342,30 +361,34 @@ void ClangFormatIndenter::indent(QTextDocument *doc,
|
||||
bool autoTriggered)
|
||||
{
|
||||
if (typedChar == QChar::Null && (cursor.hasSelection() || !autoTriggered)) {
|
||||
TextEditorWidget *editor = TextEditorWidget::currentTextEditorWidget();
|
||||
int offset;
|
||||
int length;
|
||||
int utf8Offset;
|
||||
int utf8Length;
|
||||
const QByteArray buffer = doc->toPlainText().toUtf8();
|
||||
if (cursor.hasSelection()) {
|
||||
const QTextBlock start = doc->findBlock(cursor.selectionStart());
|
||||
const QTextBlock end = doc->findBlock(cursor.selectionEnd());
|
||||
offset = start.position();
|
||||
length = std::max(0, end.position() + end.length() - start.position() - 1);
|
||||
utf8Offset = Utils::Text::utf8NthLineOffset(doc, buffer, start.blockNumber() + 1);
|
||||
QTC_ASSERT(utf8Offset >= 0, return;);
|
||||
utf8Length =
|
||||
Utils::Text::textAt(
|
||||
QTextCursor(doc),
|
||||
start.position(),
|
||||
std::max(0, end.position() + end.length() - start.position() - 1))
|
||||
.toUtf8().size();
|
||||
applyReplacements(start,
|
||||
utf8Offset,
|
||||
buffer,
|
||||
replacements(buffer, utf8Offset, utf8Length));
|
||||
} else {
|
||||
const QTextBlock block = cursor.block();
|
||||
offset = block.position();
|
||||
length = std::max(0, block.length() - 1);
|
||||
utf8Offset = Utils::Text::utf8NthLineOffset(doc, buffer, block.blockNumber() + 1);
|
||||
QTC_ASSERT(utf8Offset >= 0, return;);
|
||||
utf8Length = block.text().toUtf8().size();
|
||||
applyReplacements(block,
|
||||
utf8Offset,
|
||||
buffer,
|
||||
replacements(buffer, utf8Offset, utf8Length));
|
||||
}
|
||||
QString buffer = editor->toPlainText();
|
||||
const int totalShift = startOfIndentationBuffer(buffer, offset);
|
||||
const int cutAtPos = nextEndingScopePosition(buffer, offset + length) + 1;
|
||||
buffer = buffer.mid(totalShift, cutAtPos - totalShift);
|
||||
offset -= totalShift;
|
||||
const std::string stdStrBefore = buffer.left(offset).toStdString();
|
||||
const std::string stdStrBuffer = stdStrBefore + buffer.mid(offset).toStdString();
|
||||
applyReplacements(doc,
|
||||
stdStrBuffer,
|
||||
replacements(stdStrBuffer, stdStrBefore.length(), length),
|
||||
totalShift);
|
||||
} else {
|
||||
indentBlock(doc, cursor.block(), typedChar, tabSettings);
|
||||
}
|
||||
@@ -389,46 +412,16 @@ void ClangFormatIndenter::indentBlock(QTextDocument *doc,
|
||||
if (!editor)
|
||||
return;
|
||||
|
||||
const QTextBlock prevBlock = clearFirstNonEmptyBlockFromExtraSpaces(block);
|
||||
trimFirstNonEmptyBlock(block);
|
||||
const QByteArray buffer = doc->toPlainText().toUtf8();
|
||||
const int utf8Offset = Utils::Text::utf8NthLineOffset(doc, buffer, block.blockNumber() + 1);
|
||||
QTC_ASSERT(utf8Offset >= 0, return;);
|
||||
const int utf8Length = block.text().toUtf8().size();
|
||||
|
||||
int offset = block.position();
|
||||
int length = std::max(0, block.length() - 1);
|
||||
QString buffer = editor->toPlainText();
|
||||
|
||||
int emptySpaceLength = previousEmptyLinesLength(block);
|
||||
offset -= emptySpaceLength;
|
||||
buffer.remove(offset, emptySpaceLength);
|
||||
|
||||
int extraPrevBlockLength{0};
|
||||
int prevBlockTextLength{1};
|
||||
bool prevBlockEndsWithPunctuation = false;
|
||||
if (typedChar == QChar::Null) {
|
||||
extraPrevBlockLength = modifyToIndentEmptyLines(buffer, offset, length, block);
|
||||
|
||||
const QString prevBlockText = prevBlock.text();
|
||||
prevBlockEndsWithPunctuation = prevBlockText.size() > 0
|
||||
? prevBlockText.at(prevBlockText.size() - 1).isPunct()
|
||||
: false;
|
||||
prevBlockTextLength = prevBlockText.size() + 1;
|
||||
}
|
||||
|
||||
const int totalShift = startOfIndentationBuffer(buffer, offset);
|
||||
const int cutAtPos = nextEndingScopePosition(buffer, offset + length) + 1;
|
||||
buffer = buffer.mid(totalShift, cutAtPos - totalShift);
|
||||
offset -= totalShift;
|
||||
const std::string stdStrBefore = buffer.left(offset).toStdString();
|
||||
const std::string stdStrBuffer = stdStrBefore + buffer.mid(offset).toStdString();
|
||||
applyReplacements(doc,
|
||||
stdStrBuffer,
|
||||
replacements(stdStrBuffer,
|
||||
stdStrBefore.length(),
|
||||
length,
|
||||
true,
|
||||
typedChar,
|
||||
emptySpaceLength - extraPrevBlockLength,
|
||||
prevBlockTextLength + extraPrevBlockLength,
|
||||
prevBlockEndsWithPunctuation),
|
||||
totalShift);
|
||||
applyReplacements(block,
|
||||
utf8Offset,
|
||||
buffer,
|
||||
replacements(buffer, utf8Offset, utf8Length, &block, typedChar));
|
||||
}
|
||||
|
||||
int ClangFormatIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings &)
|
||||
@@ -437,36 +430,14 @@ int ClangFormatIndenter::indentFor(const QTextBlock &block, const TextEditor::Ta
|
||||
if (!editor)
|
||||
return -1;
|
||||
|
||||
const QTextBlock prevBlock = clearFirstNonEmptyBlockFromExtraSpaces(block);
|
||||
trimFirstNonEmptyBlock(block);
|
||||
const QTextDocument *doc = block.document();
|
||||
const QByteArray buffer = doc->toPlainText().toUtf8();
|
||||
const int utf8Offset = Utils::Text::utf8NthLineOffset(doc, buffer, block.blockNumber() + 1);
|
||||
QTC_ASSERT(utf8Offset >= 0, return 0;);
|
||||
const int utf8Length = block.text().toUtf8().size();
|
||||
|
||||
int offset = block.position();
|
||||
int length = std::max(0, block.length() - 1);
|
||||
QString buffer = editor->toPlainText();
|
||||
|
||||
int emptySpaceLength = previousEmptyLinesLength(block);
|
||||
offset -= emptySpaceLength;
|
||||
buffer.replace(offset, emptySpaceLength, "");
|
||||
int extraPrevBlockLength = modifyToIndentEmptyLines(buffer, offset, length, block);
|
||||
|
||||
const QString prevBlockText = prevBlock.text();
|
||||
bool prevBlockEndsWithPunctuation = prevBlockText.size() > 0
|
||||
? prevBlockText.at(prevBlockText.size() - 1).isPunct()
|
||||
: false;
|
||||
|
||||
const int totalShift = startOfIndentationBuffer(buffer, offset);
|
||||
const int cutAtPos = nextEndingScopePosition(buffer, offset + length) + 1;
|
||||
buffer = buffer.mid(totalShift, cutAtPos - totalShift);
|
||||
offset -= totalShift;
|
||||
const std::string stdStrBefore = buffer.left(offset).toStdString();
|
||||
const std::string stdStrBuffer = stdStrBefore + buffer.mid(offset).toStdString();
|
||||
Replacements toReplace = replacements(stdStrBuffer,
|
||||
stdStrBefore.length(),
|
||||
length,
|
||||
true,
|
||||
QChar::Null,
|
||||
emptySpaceLength - extraPrevBlockLength,
|
||||
prevBlockText.size() + extraPrevBlockLength + 1,
|
||||
prevBlockEndsWithPunctuation);
|
||||
Replacements toReplace = replacements(buffer, utf8Offset, utf8Length, &block);
|
||||
|
||||
if (toReplace.empty())
|
||||
return -1;
|
||||
@@ -481,7 +452,7 @@ int ClangFormatIndenter::indentFor(const QTextBlock &block, const TextEditor::Ta
|
||||
|
||||
TabSettings ClangFormatIndenter::tabSettings() const
|
||||
{
|
||||
FormatStyle style = formatStyle();
|
||||
FormatStyle style = formatStyle(styleConfigPath());
|
||||
TabSettings tabSettings;
|
||||
|
||||
switch (style.UseTab) {
|
||||
@@ -495,8 +466,8 @@ TabSettings ClangFormatIndenter::tabSettings() const
|
||||
tabSettings.m_tabPolicy = TabSettings::MixedTabPolicy;
|
||||
}
|
||||
|
||||
tabSettings.m_tabSize = style.TabWidth;
|
||||
tabSettings.m_indentSize = style.IndentWidth;
|
||||
tabSettings.m_tabSize = static_cast<int>(style.TabWidth);
|
||||
tabSettings.m_indentSize = static_cast<int>(style.IndentWidth);
|
||||
|
||||
if (style.AlignAfterOpenBracket)
|
||||
tabSettings.m_continuationAlignBehavior = TabSettings::ContinuationAlignWithSpaces;
|
||||
@@ -506,5 +477,4 @@ TabSettings ClangFormatIndenter::tabSettings() const
|
||||
return tabSettings;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangFormat
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <texteditor/indenter.h>
|
||||
|
||||
namespace ClangFormat {
|
||||
namespace Internal {
|
||||
|
||||
class ClangFormatIndenter final : public TextEditor::Indenter
|
||||
{
|
||||
@@ -42,9 +41,9 @@ public:
|
||||
const QTextCursor &cursor,
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
void indentBlock(QTextDocument *doc,
|
||||
const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) override;
|
||||
|
||||
bool isElectricCharacter(const QChar &ch) const override;
|
||||
@@ -53,5 +52,4 @@ public:
|
||||
TextEditor::TabSettings tabSettings() const override;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangFormat
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
#include <projectexplorer/projectpanelfactory.h>
|
||||
#include <projectexplorer/target.h>
|
||||
|
||||
#include <clang/Format/Format.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QDebug>
|
||||
#include <QMainWindow>
|
||||
@@ -56,7 +58,6 @@
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace ClangFormat {
|
||||
namespace Internal {
|
||||
|
||||
class ClangFormatOptionsPage : public Core::IOptionsPage
|
||||
{
|
||||
@@ -98,7 +99,7 @@ bool ClangFormatPlugin::initialize(const QStringList &arguments, QString *errorS
|
||||
{
|
||||
Q_UNUSED(arguments);
|
||||
Q_UNUSED(errorString);
|
||||
|
||||
#ifdef KEEP_LINE_BREAKS_FOR_NON_EMPTY_LINES_BACKPORTED
|
||||
m_optionsPage = std::make_unique<ClangFormatOptionsPage>();
|
||||
|
||||
auto panelFactory = new ProjectPanelFactory();
|
||||
@@ -112,9 +113,8 @@ bool ClangFormatPlugin::initialize(const QStringList &arguments, QString *errorS
|
||||
CppTools::CppModelManager::instance()->setCppIndenterCreator([]() {
|
||||
return new ClangFormatIndenter();
|
||||
});
|
||||
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangFormat
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <memory>
|
||||
|
||||
namespace ClangFormat {
|
||||
namespace Internal {
|
||||
|
||||
class ClangFormatOptionsPage;
|
||||
|
||||
@@ -50,5 +49,4 @@ private:
|
||||
std::unique_ptr<ClangFormatOptionsPage> m_optionsPage;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangTools
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <clang/Format/Format.h>
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace ClangFormat {
|
||||
|
||||
inline void createStyleFileIfNeeded(Utils::FileName styleConfigPath)
|
||||
{
|
||||
const QString configFile = styleConfigPath.appendPath(".clang-format").toString();
|
||||
if (QFile::exists(configFile))
|
||||
return;
|
||||
|
||||
clang::format::FormatStyle newStyle = clang::format::getLLVMStyle();
|
||||
std::fstream newStyleFile(configFile.toStdString(), std::fstream::out);
|
||||
if (newStyleFile.is_open()) {
|
||||
newStyleFile << clang::format::configurationAsText(newStyle);
|
||||
newStyleFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
variableChooser->addSupportedWidget (m_customArguments);
|
||||
|
||||
m_unusedFunction->setToolTip(tr("Disables multithreaded check."));
|
||||
m_ignorePatterns->setToolTip(tr("Comma-separated wildcards of full file paths."
|
||||
m_ignorePatterns->setToolTip(tr("Comma-separated wildcards of full file paths. "
|
||||
"Files still can be checked if others include them."));
|
||||
m_addIncludePaths->setToolTip(tr("Can find missing includes but makes "
|
||||
"checking slower. Use only when needed."));
|
||||
|
||||
@@ -1326,6 +1326,11 @@ void CppModelManager::renameIncludes(const QString &oldFileName, const QString &
|
||||
}
|
||||
}
|
||||
|
||||
void CppModelManager::setBackendJobsPostponed(bool postponed)
|
||||
{
|
||||
d->m_activeModelManagerSupport->setBackendJobsPostponed(postponed);
|
||||
}
|
||||
|
||||
void CppModelManager::onCoreAboutToClose()
|
||||
{
|
||||
Core::ProgressManager::cancelTasks(CppTools::Constants::TASK_INDEX);
|
||||
|
||||
@@ -238,6 +238,8 @@ public:
|
||||
|
||||
void renameIncludes(const QString &oldFileName, const QString &newFileName);
|
||||
|
||||
void setBackendJobsPostponed(bool postponed);
|
||||
|
||||
signals:
|
||||
/// Project data might be locked while this is emitted.
|
||||
void aboutToRemoveFiles(const QStringList &files);
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
virtual FollowSymbolInterface &followSymbolInterface() = 0;
|
||||
virtual RefactoringEngineInterface &refactoringEngineInterface() = 0;
|
||||
virtual std::unique_ptr<AbstractOverviewModel> createOverviewModel() = 0;
|
||||
virtual void setBackendJobsPostponed(bool yesno) = 0;
|
||||
};
|
||||
|
||||
class CPPTOOLS_EXPORT ModelManagerSupportProvider
|
||||
|
||||
@@ -47,6 +47,7 @@ public:
|
||||
FollowSymbolInterface &followSymbolInterface() final;
|
||||
RefactoringEngineInterface &refactoringEngineInterface() final;
|
||||
std::unique_ptr<AbstractOverviewModel> createOverviewModel() final;
|
||||
void setBackendJobsPostponed(bool) final {}
|
||||
|
||||
private:
|
||||
QScopedPointer<CppCompletionAssistProvider> m_completionAssistProvider;
|
||||
|
||||
@@ -50,6 +50,7 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
{
|
||||
"android-",
|
||||
{
|
||||
"tring-find-startswith",
|
||||
{
|
||||
"cloexec-",
|
||||
{
|
||||
@@ -76,7 +77,8 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
"open",
|
||||
"socket"
|
||||
}
|
||||
}
|
||||
},
|
||||
"comparison-in-temp-failure-retry"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -93,16 +95,69 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
"bool-pointer-implicit-conversion",
|
||||
"copy-constructor-init",
|
||||
"dangling-handle",
|
||||
"exception-escape",
|
||||
"fold-init-type",
|
||||
"forward-declaration-namespace",
|
||||
"forwarding-reference-overload",
|
||||
"inaccurate-erase",
|
||||
"incorrect-roundings",
|
||||
"integer-division",
|
||||
"misplaced-operator-in-strlen-in-alloc",
|
||||
"lambda-function-name",
|
||||
{
|
||||
"macro-",
|
||||
{
|
||||
"parentheses",
|
||||
"repeated-side-effects"
|
||||
}
|
||||
},
|
||||
{
|
||||
"misplaced-",
|
||||
{
|
||||
"operator-in-strlen-in-alloc",
|
||||
"widening-cast"
|
||||
}
|
||||
},
|
||||
"move-forwarding-reference",
|
||||
"multiple-statement-macro",
|
||||
"string-constructor",
|
||||
"suspicious-memset-usage",
|
||||
"narrowing-conversions",
|
||||
"parent-virtual-call",
|
||||
{
|
||||
"sizeof-",
|
||||
{
|
||||
"container",
|
||||
"expression"
|
||||
}
|
||||
},
|
||||
{
|
||||
"string-",
|
||||
{
|
||||
"constructor",
|
||||
"integer-assignment",
|
||||
"literal-with-embedded-nul"
|
||||
}
|
||||
},
|
||||
{
|
||||
"suspicious-",
|
||||
{
|
||||
"enum-usage",
|
||||
"memset-usage",
|
||||
"missing-comma",
|
||||
"semicolon",
|
||||
"string-compare"
|
||||
}
|
||||
},
|
||||
"swapped-arguments",
|
||||
"terminating-continue",
|
||||
"throw-keyword-missing",
|
||||
"undefined-memory-manipulation",
|
||||
"undelegated-constructor",
|
||||
{
|
||||
"unused-",
|
||||
{
|
||||
"raii",
|
||||
"return-value"
|
||||
}
|
||||
},
|
||||
"use-after-move",
|
||||
"virtual-near-miss"
|
||||
}
|
||||
@@ -126,14 +181,22 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
"fio38-c",
|
||||
"flp30-c",
|
||||
"msc30-c",
|
||||
"msc32-c",
|
||||
"msc50-cpp",
|
||||
"msc51-cpp",
|
||||
"oop11-cpp"
|
||||
}
|
||||
},
|
||||
{
|
||||
"clang-analyzer-",
|
||||
{
|
||||
"apiModeling.google.GTest",
|
||||
{
|
||||
"apiModeling.",
|
||||
{
|
||||
"TrustNonnull",
|
||||
"google.GTest"
|
||||
}
|
||||
},
|
||||
{
|
||||
"core.",
|
||||
{
|
||||
@@ -168,6 +231,7 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
{
|
||||
"cplusplus.",
|
||||
{
|
||||
"InnerPointer",
|
||||
"NewDelete",
|
||||
"NewDeleteLeaks",
|
||||
"SelfAssignment"
|
||||
@@ -207,7 +271,13 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
}
|
||||
}
|
||||
},
|
||||
"performance.Padding",
|
||||
{
|
||||
"performance.",
|
||||
{
|
||||
"GCDAntipattern",
|
||||
"Padding"
|
||||
}
|
||||
},
|
||||
"portability.UnixAPI"
|
||||
}
|
||||
},
|
||||
@@ -222,6 +292,7 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
"cocoa.",
|
||||
{
|
||||
"AtSync",
|
||||
"AutoreleaseWrite",
|
||||
"ClassRelease",
|
||||
"Dealloc",
|
||||
"IncompatibleMethodTypes",
|
||||
@@ -233,6 +304,7 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
"NonNilReturnValue",
|
||||
"ObjCGenerics",
|
||||
"RetainCount",
|
||||
"RunLoopAutoreleaseLeak",
|
||||
"SelfInit",
|
||||
"SuperDealloc",
|
||||
"UnusedIvars",
|
||||
@@ -264,6 +336,9 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
"insecureAPI.",
|
||||
{
|
||||
"UncheckedReturn",
|
||||
"bcmp",
|
||||
"bcopy",
|
||||
"bzero",
|
||||
"getpw",
|
||||
"gets",
|
||||
"mkstemp",
|
||||
@@ -306,8 +381,10 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
{
|
||||
"cppcoreguidelines-",
|
||||
{
|
||||
"avoid-goto",
|
||||
"c-copy-assignment-signature",
|
||||
"interfaces-global-init",
|
||||
"narrowing-conversions",
|
||||
"no-malloc",
|
||||
"owning-memory",
|
||||
{
|
||||
@@ -343,7 +420,12 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
"fuchsia-",
|
||||
{
|
||||
"default-arguments",
|
||||
"header-anon-namespaces",
|
||||
"multiple-inheritance",
|
||||
"overloaded-operator",
|
||||
"restrict-system-includes",
|
||||
"statically-constructed-objects",
|
||||
"trailing-return",
|
||||
"virtual-inheritance"
|
||||
}
|
||||
},
|
||||
@@ -375,7 +457,6 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
"casting",
|
||||
"function-size",
|
||||
"namespace-comments",
|
||||
"redundant-smartptr-get",
|
||||
"todo"
|
||||
}
|
||||
},
|
||||
@@ -383,7 +464,6 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
"runtime-",
|
||||
{
|
||||
"int",
|
||||
"member-string-references",
|
||||
"operator",
|
||||
"references"
|
||||
}
|
||||
@@ -393,6 +473,7 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
{
|
||||
"hicpp-",
|
||||
{
|
||||
"avoid-goto",
|
||||
"braces-around-statements",
|
||||
"deprecated-headers",
|
||||
"exception-baseclass",
|
||||
@@ -401,6 +482,7 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
"invalid-access-moved",
|
||||
"member-init",
|
||||
"move-const-arg",
|
||||
"multiway-paths-covered",
|
||||
"named-parameter",
|
||||
"new-delete-operators",
|
||||
{
|
||||
@@ -449,62 +531,19 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
"misc-",
|
||||
{
|
||||
"definitions-in-headers",
|
||||
"forwarding-reference-overload",
|
||||
"incorrect-roundings",
|
||||
"lambda-function-name",
|
||||
{
|
||||
"macro-",
|
||||
{
|
||||
"parentheses",
|
||||
"repeated-side-effects"
|
||||
}
|
||||
},
|
||||
{
|
||||
"misplaced-",
|
||||
{
|
||||
"const",
|
||||
"widening-cast"
|
||||
}
|
||||
},
|
||||
"misplaced-const",
|
||||
"new-delete-overloads",
|
||||
"non-copyable-objects",
|
||||
"redundant-expression",
|
||||
{
|
||||
"sizeof-",
|
||||
{
|
||||
"container",
|
||||
"expression"
|
||||
}
|
||||
},
|
||||
"static-assert",
|
||||
{
|
||||
"string-",
|
||||
{
|
||||
"compare",
|
||||
"integer-assignment",
|
||||
"literal-with-embedded-nul"
|
||||
}
|
||||
},
|
||||
{
|
||||
"suspicious-",
|
||||
{
|
||||
"enum-usage",
|
||||
"missing-comma",
|
||||
"semicolon",
|
||||
"string-compare"
|
||||
}
|
||||
},
|
||||
"swapped-arguments",
|
||||
"throw-by-value-catch-by-reference",
|
||||
"unconventional-assign-operator",
|
||||
"undelegated-constructor",
|
||||
"uniqueptr-reset-release",
|
||||
{
|
||||
"unused-",
|
||||
{
|
||||
"alias-decls",
|
||||
"parameters",
|
||||
"raii",
|
||||
"using-decls"
|
||||
}
|
||||
}
|
||||
@@ -554,6 +593,7 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
"nullptr",
|
||||
"override",
|
||||
"transparent-functors",
|
||||
"uncaught-exceptions",
|
||||
"using"
|
||||
}
|
||||
}
|
||||
@@ -609,7 +649,8 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
"copy-initialization",
|
||||
"value-param"
|
||||
}
|
||||
}
|
||||
},
|
||||
"simd-intrinsics"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -646,7 +687,13 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
}
|
||||
}
|
||||
},
|
||||
"simplify-boolean-expr",
|
||||
{
|
||||
"simplify-",
|
||||
{
|
||||
"boolean-expr",
|
||||
"subscript-expr"
|
||||
}
|
||||
},
|
||||
{
|
||||
"static-",
|
||||
{
|
||||
@@ -654,7 +701,9 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT
|
||||
"definition-in-anonymous-namespace"
|
||||
}
|
||||
},
|
||||
"uniqueptr-delete-release"
|
||||
"string-compare",
|
||||
"uniqueptr-delete-release",
|
||||
"rary-objects"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1709,7 +1709,6 @@ void BreakHandler::removeBreakpoint(const Breakpoint &bp)
|
||||
break;
|
||||
case BreakpointInserted:
|
||||
case BreakpointInsertionProceeding:
|
||||
bp->setState(BreakpointRemoveRequested);
|
||||
requestBreakpointRemoval(bp);
|
||||
break;
|
||||
case BreakpointNew:
|
||||
@@ -2565,17 +2564,17 @@ bool BreakpointManager::setData(const QModelIndex &idx, const QVariant &value, i
|
||||
// setCurrentIndex(index(row, 0)); FIXME
|
||||
return true;
|
||||
}
|
||||
// if (kev->key() == Qt::Key_Space) {
|
||||
// const QModelIndexList selectedIds = ev.selectedRows();
|
||||
// if (!selectedIds.isEmpty()) {
|
||||
// const GlobalBreakpoints gbps = findBreakpointsByIndex(selectedIds);
|
||||
// const bool isEnabled = gbps.isEmpty() || gbps.at(0)->isEnabled();
|
||||
// for (GlobalBreakpoint gbp : gbps)
|
||||
// gbp->m_parameters.enabled = isEnabled;
|
||||
if (kev->key() == Qt::Key_Space) {
|
||||
const QModelIndexList selectedIds = ev.selectedRows();
|
||||
if (!selectedIds.isEmpty()) {
|
||||
const GlobalBreakpoints gbps = findBreakpointsByIndex(selectedIds);
|
||||
const bool isEnabled = gbps.isEmpty() || gbps.at(0)->isEnabled();
|
||||
for (GlobalBreakpoint gbp : gbps)
|
||||
gbp->setEnabled(!isEnabled);
|
||||
// scheduleSynchronization();
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ev.as<QMouseEvent>(QEvent::MouseButtonDblClick)) {
|
||||
|
||||
@@ -98,7 +98,7 @@ private:
|
||||
void updateMarker();
|
||||
void updateMarkerIcon();
|
||||
void destroyMarker();
|
||||
void scheduleSynchronization();
|
||||
// void scheduleSynchronization();
|
||||
QPointer<DebuggerEngine> usingEngine() const;
|
||||
|
||||
bool isEngineRunning() const;
|
||||
|
||||
@@ -224,7 +224,7 @@ void CdbEngine::init()
|
||||
m_stopMode = NoStopRequested;
|
||||
m_nextCommandToken = 0;
|
||||
m_currentBuiltinResponseToken = -1;
|
||||
m_operateByInstruction = true; // Default CDB setting.
|
||||
m_lastOperateByInstruction = true; // Default CDB setting.
|
||||
m_hasDebuggee = false;
|
||||
m_sourceStepInto = false;
|
||||
m_watchPointX = m_watchPointY = 0;
|
||||
@@ -266,14 +266,13 @@ void CdbEngine::init()
|
||||
|
||||
CdbEngine::~CdbEngine() = default;
|
||||
|
||||
void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
|
||||
void CdbEngine::adjustOperateByInstruction(bool operateByInstruction)
|
||||
{
|
||||
DebuggerEngine::operateByInstructionTriggered(operateByInstruction);
|
||||
if (m_operateByInstruction == operateByInstruction)
|
||||
if (m_lastOperateByInstruction == operateByInstruction)
|
||||
return;
|
||||
m_operateByInstruction = operateByInstruction;
|
||||
runCommand({QLatin1String(m_operateByInstruction ? "l-t" : "l+t"), NoFlags});
|
||||
runCommand({QLatin1String(m_operateByInstruction ? "l-s" : "l+s"), NoFlags});
|
||||
m_lastOperateByInstruction = operateByInstruction;
|
||||
runCommand({QLatin1String(m_lastOperateByInstruction ? "l-t" : "l+t"), NoFlags});
|
||||
runCommand({QLatin1String(m_lastOperateByInstruction ? "l-s" : "l+s"), NoFlags});
|
||||
}
|
||||
|
||||
bool CdbEngine::canHandleToolTip(const DebuggerToolTipContext &context) const
|
||||
@@ -521,7 +520,7 @@ void CdbEngine::handleInitialSessionIdle()
|
||||
const DebuggerRunParameters &rp = runParameters();
|
||||
if (!rp.commandsAfterConnect.isEmpty())
|
||||
runCommand({rp.commandsAfterConnect, NoFlags});
|
||||
operateByInstructionTriggered(operatesByInstruction());
|
||||
//operateByInstructionTriggered(operatesByInstruction());
|
||||
// QmlCppEngine expects the QML engine to be connected before any breakpoints are hit
|
||||
// (attemptBreakpointSynchronization() will be directly called then)
|
||||
if (rp.breakOnMain) {
|
||||
@@ -758,10 +757,11 @@ bool CdbEngine::hasCapability(unsigned cap) const
|
||||
|AdditionalQmlStackCapability);
|
||||
}
|
||||
|
||||
void CdbEngine::executeStep()
|
||||
void CdbEngine::executeStepIn(bool byInstruction)
|
||||
{
|
||||
if (!m_operateByInstruction)
|
||||
if (!m_lastOperateByInstruction)
|
||||
m_sourceStepInto = true; // See explanation at handleStackTrace().
|
||||
adjustOperateByInstruction(byInstruction);
|
||||
runCommand({"t", NoFlags}); // Step into-> t (trace)
|
||||
STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
|
||||
notifyInferiorRunRequested();
|
||||
@@ -774,23 +774,14 @@ void CdbEngine::executeStepOut()
|
||||
notifyInferiorRunRequested();
|
||||
}
|
||||
|
||||
void CdbEngine::executeNext()
|
||||
void CdbEngine::executeStepOver(bool byInstruction)
|
||||
{
|
||||
adjustOperateByInstruction(byInstruction);
|
||||
runCommand({"p", NoFlags}); // Step over -> p
|
||||
STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
|
||||
notifyInferiorRunRequested();
|
||||
}
|
||||
|
||||
void CdbEngine::executeStepI()
|
||||
{
|
||||
executeStep();
|
||||
}
|
||||
|
||||
void CdbEngine::executeNextI()
|
||||
{
|
||||
executeNext();
|
||||
}
|
||||
|
||||
void CdbEngine::continueInferior()
|
||||
{
|
||||
STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
|
||||
@@ -1756,8 +1747,9 @@ unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
|
||||
*message = bp->msgWatchpointByExpressionTriggered(bp->expression(), tid);
|
||||
else
|
||||
*message = bp->msgBreakpointTriggered(tid);
|
||||
rc |= StopReportStatusMessage|StopNotifyStop;
|
||||
rc |= StopReportStatusMessage;
|
||||
}
|
||||
rc |= StopNotifyStop;
|
||||
return rc;
|
||||
}
|
||||
if (reason == "exception") {
|
||||
@@ -1848,7 +1840,7 @@ void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointT
|
||||
if (stack.isValid()) {
|
||||
switch (parseStackTrace(stack, sourceStepInto)) {
|
||||
case ParseStackStepInto: // Hit on a frame while step into, see parseStackTrace().
|
||||
executeStep();
|
||||
executeStepIn(operatesByInstruction());
|
||||
return;
|
||||
case ParseStackStepOut: // Hit on a frame with no source while step into.
|
||||
executeStepOut();
|
||||
|
||||
@@ -41,7 +41,7 @@ class CdbCommand;
|
||||
struct MemoryViewCookie;
|
||||
class StringInputStream;
|
||||
|
||||
class CdbEngine : public DebuggerEngine
|
||||
class CdbEngine : public CppDebuggerEngine
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -64,11 +64,9 @@ public:
|
||||
void watchPoint(const QPoint &) override;
|
||||
void setRegisterValue(const QString &name, const QString &value) override;
|
||||
|
||||
void executeStep() override;
|
||||
void executeStepOver(bool byInstruction) override;
|
||||
void executeStepIn(bool byInstruction) override;
|
||||
void executeStepOut() override;
|
||||
void executeNext() override;
|
||||
void executeStepI() override;
|
||||
void executeNextI() override;
|
||||
|
||||
void continueInferior() override;
|
||||
void interruptInferior() override;
|
||||
@@ -113,7 +111,7 @@ private:
|
||||
void processError();
|
||||
void processFinished();
|
||||
void runCommand(const DebuggerCommand &cmd) override;
|
||||
void operateByInstructionTriggered(bool) override;
|
||||
void adjustOperateByInstruction(bool);
|
||||
|
||||
void createFullBacktrace();
|
||||
|
||||
@@ -217,7 +215,7 @@ private:
|
||||
int m_currentBuiltinResponseToken = -1;
|
||||
QMap<QString, NormalizedSourceFileName> m_normalizedFileCache;
|
||||
const QString m_extensionCommandPrefix; //!< Library name used as prefix
|
||||
bool m_operateByInstruction = true; // Default CDB setting.
|
||||
bool m_lastOperateByInstruction = true; // Default CDB setting.
|
||||
bool m_hasDebuggee = false;
|
||||
enum Wow64State {
|
||||
wow64Uninitialized,
|
||||
|
||||
@@ -37,6 +37,8 @@ const char MODE_DEBUG[] = "Mode.Debug";
|
||||
const char C_DEBUGMODE[] = "Debugger.DebugMode";
|
||||
const char C_CPPDEBUGGER[] = "Gdb Debugger";
|
||||
const char C_QMLDEBUGGER[] = "Qml/JavaScript Debugger";
|
||||
const char C_DEBUGGER_RUNNING[] = "Debugger.Running";
|
||||
const char C_DEBUGGER_NOTRUNNING[] = "Debugger.NotRunning";
|
||||
|
||||
const char PRESET_PERSPECTIVE_ID[] = "Debugger.Perspective.Preset";
|
||||
|
||||
@@ -54,6 +56,7 @@ const char ABORT[] = "Debugger.Abort";
|
||||
const char STEP[] = "Debugger.StepLine";
|
||||
const char STEPOUT[] = "Debugger.StepOut";
|
||||
const char NEXT[] = "Debugger.NextLine";
|
||||
const char START_AND_BREAK_ON_MAIN[]= "Debugger.StartAndBreakOnMain";
|
||||
const char REVERSE[] = "Debugger.ReverseDirection";
|
||||
const char RESET[] = "Debugger.Reset";
|
||||
const char OPERATE_BY_INSTRUCTION[] = "Debugger.OperateByInstruction";
|
||||
|
||||
@@ -488,7 +488,7 @@ public:
|
||||
QAction m_breakAction{tr("Toggle Breakpoint")};
|
||||
QAction m_resetAction{tr("Restart Debugging")};
|
||||
OptionalAction m_operateByInstructionAction{tr("Operate by Instruction")};
|
||||
QAction m_recordForReverseOperationAction{tr("Record information to allpow reversal of Direction")};
|
||||
QAction m_recordForReverseOperationAction{tr("Record Information to Allow Reversal of Direction")};
|
||||
OptionalAction m_operateInReverseDirectionAction{tr("Reverse Direction")};
|
||||
OptionalAction m_snapshotAction{tr("Take Snapshot of Process State")};
|
||||
|
||||
@@ -526,7 +526,7 @@ void DebuggerEnginePrivate::setupViews()
|
||||
m_operateByInstructionAction.setIcon(Debugger::Icons::SINGLE_INSTRUCTION_MODE.icon());
|
||||
m_operateByInstructionAction.setCheckable(true);
|
||||
m_operateByInstructionAction.setChecked(false);
|
||||
m_operateByInstructionAction.setToolTip("<p>" + tr("This switches the debugger to instruction-wise "
|
||||
m_operateByInstructionAction.setToolTip("<p>" + tr("Switches the debugger to instruction-wise "
|
||||
"operation mode. In this mode, stepping operates on single "
|
||||
"instructions and the source location view also shows the "
|
||||
"disassembled instructions."));
|
||||
@@ -684,18 +684,18 @@ void DebuggerEnginePrivate::setupViews()
|
||||
connect(&m_abortAction, &QAction::triggered,
|
||||
m_engine, &DebuggerEngine::abortDebugger);
|
||||
|
||||
m_resetAction.setToolTip(tr("Restart the debugging session."));
|
||||
m_resetAction.setToolTip(tr("Restarts the debugging session."));
|
||||
m_resetAction.setIcon(Icons::RESTART_TOOLBAR.icon());
|
||||
connect(&m_resetAction, &QAction::triggered,
|
||||
m_engine, &DebuggerEngine::handleReset);
|
||||
|
||||
m_stepOverAction.setIcon(Icons::STEP_OVER_TOOLBAR.icon());
|
||||
connect(&m_stepOverAction, &QAction::triggered,
|
||||
m_engine, &DebuggerEngine::handleExecNext);
|
||||
m_engine, &DebuggerEngine::handleExecStepOver);
|
||||
|
||||
m_stepIntoAction.setIcon(Icons::STEP_INTO_TOOLBAR.icon());
|
||||
connect(&m_stepIntoAction, &QAction::triggered,
|
||||
m_engine, &DebuggerEngine::handleExecStep);
|
||||
m_engine, &DebuggerEngine::handleExecStepIn);
|
||||
|
||||
m_stepOutAction.setIcon(Icons::STEP_OUT_TOOLBAR.icon());
|
||||
connect(&m_stepOutAction, &QAction::triggered,
|
||||
@@ -1111,7 +1111,7 @@ void DebuggerEngine::notifyEngineSetupFailed()
|
||||
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << this << state());
|
||||
setState(EngineSetupFailed);
|
||||
if (d->m_isPrimaryEngine) {
|
||||
showMessage(tr("Debugging has failed"), NormalMessageFormat);
|
||||
showMessage(tr("Debugging has failed."), NormalMessageFormat);
|
||||
d->m_progress.setProgressValue(900);
|
||||
d->m_progress.reportCanceled();
|
||||
d->m_progress.reportFinished();
|
||||
@@ -1472,7 +1472,7 @@ void DebuggerEnginePrivate::updateReverseActions()
|
||||
m_operateInReverseDirectionAction.setVisible(canReverse);
|
||||
m_operateInReverseDirectionAction.setEnabled(canReverse && stopped && doesRecord);
|
||||
m_operateInReverseDirectionAction.setIcon(Icons::DIRECTION_BACKWARD.icon());
|
||||
m_operateInReverseDirectionAction.setText(DebuggerEngine::tr("Operate in reverse direction"));
|
||||
m_operateInReverseDirectionAction.setText(DebuggerEngine::tr("Operate in Reverse Direction"));
|
||||
}
|
||||
|
||||
void DebuggerEnginePrivate::cleanupViews()
|
||||
@@ -1787,16 +1787,16 @@ DebuggerToolTipManager *DebuggerEngine::toolTipManager()
|
||||
return &d->m_toolTipManager;
|
||||
}
|
||||
|
||||
bool DebuggerEngine::debuggerActionsEnabled() const
|
||||
{
|
||||
return debuggerActionsEnabledHelper(d->m_state);
|
||||
}
|
||||
|
||||
bool DebuggerEngine::operatesByInstruction() const
|
||||
{
|
||||
return d->m_operateByInstructionAction.isChecked();
|
||||
}
|
||||
|
||||
bool DebuggerEngine::debuggerActionsEnabled() const
|
||||
{
|
||||
return debuggerActionsEnabledHelper(d->m_state);
|
||||
}
|
||||
|
||||
void DebuggerEngine::operateByInstructionTriggered(bool on)
|
||||
{
|
||||
// Go to source only if we have the file.
|
||||
@@ -2212,7 +2212,7 @@ void DebuggerEngine::updateLocalsView(const GdbMi &all)
|
||||
static int count = 0;
|
||||
showMessage(QString("<Rebuild Watchmodel %1 @ %2 >")
|
||||
.arg(++count).arg(LogWindow::logTimeStamp()), LogMiscInput);
|
||||
showMessage(tr("Finished retrieving data"), 400, StatusBar);
|
||||
showMessage(tr("Finished retrieving data."), 400, StatusBar);
|
||||
|
||||
d->m_toolTipManager.updateToolTips();
|
||||
|
||||
@@ -2291,32 +2291,16 @@ void DebuggerEngine::handleReset()
|
||||
resetInferior();
|
||||
}
|
||||
|
||||
void DebuggerEngine::handleExecStep()
|
||||
void DebuggerEngine::handleExecStepIn()
|
||||
{
|
||||
if (state() == DebuggerNotReady) {
|
||||
DebuggerRunTool::setBreakOnMainNextTime();
|
||||
ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE);
|
||||
} else {
|
||||
resetLocation();
|
||||
if (operatesByInstruction())
|
||||
executeStepI();
|
||||
else
|
||||
executeStep();
|
||||
}
|
||||
resetLocation();
|
||||
executeStepIn(operatesByInstruction());
|
||||
}
|
||||
|
||||
void DebuggerEngine::handleExecNext()
|
||||
void DebuggerEngine::handleExecStepOver()
|
||||
{
|
||||
if (state() == DebuggerNotReady) {
|
||||
DebuggerRunTool::setBreakOnMainNextTime();
|
||||
ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE);
|
||||
} else {
|
||||
resetLocation();
|
||||
if (operatesByInstruction())
|
||||
executeNextI();
|
||||
else
|
||||
executeNext();
|
||||
}
|
||||
resetLocation();
|
||||
executeStepOver(operatesByInstruction());
|
||||
}
|
||||
|
||||
void DebuggerEngine::handleExecStepOut()
|
||||
|
||||
@@ -434,8 +434,8 @@ public:
|
||||
void handleUserStop();
|
||||
void handleAbort();
|
||||
void handleReset();
|
||||
void handleExecStep();
|
||||
void handleExecNext();
|
||||
void handleExecStepIn();
|
||||
void handleExecStepOver();
|
||||
void handleExecStepOut();
|
||||
void handleExecReturn();
|
||||
void handleExecJumpToLine();
|
||||
@@ -480,11 +480,9 @@ protected:
|
||||
virtual void resetInferior() {}
|
||||
|
||||
virtual void detachDebugger() {}
|
||||
virtual void executeStep() {}
|
||||
virtual void executeStepOver(bool /*byInstruction*/ = false) {}
|
||||
virtual void executeStepIn(bool /*byInstruction*/ = false) {}
|
||||
virtual void executeStepOut() {}
|
||||
virtual void executeNext() {}
|
||||
virtual void executeStepI() {}
|
||||
virtual void executeNextI() {}
|
||||
virtual void executeReturn() {}
|
||||
|
||||
virtual void continueInferior() {}
|
||||
|
||||
@@ -56,7 +56,6 @@ const char DEBUGGER_INFORMATION_DISPLAYNAME[] = "DisplayName";
|
||||
const char DEBUGGER_INFORMATION_ID[] = "Id";
|
||||
const char DEBUGGER_INFORMATION_ENGINETYPE[] = "EngineType";
|
||||
const char DEBUGGER_INFORMATION_AUTODETECTED[] = "AutoDetected";
|
||||
const char DEBUGGER_INFORMATION_AUTODETECTION_SOURCE[] = "AutoDetectionSource";
|
||||
const char DEBUGGER_INFORMATION_VERSION[] = "Version";
|
||||
const char DEBUGGER_INFORMATION_ABIS[] = "Abis";
|
||||
const char DEBUGGER_INFORMATION_LASTMODIFIED[] = "LastModified";
|
||||
@@ -82,7 +81,6 @@ DebuggerItem::DebuggerItem(const QVariantMap &data)
|
||||
m_workingDirectory = FileName::fromUserInput(data.value(DEBUGGER_INFORMATION_WORKINGDIRECTORY).toString());
|
||||
m_unexpandedDisplayName = data.value(DEBUGGER_INFORMATION_DISPLAYNAME).toString();
|
||||
m_isAutoDetected = data.value(DEBUGGER_INFORMATION_AUTODETECTED, false).toBool();
|
||||
m_autoDetectionSource = data.value(DEBUGGER_INFORMATION_AUTODETECTION_SOURCE).toString();
|
||||
m_version = data.value(DEBUGGER_INFORMATION_VERSION).toString();
|
||||
m_engineType = DebuggerEngineType(data.value(DEBUGGER_INFORMATION_ENGINETYPE,
|
||||
static_cast<int>(NoEngineType)).toInt());
|
||||
@@ -252,7 +250,6 @@ QVariantMap DebuggerItem::toMap() const
|
||||
data.insert(DEBUGGER_INFORMATION_WORKINGDIRECTORY, m_workingDirectory.toString());
|
||||
data.insert(DEBUGGER_INFORMATION_ENGINETYPE, int(m_engineType));
|
||||
data.insert(DEBUGGER_INFORMATION_AUTODETECTED, m_isAutoDetected);
|
||||
data.insert(DEBUGGER_INFORMATION_AUTODETECTION_SOURCE, m_autoDetectionSource);
|
||||
data.insert(DEBUGGER_INFORMATION_VERSION, m_version);
|
||||
data.insert(DEBUGGER_INFORMATION_ABIS, abiNames());
|
||||
data.insert(DEBUGGER_INFORMATION_LASTMODIFIED, m_lastModified);
|
||||
@@ -306,11 +303,6 @@ void DebuggerItem::setVersion(const QString &version)
|
||||
m_version = version;
|
||||
}
|
||||
|
||||
void DebuggerItem::setAutoDetectionSource(const QString &autoDetectionSource)
|
||||
{
|
||||
m_autoDetectionSource = autoDetectionSource;
|
||||
}
|
||||
|
||||
void DebuggerItem::setAbis(const QList<Abi> &abis)
|
||||
{
|
||||
m_abis = abis;
|
||||
|
||||
@@ -81,9 +81,6 @@ public:
|
||||
QString version() const;
|
||||
void setVersion(const QString &version);
|
||||
|
||||
QString autoDetectionSource() const { return m_autoDetectionSource; }
|
||||
void setAutoDetectionSource(const QString &autoDetectionSource);
|
||||
|
||||
const QList<ProjectExplorer::Abi> &abis() const { return m_abis; }
|
||||
void setAbis(const QList<ProjectExplorer::Abi> &abis);
|
||||
void setAbi(const ProjectExplorer::Abi &abi);
|
||||
@@ -115,7 +112,6 @@ private:
|
||||
Utils::FileName m_command;
|
||||
Utils::FileName m_workingDirectory;
|
||||
bool m_isAutoDetected = false;
|
||||
QString m_autoDetectionSource;
|
||||
QString m_version;
|
||||
QList<ProjectExplorer::Abi> m_abis;
|
||||
QDateTime m_lastModified;
|
||||
|
||||
@@ -768,7 +768,7 @@ public:
|
||||
Action m_interruptAction{tr("Interrupt"), interruptIcon(false), &DebuggerEngine::handleExecInterrupt};
|
||||
Action m_abortAction{tr("Abort Debugging"), {}, &DebuggerEngine::abortDebugger,
|
||||
tr("Aborts debugging and resets the debugger to the initial state.")};
|
||||
QAction m_stepAction{tr("Step Into")};
|
||||
QAction m_stepInAction{tr("Step Into")};
|
||||
Action m_stepOutAction{tr("Step Out"), Icons::STEP_OUT.icon(), &DebuggerEngine::handleExecStepOut};
|
||||
|
||||
Action m_runToLineAction{tr("Run to Line"), {}, &DebuggerEngine::handleExecRunToLine};
|
||||
@@ -776,7 +776,8 @@ public:
|
||||
Action m_jumpToLineAction{tr("Jump to Line"), {}, &DebuggerEngine::handleExecJumpToLine};
|
||||
// In the Debug menu.
|
||||
Action m_returnFromFunctionAction{tr("Immediately Return From Inner Function"), {}, &DebuggerEngine::executeReturn};
|
||||
QAction m_nextAction{tr("Step Over")};
|
||||
QAction m_stepOverAction{tr("Step Over")};
|
||||
QAction m_startAndBreakOnMain{tr("Start and Break on Main")};
|
||||
Action m_watchAction{tr("Add Expression Evaluator"), {}, &DebuggerEngine::handleAddToWatchWindow};
|
||||
Command *m_watchCommand = nullptr;
|
||||
QAction m_breakAction{tr("Toggle Breakpoint")};
|
||||
@@ -1012,6 +1013,11 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments,
|
||||
QString *errorMessage)
|
||||
{
|
||||
Q_UNUSED(errorMessage);
|
||||
|
||||
const Context debuggerRunning(C_DEBUGGER_RUNNING);
|
||||
const Context debuggerNotRunning(C_DEBUGGER_NOTRUNNING);
|
||||
ICore::addAdditionalContext(debuggerNotRunning);
|
||||
|
||||
m_arguments = arguments;
|
||||
if (!m_arguments.isEmpty())
|
||||
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::finishedInitialization,
|
||||
@@ -1225,34 +1231,37 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments,
|
||||
|
||||
debugMenu->addSeparator();
|
||||
|
||||
cmd = ActionManager::registerAction(&m_nextAction, Constants::NEXT);
|
||||
cmd = ActionManager::registerAction(&m_startAndBreakOnMain,
|
||||
Constants::START_AND_BREAK_ON_MAIN,
|
||||
debuggerNotRunning);
|
||||
cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? tr("Ctrl+Shift+O") : tr("F10")));
|
||||
cmd->setAttribute(Command::CA_Hide);
|
||||
cmd->setAttribute(Command::CA_UpdateText);
|
||||
debugMenu->addAction(cmd);
|
||||
m_nextAction.setIcon(Icons::STEP_OVER.icon());
|
||||
connect(&m_nextAction, &QAction::triggered, this, [] {
|
||||
if (DebuggerEngine *engine = EngineManager::currentEngine()) {
|
||||
engine->handleExecNext();
|
||||
} else {
|
||||
DebuggerRunTool::setBreakOnMainNextTime();
|
||||
ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE, false);
|
||||
}
|
||||
connect(&m_startAndBreakOnMain, &QAction::triggered, this, [] {
|
||||
DebuggerRunTool::setBreakOnMainNextTime();
|
||||
ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE, false);
|
||||
});
|
||||
|
||||
cmd = ActionManager::registerAction(&m_stepAction, Constants::STEP);
|
||||
cmd = ActionManager::registerAction(&m_stepOverAction, Constants::NEXT, debuggerRunning);
|
||||
cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? tr("Ctrl+Shift+O") : tr("F10")));
|
||||
cmd->setAttribute(Command::CA_Hide);
|
||||
debugMenu->addAction(cmd);
|
||||
m_stepOverAction.setIcon(Icons::STEP_OVER.icon());
|
||||
connect(&m_stepOverAction, &QAction::triggered, this, [] {
|
||||
DebuggerEngine *engine = EngineManager::currentEngine();
|
||||
QTC_ASSERT(engine, return);
|
||||
engine->handleExecStepOver();
|
||||
});
|
||||
|
||||
cmd = ActionManager::registerAction(&m_stepInAction, Constants::STEP, debuggerRunning);
|
||||
cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? tr("Ctrl+Shift+I") : tr("F11")));
|
||||
cmd->setAttribute(Command::CA_Hide);
|
||||
cmd->setAttribute(Command::CA_UpdateText);
|
||||
debugMenu->addAction(cmd);
|
||||
m_stepAction.setIcon(Icons::STEP_OVER.icon());
|
||||
connect(&m_stepAction, &QAction::triggered, this, [] {
|
||||
if (DebuggerEngine *engine = EngineManager::currentEngine()) {
|
||||
engine->handleExecStep();
|
||||
} else {
|
||||
DebuggerRunTool::setBreakOnMainNextTime();
|
||||
ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE, false);
|
||||
}
|
||||
m_stepInAction.setIcon(Icons::STEP_INTO.icon());
|
||||
connect(&m_stepInAction, &QAction::triggered, this, [] {
|
||||
DebuggerEngine *engine = EngineManager::currentEngine();
|
||||
QTC_ASSERT(engine, return);
|
||||
engine->handleExecStepIn();
|
||||
});
|
||||
|
||||
|
||||
@@ -1469,10 +1478,10 @@ void DebuggerPluginPrivate::updatePresetState()
|
||||
// correspond to the current start up project.
|
||||
// Step into/next: Start and break at 'main' unless a debugger is running.
|
||||
QString stepToolTip = canRun ? tr("Start \"%1\" and break at function \"main\"").arg(startupRunConfigName) : whyNot;
|
||||
m_stepAction.setToolTip(stepToolTip);
|
||||
m_nextAction.setToolTip(stepToolTip);
|
||||
m_stepAction.setEnabled(canRun);
|
||||
m_nextAction.setEnabled(canRun);
|
||||
m_stepInAction.setEnabled(canRun);
|
||||
m_stepInAction.setToolTip(stepToolTip);
|
||||
m_stepOverAction.setEnabled(canRun);
|
||||
m_stepOverAction.setToolTip(stepToolTip);
|
||||
m_startAction.setEnabled(canRun);
|
||||
m_startAction.setIcon(startIcon(false));
|
||||
m_startAction.setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
@@ -1492,8 +1501,8 @@ void DebuggerPluginPrivate::updatePresetState()
|
||||
QTC_ASSERT(currentEngine, return);
|
||||
|
||||
// We have a current engine, and it belongs to the startup runconfig.
|
||||
m_stepAction.setToolTip(QString());
|
||||
m_nextAction.setToolTip(QString());
|
||||
m_stepInAction.setToolTip(QString());
|
||||
m_stepOverAction.setToolTip(QString());
|
||||
|
||||
// The 'state' bits only affect the fat debug button, not the preset start button.
|
||||
m_startAction.setIcon(startIcon(false));
|
||||
@@ -1522,8 +1531,8 @@ void DebuggerPluginPrivate::updatePresetState()
|
||||
m_debugWithoutDeployAction.setEnabled(false);
|
||||
m_visibleStartAction.setAction(&m_continueAction);
|
||||
m_hiddenStopAction.setAction(&m_exitAction);
|
||||
m_stepAction.setEnabled(!companionPreventsAction);
|
||||
m_nextAction.setEnabled(!companionPreventsAction);
|
||||
m_stepInAction.setEnabled(!companionPreventsAction);
|
||||
m_stepOverAction.setEnabled(!companionPreventsAction);
|
||||
m_jumpToLineAction.setEnabled(currentEngine->hasCapability(JumpToLineCapability));
|
||||
m_returnFromFunctionAction.setEnabled(currentEngine->hasCapability(ReturnFromFunctionCapability));
|
||||
m_detachAction.setEnabled(!isCore);
|
||||
@@ -1541,8 +1550,8 @@ void DebuggerPluginPrivate::updatePresetState()
|
||||
m_debugWithoutDeployAction.setEnabled(false);
|
||||
m_visibleStartAction.setAction(&m_interruptAction);
|
||||
m_hiddenStopAction.setAction(&m_interruptAction);
|
||||
m_stepAction.setEnabled(false);
|
||||
m_nextAction.setEnabled(false);
|
||||
m_stepInAction.setEnabled(false);
|
||||
m_stepOverAction.setEnabled(false);
|
||||
m_jumpToLineAction.setEnabled(false);
|
||||
m_returnFromFunctionAction.setEnabled(false);
|
||||
m_detachAction.setEnabled(false);
|
||||
@@ -1560,8 +1569,8 @@ void DebuggerPluginPrivate::updatePresetState()
|
||||
m_debugWithoutDeployAction.setEnabled(canRun);
|
||||
m_visibleStartAction.setAction(&m_startAction);
|
||||
m_hiddenStopAction.setAction(&m_undisturbableAction);
|
||||
m_stepAction.setEnabled(false);
|
||||
m_nextAction.setEnabled(false);
|
||||
m_stepInAction.setEnabled(false);
|
||||
m_stepOverAction.setEnabled(false);
|
||||
m_jumpToLineAction.setEnabled(false);
|
||||
m_returnFromFunctionAction.setEnabled(false);
|
||||
m_detachAction.setEnabled(false);
|
||||
@@ -1579,8 +1588,8 @@ void DebuggerPluginPrivate::updatePresetState()
|
||||
m_debugWithoutDeployAction.setEnabled(false);
|
||||
m_visibleStartAction.setAction(&m_exitAction);
|
||||
m_hiddenStopAction.setAction(&m_exitAction);
|
||||
m_stepAction.setEnabled(false);
|
||||
m_nextAction.setEnabled(false);
|
||||
m_stepInAction.setEnabled(false);
|
||||
m_stepOverAction.setEnabled(false);
|
||||
m_jumpToLineAction.setEnabled(false);
|
||||
m_returnFromFunctionAction.setEnabled(false);
|
||||
m_detachAction.setEnabled(false);
|
||||
@@ -1601,8 +1610,8 @@ void DebuggerPluginPrivate::updatePresetState()
|
||||
m_debugWithoutDeployAction.setEnabled(false);
|
||||
m_visibleStartAction.setAction(&m_undisturbableAction);
|
||||
m_hiddenStopAction.setAction(&m_undisturbableAction);
|
||||
m_stepAction.setEnabled(false);
|
||||
m_nextAction.setEnabled(false);
|
||||
m_stepInAction.setEnabled(false);
|
||||
m_stepOverAction.setEnabled(false);
|
||||
m_jumpToLineAction.setEnabled(false);
|
||||
m_returnFromFunctionAction.setEnabled(false);
|
||||
m_detachAction.setEnabled(false);
|
||||
@@ -2064,13 +2073,13 @@ void DebuggerPluginPrivate::setInitialState()
|
||||
m_interruptAction.setEnabled(false);
|
||||
m_continueAction.setEnabled(false);
|
||||
|
||||
m_stepAction.setEnabled(true);
|
||||
m_stepInAction.setEnabled(true);
|
||||
m_stepOutAction.setEnabled(false);
|
||||
m_runToLineAction.setEnabled(false);
|
||||
m_runToSelectedFunctionAction.setEnabled(true);
|
||||
m_returnFromFunctionAction.setEnabled(false);
|
||||
m_jumpToLineAction.setEnabled(false);
|
||||
m_nextAction.setEnabled(true);
|
||||
m_stepOverAction.setEnabled(true);
|
||||
|
||||
action(AutoDerefPointers)->setEnabled(true);
|
||||
action(ExpandStack)->setEnabled(false);
|
||||
|
||||
@@ -774,11 +774,6 @@ void DebuggerRunTool::setSolibSearchPath(const QStringList &list)
|
||||
m_runParameters.solibSearchPath = list;
|
||||
}
|
||||
|
||||
void DebuggerRunTool::quitDebugger()
|
||||
{
|
||||
initiateStop();
|
||||
}
|
||||
|
||||
bool DebuggerRunTool::fixupParameters()
|
||||
{
|
||||
DebuggerRunParameters &rp = m_runParameters;
|
||||
|
||||
@@ -59,8 +59,6 @@ public:
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
void quitDebugger();
|
||||
|
||||
bool isCppDebugging() const;
|
||||
bool isQmlDebugging() const;
|
||||
int portsUsedByDebugger() const;
|
||||
|
||||
@@ -400,6 +400,7 @@ void DisassemblerAgent::updateBreakpointMarker(const Breakpoint &bp)
|
||||
|
||||
auto marker = new DisassemblerBreakpointMarker(bp, lineNumber);
|
||||
d->breakpointMarks.append(marker);
|
||||
QTC_ASSERT(d->document, return);
|
||||
d->document->addMark(marker);
|
||||
}
|
||||
|
||||
|
||||
@@ -344,8 +344,13 @@ void EngineManagerPrivate::selectUiForCurrentEngine()
|
||||
int row = 0;
|
||||
|
||||
if (m_currentItem && m_currentItem->m_engine) {
|
||||
ICore::updateAdditionalContexts(Context(Debugger::Constants::C_DEBUGGER_NOTRUNNING),
|
||||
Context(Debugger::Constants::C_DEBUGGER_RUNNING));
|
||||
perspective = m_currentItem->m_engine->perspective();
|
||||
m_currentItem->m_engine->updateState(false);
|
||||
} else {
|
||||
ICore::updateAdditionalContexts(Context(Debugger::Constants::C_DEBUGGER_RUNNING),
|
||||
Context(Debugger::Constants::C_DEBUGGER_NOTRUNNING));
|
||||
}
|
||||
|
||||
if (m_currentItem)
|
||||
|
||||
@@ -901,7 +901,7 @@ void GdbEngine::handleResultRecord(DebuggerResponse *response)
|
||||
// there is no debug information. Divert to "-exec-next-step"
|
||||
showMessage("APPLYING WORKAROUND #3");
|
||||
notifyInferiorStopOk();
|
||||
executeNextI();
|
||||
executeStepOver(true);
|
||||
} else if (msg.startsWith("Couldn't get registers: No such process.")) {
|
||||
// Happens on archer-tromey-python 6.8.50.20090910-cvs
|
||||
// There might to be a race between a process shutting down
|
||||
@@ -1324,7 +1324,7 @@ void GdbEngine::handleStop1(const GdbMi &data)
|
||||
if (isSkippableFunction(funcName, fileName)) {
|
||||
//showMessage(_("SKIPPING ") + funcName);
|
||||
++stepCounter;
|
||||
executeStep();
|
||||
executeStepIn(false);
|
||||
return;
|
||||
}
|
||||
//if (stepCounter)
|
||||
@@ -1838,25 +1838,32 @@ void GdbEngine::continueInferior()
|
||||
continueInferiorInternal();
|
||||
}
|
||||
|
||||
void GdbEngine::executeStep()
|
||||
void GdbEngine::executeStepIn(bool byInstruction)
|
||||
{
|
||||
CHECK_STATE(InferiorStopOk);
|
||||
setTokenBarrier();
|
||||
notifyInferiorRunRequested();
|
||||
showStatusMessage(tr("Step requested..."), 5000);
|
||||
|
||||
DebuggerCommand cmd;
|
||||
if (isNativeMixedActiveFrame()) {
|
||||
DebuggerCommand cmd("executeStep", RunRequest);
|
||||
cmd.flags = RunRequest;
|
||||
cmd.function = "executeStep";
|
||||
cmd.callback = CB(handleExecuteStep);
|
||||
runCommand(cmd);
|
||||
} else if (byInstruction) {
|
||||
cmd.flags = RunRequest|NeedsFlush;
|
||||
cmd.function = "-exec-step-instruction";
|
||||
if (isReverseDebugging())
|
||||
cmd.function += "--reverse";
|
||||
cmd.callback = CB(handleExecuteContinue);
|
||||
} else {
|
||||
DebuggerCommand cmd;
|
||||
cmd.flags = RunRequest|NeedsFlush;
|
||||
cmd.function = "-exec-step";
|
||||
if (isReverseDebugging())
|
||||
cmd.function += " --reverse";
|
||||
cmd.callback = CB(handleExecuteStep);
|
||||
runCommand(cmd);
|
||||
}
|
||||
runCommand(cmd);
|
||||
}
|
||||
|
||||
void GdbEngine::handleExecuteStep(const DebuggerResponse &response)
|
||||
@@ -1882,7 +1889,7 @@ void GdbEngine::handleExecuteStep(const DebuggerResponse &response)
|
||||
notifyInferiorRunFailed();
|
||||
if (isDying())
|
||||
return;
|
||||
executeStepI(); // Fall back to instruction-wise stepping.
|
||||
executeStepIn(true); // Fall back to instruction-wise stepping.
|
||||
} else if (msg.startsWith("Cannot execute this command while the selected thread is running.")) {
|
||||
showExecutionError(msg);
|
||||
notifyInferiorRunFailed();
|
||||
@@ -1895,21 +1902,6 @@ void GdbEngine::handleExecuteStep(const DebuggerResponse &response)
|
||||
}
|
||||
}
|
||||
|
||||
void GdbEngine::executeStepI()
|
||||
{
|
||||
CHECK_STATE(InferiorStopOk);
|
||||
setTokenBarrier();
|
||||
notifyInferiorRunRequested();
|
||||
showStatusMessage(tr("Step by instruction requested..."), 5000);
|
||||
DebuggerCommand cmd;
|
||||
cmd.flags = RunRequest|NeedsFlush;
|
||||
cmd.function = "-exec-step-instruction";
|
||||
if (isReverseDebugging())
|
||||
cmd.function += "--reverse";
|
||||
cmd.callback = CB(handleExecuteContinue);
|
||||
runCommand(cmd);
|
||||
}
|
||||
|
||||
void GdbEngine::executeStepOut()
|
||||
{
|
||||
CHECK_STATE(InferiorStopOk);
|
||||
@@ -1928,23 +1920,29 @@ void GdbEngine::executeStepOut()
|
||||
}
|
||||
}
|
||||
|
||||
void GdbEngine::executeNext()
|
||||
void GdbEngine::executeStepOver(bool byInstruction)
|
||||
{
|
||||
CHECK_STATE(InferiorStopOk);
|
||||
setTokenBarrier();
|
||||
notifyInferiorRunRequested();
|
||||
showStatusMessage(tr("Step next requested..."), 5000);
|
||||
|
||||
DebuggerCommand cmd;
|
||||
cmd.flags = RunRequest;
|
||||
if (isNativeMixedActiveFrame()) {
|
||||
runCommand({"executeNext", RunRequest});
|
||||
cmd.function = "executeNext";
|
||||
} else if (byInstruction) {
|
||||
cmd.function = "-exec-next-instruction";
|
||||
if (isReverseDebugging())
|
||||
cmd.function += " --reverse";
|
||||
cmd.callback = CB(handleExecuteContinue);
|
||||
} else {
|
||||
DebuggerCommand cmd;
|
||||
cmd.flags = RunRequest;
|
||||
cmd.function = "-exec-next";
|
||||
if (isReverseDebugging())
|
||||
cmd.function += " --reverse";
|
||||
cmd.callback = CB(handleExecuteNext);
|
||||
runCommand(cmd);
|
||||
}
|
||||
runCommand(cmd);
|
||||
}
|
||||
|
||||
void GdbEngine::handleExecuteNext(const DebuggerResponse &response)
|
||||
@@ -1967,7 +1965,7 @@ void GdbEngine::handleExecuteNext(const DebuggerResponse &response)
|
||||
|| msg.contains("Error accessing memory address ")) {
|
||||
notifyInferiorRunFailed();
|
||||
if (!isDying())
|
||||
executeNextI(); // Fall back to instruction-wise stepping.
|
||||
executeStepOver(true); // Fall back to instruction-wise stepping.
|
||||
} else if (msg.startsWith("Cannot execute this command while the selected thread is running.")) {
|
||||
showExecutionError(msg);
|
||||
notifyInferiorRunFailed();
|
||||
@@ -1981,21 +1979,6 @@ void GdbEngine::handleExecuteNext(const DebuggerResponse &response)
|
||||
}
|
||||
}
|
||||
|
||||
void GdbEngine::executeNextI()
|
||||
{
|
||||
CHECK_STATE(InferiorStopOk);
|
||||
setTokenBarrier();
|
||||
notifyInferiorRunRequested();
|
||||
showStatusMessage(tr("Step next instruction requested..."), 5000);
|
||||
DebuggerCommand cmd;
|
||||
cmd.flags = RunRequest;
|
||||
cmd.function = "-exec-next-instruction";
|
||||
if (isReverseDebugging())
|
||||
cmd.function += " --reverse";
|
||||
cmd.callback = CB(handleExecuteContinue);
|
||||
runCommand(cmd);
|
||||
}
|
||||
|
||||
static QString addressSpec(quint64 address)
|
||||
{
|
||||
return "*0x" + QString::number(address, 16);
|
||||
|
||||
@@ -192,11 +192,9 @@ private: ////////// General Interface //////////
|
||||
void updateBreakpoint(const Breakpoint &bp) final;
|
||||
void enableSubBreakpoint(const SubBreakpoint &sbp, bool on) final;
|
||||
|
||||
void executeStep() final;
|
||||
void executeStepIn(bool byInstruction) final;
|
||||
void executeStepOut() final;
|
||||
void executeNext() final;
|
||||
void executeStepI() final;
|
||||
void executeNextI() final;
|
||||
void executeStepOver(bool byInstruction) final;
|
||||
|
||||
void continueInferiorInternal();
|
||||
void continueInferior() final;
|
||||
|
||||
@@ -336,16 +336,10 @@ void LldbEngine::interruptInferior()
|
||||
runCommand({"interruptInferior"});
|
||||
}
|
||||
|
||||
void LldbEngine::executeStep()
|
||||
void LldbEngine::executeStepIn(bool byInstruction)
|
||||
{
|
||||
notifyInferiorRunRequested();
|
||||
runCommand({"executeStep"});
|
||||
}
|
||||
|
||||
void LldbEngine::executeStepI()
|
||||
{
|
||||
notifyInferiorRunRequested();
|
||||
runCommand({"executeStepI"});
|
||||
runCommand({QLatin1String(byInstruction ? "executeStepI" : "executeStep")});
|
||||
}
|
||||
|
||||
void LldbEngine::executeStepOut()
|
||||
@@ -354,16 +348,10 @@ void LldbEngine::executeStepOut()
|
||||
runCommand({"executeStepOut"});
|
||||
}
|
||||
|
||||
void LldbEngine::executeNext()
|
||||
void LldbEngine::executeStepOver(bool byInstruction)
|
||||
{
|
||||
notifyInferiorRunRequested();
|
||||
runCommand({"executeNext"});
|
||||
}
|
||||
|
||||
void LldbEngine::executeNextI()
|
||||
{
|
||||
notifyInferiorRunRequested();
|
||||
runCommand({"executeNextI"});
|
||||
runCommand({QLatin1String(byInstruction ? "executeNextI" : "executeNext")});
|
||||
}
|
||||
|
||||
void LldbEngine::continueInferior()
|
||||
|
||||
@@ -60,11 +60,9 @@ signals:
|
||||
void outputReady(const QString &data);
|
||||
|
||||
private:
|
||||
void executeStep() override;
|
||||
void executeStepIn(bool byInstruction) override;
|
||||
void executeStepOut() override;
|
||||
void executeNext() override;
|
||||
void executeStepI() override;
|
||||
void executeNextI() override;
|
||||
void executeStepOver(bool byInstruction) override;
|
||||
|
||||
void setupEngine() override;
|
||||
void runEngine() override;
|
||||
|
||||
@@ -167,14 +167,7 @@ void PdbEngine::interruptInferior()
|
||||
interruptProcess(m_proc.processId(), GdbEngineType, &error);
|
||||
}
|
||||
|
||||
void PdbEngine::executeStep()
|
||||
{
|
||||
notifyInferiorRunRequested();
|
||||
notifyInferiorRunOk();
|
||||
postDirectCommand("step");
|
||||
}
|
||||
|
||||
void PdbEngine::executeStepI()
|
||||
void PdbEngine::executeStepIn(bool)
|
||||
{
|
||||
notifyInferiorRunRequested();
|
||||
notifyInferiorRunOk();
|
||||
@@ -188,14 +181,7 @@ void PdbEngine::executeStepOut()
|
||||
postDirectCommand("return");
|
||||
}
|
||||
|
||||
void PdbEngine::executeNext()
|
||||
{
|
||||
notifyInferiorRunRequested();
|
||||
notifyInferiorRunOk();
|
||||
postDirectCommand("next");
|
||||
}
|
||||
|
||||
void PdbEngine::executeNextI()
|
||||
void PdbEngine::executeStepOver(bool)
|
||||
{
|
||||
notifyInferiorRunRequested();
|
||||
notifyInferiorRunOk();
|
||||
@@ -269,8 +255,24 @@ void PdbEngine::insertBreakpoint(const Breakpoint &bp)
|
||||
|
||||
void PdbEngine::updateBreakpoint(const Breakpoint &bp)
|
||||
{
|
||||
Q_UNUSED(bp);
|
||||
QTC_CHECK(false);
|
||||
QTC_ASSERT(bp, return);
|
||||
const BreakpointState state = bp->state();
|
||||
if (QTC_GUARD(state == BreakpointUpdateRequested))
|
||||
notifyBreakpointChangeProceeding(bp);
|
||||
if (bp->responseId().isEmpty()) // FIXME postpone update somehow (QTimer::singleShot?)
|
||||
return;
|
||||
|
||||
// FIXME figure out what needs to be changed (there might be more than enabled state)
|
||||
const BreakpointParameters &requested = bp->requestedParameters();
|
||||
if (requested.enabled != bp->isEnabled()) {
|
||||
if (bp->isEnabled())
|
||||
postDirectCommand("disable " + bp->responseId());
|
||||
else
|
||||
postDirectCommand("enable " + bp->responseId());
|
||||
bp->setEnabled(!bp->isEnabled());
|
||||
}
|
||||
// Pretend it succeeds without waiting for response.
|
||||
notifyBreakpointChangeOk(bp);
|
||||
}
|
||||
|
||||
void PdbEngine::removeBreakpoint(const Breakpoint &bp)
|
||||
@@ -278,6 +280,10 @@ void PdbEngine::removeBreakpoint(const Breakpoint &bp)
|
||||
QTC_ASSERT(bp, return);
|
||||
QTC_CHECK(bp->state() == BreakpointRemoveRequested);
|
||||
notifyBreakpointRemoveProceeding(bp);
|
||||
if (bp->responseId().isEmpty()) {
|
||||
notifyBreakpointRemoveFailed(bp);
|
||||
return;
|
||||
}
|
||||
showMessage(QString("DELETING BP %1 IN %2")
|
||||
.arg(bp->responseId()).arg(bp->fileName()));
|
||||
postDirectCommand("clear " + bp->responseId());
|
||||
@@ -504,8 +510,12 @@ void PdbEngine::handleOutput2(const QString &data)
|
||||
bp->setLineNumber(lineNumber);
|
||||
bp->adjustMarker();
|
||||
bp->setPending(false);
|
||||
QTC_CHECK(!bp->needsChange());
|
||||
notifyBreakpointInsertOk(bp);
|
||||
if (bp->needsChange()) {
|
||||
bp->gotoState(BreakpointUpdateRequested, BreakpointInserted);
|
||||
updateBreakpoint(bp);
|
||||
// QTC_CHECK(!bp->needsChange());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,12 +47,9 @@ public:
|
||||
PdbEngine();
|
||||
|
||||
private:
|
||||
// DebuggerEngine implementation
|
||||
void executeStep() override;
|
||||
void executeStepIn(bool) override;
|
||||
void executeStepOut() override;
|
||||
void executeNext() override;
|
||||
void executeStepI() override;
|
||||
void executeNextI() override;
|
||||
void executeStepOver(bool) override;
|
||||
|
||||
void setupEngine() override;
|
||||
void runEngine() override;
|
||||
|
||||
@@ -610,15 +610,7 @@ void QmlEngine::interruptInferior()
|
||||
showStatusMessage(tr("Waiting for JavaScript engine to interrupt on next statement."));
|
||||
}
|
||||
|
||||
void QmlEngine::executeStep()
|
||||
{
|
||||
clearExceptionSelection();
|
||||
d->continueDebugging(StepIn);
|
||||
notifyInferiorRunRequested();
|
||||
notifyInferiorRunOk();
|
||||
}
|
||||
|
||||
void QmlEngine::executeStepI()
|
||||
void QmlEngine::executeStepIn(bool)
|
||||
{
|
||||
clearExceptionSelection();
|
||||
d->continueDebugging(StepIn);
|
||||
@@ -634,7 +626,7 @@ void QmlEngine::executeStepOut()
|
||||
notifyInferiorRunOk();
|
||||
}
|
||||
|
||||
void QmlEngine::executeNext()
|
||||
void QmlEngine::executeStepOver(bool)
|
||||
{
|
||||
clearExceptionSelection();
|
||||
d->continueDebugging(Next);
|
||||
@@ -642,11 +634,6 @@ void QmlEngine::executeNext()
|
||||
notifyInferiorRunOk();
|
||||
}
|
||||
|
||||
void QmlEngine::executeNextI()
|
||||
{
|
||||
executeNext();
|
||||
}
|
||||
|
||||
void QmlEngine::executeRunToLine(const ContextData &data)
|
||||
{
|
||||
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
||||
|
||||
@@ -72,11 +72,9 @@ private:
|
||||
|
||||
void resetLocation() override;
|
||||
|
||||
void executeStep() override;
|
||||
void executeStepOver(bool) override;
|
||||
void executeStepIn(bool) override;
|
||||
void executeStepOut() override;
|
||||
void executeNext() override;
|
||||
void executeStepI() override;
|
||||
void executeNextI() override;
|
||||
|
||||
void setupEngine() override;
|
||||
void runEngine() override;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
Commands:
|
||||
- P3:
|
||||
- allow to use external viewer instead of greenhouse one
|
||||
as these have more functionality usually
|
||||
|
||||
GUI:
|
||||
- Better diff view
|
||||
- Commit action View
|
||||
- Able to add further files to commit (list of modified/untracked files)
|
||||
- use List for Log (and allow 10+ entries)
|
||||
Backend:
|
||||
- Don't use forked processes, instead find a library connection like libgit-thin
|
||||
- http://repo.or.cz/w/git/libgit-gsoc.git
|
||||
- apply to SCM Manager in Greenhouse, currently it's mostly independent
|
||||
|
||||
Suggestions:
|
||||
- Bjorn: Use a "Summary" Lineedit in the commit dialog to make commits look nicer on gitweb or such.
|
||||
@@ -123,8 +123,6 @@ public:
|
||||
|
||||
QStringList fullName(bool includePrefix = false) const
|
||||
{
|
||||
QTC_ASSERT(isLeaf(), return QStringList());
|
||||
|
||||
QStringList fn;
|
||||
QList<const BranchNode *> nodes;
|
||||
const BranchNode *current = this;
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <coreplugin/documentmanager.h>
|
||||
#include <coreplugin/inavigationwidgetfactory.h>
|
||||
#include <utils/elidinglabel.h>
|
||||
#include <utils/fancylineedit.h>
|
||||
#include <utils/navigationtreeview.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/utilsicons.h>
|
||||
@@ -48,6 +49,7 @@
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QPoint>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QTreeView>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
@@ -57,6 +59,21 @@ using namespace Core;
|
||||
namespace Git {
|
||||
namespace Internal {
|
||||
|
||||
class BranchFilterModel : public QSortFilterProxyModel
|
||||
{
|
||||
public:
|
||||
BranchFilterModel(QObject *parent) : QSortFilterProxyModel(parent) {}
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
|
||||
{
|
||||
QAbstractItemModel *m = sourceModel();
|
||||
// Filter leaves only. The root node and all intermediate nodes should always be visible
|
||||
if (!sourceParent.isValid() || m->rowCount(m->index(sourceRow, 0, sourceParent)) > 0)
|
||||
return true;
|
||||
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
|
||||
}
|
||||
};
|
||||
|
||||
BranchView::BranchView() :
|
||||
m_includeOldEntriesAction(new QAction(tr("Include Old Entries"), this)),
|
||||
m_includeTagsAction(new QAction(tr("Include Tags"), this)),
|
||||
@@ -64,7 +81,8 @@ BranchView::BranchView() :
|
||||
m_refreshButton(new QToolButton(this)),
|
||||
m_repositoryLabel(new Utils::ElidingLabel(this)),
|
||||
m_branchView(new Utils::NavigationTreeView(this)),
|
||||
m_model(new BranchModel(GitPlugin::client(), this))
|
||||
m_model(new BranchModel(GitPlugin::client(), this)),
|
||||
m_filterModel(new BranchFilterModel(this))
|
||||
{
|
||||
m_addButton->setIcon(Utils::Icons::PLUS_TOOLBAR.icon());
|
||||
m_addButton->setProperty("noArrow", true);
|
||||
@@ -75,12 +93,21 @@ BranchView::BranchView() :
|
||||
m_refreshButton->setProperty("noArrow", true);
|
||||
connect(m_refreshButton, &QToolButton::clicked, this, &BranchView::refreshCurrentRepository);
|
||||
|
||||
m_branchView->setModel(m_model);
|
||||
m_branchView->setHeaderHidden(true);
|
||||
setFocus();
|
||||
|
||||
m_repositoryLabel->setElideMode(Qt::ElideLeft);
|
||||
|
||||
m_filterModel->setSourceModel(m_model);
|
||||
m_filterModel->setFilterRole(Qt::EditRole);
|
||||
m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
m_branchView->setModel(m_filterModel);
|
||||
auto filterEdit = new Utils::FancyLineEdit(this);
|
||||
filterEdit->setFiltering(true);
|
||||
connect(filterEdit, &Utils::FancyLineEdit::textChanged,
|
||||
m_filterModel, QOverload<const QString &>::of(&BranchFilterModel::setFilterRegExp));
|
||||
auto layout = new QVBoxLayout(this);
|
||||
layout->addWidget(filterEdit);
|
||||
layout->addWidget(m_repositoryLabel);
|
||||
layout->addWidget(m_branchView);
|
||||
layout->setContentsMargins(0, 2, 0, 0);
|
||||
@@ -100,7 +127,7 @@ BranchView::BranchView() :
|
||||
|
||||
m_branchView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(m_branchView, &QAbstractItemView::doubleClicked,
|
||||
this, &BranchView::log);
|
||||
this, [this](const QModelIndex &idx) { log(m_filterModel->mapToSource(idx)); });
|
||||
connect(m_branchView, &QWidget::customContextMenuRequested,
|
||||
this, &BranchView::slotCustomContextMenu);
|
||||
connect(m_model, &QAbstractItemModel::modelReset,
|
||||
@@ -161,10 +188,11 @@ void BranchView::resizeColumns()
|
||||
|
||||
void BranchView::slotCustomContextMenu(const QPoint &point)
|
||||
{
|
||||
const QModelIndex index = m_branchView->indexAt(point);
|
||||
if (!index.isValid())
|
||||
const QModelIndex filteredIndex = m_branchView->indexAt(point);
|
||||
if (!filteredIndex.isValid())
|
||||
return;
|
||||
|
||||
const QModelIndex index = m_filterModel->mapToSource(filteredIndex);
|
||||
const QModelIndex currentBranch = m_model->currentBranch();
|
||||
const bool currentSelected = index.row() == currentBranch.row();
|
||||
const bool isLocal = m_model->isLocal(index);
|
||||
@@ -185,9 +213,9 @@ void BranchView::slotCustomContextMenu(const QPoint &point)
|
||||
}
|
||||
if (hasActions) {
|
||||
if (!currentSelected && (isLocal || isTag))
|
||||
contextMenu.addAction(tr("Remove"), this, &BranchView::remove);
|
||||
contextMenu.addAction(tr("Remove..."), this, &BranchView::remove);
|
||||
if (isLocal || isTag)
|
||||
contextMenu.addAction(tr("Rename"), this, &BranchView::rename);
|
||||
contextMenu.addAction(tr("Rename..."), this, &BranchView::rename);
|
||||
if (!currentSelected)
|
||||
contextMenu.addAction(tr("Checkout"), this, &BranchView::checkout);
|
||||
contextMenu.addSeparator();
|
||||
@@ -246,7 +274,7 @@ QModelIndex BranchView::selectedIndex()
|
||||
QModelIndexList selected = m_branchView->selectionModel()->selectedIndexes();
|
||||
if (selected.isEmpty())
|
||||
return QModelIndex();
|
||||
return selected.at(0);
|
||||
return m_filterModel->mapToSource(selected.at(0));
|
||||
}
|
||||
|
||||
bool BranchView::add()
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace Git {
|
||||
namespace Internal {
|
||||
|
||||
class BranchModel;
|
||||
class BranchFilterModel;
|
||||
|
||||
class BranchView : public QWidget
|
||||
{
|
||||
@@ -87,6 +88,7 @@ private:
|
||||
Utils::ElidingLabel *m_repositoryLabel = nullptr;
|
||||
Utils::NavigationTreeView *m_branchView = nullptr;
|
||||
BranchModel *m_model = nullptr;
|
||||
BranchFilterModel *m_filterModel = nullptr;
|
||||
QString m_repository;
|
||||
};
|
||||
|
||||
|
||||
@@ -274,7 +274,7 @@ HelpPluginPrivate::HelpPluginPrivate()
|
||||
textEditorContextMenu->addAction(cmd, Core::Constants::G_HELP);
|
||||
}
|
||||
|
||||
action = new QAction(HelpPlugin::tr("Technical Support"), this);
|
||||
action = new QAction(HelpPlugin::tr("Technical Support..."), this);
|
||||
cmd = ActionManager::registerAction(action, "Help.TechSupport");
|
||||
ActionManager::actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_SUPPORT);
|
||||
connect(action, &QAction::triggered, this, [this] {
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "baseclient.h"
|
||||
#include "languageclientcodeassist.h"
|
||||
#include "languageclientmanager.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
@@ -63,6 +62,7 @@ static Q_LOGGING_CATEGORY(LOGLSPCLIENTV, "qtc.languageclient.messages", QtWarnin
|
||||
|
||||
BaseClient::BaseClient()
|
||||
: m_id(Core::Id::fromString(QUuid::createUuid().toString()))
|
||||
, m_completionProvider(this)
|
||||
{
|
||||
m_buffer.open(QIODevice::ReadWrite | QIODevice::Append);
|
||||
m_contentHandler.insert(JsonRpcMessageHandler::jsonRpcMimeType(),
|
||||
@@ -72,6 +72,10 @@ BaseClient::BaseClient()
|
||||
BaseClient::~BaseClient()
|
||||
{
|
||||
m_buffer.close();
|
||||
// FIXME: instead of replacing the completion provider in the text document store the
|
||||
// completion provider as a prioritised list in the text document
|
||||
for (TextEditor::TextDocument *document : m_resetCompletionProvider)
|
||||
document->setCompletionAssistProvider(nullptr);
|
||||
}
|
||||
|
||||
void BaseClient::initialize()
|
||||
@@ -117,7 +121,7 @@ BaseClient::State BaseClient::state() const
|
||||
void BaseClient::openDocument(Core::IDocument *document)
|
||||
{
|
||||
using namespace TextEditor;
|
||||
if (!isSupportedMimeType(document->mimeType()))
|
||||
if (!isSupportedDocument(document))
|
||||
return;
|
||||
const FileName &filePath = document->filePath();
|
||||
const QString method(DidOpenTextDocumentNotification::methodName);
|
||||
@@ -149,7 +153,11 @@ void BaseClient::openDocument(Core::IDocument *document)
|
||||
documentContentsChanged(document);
|
||||
});
|
||||
if (textDocument) {
|
||||
textDocument->setCompletionAssistProvider(new LanguageClientCompletionAssistProvider(this));
|
||||
m_resetCompletionProvider << textDocument;
|
||||
textDocument->setCompletionAssistProvider(&m_completionProvider);
|
||||
connect(textDocument, &QObject::destroyed, this, [this, textDocument]{
|
||||
m_resetCompletionProvider.remove(textDocument);
|
||||
});
|
||||
if (BaseTextEditor *editor = BaseTextEditor::textEditorForDocument(textDocument)) {
|
||||
if (QPointer<TextEditorWidget> widget = editor->editorWidget()) {
|
||||
connect(widget, &TextEditorWidget::cursorPositionChanged, this, [this, widget](){
|
||||
@@ -489,19 +497,29 @@ void BaseClient::projectClosed(ProjectExplorer::Project *project)
|
||||
sendContent(change);
|
||||
}
|
||||
|
||||
void BaseClient::setSupportedMimeType(const QStringList &supportedMimeTypes)
|
||||
void BaseClient::setSupportedLanguage(const LanguageFilter &filter)
|
||||
{
|
||||
m_supportedMimeTypes = supportedMimeTypes;
|
||||
m_languagFilter = filter;
|
||||
}
|
||||
|
||||
bool BaseClient::isSupportedMimeType(const QString &mimeType) const
|
||||
bool BaseClient::isSupportedDocument(const Core::IDocument *document) const
|
||||
{
|
||||
return m_supportedMimeTypes.isEmpty() || m_supportedMimeTypes.contains(mimeType);
|
||||
QTC_ASSERT(document, return false);
|
||||
if (m_languagFilter.mimeTypes.isEmpty() || m_languagFilter.mimeTypes.contains(document->mimeType()))
|
||||
return true;
|
||||
auto regexps = Utils::transform(m_languagFilter.filePattern, [](const QString &pattern){
|
||||
return QRegExp(pattern, Utils::HostOsInfo::fileNameCaseSensitivity(), QRegExp::Wildcard);
|
||||
});
|
||||
return Utils::anyOf(regexps, [filePath = document->filePath()](const QRegExp ®){
|
||||
return reg.exactMatch(filePath.toString()) || reg.exactMatch(filePath.fileName());
|
||||
});
|
||||
}
|
||||
|
||||
bool BaseClient::needsRestart(const BaseSettings *) const
|
||||
bool BaseClient::needsRestart(const BaseSettings *settings) const
|
||||
{
|
||||
return false;
|
||||
QTC_ASSERT(settings, return false);
|
||||
return m_languagFilter.mimeTypes != settings->m_languageFilter.mimeTypes
|
||||
|| m_languagFilter.filePattern != settings->m_languageFilter.filePattern;
|
||||
}
|
||||
|
||||
bool BaseClient::reset()
|
||||
@@ -640,7 +658,7 @@ void BaseClient::intializeCallback(const InitializeResponse &initResponse)
|
||||
if (optional<ResponseError<InitializeError>> error = initResponse.error()) {
|
||||
if (error.value().data().has_value()
|
||||
&& error.value().data().value().retry().value_or(false)) {
|
||||
const QString title(tr("Language Server \"%1\" initialize error"));
|
||||
const QString title(tr("Language Server \"%1\" Initialize Error"));
|
||||
auto result = QMessageBox::warning(Core::ICore::dialogParent(),
|
||||
title,
|
||||
error.value().message(),
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "dynamiccapabilities.h"
|
||||
#include "languageclientcodeassist.h"
|
||||
#include "languageclientsettings.h"
|
||||
|
||||
#include <coreplugin/id.h>
|
||||
@@ -104,8 +105,8 @@ public:
|
||||
const LanguageServerProtocol::IContent &content);
|
||||
void cancelRequest(const LanguageServerProtocol::MessageId &id);
|
||||
|
||||
void setSupportedMimeType(const QStringList &supportedMimeTypes);
|
||||
bool isSupportedMimeType(const QString &mimeType) const;
|
||||
void setSupportedLanguage(const LanguageFilter &filter);
|
||||
bool isSupportedDocument(const Core::IDocument *document) const;
|
||||
|
||||
void setName(const QString &name) { m_displayName = name; }
|
||||
QString name() const { return m_displayName; }
|
||||
@@ -153,11 +154,13 @@ private:
|
||||
QHash<QByteArray, ContentHandler> m_contentHandler;
|
||||
QBuffer m_buffer;
|
||||
QString m_displayName;
|
||||
QStringList m_supportedMimeTypes;
|
||||
LanguageFilter m_languagFilter;
|
||||
QList<Utils::FileName> m_openedDocument;
|
||||
Core::Id m_id;
|
||||
LanguageServerProtocol::ServerCapabilities m_serverCapabilities;
|
||||
DynamicCapabilities m_dynamicCapabilities;
|
||||
LanguageClientCompletionAssistProvider m_completionProvider;
|
||||
QSet<TextEditor::TextDocument *> m_resetCompletionProvider;
|
||||
LanguageServerProtocol::BaseMessage m_currentMessage;
|
||||
QHash<LanguageServerProtocol::DocumentUri, LanguageServerProtocol::MessageId> m_highlightRequests;
|
||||
int m_restartsLeft = 5;
|
||||
|
||||
@@ -69,6 +69,8 @@ public:
|
||||
|
||||
bool operator <(const LanguageClientCompletionItem &other) const;
|
||||
|
||||
bool isPerfectMatch(int pos, QTextDocument *doc) const;
|
||||
|
||||
private:
|
||||
CompletionItem m_item;
|
||||
mutable QString m_sortText;
|
||||
@@ -194,6 +196,26 @@ bool LanguageClientCompletionItem::operator <(const LanguageClientCompletionItem
|
||||
return sortText() < other.sortText();
|
||||
}
|
||||
|
||||
bool LanguageClientCompletionItem::isPerfectMatch(int pos, QTextDocument *doc) const
|
||||
{
|
||||
QTC_ASSERT(doc, return false);
|
||||
using namespace Utils::Text;
|
||||
if (auto additionalEdits = m_item.additionalTextEdits()) {
|
||||
if (!additionalEdits.value().isEmpty())
|
||||
return false;
|
||||
}
|
||||
if (auto edit = m_item.textEdit()) {
|
||||
auto range = edit->range();
|
||||
const int start = positionInText(doc, range.start().line() + 1, range.start().character() + 1);
|
||||
const int end = positionInText(doc, range.end().line() + 1, range.end().character() + 1);
|
||||
auto text = textAt(QTextCursor(doc), start, end - start);
|
||||
return text == edit->newText();
|
||||
}
|
||||
const QString textToInsert(m_item.insertText().value_or(text()));
|
||||
const int length = textToInsert.length();
|
||||
return textToInsert == textAt(QTextCursor(doc), pos - length, length);
|
||||
}
|
||||
|
||||
class LanguageClientCompletionModel : public TextEditor::GenericProposalModel
|
||||
{
|
||||
public:
|
||||
@@ -201,6 +223,9 @@ public:
|
||||
bool isSortable(const QString &/*prefix*/) const override { return true; }
|
||||
void sort(const QString &/*prefix*/) override;
|
||||
bool supportsPrefixExpansion() const override { return false; }
|
||||
|
||||
QList<LanguageClientCompletionItem *> items() const
|
||||
{ return Utils::static_container_cast<LanguageClientCompletionItem *>(m_currentItems); }
|
||||
};
|
||||
|
||||
void LanguageClientCompletionModel::sort(const QString &/*prefix*/)
|
||||
@@ -213,16 +238,44 @@ void LanguageClientCompletionModel::sort(const QString &/*prefix*/)
|
||||
});
|
||||
}
|
||||
|
||||
class LanguageClientCompletionProposal : public TextEditor::GenericProposal
|
||||
{
|
||||
public:
|
||||
LanguageClientCompletionProposal(int cursorPos, LanguageClientCompletionModel *model)
|
||||
: TextEditor::GenericProposal(cursorPos, TextEditor::GenericProposalModelPtr(model))
|
||||
, m_model(model)
|
||||
{ }
|
||||
|
||||
// IAssistProposal interface
|
||||
bool hasItemsToPropose(const QString &/*text*/, TextEditor::AssistReason reason) const override
|
||||
{
|
||||
if (m_model->size() <= 0 || m_document.isNull())
|
||||
return false;
|
||||
|
||||
return m_model->keepPerfectMatch(reason)
|
||||
|| !Utils::anyOf(m_model->items(), [this](LanguageClientCompletionItem *item){
|
||||
return item->isPerfectMatch(m_pos, m_document);
|
||||
});
|
||||
}
|
||||
|
||||
LanguageClientCompletionModel *m_model;
|
||||
QPointer<QTextDocument> m_document;
|
||||
int m_pos = -1;
|
||||
};
|
||||
|
||||
|
||||
class LanguageClientCompletionAssistProcessor : public TextEditor::IAssistProcessor
|
||||
{
|
||||
public:
|
||||
LanguageClientCompletionAssistProcessor(BaseClient *client);
|
||||
TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override;
|
||||
bool running() override;
|
||||
bool needsRestart() const override { return true; }
|
||||
|
||||
private:
|
||||
void handleCompletionResponse(const Response<CompletionResult, LanguageClientNull> &response);
|
||||
|
||||
QPointer<QTextDocument> m_document;
|
||||
QPointer<BaseClient> m_client;
|
||||
bool m_running = false;
|
||||
int m_pos = -1;
|
||||
@@ -247,11 +300,15 @@ TextEditor::IAssistProposal *LanguageClientCompletionAssistProcessor::perform(
|
||||
{
|
||||
QTC_ASSERT(m_client, return nullptr);
|
||||
m_pos = interface->position();
|
||||
// const QRegExp regexp("[_a-zA-Z][_a-zA-Z0-9]*"); // FIXME
|
||||
// int delta = 0;
|
||||
// while (m_pos - delta > 0 && regexp.exactMatch(interface->textAt(m_pos - delta - 1, delta + 1)))
|
||||
// ++delta;
|
||||
// m_pos -= delta;
|
||||
if (interface->reason() == TextEditor::IdleEditor) {
|
||||
// Trigger an automatic completion request only when we are on a word with more than 2 "identifier" character
|
||||
const QRegExp regexp("[_a-zA-Z0-9]*");
|
||||
int delta = 0;
|
||||
while (m_pos - delta > 0 && regexp.exactMatch(interface->textAt(m_pos - delta - 1, delta + 1)))
|
||||
++delta;
|
||||
if (delta < 3)
|
||||
return nullptr;
|
||||
}
|
||||
CompletionRequest completionRequest;
|
||||
CompletionParams::CompletionContext context;
|
||||
context.setTriggerKind(interface->reason() == TextEditor::ActivationCharacter
|
||||
@@ -263,6 +320,7 @@ TextEditor::IAssistProposal *LanguageClientCompletionAssistProcessor::perform(
|
||||
if (!Utils::Text::convertPosition(interface->textDocument(), m_pos, &line, &column))
|
||||
return nullptr;
|
||||
--line; // line is 0 based in the protocol
|
||||
--column; // column is 0 based in the protocol
|
||||
params.setPosition({line, column});
|
||||
params.setTextDocument(
|
||||
DocumentUri::fromFileName(Utils::FileName::fromString(interface->fileName())));
|
||||
@@ -272,6 +330,7 @@ TextEditor::IAssistProposal *LanguageClientCompletionAssistProcessor::perform(
|
||||
completionRequest.setParams(params);
|
||||
m_client->sendContent(completionRequest);
|
||||
m_running = true;
|
||||
m_document = interface->textDocument();
|
||||
qCDebug(LOGLSPCOMPLETION) << QTime::currentTime()
|
||||
<< " : request completions at " << m_pos
|
||||
<< " by " << assistReasonString(interface->reason());
|
||||
@@ -309,7 +368,9 @@ void LanguageClientCompletionAssistProcessor::handleCompletionResponse(
|
||||
model->loadContent(Utils::transform(items, [](const CompletionItem &item){
|
||||
return static_cast<AssistProposalItemInterface *>(new LanguageClientCompletionItem(item));
|
||||
}));
|
||||
auto proposal = new GenericProposal(m_pos, GenericProposalModelPtr(model));
|
||||
auto proposal = new LanguageClientCompletionProposal(m_pos, model);
|
||||
proposal->m_document = m_document;
|
||||
proposal->m_pos = m_pos;
|
||||
proposal->setFragile(true);
|
||||
setAsyncProposalAvailable(proposal);
|
||||
qCDebug(LOGLSPCOMPLETION) << QTime::currentTime() << " : "
|
||||
|
||||
@@ -32,24 +32,33 @@
|
||||
#include <coreplugin/icore.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/delegates.h>
|
||||
#include <utils/fancylineedit.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/mimetypes/mimedatabase.h>
|
||||
#include <languageserverprotocol/lsptypes.h>
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QCompleter>
|
||||
#include <QCoreApplication>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QHeaderView>
|
||||
#include <QLabel>
|
||||
#include <QListView>
|
||||
#include <QPushButton>
|
||||
#include <QSettings>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QStringListModel>
|
||||
#include <QTreeView>
|
||||
|
||||
constexpr char nameKey[] = "name";
|
||||
constexpr char enabledKey[] = "enabled";
|
||||
constexpr char mimeTypeKey[] = "mimeType";
|
||||
constexpr char filePatternKey[] = "filePattern";
|
||||
constexpr char executableKey[] = "executable";
|
||||
constexpr char argumentsKey[] = "arguments";
|
||||
constexpr char settingsGroupKey[] = "LanguageClient";
|
||||
@@ -57,34 +66,25 @@ constexpr char clientsKey[] = "clients";
|
||||
|
||||
namespace LanguageClient {
|
||||
|
||||
class LanguageClientSettingsModel : public QAbstractTableModel
|
||||
class LanguageClientSettingsModel : public QAbstractListModel
|
||||
{
|
||||
public:
|
||||
LanguageClientSettingsModel() = default;
|
||||
~LanguageClientSettingsModel();
|
||||
|
||||
// QAbstractItemModel interface
|
||||
int rowCount(const QModelIndex &/*parent*/ = QModelIndex()) const override { return m_settings.count(); }
|
||||
int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const override { return ColumnCount; }
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
bool removeRows(int row, int count = 1, const QModelIndex &parent = QModelIndex()) override;
|
||||
bool insertRows(int row, int count = 1, const QModelIndex &parent = QModelIndex()) override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
int rowCount(const QModelIndex &/*parent*/ = QModelIndex()) const final { return m_settings.count(); }
|
||||
QVariant data(const QModelIndex &index, int role) const final;
|
||||
bool removeRows(int row, int count = 1, const QModelIndex &parent = QModelIndex()) final;
|
||||
bool insertRows(int row, int count = 1, const QModelIndex &parent = QModelIndex()) final;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) final;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const final;
|
||||
|
||||
void reset(const QList<StdIOSettings *> &settings);
|
||||
QList<StdIOSettings *> settings() const { return m_settings; }
|
||||
QList<StdIOSettings *> removed() const { return m_removed; }
|
||||
|
||||
enum Columns {
|
||||
DisplayNameColumn = 0,
|
||||
EnabledColumn,
|
||||
MimeTypeColumn,
|
||||
ExecutableColumn,
|
||||
ArgumentsColumn,
|
||||
ColumnCount
|
||||
};
|
||||
StdIOSettings *settingForIndex(const QModelIndex &index) const;
|
||||
QModelIndex indexForSetting(StdIOSettings *setting) const;
|
||||
|
||||
private:
|
||||
QList<StdIOSettings *> m_settings; // owned
|
||||
@@ -95,10 +95,18 @@ class LanguageClientSettingsPageWidget : public QWidget
|
||||
{
|
||||
public:
|
||||
LanguageClientSettingsPageWidget(LanguageClientSettingsModel &settings);
|
||||
void currentChanged(const QModelIndex &index);
|
||||
int currentRow() const;
|
||||
void resetCurrentSettings(int row);
|
||||
void applyCurrentSettings();
|
||||
|
||||
private:
|
||||
LanguageClientSettingsModel &m_settings;
|
||||
QTreeView *m_view;
|
||||
QTreeView *m_view = nullptr;
|
||||
struct CurrentSettings {
|
||||
StdIOSettings *setting = nullptr;
|
||||
QWidget *widget = nullptr;
|
||||
} m_currentSettings;
|
||||
|
||||
void addItem();
|
||||
void deleteItem();
|
||||
@@ -123,61 +131,29 @@ private:
|
||||
QPointer<LanguageClientSettingsPageWidget> m_widget;
|
||||
};
|
||||
|
||||
class LanguageChooseDelegate : public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
QWidget *createEditor(QWidget *parent,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
|
||||
};
|
||||
|
||||
QWidget *LanguageChooseDelegate::createEditor(QWidget *parent,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
Q_UNUSED(option);
|
||||
Q_UNUSED(index);
|
||||
auto editor = new QComboBox(parent);
|
||||
editor->addItem(noLanguageFilter);
|
||||
editor->addItems(LanguageServerProtocol::languageIds().values());
|
||||
return editor;
|
||||
}
|
||||
|
||||
void LanguageChooseDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
||||
{
|
||||
if (auto comboBox = qobject_cast<QComboBox*>(editor))
|
||||
comboBox->setCurrentText(index.data().toString());
|
||||
}
|
||||
|
||||
LanguageClientSettingsPageWidget::LanguageClientSettingsPageWidget(LanguageClientSettingsModel &settings)
|
||||
: m_settings(settings)
|
||||
, m_view(new QTreeView())
|
||||
{
|
||||
auto mainLayout = new QVBoxLayout();
|
||||
auto layout = new QHBoxLayout();
|
||||
m_view->setModel(&m_settings);
|
||||
m_view->header()->setStretchLastSection(true);
|
||||
m_view->setRootIsDecorated(false);
|
||||
m_view->setItemsExpandable(false);
|
||||
m_view->setHeaderHidden(true);
|
||||
m_view->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
m_view->setSelectionBehavior(QAbstractItemView::SelectItems);
|
||||
connect(m_view->selectionModel(), &QItemSelectionModel::currentChanged,
|
||||
this, &LanguageClientSettingsPageWidget::currentChanged);
|
||||
auto mimeTypes = Utils::transform(Utils::allMimeTypes(), [](const Utils::MimeType &mimeType){
|
||||
return mimeType.name();
|
||||
});
|
||||
auto mimeTypeCompleter = new QCompleter(mimeTypes);
|
||||
mimeTypeCompleter->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
mimeTypeCompleter->setFilterMode(Qt::MatchContains);
|
||||
m_view->setItemDelegateForColumn(LanguageClientSettingsModel::MimeTypeColumn,
|
||||
new Utils::CompleterDelegate(mimeTypeCompleter));
|
||||
auto executableDelegate = new Utils::PathChooserDelegate();
|
||||
executableDelegate->setExpectedKind(Utils::PathChooser::File);
|
||||
executableDelegate->setHistoryCompleter("LanguageClient.ServerPathHistory");
|
||||
m_view->setItemDelegateForColumn(LanguageClientSettingsModel::ExecutableColumn, executableDelegate);
|
||||
auto buttonLayout = new QVBoxLayout();
|
||||
auto addButton = new QPushButton(tr("&Add"));
|
||||
connect(addButton, &QPushButton::pressed, this, &LanguageClientSettingsPageWidget::addItem);
|
||||
auto deleteButton = new QPushButton(tr("&Delete"));
|
||||
connect(deleteButton, &QPushButton::pressed, this, &LanguageClientSettingsPageWidget::deleteItem);
|
||||
|
||||
setLayout(layout);
|
||||
mainLayout->addLayout(layout);
|
||||
setLayout(mainLayout);
|
||||
layout->addWidget(m_view);
|
||||
layout->addLayout(buttonLayout);
|
||||
buttonLayout->addWidget(addButton);
|
||||
@@ -185,6 +161,51 @@ LanguageClientSettingsPageWidget::LanguageClientSettingsPageWidget(LanguageClien
|
||||
buttonLayout->addStretch(10);
|
||||
}
|
||||
|
||||
void LanguageClientSettingsPageWidget::currentChanged(const QModelIndex &index)
|
||||
{
|
||||
if (m_currentSettings.widget) {
|
||||
applyCurrentSettings();
|
||||
layout()->removeWidget(m_currentSettings.widget);
|
||||
delete m_currentSettings.widget;
|
||||
}
|
||||
|
||||
if (index.isValid()) {
|
||||
m_currentSettings.setting = m_settings.settingForIndex(index);
|
||||
m_currentSettings.widget = m_currentSettings.setting->createSettingsWidget(this);
|
||||
layout()->addWidget(m_currentSettings.widget);
|
||||
} else {
|
||||
m_currentSettings.setting = nullptr;
|
||||
m_currentSettings.widget = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int LanguageClientSettingsPageWidget::currentRow() const
|
||||
{
|
||||
return m_settings.indexForSetting(m_currentSettings.setting).row();
|
||||
}
|
||||
|
||||
void LanguageClientSettingsPageWidget::resetCurrentSettings(int row)
|
||||
{
|
||||
if (m_currentSettings.widget) {
|
||||
layout()->removeWidget(m_currentSettings.widget);
|
||||
delete m_currentSettings.widget;
|
||||
}
|
||||
|
||||
m_currentSettings.setting = nullptr;
|
||||
m_currentSettings.widget = nullptr;
|
||||
m_view->setCurrentIndex(m_settings.index(row));
|
||||
}
|
||||
|
||||
void LanguageClientSettingsPageWidget::applyCurrentSettings()
|
||||
{
|
||||
if (!m_currentSettings.setting)
|
||||
return;
|
||||
|
||||
m_currentSettings.setting->applyFromSettingsWidget(m_currentSettings.widget);
|
||||
auto index = m_settings.indexForSetting(m_currentSettings.setting);
|
||||
m_settings.dataChanged(index, index);
|
||||
}
|
||||
|
||||
void LanguageClientSettingsPageWidget::addItem()
|
||||
{
|
||||
const int row = m_settings.rowCount();
|
||||
@@ -233,6 +254,8 @@ QWidget *LanguageClientSettingsPage::widget()
|
||||
void LanguageClientSettingsPage::apply()
|
||||
{
|
||||
qDeleteAll(m_settings);
|
||||
if (m_widget)
|
||||
m_widget->applyCurrentSettings();
|
||||
m_settings = Utils::transform(m_model.settings(), [](const StdIOSettings *other){
|
||||
return dynamic_cast<StdIOSettings *>(other->copy());
|
||||
});
|
||||
@@ -255,11 +278,19 @@ void LanguageClientSettingsPage::apply()
|
||||
}
|
||||
}
|
||||
}
|
||||
m_model.reset(m_settings);
|
||||
|
||||
if (m_widget) {
|
||||
int row = m_widget->currentRow();
|
||||
m_model.reset(m_settings);
|
||||
m_widget->resetCurrentSettings(row);
|
||||
} else {
|
||||
m_model.reset(m_settings);
|
||||
}
|
||||
}
|
||||
|
||||
void LanguageClientSettingsPage::finish()
|
||||
{
|
||||
m_model.reset(m_settings);
|
||||
}
|
||||
|
||||
LanguageClientSettingsModel::~LanguageClientSettingsModel()
|
||||
@@ -269,35 +300,13 @@ LanguageClientSettingsModel::~LanguageClientSettingsModel()
|
||||
|
||||
QVariant LanguageClientSettingsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
StdIOSettings *setting = settingForIndex(index);
|
||||
if (!setting)
|
||||
return QVariant();
|
||||
StdIOSettings *setting = m_settings[index.row()];
|
||||
QTC_ASSERT(setting, return false);
|
||||
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
||||
switch (index.column()) {
|
||||
case DisplayNameColumn: return setting->m_name;
|
||||
case MimeTypeColumn: return setting->m_mimeType;
|
||||
case ExecutableColumn: return setting->m_executable;
|
||||
case ArgumentsColumn: return setting->m_arguments;
|
||||
}
|
||||
} else if (role == Qt::CheckStateRole && index.column() == EnabledColumn) {
|
||||
if (role == Qt::DisplayRole)
|
||||
return setting->m_name;
|
||||
else if (role == Qt::CheckStateRole)
|
||||
return setting->m_enabled ? Qt::Checked : Qt::Unchecked;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant LanguageClientSettingsModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
|
||||
return QVariant();
|
||||
|
||||
switch (section) {
|
||||
case DisplayNameColumn: return tr("Name");
|
||||
case EnabledColumn: return tr("Enabled");
|
||||
case MimeTypeColumn: return tr("Mime Type");
|
||||
case ExecutableColumn: return tr("Executable");
|
||||
case ArgumentsColumn: return tr("Arguments");
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
@@ -326,44 +335,20 @@ bool LanguageClientSettingsModel::insertRows(int row, int count, const QModelInd
|
||||
|
||||
bool LanguageClientSettingsModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (!index.isValid())
|
||||
StdIOSettings *setting = settingForIndex(index);
|
||||
if (!setting || role != Qt::CheckStateRole)
|
||||
return false;
|
||||
StdIOSettings *setting = m_settings[index.row()];
|
||||
QTC_ASSERT(setting, return false);
|
||||
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
||||
const QString strVal(value.toString());
|
||||
QString *settingsValue = nullptr;
|
||||
switch (index.column()) {
|
||||
case DisplayNameColumn: settingsValue = &setting->m_name; break;
|
||||
case MimeTypeColumn: settingsValue = &setting->m_mimeType; break;
|
||||
case ExecutableColumn: settingsValue = &setting->m_executable; break;
|
||||
case ArgumentsColumn: settingsValue = &setting->m_arguments; break;
|
||||
}
|
||||
if (settingsValue) {
|
||||
if (strVal != *settingsValue) {
|
||||
*settingsValue = value.toString();
|
||||
emit dataChanged(index, index, { Qt::EditRole, Qt::DisplayRole });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (role == Qt::CheckStateRole && index.column() == EnabledColumn) {
|
||||
if (setting->m_enabled != value.toBool()) {
|
||||
setting->m_enabled = !setting->m_enabled;
|
||||
emit dataChanged(index, index, { Qt::CheckStateRole });
|
||||
}
|
||||
return true;
|
||||
if (setting->m_enabled != value.toBool()) {
|
||||
setting->m_enabled = !setting->m_enabled;
|
||||
emit dataChanged(index, index, { Qt::CheckStateRole });
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Qt::ItemFlags LanguageClientSettingsModel::flags(const QModelIndex &index) const
|
||||
Qt::ItemFlags LanguageClientSettingsModel::flags(const QModelIndex &/*index*/) const
|
||||
{
|
||||
const auto defaultFlags = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
|
||||
if (index.column() == EnabledColumn)
|
||||
return defaultFlags | Qt::ItemIsUserCheckable;
|
||||
return defaultFlags;
|
||||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
|
||||
}
|
||||
|
||||
void LanguageClientSettingsModel::reset(const QList<StdIOSettings *> &settings)
|
||||
@@ -378,6 +363,32 @@ void LanguageClientSettingsModel::reset(const QList<StdIOSettings *> &settings)
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
StdIOSettings *LanguageClientSettingsModel::settingForIndex(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid() || index.row() >= m_settings.size())
|
||||
return nullptr;
|
||||
return m_settings[index.row()];
|
||||
}
|
||||
|
||||
QModelIndex LanguageClientSettingsModel::indexForSetting(StdIOSettings *setting) const
|
||||
{
|
||||
const int index = m_settings.indexOf(setting);
|
||||
return index < 0 ? QModelIndex() : createIndex(index, 0, setting);
|
||||
}
|
||||
|
||||
void BaseSettings::applyFromSettingsWidget(QWidget *widget)
|
||||
{
|
||||
if (auto settingsWidget = qobject_cast<BaseSettingsWidget *>(widget)) {
|
||||
m_name = settingsWidget->name();
|
||||
m_languageFilter = settingsWidget->filter();
|
||||
}
|
||||
}
|
||||
|
||||
QWidget *BaseSettings::createSettingsWidget(QWidget *parent) const
|
||||
{
|
||||
return new BaseSettingsWidget(this, parent);
|
||||
}
|
||||
|
||||
bool BaseSettings::needsRestart() const
|
||||
{
|
||||
return m_client ? !m_enabled || m_client->needsRestart(this) : m_enabled;
|
||||
@@ -398,7 +409,8 @@ QVariantMap BaseSettings::toMap() const
|
||||
QVariantMap map;
|
||||
map.insert(nameKey, m_name);
|
||||
map.insert(enabledKey, m_enabled);
|
||||
map.insert(mimeTypeKey, m_mimeType);
|
||||
map.insert(mimeTypeKey, m_languageFilter.mimeTypes);
|
||||
map.insert(filePatternKey, m_languageFilter.filePattern);
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -406,7 +418,8 @@ void BaseSettings::fromMap(const QVariantMap &map)
|
||||
{
|
||||
m_name = map[nameKey].toString();
|
||||
m_enabled = map[enabledKey].toBool();
|
||||
m_mimeType = map[mimeTypeKey].toString();
|
||||
m_languageFilter.mimeTypes = map[mimeTypeKey].toStringList();
|
||||
m_languageFilter.filePattern = map[filePatternKey].toStringList();
|
||||
}
|
||||
|
||||
void LanguageClientSettings::init()
|
||||
@@ -438,6 +451,20 @@ void LanguageClientSettings::toSettings(QSettings *settings, const QList<StdIOSe
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
void StdIOSettings::applyFromSettingsWidget(QWidget *widget)
|
||||
{
|
||||
if (auto settingsWidget = qobject_cast<StdIOSettingsWidget *>(widget)) {
|
||||
BaseSettings::applyFromSettingsWidget(settingsWidget);
|
||||
m_executable = settingsWidget->executable();
|
||||
m_arguments = settingsWidget->arguments();
|
||||
}
|
||||
}
|
||||
|
||||
QWidget *StdIOSettings::createSettingsWidget(QWidget *parent) const
|
||||
{
|
||||
return new StdIOSettingsWidget(this, parent);
|
||||
}
|
||||
|
||||
bool StdIOSettings::needsRestart() const
|
||||
{
|
||||
if (BaseSettings::needsRestart())
|
||||
@@ -456,8 +483,7 @@ BaseClient *StdIOSettings::createClient() const
|
||||
{
|
||||
auto client = new StdIOClient(m_executable, m_arguments);
|
||||
client->setName(m_name);
|
||||
if (m_mimeType != noLanguageFilter)
|
||||
client->setSupportedMimeType({m_mimeType});
|
||||
client->setSupportedLanguage(m_languageFilter);
|
||||
return client;
|
||||
}
|
||||
|
||||
@@ -476,4 +502,160 @@ void StdIOSettings::fromMap(const QVariantMap &map)
|
||||
m_arguments = map[argumentsKey].toString();
|
||||
}
|
||||
|
||||
BaseSettingsWidget::BaseSettingsWidget(const BaseSettings *settings, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_name(new QLineEdit(settings->m_name, this))
|
||||
, m_mimeTypes(new QLabel(settings->m_languageFilter.mimeTypes.join(filterSeparator), this))
|
||||
, m_filePattern(new QLineEdit(settings->m_languageFilter.filePattern.join(filterSeparator), this))
|
||||
{
|
||||
int row = 0;
|
||||
auto *mainLayout = new QGridLayout;
|
||||
mainLayout->addWidget(new QLabel(tr("Name:")), row, 0);
|
||||
mainLayout->addWidget(m_name, row, 1);
|
||||
mainLayout->addWidget(new QLabel(tr("Language:")), ++row, 0);
|
||||
auto mimeLayout = new QHBoxLayout;
|
||||
mimeLayout->addWidget(m_mimeTypes);
|
||||
mimeLayout->addStretch();
|
||||
auto addMimeTypeButton = new QPushButton(tr("Set MIME Types..."), this);
|
||||
mimeLayout->addWidget(addMimeTypeButton);
|
||||
mainLayout->addLayout(mimeLayout, row, 1);
|
||||
m_filePattern->setPlaceholderText(tr("File pattern"));
|
||||
mainLayout->addWidget(m_filePattern, ++row, 1);
|
||||
|
||||
connect(addMimeTypeButton, &QPushButton::pressed,
|
||||
this, &BaseSettingsWidget::showAddMimeTypeDialog);
|
||||
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
|
||||
QString BaseSettingsWidget::name() const
|
||||
{
|
||||
return m_name->text();
|
||||
}
|
||||
|
||||
LanguageFilter BaseSettingsWidget::filter() const
|
||||
{
|
||||
return {m_mimeTypes->text().split(filterSeparator),
|
||||
m_filePattern->text().split(filterSeparator)};
|
||||
}
|
||||
|
||||
class MimeTypeModel : public QStringListModel
|
||||
{
|
||||
public:
|
||||
using QStringListModel::QStringListModel;
|
||||
QVariant data(const QModelIndex &index, int role) const final
|
||||
{
|
||||
if (index.isValid() && role == Qt::CheckStateRole)
|
||||
return m_selectedMimeTypes.contains(index.data().toString()) ? Qt::Checked : Qt::Unchecked;
|
||||
return QStringListModel::data(index, role);
|
||||
}
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) final
|
||||
{
|
||||
if (index.isValid() && role == Qt::CheckStateRole) {
|
||||
QString mimeType = index.data().toString();
|
||||
if (value.toInt() == Qt::Checked) {
|
||||
if (!m_selectedMimeTypes.contains(mimeType))
|
||||
m_selectedMimeTypes.append(index.data().toString());
|
||||
} else {
|
||||
m_selectedMimeTypes.removeAll(index.data().toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return QStringListModel::setData(index, value, role);
|
||||
}
|
||||
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const final
|
||||
{
|
||||
if (!index.isValid())
|
||||
return Qt::NoItemFlags;
|
||||
return (QStringListModel::flags(index)
|
||||
& ~(Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled))
|
||||
| Qt::ItemIsUserCheckable;
|
||||
}
|
||||
QStringList m_selectedMimeTypes;
|
||||
};
|
||||
|
||||
class MimeTypeDialog : public QDialog
|
||||
{
|
||||
public:
|
||||
explicit MimeTypeDialog(const QStringList &selectedMimeTypes, QWidget *parent = nullptr)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Select MIME Types"));
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
auto filter = new Utils::FancyLineEdit(this);
|
||||
filter->setFiltering(true);
|
||||
mainLayout->addWidget(filter);
|
||||
auto listView = new QListView(this);
|
||||
mainLayout->addWidget(listView);
|
||||
auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
mainLayout->addWidget(buttons);
|
||||
setLayout(mainLayout);
|
||||
|
||||
filter->setPlaceholderText(tr("Filter"));
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
auto proxy = new QSortFilterProxyModel(this);
|
||||
m_mimeTypeModel = new MimeTypeModel(Utils::transform(Utils::allMimeTypes(),
|
||||
&Utils::MimeType::name), this);
|
||||
m_mimeTypeModel->m_selectedMimeTypes = selectedMimeTypes;
|
||||
proxy->setSourceModel(m_mimeTypeModel);
|
||||
proxy->sort(0);
|
||||
connect(filter, &QLineEdit::textChanged, proxy, &QSortFilterProxyModel::setFilterWildcard);
|
||||
listView->setModel(proxy);
|
||||
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setModal(true);
|
||||
}
|
||||
|
||||
MimeTypeDialog(const MimeTypeDialog &other) = delete;
|
||||
MimeTypeDialog(MimeTypeDialog &&other) = delete;
|
||||
|
||||
MimeTypeDialog operator=(const MimeTypeDialog &other) = delete;
|
||||
MimeTypeDialog operator=(MimeTypeDialog &&other) = delete;
|
||||
|
||||
|
||||
QStringList mimeTypes() const
|
||||
{
|
||||
return m_mimeTypeModel->m_selectedMimeTypes;
|
||||
}
|
||||
private:
|
||||
MimeTypeModel *m_mimeTypeModel = nullptr;
|
||||
};
|
||||
|
||||
void BaseSettingsWidget::showAddMimeTypeDialog()
|
||||
{
|
||||
MimeTypeDialog dialog(m_mimeTypes->text().split(filterSeparator, QString::SkipEmptyParts),
|
||||
Core::ICore::dialogParent());
|
||||
if (dialog.exec() == QDialog::Rejected)
|
||||
return;
|
||||
m_mimeTypes->setText(dialog.mimeTypes().join(filterSeparator));
|
||||
}
|
||||
|
||||
StdIOSettingsWidget::StdIOSettingsWidget(const StdIOSettings *settings, QWidget *parent)
|
||||
: BaseSettingsWidget(settings, parent)
|
||||
, m_executable(new Utils::PathChooser(this))
|
||||
, m_arguments(new QLineEdit(settings->m_arguments, this))
|
||||
{
|
||||
auto mainLayout = qobject_cast<QGridLayout *>(layout());
|
||||
QTC_ASSERT(mainLayout, return);
|
||||
const int baseRows = mainLayout->rowCount();
|
||||
mainLayout->addWidget(new QLabel(tr("Executable:")), baseRows, 0);
|
||||
mainLayout->addWidget(m_executable, baseRows, 1);
|
||||
mainLayout->addWidget(new QLabel(tr("Arguments:")), baseRows + 1, 0);
|
||||
m_executable->setExpectedKind(Utils::PathChooser::ExistingCommand);
|
||||
m_executable->setPath(QDir::toNativeSeparators(settings->m_executable));
|
||||
mainLayout->addWidget(m_arguments, baseRows + 1, 1);
|
||||
}
|
||||
|
||||
QString StdIOSettingsWidget::executable() const
|
||||
{
|
||||
return m_executable->path();
|
||||
}
|
||||
|
||||
QString StdIOSettingsWidget::arguments() const
|
||||
{
|
||||
return m_arguments->text();
|
||||
}
|
||||
|
||||
} // namespace LanguageClient
|
||||
|
||||
@@ -28,32 +28,48 @@
|
||||
#include <coreplugin/dialogs/ioptionspage.h>
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QLabel>
|
||||
#include <QPointer>
|
||||
#include <QWidget>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QCheckBox;
|
||||
class QLineEdit;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils { class PathChooser; }
|
||||
|
||||
namespace LanguageClient {
|
||||
|
||||
constexpr char noLanguageFilter[] = "No Filter";
|
||||
|
||||
class BaseClient;
|
||||
|
||||
struct LanguageFilter
|
||||
{
|
||||
QStringList mimeTypes;
|
||||
QStringList filePattern;
|
||||
};
|
||||
|
||||
class BaseSettings
|
||||
{
|
||||
public:
|
||||
BaseSettings() = default;
|
||||
BaseSettings(const QString &name, bool enabled, const QString &mimeTypeName)
|
||||
BaseSettings(const QString &name, bool enabled, const LanguageFilter &filter)
|
||||
: m_name(name)
|
||||
, m_enabled(enabled)
|
||||
, m_mimeType(mimeTypeName)
|
||||
, m_languageFilter(filter)
|
||||
{}
|
||||
|
||||
virtual ~BaseSettings() = default;
|
||||
|
||||
QString m_name = QString("New Language Server");
|
||||
bool m_enabled = true;
|
||||
QString m_mimeType = QLatin1String(noLanguageFilter);
|
||||
LanguageFilter m_languageFilter;
|
||||
QPointer<BaseClient> m_client; // not owned
|
||||
|
||||
virtual void applyFromSettingsWidget(QWidget *widget);
|
||||
virtual QWidget *createSettingsWidget(QWidget *parent = nullptr) const;
|
||||
virtual BaseSettings *copy() const { return new BaseSettings(*this); }
|
||||
virtual bool needsRestart() const;
|
||||
virtual bool isValid() const ;
|
||||
@@ -72,9 +88,9 @@ class StdIOSettings : public BaseSettings
|
||||
{
|
||||
public:
|
||||
StdIOSettings() = default;
|
||||
StdIOSettings(const QString &name, bool enabled, const QString &mimeTypeName,
|
||||
StdIOSettings(const QString &name, bool enabled, const LanguageFilter &filter,
|
||||
const QString &executable, const QString &arguments)
|
||||
: BaseSettings(name, enabled, mimeTypeName)
|
||||
: BaseSettings(name, enabled, filter)
|
||||
, m_executable(executable)
|
||||
, m_arguments(arguments)
|
||||
{}
|
||||
@@ -84,6 +100,8 @@ public:
|
||||
QString m_executable;
|
||||
QString m_arguments;
|
||||
|
||||
void applyFromSettingsWidget(QWidget *widget) override;
|
||||
QWidget *createSettingsWidget(QWidget *parent = nullptr) const override;
|
||||
BaseSettings *copy() const override { return new StdIOSettings(*this); }
|
||||
bool needsRestart() const override;
|
||||
bool isValid() const override;
|
||||
@@ -106,4 +124,39 @@ public:
|
||||
static void toSettings(QSettings *settings, const QList<StdIOSettings *> &languageClientSettings);
|
||||
};
|
||||
|
||||
class BaseSettingsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit BaseSettingsWidget(const BaseSettings* settings, QWidget *parent = nullptr);
|
||||
~BaseSettingsWidget() = default;
|
||||
|
||||
QString name() const;
|
||||
LanguageFilter filter() const;
|
||||
|
||||
private:
|
||||
void showAddMimeTypeDialog();
|
||||
|
||||
QLineEdit *m_name = nullptr;
|
||||
QLabel *m_mimeTypes = nullptr;
|
||||
QLineEdit *m_filePattern = nullptr;
|
||||
|
||||
static constexpr char filterSeparator = ';';
|
||||
};
|
||||
|
||||
class StdIOSettingsWidget : public BaseSettingsWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit StdIOSettingsWidget(const StdIOSettings* settings, QWidget *parent = nullptr);
|
||||
~StdIOSettingsWidget() = default;
|
||||
|
||||
QString executable() const;
|
||||
QString arguments() const;
|
||||
|
||||
private:
|
||||
Utils::PathChooser *m_executable = nullptr;
|
||||
QLineEdit *m_arguments = nullptr;
|
||||
};
|
||||
|
||||
} // namespace LanguageClient
|
||||
|
||||
@@ -278,11 +278,6 @@ QString CustomExecutableRunConfiguration::defaultDisplayName() const
|
||||
return tr("Run %1").arg(QDir::toNativeSeparators(rawExecutable()));
|
||||
}
|
||||
|
||||
Abi CustomExecutableRunConfiguration::abi() const
|
||||
{
|
||||
return Abi(); // return an invalid ABI: We do not know what we will end up running!
|
||||
}
|
||||
|
||||
// Factory
|
||||
|
||||
CustomExecutableRunConfigurationFactory::CustomExecutableRunConfigurationFactory() :
|
||||
|
||||
@@ -45,7 +45,6 @@ public:
|
||||
/** Returns whether this runconfiguration ever was configured with an executable
|
||||
*/
|
||||
bool isConfigured() const override;
|
||||
Abi abi() const override;
|
||||
ConfigurationState ensureConfigured(QString *errorMessage) override;
|
||||
|
||||
QString defaultDisplayName() const;
|
||||
|
||||
@@ -489,7 +489,7 @@ LanguageExtensions GccToolChain::languageExtensions(const QStringList &cxxflags)
|
||||
else
|
||||
extensions &= ~LanguageExtensions(LanguageExtension::Gnu);
|
||||
} else if (flag == "-fopenmp") {
|
||||
extensions |= LanguageExtension::Gnu;
|
||||
extensions |= LanguageExtension::OpenMP;
|
||||
} else if (flag == "-fms-extensions") {
|
||||
extensions |= LanguageExtension::Microsoft;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "../project.h"
|
||||
#include "../projectexplorer.h"
|
||||
#include "../projectexplorerconstants.h"
|
||||
#include "../projecttree.h"
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/messagemanager.h>
|
||||
|
||||
@@ -290,7 +291,7 @@ void JsonWizard::accept()
|
||||
openFiles(m_files);
|
||||
|
||||
auto node = static_cast<ProjectExplorer::Node*>(value(ProjectExplorer::Constants::PREFERRED_PROJECT_NODE).value<void*>());
|
||||
if (node) // PREFERRED_PROJECT_NODE is not set for newly created projects
|
||||
if (node && ProjectTree::hasNode(node)) // PREFERRED_PROJECT_NODE is not set for newly created projects
|
||||
openProjectForNode(node);
|
||||
}
|
||||
|
||||
|
||||
@@ -1296,6 +1296,11 @@ bool MsvcToolChain::operator ==(const ToolChain &other) const
|
||||
return m_varsBatArg == msvcTc->m_varsBatArg;
|
||||
}
|
||||
|
||||
void MsvcToolChain::cancelMsvcToolChainDetection()
|
||||
{
|
||||
envModThreadPool()->clear();
|
||||
}
|
||||
|
||||
bool MsvcToolChainFactory::canRestore(const QVariantMap &data)
|
||||
{
|
||||
const Core::Id id = typeIdFromMap(data);
|
||||
|
||||
@@ -82,6 +82,8 @@ public:
|
||||
|
||||
bool operator == (const ToolChain &) const override;
|
||||
|
||||
static void cancelMsvcToolChainDetection();
|
||||
|
||||
protected:
|
||||
explicit MsvcToolChain(Core::Id typeId, const QString &name, const Abi &abi,
|
||||
const QString &varsBat, const QString &varsBatArg,
|
||||
|
||||
@@ -1739,6 +1739,7 @@ ExtensionSystem::IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown()
|
||||
disconnect(ModeManager::instance(), &ModeManager::currentModeChanged,
|
||||
dd, &ProjectExplorerPluginPrivate::currentModeChanged);
|
||||
ProjectTree::aboutToShutDown();
|
||||
ToolChainManager::aboutToShutdown();
|
||||
SessionManager::closeAllProjects();
|
||||
|
||||
dd->m_shuttingDown = true;
|
||||
|
||||
@@ -127,12 +127,18 @@ ProjectWelcomePage::ProjectWelcomePage()
|
||||
auto act = new QAction(tr("Open Session #%1").arg(i), this);
|
||||
Command *cmd = ActionManager::registerAction(act, sessionBase.withSuffix(i), welcomeContext);
|
||||
cmd->setDefaultKeySequence(QKeySequence((useMacShortcuts ? tr("Ctrl+Meta+%1") : tr("Ctrl+Alt+%1")).arg(i)));
|
||||
connect(act, &QAction::triggered, this, [this, i] { openSessionAt(i - 1); });
|
||||
connect(act, &QAction::triggered, this, [this, i] {
|
||||
if (i <= m_sessionModel->rowCount())
|
||||
openSessionAt(i - 1);
|
||||
});
|
||||
|
||||
act = new QAction(tr("Open Recent Project #%1").arg(i), this);
|
||||
cmd = ActionManager::registerAction(act, projectBase.withSuffix(i), welcomeContext);
|
||||
cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+%1").arg(i)));
|
||||
connect(act, &QAction::triggered, this, [this, i] { openProjectAt(i - 1); });
|
||||
connect(act, &QAction::triggered, this, [this, i] {
|
||||
if (i <= m_projectModel->rowCount(QModelIndex()))
|
||||
openProjectAt(i - 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -456,10 +456,11 @@ void ProjectWizardPage::initializeProjectTree(Node *context, const QStringList &
|
||||
}
|
||||
root->prependChild(createNoneNode(&selector));
|
||||
|
||||
// Set combobox to context node:
|
||||
// Set combobox to context node if that appears in the tree:
|
||||
auto predicate = [context](TreeItem *ti) { return static_cast<AddNewTree*>(ti)->node() == context; };
|
||||
TreeItem *contextItem = root->findAnyChild(predicate);
|
||||
m_ui->projectComboBox->setCurrentIndex(m_model.indexForItem(contextItem));
|
||||
if (contextItem)
|
||||
m_ui->projectComboBox->setCurrentIndex(m_model.indexForItem(contextItem));
|
||||
|
||||
setAdditionalInfo(selector.deployingProjects());
|
||||
setBestNode(selector.bestChoice());
|
||||
|
||||
@@ -310,17 +310,6 @@ QVariantMap RunConfiguration::toMap() const
|
||||
return map;
|
||||
}
|
||||
|
||||
Abi RunConfiguration::abi() const
|
||||
{
|
||||
BuildConfiguration *bc = target()->activeBuildConfiguration();
|
||||
if (!bc)
|
||||
return Abi::hostAbi();
|
||||
ToolChain *tc = ToolChainKitInformation::toolChain(target()->kit(), Constants::CXX_LANGUAGE_ID);
|
||||
if (!tc)
|
||||
return Abi::hostAbi();
|
||||
return tc->targetAbi();
|
||||
}
|
||||
|
||||
BuildTargetInfo RunConfiguration::buildTargetInfo() const
|
||||
{
|
||||
return target()->applicationTargets().buildTargetInfo(m_buildKey);
|
||||
@@ -1334,13 +1323,6 @@ Utils::Icon RunControl::icon() const
|
||||
return d->icon;
|
||||
}
|
||||
|
||||
Abi RunControl::abi() const
|
||||
{
|
||||
if (const RunConfiguration *rc = d->runConfiguration.data())
|
||||
return rc->abi();
|
||||
return Abi();
|
||||
}
|
||||
|
||||
IDevice::ConstPtr RunControl::device() const
|
||||
{
|
||||
return d->device;
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
namespace Utils { class OutputFormatter; }
|
||||
|
||||
namespace ProjectExplorer {
|
||||
class Abi;
|
||||
class BuildConfiguration;
|
||||
class GlobalOrProjectAspect;
|
||||
class Node;
|
||||
@@ -172,7 +171,6 @@ public:
|
||||
QVariantMap toMap() const override;
|
||||
|
||||
virtual Runnable runnable() const;
|
||||
virtual Abi abi() const;
|
||||
|
||||
// Return a handle to the build system target that created this run configuration.
|
||||
// May return an empty string if no target built the executable!
|
||||
@@ -439,7 +437,6 @@ public:
|
||||
|
||||
Utils::ProcessHandle applicationProcessHandle() const;
|
||||
void setApplicationProcessHandle(const Utils::ProcessHandle &handle);
|
||||
Abi abi() const;
|
||||
IDevice::ConstPtr device() const;
|
||||
|
||||
RunConfiguration *runConfiguration() const;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "abi.h"
|
||||
#include "kitinformation.h"
|
||||
#include "msvctoolchain.h"
|
||||
#include "toolchain.h"
|
||||
#include "toolchainsettingsaccessor.h"
|
||||
|
||||
@@ -249,4 +250,11 @@ bool ToolChainManager::isLanguageSupported(const Core::Id &id)
|
||||
return Utils::contains(d->m_languages, Utils::equal(&LanguageDisplayPair::id, id));
|
||||
}
|
||||
|
||||
void ToolChainManager::aboutToShutdown()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
MsvcToolChain::cancelMsvcToolChainDetection();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ProjectExplorer
|
||||
|
||||
@@ -74,6 +74,8 @@ public:
|
||||
static QString displayNameOfLanguageId(const Core::Id &id);
|
||||
static bool isLanguageSupported(const Core::Id &id);
|
||||
|
||||
static void aboutToShutdown();
|
||||
|
||||
void saveToolChains();
|
||||
|
||||
signals:
|
||||
|
||||
@@ -177,6 +177,8 @@ void WinDebugInterface::dispatchDebugOutput()
|
||||
m_outputMutex.lock();
|
||||
for (auto &entry : m_debugOutput) {
|
||||
std::vector<QString> &src = entry.second;
|
||||
if (src.empty())
|
||||
continue;
|
||||
QString dst;
|
||||
size_t n = std::min(maxMessagesToSend, src.size());
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user