Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Tobias Hunger
qt-creator
Commits
80b52e58
Commit
80b52e58
authored
Nov 26, 2009
by
Christian Kamm
Browse files
Improve ChangeSet to support more rewriting operations.
parent
5bcd46b6
Changes
6
Hide whitespace changes
Inline
Side-by-side
src/libs/utils/changeset.cpp
View file @
80b52e58
...
...
@@ -44,196 +44,312 @@
namespace
Utils
{
ChangeSet
::
ChangeSet
()
:
m_string
(
0
),
m_cursor
(
0
)
:
m_string
(
0
),
m_cursor
(
0
)
,
m_error
(
false
)
{
}
static
bool
overlaps
(
int
posA
,
int
lengthA
,
int
posB
,
int
lengthB
)
{
return
(
posA
<
posB
+
lengthB
&&
posA
+
lengthA
>
posB
+
lengthB
)
||
(
posA
<
posB
&&
posA
+
lengthA
>
posB
);
if
(
lengthB
>
0
)
{
return
// right edge of B contained in A
(
posA
<
posB
+
lengthB
&&
posA
+
lengthA
>=
posB
+
lengthB
)
// left edge of B contained in A
||
(
posA
<=
posB
&&
posA
+
lengthA
>
posB
)
// A contained in B
||
(
posB
<
posA
&&
posB
+
lengthB
>
posA
+
lengthA
);
}
else
{
return
(
posB
>
posA
&&
posB
<
posA
+
lengthA
);
}
}
bool
ChangeSet
::
hasOverlap
(
int
pos
,
int
length
)
{
{
QListIterator
<
Replace
>
i
(
m_replaceList
);
while
(
i
.
hasNext
())
{
const
Replace
&
cmd
=
i
.
next
();
if
(
overlaps
(
pos
,
length
,
cmd
.
pos
,
cmd
.
length
))
QListIterator
<
EditOp
>
i
(
m_operationList
);
while
(
i
.
hasNext
())
{
const
EditOp
&
cmd
=
i
.
next
();
switch
(
cmd
.
type
)
{
case
EditOp
::
Replace
:
if
(
overlaps
(
pos
,
length
,
cmd
.
pos1
,
cmd
.
length
))
return
true
;
}
}
{
QListIterator
<
Move
>
i
(
m_moveList
);
while
(
i
.
hasNext
())
{
const
Move
&
cmd
=
i
.
next
();
if
(
overlaps
(
pos
,
length
,
cmd
.
pos
,
cmd
.
length
))
break
;
case
EditOp
::
Move
:
if
(
overlaps
(
pos
,
length
,
cmd
.
pos1
,
cmd
.
length
))
return
true
;
if
(
cmd
.
pos2
>
pos
&&
cmd
.
pos2
<
pos
+
length
)
return
true
;
break
;
case
EditOp
::
Insert
:
if
(
cmd
.
pos1
>
pos
&&
cmd
.
pos1
<
pos
+
length
)
return
true
;
break
;
case
EditOp
::
Remove
:
if
(
overlaps
(
pos
,
length
,
cmd
.
pos1
,
cmd
.
length
))
return
true
;
break
;
case
EditOp
::
Flip
:
if
(
overlaps
(
pos
,
length
,
cmd
.
pos1
,
cmd
.
length
))
return
true
;
if
(
overlaps
(
pos
,
length
,
cmd
.
pos2
,
cmd
.
length
))
return
true
;
break
;
case
EditOp
::
Copy
:
if
(
overlaps
(
pos
,
length
,
cmd
.
pos1
,
cmd
.
length
))
return
true
;
if
(
cmd
.
pos2
>
pos
&&
cmd
.
pos2
<
pos
+
length
)
return
true
;
break
;
case
EditOp
::
Unset
:
break
;
}
return
false
;
}
}
bool
ChangeSet
::
hasMoveInto
(
int
pos
,
int
length
)
{
QListIterator
<
Move
>
i
(
m_moveList
);
while
(
i
.
hasNext
())
{
const
Move
&
cmd
=
i
.
next
();
if
(
cmd
.
to
>=
pos
&&
cmd
.
to
<
pos
+
length
)
return
true
;
}
return
false
;
}
bool
ChangeSet
::
isEmpty
()
const
{
if
(
m_replaceList
.
isEmpty
()
&&
m_move
List
.
isEmpty
()
)
return
true
;
return
m_operation
List
.
isEmpty
()
;
}
return
false
;
QList
<
ChangeSet
::
EditOp
>
ChangeSet
::
operationList
()
const
{
return
m_operationList
;
}
void
ChangeSet
::
clear
()
{
m_string
=
0
;
m_cursor
=
0
;
m_operationList
.
clear
();
m_error
=
false
;
}
QList
<
ChangeSet
::
R
eplace
>
ChangeSet
::
replaceList
()
const
bool
ChangeSet
::
r
eplace
(
int
pos
,
int
length
,
const
QString
&
replacement
)
{
return
m_replaceList
;
if
(
hasOverlap
(
pos
,
length
))
m_error
=
true
;
EditOp
cmd
(
EditOp
::
Replace
);
cmd
.
pos1
=
pos
;
cmd
.
length
=
length
;
cmd
.
text
=
replacement
;
m_operationList
+=
cmd
;
return
!
m_error
;
}
QList
<
ChangeSet
::
Move
>
ChangeSet
::
moveList
()
const
bool
ChangeSet
::
move
(
int
pos
,
int
length
,
int
to
)
{
return
m_moveList
;
if
(
hasOverlap
(
pos
,
length
)
||
hasOverlap
(
to
,
0
)
||
overlaps
(
pos
,
length
,
to
,
0
))
m_error
=
true
;
EditOp
cmd
(
EditOp
::
Move
);
cmd
.
pos1
=
pos
;
cmd
.
length
=
length
;
cmd
.
pos2
=
to
;
m_operationList
+=
cmd
;
return
!
m_error
;
}
void
ChangeSet
::
clear
(
)
bool
ChangeSet
::
insert
(
int
pos
,
const
QString
&
text
)
{
m_string
=
0
;
m_cursor
=
0
;
m_replaceList
.
clear
();
m_moveList
.
clear
();
if
(
hasOverlap
(
pos
,
0
))
m_error
=
true
;
EditOp
cmd
(
EditOp
::
Insert
);
cmd
.
pos1
=
pos
;
cmd
.
text
=
text
;
m_operationList
+=
cmd
;
return
!
m_error
;
}
void
ChangeSet
::
re
plac
e
(
int
pos
,
int
length
,
const
QString
&
replacement
)
bool
ChangeSet
::
re
mov
e
(
int
pos
,
int
length
)
{
Q_ASSERT
(
!
hasOverlap
(
pos
,
length
))
;
Q_ASSERT
(
!
hasMoveInto
(
pos
,
length
))
;
if
(
hasOverlap
(
pos
,
length
))
m_error
=
true
;
Replace
cmd
;
cmd
.
pos
=
pos
;
EditOp
cmd
(
EditOp
::
Remove
)
;
cmd
.
pos
1
=
pos
;
cmd
.
length
=
length
;
cmd
.
replacement
=
replacement
;
m_replaceList
+=
cmd
;
m_operationList
+=
cmd
;
return
!
m_error
;
}
void
ChangeSet
::
move
(
int
pos
,
int
length
,
int
to
)
bool
ChangeSet
::
flip
(
int
pos
1
,
int
length
,
int
pos2
)
{
Q_ASSERT
(
!
hasOverlap
(
pos
,
length
));
if
(
hasOverlap
(
pos1
,
length
)
||
hasOverlap
(
pos2
,
length
)
||
overlaps
(
pos1
,
length
,
pos2
,
length
))
m_error
=
true
;
Move
cmd
;
cmd
.
pos
=
pos
;
EditOp
cmd
(
EditOp
::
Flip
)
;
cmd
.
pos
1
=
pos
1
;
cmd
.
length
=
length
;
cmd
.
to
=
to
;
m_moveList
+=
cmd
;
cmd
.
pos2
=
pos2
;
m_operationList
+=
cmd
;
return
!
m_error
;
}
void
ChangeSet
::
doReplace
(
const
Replace
&
replace
)
bool
ChangeSet
::
copy
(
int
pos
,
int
length
,
int
to
)
{
int
diff
=
replace
.
replacement
.
size
()
-
replace
.
length
;
{
QMutableListIterator
<
Replace
>
i
(
m_replaceList
);
while
(
i
.
hasNext
())
{
Replace
&
c
=
i
.
next
();
if
(
replace
.
pos
<
c
.
pos
)
c
.
pos
+=
diff
;
else
if
(
replace
.
pos
+
replace
.
length
<
c
.
pos
+
c
.
length
)
c
.
length
+=
diff
;
}
}
if
(
hasOverlap
(
pos
,
length
)
||
hasOverlap
(
to
,
0
)
||
overlaps
(
pos
,
length
,
to
,
0
))
m_error
=
true
;
EditOp
cmd
(
EditOp
::
Copy
);
cmd
.
pos1
=
pos
;
cmd
.
length
=
length
;
cmd
.
pos2
=
to
;
m_operationList
+=
cmd
;
return
!
m_error
;
}
void
ChangeSet
::
doReplace
(
const
EditOp
&
replace
,
QList
<
EditOp
>
*
replaceList
)
{
Q_ASSERT
(
replace
.
type
==
EditOp
::
Replace
);
int
diff
=
replace
.
text
.
size
()
-
replace
.
length
;
{
QMutableListIterator
<
Move
>
i
(
m_mov
eList
);
QMutableListIterator
<
EditOp
>
i
(
*
replac
eList
);
while
(
i
.
hasNext
())
{
Move
&
c
=
i
.
next
();
if
(
replace
.
pos
<
c
.
pos
)
c
.
pos
+=
diff
;
else
if
(
replace
.
pos
+
replace
.
length
<
c
.
pos
+
c
.
length
)
c
.
length
+=
diff
;
if
(
replace
.
pos
<
c
.
to
)
c
.
to
+=
diff
;
EditOp
&
c
=
i
.
next
();
if
(
replace
.
pos1
<=
c
.
pos1
)
c
.
pos1
+=
diff
;
}
}
if
(
m_string
)
{
m_string
->
replace
(
replace
.
pos
,
replace
.
length
,
replace
.
replacemen
t
);
m_string
->
replace
(
replace
.
pos
1
,
replace
.
length
,
replace
.
tex
t
);
}
else
if
(
m_cursor
)
{
m_cursor
->
setPosition
(
replace
.
pos
);
m_cursor
->
setPosition
(
replace
.
pos
+
replace
.
length
,
QTextCursor
::
KeepAnchor
);
m_cursor
->
insertText
(
replace
.
replacemen
t
);
m_cursor
->
setPosition
(
replace
.
pos
1
);
m_cursor
->
setPosition
(
replace
.
pos
1
+
replace
.
length
,
QTextCursor
::
KeepAnchor
);
m_cursor
->
insertText
(
replace
.
tex
t
);
}
}
void
ChangeSet
::
doMove
(
const
Move
&
move
)
void
ChangeSet
::
convertToReplace
(
const
EditOp
&
op
,
QList
<
EditOp
>
*
replaceList
)
{
QString
text
;
if
(
m_string
)
{
text
=
m_string
->
mid
(
move
.
pos
,
move
.
length
);
}
else
if
(
m_cursor
)
{
m_cursor
->
setPosition
(
move
.
pos
);
m_cursor
->
setPosition
(
move
.
pos
+
move
.
length
,
QTextCursor
::
KeepAnchor
);
text
=
m_cursor
->
selectedText
();
}
EditOp
replace1
(
EditOp
::
Replace
);
EditOp
replace2
(
EditOp
::
Replace
);
switch
(
op
.
type
)
{
case
EditOp
::
Replace
:
replaceList
->
append
(
op
);
break
;
case
EditOp
::
Move
:
replace1
.
pos1
=
op
.
pos1
;
replace1
.
length
=
op
.
length
;
replaceList
->
append
(
replace1
);
replace2
.
pos1
=
op
.
pos2
;
replace2
.
text
=
textAt
(
op
.
pos1
,
op
.
length
);
replaceList
->
append
(
replace2
);
break
;
case
EditOp
::
Insert
:
replace1
.
pos1
=
op
.
pos1
;
replace1
.
text
=
op
.
text
;
replaceList
->
append
(
replace1
);
break
;
Replace
cut
;
cut
.
pos
=
move
.
pos
;
cut
.
length
=
move
.
length
;
Replace
paste
;
paste
.
pos
=
move
.
to
;
paste
.
length
=
0
;
paste
.
replacement
=
text
;
m_replaceList
.
append
(
cut
);
m_replaceList
.
append
(
paste
);
Replace
cmd
;
while
(
!
m_replaceList
.
isEmpty
())
{
cmd
=
m_replaceList
.
first
();
m_replaceList
.
removeFirst
();
doReplace
(
cmd
);
case
EditOp
::
Remove
:
replace1
.
pos1
=
op
.
pos1
;
replace1
.
length
=
op
.
length
;
replaceList
->
append
(
replace1
);
break
;
case
EditOp
::
Flip
:
replace1
.
pos1
=
op
.
pos1
;
replace1
.
length
=
op
.
length
;
replace1
.
text
=
textAt
(
op
.
pos2
,
op
.
length
);
replaceList
->
append
(
replace1
);
replace2
.
pos1
=
op
.
pos2
;
replace2
.
length
=
op
.
length
;
replace2
.
text
=
textAt
(
op
.
pos1
,
op
.
length
);
replaceList
->
append
(
replace2
);
break
;
case
EditOp
::
Copy
:
replace1
.
pos1
=
op
.
pos2
;
replace1
.
text
=
textAt
(
op
.
pos1
,
op
.
length
);
replaceList
->
append
(
replace1
);
break
;
case
EditOp
::
Unset
:
break
;
}
}
void
ChangeSet
::
write
(
QString
*
s
)
bool
ChangeSet
::
hadErrors
()
{
return
m_error
;
}
void
ChangeSet
::
apply
(
QString
*
s
)
{
m_string
=
s
;
write
_helper
();
apply
_helper
();
m_string
=
0
;
}
void
ChangeSet
::
write
(
QTextCursor
*
textCursor
)
void
ChangeSet
::
apply
(
QTextCursor
*
textCursor
)
{
m_cursor
=
textCursor
;
write
_helper
();
apply
_helper
();
m_cursor
=
0
;
}
void
ChangeSet
::
write_helper
(
)
QString
ChangeSet
::
textAt
(
int
pos
,
int
length
)
{
if
(
m_cursor
)
m_cursor
->
beginEditBlock
();
{
Replace
cmd
;
while
(
!
m_replaceList
.
isEmpty
())
{
cmd
=
m_replaceList
.
first
();
m_replaceList
.
removeFirst
();
doReplace
(
cmd
);
}
if
(
m_string
)
{
return
m_string
->
mid
(
pos
,
length
);
}
else
if
(
m_cursor
)
{
m_cursor
->
setPosition
(
pos
);
m_cursor
->
setPosition
(
pos
+
length
,
QTextCursor
::
KeepAnchor
);
return
m_cursor
->
selectedText
();
}
return
QString
();
}
void
ChangeSet
::
apply_helper
()
{
// convert all ops to replace
QList
<
EditOp
>
replaceList
;
{
Move
cmd
;
while
(
!
m_moveList
.
isEmpty
())
{
cmd
=
m_moveList
.
first
();
m_moveList
.
removeFirst
();
doMove
(
cmd
);
while
(
!
m_operationList
.
isEmpty
())
{
const
EditOp
cmd
(
m_operationList
.
first
());
m_operationList
.
removeFirst
();
convertToReplace
(
cmd
,
&
replaceList
);
}
}
// execute replaces
if
(
m_cursor
)
m_cursor
->
beginEditBlock
();
while
(
!
replaceList
.
isEmpty
())
{
const
EditOp
cmd
(
replaceList
.
first
());
replaceList
.
removeFirst
();
doReplace
(
cmd
,
&
replaceList
);
}
if
(
m_cursor
)
m_cursor
->
endEditBlock
();
}
...
...
src/libs/utils/changeset.h
View file @
80b52e58
...
...
@@ -46,6 +46,7 @@
#include <QtCore/QString>
#include <QtCore/QList>
#include <QtCore/QSharedPointer>
#include <QtGui/QTextCursor>
namespace
Utils
{
...
...
@@ -53,20 +54,26 @@ namespace Utils {
class
QTCREATOR_UTILS_EXPORT
ChangeSet
{
public:
struct
Replace
{
Replace
()
:
pos
(
0
),
length
(
0
)
{}
int
pos
;
struct
EditOp
{
enum
Type
{
Unset
,
Replace
,
Move
,
Insert
,
Remove
,
Flip
,
Copy
};
EditOp
()
:
type
(
Unset
),
pos1
(
0
),
pos2
(
0
),
length
(
0
)
{}
EditOp
(
Type
t
)
:
type
(
t
),
pos1
(
0
),
pos2
(
0
),
length
(
0
)
{}
Type
type
;
int
pos1
;
int
pos2
;
int
length
;
QString
replacement
;
};
struct
Move
{
Move
()
:
pos
(
0
),
length
(
0
),
to
(
0
)
{}
int
pos
;
int
length
;
int
to
;
QString
text
;
};
public:
...
...
@@ -74,31 +81,37 @@ public:
bool
isEmpty
()
const
;
QList
<
Replace
>
replaceList
()
const
;
QList
<
Move
>
moveList
()
const
;
// ### TODO: merge with replaceList
QList
<
EditOp
>
operationList
()
const
;
void
clear
();
void
replace
(
int
pos
,
int
length
,
const
QString
&
replacement
);
void
move
(
int
pos
,
int
length
,
int
to
);
bool
replace
(
int
pos
,
int
length
,
const
QString
&
replacement
);
bool
move
(
int
pos
,
int
length
,
int
to
);
bool
insert
(
int
pos
,
const
QString
&
text
);
bool
remove
(
int
pos
,
int
length
);
bool
flip
(
int
pos1
,
int
length
,
int
pos2
);
bool
copy
(
int
pos
,
int
length
,
int
to
);
bool
hadErrors
();
void
write
(
QString
*
s
);
void
write
(
QTextCursor
*
textCursor
);
void
apply
(
QString
*
s
);
void
apply
(
QTextCursor
*
textCursor
);
private:
bool
hasOverlap
(
int
pos
,
int
length
);
bool
hasMoveInto
(
int
pos
,
int
length
);
QString
textAt
(
int
pos
,
int
length
);
void
doReplace
(
const
Replace
&
replace
);
void
doMove
(
const
Move
&
move
);
void
doReplace
(
const
EditOp
&
replace
,
QList
<
EditOp
>
*
replace
List
);
void
convertToReplace
(
const
EditOp
&
op
,
QList
<
EditOp
>
*
replaceList
);
void
write
_helper
();
void
apply
_helper
();
private:
QString
*
m_string
;
QTextCursor
*
m_cursor
;
QList
<
Replace
>
m_replaceList
;
QList
<
Move
>
m_moveList
;
QList
<
EditOp
>
m_operationList
;
bool
m_error
;
};
}
// namespace Utils
...
...
src/plugins/cppeditor/cppquickfix.cpp
View file @
80b52e58
...
...
@@ -769,7 +769,7 @@ void QuickFixOperation::apply()
_textCursor
.
beginEditBlock
();
_changeSet
.
write
(
&
_textCursor
);
_changeSet
.
apply
(
&
_textCursor
);
if
(
_topLevelNode
)
reindent
(
range
);
...
...
tests/auto/auto.pro
View file @
80b52e58
...
...
@@ -5,4 +5,5 @@ SUBDIRS += \
debugger
\
fakevim
\
#
profilereader
\
aggregation
aggregation
\
changeset
tests/auto/changeset/changeset.pro
0 → 100644
View file @
80b52e58
QT
+=
testlib
#
Defines
import
symbol
as
empty
DEFINES
+=
QTCREATOR_UTILS_STATIC_LIB
UTILSDIR
=
..
/../../
src
/
libs
SOURCES
+=
\
tst_changeset
.
cpp
\
$$
UTILSDIR
/
utils
/
changeset
.
cpp
HEADERS
+=
\
$$
UTILSDIR
/
utils
/
changeset
.
h
INCLUDEPATH
+=
$$
UTILSDIR
TARGET
=
tst_
$$
TARGET
tests/auto/changeset/tst_changeset.cpp
0 → 100644
View file @
80b52e58
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include <utils/changeset.h>
#include <QtTest/QtTest>
class
tst_ChangeSet
:
public
QObject
{