Code Coverage Statistics for Source File
c:\Tools\SD3\src\Libraries\ICSharpCode.TextEditor\Project\Src\Document\TextUtilities.cs
|
Sequence Point Coverage
N/A
0 of 0
|
Branch Coverage
N/A
0 of 0
|
Lines
313
|
Highlight:
Uncovered Code
Covered Code
| L | V | Source |
|---|---|---|
1 |
// <file> |
|
2 |
// <copyright see="prj:///doc/copyright.txt"/> |
|
3 |
// <license see="prj:///doc/license.txt"/> |
|
4 |
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/> |
|
5 |
// <version>$Revision: 2533 $</version> |
|
6 |
// </file> |
|
7 |
||
8 |
using System; |
|
9 |
using System.Diagnostics; |
|
10 |
using System.Text; |
|
11 |
||
12 |
namespace ICSharpCode.TextEditor.Document |
|
13 |
{ |
|
14 |
||
15 |
public sealed class TextUtilities |
|
16 |
{ |
|
17 |
/// <remarks> |
|
18 |
/// This function takes a string and converts the whitespace in front of |
|
19 |
/// it to tabs. If the length of the whitespace at the start of the string |
|
20 |
/// was not a whole number of tabs then there will still be some spaces just |
|
21 |
/// before the text starts. |
|
22 |
/// the output string will be of the form: |
|
23 |
/// 1. zero or more tabs |
|
24 |
/// 2. zero or more spaces (less than tabIndent) |
|
25 |
/// 3. the rest of the line |
|
26 |
/// </remarks> |
|
27 |
public static string LeadingWhiteSpaceToTabs(string line, int tabIndent) { |
|
28 |
StringBuilder sb = new StringBuilder(line.Length); |
|
29 |
int consecutiveSpaces = 0; |
|
30 |
int i = 0; |
|
31 |
for(i = 0; i < line.Length; i++) { |
|
32 |
if(line[i] == ' ') { |
|
33 |
consecutiveSpaces++; |
|
34 |
if(consecutiveSpaces == tabIndent) { |
|
35 |
sb.Append('\t'); |
|
36 |
consecutiveSpaces = 0; |
|
37 |
} |
|
38 |
} |
|
39 |
else if(line[i] == '\t') { |
|
40 |
sb.Append('\t'); |
|
41 |
// if we had say 3 spaces then a tab and tabIndent was 4 then |
|
42 |
// we would want to simply replace all of that with 1 tab |
|
43 |
consecutiveSpaces = 0; |
|
44 |
} |
|
45 |
else { |
|
46 |
break; |
|
47 |
} |
|
48 |
} |
|
49 |
||
50 |
if(i < line.Length) { |
|
51 |
sb.Append(line.Substring(i-consecutiveSpaces)); |
|
52 |
} |
|
53 |
return sb.ToString(); |
|
54 |
} |
|
55 |
||
56 |
public static bool IsLetterDigitOrUnderscore(char c) |
|
57 |
{ |
|
58 |
if(!Char.IsLetterOrDigit(c)) { |
|
59 |
return c == '_'; |
|
60 |
} |
|
61 |
return true; |
|
62 |
} |
|
63 |
||
64 |
public enum CharacterType { |
|
65 |
LetterDigitOrUnderscore, |
|
66 |
WhiteSpace, |
|
67 |
Other |
|
68 |
} |
|
69 |
||
70 |
/// <remarks> |
|
71 |
/// This method returns the expression before a specified offset. |
|
72 |
/// That method is used in code completion to determine the expression given |
|
73 |
/// to the parser for type resolve. |
|
74 |
/// </remarks> |
|
75 |
public static string GetExpressionBeforeOffset(TextArea textArea, int initialOffset) |
|
76 |
{ |
|
77 |
IDocument document = textArea.Document; |
|
78 |
int offset = initialOffset; |
|
79 |
while (offset - 1 > 0) { |
|
80 |
switch (document.GetCharAt(offset - 1)) { |
|
81 |
case '\n': |
|
82 |
case '\r': |
|
83 |
case '}': |
|
84 |
goto done; |
|
85 |
// offset = SearchBracketBackward(document, offset - 2, '{','}'); |
|
86 |
// break; |
|
87 |
case ']': |
|
88 |
offset = SearchBracketBackward(document, offset - 2, '[',']'); |
|
89 |
break; |
|
90 |
case ')': |
|
91 |
offset = SearchBracketBackward(document, offset - 2, '(',')'); |
|
92 |
break; |
|
93 |
case '.': |
|
94 |
--offset; |
|
95 |
break; |
|
96 |
case '"': |
|
97 |
if (offset < initialOffset - 1) { |
|
98 |
return null; |
|
99 |
} |
|
100 |
return "\"\""; |
|
101 |
case '\'': |
|
102 |
if (offset < initialOffset - 1) { |
|
103 |
return null; |
|
104 |
} |
|
105 |
return "'a'"; |
|
106 |
case '>': |
|
107 |
if (document.GetCharAt(offset - 2) == '-') { |
|
108 |
offset -= 2; |
|
109 |
break; |
|
110 |
} |
|
111 |
goto done; |
|
112 |
default: |
|
113 |
if (Char.IsWhiteSpace(document.GetCharAt(offset - 1))) { |
|
114 |
--offset; |
|
115 |
break; |
|
116 |
} |
|
117 |
int start = offset - 1; |
|
118 |
if (!IsLetterDigitOrUnderscore(document.GetCharAt(start))) { |
|
119 |
goto done; |
|
120 |
} |
|
121 |
||
122 |
while (start > 0 && IsLetterDigitOrUnderscore(document.GetCharAt(start - 1))) { |
|
123 |
--start; |
|
124 |
} |
|
125 |
string word = document.GetText(start, offset - start).Trim(); |
|
126 |
switch (word) { |
|
127 |
case "ref": |
|
128 |
case "out": |
|
129 |
case "in": |
|
130 |
case "return": |
|
131 |
case "throw": |
|
132 |
case "case": |
|
133 |
goto done; |
|
134 |
} |
|
135 |
||
136 |
if (word.Length > 0 && !IsLetterDigitOrUnderscore(word[0])) { |
|
137 |
goto done; |
|
138 |
} |
|
139 |
offset = start; |
|
140 |
break; |
|
141 |
} |
|
142 |
} |
|
143 |
done: |
|
144 |
//// simple exit fails when : is inside comment line or any other character |
|
145 |
//// we have to check if we got several ids in resulting line, which usually happens when |
|
146 |
//// id. is typed on next line after comment one |
|
147 |
//// Would be better if lexer would parse properly such expressions. However this will cause |
|
148 |
//// modifications in this area too - to get full comment line and remove it afterwards |
|
149 |
if (offset < 0) |
|
150 |
return string.Empty; |
|
151 |
||
152 |
string resText=document.GetText(offset, textArea.Caret.Offset - offset ).Trim(); |
|
153 |
int pos=resText.LastIndexOf('\n'); |
|
154 |
if (pos>=0) { |
|
155 |
offset+=pos+1; |
|
156 |
//// whitespaces and tabs, which might be inside, will be skipped by trim below |
|
157 |
} |
|
158 |
string expression = document.GetText(offset, textArea.Caret.Offset - offset ).Trim(); |
|
159 |
return expression; |
|
160 |
} |
|
161 |
||
162 |
||
163 |
public static CharacterType GetCharacterType(char c) |
|
164 |
{ |
|
165 |
if(IsLetterDigitOrUnderscore(c)) |
|
166 |
return CharacterType.LetterDigitOrUnderscore; |
|
167 |
if(Char.IsWhiteSpace(c)) |
|
168 |
return CharacterType.WhiteSpace; |
|
169 |
return CharacterType.Other; |
|
170 |
} |
|
171 |
||
172 |
public static int GetFirstNonWSChar(IDocument document, int offset) |
|
173 |
{ |
|
174 |
while (offset < document.TextLength && Char.IsWhiteSpace(document.GetCharAt(offset))) { |
|
175 |
++offset; |
|
176 |
} |
|
177 |
return offset; |
|
178 |
} |
|
179 |
||
180 |
public static int FindWordEnd(IDocument document, int offset) |
|
181 |
{ |
|
182 |
LineSegment line = document.GetLineSegmentForOffset(offset); |
|
183 |
int endPos = line.Offset + line.Length; |
|
184 |
while (offset < endPos && IsLetterDigitOrUnderscore(document.GetCharAt(offset))) { |
|
185 |
++offset; |
|
186 |
} |
|
187 |
||
188 |
return offset; |
|
189 |
} |
|
190 |
||
191 |
public static int FindWordStart(IDocument document, int offset) |
|
192 |
{ |
|
193 |
LineSegment line = document.GetLineSegmentForOffset(offset); |
|
194 |
||
195 |
while (offset > line.Offset && !IsLetterDigitOrUnderscore(document.GetCharAt(offset - 1))) { |
|
196 |
--offset; |
|
197 |
} |
|
198 |
||
199 |
return offset; |
|
200 |
} |
|
201 |
||
202 |
// go forward to the start of the next word |
|
203 |
// if the cursor is at the start or in the middle of a word we move to the end of the word |
|
204 |
// and then past any whitespace that follows it |
|
205 |
// if the cursor is at the start or in the middle of some whitespace we move to the start of the |
|
206 |
// next word |
|
207 |
public static int FindNextWordStart(IDocument document, int offset) |
|
208 |
{ |
|
209 |
int originalOffset = offset; |
|
210 |
LineSegment line = document.GetLineSegmentForOffset(offset); |
|
211 |
int endPos = line.Offset + line.Length; |
|
212 |
// lets go to the end of the word, whitespace or operator |
|
213 |
CharacterType t = GetCharacterType(document.GetCharAt(offset)); |
|
214 |
while (offset < endPos && GetCharacterType(document.GetCharAt(offset)) == t) { |
|
215 |
++offset; |
|
216 |
} |
|
217 |
||
218 |
// now we're at the end of the word, lets find the start of the next one by skipping whitespace |
|
219 |
while (offset < endPos && GetCharacterType(document.GetCharAt(offset)) == CharacterType.WhiteSpace) { |
|
220 |
++offset; |
|
221 |
} |
|
222 |
||
223 |
return offset; |
|
224 |
} |
|
225 |
||
226 |
// go back to the start of the word we are on |
|
227 |
// if we are already at the start of a word or if we are in whitespace, then go back |
|
228 |
// to the start of the previous word |
|
229 |
public static int FindPrevWordStart(IDocument document, int offset) |
|
230 |
{ |
|
231 |
int originalOffset = offset; |
|
232 |
if (offset > 0) { |
|
233 |
LineSegment line = document.GetLineSegmentForOffset(offset); |
|
234 |
CharacterType t = GetCharacterType(document.GetCharAt(offset - 1)); |
|
235 |
while (offset > line.Offset && GetCharacterType(document.GetCharAt(offset - 1)) == t) { |
|
236 |
--offset; |
|
237 |
} |
|
238 |
||
239 |
// if we were in whitespace, and now we're at the end of a word or operator, go back to the beginning of it |
|
240 |
if(t == CharacterType.WhiteSpace && offset > line.Offset) { |
|
241 |
t = GetCharacterType(document.GetCharAt(offset - 1)); |
|
242 |
while (offset > line.Offset && GetCharacterType(document.GetCharAt(offset - 1)) == t) { |
|
243 |
--offset; |
|
244 |
} |
|
245 |
} |
|
246 |
} |
|
247 |
||
248 |
return offset; |
|
249 |
} |
|
250 |
||
251 |
public static string GetLineAsString(IDocument document, int lineNumber) |
|
252 |
{ |
|
253 |
LineSegment line = document.GetLineSegment(lineNumber); |
|
254 |
return document.GetText(line.Offset, line.Length); |
|
255 |
} |
|
256 |
||
257 |
public static int SearchBracketBackward(IDocument document, int offset, char openBracket, char closingBracket) |
|
258 |
{ |
|
259 |
return document.FormattingStrategy.SearchBracketBackward(document, offset, openBracket, closingBracket); |
|
260 |
} |
|
261 |
||
262 |
public static int SearchBracketForward(IDocument document, int offset, char openBracket, char closingBracket) |
|
263 |
{ |
|
264 |
return document.FormattingStrategy.SearchBracketForward(document, offset, openBracket, closingBracket); |
|
265 |
} |
|
266 |
||
267 |
/// <remarks> |
|
268 |
/// Returns true, if the line lineNumber is empty or filled with whitespaces. |
|
269 |
/// </remarks> |
|
270 |
public static bool IsEmptyLine(IDocument document, int lineNumber) |
|
271 |
{ |
|
272 |
return IsEmptyLine(document, document.GetLineSegment(lineNumber)); |
|
273 |
} |
|
274 |
||
275 |
/// <remarks> |
|
276 |
/// Returns true, if the line lineNumber is empty or filled with whitespaces. |
|
277 |
/// </remarks> |
|
278 |
public static bool IsEmptyLine(IDocument document, LineSegment line) |
|
279 |
{ |
|
280 |
for (int i = line.Offset; i < line.Offset + line.Length; ++i) { |
|
281 |
char ch = document.GetCharAt(i); |
|
282 |
if (!Char.IsWhiteSpace(ch)) { |
|
283 |
return false; |
|
284 |
} |
|
285 |
} |
|
286 |
return true; |
|
287 |
} |
|
288 |
||
289 |
static bool IsWordPart(char ch) |
|
290 |
{ |
|
291 |
return IsLetterDigitOrUnderscore(ch) || ch == '.'; |
|
292 |
} |
|
293 |
||
294 |
public static string GetWordAt(IDocument document, int offset) |
|
295 |
{ |
|
296 |
if (offset < 0 || offset >= document.TextLength - 1 || !IsWordPart(document.GetCharAt(offset))) { |
|
297 |
return String.Empty; |
|
298 |
} |
|
299 |
int startOffset = offset; |
|
300 |
int endOffset = offset; |
|
301 |
while (startOffset > 0 && IsWordPart(document.GetCharAt(startOffset - 1))) { |
|
302 |
--startOffset; |
|
303 |
} |
|
304 |
||
305 |
while (endOffset < document.TextLength - 1 && IsWordPart(document.GetCharAt(endOffset + 1))) { |
|
306 |
++endOffset; |
|
307 |
} |
|
308 |
||
309 |
Debug.Assert(endOffset >= startOffset); |
|
310 |
return document.GetText(startOffset, endOffset - startOffset + 1); |
|
311 |
} |
|
312 |
} |
|
313 |
} |