PageRenderTime 50ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/textarea/BufferHandler.java

#
Java | 458 lines | 304 code | 52 blank | 102 comment | 65 complexity | 909a972206c4f3a8e57dbdb5f9cf13b3 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * BufferHandler.java
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2001, 2005 Slava Pestov
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22. package org.gjt.sp.jedit.textarea;
  23. import java.util.Iterator;
  24. import org.gjt.sp.jedit.buffer.*;
  25. import org.gjt.sp.jedit.Debug;
  26. /**
  27. * Note that in this class we take great care to defer complicated
  28. * calculations to the end of the current transaction if the buffer
  29. * informs us a compound edit is in progress
  30. * (<code>isTransactionInProgress()</code>).
  31. *
  32. * This greatly speeds up replace all for example, by only doing certain
  33. * things once, particularly in <code>moveCaretPosition()</code>.
  34. *
  35. * Try doing a replace all in a large file, for example. It is very slow
  36. * in 3.2, faster in 4.0 (where the transaction optimization was
  37. * introduced) and faster still in 4.1 (where it was further improved).
  38. *
  39. * There is still work to do; see TODO.txt.
  40. */
  41. class BufferHandler implements BufferListener
  42. {
  43. private final DisplayManager displayManager;
  44. private final TextArea textArea;
  45. private final JEditBuffer buffer;
  46. private boolean delayedUpdate;
  47. private boolean delayedMultilineUpdate;
  48. private int delayedUpdateStart;
  49. private int delayedUpdateEnd;
  50. //{{{ BufferChangeHandler constructor
  51. BufferHandler(DisplayManager displayManager,
  52. TextArea textArea,
  53. JEditBuffer buffer)
  54. {
  55. this.displayManager = displayManager;
  56. this.textArea = textArea;
  57. this.buffer = buffer;
  58. } //}}}
  59. //{{{ bufferLoaded() method
  60. public void bufferLoaded(JEditBuffer buffer)
  61. {
  62. displayManager.bufferLoaded();
  63. textArea._finishCaretUpdate();
  64. } //}}}
  65. //{{{ foldHandlerChanged() method
  66. public void foldHandlerChanged(JEditBuffer buffer)
  67. {
  68. displayManager.foldHandlerChanged();
  69. } //}}}
  70. //{{{ foldLevelChanged() method
  71. public void foldLevelChanged(JEditBuffer buffer, int start, int end)
  72. {
  73. //System.err.println("foldLevelChanged " + (start-1) + " to " + textArea.getLastPhysicalLine() + "," + end);
  74. if(textArea.getDisplayManager() == displayManager
  75. && end != 0 && !buffer.isLoading())
  76. {
  77. textArea.invalidateLineRange(start - 1,
  78. textArea.getLastPhysicalLine());
  79. }
  80. } //}}}
  81. //{{{ contentInserted() method
  82. public void contentInserted(JEditBuffer buffer, int startLine,
  83. int offset, int numLines, int length)
  84. {
  85. if(buffer.isLoading())
  86. return;
  87. displayManager.screenLineMgr.contentInserted(startLine,numLines);
  88. int endLine = startLine + numLines;
  89. if(numLines != 0)
  90. delayedMultilineUpdate = true;
  91. displayManager.folds.contentInserted(startLine,numLines);
  92. FirstLine firstLine = displayManager.firstLine;
  93. ScrollLineCount scrollLineCount = displayManager.scrollLineCount;
  94. if(textArea.getDisplayManager() == displayManager)
  95. {
  96. if(numLines != 0)
  97. {
  98. firstLine.contentInserted(startLine,numLines);
  99. scrollLineCount.contentInserted(startLine,numLines);
  100. }
  101. if(delayedUpdateEnd >= startLine)
  102. delayedUpdateEnd += numLines;
  103. delayUpdate(startLine,endLine);
  104. //{{{ resize selections if necessary
  105. Iterator<Selection> iter = textArea.getSelectionIterator();
  106. while(iter.hasNext())
  107. {
  108. Selection s = iter.next();
  109. if(s.contentInserted(buffer,startLine,offset,
  110. numLines,length))
  111. {
  112. delayUpdate(s.startLine,s.endLine);
  113. }
  114. } //}}}
  115. int caret = textArea.getCaretPosition();
  116. if(caret >= offset)
  117. {
  118. int scrollMode = textArea.caretAutoScroll()
  119. ? TextArea.ELECTRIC_SCROLL
  120. : TextArea.NO_SCROLL;
  121. textArea.moveCaretPosition(
  122. caret + length,scrollMode);
  123. }
  124. else
  125. {
  126. int scrollMode = textArea.caretAutoScroll()
  127. ? TextArea.NORMAL_SCROLL
  128. : TextArea.NO_SCROLL;
  129. textArea.moveCaretPosition(
  130. caret,scrollMode);
  131. }
  132. }
  133. else
  134. {
  135. firstLine.callReset = true;
  136. scrollLineCount.callReset = true;
  137. }
  138. } //}}}
  139. //{{{ preContentInserted() method
  140. /**
  141. * Called when text is about to be inserted in the buffer.
  142. * @param buffer The buffer in question
  143. * @param startLine The first line
  144. * @param offset The start offset, from the beginning of the buffer
  145. * @param numLines The number of lines inserted
  146. * @param length The number of characters inserted
  147. * @since jEdit 4.3pre11
  148. */
  149. public void preContentInserted(JEditBuffer buffer, int startLine, int offset, int numLines, int length)
  150. {
  151. if(textArea.getDisplayManager() == displayManager)
  152. {
  153. getReadyToBreakFold(startLine);
  154. }
  155. } //}}}
  156. //{{{ preContentRemoved() method
  157. /**
  158. * Called when text is about to be removed from the buffer, but is
  159. * still present.
  160. * @param buffer The buffer in question
  161. * @param startLine The first line
  162. * @param offset The start offset, from the beginning of the buffer
  163. * @param numLines The number of lines to be removed
  164. * @param length The number of characters to be removed
  165. * @since jEdit 4.3pre3
  166. */
  167. public void preContentRemoved(JEditBuffer buffer, int startLine,
  168. int offset, int numLines, int length)
  169. {
  170. if(buffer.isLoading())
  171. return;
  172. FirstLine firstLine = displayManager.firstLine;
  173. ScrollLineCount scrollLineCount = displayManager.scrollLineCount;
  174. if(textArea.getDisplayManager() == displayManager)
  175. {
  176. if(numLines == 0)
  177. {
  178. getReadyToBreakFold(startLine);
  179. }
  180. else
  181. {
  182. int lastLine = startLine + numLines;
  183. if(!displayManager.isLineVisible(startLine)
  184. || !displayManager.isLineVisible(lastLine)
  185. || offset != buffer.getLineStartOffset(startLine)
  186. || offset + length != buffer.getLineStartOffset(lastLine))
  187. {
  188. getReadyToBreakFold(startLine);
  189. getReadyToBreakFold(lastLine);
  190. }
  191. else
  192. {
  193. // The removal will not touch
  194. // inside of folds and wll not
  195. // modify any remaining lines.
  196. }
  197. }
  198. if(numLines != 0)
  199. {
  200. firstLine.preContentRemoved(startLine,offset, numLines);
  201. scrollLineCount.preContentRemoved(startLine, offset, numLines);
  202. }
  203. if(delayedUpdateEnd >= startLine)
  204. delayedUpdateEnd -= numLines;
  205. delayUpdate(startLine,startLine);
  206. }
  207. else
  208. {
  209. firstLine.callReset = true;
  210. scrollLineCount.callReset = true;
  211. }
  212. displayManager.screenLineMgr.contentRemoved(startLine,numLines);
  213. if(numLines == 0)
  214. return;
  215. delayedMultilineUpdate = true;
  216. if(displayManager.folds.preContentRemoved(startLine,numLines))
  217. {
  218. displayManager.folds.reset(buffer.getLineCount());
  219. firstLine.callReset = true;
  220. scrollLineCount.callReset = true;
  221. }
  222. if(firstLine.physicalLine
  223. > displayManager.getLastVisibleLine()
  224. || firstLine.physicalLine
  225. < displayManager.getFirstVisibleLine())
  226. {
  227. // will be handled later.
  228. // see comments at the end of
  229. // transactionComplete().
  230. }
  231. // very subtle... if we leave this for
  232. // ensurePhysicalLineIsVisible(), an
  233. // extra line will be added to the
  234. // scroll line count.
  235. else if(!displayManager.isLineVisible(
  236. firstLine.physicalLine))
  237. {
  238. firstLine.physicalLine =
  239. displayManager.getNextVisibleLine(
  240. firstLine.physicalLine);
  241. }
  242. } //}}}
  243. //{{{ contentRemoved() method
  244. public void contentRemoved(JEditBuffer buffer, int startLine,
  245. int start, int numLines, int length)
  246. {
  247. if(buffer.isLoading())
  248. return;
  249. if(textArea.getDisplayManager() == displayManager)
  250. {
  251. //{{{ resize selections if necessary
  252. int nSel = textArea.getSelectionCount();
  253. Iterator<Selection> iter = textArea.getSelectionIterator();
  254. while(iter.hasNext())
  255. {
  256. Selection s = iter.next();
  257. if(s.contentRemoved(buffer,startLine,
  258. start,numLines,length))
  259. {
  260. delayUpdate(s.startLine,s.endLine);
  261. if(nSel == 1 && s.start == s.end)
  262. iter.remove();
  263. }
  264. } //}}}
  265. int caret = textArea.getCaretPosition();
  266. if(caret >= start + length)
  267. {
  268. int scrollMode = textArea.caretAutoScroll()
  269. ? TextArea.ELECTRIC_SCROLL
  270. : TextArea.NO_SCROLL;
  271. textArea.moveCaretPosition(
  272. caret - length,
  273. scrollMode);
  274. }
  275. else if(caret >= start)
  276. {
  277. int scrollMode = textArea.caretAutoScroll()
  278. ? TextArea.ELECTRIC_SCROLL
  279. : TextArea.NO_SCROLL;
  280. textArea.moveCaretPosition(
  281. start,scrollMode);
  282. }
  283. else
  284. {
  285. int scrollMode = textArea.caretAutoScroll()
  286. ? TextArea.NORMAL_SCROLL
  287. : TextArea.NO_SCROLL;
  288. textArea.moveCaretPosition(caret,scrollMode);
  289. }
  290. }
  291. }
  292. //}}}
  293. //{{{ transactionComplete() method
  294. public void transactionComplete(JEditBuffer buffer)
  295. {
  296. if(textArea.getDisplayManager() != displayManager)
  297. {
  298. delayedUpdate = false;
  299. return;
  300. }
  301. if(delayedUpdate)
  302. doDelayedUpdate();
  303. textArea._finishCaretUpdate();
  304. delayedUpdate = false;
  305. //{{{ Debug code
  306. if(Debug.SCROLL_VERIFY)
  307. {
  308. int line = delayedUpdateStart;
  309. if(!displayManager.isLineVisible(line))
  310. line = displayManager.getNextVisibleLine(line);
  311. System.err.println(delayedUpdateStart + ":" + delayedUpdateEnd + ':' + textArea.getLineCount());
  312. int scrollLineCount = 0;
  313. while(line != -1 && line <= delayedUpdateEnd)
  314. {
  315. scrollLineCount += displayManager.getScreenLineCount(line);
  316. line = displayManager.getNextVisibleLine(line);
  317. }
  318. if(scrollLineCount != displayManager.getScrollLineCount())
  319. {
  320. throw new InternalError(scrollLineCount
  321. + " != "
  322. + displayManager.getScrollLineCount());
  323. }
  324. } //}}}
  325. } //}}}
  326. //{{{ doDelayedUpdate() method
  327. private void doDelayedUpdate()
  328. {
  329. // must update screen line counts before we call
  330. // notifyScreenLineChanges() since that calls
  331. // updateScrollBar() which needs valid info
  332. int line = delayedUpdateStart;
  333. if(!displayManager.isLineVisible(line))
  334. line = displayManager.getNextVisibleLine(line);
  335. while(line != -1 && line <= delayedUpdateEnd)
  336. {
  337. displayManager.updateScreenLineCount(line);
  338. line = displayManager.getNextVisibleLine(line);
  339. }
  340. // must be before the below call
  341. // so that the chunk cache is not
  342. // updated with an invisible first
  343. // line (see above)
  344. displayManager.notifyScreenLineChanges();
  345. if(delayedMultilineUpdate)
  346. {
  347. textArea.invalidateScreenLineRange(
  348. textArea.chunkCache
  349. .getScreenLineOfOffset(
  350. delayedUpdateStart,0),
  351. textArea.getVisibleLines());
  352. delayedMultilineUpdate = false;
  353. }
  354. else
  355. {
  356. textArea.invalidateLineRange(
  357. delayedUpdateStart,
  358. delayedUpdateEnd);
  359. }
  360. // update visible lines
  361. int visibleLines = textArea.getVisibleLines();
  362. if(visibleLines != 0)
  363. {
  364. textArea.chunkCache.getLineInfo(
  365. visibleLines - 1);
  366. }
  367. // force the fold levels to be
  368. // updated.
  369. // when painting the last line of
  370. // a buffer, Buffer.isFoldStart()
  371. // doesn't call getFoldLevel(),
  372. // hence the foldLevelChanged()
  373. // event might not be sent for the
  374. // previous line.
  375. buffer.getFoldLevel(delayedUpdateEnd);
  376. } //}}}
  377. //{{{ delayUpdate() method
  378. private void delayUpdate(int startLine, int endLine)
  379. {
  380. textArea.chunkCache.invalidateChunksFromPhys(startLine);
  381. if(!delayedUpdate)
  382. {
  383. delayedUpdateStart = startLine;
  384. delayedUpdateEnd = endLine;
  385. delayedUpdate = true;
  386. }
  387. else
  388. {
  389. delayedUpdateStart = Math.min(
  390. delayedUpdateStart,
  391. startLine);
  392. delayedUpdateEnd = Math.max(
  393. delayedUpdateEnd,
  394. endLine);
  395. }
  396. } //}}}
  397. //{{{ getReadyToBreakFold() method
  398. // This is a fix for black hole bug.
  399. // If you modify a part of folded lines, like {{{ (followed by }}}),
  400. // the fold is removed so it must be expanded otherwise the text
  401. // remains invisible.
  402. private void getReadyToBreakFold(int line)
  403. {
  404. displayManager.expandFold(line, false);
  405. } //}}}
  406. }