PageRenderTime 179ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/IDE/ICSharpCode.TextEditor/Document/LineManager/DefaultLineManager.cs

https://bitbucket.org/AdamMil/boaold
C# | 530 lines | 420 code | 90 blank | 20 comment | 110 complexity | fbfdd086bed94ab2ffed2f6f5e942368 MD5 | raw file
Possible License(s): GPL-2.0
  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 value="$version"/>
  6. // </file>
  7. using System;
  8. using System.Diagnostics;
  9. using System.Collections;
  10. namespace ICSharpCode.TextEditor.Document
  11. {
  12. internal class DefaultLineManager : ILineManager
  13. {
  14. ArrayList lineCollection = new ArrayList();
  15. IDocument document;
  16. IHighlightingStrategy highlightingStrategy;
  17. // keep track of the textlength ourselves
  18. int textLength;
  19. public ArrayList LineSegmentCollection {
  20. get {
  21. return lineCollection;
  22. }
  23. }
  24. public int TotalNumberOfLines {
  25. get {
  26. if (lineCollection.Count == 0) {
  27. return 1;
  28. }
  29. return ((LineSegment)lineCollection[lineCollection.Count - 1]).DelimiterLength > 0 ? lineCollection.Count + 1 : lineCollection.Count;
  30. }
  31. }
  32. public IHighlightingStrategy HighlightingStrategy {
  33. get {
  34. return highlightingStrategy;
  35. }
  36. set {
  37. if (highlightingStrategy != value) {
  38. highlightingStrategy = value;
  39. if (highlightingStrategy != null) {
  40. highlightingStrategy.MarkTokens(document);
  41. }
  42. }
  43. }
  44. }
  45. public DefaultLineManager(IDocument document, IHighlightingStrategy highlightingStrategy)
  46. {
  47. this.document = document;
  48. this.highlightingStrategy = highlightingStrategy;
  49. }
  50. public int GetLineNumberForOffset(int offset)
  51. {
  52. if (offset < 0 || offset > textLength) {
  53. throw new ArgumentOutOfRangeException("offset", offset, "should be between 0 and " + textLength);
  54. }
  55. if (offset == textLength) {
  56. if (lineCollection.Count == 0) {
  57. return 0;
  58. }
  59. LineSegment lastLine = (LineSegment)lineCollection[lineCollection.Count - 1];
  60. return lastLine.DelimiterLength > 0 ? lineCollection.Count : lineCollection.Count - 1;
  61. }
  62. return FindLineNumber(offset);
  63. }
  64. public LineSegment GetLineSegmentForOffset(int offset)
  65. {
  66. if (offset < 0 || offset > textLength) {
  67. throw new ArgumentOutOfRangeException("offset", offset, "should be between 0 and " + textLength);
  68. }
  69. if (offset == textLength) {
  70. if (lineCollection.Count == 0) {
  71. return new LineSegment(0, 0);
  72. }
  73. LineSegment lastLine = (LineSegment)lineCollection[lineCollection.Count - 1];
  74. return lastLine.DelimiterLength > 0 ? new LineSegment(textLength, 0) : lastLine;
  75. }
  76. return GetLineSegment(FindLineNumber(offset));
  77. }
  78. public LineSegment GetLineSegment(int lineNr)
  79. {
  80. if (lineNr < 0 || lineNr > lineCollection.Count) {
  81. throw new ArgumentOutOfRangeException("lineNr", lineNr, "should be between 0 and " + lineCollection.Count);
  82. }
  83. if (lineNr == lineCollection.Count) {
  84. if (lineCollection.Count == 0) {
  85. return new LineSegment(0, 0);
  86. }
  87. LineSegment lastLine = (LineSegment)lineCollection[lineCollection.Count - 1];
  88. return lastLine.DelimiterLength > 0 ? new LineSegment(lastLine.Offset + lastLine.TotalLength, 0) : lastLine;
  89. }
  90. return (LineSegment)lineCollection[lineNr];
  91. }
  92. int Insert(int lineNumber, int offset, string text)
  93. {
  94. if (text == null || text.Length == 0) {
  95. return 0;
  96. }
  97. textLength += text.Length;
  98. if (lineCollection.Count == 0 || lineNumber >= lineCollection.Count) {
  99. return CreateLines(text, lineCollection.Count, offset);
  100. }
  101. LineSegment line = (LineSegment)lineCollection[lineNumber];
  102. ISegment nextDelimiter = NextDelimiter(text, 0);
  103. if (nextDelimiter == null || nextDelimiter.Offset < 0) {
  104. line.TotalLength += text.Length;
  105. // Console.WriteLine("1:Line length of line {0} set to {1}", lineNumber, line.TotalLength - line.DelimiterLength);
  106. markLines.Add(line);
  107. OnLineLengthChanged(new LineLengthEventArgs(document, lineNumber, offset -line.Offset, text.Length));
  108. return 0;
  109. }
  110. int restLength = line.Offset + line.TotalLength - offset;
  111. if (restLength > 0) {
  112. // Console.WriteLine("Insert: Set restline in line {0} to length {1}", lineNumber, restLength);
  113. LineSegment lineRest = new LineSegment(offset, restLength);
  114. lineRest.DelimiterLength = line.DelimiterLength;
  115. lineRest.Offset += text.Length;
  116. markLines.Add(lineRest);
  117. if (restLength - line.DelimiterLength < 0) {
  118. throw new ApplicationException("tried to insert inside delimiter string " + lineRest.ToString() + "!!!");
  119. }
  120. lineCollection.Insert(lineNumber + 1, lineRest);
  121. OnLineCountChanged(new LineManagerEventArgs(document, lineNumber - 1, 1));
  122. }
  123. line.DelimiterLength = nextDelimiter.Length;
  124. int nextStart = offset + nextDelimiter.Offset + nextDelimiter.Length;
  125. line.TotalLength = nextStart - line.Offset;
  126. // Console.WriteLine("2:Line length of line {0} set to {1}", lineNumber, line.TotalLength - line.DelimiterLength);
  127. markLines.Add(line);
  128. text = text.Substring(nextDelimiter.Offset + nextDelimiter.Length);
  129. return CreateLines(text, lineNumber + 1, nextStart) + 1;
  130. }
  131. bool Remove(int lineNumber, int offset, int length)
  132. {
  133. if (length == 0) {
  134. return false;
  135. }
  136. int removedLineEnds = GetNumberOfLines(lineNumber, offset, length) - 1;
  137. LineSegment line = (LineSegment)lineCollection[lineNumber];
  138. if ((lineNumber == lineCollection.Count - 1) && removedLineEnds > 0) {
  139. line.TotalLength -= length;
  140. // Console.WriteLine("3:Line length of line {0} set to {1}", lineNumber, line.TotalLength - line.DelimiterLength);
  141. line.DelimiterLength = 0;
  142. } else {
  143. ++lineNumber;
  144. for (int i = 1; i <= removedLineEnds; ++i) {
  145. if (lineNumber == lineCollection.Count) {
  146. line.DelimiterLength = 0;
  147. break;
  148. }
  149. LineSegment line2 = (LineSegment)lineCollection[lineNumber];
  150. line.TotalLength += line2.TotalLength;
  151. // Console.WriteLine("4:Line length of line {0} set to {1}", lineNumber, line.TotalLength - line.DelimiterLength);
  152. line.DelimiterLength = line2.DelimiterLength;
  153. lineCollection.RemoveAt(lineNumber);
  154. }
  155. line.TotalLength -= length;
  156. // Console.WriteLine("5:Line length of line {0} set to {1}", lineNumber, line.TotalLength - line.DelimiterLength);
  157. if (lineNumber < lineCollection.Count && removedLineEnds > 0) {
  158. markLines.Add(lineCollection[lineNumber]);
  159. }
  160. }
  161. textLength -= length;
  162. if (line.TotalLength == 0) {
  163. lineCollection.Remove(line);
  164. OnLineCountChanged(new LineManagerEventArgs(document, lineNumber, -removedLineEnds));
  165. return true;
  166. }
  167. markLines.Add(line);
  168. OnLineCountChanged(new LineManagerEventArgs(document, lineNumber, -removedLineEnds));
  169. return false;
  170. }
  171. ArrayList markLines = new ArrayList();
  172. public void Insert(int offset, string text)
  173. {
  174. Replace(offset, 0, text);
  175. }
  176. public void Remove(int offset, int length)
  177. {
  178. Replace(offset, length, String.Empty);
  179. }
  180. public void Replace(int offset, int length, string text)
  181. {
  182. int lineNumber = GetLineNumberForOffset(offset);
  183. int insertLineNumber = lineNumber;
  184. if (Remove(lineNumber, offset, length)) {
  185. --lineNumber;
  186. }
  187. lineNumber += Insert(insertLineNumber, offset, text);
  188. int delta = -length;
  189. if (text != null) {
  190. delta = text.Length + delta;
  191. }
  192. if (delta != 0) {
  193. AdaptLineOffsets(lineNumber, delta);
  194. }
  195. RunHighlighter();
  196. }
  197. void RunHighlighter()
  198. {
  199. DateTime time = DateTime.Now;
  200. if (highlightingStrategy != null) {
  201. highlightingStrategy.MarkTokens(document, markLines);
  202. }
  203. markLines.Clear();
  204. }
  205. public void SetContent(string text)
  206. {
  207. lineCollection.Clear();
  208. if (text != null) {
  209. textLength = text.Length;
  210. CreateLines(text, 0, 0);
  211. RunHighlighter();
  212. }
  213. }
  214. void AdaptLineOffsets(int lineNumber, int delta)
  215. {
  216. for (int i = lineNumber + 1; i < lineCollection.Count; ++i) {
  217. ((LineSegment)lineCollection[i]).Offset += delta;
  218. }
  219. }
  220. int GetNumberOfLines(int startLine, int offset, int length)
  221. {
  222. if (length == 0) {
  223. return 1;
  224. }
  225. int target = offset + length;
  226. LineSegment l = (LineSegment)lineCollection[startLine];
  227. if (l.DelimiterLength == 0) {
  228. return 1;
  229. }
  230. if (l.Offset + l.TotalLength > target) {
  231. return 1;
  232. }
  233. if (l.Offset + l.TotalLength == target) {
  234. return 2;
  235. }
  236. return GetLineNumberForOffset(target) - startLine + 1;
  237. }
  238. int FindLineNumber(int offset)
  239. {
  240. if (lineCollection.Count == 0) {
  241. return - 1;
  242. }
  243. int leftIndex = 0;
  244. int rightIndex = lineCollection.Count - 1;
  245. LineSegment curLine = null;
  246. while (leftIndex < rightIndex) {
  247. int pivotIndex = (leftIndex + rightIndex) / 2;
  248. curLine = (LineSegment)lineCollection[pivotIndex];
  249. if (offset < curLine.Offset) {
  250. rightIndex = pivotIndex - 1;
  251. } else if (offset > curLine.Offset) {
  252. leftIndex = pivotIndex + 1;
  253. } else {
  254. leftIndex = pivotIndex;
  255. break;
  256. }
  257. }
  258. return ((LineSegment)lineCollection[leftIndex]).Offset > offset ? leftIndex - 1 : leftIndex;
  259. }
  260. // OLD 'SAFE & TESTED' CreateLines
  261. int CreateLines(string text, int insertPosition, int offset)
  262. {
  263. int count = 0;
  264. int start = 0;
  265. ISegment nextDelimiter = NextDelimiter(text, 0);
  266. while (nextDelimiter != null && nextDelimiter.Offset >= 0) {
  267. int index = nextDelimiter.Offset + (nextDelimiter.Length - 1);
  268. LineSegment newLine = new LineSegment(offset + start, offset + index, nextDelimiter.Length);
  269. markLines.Add(newLine);
  270. if (insertPosition + count >= lineCollection.Count) {
  271. lineCollection.Add(newLine);
  272. } else {
  273. lineCollection.Insert(insertPosition + count, newLine);
  274. }
  275. ++count;
  276. start = index + 1;
  277. nextDelimiter = NextDelimiter(text, start);
  278. }
  279. if (start < text.Length) {
  280. if (insertPosition + count < lineCollection.Count) {
  281. LineSegment l = (LineSegment)lineCollection[insertPosition + count];
  282. int delta = text.Length - start;
  283. l.Offset -= delta;
  284. l.TotalLength += delta;
  285. } else {
  286. LineSegment newLine = new LineSegment(offset + start, text.Length - start);
  287. markLines.Add(newLine);
  288. lineCollection.Add(newLine);
  289. ++count;
  290. }
  291. }
  292. OnLineCountChanged(new LineManagerEventArgs(document, insertPosition, count));
  293. return count;
  294. }
  295. public int GetVisibleLine(int logicalLineNumber)
  296. {
  297. if (!document.TextEditorProperties.EnableFolding) {
  298. return logicalLineNumber;
  299. }
  300. int visibleLine = 0;
  301. int foldEnd = 0;
  302. ArrayList foldings = document.FoldingManager.GetTopLevelFoldedFoldings();
  303. foreach (FoldMarker fm in foldings) {
  304. if (fm.StartLine >= logicalLineNumber) {
  305. break;
  306. }
  307. if (fm.StartLine >= foldEnd) {
  308. visibleLine += fm.StartLine - foldEnd;
  309. if (fm.EndLine > logicalLineNumber) {
  310. return visibleLine;
  311. }
  312. foldEnd = fm.EndLine;
  313. }
  314. }
  315. // Debug.Assert(logicalLineNumber >= foldEnd);
  316. visibleLine += logicalLineNumber - foldEnd;
  317. return visibleLine;
  318. }
  319. public int GetFirstLogicalLine(int visibleLineNumber)
  320. {
  321. if (!document.TextEditorProperties.EnableFolding) {
  322. return visibleLineNumber;
  323. }
  324. int v = 0;
  325. int foldEnd = 0;
  326. ArrayList foldings = document.FoldingManager.GetTopLevelFoldedFoldings();
  327. foreach (FoldMarker fm in foldings) {
  328. if (fm.StartLine >= foldEnd) {
  329. if (v + fm.StartLine - foldEnd >= visibleLineNumber) {
  330. break;
  331. }
  332. v += fm.StartLine - foldEnd;
  333. foldEnd = fm.EndLine;
  334. }
  335. }
  336. // help GC
  337. foldings.Clear();
  338. foldings = null;
  339. return foldEnd + visibleLineNumber - v;
  340. }
  341. public int GetLastLogicalLine(int visibleLineNumber)
  342. {
  343. if (!document.TextEditorProperties.EnableFolding) {
  344. return visibleLineNumber;
  345. }
  346. return GetFirstLogicalLine(visibleLineNumber + 1) - 1;
  347. }
  348. // TODO : speedup the next/prev visible line search
  349. // HOW? : save the foldings in a sorted list and lookup the
  350. // line numbers in this list
  351. public int GetNextVisibleLineAbove(int lineNumber, int lineCount)
  352. {
  353. int curLineNumber = lineNumber;
  354. if (document.TextEditorProperties.EnableFolding) {
  355. for (int i = 0; i < lineCount && curLineNumber < TotalNumberOfLines; ++i) {
  356. ++curLineNumber;
  357. while (curLineNumber < TotalNumberOfLines && (curLineNumber >= lineCollection.Count || !document.FoldingManager.IsLineVisible(curLineNumber))) {
  358. ++curLineNumber;
  359. }
  360. }
  361. } else {
  362. curLineNumber += lineCount;
  363. }
  364. return Math.Min(TotalNumberOfLines - 1, curLineNumber);
  365. }
  366. public int GetNextVisibleLineBelow(int lineNumber, int lineCount)
  367. {
  368. int curLineNumber = lineNumber;
  369. if (document.TextEditorProperties.EnableFolding) {
  370. for (int i = 0; i < lineCount; ++i) {
  371. --curLineNumber;
  372. while (curLineNumber >= 0 && !document.FoldingManager.IsLineVisible(curLineNumber)) {
  373. --curLineNumber;
  374. }
  375. }
  376. } else {
  377. curLineNumber -= lineCount;
  378. }
  379. return Math.Max(0, curLineNumber);
  380. }
  381. protected virtual void OnLineCountChanged(LineManagerEventArgs e)
  382. {
  383. if (LineCountChanged != null) {
  384. LineCountChanged(this, e);
  385. }
  386. }
  387. // use always the same ISegment object for the DelimiterInfo
  388. DelimiterSegment delimiterSegment = new DelimiterSegment();
  389. ISegment NextDelimiter(string text, int offset)
  390. {
  391. for (int i = offset; i < text.Length; i++) {
  392. switch (text[i]) {
  393. case '\r':
  394. if (i + 1 < text.Length) {
  395. if (text[i + 1] == '\n') {
  396. delimiterSegment.Offset = i;
  397. delimiterSegment.Length = 2;
  398. return delimiterSegment;
  399. }
  400. }
  401. goto case '\n';
  402. case '\n':
  403. delimiterSegment.Offset = i;
  404. delimiterSegment.Length = 1;
  405. return delimiterSegment;
  406. }
  407. }
  408. return null;
  409. }
  410. protected virtual void OnLineLengthChanged(LineLengthEventArgs e)
  411. {
  412. if (LineLengthChanged != null) {
  413. LineLengthChanged(this, e);
  414. }
  415. }
  416. public event LineLengthEventHandler LineLengthChanged;
  417. public event LineManagerEventHandler LineCountChanged;
  418. public class DelimiterSegment : ISegment
  419. {
  420. int offset;
  421. int length;
  422. public int Offset {
  423. get {
  424. return offset;
  425. }
  426. set {
  427. offset = value;
  428. }
  429. }
  430. public int Length {
  431. get {
  432. return length;
  433. }
  434. set {
  435. length = value;
  436. }
  437. }
  438. }
  439. }
  440. }