2 Copyright 2007 David Nolden <david.nolden.kdevelop@art-master.de>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
19 #include "stringhelpers.h"
20 #include "safetycounter.h"
23 #include <QStringList>
28 class ParamIteratorPrivate
40 return findCommaOrEnd( m_source
, m_cur
, m_parens
[ 1 ] );
45 bool parenFits( QChar c1
, QChar c2
)
47 if( c1
== '<' && c2
== '>' ) return true;
48 else if( c1
== '(' && c2
== ')' ) return true;
49 else if( c1
== '[' && c2
== ']' ) return true;
50 else if( c1
== '{' && c2
== '}' ) return true;
55 int findClose( const QString
& str
, int pos
)
61 for( int a
= pos
; a
< (int)str
.length(); a
++)
63 switch(str
[a
].unicode()) {
68 st
.push_front( str
[a
] );
72 if( last
== '-' ) break;
76 if( !st
.isEmpty() && parenFits(st
.front(), str
[a
]) )
85 while( a
< (int)str
.length() && (str
[a
] != '"' || last
== '\\'))
95 while( a
< (int)str
.length() && (str
[a
] != '\'' || last
== '\\'))
115 int findCommaOrEnd( const QString
& str
, int pos
, QChar validEnd
)
118 for( int a
= pos
; a
< (int)str
.length(); a
++)
120 switch(str
[a
].unicode())
127 a
= findClose( str
, a
);
128 if( a
== -1 ) return str
.length();
134 if( validEnd
!= ' ' && validEnd
!= str
[a
] )
145 QString
reverse( const QString
& str
) {
147 int len
= str
.length();
148 for( int a
= len
-1; a
>= 0; --a
) {
149 switch(str
[a
].toAscii()) {
182 void skipFunctionArguments(QString str
, QStringList
& skippedArguments
, int& argumentsStart
) {
183 //Blank out everything that can confuse the bracket-matching algorithm
184 str
.replace("<<", "__");
185 str
.replace(">>", "__");
186 str
.replace("\\\"", "__");
187 str
.replace("->", "__");
188 QString reversed
= reverse( str
.left(argumentsStart
) );
189 //Now we should decrease argumentStart at the end by the count of steps we go right until we find the beginning of the function
190 SafetyCounter
s( 1000 );
193 int len
= reversed
.length();
194 //we are searching for an opening-brace, but the reversion has also reversed the brace
195 while( pos
< len
&& s
) {
197 pos
= KDevelop::findCommaOrEnd( reversed
, pos
) ;
198 if( pos
> lastPos
) {
199 QString arg
= reverse( reversed
.mid(lastPos
, pos
-lastPos
) ).trimmed();
201 skippedArguments
.push_front( arg
); //We are processing the reversed reverseding, so push to front
203 if( reversed
[pos
] == ')' )
210 kDebug(9007) << "skipFunctionArguments: Safety-counter triggered";
213 argumentsStart
-= pos
;
216 QString
reduceWhiteSpace(QString str
) {
220 QChar spaceChar
= ' ';
222 bool hadSpace
= false;
223 for( int a
= 0; a
< str
.length(); a
++ ) {
224 if( str
[a
].isSpace() ) {
238 void fillString( QString
& str
, int start
, int end
, QChar replacement
) {
239 for( int a
= start
; a
< end
; a
++) str
[a
] = replacement
;
242 QString
stripFinalWhitespace(QString str
) {
244 for( int a
= str
.length() - 1; a
>= 0; --a
) {
245 if( !str
[a
].isSpace() )
246 return str
.left( a
+1 );
252 QString
clearComments( QString str
, QChar replacement
) {
253 if( str
.isEmpty() ) return "";
255 SafetyCounter
s( 1000 );
258 int len
= str
.length();
259 while( (pos
= str
.indexOf( "/*", lastPos
)) != -1 ) {
261 int i
= str
.indexOf( "*/", pos
);
262 if( i
!= -1 && i
<= len
- 2 ) {
263 fillString( str
, pos
, i
+2, replacement
);
265 if( lastPos
== len
) break;
272 while( (pos
= str
.indexOf( "//", lastPos
)) != -1 ) {
274 int i
= str
.indexOf( "\n", pos
);
275 if( i
!= -1 && i
<= len
- 1 ) {
276 fillString( str
, pos
, i
+1, replacement
);
279 fillString( str
, pos
, len
, replacement
);
287 QString
clearStrings( QString str
, QChar
/*replacement*/ ) {
288 ///@todo implement: Replace all strings with the given replacement-character
293 static inline bool isWhite( QChar c
) {
296 void rStrip( const QString
& str
, QString
& from
) {
297 if( str
.isEmpty() ) return;
300 int ip
= from
.length();
301 int s
= from
.length();
303 for( int a
= s
-1; a
>= 0; a
-- ) {
304 if( isWhite( from
[a
] ) ) {
307 if( from
[a
] == str
[i
] ) {
310 if( i
== (int)str
.length() ) break;
317 if( ip
!= (int)from
.length() ) from
= from
.left( ip
);
320 void strip( const QString
& str
, QString
& from
) {
321 if( str
.isEmpty() ) return;
325 int s
= from
.length();
327 for( int a
= 0; a
< s
; a
++ ) {
328 if( isWhite( from
[a
] ) ) {
331 if( from
[a
] == str
[i
] ) {
334 if( i
== (int)str
.length() ) break;
341 if( ip
) from
= from
.mid( ip
);
344 QString
formatComment( const QString
& comment
) {
349 ret
= comment
.mid( i
);
351 ///remove the star in each line
352 QStringList lines
= comment
.split( "\n", QString::KeepEmptyParts
);
354 if( lines
.isEmpty() ) return ret
;
356 QStringList::iterator it
= lines
.begin();
357 QStringList::iterator eit
= lines
.end();
359 if( it
!= lines
.end() ) {
361 for( ; it
!= eit
; ++it
) {
364 rStrip( "/**", *it
);
367 if( lines
.front().trimmed().isEmpty() )
370 if( !lines
.isEmpty() && lines
.back().trimmed().isEmpty() )
374 ret
= lines
.join( "\n" );
380 ParamIterator::~ParamIterator()
385 ParamIterator::ParamIterator( QString parens
, QString source
, int offset
) : d(new ParamIteratorPrivate
)
387 d
->m_source
= source
;
388 d
->m_parens
= parens
;
391 d
->m_curEnd
= offset
;
392 d
->m_end
= d
->m_source
.length();
394 ///The whole search should be stopped when: A) The end-sign is found on the top-level B) A closing-brace of parameters was found
395 int parenBegin
= d
->m_source
.indexOf( parens
[ 0 ], offset
);
397 //Search for an interrupting end-sign that comes before the found paren-begin
399 if( parens
.length() > 2 ) {
400 foundEnd
= d
->m_source
.indexOf( parens
[2], offset
);
401 if( foundEnd
> parenBegin
&& parenBegin
!= -1 )
405 if( foundEnd
!= -1 ) {
406 //We have to stop the search, because we found an interrupting end-sign before the opening-paren
407 d
->m_prefix
= d
->m_source
.mid( offset
, foundEnd
- offset
);
409 d
->m_curEnd
= d
->m_end
= d
->m_cur
= foundEnd
;
411 if( parenBegin
!= -1 ) {
412 //We have a valid prefix before an opening-paren. Take the prefix, and start iterating parameters.
413 d
->m_prefix
= d
->m_source
.mid( offset
, parenBegin
- offset
);
414 d
->m_cur
= parenBegin
+ 1;
415 d
->m_curEnd
= d
->next();
416 if( d
->m_curEnd
== d
->m_source
.length() ) {
417 //The paren was not closed. It might be an identifier like "operator<", so count everything as prefix.
418 d
->m_prefix
= d
->m_source
.mid(offset
);
419 d
->m_curEnd
= d
->m_end
= d
->m_cur
= d
->m_source
.length();
422 //We have neither found an ending-character, nor an opening-paren, so take the whole input and end
423 d
->m_prefix
= d
->m_source
.mid(offset
);
424 d
->m_curEnd
= d
->m_end
= d
->m_cur
= d
->m_source
.length();
429 ParamIterator
& ParamIterator::operator ++()
431 if( d
->m_source
[d
->m_curEnd
] == d
->m_parens
[1] ) {
432 //We have reached the end-paren. Stop iterating.
433 d
->m_cur
= d
->m_end
= d
->m_curEnd
+ 1;
435 //Iterate on through parameters
436 d
->m_cur
= d
->m_curEnd
+ 1;
437 if ( d
->m_cur
< ( int ) d
->m_source
.length() )
439 d
->m_curEnd
= d
->next();
445 QString
ParamIterator::operator *()
447 return d
->m_source
.mid( d
->m_cur
, d
->m_curEnd
- d
->m_cur
).trimmed();
450 ParamIterator::operator bool() const
452 return d
->m_cur
< ( int ) d
->m_end
;
455 QString
ParamIterator::prefix() const
460 uint
ParamIterator::position() const {
461 return (uint
)d
->m_cur
;