Add AbstractDeclarationNavigationContext, and move the html-method from
[kdevelopdvcssupport.git] / language / duchain / stringhelpers.cpp
blob2cdd128bcf67144b9e154ecb0447f810c85ba15b
1 /*
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"
22 #include <QString>
23 #include <QStringList>
25 namespace KDevelop
28 class ParamIteratorPrivate
30 public:
31 QString m_prefix;
32 QString m_source;
33 QString m_parens;
34 int m_cur;
35 int m_curEnd;
36 int m_end;
38 int next() const
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;
51 else
52 return false;
55 int findClose( const QString& str , int pos )
57 int depth = 0;
58 QList<QChar> st;
59 QChar last = ' ';
61 for( int a = pos; a < (int)str.length(); a++)
63 switch(str[a].unicode()) {
64 case '<':
65 case '(':
66 case '[':
67 case '{':
68 st.push_front( str[a] );
69 depth++;
70 break;
71 case '>':
72 if( last == '-' ) break;
73 case ')':
74 case ']':
75 case '}':
76 if( !st.isEmpty() && parenFits(st.front(), str[a]) )
78 depth--;
79 st.pop_front();
81 break;
82 case '"':
83 last = str[a];
84 a++;
85 while( a < (int)str.length() && (str[a] != '"' || last == '\\'))
87 last = str[a];
88 a++;
90 continue;
91 break;
92 case '\'':
93 last = str[a];
94 a++;
95 while( a < (int)str.length() && (str[a] != '\'' || last == '\\'))
97 last = str[a];
98 a++;
100 continue;
101 break;
104 last = str[a];
106 if( depth == 0 )
108 return a;
112 return -1;
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())
122 case '"':
123 case '(':
124 case '[':
125 case '{':
126 case '<':
127 a = findClose( str, a );
128 if( a == -1 ) return str.length();
129 break;
130 case ')':
131 case ']':
132 case '}':
133 case '>':
134 if( validEnd != ' ' && validEnd != str[a] )
135 continue;
136 case ',':
137 return a;
141 return str.length();
145 QString reverse( const QString& str ) {
146 QString ret;
147 int len = str.length();
148 for( int a = len-1; a >= 0; --a ) {
149 switch(str[a].toAscii()) {
150 case '(':
151 ret += ')';
152 continue;
153 case '[':
154 ret += ']';
155 continue;
156 case '{':
157 ret += '}';
158 continue;
159 case '<':
160 ret += '>';
161 continue;
162 case ')':
163 ret += '(';
164 continue;
165 case ']':
166 ret += '[';
167 continue;
168 case '}':
169 ret += '{';
170 continue;
171 case '>':
172 ret += '<';
173 continue;
174 default:
175 ret += str[a];
176 continue;
179 return ret;
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 );
192 int pos = 0;
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 ) {
196 int lastPos = pos;
197 pos = KDevelop::findCommaOrEnd( reversed, pos ) ;
198 if( pos > lastPos ) {
199 QString arg = reverse( reversed.mid(lastPos, pos-lastPos) ).trimmed();
200 if( !arg.isEmpty() )
201 skippedArguments.push_front( arg ); //We are processing the reversed reverseding, so push to front
203 if( reversed[pos] == ')' )
204 break;
205 else
206 ++pos;
209 if( !s ) {
210 kDebug(9007) << "skipFunctionArguments: Safety-counter triggered";
213 argumentsStart -= pos;
216 QString reduceWhiteSpace(QString str) {
217 str = str.trimmed();
218 QString ret;
220 QChar spaceChar = ' ';
222 bool hadSpace = false;
223 for( int a = 0; a < str.length(); a++ ) {
224 if( str[a].isSpace() ) {
225 hadSpace = true;
226 } else {
227 if( hadSpace ) {
228 hadSpace = false;
229 ret += spaceChar;
231 ret += str[a];
235 return ret;
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 );
249 return QString();
252 QString clearComments( QString str, QChar replacement ) {
253 if( str.isEmpty() ) return "";
255 SafetyCounter s( 1000 );
256 int lastPos = 0;
257 int pos;
258 int len = str.length();
259 while( (pos = str.indexOf( "/*", lastPos )) != -1 ) {
260 if( !s ) return str;
261 int i = str.indexOf( "*/", pos );
262 if( i != -1 && i <= len - 2 ) {
263 fillString( str, pos, i+2, replacement );
264 lastPos = i+2;
265 if( lastPos == len ) break;
266 } else {
267 break;
271 lastPos = 0;
272 while( (pos = str.indexOf( "//", lastPos )) != -1 ) {
273 if( !s ) return str;
274 int i = str.indexOf( "\n", pos );
275 if( i != -1 && i <= len - 1 ) {
276 fillString( str, pos, i+1, replacement );
277 lastPos = i+1;
278 } else {
279 fillString( str, pos, len, replacement );
280 break;
284 return str;
287 QString clearStrings( QString str, QChar /*replacement*/ ) {
288 ///@todo implement: Replace all strings with the given replacement-character
289 return str;
293 static inline bool isWhite( QChar c ) {
294 return c.isSpace();
296 void rStrip( const QString& str, QString& from ) {
297 if( str.isEmpty() ) return;
299 int i = 0;
300 int ip = from.length();
301 int s = from.length();
303 for( int a = s-1; a >= 0; a-- ) {
304 if( isWhite( from[a] ) ) {
305 continue;
306 } else {
307 if( from[a] == str[i] ) {
308 i++;
309 ip = a;
310 if( i == (int)str.length() ) break;
311 } else {
312 break;
317 if( ip != (int)from.length() ) from = from.left( ip );
320 void strip( const QString& str, QString& from ) {
321 if( str.isEmpty() ) return;
323 int i = 0;
324 int ip = 0;
325 int s = from.length();
327 for( int a = 0; a < s; a++ ) {
328 if( isWhite( from[a] ) ) {
329 continue;
330 } else {
331 if( from[a] == str[i] ) {
332 i++;
333 ip = a+1;
334 if( i == (int)str.length() ) break;
335 } else {
336 break;
341 if( ip ) from = from.mid( ip );
344 QString formatComment( const QString& comment ) {
345 QString ret;
346 int i = 0;
348 if( i > 1 ) {
349 ret = comment.mid( i );
350 } else {
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 ) {
362 strip( "//", *it );
363 strip( "**", *it );
364 rStrip( "/**", *it );
367 if( lines.front().trimmed().isEmpty() )
368 lines.pop_front();
370 if( !lines.isEmpty() && lines.back().trimmed().isEmpty() )
371 lines.pop_back();
374 ret = lines.join( "\n" );
377 return ret;
380 ParamIterator::~ParamIterator()
382 delete d;
385 ParamIterator::ParamIterator( QString parens, QString source, int offset ) : d(new ParamIteratorPrivate)
387 d->m_source = source;
388 d->m_parens = parens;
390 d->m_cur = offset;
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
398 int foundEnd = -1;
399 if( parens.length() > 2 ) {
400 foundEnd = d->m_source.indexOf( parens[2], offset );
401 if( foundEnd > parenBegin && parenBegin != -1 )
402 foundEnd = -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;
410 } else {
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();
421 } else {
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;
434 } else {
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();
442 return *this;
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
457 return d->m_prefix;
460 uint ParamIterator::position() const {
461 return (uint)d->m_cur;