PageRenderTime 62ms CodeModel.GetById 23ms 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

Large files files are truncated, but you can click here to view the full file

  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()}…

Large files files are truncated, but you can click here to view the full file