PageRenderTime 60ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/buffer/JEditBuffer.java

#
Java | 2863 lines | 1719 code | 333 blank | 811 comment | 279 complexity | 11216d492b980f13a8d1b081d0cdd7b6 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. * JEditBuffer.java - jEdit buffer
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1998, 2005 Slava Pestov
  7. * Portions copyright (C) 1999, 2000 mike dillon
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  22. */
  23. package org.gjt.sp.jedit.buffer;
  24. //{{{ Imports
  25. import org.gjt.sp.jedit.Debug;
  26. import org.gjt.sp.jedit.Mode;
  27. import org.gjt.sp.jedit.TextUtilities;
  28. import org.gjt.sp.jedit.indent.IndentAction;
  29. import org.gjt.sp.jedit.indent.IndentRule;
  30. import org.gjt.sp.jedit.syntax.*;
  31. import org.gjt.sp.jedit.textarea.ColumnBlock;
  32. import org.gjt.sp.jedit.textarea.ColumnBlockLine;
  33. import org.gjt.sp.jedit.textarea.Node;
  34. import org.gjt.sp.jedit.textarea.TextArea;
  35. import org.gjt.sp.util.IntegerArray;
  36. import org.gjt.sp.util.Log;
  37. import org.gjt.sp.util.StandardUtilities;
  38. import javax.swing.text.Position;
  39. import javax.swing.text.Segment;
  40. import java.awt.*;
  41. import java.util.*;
  42. import java.util.List;
  43. import java.util.concurrent.locks.ReentrantReadWriteLock;
  44. import java.util.regex.Pattern;
  45. //}}}
  46. /**
  47. * A <code>JEditBuffer</code> represents the contents of an open text
  48. * file as it is maintained in the computer's memory (as opposed to
  49. * how it may be stored on a disk).<p>
  50. *
  51. * This class is partially thread-safe, however you must pay attention to two
  52. * very important guidelines:
  53. * <ul>
  54. * <li>Operations such as insert() and remove(),
  55. * undo(), change Buffer data in a writeLock(), and must
  56. * be called from the AWT thread.
  57. * <li>When accessing the buffer from another thread, you must
  58. * call readLock() before and readUnLock() after, if you plan on performing
  59. * more than one read, to ensure that the buffer contents are not changed by
  60. * the AWT thread for the duration of the lock. Only methods whose descriptions
  61. * specify thread safety can be invoked from other threads.
  62. * </ul>
  63. *
  64. * @author Slava Pestov
  65. * @version $Id: JEditBuffer.java 20108 2011-10-18 12:16:38Z evanpw $
  66. *
  67. * @since jEdit 4.3pre3
  68. */
  69. public class JEditBuffer
  70. {
  71. /**
  72. * Line separator property.
  73. */
  74. public static final String LINESEP = "lineSeparator";
  75. /**
  76. * Character encoding used when loading and saving.
  77. * @since jEdit 3.2pre4
  78. */
  79. public static final String ENCODING = "encoding";
  80. //{{{ JEditBuffer constructors
  81. public JEditBuffer(Map props)
  82. {
  83. bufferListeners = new Vector<Listener>();
  84. lock = new ReentrantReadWriteLock();
  85. contentMgr = new ContentManager();
  86. lineMgr = new LineManager();
  87. positionMgr = new PositionManager(this);
  88. undoMgr = new UndoManager(this);
  89. integerArray = new IntegerArray();
  90. propertyLock = new Object();
  91. properties = new HashMap<Object, PropValue>();
  92. //{{{ need to convert entries of 'props' to PropValue instances
  93. Set<Map.Entry> set = props.entrySet();
  94. for (Map.Entry entry : set)
  95. {
  96. properties.put(entry.getKey(),new PropValue(entry.getValue(),false));
  97. } //}}}
  98. // fill in defaults for these from system properties if the
  99. // corresponding buffer.XXX properties not set
  100. if(getProperty(ENCODING) == null)
  101. properties.put(ENCODING,new PropValue(System.getProperty("file.encoding"),false));
  102. if(getProperty(LINESEP) == null)
  103. properties.put(LINESEP,new PropValue(System.getProperty("line.separator"),false));
  104. }
  105. /**
  106. * Create a new JEditBuffer.
  107. * It is used by independent textarea only
  108. */
  109. public JEditBuffer()
  110. {
  111. bufferListeners = new Vector<Listener>();
  112. lock = new ReentrantReadWriteLock();
  113. contentMgr = new ContentManager();
  114. lineMgr = new LineManager();
  115. positionMgr = new PositionManager(this);
  116. undoMgr = new UndoManager(this);
  117. integerArray = new IntegerArray();
  118. propertyLock = new Object();
  119. properties = new HashMap<Object, PropValue>();
  120. properties.put("wrap",new PropValue("none",false));
  121. properties.put("folding",new PropValue("none",false));
  122. tokenMarker = new TokenMarker();
  123. tokenMarker.addRuleSet(new ParserRuleSet("text","MAIN"));
  124. setTokenMarker(tokenMarker);
  125. loadText(null,null);
  126. // corresponding buffer.XXX properties not set
  127. if(getProperty(ENCODING) == null)
  128. properties.put(ENCODING,new PropValue(System.getProperty("file.encoding"),false));
  129. if(getProperty(LINESEP) == null)
  130. properties.put(LINESEP,new PropValue(System.getProperty("line.separator"),false));
  131. setFoldHandler(new DummyFoldHandler());
  132. } //}}}
  133. //{{{ Flags
  134. //{{{ isDirty() method
  135. /**
  136. * Returns whether there have been unsaved changes to this buffer.
  137. * This method is thread-safe.
  138. */
  139. public boolean isDirty()
  140. {
  141. return dirty;
  142. } //}}}
  143. //{{{ isLoading() method
  144. public boolean isLoading()
  145. {
  146. return loading;
  147. } //}}}
  148. //{{{ setLoading() method
  149. public void setLoading(boolean loading)
  150. {
  151. this.loading = loading;
  152. } //}}}
  153. //{{{ isPerformingIO() method
  154. /**
  155. * Returns true if the buffer is currently performing I/O.
  156. * This method is thread-safe.
  157. * @since jEdit 2.7pre1
  158. */
  159. public boolean isPerformingIO()
  160. {
  161. return isLoading() || io;
  162. } //}}}
  163. //{{{ setPerformingIO() method
  164. /**
  165. * Returns true if the buffer is currently performing I/O.
  166. * This method is thread-safe.
  167. * @since jEdit 2.7pre1
  168. */
  169. public void setPerformingIO(boolean io)
  170. {
  171. this.io = io;
  172. } //}}}
  173. //{{{ isEditable() method
  174. /**
  175. * Returns true if this file is editable, false otherwise. A file may
  176. * become uneditable if it is read only, or if I/O is in progress.
  177. * This method is thread-safe.
  178. * @since jEdit 2.7pre1
  179. */
  180. public boolean isEditable()
  181. {
  182. return !(isReadOnly() || isPerformingIO());
  183. } //}}}
  184. //{{{ isReadOnly() method
  185. /**
  186. * Returns true if this file is read only, false otherwise.
  187. * This method is thread-safe.
  188. */
  189. public boolean isReadOnly()
  190. {
  191. return readOnly || readOnlyOverride;
  192. } //}}}
  193. //{{{ setReadOnly() method
  194. /**
  195. * Sets the read only flag.
  196. * @param readOnly The read only flag
  197. */
  198. public void setReadOnly(boolean readOnly)
  199. {
  200. readOnlyOverride = readOnly;
  201. } //}}}
  202. //{{{ setDirty() method
  203. /**
  204. * Sets the 'dirty' (changed since last save) flag of this buffer.
  205. */
  206. public void setDirty(boolean d)
  207. {
  208. boolean editable = isEditable();
  209. if(d)
  210. {
  211. if(editable)
  212. dirty = true;
  213. }
  214. else
  215. {
  216. dirty = false;
  217. // fixes dirty flag not being reset on
  218. // save/insert/undo/redo/undo
  219. if(!isUndoInProgress())
  220. {
  221. // this ensures that undo can clear the dirty flag properly
  222. // when all edits up to a save are undone
  223. undoMgr.resetClearDirty();
  224. }
  225. }
  226. } //}}}
  227. //}}}
  228. //{{{ Thread safety
  229. //{{{ readLock() method
  230. /**
  231. * The buffer is guaranteed not to change between calls to
  232. * {@link #readLock()} and {@link #readUnlock()}.
  233. */
  234. public void readLock()
  235. {
  236. lock.readLock().lock();
  237. } //}}}
  238. //{{{ readUnlock() method
  239. /**
  240. * The buffer is guaranteed not to change between calls to
  241. * {@link #readLock()} and {@link #readUnlock()}.
  242. */
  243. public void readUnlock()
  244. {
  245. lock.readLock().unlock();
  246. } //}}}
  247. //{{{ writeLock() method
  248. /**
  249. * Attempting to obtain read lock will block between calls to
  250. * {@link #writeLock()} and {@link #writeUnlock()}.
  251. */
  252. public void writeLock()
  253. {
  254. lock.writeLock().lock();
  255. } //}}}
  256. //{{{ writeUnlock() method
  257. /**
  258. * Attempting to obtain read lock will block between calls to
  259. * {@link #writeLock()} and {@link #writeUnlock()}.
  260. */
  261. public void writeUnlock()
  262. {
  263. lock.writeLock().unlock();
  264. } //}}}
  265. //}}}
  266. //{{{ Line offset methods
  267. //{{{ getLength() method
  268. /**
  269. * Returns the number of characters in the buffer. This method is thread-safe.
  270. */
  271. public int getLength()
  272. {
  273. // no need to lock since this just returns a value and that's it
  274. return contentMgr.getLength();
  275. } //}}}
  276. //{{{ getLineCount() method
  277. /**
  278. * Returns the number of physical lines in the buffer.
  279. * This method is thread-safe.
  280. * @since jEdit 3.1pre1
  281. */
  282. public int getLineCount()
  283. {
  284. // no need to lock since this just returns a value and that's it
  285. return lineMgr.getLineCount();
  286. } //}}}
  287. //{{{ getLineOfOffset() method
  288. /**
  289. * Returns the line containing the specified offset.
  290. * This method is thread-safe.
  291. * @param offset The offset
  292. * @since jEdit 4.0pre1
  293. */
  294. public int getLineOfOffset(int offset)
  295. {
  296. try
  297. {
  298. readLock();
  299. if(offset < 0 || offset > getLength())
  300. throw new ArrayIndexOutOfBoundsException(offset);
  301. return lineMgr.getLineOfOffset(offset);
  302. }
  303. finally
  304. {
  305. readUnlock();
  306. }
  307. } //}}}
  308. //{{{ getLineStartOffset() method
  309. /**
  310. * Returns the start offset of the specified line.
  311. * This method is thread-safe.
  312. * @param line The line
  313. * @return The start offset of the specified line
  314. * @since jEdit 4.0pre1
  315. */
  316. public int getLineStartOffset(int line)
  317. {
  318. try
  319. {
  320. readLock();
  321. if(line < 0 || line >= lineMgr.getLineCount())
  322. throw new ArrayIndexOutOfBoundsException(line);
  323. else if(line == 0)
  324. return 0;
  325. return lineMgr.getLineEndOffset(line - 1);
  326. }
  327. finally
  328. {
  329. readUnlock();
  330. }
  331. } //}}}
  332. //{{{ getLineEndOffset() method
  333. /**
  334. * Returns the end offset of the specified line.
  335. * This method is thread-safe.
  336. * @param line The line
  337. * @return The end offset of the specified line
  338. * invalid.
  339. * @since jEdit 4.0pre1
  340. */
  341. public int getLineEndOffset(int line)
  342. {
  343. try
  344. {
  345. readLock();
  346. if(line < 0 || line >= lineMgr.getLineCount())
  347. throw new ArrayIndexOutOfBoundsException(line);
  348. return lineMgr.getLineEndOffset(line);
  349. }
  350. finally
  351. {
  352. readUnlock();
  353. }
  354. } //}}}
  355. //{{{ getLineLength() method
  356. /**
  357. * Returns the length of the specified line.
  358. * This method is thread-safe.
  359. * @param line The line
  360. * @since jEdit 4.0pre1
  361. */
  362. public int getLineLength(int line)
  363. {
  364. try
  365. {
  366. readLock();
  367. return getLineEndOffset(line)
  368. - getLineStartOffset(line) - 1;
  369. }
  370. finally
  371. {
  372. readUnlock();
  373. }
  374. } //}}}
  375. //{{{ getPriorNonEmptyLine() method
  376. /**
  377. * Auto indent needs this.
  378. */
  379. public int getPriorNonEmptyLine(int lineIndex)
  380. {
  381. if (!mode.getIgnoreWhitespace())
  382. {
  383. return lineIndex - 1;
  384. }
  385. int returnValue = -1;
  386. for(int i = lineIndex - 1; i >= 0; i--)
  387. {
  388. Segment seg = new Segment();
  389. getLineText(i,seg);
  390. if(seg.count != 0)
  391. returnValue = i;
  392. for(int j = 0; j < seg.count; j++)
  393. {
  394. char ch = seg.array[seg.offset + j];
  395. if(!Character.isWhitespace(ch))
  396. return i;
  397. }
  398. }
  399. // didn't find a line that contains non-whitespace chars
  400. // so return index of prior whitespace line
  401. return returnValue;
  402. } //}}}
  403. //}}}
  404. //{{{ Text getters and setters
  405. //{{{ getLineText() methods
  406. /**
  407. * Returns the text on the specified line.
  408. * This method is thread-safe.
  409. * @param line The line
  410. * @return The text, or null if the line is invalid
  411. * @since jEdit 4.0pre1
  412. */
  413. public String getLineText(int line)
  414. {
  415. if(line < 0 || line >= lineMgr.getLineCount())
  416. throw new ArrayIndexOutOfBoundsException(line);
  417. try
  418. {
  419. readLock();
  420. int start = line == 0 ? 0 : lineMgr.getLineEndOffset(line - 1);
  421. int end = lineMgr.getLineEndOffset(line);
  422. return getText(start,end - start - 1);
  423. }
  424. finally
  425. {
  426. readUnlock();
  427. }
  428. }
  429. /**
  430. * Returns the specified line in a <code>Segment</code>.<p>
  431. *
  432. * Using a <classname>Segment</classname> is generally more
  433. * efficient than using a <classname>String</classname> because it
  434. * results in less memory allocation and array copying.<p>
  435. *
  436. * This method is thread-safe.
  437. *
  438. * @param line The line
  439. * @since jEdit 4.0pre1
  440. */
  441. public void getLineText(int line, Segment segment)
  442. {
  443. getLineText(line, 0, segment);
  444. }
  445. /**
  446. * Returns the specified line from the starting point passed in relativeStartOffset in a <code>Segment</code>.<p>
  447. *
  448. * Using a <classname>Segment</classname> is generally more
  449. * efficient than using a <classname>String</classname> because it
  450. * results in less memory allocation and array copying.<p>
  451. *
  452. * This method is thread-safe.
  453. *
  454. * @param line The line
  455. * @since jEdit 4.0pre1
  456. */
  457. public void getLineText(int line,int relativeStartOffset, Segment segment)
  458. {
  459. if(line < 0 || line >= lineMgr.getLineCount())
  460. throw new ArrayIndexOutOfBoundsException(line);
  461. try
  462. {
  463. readLock();
  464. int start = (line == 0 ? 0 : lineMgr.getLineEndOffset(line - 1));
  465. int end = lineMgr.getLineEndOffset(line);
  466. if((start+relativeStartOffset)>end)
  467. {
  468. throw new IllegalArgumentException("This index is outside the line length (start+relativeOffset):"+start+" + "+relativeStartOffset+" > "+"endffset:"+end);
  469. }
  470. else
  471. {
  472. getText(start+relativeStartOffset,end - start -relativeStartOffset- 1,segment);
  473. }
  474. }
  475. finally
  476. {
  477. readUnlock();
  478. }
  479. } //}}}
  480. //{{{ getLineSegment() method
  481. /**
  482. * Returns the text on the specified line.
  483. * This method is thread-safe.
  484. *
  485. * @param line The line index.
  486. * @return The text, or null if the line is invalid
  487. *
  488. * @since jEdit 4.3pre15
  489. */
  490. public CharSequence getLineSegment(int line)
  491. {
  492. if(line < 0 || line >= lineMgr.getLineCount())
  493. throw new ArrayIndexOutOfBoundsException(line);
  494. try
  495. {
  496. readLock();
  497. int start = line == 0 ? 0 : lineMgr.getLineEndOffset(line - 1);
  498. int end = lineMgr.getLineEndOffset(line);
  499. return getSegment(start,end - start - 1);
  500. }
  501. finally
  502. {
  503. readUnlock();
  504. }
  505. } //}}}
  506. //{{{ getText() methods
  507. /**
  508. * Returns the specified text range. This method is thread-safe.
  509. * @param start The start offset
  510. * @param length The number of characters to get
  511. */
  512. public String getText(int start, int length)
  513. {
  514. try
  515. {
  516. readLock();
  517. if(start < 0 || length < 0
  518. || start + length > contentMgr.getLength())
  519. throw new ArrayIndexOutOfBoundsException(start + ":" + length);
  520. return contentMgr.getText(start,length);
  521. }
  522. finally
  523. {
  524. readUnlock();
  525. }
  526. }
  527. /**
  528. * Returns the full buffer content. This method is thread-safe
  529. * @since 4.4.1
  530. */
  531. public String getText()
  532. {
  533. try
  534. {
  535. readLock();
  536. return contentMgr.getText(0, getLength());
  537. }
  538. finally
  539. {
  540. readUnlock();
  541. }
  542. }
  543. /**
  544. * Returns the specified text range in a <code>Segment</code>.<p>
  545. *
  546. * Using a <classname>Segment</classname> is generally more
  547. * efficient than using a <classname>String</classname> because it
  548. * results in less memory allocation and array copying.<p>
  549. *
  550. * This method is thread-safe.
  551. *
  552. * @param start The start offset
  553. * @param length The number of characters to get
  554. * @param seg The segment to copy the text to
  555. */
  556. public void getText(int start, int length, Segment seg)
  557. {
  558. try
  559. {
  560. readLock();
  561. if(start < 0 || length < 0
  562. || start + length > contentMgr.getLength())
  563. throw new ArrayIndexOutOfBoundsException(start + ":" + length);
  564. contentMgr.getText(start,length,seg);
  565. }
  566. finally
  567. {
  568. readUnlock();
  569. }
  570. } //}}}
  571. //{{{ getSegment() method
  572. /**
  573. * Returns the specified text range. This method is thread-safe.
  574. * It doesn't copy the text
  575. *
  576. * @param start The start offset
  577. * @param length The number of characters to get
  578. *
  579. * @return a CharSequence that contains the text wanted text
  580. * @since jEdit 4.3pre15
  581. */
  582. public CharSequence getSegment(int start, int length)
  583. {
  584. try
  585. {
  586. readLock();
  587. if(start < 0 || length < 0
  588. || start + length > contentMgr.getLength())
  589. throw new ArrayIndexOutOfBoundsException(start + ":" + length);
  590. return contentMgr.getSegment(start,length);
  591. }
  592. finally
  593. {
  594. readUnlock();
  595. }
  596. } //}}}
  597. //{{{ insert() methods
  598. /**
  599. * Inserts a string into the buffer.
  600. * @param offset The offset
  601. * @param str The string
  602. * @since jEdit 4.0pre1
  603. */
  604. public void insert(int offset, String str)
  605. {
  606. if(str == null)
  607. return;
  608. int len = str.length();
  609. if(len == 0)
  610. return;
  611. if(isReadOnly())
  612. throw new RuntimeException("buffer read-only");
  613. try
  614. {
  615. writeLock();
  616. if(offset < 0 || offset > contentMgr.getLength())
  617. throw new ArrayIndexOutOfBoundsException(offset);
  618. contentMgr.insert(offset,str);
  619. integerArray.clear();
  620. for(int i = 0; i < len; i++)
  621. {
  622. if(str.charAt(i) == '\n')
  623. integerArray.add(i + 1);
  624. }
  625. if(!undoInProgress)
  626. {
  627. undoMgr.contentInserted(offset,len,str,!dirty);
  628. }
  629. contentInserted(offset,len,integerArray);
  630. }
  631. finally
  632. {
  633. writeUnlock();
  634. }
  635. }
  636. /**
  637. * Inserts a string into the buffer.
  638. * @param offset The offset
  639. * @param seg The segment
  640. * @since jEdit 4.0pre1
  641. */
  642. public void insert(int offset, Segment seg)
  643. {
  644. if(seg.count == 0)
  645. return;
  646. if(isReadOnly())
  647. throw new RuntimeException("buffer read-only");
  648. try
  649. {
  650. writeLock();
  651. if(offset < 0 || offset > contentMgr.getLength())
  652. throw new ArrayIndexOutOfBoundsException(offset);
  653. contentMgr.insert(offset,seg);
  654. integerArray.clear();
  655. for(int i = 0; i < seg.count; i++)
  656. {
  657. if(seg.array[seg.offset + i] == '\n')
  658. integerArray.add(i + 1);
  659. }
  660. if(!undoInProgress)
  661. {
  662. undoMgr.contentInserted(offset,seg.count,
  663. seg.toString(),!dirty);
  664. }
  665. contentInserted(offset,seg.count,integerArray);
  666. }
  667. finally
  668. {
  669. writeUnlock();
  670. }
  671. } //}}}
  672. //{{{ remove() method
  673. /**
  674. * Removes the specified rang efrom the buffer.
  675. * @param offset The start offset
  676. * @param length The number of characters to remove
  677. */
  678. public void remove(int offset, int length)
  679. {
  680. if(length == 0)
  681. return;
  682. if(isReadOnly())
  683. throw new RuntimeException("buffer read-only");
  684. try
  685. {
  686. transaction = true;
  687. writeLock();
  688. if(offset < 0 || length < 0
  689. || offset + length > contentMgr.getLength())
  690. throw new ArrayIndexOutOfBoundsException(offset + ":" + length);
  691. int startLine = lineMgr.getLineOfOffset(offset);
  692. int endLine = lineMgr.getLineOfOffset(offset + length);
  693. int numLines = endLine - startLine;
  694. if(!undoInProgress && !loading)
  695. {
  696. undoMgr.contentRemoved(offset,length,
  697. getText(offset,length),
  698. !dirty);
  699. }
  700. firePreContentRemoved(startLine,offset,numLines,length);
  701. contentMgr.remove(offset,length);
  702. lineMgr.contentRemoved(startLine,offset,numLines,length);
  703. positionMgr.contentRemoved(offset,length);
  704. setDirty(true);
  705. fireContentRemoved(startLine,offset,numLines,length);
  706. /* otherwise it will be delivered later */
  707. if(!undoInProgress && !insideCompoundEdit())
  708. fireTransactionComplete();
  709. }
  710. finally
  711. {
  712. transaction = false;
  713. writeUnlock();
  714. }
  715. } //}}}
  716. //}}}
  717. //{{{ Indentation
  718. //{{{ removeTrailingWhiteSpace() method
  719. /**
  720. * Removes trailing whitespace from all lines in the specified list.
  721. * @param lines The line numbers
  722. * @since jEdit 3.2pre1
  723. */
  724. public void removeTrailingWhiteSpace(int[] lines)
  725. {
  726. try
  727. {
  728. beginCompoundEdit();
  729. for(int i = 0; i < lines.length; i++)
  730. {
  731. Segment seg = new Segment();
  732. getLineText(lines[i],seg);
  733. // blank line
  734. if (seg.count == 0) continue;
  735. int lineStart = seg.offset;
  736. int lineEnd = seg.offset + seg.count - 1;
  737. int pos;
  738. for (pos = lineEnd; pos >= lineStart; pos--)
  739. {
  740. if (!Character.isWhitespace(seg.array[pos]))
  741. break;
  742. }
  743. int tail = lineEnd - pos;
  744. // no whitespace
  745. if (tail == 0) continue;
  746. remove(getLineEndOffset(lines[i]) - 1 - tail,tail);
  747. }
  748. }
  749. finally
  750. {
  751. endCompoundEdit();
  752. }
  753. } //}}}
  754. //{{{ shiftIndentLeft() method
  755. /**
  756. * Shifts the indent of each line in the specified list to the left.
  757. * @param lines The line numbers
  758. * @since jEdit 3.2pre1
  759. */
  760. public void shiftIndentLeft(int[] lines)
  761. {
  762. int tabSize = getTabSize();
  763. int indentSize = getIndentSize();
  764. boolean noTabs = getBooleanProperty("noTabs");
  765. try
  766. {
  767. beginCompoundEdit();
  768. for(int i = 0; i < lines.length; i++)
  769. {
  770. int lineStart = getLineStartOffset(lines[i]);
  771. CharSequence line = getLineSegment(lines[i]);
  772. int whiteSpace = StandardUtilities
  773. .getLeadingWhiteSpace(line);
  774. if(whiteSpace == 0)
  775. continue;
  776. int whiteSpaceWidth = Math.max(0,StandardUtilities
  777. .getLeadingWhiteSpaceWidth(line,tabSize)
  778. - indentSize);
  779. insert(lineStart + whiteSpace,StandardUtilities
  780. .createWhiteSpace(whiteSpaceWidth,
  781. noTabs ? 0 : tabSize));
  782. remove(lineStart,whiteSpace);
  783. }
  784. }
  785. finally
  786. {
  787. endCompoundEdit();
  788. }
  789. } //}}}
  790. //{{{ shiftIndentRight() method
  791. /**
  792. * Shifts the indent of each line in the specified list to the right.
  793. * @param lines The line numbers
  794. * @since jEdit 3.2pre1
  795. */
  796. public void shiftIndentRight(int[] lines)
  797. {
  798. try
  799. {
  800. beginCompoundEdit();
  801. int tabSize = getTabSize();
  802. int indentSize = getIndentSize();
  803. boolean noTabs = getBooleanProperty("noTabs");
  804. for(int i = 0; i < lines.length; i++)
  805. {
  806. int lineStart = getLineStartOffset(lines[i]);
  807. CharSequence line = getLineSegment(lines[i]);
  808. int whiteSpace = StandardUtilities
  809. .getLeadingWhiteSpace(line);
  810. // silly usability hack
  811. //if(lines.length != 1 && whiteSpace == 0)
  812. // continue;
  813. int whiteSpaceWidth = StandardUtilities
  814. .getLeadingWhiteSpaceWidth(
  815. line,tabSize) + indentSize;
  816. insert(lineStart + whiteSpace,StandardUtilities
  817. .createWhiteSpace(whiteSpaceWidth,
  818. noTabs ? 0 : tabSize));
  819. remove(lineStart,whiteSpace);
  820. }
  821. }
  822. finally
  823. {
  824. endCompoundEdit();
  825. }
  826. } //}}}
  827. //{{{ indentLines() methods
  828. /**
  829. * Indents all specified lines.
  830. * @param start The first line to indent
  831. * @param end The last line to indent
  832. * @since jEdit 3.1pre3
  833. */
  834. public void indentLines(int start, int end)
  835. {
  836. try
  837. {
  838. beginCompoundEdit();
  839. for(int i = start; i <= end; i++)
  840. indentLine(i,true);
  841. }
  842. finally
  843. {
  844. endCompoundEdit();
  845. }
  846. }
  847. /**
  848. * Indents all specified lines.
  849. * @param lines The line numbers
  850. * @since jEdit 3.2pre1
  851. */
  852. public void indentLines(int[] lines)
  853. {
  854. try
  855. {
  856. beginCompoundEdit();
  857. for(int i = 0; i < lines.length; i++)
  858. indentLine(lines[i],true);
  859. }
  860. finally
  861. {
  862. endCompoundEdit();
  863. }
  864. } //}}}
  865. //{{{ indentLine() methods
  866. /**
  867. * Indents the specified line.
  868. * @param lineIndex The line number to indent
  869. * @param canDecreaseIndent If true, the indent can be decreased as a
  870. * result of this. Set this to false for Tab key.
  871. * @return true If indentation took place, false otherwise.
  872. * @since jEdit 4.2pre2
  873. */
  874. public boolean indentLine(int lineIndex, boolean canDecreaseIndent)
  875. {
  876. int[] whitespaceChars = new int[1];
  877. int currentIndent = getCurrentIndentForLine(lineIndex,
  878. whitespaceChars);
  879. int prevLineIndex = getPriorNonEmptyLine(lineIndex);
  880. int prevLineIndent = (prevLineIndex == -1) ? 0 :
  881. StandardUtilities.getLeadingWhiteSpaceWidth(getLineSegment(
  882. prevLineIndex), getTabSize());
  883. int idealIndent = getIdealIndentForLine(lineIndex, prevLineIndex,
  884. prevLineIndent);
  885. if (idealIndent == -1 || idealIndent == currentIndent ||
  886. (!canDecreaseIndent && idealIndent < currentIndent))
  887. return false;
  888. // Do it
  889. try
  890. {
  891. beginCompoundEdit();
  892. int start = getLineStartOffset(lineIndex);
  893. remove(start,whitespaceChars[0]);
  894. String prevIndentString = (prevLineIndex >= 0) ?
  895. StandardUtilities.getIndentString(getLineText(
  896. prevLineIndex)) : null;
  897. String indentString;
  898. if (prevIndentString == null)
  899. {
  900. indentString = StandardUtilities.createWhiteSpace(
  901. idealIndent,
  902. getBooleanProperty("noTabs") ? 0 : getTabSize());
  903. }
  904. else if (idealIndent == prevLineIndent)
  905. indentString = prevIndentString;
  906. else if (idealIndent < prevLineIndent)
  907. indentString = StandardUtilities.truncateWhiteSpace(
  908. idealIndent, getTabSize(), prevIndentString);
  909. else
  910. indentString = prevIndentString +
  911. StandardUtilities.createWhiteSpace(
  912. idealIndent - prevLineIndent,
  913. getBooleanProperty("noTabs") ? 0 : getTabSize(),
  914. prevLineIndent);
  915. insert(start, indentString);
  916. }
  917. finally
  918. {
  919. endCompoundEdit();
  920. }
  921. return true;
  922. } //}}}
  923. //{{{ getCurrentIndentForLine() method
  924. /**
  925. * Returns the line's current leading indent.
  926. * @param lineIndex The line number
  927. * @param whitespaceChars If this is non-null, the number of whitespace
  928. * characters is stored at the 0 index
  929. * @since jEdit 4.2pre2
  930. */
  931. public int getCurrentIndentForLine(int lineIndex, int[] whitespaceChars)
  932. {
  933. Segment seg = new Segment();
  934. getLineText(lineIndex,seg);
  935. int tabSize = getTabSize();
  936. int currentIndent = 0;
  937. loop: for(int i = 0; i < seg.count; i++)
  938. {
  939. char c = seg.array[seg.offset + i];
  940. switch(c)
  941. {
  942. case ' ':
  943. currentIndent++;
  944. if(whitespaceChars != null)
  945. whitespaceChars[0]++;
  946. break;
  947. case '\t':
  948. currentIndent += tabSize - (currentIndent
  949. % tabSize);
  950. if(whitespaceChars != null)
  951. whitespaceChars[0]++;
  952. break;
  953. default:
  954. break loop;
  955. }
  956. }
  957. return currentIndent;
  958. } //}}}
  959. //{{{ getIdealIndentForLine() method
  960. /**
  961. * Returns the ideal leading indent for the specified line.
  962. * This will apply the various auto-indent rules.
  963. * @param lineIndex The line number
  964. */
  965. public int getIdealIndentForLine(int lineIndex)
  966. {
  967. int prevLineIndex = getPriorNonEmptyLine(lineIndex);
  968. int oldIndent = prevLineIndex == -1 ? 0 :
  969. StandardUtilities.getLeadingWhiteSpaceWidth(
  970. getLineSegment(prevLineIndex),
  971. getTabSize());
  972. return getIdealIndentForLine(lineIndex, prevLineIndex,
  973. oldIndent);
  974. } //}}}
  975. //{{{ getIdealIndentForLine() method
  976. /**
  977. * Returns the ideal leading indent for the specified line.
  978. * This will apply the various auto-indent rules.
  979. * @param lineIndex The line number
  980. * @param prevLineIndex The index of the previous non-empty line
  981. * @param oldIndent The indent width of the previous line (or 0)
  982. */
  983. private int getIdealIndentForLine(int lineIndex, int prevLineIndex,
  984. int oldIndent)
  985. {
  986. int prevPrevLineIndex = prevLineIndex < 0 ? -1
  987. : getPriorNonEmptyLine(prevLineIndex);
  988. int newIndent = oldIndent;
  989. List<IndentRule> indentRules = getIndentRules(lineIndex);
  990. List<IndentAction> actions = new LinkedList<IndentAction>();
  991. for (int i = 0;i<indentRules.size();i++)
  992. {
  993. IndentRule rule = indentRules.get(i);
  994. rule.apply(this,lineIndex,prevLineIndex,
  995. prevPrevLineIndex,actions);
  996. }
  997. for (IndentAction action : actions)
  998. {
  999. newIndent = action.calculateIndent(this, lineIndex,
  1000. oldIndent, newIndent);
  1001. if (!action.keepChecking())
  1002. break;
  1003. }
  1004. if (newIndent < 0)
  1005. newIndent = 0;
  1006. return newIndent;
  1007. } //}}}
  1008. //{{{ getVirtualWidth() method
  1009. /**
  1010. * Returns the virtual column number (taking tabs into account) of the
  1011. * specified position.
  1012. *
  1013. * @param line The line number
  1014. * @param column The column number
  1015. * @since jEdit 4.1pre1
  1016. */
  1017. public int getVirtualWidth(int line, int column)
  1018. {
  1019. try
  1020. {
  1021. readLock();
  1022. int start = getLineStartOffset(line);
  1023. Segment seg = new Segment();
  1024. getText(start,column,seg);
  1025. return StandardUtilities.getVirtualWidth(seg,getTabSize());
  1026. }
  1027. finally
  1028. {
  1029. readUnlock();
  1030. }
  1031. } //}}}
  1032. //{{{ getOffsetOfVirtualColumn() method
  1033. /**
  1034. * Returns the offset of a virtual column number (taking tabs
  1035. * into account) relative to the start of the line in question.
  1036. *
  1037. * @param line The line number
  1038. * @param column The virtual column number
  1039. * @param totalVirtualWidth If this array is non-null, the total
  1040. * virtual width will be stored in its first location if this method
  1041. * returns -1.
  1042. *
  1043. * @return -1 if the column is out of bounds
  1044. *
  1045. * @since jEdit 4.1pre1
  1046. */
  1047. public int getOffsetOfVirtualColumn(int line, int column,
  1048. int[] totalVirtualWidth)
  1049. {
  1050. try
  1051. {
  1052. readLock();
  1053. Segment seg = new Segment();
  1054. getLineText(line,seg);
  1055. return StandardUtilities.getOffsetOfVirtualColumn(seg,
  1056. getTabSize(),column,totalVirtualWidth);
  1057. }
  1058. finally
  1059. {
  1060. readUnlock();
  1061. }
  1062. } //}}}
  1063. //{{{ insertAtColumn() method
  1064. /**
  1065. * Like the {@link #insert(int,String)} method, but inserts the string at
  1066. * the specified virtual column. Inserts spaces as appropriate if
  1067. * the line is shorter than the column.
  1068. * @param line The line number
  1069. * @param col The virtual column number
  1070. * @param str The string
  1071. */
  1072. public void insertAtColumn(int line, int col, String str)
  1073. {
  1074. try
  1075. {
  1076. writeLock();
  1077. int[] total = new int[1];
  1078. int offset = getOffsetOfVirtualColumn(line,col,total);
  1079. if(offset == -1)
  1080. {
  1081. offset = getLineEndOffset(line) - 1;
  1082. str = StandardUtilities.createWhiteSpace(col - total[0],0) + str;
  1083. }
  1084. else
  1085. offset += getLineStartOffset(line);
  1086. insert(offset,str);
  1087. }
  1088. finally
  1089. {
  1090. writeUnlock();
  1091. }
  1092. } //}}}
  1093. //{{{ insertIndented() method
  1094. /**
  1095. * Inserts a string into the buffer, indenting each line of the string
  1096. * to match the indent of the first line.
  1097. *
  1098. * @param offset The offset
  1099. * @param text The text
  1100. *
  1101. * @return The number of characters of indent inserted on each new
  1102. * line. This is used by the abbreviations code.
  1103. *
  1104. * @since jEdit 4.2pre14
  1105. */
  1106. public int insertIndented(int offset, String text)
  1107. {
  1108. try
  1109. {
  1110. beginCompoundEdit();
  1111. // obtain the leading indent for later use
  1112. int firstLine = getLineOfOffset(offset);
  1113. CharSequence lineText = getLineSegment(firstLine);
  1114. int leadingIndent
  1115. = StandardUtilities.getLeadingWhiteSpaceWidth(
  1116. lineText,getTabSize());
  1117. String whiteSpace = StandardUtilities.createWhiteSpace(
  1118. leadingIndent,getBooleanProperty("noTabs")
  1119. ? 0 : getTabSize());
  1120. insert(offset,text);
  1121. int lastLine = getLineOfOffset(offset + text.length());
  1122. // note that if firstLine == lastLine, loop does not
  1123. // execute
  1124. for(int i = firstLine + 1; i <= lastLine; i++)
  1125. {
  1126. insert(getLineStartOffset(i),whiteSpace);
  1127. }
  1128. return whiteSpace.length();
  1129. }
  1130. finally
  1131. {
  1132. endCompoundEdit();
  1133. }
  1134. } //}}}
  1135. //{{{ isElectricKey() methods
  1136. /**
  1137. * Should inserting this character trigger a re-indent of
  1138. * the current line?
  1139. * @since jEdit 4.3pre9
  1140. */
  1141. public boolean isElectricKey(char ch, int line)
  1142. {
  1143. TokenMarker.LineContext ctx = lineMgr.getLineContext(line);
  1144. Mode mode = ModeProvider.instance.getMode(ctx.rules.getModeName());
  1145. // mode can be null, though that's probably an error "further up":
  1146. if (mode == null)
  1147. return false;
  1148. return mode.isElectricKey(ch);
  1149. } //}}}
  1150. //}}}
  1151. //{{{ Syntax highlighting
  1152. //{{{ markTokens() method
  1153. /**
  1154. * Returns the syntax tokens for the specified line.
  1155. * @param lineIndex The line number
  1156. * @param tokenHandler The token handler that will receive the syntax
  1157. * tokens
  1158. * @since jEdit 4.1pre1
  1159. */
  1160. public void markTokens(int lineIndex, TokenHandler tokenHandler)
  1161. {
  1162. Segment seg = new Segment();
  1163. if(lineIndex < 0 || lineIndex >= lineMgr.getLineCount())
  1164. throw new ArrayIndexOutOfBoundsException(lineIndex);
  1165. int firstInvalidLineContext = lineMgr.getFirstInvalidLineContext();
  1166. int start;
  1167. if(contextInsensitive || firstInvalidLineContext == -1)
  1168. {
  1169. start = lineIndex;
  1170. }
  1171. else
  1172. {
  1173. start = Math.min(firstInvalidLineContext,
  1174. lineIndex);
  1175. }
  1176. if(Debug.TOKEN_MARKER_DEBUG)
  1177. Log.log(Log.DEBUG,this,"tokenize from " + start + " to " + lineIndex);
  1178. TokenMarker.LineContext oldContext = null;
  1179. TokenMarker.LineContext context = null;
  1180. for(int i = start; i <= lineIndex; i++)
  1181. {
  1182. getLineText(i,seg);
  1183. oldContext = lineMgr.getLineContext(i);
  1184. TokenMarker.LineContext prevContext = (
  1185. (i == 0 || contextInsensitive) ? null
  1186. : lineMgr.getLineContext(i - 1)
  1187. );
  1188. context = tokenMarker.markTokens(prevContext,
  1189. (i == lineIndex ? tokenHandler
  1190. : DummyTokenHandler.INSTANCE), seg);
  1191. lineMgr.setLineContext(i,context);
  1192. }
  1193. int lineCount = lineMgr.getLineCount();
  1194. if(lineCount - 1 == lineIndex)
  1195. lineMgr.setFirstInvalidLineContext(-1);
  1196. else if(oldContext != context)
  1197. lineMgr.setFirstInvalidLineContext(lineIndex + 1);
  1198. else if(firstInvalidLineContext == -1)
  1199. /* do nothing */;
  1200. else
  1201. {
  1202. lineMgr.setFirstInvalidLineContext(Math.max(
  1203. firstInvalidLineContext,lineIndex + 1));
  1204. }
  1205. } //}}}
  1206. //{{{ getTokenMarker() method
  1207. public TokenMarker getTokenMarker()
  1208. {
  1209. return tokenMarker;
  1210. } //}}}
  1211. //{{{ setTokenMarker() method
  1212. public void setTokenMarker(TokenMarker tokenMarker)
  1213. {
  1214. TokenMarker oldTokenMarker = this.tokenMarker;
  1215. this.tokenMarker = tokenMarker;
  1216. // don't do this on initial token marker
  1217. if(oldTokenMarker != null && tokenMarker != oldTokenMarker)
  1218. {
  1219. lineMgr.setFirstInvalidLineContext(0);
  1220. }
  1221. } //}}}
  1222. //{{{ createPosition() method
  1223. /**
  1224. * Creates a floating position.
  1225. * @param offset The offset
  1226. */
  1227. public Position createPosition(int offset)
  1228. {
  1229. try
  1230. {
  1231. readLock();
  1232. if(offset < 0 || offset > contentMgr.getLength())
  1233. throw new ArrayIndexOutOfBoundsException(offset);
  1234. return positionMgr.createPosition(offset);
  1235. }
  1236. finally
  1237. {
  1238. readUnlock();
  1239. }
  1240. } //}}}
  1241. //}}}
  1242. //{{{ Property methods
  1243. //{{{ propertiesChanged() method
  1244. /**
  1245. * Reloads settings from the properties. This should be called
  1246. * after the <code>syntax</code> or <code>folding</code>
  1247. * buffer-local properties are changed.
  1248. */
  1249. public void propertiesChanged()
  1250. {
  1251. String folding = getStringProperty("folding");
  1252. FoldHandler handler = FoldHandler.getFoldHandler(folding);
  1253. if(handler != null)
  1254. {
  1255. setFoldHandler(handler);
  1256. }
  1257. else
  1258. {
  1259. setFoldHandler(new DummyFoldHandler());
  1260. }
  1261. } //}}}
  1262. //{{{ getTabSize() method
  1263. /**
  1264. * Returns the tab size used in this buffer. This is equivalent
  1265. * to calling <code>getProperty("tabSize")</code>.
  1266. * This method is thread-safe.
  1267. */
  1268. public int getTabSize()
  1269. {
  1270. int tabSize = getIntegerProperty("tabSize",8);
  1271. if(tabSize <= 0)
  1272. return 8;
  1273. else
  1274. return tabSize;
  1275. } //}}}
  1276. //{{{ getIndentSize() method
  1277. /**
  1278. * Returns the indent size used in this buffer. This is equivalent
  1279. * to calling <code>getProperty("indentSize")</code>.
  1280. * This method is thread-safe.
  1281. * @since jEdit 2.7pre1
  1282. */
  1283. public int getIndentSize()
  1284. {
  1285. int indentSize = getIntegerProperty("indentSize",8);
  1286. if(indentSize <= 0)
  1287. return 8;
  1288. else
  1289. return indentSize;
  1290. } //}}}
  1291. //{{{ getProperty() method
  1292. /**
  1293. * Returns the value of a buffer-local property.<p>
  1294. *
  1295. * Using this method is generally discouraged, because it returns an
  1296. * <code>Object</code> which must be cast to another type
  1297. * in order to be useful, and this can cause problems if the object
  1298. * is of a different type than what the caller expects.<p>
  1299. *
  1300. * The following methods should be used instead:
  1301. * <ul>
  1302. * <li>{@link #getStringProperty(String)}</li>
  1303. * <li>{@link #getBooleanProperty(String)}</li>
  1304. * <li>{@link #getIntegerProperty(String,int)}</li>
  1305. * </ul>
  1306. *
  1307. * This method is thread-safe.
  1308. *
  1309. * @param name The property name. For backwards compatibility, this
  1310. * is an <code>Object</code>, not a <code>String</code>.
  1311. */
  1312. public Object getProperty(Object name)
  1313. {
  1314. synchronized(propertyLock)
  1315. {
  1316. // First try the buffer-local properties
  1317. PropValue o = properties.get(name);
  1318. if(o != null)
  1319. return o.value;
  1320. // For backwards compatibility
  1321. if(!(name instanceof String))
  1322. return null;
  1323. Object retVal = getDefaultProperty((String)name);
  1324. if(retVal == null)
  1325. return null;
  1326. else
  1327. {
  1328. properties.put(name,new PropValue(retVal,true));
  1329. return retVal;
  1330. }
  1331. }
  1332. } //}}}
  1333. //{{{ getDefaultProperty() method
  1334. public Object getDefaultProperty(String key)
  1335. {
  1336. return null;
  1337. } //}}}
  1338. //{{{ setProperty() method
  1339. /**
  1340. * Sets the value of a buffer-local property.
  1341. * @param name The property name
  1342. * @param value The property value
  1343. * @since jEdit 4.0pre1
  1344. */
  1345. public void setProperty(String name, Object value)
  1346. {
  1347. if(value == null)
  1348. properties.remove(name);
  1349. else
  1350. {
  1351. PropValue test = properties.get(name);
  1352. if(test == null)
  1353. properties.put(name,new PropValue(value,false));
  1354. else if(test.value.equals(value))
  1355. {
  1356. // do nothing
  1357. }
  1358. else
  1359. {
  1360. test.value = value;
  1361. test.defaultValue = false;
  1362. }
  1363. }
  1364. } //}}}
  1365. //{{{ setDefaultProperty() method
  1366. public void setDefaultProperty(String name, Object value)
  1367. {
  1368. properties.put(name,new PropValue(value,true));
  1369. } //}}}
  1370. //{{{ unsetProperty() method
  1371. /**
  1372. * Clears the value of a buffer-local property.
  1373. * @param name The property name
  1374. * @since jEdit 4.0pre1
  1375. */
  1376. public void unsetProperty(String name)
  1377. {
  1378. properties.remove(name);
  1379. } //}}}
  1380. //{{{ resetCachedProperties() method
  1381. public void resetCachedProperties()
  1382. {
  1383. // Need to reset properties that were cached defaults,
  1384. // since the defaults might have changed.
  1385. Iterator<PropValue> iter = properties.values().iterator();
  1386. while(iter.hasNext())
  1387. {
  1388. PropValue value = iter.next();
  1389. if(value.defaultValue)
  1390. iter.remove();
  1391. }
  1392. } //}}}
  1393. //{{{ getStringProperty() method
  1394. /**
  1395. * Returns the value of a string property. This method is thread-safe.
  1396. * @param name The property name
  1397. * @since jEdit 4.0pre1
  1398. */
  1399. public String getStringProperty(String name)
  1400. {
  1401. Object obj = getProperty(name);
  1402. if(obj != null)
  1403. return obj.toString();
  1404. else
  1405. return null;
  1406. } //}}}
  1407. //{{{ setStringProperty() method
  1408. /**
  1409. * Sets a string property.
  1410. * @param name The property name
  1411. * @param value The value
  1412. * @since jEdit 4.0pre1
  1413. */
  1414. public void setStringProperty(String name, String value)
  1415. {
  1416. setProperty(name,value);
  1417. } //}}}
  1418. //{{{ getBooleanProperty() methods
  1419. /**
  1420. * Returns the value of a boolean property. This method is thread-safe.
  1421. * @param name The property name
  1422. * @since jEdit 4.0pre1
  1423. */
  1424. public boolean getBooleanProperty(String name)
  1425. {
  1426. return getBooleanProperty(name, false);
  1427. }
  1428. /**
  1429. * Returns the value of a boolean property. This method is thread-safe.
  1430. * @param name The property name
  1431. * @param def The default value
  1432. * @since jEdit 4.3pre17
  1433. */
  1434. public boolean getBooleanProperty(String name, boolean def)
  1435. {
  1436. Object obj = getProperty(name);
  1437. return StandardUtilities.getBoolean(obj, def);
  1438. } //}}}
  1439. //{{{ setBooleanProperty() method
  1440. /**
  1441. * Sets a boolean property.
  1442. * @param name The property name
  1443. * @param value The value
  1444. * @since jEdit 4.0pre1
  1445. */
  1446. public void setBooleanProperty(String name, boolean value)
  1447. {
  1448. setProperty(name,value ? Boolean.TRUE : Boolean.FALSE);
  1449. } //}}}
  1450. //{{{ getIntegerProperty() method
  1451. /**
  1452. * Returns the value of an integer property. This method is thread-safe.
  1453. * @param name The property name
  1454. * @since jEdit 4.0pre1
  1455. */
  1456. public int getIntegerProperty(String name, int defaultValue)
  1457. {
  1458. boolean defaultValueFlag;
  1459. Object obj;
  1460. PropValue value = properties.get(name);
  1461. if(value != null)
  1462. {
  1463. obj = value.value;
  1464. defaultValueFlag = value.defaultValue;
  1465. }
  1466. else
  1467. {
  1468. obj = getProperty(name);
  1469. // will be cached from now on...
  1470. defaultValueFlag = true;
  1471. }
  1472. if(obj == null)
  1473. return defaultValue;
  1474. else if(obj instanceof Number)
  1475. return ((Number)obj).intValue();
  1476. else
  1477. {
  1478. try
  1479. {
  1480. int returnValue = Integer.parseInt(
  1481. obj.toString().trim());
  1482. properties.put(name,new PropValue(
  1483. returnValue,
  1484. defaultValueFlag));
  1485. return returnValue;
  1486. }
  1487. catch(Exception e)
  1488. {
  1489. return defaultValue;
  1490. }
  1491. }
  1492. } //}}}
  1493. //{{{ setIntegerProperty() method
  1494. /**
  1495. * Sets an integer property.
  1496. * @param name The property name
  1497. * @param value The value
  1498. * @since jEdit 4.0pre1
  1499. */
  1500. public void setIntegerProperty(String name, int value)
  1501. {
  1502. setProperty(name,value);
  1503. } //}}}
  1504. //{{{ getPatternProperty()
  1505. /**
  1506. * Returns the value of a property as a regular expression.
  1507. * This method is thread-safe.
  1508. * @param name The property name
  1509. * @param flags Regular expression compilation flags
  1510. * @since jEdit 4.3pre5
  1511. */
  1512. public Pattern getPatternProperty(String name, int flags)
  1513. {
  1514. synchronized(propertyLock)
  1515. {
  1516. boolean defaultValueFlag;
  1517. Object obj;
  1518. PropValue value = properties.get(name);
  1519. if(value != null)
  1520. {
  1521. obj = value.value;
  1522. defaultValueFlag = value.defaultValue;
  1523. }
  1524. else
  1525. {
  1526. obj = getProperty(name);
  1527. // will be cached from now on...
  1528. defaultValueFlag = true;
  1529. }
  1530. if(obj == null)
  1531. return null;
  1532. else if (obj instanceof Pattern)
  1533. return (Pattern) obj;
  1534. else
  1535. {
  1536. Pattern re = Pattern.compile(obj.toString(),flags);
  1537. properties.put(name,new PropValue(re,
  1538. defaultValueFlag));
  1539. return re;
  1540. }
  1541. }
  1542. } //}}}
  1543. //{{{ getRuleSetAtOffset() method
  1544. /**
  1545. * Returns the syntax highlighting ruleset at the specified offset.
  1546. * @since jEdit 4.1pre1
  1547. */
  1548. public ParserRuleSet getRuleSetAtOffset(int offset)
  1549. {
  1550. int line = getLineOfOffset(offset);
  1551. offset -= getLineStartOffset(line);
  1552. if(offset != 0)
  1553. offset--;
  1554. DefaultTokenHandler tokens = new DefaultTokenHandler();
  1555. markTokens(line,tokens);
  1556. Token token = TextUtilities.getTokenAtOffset(tokens.getTokens(),offset);
  1557. return token.rules;
  1558. } //}}}
  1559. //{{{ getKeywordMapAtOffset() method
  1560. /**
  1561. * Returns the syntax highlighting keyword map in effect at the
  1562. * specified offset. Used by the <b>Complete Word</b> command to
  1563. * complete keywords.
  1564. * @param offset The offset
  1565. * @since jEdit 4.0pre3
  1566. */
  1567. public KeywordMap getKeywordMapAtOffset(int offset)
  1568. {
  1569. return getRuleSetAtOffset(offset).getKeywords();
  1570. } //}}}
  1571. //{{{ getContextSensitiveProperty() method
  1572. /**
  1573. * Some settings, like comment start and end strings, can
  1574. * vary between different parts of a buffer (HTML text and inline
  1575. * JavaScript, for example).
  1576. * @param offset The offset
  1577. * @param name The property name
  1578. * @since jEdit 4.0pre3
  1579. */
  1580. public String getContextSensitiveProperty(int offset, String name)
  1581. {
  1582. ParserRuleSet rules = getRuleSetAtOffset(offset);
  1583. Object value = null;
  1584. Map<String, String> rulesetProps = rules.getProperties();
  1585. if(rulesetProps != null)
  1586. value = rulesetProps.get(name);
  1587. if(value == null)
  1588. return null;
  1589. else
  1590. return String.valueOf(value);
  1591. } //}}}
  1592. //{{{ getMode() method
  1593. /**
  1594. * Returns this buffer's edit mode. This method is thread-safe.
  1595. */
  1596. public Mode getMode()
  1597. {
  1598. return mode;
  1599. } //}}}
  1600. //{{{ setMode() methods
  1601. /**
  1602. * Sets this buffer's edit mode. Note that calling this before a buffer
  1603. * is loaded will have no effect; in that case, set the "mode" property
  1604. * to the name of the mode. A bit inelegant, I know...
  1605. * @param mode The mode name
  1606. * @since jEdit 4.2pre1
  1607. */
  1608. public void setMode(String mode)
  1609. {
  1610. setMode(ModeProvider.instance.getMode(mode));
  1611. }
  1612. /**
  1613. * Sets this buffer's edit mode. Note that calling this before a buffer
  1614. * is loaded will have no effect; in that case, set the "mode" property
  1615. * to the name of the mode. A bit inelegant, I know...
  1616. * @param mode The mode
  1617. */
  1618. public void setMode(Mode mode)
  1619. {
  1620. setMode(mode, false);
  1621. }
  1622. /**
  1623. * Sets this buffer's edit mode. Note that calling this before a buffer
  1624. * is loaded will have no effect; in that case, set the "mode" property
  1625. * to the name of the mode. A bit inelegant, I know...
  1626. * @param mode The mode
  1627. * @param forceContextInsensitive true if you want to force the buffer to be
  1628. * insensitive to the context. Careful it can break syntax highlight. Default
  1629. * value is false
  1630. * @since jEdit 4.5pre1
  1631. */
  1632. public void setMode(Mode mode, boolean forceContextInsensitive)
  1633. {
  1634. /* This protects against stupid people (like me)
  1635. * doing stuff like buffer.setMode(jEdit.getMode(...)); */
  1636. if(mode == null)
  1637. throw new NullPointerException("Mode must be non-null");
  1638. this.mode = mode;
  1639. contextInsensitive = forceContextInsensitive ||
  1640. mode.getBooleanProperty("contextInsensitive");
  1641. setTokenMarker(mode.getTokenMarker());
  1642. resetCachedProperties();
  1643. propertiesChanged();
  1644. }//}}}
  1645. //}}}
  1646. //{{{ Folding methods
  1647. //{{{ isFoldStart() method
  1648. /**
  1649. * Returns if the specified line begins a fold.
  1650. * @since jEdit 3.1pre1
  1651. */
  1652. public boolean isFoldStart(int line)
  1653. {
  1654. return line != getLineCount() - 1
  1655. && getFoldLevel(line) < getFoldLevel(line + 1);
  1656. } //}}}
  1657. //{{{ isFoldEnd() method
  1658. /**
  1659. * Returns if the specified line ends a fold.
  1660. * @since jEdit 4.2pre5
  1661. */
  1662. public boolean isFoldEnd(int line)
  1663. {
  1664. return line != getLineCount() - 1
  1665. && getFoldLevel(line) > getFoldLevel(line + 1);
  1666. } //}}}
  1667. //{{{ invalidateCachedFoldLevels() method
  1668. /**
  1669. * Invalidates all cached fold level information.
  1670. * @since jEdit 4.1pre11
  1671. */
  1672. public void invalidateCachedFoldLevels()
  1673. {
  1674. lineMgr.setFirstInvalidFoldLevel(0);
  1675. fireFoldLevelChanged(0,getLineCount());
  1676. } //}}}
  1677. //{{{ getFoldLevel() method
  1678. /**
  1679. * Returns the fold level of the specified line.
  1680. * @param line A physical line index
  1681. * @since jEdit 3.1pre1
  1682. */
  1683. public int getFoldLevel(int line)
  1684. {
  1685. if(line < 0 || line >= lineMgr.getLineCount())
  1686. throw new ArrayIndexOutOfBoundsException(line);
  1687. if(foldHandler instanceof DummyFoldHandler)
  1688. return 0;
  1689. int firstInvalidFoldLevel = lineMgr.getFirstInvalidFoldLevel();
  1690. if(firstInvalidFoldLevel == -1 || line < firstInvalidFoldLevel)
  1691. {
  1692. return lineMgr.getFoldLevel(line);
  1693. }
  1694. else
  1695. {
  1696. if(Debug.FOLD_DEBUG)
  1697. Log.log(Log.DEBUG,this,"Invalid fold levels from " + firstInvalidFoldLevel + " to " + line);
  1698. int newFoldLevel = 0;
  1699. boolean changed = false;
  1700. int firstUpdatedFoldLevel = firstInvalidFoldLevel;
  1701. for(int i = firstInvalidFoldLevel; i <= line; i++)
  1702. {
  1703. Segment seg = new Segment();
  1704. newFoldLevel = foldHandler.getFoldLevel(this,i,seg);
  1705. if(newFoldLevel != lineMgr.getFoldLevel(i))
  1706. {
  1707. if(Debug.FOLD_DEBUG)
  1708. Log.log(Log.DEBUG,this,i + " fold level changed");
  1709. changed = true;
  1710. // Update preceding fold levels if necessary
  1711. if (i == firstInvalidFoldLevel)
  1712. {
  1713. List<Integer> precedingFoldLevels =
  1714. foldHandler.getPrecedingFoldLevels(
  1715. this,i,seg,newFoldLevel);
  1716. if (precedingFoldLevels != null)
  1717. {
  1718. int j = i;
  1719. for (Integer foldLevel: precedingFoldLevels)
  1720. {
  1721. j--;
  1722. lineMgr.setFoldLevel(j,foldLevel.intValue());
  1723. }
  1724. if (j < firstUpdatedFoldLevel)
  1725. firstUpdatedFoldLevel = j;
  1726. }
  1727. }
  1728. }
  1729. lineMgr.setFoldLevel(i,newFoldLevel);
  1730. }
  1731. if(line == lineMgr.getLineCount() - 1)
  1732. lineMgr.setFirstInvalidFoldLevel(-1);
  1733. else
  1734. lineMgr.setFirstInvalidFoldLevel(line + 1);
  1735. if(changed)
  1736. {
  1737. if(Debug.FOLD_DEBUG)
  1738. Log.log(Log.DEBUG,this,"fold level changed: " + firstUpdatedFoldLevel + ',' + line);
  1739. fireFoldLevelChanged(firstUpdatedFoldLevel,line);
  1740. }
  1741. return newFoldLevel;
  1742. }
  1743. } //}}}
  1744. //{{{ getFoldAtLine() method
  1745. /**
  1746. * Returns an array. The first element is the start line, the
  1747. * second element is the end line, of the fold containing the
  1748. * specified line number.
  1749. * @param line The line number
  1750. * @since jEdit 4.0pre3
  1751. */
  1752. public int[] getFoldAtLine(int line)
  1753. {
  1754. int start, end;
  1755. if(isFoldStart(line))
  1756. {
  1757. start = line;
  1758. int foldLevel = getFoldLevel(line);
  1759. line++;
  1760. while(getFoldLevel(line) > foldLevel)
  1761. {
  1762. line++;
  1763. if(line == getLineCount())
  1764. break;
  1765. }
  1766. end = line - 1;
  1767. }
  1768. else
  1769. {
  1770. start = line;
  1771. int foldLevel = getFoldLevel(line);
  1772. while(getFoldLevel(start) >= foldLevel)
  1773. {
  1774. if(start == 0)
  1775. break;
  1776. else
  1777. start--;
  1778. }
  1779. end = line;
  1780. while(getFoldLevel(end) >= foldLevel)
  1781. {
  1782. end++;
  1783. if(end == getLineCount())
  1784. break;
  1785. }
  1786. end--;
  1787. }
  1788. while(getLineLength(end) == 0 && end > start)
  1789. end--;
  1790. return new int[] { start, end };
  1791. } //}}}
  1792. //{{{ getFoldHandler() method
  1793. /**
  1794. * Returns the current buffer's fold handler.
  1795. * @since jEdit 4.2pre1
  1796. */
  1797. public FoldHandler getFoldHandler()
  1798. {
  1799. return foldHandler;
  1800. } //}}}
  1801. //{{{ setFoldHandler() method
  1802. /**
  1803. * Sets the buffer's fold handler.
  1804. * @since jEdit 4.2pre2
  1805. */
  1806. public void setFoldHandler(FoldHandler foldHandler)
  1807. {
  1808. FoldHandler oldFoldHandler = this.foldHandler;
  1809. if(foldHandler.equals(oldFoldHandler))
  1810. return;
  1811. this.foldHandler = foldHandler;
  1812. lineMgr.setFirstInvalidFoldLevel(0);
  1813. fireFoldHandlerChanged();
  1814. } //}}}
  1815. //}}}
  1816. //{{{ Undo
  1817. //{{{ undo() method
  1818. /**
  1819. * Undoes the most recent edit.
  1820. *
  1821. * @since jEdit 4.0pre1
  1822. */
  1823. public void undo(TextArea textArea)
  1824. {
  1825. if(undoMgr == null)
  1826. return;
  1827. if(!isEditable())
  1828. {
  1829. textArea.getToolkit().beep();
  1830. return;
  1831. }
  1832. try
  1833. {
  1834. writeLock();
  1835. undoInProgress = true;
  1836. fireBeginUndo();
  1837. int caret = undoMgr.undo();
  1838. if(caret == -1)
  1839. textArea.getToolkit().beep();
  1840. else
  1841. textArea.setCaretPosition(caret);
  1842. fireEndUndo();
  1843. fireTransactionComplete();
  1844. }
  1845. finally
  1846. {
  1847. undoInProgress = false;
  1848. writeUnlock();
  1849. }
  1850. } //}}}
  1851. //{{{ redo() method
  1852. /**
  1853. * Redoes the most recently undone edit.
  1854. *
  1855. * @since jEdit 2.7pre2
  1856. */
  1857. public void redo(TextArea textArea)
  1858. {
  1859. if(undoMgr == null)
  1860. return;
  1861. if(!isEditable())
  1862. {
  1863. Toolkit.getDefaultToolkit().beep();
  1864. return;
  1865. }
  1866. try
  1867. {
  1868. writeLock();
  1869. undoInProgress = true;
  1870. fireBeginRedo();
  1871. int caret = undoMgr.redo();
  1872. if(caret == -1)
  1873. textArea.getToolkit().beep();
  1874. else
  1875. textArea.setCaretPosition(caret);
  1876. fireEndRedo();
  1877. fireTransactionComplete();
  1878. }
  1879. finally
  1880. {
  1881. undoInProgress = false;
  1882. writeUnlock();
  1883. }
  1884. } //}}}
  1885. //{{{ isTransactionInProgress() method
  1886. /**
  1887. * Returns if an undo or compound edit is currently in progress. If this
  1888. * method returns true, then eventually a
  1889. * {@link org.gjt.sp.jedit.buffer.BufferListener#transactionComplete(JEditBuffer)}
  1890. * buffer event will get fired.
  1891. * @since jEdit 4.0pre6
  1892. */
  1893. public boolean isTransactionInProgress()
  1894. {
  1895. return transaction || undoInProgress || insideCompoundEdit() || loading;
  1896. } //}}}
  1897. //{{{ beginCompoundEdit() method
  1898. /**
  1899. * Starts a compound edit. All edits from now on until
  1900. * {@link #endCompoundEdit()} are called will be merged
  1901. * into one. This can be used to make a complex operation
  1902. * undoable in one step. Nested calls to
  1903. * {@link #beginCompoundEdit()} behave as expected,
  1904. * requiring the same number of {@link #endCompoundEdit()}
  1905. * calls to end the edit.
  1906. * @see #endCompoundEdit()
  1907. */
  1908. public void beginCompoundEdit()
  1909. {
  1910. try
  1911. {
  1912. writeLock();
  1913. undoMgr.beginCompoundEdit();
  1914. }
  1915. finally
  1916. {
  1917. writeUnlock();
  1918. }
  1919. } //}}}
  1920. //{{{ endCompoundEdit() method
  1921. /**
  1922. * Ends a compound edit. All edits performed since
  1923. * {@link #beginCompoundEdit()} was called can now
  1924. * be undone in one step by calling {@link #undo(TextArea)}.
  1925. * @see #beginCompoundEdit()
  1926. */
  1927. public void endCompoundEdit()
  1928. {
  1929. try
  1930. {
  1931. writeLock();
  1932. undoMgr.endCompoundEdit();
  1933. if(!insideCompoundEdit())
  1934. fireTransactionComplete();
  1935. }
  1936. finally
  1937. {
  1938. writeUnlock();
  1939. }
  1940. }//}}}
  1941. //{{{ insideCompoundEdit() method
  1942. /**
  1943. * Returns if a compound edit is currently active.
  1944. * @since jEdit 3.1pre1
  1945. */
  1946. public boolean insideCompoundEdit()
  1947. {
  1948. return undoMgr.insideCompoundEdit();
  1949. } //}}}
  1950. //{{{ isUndoInProgress() method
  1951. /**
  1952. * Returns if an undo or redo is currently being performed.
  1953. * @since jEdit 4.3pre3
  1954. */
  1955. public boolean isUndoInProgress()
  1956. {
  1957. return undoInProgress;
  1958. } //}}}
  1959. //{{{ getUndoId() method
  1960. /**
  1961. * Returns an object that identifies the undo operation to which the
  1962. * current content change belongs. This method can be used by buffer
  1963. * listeners during content changes (contentInserted/contentRemoved)
  1964. * to find out which content changes belong to the same "undo" operation.
  1965. * The same undoId object will be returned for all content changes
  1966. * belonging to the same undo operation. Only the identity of the
  1967. * undoId can be used, by comparing it with a previously-returned undoId
  1968. * using "==".
  1969. * @since jEdit 4.3pre18
  1970. */
  1971. public Object getUndoId()
  1972. {
  1973. return undoMgr.getUndoId();
  1974. } //}}}
  1975. //}}}
  1976. //{{{ Buffer events
  1977. public static final int NORMAL_PRIORITY = 0;
  1978. public static final int HIGH_PRIORITY = 1;
  1979. static class Listener
  1980. {
  1981. BufferListener listener;
  1982. int priority;
  1983. Listener(BufferListener listener, int priority)
  1984. {
  1985. this.listener = listener;
  1986. this.priority = priority;
  1987. }
  1988. }
  1989. //{{{ addBufferListener() methods
  1990. /**
  1991. * Adds a buffer change listener.
  1992. * @param listener The listener
  1993. * @param priority Listeners with HIGH_PRIORITY get the event before
  1994. * listeners with NORMAL_PRIORITY
  1995. * @since jEdit 4.3pre3
  1996. */
  1997. public void addBufferListener(BufferListener listener,
  1998. int priority)
  1999. {
  2000. Listener l = new Listener(listener,priority);
  2001. for(int i = 0; i < bufferListeners.size(); i++)
  2002. {
  2003. Listener _l = bufferListeners.get(i);
  2004. if(_l.priority < priority)
  2005. {
  2006. bufferListeners.add(i,l);
  2007. return;
  2008. }
  2009. }
  2010. bufferListeners.add(l);
  2011. }
  2012. /**
  2013. * Adds a buffer change listener.
  2014. * @param listener The listener
  2015. * @since jEdit 4.3pre3
  2016. */
  2017. public void addBufferListener(BufferListener listener)
  2018. {
  2019. addBufferListener(listener,NORMAL_PRIORITY);
  2020. } //}}}
  2021. //{{{ removeBufferListener() method
  2022. /**
  2023. * Removes a buffer change listener.
  2024. * @param listener The listener
  2025. * @since jEdit 4.3pre3
  2026. */
  2027. public void removeBufferListener(BufferListener listener)
  2028. {
  2029. for(int i = 0; i < bufferListeners.size(); i++)
  2030. {
  2031. if(bufferListeners.get(i).listener == listener)
  2032. {
  2033. bufferListeners.remove(i);
  2034. return;
  2035. }
  2036. }
  2037. } //}}}
  2038. //{{{ getBufferListeners() method
  2039. /**
  2040. * Returns an array of registered buffer change listeners.
  2041. * @since jEdit 4.3pre3
  2042. */
  2043. public BufferListener[] getBufferListeners()
  2044. {
  2045. BufferListener[] returnValue
  2046. = new BufferListener[
  2047. bufferListeners.size()];
  2048. for(int i = 0; i < returnValue.length; i++)
  2049. {
  2050. returnValue[i] = bufferListeners.get(i).listener;
  2051. }
  2052. return returnValue;
  2053. } //}}}
  2054. //{{{ setUndoLimit() method
  2055. /**
  2056. * Set the undo limit of the Undo Manager.
  2057. *
  2058. * @param limit the new limit
  2059. * @since jEdit 4.3pre16
  2060. */
  2061. public void setUndoLimit(int limit)
  2062. {
  2063. if (undoMgr != null)
  2064. undoMgr.setLimit(limit);
  2065. } //}}}
  2066. //{{{ canUndo() method
  2067. /**
  2068. * Returns true if an undo operation can be performed.
  2069. * @since jEdit 4.3pre18
  2070. */
  2071. public boolean canUndo()
  2072. {
  2073. if (undoMgr == null)
  2074. return false;
  2075. return undoMgr.canUndo();
  2076. } //}}}
  2077. //{{{ canRedo() method
  2078. /**
  2079. * Returns true if a redo operation can be performed.
  2080. * @since jEdit 4.3pre18
  2081. */
  2082. public boolean canRedo()
  2083. {
  2084. if (undoMgr == null)
  2085. return false;
  2086. return undoMgr.canRedo();
  2087. } //}}}
  2088. //{{{ isContextInsensitive() method
  2089. /**
  2090. * Returns true if the buffer highlight is
  2091. * not sensitive to the context.
  2092. * @return true if the highlight is insensitive to
  2093. * the context
  2094. * @since jEdit 4.5pre1
  2095. */
  2096. public boolean isContextInsensitive()
  2097. {
  2098. return contextInsensitive;
  2099. }//}}}
  2100. //{{{ setContextInsensitive() method
  2101. /**
  2102. * Set the buffer to be insensitive to the context during
  2103. * highlight.
  2104. * @param contextInsensitive the new contextInsensitive value
  2105. * the context
  2106. * @since jEdit 4.5pre1
  2107. */
  2108. public void setContextInsensitive(boolean contextInsensitive)
  2109. {
  2110. this.contextInsensitive = contextInsensitive;
  2111. }//}}}
  2112. //}}}
  2113. //{{{ Protected members
  2114. /**
  2115. * The edit mode of the buffer.
  2116. */
  2117. protected Mode mode;
  2118. /**
  2119. * If true the syntax highlight is context insensitive.
  2120. * To highlight a line we don't keed the context of the previous line.
  2121. */
  2122. protected boolean contextInsensitive;
  2123. protected UndoManager undoMgr;
  2124. //{{{ Event firing methods
  2125. //{{{ fireFoldLevelChanged() method
  2126. protected void fireFoldLevelChanged(int start, int end)
  2127. {
  2128. for(int i = 0; i < bufferListeners.size(); i++)
  2129. {
  2130. BufferListener listener = getListener(i);
  2131. try
  2132. {
  2133. listener.foldLevelChanged(this,start,end);
  2134. }
  2135. catch(Throwable t)
  2136. {
  2137. Log.log(Log.ERROR,this,"Exception while sending buffer event to "+ listener +" :");
  2138. Log.log(Log.ERROR,this,t);
  2139. }
  2140. }
  2141. } //}}}
  2142. //{{{ fireContentInserted() method
  2143. protected void fireContentInserted(int startLine, int offset,
  2144. int numLines, int length)
  2145. {
  2146. for(int i = 0; i < bufferListeners.size(); i++)
  2147. {
  2148. BufferListener listener = getListener(i);
  2149. try
  2150. {
  2151. listener.contentInserted(this,startLine,
  2152. offset,numLines,length);
  2153. }
  2154. catch(Throwable t)
  2155. {
  2156. Log.log(Log.ERROR,this,"Exception while sending buffer event to "+ listener +" :");
  2157. Log.log(Log.ERROR,this,t);
  2158. }
  2159. }
  2160. } //}}}
  2161. //{{{ fireContentRemoved() method
  2162. protected void fireContentRemoved(int startLine, int offset,
  2163. int numLines, int length)
  2164. {
  2165. for(int i = 0; i < bufferListeners.size(); i++)
  2166. {
  2167. BufferListener listener = getListener(i);
  2168. try
  2169. {
  2170. listener.contentRemoved(this,startLine,
  2171. offset,numLines,length);
  2172. }
  2173. catch(Throwable t)
  2174. {
  2175. Log.log(Log.ERROR,this,"Exception while sending buffer event to "+ listener +" :");
  2176. Log.log(Log.ERROR,this,t);
  2177. }
  2178. }
  2179. } //}}}
  2180. //{{{ firePreContentInserted() method
  2181. protected void firePreContentInserted(int startLine, int offset,
  2182. int numLines, int length)
  2183. {
  2184. for(int i = 0; i < bufferListeners.size(); i++)
  2185. {
  2186. BufferListener listener = getListener(i);
  2187. try
  2188. {
  2189. listener.preContentInserted(this,startLine,
  2190. offset,numLines,length);
  2191. }
  2192. catch(Throwable t)
  2193. {
  2194. Log.log(Log.ERROR,this,"Exception while sending buffer event to "+ listener +" :");
  2195. Log.log(Log.ERROR,this,t);
  2196. }
  2197. }
  2198. } //}}}
  2199. //{{{ firePreContentRemoved() method
  2200. protected void firePreContentRemoved(int startLine, int offset,
  2201. int numLines, int length)
  2202. {
  2203. for(int i = 0; i < bufferListeners.size(); i++)
  2204. {
  2205. BufferListener listener = getListener(i);
  2206. try
  2207. {
  2208. listener.preContentRemoved(this,startLine,
  2209. offset,numLines,length);
  2210. }
  2211. catch(Throwable t)
  2212. {
  2213. Log.log(Log.ERROR,this,"Exception while sending buffer event to "+ listener +" :");
  2214. Log.log(Log.ERROR,this,t);
  2215. }
  2216. }
  2217. } //}}}
  2218. //{{{ fireBeginUndo() method
  2219. protected void fireBeginUndo()
  2220. {
  2221. } //}}}
  2222. //{{{ fireEndUndo() method
  2223. protected void fireEndUndo()
  2224. {
  2225. } //}}}
  2226. //{{{ fireBeginRedo() method
  2227. protected void fireBeginRedo()
  2228. {
  2229. } //}}}
  2230. //{{{ fireEndRedo() method
  2231. protected void fireEndRedo()
  2232. {
  2233. } //}}}
  2234. //{{{ fireTransactionComplete() method
  2235. protected void fireTransactionComplete()
  2236. {
  2237. for(int i = 0; i < bufferListeners.size(); i++)
  2238. {
  2239. BufferListener listener = getListener(i);
  2240. try
  2241. {
  2242. listener.transactionComplete(this);
  2243. }
  2244. catch(Throwable t)
  2245. {
  2246. Log.log(Log.ERROR,this,"Exception while sending buffer event to "+ listener +" :");
  2247. Log.log(Log.ERROR,this,t);
  2248. }
  2249. }
  2250. } //}}}
  2251. //{{{ fireFoldHandlerChanged() method
  2252. protected void fireFoldHandlerChanged()
  2253. {
  2254. for(int i = 0; i < bufferListeners.size(); i++)
  2255. {
  2256. BufferListener listener = getListener(i);
  2257. try
  2258. {
  2259. listener.foldHandlerChanged(this);
  2260. }
  2261. catch(Throwable t)
  2262. {
  2263. Log.log(Log.ERROR,this,"Exception while sending buffer event to "+ listener +" :");
  2264. Log.log(Log.ERROR,this,t);
  2265. }
  2266. }
  2267. } //}}}
  2268. //{{{ fireBufferLoaded() method
  2269. protected void fireBufferLoaded()
  2270. {
  2271. for(int i = 0; i < bufferListeners.size(); i++)
  2272. {
  2273. BufferListener listener = getListener(i);
  2274. try
  2275. {
  2276. listener.bufferLoaded(this);
  2277. }
  2278. catch(Throwable t)
  2279. {
  2280. Log.log(Log.ERROR,this,"Exception while sending buffer event to "+ listener +" :");
  2281. Log.log(Log.ERROR,this,t);
  2282. }
  2283. }
  2284. } //}}}
  2285. //}}}
  2286. //{{{ isFileReadOnly() method
  2287. protected boolean isFileReadOnly()
  2288. {
  2289. return readOnly;
  2290. } //}}}
  2291. //{{{ setFileReadOnly() method
  2292. protected void setFileReadOnly(boolean readOnly)
  2293. {
  2294. this.readOnly = readOnly;
  2295. } //}}}
  2296. //{{{ loadText() method
  2297. protected void loadText(Segment seg, IntegerArray endOffsets)
  2298. {
  2299. if(seg == null)
  2300. seg = new Segment(new char[1024],0,0);
  2301. if(endOffsets == null)
  2302. {
  2303. endOffsets = new IntegerArray();
  2304. endOffsets.add(1);
  2305. }
  2306. try
  2307. {
  2308. writeLock();
  2309. // For `reload' command
  2310. // contentMgr.remove() changes this!
  2311. int length = getLength();
  2312. firePreContentRemoved(0,0,getLineCount()
  2313. - 1,length);
  2314. contentMgr.remove(0,length);
  2315. lineMgr.contentRemoved(0,0,getLineCount()
  2316. - 1,length);
  2317. positionMgr.contentRemoved(0,length);
  2318. fireContentRemoved(0,0,getLineCount()
  2319. - 1,length);
  2320. firePreContentInserted(0, 0, endOffsets.getSize() - 1, seg.count - 1);
  2321. // theoretically a segment could
  2322. // have seg.offset != 0 but
  2323. // SegmentBuffer never does that
  2324. contentMgr._setContent(seg.array,seg.count);
  2325. lineMgr._contentInserted(endOffsets);
  2326. positionMgr.contentInserted(0,seg.count);
  2327. fireContentInserted(0,0,
  2328. endOffsets.getSize() - 1,
  2329. seg.count - 1);
  2330. }
  2331. finally
  2332. {
  2333. writeUnlock();
  2334. }
  2335. } //}}}
  2336. //{{{ invalidateFoldLevels() method
  2337. protected void invalidateFoldLevels()
  2338. {
  2339. lineMgr.setFirstInvalidFoldLevel(0);
  2340. } //}}}
  2341. //{{{ parseBufferLocalProperties() method
  2342. protected void parseBufferLocalProperties()
  2343. {
  2344. int maxRead = 10000;
  2345. int lineCount = getLineCount();
  2346. int lastLine = Math.min(9, lineCount - 1);
  2347. int max = Math.min(maxRead, getLineEndOffset(lastLine) - 1);
  2348. parseBufferLocalProperties(getSegment(0, max));
  2349. // first line for last 10 lines, make sure not to overlap
  2350. // with the first 10
  2351. int firstLine = Math.max(lastLine + 1, lineCount - 10);
  2352. if(firstLine < lineCount)
  2353. {
  2354. int firstLineStartOffset = getLineStartOffset(firstLine);
  2355. int length = getLineEndOffset(lineCount - 1)
  2356. - (firstLineStartOffset + 1);
  2357. if (length > maxRead)
  2358. {
  2359. firstLineStartOffset += length - maxRead;
  2360. length = maxRead;
  2361. }
  2362. parseBufferLocalProperties(getSegment(firstLineStartOffset,length));
  2363. }
  2364. } //}}}
  2365. //{{{ Used to store property values
  2366. protected static class PropValue
  2367. {
  2368. PropValue(Object value, boolean defaultValue)
  2369. {
  2370. if(value == null)
  2371. throw new NullPointerException();
  2372. this.value = value;
  2373. this.defaultValue = defaultValue;
  2374. }
  2375. Object value;
  2376. /**
  2377. * If this is true, then this value is cached from the mode
  2378. * or global defaults, so when the defaults change this property
  2379. * value must be reset.
  2380. */
  2381. boolean defaultValue;
  2382. /**
  2383. * For debugging purposes.
  2384. */
  2385. public String toString()
  2386. {
  2387. return value.toString();
  2388. }
  2389. } //}}}
  2390. //}}}
  2391. //{{{ Private members
  2392. private final List<Listener> bufferListeners;
  2393. private final ReentrantReadWriteLock lock;
  2394. private final ContentManager contentMgr;
  2395. private final LineManager lineMgr;
  2396. private final PositionManager positionMgr;
  2397. private FoldHandler foldHandler;
  2398. private final IntegerArray integerArray;
  2399. private TokenMarker tokenMarker;
  2400. private boolean undoInProgress;
  2401. private boolean dirty;
  2402. private boolean readOnly;
  2403. private boolean readOnlyOverride;
  2404. private boolean transaction;
  2405. private boolean loading;
  2406. private boolean io;
  2407. private final Map<Object, PropValue> properties;
  2408. private final Object propertyLock;
  2409. public boolean elasticTabstopsOn = false;
  2410. private ColumnBlock columnBlock;
  2411. //{{{ getListener() method
  2412. private BufferListener getListener(int index)
  2413. {
  2414. return bufferListeners.get(index).listener;
  2415. } //}}}
  2416. //{{{ contentInserted() method
  2417. private void contentInserted(int offset, int length,
  2418. IntegerArray endOffsets)
  2419. {
  2420. try
  2421. {
  2422. transaction = true;
  2423. int startLine = lineMgr.getLineOfOffset(offset);
  2424. int numLines = endOffsets.getSize();
  2425. if (!loading)
  2426. {
  2427. firePreContentInserted(startLine, offset, numLines, length);
  2428. }
  2429. lineMgr.contentInserted(startLine,offset,numLines,length,
  2430. endOffsets);
  2431. positionMgr.contentInserted(offset,length);
  2432. setDirty(true);
  2433. if(!loading)
  2434. {
  2435. fireContentInserted(startLine,offset,numLines,length);
  2436. if(!undoInProgress && !insideCompoundEdit())
  2437. fireTransactionComplete();
  2438. }
  2439. }
  2440. finally
  2441. {
  2442. transaction = false;
  2443. }
  2444. } //}}}
  2445. //{{{ parseBufferLocalProperties() method
  2446. private void parseBufferLocalProperties(CharSequence prop)
  2447. {
  2448. StringBuilder buf = new StringBuilder();
  2449. String name = null;
  2450. boolean escape = false;
  2451. int length = prop.length();
  2452. for(int i = 0; i < length; i++)
  2453. {
  2454. char c = prop.charAt(i);
  2455. switch(c)
  2456. {
  2457. case ':':
  2458. if(escape)
  2459. {
  2460. escape = false;
  2461. buf.append(':');
  2462. break;
  2463. }
  2464. if(name != null)
  2465. {
  2466. // use the low-level property setting code
  2467. // so that if we have a buffer-local
  2468. // property with the same value as a default,
  2469. // later changes in the default don't affect
  2470. // the buffer-local property
  2471. properties.put(name,new PropValue(buf.toString(),false));
  2472. name = null;
  2473. }
  2474. buf.setLength(0);
  2475. break;
  2476. case '=':
  2477. if(escape)
  2478. {
  2479. escape = false;
  2480. buf.append('=');
  2481. break;
  2482. }
  2483. name = buf.toString();
  2484. buf.setLength(0);
  2485. break;
  2486. case '\\':
  2487. if(escape)
  2488. buf.append('\\');
  2489. escape = !escape;
  2490. break;
  2491. case 'n':
  2492. if(escape)
  2493. { buf.append('\n');
  2494. escape = false;
  2495. break;
  2496. }
  2497. case 'r':
  2498. if(escape)
  2499. { buf.append('\r');
  2500. escape = false;
  2501. break;
  2502. }
  2503. case 't':
  2504. if(escape)
  2505. {
  2506. buf.append('\t');
  2507. escape = false;
  2508. break;
  2509. }
  2510. default:
  2511. buf.append(c);
  2512. break;
  2513. }
  2514. }
  2515. } //}}}
  2516. //{{{ getIndentRules() method
  2517. private List<IndentRule> getIndentRules(int line)
  2518. {
  2519. String modeName = null;
  2520. TokenMarker.LineContext ctx = lineMgr.getLineContext(line);
  2521. if (ctx != null && ctx.rules != null)
  2522. modeName = ctx.rules.getModeName();
  2523. if (modeName == null)
  2524. modeName = tokenMarker.getMainRuleSet().getModeName();
  2525. return ModeProvider.instance.getMode(modeName).getIndentRules();
  2526. } //}}}
  2527. //{{{ updateColumnBlocks() method
  2528. public void updateColumnBlocks(int startLine,int endLine,int startColumn,Node parent)
  2529. {
  2530. if((parent!=null)&&(startLine>=0)&&(endLine>=0)&&(startLine<=endLine))