PageRenderTime 56ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/jEdit/tags/jedit-4-0-pre5/org/gjt/sp/jedit/Buffer.java

#
Java | 2985 lines | 1684 code | 391 blank | 910 comment | 359 complexity | e7e005124e51ebf6a5314a7c2f6d3ac7 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. * Buffer.java - jEdit buffer
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1998, 1999, 2000, 2001, 2002 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;
  24. //{{{ Imports
  25. import gnu.regexp.*;
  26. import javax.swing.*;
  27. import javax.swing.event.*;
  28. import javax.swing.text.*;
  29. import java.awt.Toolkit;
  30. import java.io.File;
  31. import java.util.*;
  32. import org.gjt.sp.jedit.browser.VFSBrowser;
  33. import org.gjt.sp.jedit.buffer.*;
  34. import org.gjt.sp.jedit.io.*;
  35. import org.gjt.sp.jedit.msg.*;
  36. import org.gjt.sp.jedit.search.RESearchMatcher;
  37. import org.gjt.sp.jedit.syntax.*;
  38. import org.gjt.sp.jedit.textarea.*;
  39. import org.gjt.sp.util.*;
  40. //}}}
  41. /**
  42. * An in-memory copy of an open file.
  43. * Note that only very few methods in this class are thread safe; namely,
  44. * those that deal with obtaining buffer contents (<code>getText()</code>,
  45. * <code>getLineStartOffset()</code>, and so on).
  46. *
  47. * @author Slava Pestov
  48. * @version $Id: Buffer.java 4012 2002-02-05 06:28:10Z spestov $
  49. */
  50. public class Buffer implements EBComponent
  51. {
  52. //{{{ Some constants
  53. /**
  54. * Line separator property.
  55. */
  56. public static final String LINESEP = "lineSeparator";
  57. /**
  58. * Backed up property.
  59. * @since jEdit 3.2pre2
  60. */
  61. public static final String BACKED_UP = "Buffer__backedUp";
  62. /**
  63. * Caret info properties.
  64. * @since jEdit 3.2pre1
  65. */
  66. public static final String CARET = "Buffer__caret";
  67. public static final String SELECTION = "Buffer__selection";
  68. /**
  69. * This should be a physical line number, so that the scroll
  70. * position is preserved correctly across reloads (which will
  71. * affect virtual line numbers, due to fold being reset)
  72. */
  73. public static final String SCROLL_VERT = "Buffer__scrollVert";
  74. public static final String SCROLL_HORIZ = "Buffer__scrollHoriz";
  75. /**
  76. * Character encoding used when loading and saving.
  77. * @since jEdit 3.2pre4
  78. */
  79. public static final String ENCODING = "encoding";
  80. /**
  81. * This property is set to 'true' if the file has a trailing newline.
  82. * @since jEdit 4.0pre1
  83. */
  84. public static final String TRAILING_EOL = "trailingEOL";
  85. /**
  86. * This property is set to 'true' if the file should be GZipped.
  87. * @since jEdit 4.0pre4
  88. */
  89. public static final String GZIPPED = "gzipped";
  90. //}}}
  91. //{{{ Input/output methods
  92. //{{{ showInsertFileDialog() method
  93. /**
  94. * Displays the 'insert file' dialog box and inserts the selected file
  95. * into the buffer.
  96. * @param view The view
  97. * @since jEdit 2.7pre2
  98. */
  99. public void showInsertFileDialog(View view)
  100. {
  101. String[] files = GUIUtilities.showVFSFileDialog(view,null,
  102. VFSBrowser.OPEN_DIALOG,false);
  103. if(files != null)
  104. insertFile(view,files[0]);
  105. } //}}}
  106. //{{{ reload() method
  107. /**
  108. * Reloads the buffer from disk, asking for confirmation if the buffer
  109. * is dirty.
  110. * @param view The view
  111. * @since jEdit 2.7pre2
  112. */
  113. public void reload(View view)
  114. {
  115. if(getFlag(DIRTY))
  116. {
  117. String[] args = { name };
  118. int result = GUIUtilities.confirm(view,"changedreload",
  119. args,JOptionPane.YES_NO_OPTION,
  120. JOptionPane.WARNING_MESSAGE);
  121. if(result != JOptionPane.YES_OPTION)
  122. return;
  123. }
  124. view.getEditPane().saveCaretInfo();
  125. load(view,true);
  126. } //}}}
  127. //{{{ load() method
  128. /**
  129. * Loads the buffer from disk, even if it is loaded already.
  130. * @param view The view
  131. * @param reload If true, user will not be asked to recover autosave
  132. * file, if any
  133. *
  134. * @since 2.5pre1
  135. */
  136. public boolean load(final View view, final boolean reload)
  137. {
  138. if(isPerformingIO())
  139. {
  140. GUIUtilities.error(view,"buffer-multiple-io",null);
  141. return false;
  142. }
  143. setFlag(LOADING,true);
  144. // view text areas temporarily blank out while a buffer is
  145. // being loaded, to indicate to the user that there is no
  146. // data available yet.
  147. if(!getFlag(TEMPORARY))
  148. EditBus.send(new BufferUpdate(this,view,BufferUpdate.LOAD_STARTED));
  149. final boolean loadAutosave;
  150. if(reload || !getFlag(NEW_FILE))
  151. {
  152. if(file != null)
  153. modTime = file.lastModified();
  154. // Only on initial load
  155. if(!reload && autosaveFile != null && autosaveFile.exists())
  156. loadAutosave = recoverAutosave(view);
  157. else
  158. {
  159. if(autosaveFile != null)
  160. autosaveFile.delete();
  161. loadAutosave = false;
  162. }
  163. if(!loadAutosave)
  164. {
  165. // this returns false if initial sanity
  166. // checks (if the file is a directory, etc)
  167. // fail
  168. if(!vfs.load(view,this,path))
  169. {
  170. setFlag(LOADING,false);
  171. return false;
  172. }
  173. }
  174. }
  175. else
  176. loadAutosave = false;
  177. //{{{ Do some stuff once loading is finished
  178. Runnable runnable = new Runnable()
  179. {
  180. public void run()
  181. {
  182. String newPath = getStringProperty(
  183. BufferIORequest.NEW_PATH);
  184. Segment seg = (Segment)getProperty(
  185. BufferIORequest.LOAD_DATA);
  186. IntegerArray endOffsets = (IntegerArray)
  187. getProperty(BufferIORequest.END_OFFSETS);
  188. // below remove() call only works if read only
  189. // is false. this is a slightly silly workaround.
  190. boolean readOnly = isReadOnly();
  191. setFlag(READ_ONLY,false);
  192. // For `reload' command
  193. remove(0,getLength());
  194. if(seg != null && endOffsets != null)
  195. {
  196. // This is faster than Buffer.insert()
  197. try
  198. {
  199. writeLock();
  200. contentMgr.insert(0,seg.toString());
  201. contentInserted(0,seg.count,endOffsets);
  202. }
  203. finally
  204. {
  205. writeUnlock();
  206. }
  207. }
  208. setFlag(READ_ONLY,readOnly);
  209. unsetProperty(BufferIORequest.LOAD_DATA);
  210. unsetProperty(BufferIORequest.END_OFFSETS);
  211. unsetProperty(BufferIORequest.NEW_PATH);
  212. undoMgr.clear();
  213. undoMgr.setLimit(jEdit.getIntegerProperty(
  214. "buffer.undoCount",100));
  215. if(!getFlag(TEMPORARY))
  216. finishLoading();
  217. /* Ultra-obscure: we have to fire this event
  218. * after the buffer might have been collapsed
  219. * by finishLoading(), since finishLoading(),
  220. * unlike "official" fold APIs, does not notify
  221. * the text area to invalidate its cached
  222. * virtual to physical information. Note that
  223. * the text area's contentInserted() handler
  224. * updates 'lastPhysLine' even if the LOADING
  225. * flag is set. */
  226. fireContentInserted(0,0,getLineCount(),getLength() - 1);
  227. setFlag(LOADING,false);
  228. // if reloading a file, clear dirty flag
  229. if(reload)
  230. setDirty(false);
  231. if(!loadAutosave && newPath != null && !path.equals(newPath))
  232. setPath(newPath);
  233. // if loadAutosave is false, we loaded an
  234. // autosave file, so we set 'dirty' to true
  235. // note that we don't use setDirty(),
  236. // because a) that would send an unnecessary
  237. // message, b) it would also set the
  238. // AUTOSAVE_DIRTY flag, which will make
  239. // the autosave thread write out a
  240. // redundant autosave file
  241. if(loadAutosave)
  242. setFlag(DIRTY,true);
  243. // send some EditBus messages
  244. if(!getFlag(TEMPORARY))
  245. {
  246. EditBus.send(new BufferUpdate(Buffer.this,
  247. view,BufferUpdate.LOADED));
  248. EditBus.send(new BufferUpdate(Buffer.this,
  249. view,BufferUpdate.MARKERS_CHANGED));
  250. }
  251. }
  252. }; //}}}
  253. if(getFlag(TEMPORARY))
  254. runnable.run();
  255. else
  256. VFSManager.runInAWTThread(runnable);
  257. return true;
  258. } //}}}
  259. //{{{ insertFile() method
  260. /**
  261. * Loads a file from disk, and inserts it into this buffer.
  262. * @param view The view
  263. *
  264. * @since 4.0pre1
  265. */
  266. public boolean insertFile(final View view, String path)
  267. {
  268. if(isPerformingIO())
  269. {
  270. GUIUtilities.error(view,"buffer-multiple-io",null);
  271. return false;
  272. }
  273. path = MiscUtilities.constructPath(this.path,path);
  274. Buffer buffer = jEdit.getBuffer(path);
  275. if(buffer != null)
  276. {
  277. view.getTextArea().setSelectedText(
  278. buffer.getText(0,buffer.getLength()));
  279. return true;
  280. }
  281. VFS vfs = VFSManager.getVFSForPath(path);
  282. setFlag(IO,true);
  283. // this returns false if initial sanity
  284. // checks (if the file is a directory, etc)
  285. // fail
  286. if(!vfs.insert(view,this,path))
  287. {
  288. setFlag(IO,false);
  289. return false;
  290. }
  291. // Do some stuff once loading is finished
  292. VFSManager.runInAWTThread(new Runnable()
  293. {
  294. public void run()
  295. {
  296. setFlag(IO,false);
  297. SegmentBuffer sbuf = (SegmentBuffer)getProperty(
  298. BufferIORequest.LOAD_DATA);
  299. if(sbuf != null)
  300. {
  301. unsetProperty(BufferIORequest.LOAD_DATA);
  302. view.getTextArea().setSelectedText(sbuf.toString());
  303. }
  304. }
  305. });
  306. return true;
  307. } //}}}
  308. //{{{ autosave() method
  309. /**
  310. * Autosaves this buffer.
  311. */
  312. public void autosave()
  313. {
  314. if(autosaveFile == null || !getFlag(AUTOSAVE_DIRTY)
  315. || !getFlag(DIRTY)
  316. || getFlag(LOADING)
  317. || getFlag(IO))
  318. return;
  319. setFlag(AUTOSAVE_DIRTY,false);
  320. VFSManager.runInWorkThread(new BufferIORequest(
  321. BufferIORequest.AUTOSAVE,null,this,null,
  322. VFSManager.getFileVFS(),autosaveFile.getPath()));
  323. } //}}}
  324. //{{{ saveAs() method
  325. /**
  326. * Prompts the user for a file to save this buffer to.
  327. * @param view The view
  328. * @param rename True if the buffer's path should be changed, false
  329. * if only a copy should be saved to the specified filename
  330. * @since jEdit 2.6pre5
  331. */
  332. public boolean saveAs(View view, boolean rename)
  333. {
  334. String[] files = GUIUtilities.showVFSFileDialog(view,path,
  335. VFSBrowser.SAVE_DIALOG,false);
  336. // files[] should have length 1, since the dialog type is
  337. // SAVE_DIALOG
  338. if(files == null)
  339. return false;
  340. return save(view,files[0],rename);
  341. } //}}}
  342. //{{{ save() method
  343. /**
  344. * Saves this buffer to the specified path name, or the current path
  345. * name if it's null.
  346. * @param view The view
  347. * @param path The path name to save the buffer to, or null to use
  348. * the existing path
  349. */
  350. public boolean save(View view, String path)
  351. {
  352. return save(view,path,true);
  353. } //}}}
  354. //{{{ save() method
  355. /**
  356. * Saves this buffer to the specified path name, or the current path
  357. * name if it's null.
  358. * @param view The view
  359. * @param path The path name to save the buffer to, or null to use
  360. * the existing path
  361. * @param rename True if the buffer's path should be changed, false
  362. * if only a copy should be saved to the specified filename
  363. * @since jEdit 2.6pre5
  364. */
  365. public boolean save(final View view, String path, final boolean rename)
  366. {
  367. if(isPerformingIO())
  368. {
  369. GUIUtilities.error(view,"buffer-multiple-io",null);
  370. return false;
  371. }
  372. if(path == null && getFlag(NEW_FILE))
  373. return saveAs(view,rename);
  374. if(path == null && file != null)
  375. {
  376. long newModTime = file.lastModified();
  377. if(newModTime != modTime)
  378. {
  379. Object[] args = { this.path };
  380. int result = GUIUtilities.confirm(view,
  381. "filechanged-save",args,
  382. JOptionPane.YES_NO_OPTION,
  383. JOptionPane.WARNING_MESSAGE);
  384. if(result != JOptionPane.YES_OPTION)
  385. return false;
  386. }
  387. }
  388. setFlag(IO,true);
  389. EditBus.send(new BufferUpdate(this,view,BufferUpdate.SAVING));
  390. if(path == null)
  391. path = this.path;
  392. // can't call setPath() here because we don't want a failed
  393. // 'save as' to change the buffer's path, so obtain the VFS
  394. // instance 'manually'
  395. VFS vfs = VFSManager.getVFSForPath(path);
  396. if(!vfs.save(view,this,path))
  397. {
  398. setFlag(IO,false);
  399. return false;
  400. }
  401. final String oldPath = this.path;
  402. if(rename)
  403. setPath(path);
  404. // Once save is complete, do a few other things
  405. VFSManager.runInAWTThread(new Runnable()
  406. {
  407. public void run()
  408. {
  409. // Saving a NEW_FILE will create a file on
  410. // disk, thus file system browsers must reload
  411. if(getFlag(NEW_FILE) || !getPath().equals(oldPath))
  412. VFSManager.sendVFSUpdate(getVFS(),getPath(),true);
  413. setFlag(IO,false);
  414. if(rename)
  415. {
  416. // we do a write lock so that the
  417. // autosave, which grabs a read lock,
  418. // is not executed between the
  419. // deletion of the autosave file
  420. // and clearing of the dirty flag
  421. try
  422. {
  423. writeLock();
  424. if(autosaveFile != null)
  425. autosaveFile.delete();
  426. setFlag(AUTOSAVE_DIRTY,false);
  427. setFlag(READ_ONLY,false);
  428. setFlag(NEW_FILE,false);
  429. setFlag(UNTITLED,false);
  430. setFlag(DIRTY,false);
  431. // this ensures that undo can clear
  432. // the dirty flag properly when all
  433. // edits up to a save are undone
  434. undoMgr.bufferSaved();
  435. }
  436. finally
  437. {
  438. writeUnlock();
  439. }
  440. parseBufferLocalProperties();
  441. if(!getPath().equals(oldPath))
  442. {
  443. jEdit.updatePosition(Buffer.this);
  444. setMode();
  445. }
  446. else
  447. propertiesChanged();
  448. if(file != null)
  449. modTime = file.lastModified();
  450. EditBus.send(new BufferUpdate(Buffer.this,
  451. view,BufferUpdate.DIRTY_CHANGED));
  452. // new message type introduced in 4.0pre4
  453. EditBus.send(new BufferUpdate(Buffer.this,
  454. view,BufferUpdate.SAVED));
  455. }
  456. }
  457. });
  458. return true;
  459. } //}}}
  460. //{{{ checkModTime() method
  461. /**
  462. * Check if the buffer has changed on disk.
  463. */
  464. public void checkModTime(View view)
  465. {
  466. // don't do these checks while a save is in progress,
  467. // because for a moment newModTime will be greater than
  468. // oldModTime, due to the multithreading
  469. if(file == null || getFlag(NEW_FILE) || getFlag(IO))
  470. return;
  471. boolean newReadOnly = (file.exists() && !file.canWrite());
  472. if(newReadOnly != getFlag(READ_ONLY))
  473. {
  474. setFlag(READ_ONLY,newReadOnly);
  475. EditBus.send(new BufferUpdate(this,
  476. view,BufferUpdate.DIRTY_CHANGED));
  477. }
  478. if(!jEdit.getBooleanProperty("view.checkModStatus"))
  479. return;
  480. long oldModTime = modTime;
  481. long newModTime = file.lastModified();
  482. if(newModTime != oldModTime)
  483. {
  484. modTime = newModTime;
  485. if(!file.exists())
  486. {
  487. setFlag(NEW_FILE,true);
  488. EditBus.send(new BufferUpdate(this,
  489. view,BufferUpdate.DIRTY_CHANGED));
  490. Object[] args = { path };
  491. GUIUtilities.message(view,"filedeleted",args);
  492. return;
  493. }
  494. String prop = (isDirty() ? "filechanged-dirty"
  495. : "filechanged-focus");
  496. Object[] args = { path };
  497. int result = GUIUtilities.confirm(view,
  498. prop,args,JOptionPane.YES_NO_OPTION,
  499. JOptionPane.WARNING_MESSAGE);
  500. if(result == JOptionPane.YES_OPTION)
  501. {
  502. view.getEditPane().saveCaretInfo();
  503. load(view,true);
  504. }
  505. }
  506. } //}}}
  507. //}}}
  508. //{{{ Getters/setter methods for various things
  509. //{{{ getLastModified() method
  510. /**
  511. * Returns the last time jEdit modified the file on disk.
  512. */
  513. public long getLastModified()
  514. {
  515. return modTime;
  516. } //}}}
  517. //{{{ setLastModified() method
  518. /**
  519. * Sets the last time jEdit modified the file on disk.
  520. * @param modTime The new modification time
  521. */
  522. public void setLastModified(long modTime)
  523. {
  524. this.modTime = modTime;
  525. } //}}}
  526. //{{{ getVFS() method
  527. /**
  528. * Returns the virtual filesystem responsible for loading and
  529. * saving this buffer.
  530. */
  531. public VFS getVFS()
  532. {
  533. return vfs;
  534. } //}}}
  535. //{{{ getFile() method
  536. /**
  537. * Returns the file for this buffer. This may be null if the buffer
  538. * is non-local.
  539. */
  540. public final File getFile()
  541. {
  542. return file;
  543. } //}}}
  544. //{{{ getAutosaveFile() method
  545. /**
  546. * Returns the autosave file for this buffer. This may be null if
  547. * the file is non-local.
  548. */
  549. public final File getAutosaveFile()
  550. {
  551. return autosaveFile;
  552. } //}}}
  553. //{{{ getName() method
  554. /**
  555. * Returns the name of this buffer.
  556. */
  557. public final String getName()
  558. {
  559. return name;
  560. } //}}}
  561. //{{{ getPath() method
  562. /**
  563. * Returns the path name of this buffer.
  564. */
  565. public final String getPath()
  566. {
  567. return path;
  568. } //}}}
  569. //{{{ isClosed() method
  570. /**
  571. * Returns true if this buffer has been closed with
  572. * <code>jEdit.closeBuffer()</code>.
  573. */
  574. public final boolean isClosed()
  575. {
  576. return getFlag(CLOSED);
  577. } //}}}
  578. //{{{ isLoaded() method
  579. /**
  580. * Returns true if the buffer is loaded.
  581. */
  582. public final boolean isLoaded()
  583. {
  584. return !getFlag(LOADING);
  585. } //}}}
  586. //{{{ isPerformingIO() method
  587. /**
  588. * Returns true if the buffer is currently performing I/O.
  589. * @since jEdit 2.7pre1
  590. */
  591. public final boolean isPerformingIO()
  592. {
  593. return getFlag(LOADING) || getFlag(IO);
  594. } //}}}
  595. //{{{ isNewFile() method
  596. /**
  597. * Returns true if this file doesn't exist on disk.
  598. */
  599. public final boolean isNewFile()
  600. {
  601. return getFlag(NEW_FILE);
  602. } //}}}
  603. //{{{ setNewFile() method
  604. /**
  605. * Sets the new file flag.
  606. * @param newFile The new file flag
  607. */
  608. public final void setNewFile(boolean newFile)
  609. {
  610. setFlag(NEW_FILE,newFile);
  611. if(!newFile)
  612. setFlag(UNTITLED,false);
  613. } //}}}
  614. //{{{ isUntitled() method
  615. /**
  616. * Returns true if this file is 'untitled'.
  617. */
  618. public final boolean isUntitled()
  619. {
  620. return getFlag(UNTITLED);
  621. } //}}}
  622. //{{{ isDirty() method
  623. /**
  624. * Returns true if this file has changed since last save, false
  625. * otherwise.
  626. */
  627. public final boolean isDirty()
  628. {
  629. return getFlag(DIRTY);
  630. } //}}}
  631. //{{{ isReadOnly() method
  632. /**
  633. * Returns true if this file is read only, false otherwise.
  634. */
  635. public final boolean isReadOnly()
  636. {
  637. return getFlag(READ_ONLY);
  638. } //}}}
  639. //{{{ isEditable() method
  640. /**
  641. * Returns true if this file is editable, false otherwise.
  642. * @since jEdit 2.7pre1
  643. */
  644. public final boolean isEditable()
  645. {
  646. return !(getFlag(READ_ONLY) || getFlag(IO) || getFlag(LOADING));
  647. } //}}}
  648. //{{{ isReadOnly() method
  649. /**
  650. * Sets the read only flag.
  651. * @param readOnly The read only flag
  652. */
  653. public final void setReadOnly(boolean readOnly)
  654. {
  655. setFlag(READ_ONLY,readOnly);
  656. } //}}}
  657. //{{{ setDirty() method
  658. /**
  659. * Sets the `dirty' (changed since last save) flag of this buffer.
  660. */
  661. public void setDirty(boolean d)
  662. {
  663. boolean old_d = getFlag(DIRTY);
  664. if(d)
  665. {
  666. if(getFlag(LOADING) || getFlag(READ_ONLY))
  667. return;
  668. if(getFlag(DIRTY) && getFlag(AUTOSAVE_DIRTY))
  669. return;
  670. setFlag(DIRTY,true);
  671. setFlag(AUTOSAVE_DIRTY,true);
  672. }
  673. else
  674. {
  675. setFlag(DIRTY,false);
  676. setFlag(AUTOSAVE_DIRTY,false);
  677. // this ensures that undo can clear the dirty flag properly
  678. // when all edits up to a save are undone
  679. undoMgr.bufferSaved();
  680. }
  681. if(d != old_d)
  682. {
  683. EditBus.send(new BufferUpdate(this,null,
  684. BufferUpdate.DIRTY_CHANGED));
  685. }
  686. } //}}}
  687. //{{{ isTemporary() method
  688. /**
  689. * Returns if this is a temporary buffer.
  690. * @see jEdit#openTemporary(View,String,String,boolean)
  691. * @see jEdit#commitTemporary(Buffer)
  692. * @since jEdit 2.2pre7
  693. */
  694. public boolean isTemporary()
  695. {
  696. return getFlag(TEMPORARY);
  697. } //}}}
  698. //{{{ getIcon() method
  699. /**
  700. * Returns this buffer's icon.
  701. * @since jEdit 2.6pre6
  702. */
  703. public Icon getIcon()
  704. {
  705. if(getFlag(DIRTY))
  706. return GUIUtilities.DIRTY_BUFFER_ICON;
  707. else if(getFlag(READ_ONLY))
  708. return GUIUtilities.READ_ONLY_BUFFER_ICON;
  709. else if(getFlag(NEW_FILE))
  710. return GUIUtilities.NEW_BUFFER_ICON;
  711. else
  712. return GUIUtilities.NORMAL_BUFFER_ICON;
  713. } //}}}
  714. //}}}
  715. //{{{ Thread safety
  716. //{{{ readLock() method
  717. /**
  718. * The buffer is guaranteed not to change between calls to
  719. * <code>readLock()</code> and <code>readUnlock()</code>.
  720. */
  721. public final void readLock()
  722. {
  723. lock.readLock();
  724. } //}}}
  725. //{{{ readUnlock() method
  726. /**
  727. * The buffer is guaranteed not to change between calls to
  728. * <code>readLock()</code> and <code>readUnlock()</code>.
  729. */
  730. public final void readUnlock()
  731. {
  732. lock.readUnlock();
  733. } //}}}
  734. //{{{ writeLock() method
  735. /**
  736. * The buffer cintents are guaranteed not to be read or written
  737. * by other threads between calls to <code>writeLock()</code>
  738. * and <code>writeUnlock()</code>.
  739. */
  740. public final void writeLock()
  741. {
  742. lock.writeLock();
  743. } //}}}
  744. //{{{ writeUnlock() method
  745. /**
  746. * The buffer cintents are guaranteed not to be read or written
  747. * by other threads between calls to <code>writeLock()</code>
  748. * and <code>writeUnlock()</code>.
  749. */
  750. public final void writeUnlock()
  751. {
  752. lock.writeUnlock();
  753. } //}}}
  754. //}}}
  755. //{{{ Text reading methods
  756. //{{{ getLength() method
  757. /**
  758. * Returns the number of characters in the buffer.
  759. */
  760. public int getLength()
  761. {
  762. // no need to lock since this just returns a value and that's it
  763. return contentMgr.getLength();
  764. } //}}}
  765. //{{{ getLineCount() method
  766. /**
  767. * Returns the number of physical lines in the buffer.
  768. * This method is thread-safe.
  769. * @since jEdit 3.1pre1
  770. */
  771. public int getLineCount()
  772. {
  773. // no need to lock since this just returns a value and that's it
  774. return offsetMgr.getLineCount();
  775. } //}}}
  776. //{{{ getLineOfOffset() method
  777. /**
  778. * Returns the line containing the specified offset.
  779. * This method is thread-safe.
  780. * @param offset The offset
  781. * @since jEdit 4.0pre1
  782. */
  783. public final int getLineOfOffset(int offset)
  784. {
  785. try
  786. {
  787. readLock();
  788. if(offset < 0 || offset > getLength())
  789. throw new ArrayIndexOutOfBoundsException(offset);
  790. return offsetMgr.getLineOfOffset(offset);
  791. }
  792. finally
  793. {
  794. readUnlock();
  795. }
  796. } //}}}
  797. //{{{ getLineStartOffset() method
  798. /**
  799. * Returns the start offset of the specified line.
  800. * This method is thread-safe.
  801. * @param line The line
  802. * @return The start offset of the specified line
  803. * @since jEdit 4.0pre1
  804. */
  805. public int getLineStartOffset(int line)
  806. {
  807. try
  808. {
  809. readLock();
  810. if(line < 0 || line >= offsetMgr.getLineCount())
  811. throw new ArrayIndexOutOfBoundsException(line);
  812. else if(line == 0)
  813. return 0;
  814. return offsetMgr.getLineEndOffset(line - 1);
  815. }
  816. finally
  817. {
  818. readUnlock();
  819. }
  820. } //}}}
  821. //{{{ getLineEndOffset() method
  822. /**
  823. * Returns the end offset of the specified line.
  824. * This method is thread-safe.
  825. * @param line The line
  826. * @return The end offset of the specified line
  827. * invalid.
  828. * @since jEdit 4.0pre1
  829. */
  830. public int getLineEndOffset(int line)
  831. {
  832. try
  833. {
  834. readLock();
  835. if(line < 0 || line >= offsetMgr.getLineCount())
  836. throw new ArrayIndexOutOfBoundsException(line);
  837. return offsetMgr.getLineEndOffset(line);
  838. }
  839. finally
  840. {
  841. readUnlock();
  842. }
  843. } //}}}
  844. //{{{ getLineLength() method
  845. /**
  846. * Returns the length of the specified line.
  847. * This method is thread-safe.
  848. * @param line The line
  849. * @since jEdit 4.0pre1
  850. */
  851. public int getLineLength(int line)
  852. {
  853. try
  854. {
  855. readLock();
  856. return getLineEndOffset(line)
  857. - getLineStartOffset(line) - 1;
  858. }
  859. finally
  860. {
  861. readUnlock();
  862. }
  863. } //}}}
  864. //{{{ getLineText() method
  865. /**
  866. * Returns the text on the specified line.
  867. * This method is thread-safe.
  868. * @param lineIndex The line
  869. * @return The text, or null if the line is invalid
  870. * @since jEdit 4.0pre1
  871. */
  872. public String getLineText(int lineIndex)
  873. {
  874. try
  875. {
  876. readLock();
  877. return getText(getLineStartOffset(lineIndex),
  878. getLineLength(lineIndex));
  879. }
  880. finally
  881. {
  882. readUnlock();
  883. }
  884. } //}}}
  885. //{{{ getLineText() method
  886. /**
  887. * Copies the text on the specified line into a segment.
  888. * This method is thread-safe.
  889. * @param lineIndex The line
  890. * @since jEdit 4.0pre1
  891. */
  892. public void getLineText(int lineIndex, Segment segment)
  893. {
  894. try
  895. {
  896. readLock();
  897. getText(getLineStartOffset(lineIndex),
  898. getLineLength(lineIndex),segment);
  899. }
  900. finally
  901. {
  902. readUnlock();
  903. }
  904. } //}}}
  905. //{{{ getText() method
  906. /**
  907. * Returns the specified text range.
  908. * @param start The start offset
  909. * @param length The number of characters to get
  910. */
  911. public String getText(int start, int length)
  912. {
  913. try
  914. {
  915. readLock();
  916. if(start < 0 || length < 0
  917. || start + length > contentMgr.getLength())
  918. throw new ArrayIndexOutOfBoundsException(start + ":" + length);
  919. return contentMgr.getText(start,length);
  920. }
  921. finally
  922. {
  923. readUnlock();
  924. }
  925. } //}}}
  926. //{{{ getText() method
  927. /**
  928. * Returns the specified text range.
  929. * @param start The start offset
  930. * @param length The number of characters to get
  931. * @param seg The segment to copy the text to
  932. */
  933. public void getText(int start, int length, Segment seg)
  934. {
  935. try
  936. {
  937. readLock();
  938. if(start < 0 || length < 0
  939. || start + length > contentMgr.getLength())
  940. throw new ArrayIndexOutOfBoundsException(start + ":" + length);
  941. contentMgr.getText(start,length,seg);
  942. }
  943. finally
  944. {
  945. readUnlock();
  946. }
  947. } //}}}
  948. //}}}
  949. //{{{ Text writing methods
  950. //{{{ insert() method
  951. /**
  952. * Inserts a string into the buffer.
  953. * @param offset The offset
  954. * @param str The string
  955. * @since jEdit 4.0pre1
  956. */
  957. public void insert(int offset, String str)
  958. {
  959. if(str == null || str.length() == 0)
  960. return;
  961. if(isReadOnly())
  962. throw new RuntimeException("buffer read-only");
  963. try
  964. {
  965. writeLock();
  966. if(offset < 0 || offset > contentMgr.getLength())
  967. throw new ArrayIndexOutOfBoundsException(offset);
  968. contentMgr.insert(offset,str);
  969. integerArray.clear();
  970. for(int i = 0; i < str.length(); i++)
  971. {
  972. if(str.charAt(i) == '\n')
  973. integerArray.add(i);
  974. }
  975. if(!getFlag(UNDO_IN_PROGRESS))
  976. {
  977. undoMgr.contentInserted(offset,str.length(),str,
  978. !getFlag(DIRTY));
  979. }
  980. contentInserted(offset,str.length(),integerArray);
  981. }
  982. finally
  983. {
  984. writeUnlock();
  985. }
  986. } //}}}
  987. //{{{ insert() method
  988. /**
  989. * Inserts a string into the buffer.
  990. * @param offset The offset
  991. * @param seg The segment
  992. * @since jEdit 4.0pre1
  993. */
  994. public void insert(int offset, Segment seg)
  995. {
  996. if(seg.count == 0)
  997. return;
  998. if(isReadOnly())
  999. throw new RuntimeException("buffer read-only");
  1000. try
  1001. {
  1002. writeLock();
  1003. if(offset < 0 || offset > contentMgr.getLength())
  1004. throw new ArrayIndexOutOfBoundsException(offset);
  1005. contentMgr.insert(offset,seg);
  1006. integerArray.clear();
  1007. for(int i = 0; i < seg.count; i++)
  1008. {
  1009. if(seg.array[seg.offset + i] == '\n')
  1010. integerArray.add(i);
  1011. }
  1012. if(!getFlag(UNDO_IN_PROGRESS))
  1013. {
  1014. undoMgr.contentInserted(offset,seg.count,
  1015. seg.toString(),!getFlag(DIRTY));
  1016. }
  1017. contentInserted(offset,seg.count,integerArray);
  1018. }
  1019. finally
  1020. {
  1021. writeUnlock();
  1022. }
  1023. } //}}}
  1024. //{{{ remove() method
  1025. /**
  1026. * Removes the specified rang efrom the buffer.
  1027. * @param offset The start offset
  1028. * @param length The number of characters to remove
  1029. */
  1030. public void remove(int offset, int length)
  1031. {
  1032. if(length == 0)
  1033. return;
  1034. if(isReadOnly())
  1035. throw new RuntimeException("buffer read-only");
  1036. try
  1037. {
  1038. writeLock();
  1039. if(offset < 0 || length < 0
  1040. || offset + length > contentMgr.getLength())
  1041. throw new ArrayIndexOutOfBoundsException(offset + ":" + length);
  1042. int startLine = offsetMgr.getLineOfOffset(offset);
  1043. contentMgr.getText(offset,length,seg);
  1044. int numLines = 0;
  1045. for(int i = 0; i < seg.count; i++)
  1046. {
  1047. if(seg.array[seg.offset + i] == '\n')
  1048. numLines++;
  1049. }
  1050. if(!getFlag(UNDO_IN_PROGRESS))
  1051. {
  1052. undoMgr.contentRemoved(offset,length,
  1053. seg.toString(),!getFlag(DIRTY));
  1054. }
  1055. contentMgr.remove(offset,length);
  1056. if(lastTokenizedLine >= startLine)
  1057. lastTokenizedLine = -1;
  1058. offsetMgr.contentRemoved(startLine,offset,numLines,length);
  1059. if(numLines > 0)
  1060. {
  1061. for(int i = 0; i < inUseFVMs.length; i++)
  1062. {
  1063. if(inUseFVMs[i] != null)
  1064. inUseFVMs[i]._invalidate(startLine);
  1065. }
  1066. }
  1067. fireContentRemoved(startLine,offset,numLines,length);
  1068. setDirty(true);
  1069. }
  1070. finally
  1071. {
  1072. writeUnlock();
  1073. }
  1074. } //}}}
  1075. //{{{ removeTrailingWhiteSpace() method
  1076. /**
  1077. * Removes trailing whitespace from all lines in the specified list.
  1078. * @param list The line numbers
  1079. * @since jEdit 3.2pre1
  1080. */
  1081. public void removeTrailingWhiteSpace(int[] lines)
  1082. {
  1083. try
  1084. {
  1085. beginCompoundEdit();
  1086. for(int i = 0; i < lines.length; i++)
  1087. {
  1088. int pos, lineStart, lineEnd, tail;
  1089. getLineText(lines[i],seg);
  1090. // blank line
  1091. if (seg.count == 0) continue;
  1092. lineStart = seg.offset;
  1093. lineEnd = seg.offset + seg.count - 1;
  1094. for (pos = lineEnd; pos >= lineStart; pos--)
  1095. {
  1096. if (!Character.isWhitespace(seg.array[pos]))
  1097. break;
  1098. }
  1099. tail = lineEnd - pos;
  1100. // no whitespace
  1101. if (tail == 0) continue;
  1102. remove(getLineEndOffset(lines[i]) - 1 - tail,tail);
  1103. }
  1104. }
  1105. finally
  1106. {
  1107. endCompoundEdit();
  1108. }
  1109. } //}}}
  1110. //{{{ shiftIndentLeft() method
  1111. /**
  1112. * Shifts the indent of each line in the specified list to the left.
  1113. * @param lines The line numbers
  1114. * @since jEdit 3.2pre1
  1115. */
  1116. public void shiftIndentLeft(int[] lines)
  1117. {
  1118. int tabSize = getTabSize();
  1119. int indentSize = getIndentSize();
  1120. boolean noTabs = getBooleanProperty("noTabs");
  1121. try
  1122. {
  1123. beginCompoundEdit();
  1124. for(int i = 0; i < lines.length; i++)
  1125. {
  1126. int lineStart = getLineStartOffset(lines[i]);
  1127. String line = getLineText(lines[i]);
  1128. int whiteSpace = MiscUtilities
  1129. .getLeadingWhiteSpace(line);
  1130. if(whiteSpace == 0)
  1131. continue;
  1132. int whiteSpaceWidth = Math.max(0,MiscUtilities
  1133. .getLeadingWhiteSpaceWidth(line,tabSize)
  1134. - indentSize);
  1135. remove(lineStart,whiteSpace);
  1136. insert(lineStart,MiscUtilities
  1137. .createWhiteSpace(whiteSpaceWidth,
  1138. (noTabs ? 0 : tabSize)));
  1139. }
  1140. }
  1141. finally
  1142. {
  1143. endCompoundEdit();
  1144. }
  1145. } //}}}
  1146. //{{{ shiftIndentRight() method
  1147. /**
  1148. * Shifts the indent of each line in the specified list to the right.
  1149. * @param lines The line numbers
  1150. * @since jEdit 3.2pre1
  1151. */
  1152. public void shiftIndentRight(int[] lines)
  1153. {
  1154. try
  1155. {
  1156. beginCompoundEdit();
  1157. int tabSize = getTabSize();
  1158. int indentSize = getIndentSize();
  1159. boolean noTabs = getBooleanProperty("noTabs");
  1160. for(int i = 0; i < lines.length; i++)
  1161. {
  1162. int lineStart = getLineStartOffset(lines[i]);
  1163. String line = getLineText(lines[i]);
  1164. int whiteSpace = MiscUtilities
  1165. .getLeadingWhiteSpace(line);
  1166. // silly usability hack
  1167. //if(lines.length != 1 && whiteSpace == 0)
  1168. // continue;
  1169. int whiteSpaceWidth = MiscUtilities
  1170. .getLeadingWhiteSpaceWidth(
  1171. line,tabSize) + indentSize;
  1172. remove(lineStart,whiteSpace);
  1173. insert(lineStart,MiscUtilities
  1174. .createWhiteSpace(whiteSpaceWidth,
  1175. (noTabs ? 0 : tabSize)));
  1176. }
  1177. }
  1178. finally
  1179. {
  1180. endCompoundEdit();
  1181. }
  1182. } //}}}
  1183. //}}}
  1184. //{{{ Undo
  1185. //{{{ undo() method
  1186. /**
  1187. * Undoes the most recent edit.
  1188. *
  1189. * @since jEdit 4.0pre1
  1190. */
  1191. public void undo(JEditTextArea textArea)
  1192. {
  1193. if(undoMgr == null)
  1194. return;
  1195. if(!isEditable())
  1196. {
  1197. textArea.getToolkit().beep();
  1198. return;
  1199. }
  1200. try
  1201. {
  1202. writeLock();
  1203. setFlag(UNDO_IN_PROGRESS,true);
  1204. if(!undoMgr.undo(textArea))
  1205. textArea.getToolkit().beep();
  1206. }
  1207. finally
  1208. {
  1209. setFlag(UNDO_IN_PROGRESS,false);
  1210. writeUnlock();
  1211. }
  1212. } //}}}
  1213. //{{{ redo() method
  1214. /**
  1215. * Redoes the most recently undone edit. Returns true if the redo was
  1216. * successful.
  1217. *
  1218. * @since jEdit 2.7pre2
  1219. */
  1220. public void redo(JEditTextArea textArea)
  1221. {
  1222. if(undoMgr == null)
  1223. return;
  1224. if(!isEditable())
  1225. {
  1226. Toolkit.getDefaultToolkit().beep();
  1227. return;
  1228. }
  1229. try
  1230. {
  1231. writeLock();
  1232. setFlag(UNDO_IN_PROGRESS,true);
  1233. if(!undoMgr.redo(textArea))
  1234. textArea.getToolkit().beep();
  1235. }
  1236. finally
  1237. {
  1238. setFlag(UNDO_IN_PROGRESS,false);
  1239. writeUnlock();
  1240. }
  1241. } //}}}
  1242. //{{{ beginCompoundEdit() method
  1243. /**
  1244. * Starts a compound edit. All edits from now on until
  1245. * <code>endCompoundEdit()</code> are called will be merged
  1246. * into one. This can be used to make a complex operation
  1247. * undoable in one step. Nested calls to
  1248. * <code>beginCompoundEdit()</code> behave as expected,
  1249. * requiring the same number of <code>endCompoundEdit()</code>
  1250. * calls to end the edit.
  1251. * @see #endCompoundEdit()
  1252. */
  1253. public void beginCompoundEdit()
  1254. {
  1255. if(getFlag(TEMPORARY))
  1256. return;
  1257. try
  1258. {
  1259. writeLock();
  1260. undoMgr.beginCompoundEdit();
  1261. }
  1262. finally
  1263. {
  1264. writeUnlock();
  1265. }
  1266. } //}}}
  1267. //{{{ endCompoundEdit() method
  1268. /**
  1269. * Ends a compound edit. All edits performed since
  1270. * <code>beginCompoundEdit()</code> was called can now
  1271. * be undone in one step by calling <code>undo()</code>.
  1272. * @see #beginCompoundEdit()
  1273. */
  1274. public void endCompoundEdit()
  1275. {
  1276. if(getFlag(TEMPORARY))
  1277. return;
  1278. try
  1279. {
  1280. writeLock();
  1281. undoMgr.endCompoundEdit();
  1282. }
  1283. finally
  1284. {
  1285. writeUnlock();
  1286. }
  1287. }//}}}
  1288. //{{{ insideCompoundEdit() method
  1289. /**
  1290. * Returns if a compound edit is currently active.
  1291. * @since jEdit 3.1pre1
  1292. */
  1293. public boolean insideCompoundEdit()
  1294. {
  1295. return undoMgr.insideCompoundEdit();
  1296. } //}}}
  1297. //}}}
  1298. //{{{ Buffer events
  1299. //{{{ addBufferChangeListener() method
  1300. /**
  1301. * Adds a buffer change listener.
  1302. * @param listener The listener
  1303. * @since jEdit 4.0pre1
  1304. */
  1305. public void addBufferChangeListener(BufferChangeListener l)
  1306. {
  1307. bufferListeners.addElement(l);
  1308. } //}}}
  1309. //{{{ removeBufferChangeListener() method
  1310. /**
  1311. * Removes a buffer change listener.
  1312. * @param listener The listener
  1313. * @since jEdit 4.0pre1
  1314. */
  1315. public void removeBufferChangeListener(BufferChangeListener l)
  1316. {
  1317. bufferListeners.removeElement(l);
  1318. } //}}}
  1319. //}}}
  1320. //{{{ Property methods
  1321. //{{{ propertiesChanged() method
  1322. /**
  1323. * Reloads settings from the properties. This should be called
  1324. * after the <code>syntax</code> or <code>folding</code>
  1325. * buffer-local properties are changed.
  1326. */
  1327. public void propertiesChanged()
  1328. {
  1329. parseFully = jEdit.getBooleanProperty("parseFully");
  1330. setTokenMarker(mode.getTokenMarker());
  1331. String folding = getStringProperty("folding");
  1332. if("explicit".equals(folding))
  1333. setFoldHandler(new ExplicitFoldHandler());
  1334. else if("indent".equals(folding))
  1335. setFoldHandler(new IndentFoldHandler());
  1336. else
  1337. setFoldHandler(new DummyFoldHandler());
  1338. } //}}}
  1339. //{{{ getTabSize() method
  1340. /**
  1341. * Returns the tab size used in this buffer. This is equivalent
  1342. * to calling getProperty("tabSize").
  1343. */
  1344. public int getTabSize()
  1345. {
  1346. return getIntegerProperty("tabSize",8);
  1347. } //}}}
  1348. //{{{ getIndentSize() method
  1349. /**
  1350. * Returns the indent size used in this buffer. This is equivalent
  1351. * to calling getProperty("indentSize").
  1352. * @since jEdit 2.7pre1
  1353. */
  1354. public final int getIndentSize()
  1355. {
  1356. return getIntegerProperty("indentSize",8);
  1357. } //}}}
  1358. //{{{ getProperty() method
  1359. /**
  1360. * Returns the value of a buffer-local property.
  1361. * @param name The property name. For backwards compatibility, this
  1362. * is an <code>Object</code>, not a <code>String</code>.
  1363. */
  1364. public Object getProperty(Object name)
  1365. {
  1366. // First try the buffer-local properties
  1367. Object o = properties.get(name);
  1368. if(o != null)
  1369. return o;
  1370. // For backwards compatibility
  1371. if(!(name instanceof String))
  1372. return null;
  1373. // Now try mode.<mode>.<property>
  1374. if(mode != null)
  1375. return mode.getProperty((String)name);
  1376. else
  1377. {
  1378. // Now try buffer.<property>
  1379. String value = jEdit.getProperty("buffer." + name);
  1380. if(value == null)
  1381. return null;
  1382. // Try returning it as an integer first
  1383. try
  1384. {
  1385. return new Integer(value);
  1386. }
  1387. catch(NumberFormatException nf)
  1388. {
  1389. return value;
  1390. }
  1391. }
  1392. } //}}}
  1393. //{{{ setProperty() method
  1394. /**
  1395. * Sets the value of a buffer-local property.
  1396. * @param name The property name
  1397. * @param value The property value
  1398. * @since jEdit 4.0pre1
  1399. */
  1400. public void setProperty(String name, Object value)
  1401. {
  1402. putProperty(name,value);
  1403. } //}}}
  1404. //{{{ unsetProperty() method
  1405. /**
  1406. * Clears the value of a buffer-local property.
  1407. * @param name The property name
  1408. * @since jEdit 4.0pre1
  1409. */
  1410. public void unsetProperty(String name)
  1411. {
  1412. properties.remove(name);
  1413. } //}}}
  1414. //{{{ getStringProperty() method
  1415. /**
  1416. * Returns the value of a string property.
  1417. * @param name The property name
  1418. * @since jEdit 4.0pre1
  1419. */
  1420. public String getStringProperty(String name)
  1421. {
  1422. Object obj = getProperty(name);
  1423. if(obj != null)
  1424. return obj.toString();
  1425. else
  1426. return null;
  1427. } //}}}
  1428. //{{{ setStringProperty() method
  1429. /**
  1430. * Sets a string property.
  1431. * @param name The property name
  1432. * @param value The value
  1433. * @since jEdit 4.0pre1
  1434. */
  1435. public void setStringProperty(String name, String value)
  1436. {
  1437. setProperty(name,value);
  1438. } //}}}
  1439. //{{{ getBooleanProperty() method
  1440. /**
  1441. * Returns the value of a boolean property.
  1442. * @param name The property name
  1443. * @since jEdit 4.0pre1
  1444. */
  1445. public boolean getBooleanProperty(String name)
  1446. {
  1447. Object obj = getProperty(name);
  1448. if(obj instanceof Boolean)
  1449. return ((Boolean)obj).booleanValue();
  1450. else if("true".equals(obj) || "on".equals(obj) || "yes".equals(obj))
  1451. return true;
  1452. else
  1453. return false;
  1454. } //}}}
  1455. //{{{ setBooleanProperty() method
  1456. /**
  1457. * Sets a boolean property.
  1458. * @param name The property name
  1459. * @param value The value
  1460. * @since jEdit 4.0pre1
  1461. */
  1462. public void setBooleanProperty(String name, boolean value)
  1463. {
  1464. setProperty(name,value ? Boolean.TRUE : Boolean.FALSE);
  1465. } //}}}
  1466. //{{{ getIntegerProperty() method
  1467. /**
  1468. * Returns the value of an integer property.
  1469. * @param name The property name
  1470. * @since jEdit 4.0pre1
  1471. */
  1472. public int getIntegerProperty(String name, int defaultValue)
  1473. {
  1474. Object obj = getProperty(name);
  1475. if(obj instanceof Number)
  1476. return ((Number)obj).intValue();
  1477. else
  1478. {
  1479. try
  1480. {
  1481. int value = Integer.parseInt(getStringProperty(name));
  1482. properties.put(name,new Integer(value));
  1483. return value;
  1484. }
  1485. catch(Exception e)
  1486. {
  1487. return defaultValue;
  1488. }
  1489. }
  1490. } //}}}
  1491. //{{{ setIntegerProperty() method
  1492. /**
  1493. * Sets an integer property.
  1494. * @param name The property name
  1495. * @param value The value
  1496. * @since jEdit 4.0pre1
  1497. */
  1498. public void setIntegerProperty(String name, int value)
  1499. {
  1500. setProperty(name,new Integer(value));
  1501. } //}}}
  1502. //{{{ getKeywordMapAtOffset() method
  1503. /**
  1504. * Returns the syntax highlighting keyword map in effect at the
  1505. * specified offset. Used by the <b>Complete Word</b> command to
  1506. * complete keywords.
  1507. * @param offset The offset
  1508. * @since jEdit 4.0pre3
  1509. */
  1510. public KeywordMap getKeywordMapAtOffset(int offset)
  1511. {
  1512. return getRuleSetAtOffset(offset).getKeywords();
  1513. } //}}}
  1514. //{{{ getContextSensitiveProperty() method
  1515. /**
  1516. * Some settings, like comment start and end strings, can
  1517. * vary between different parts of a buffer (HTML text and inline
  1518. * JavaScript, for example).
  1519. * @param offset The offset
  1520. * @param name The property name
  1521. * @since jEdit 4.0pre3
  1522. */
  1523. public String getContextSensitiveProperty(int offset, String name)
  1524. {
  1525. ParserRuleSet rules = getRuleSetAtOffset(offset);
  1526. Object value = null;
  1527. Hashtable rulesetProps = rules.getProperties();
  1528. if(rulesetProps != null)
  1529. value = rulesetProps.get(name);
  1530. if(value == null)
  1531. {
  1532. value = rules.getMode().getProperty(name);
  1533. if(value == null)
  1534. value = mode.getProperty(name);
  1535. }
  1536. if(value == null)
  1537. return null;
  1538. else
  1539. return String.valueOf(value);
  1540. } //}}}
  1541. //}}}
  1542. //{{{ Edit modes, syntax highlighting, auto indent
  1543. //{{{ getMode() method
  1544. /**
  1545. * Returns this buffer's edit mode.
  1546. */
  1547. public final Mode getMode()
  1548. {
  1549. return mode;
  1550. } //}}}
  1551. //{{{ setMode() method
  1552. /**
  1553. * Sets this buffer's edit mode. Note that calling this before a buffer
  1554. * is loaded will have no effect; in that case, set the "mode" property
  1555. * to the name of the mode. A bit inelegant, I know...
  1556. * @param mode The mode
  1557. */
  1558. public void setMode(Mode mode)
  1559. {
  1560. /* This protects against stupid people (like me)
  1561. * doing stuff like buffer.setMode(jEdit.getMode(...)); */
  1562. if(mode == null)
  1563. throw new NullPointerException("Mode must be non-null");
  1564. // still need to set up new fold handler, etc even if mode not
  1565. // changed.
  1566. //if(this.mode == mode)
  1567. // return;
  1568. //{{{ Reset cached properties
  1569. if(getProperty("tabSize")
  1570. .equals(mode.getProperty("tabSize")))
  1571. unsetProperty("tabSize");
  1572. if(getProperty("indentSize")
  1573. .equals(mode.getProperty("indentSize")))
  1574. unsetProperty("indentSize");
  1575. if(getProperty("maxLineLen")
  1576. .equals(mode.getProperty("maxLineLen")))
  1577. unsetProperty("maxLineLen");
  1578. //}}}
  1579. Mode oldMode = this.mode;
  1580. this.mode = mode;
  1581. //{{{ Cache these for improved performance
  1582. putProperty("tabSize",getProperty("tabSize"));
  1583. putProperty("indentSize",getProperty("indentSize"));
  1584. putProperty("maxLineLen",getProperty("maxLineLen"));
  1585. //}}}
  1586. propertiesChanged(); // sets up token marker
  1587. // don't fire it for initial mode set
  1588. if(oldMode != null && !getFlag(TEMPORARY))
  1589. {
  1590. EditBus.send(new BufferUpdate(this,null,
  1591. BufferUpdate.MODE_CHANGED));
  1592. }
  1593. } //}}}
  1594. //{{{ setMode() method
  1595. /**
  1596. * Sets this buffer's edit mode by calling the accept() method
  1597. * of each registered edit mode.
  1598. */
  1599. public void setMode()
  1600. {
  1601. String userMode = getStringProperty("mode");
  1602. if(userMode != null)
  1603. {
  1604. Mode m = jEdit.getMode(userMode);
  1605. if(m != null)
  1606. {
  1607. setMode(m);
  1608. return;
  1609. }
  1610. }
  1611. String nogzName = name.substring(0,name.length() -
  1612. (name.endsWith(".gz") ? 3 : 0));
  1613. Mode[] modes = jEdit.getModes();
  1614. String firstLine = getLineText(0);
  1615. for(int i = 0; i < modes.length; i++)
  1616. {
  1617. if(modes[i].accept(nogzName,firstLine))
  1618. {
  1619. setMode(modes[i]);
  1620. return;
  1621. }
  1622. }
  1623. Mode defaultMode = jEdit.getMode(jEdit.getProperty("buffer.defaultMode"));
  1624. if(defaultMode == null)
  1625. defaultMode = jEdit.getMode("text");
  1626. setMode(defaultMode);
  1627. } //}}}
  1628. //{{{ indentLine() method
  1629. /**
  1630. * If auto indent is enabled, this method is called when the `Tab'
  1631. * or `Enter' key is pressed to perform mode-specific indentation
  1632. * and return true, or return false if a normal tab is to be inserted.
  1633. * @param line The line number to indent
  1634. * @param canIncreaseIndent If false, nothing will be done if the
  1635. * calculated indent is greater than the current
  1636. * @param canDecreaseIndent If false, nothing will be done if the
  1637. * calculated indent is less than the current
  1638. * @return true if the tab key event should be swallowed (ignored)
  1639. * false if a real tab should be inserted
  1640. */
  1641. public boolean indentLine(int lineIndex, boolean canIncreaseIndent,
  1642. boolean canDecreaseIndent)
  1643. {
  1644. if(lineIndex == 0)
  1645. return false;
  1646. // Get properties
  1647. String openBrackets = (String)getProperty("indentOpenBrackets");
  1648. String closeBrackets = (String)getProperty("indentCloseBrackets");
  1649. String _indentPrevLine = (String)getProperty("indentPrevLine");
  1650. boolean doubleBracketIndent = getBooleanProperty("doubleBracketIndent");
  1651. RE indentPrevLineRE = null;
  1652. if(openBrackets == null)
  1653. openBrackets = "";
  1654. if(closeBrackets == null)
  1655. closeBrackets = "";
  1656. if(_indentPrevLine != null)
  1657. {
  1658. try
  1659. {
  1660. indentPrevLineRE = new RE(_indentPrevLine,
  1661. RE.REG_ICASE,RESearchMatcher.RE_SYNTAX_JEDIT);
  1662. }
  1663. catch(REException re)
  1664. {
  1665. Log.log(Log.ERROR,this,"Invalid 'indentPrevLine'"
  1666. + " regexp: " + _indentPrevLine);
  1667. Log.log(Log.ERROR,this,re);
  1668. }
  1669. }
  1670. int tabSize = getTabSize();
  1671. int indentSize = getIndentSize();
  1672. boolean noTabs = getBooleanProperty("noTabs");
  1673. Element map = getDefaultRootElement();
  1674. String prevLine = null;
  1675. String line = null;
  1676. Element lineElement = map.getElement(lineIndex);
  1677. int start = lineElement.getStartOffset();
  1678. // Get line text
  1679. line = getText(start,lineElement.getEndOffset() - start - 1);
  1680. for(int i = lineIndex - 1; i >= 0; i--)
  1681. {
  1682. lineElement = map.getElement(i);
  1683. int lineStart = lineElement.getStartOffset();
  1684. int len = lineElement.getEndOffset() - lineStart - 1;
  1685. if(len != 0)
  1686. {
  1687. prevLine = getText(lineStart,len);
  1688. break;
  1689. }
  1690. }
  1691. if(prevLine == null)
  1692. return false;
  1693. /*
  1694. * If 'prevLineIndent' matches a line --> +1
  1695. */
  1696. boolean prevLineMatches = (indentPrevLineRE == null ? false
  1697. : indentPrevLineRE.isMatch(prevLine));
  1698. /*
  1699. * On the previous line,
  1700. * if(bob) { --> +1
  1701. * if(bob) { } --> 0
  1702. * } else if(bob) { --> +1
  1703. */
  1704. boolean prevLineStart = true; // False after initial indent
  1705. int prevLineIndent = 0; // Indent width (tab expanded)
  1706. int prevLineBrackets = 0; // Additional bracket indent
  1707. for(int i = 0; i < prevLine.length(); i++)
  1708. {
  1709. char c = prevLine.charAt(i);
  1710. switch(c)
  1711. {
  1712. case ' ':
  1713. if(prevLineStart)
  1714. prevLineIndent++;
  1715. break;
  1716. case '\t':
  1717. if(prevLineStart)
  1718. {
  1719. prevLineIndent += (tabSize
  1720. - (prevLineIndent
  1721. % tabSize));
  1722. }
  1723. break;
  1724. default:
  1725. prevLineStart = false;
  1726. if(closeBrackets.indexOf(c) != -1)
  1727. prevLineBrackets = Math.max(
  1728. prevLineBrackets-1,0);
  1729. else if(openBrackets.indexOf(c) != -1)
  1730. {
  1731. /*
  1732. * If supressBracketAfterIndent is true
  1733. * and we have something that looks like:
  1734. * if(bob)
  1735. * {
  1736. * then the 'if' will not shift the indent,
  1737. * because of the {.
  1738. *
  1739. * If supressBracketAfterIndent is false,
  1740. * the above would be indented like:
  1741. * if(bob)
  1742. * {
  1743. */
  1744. if(!doubleBracketIndent)
  1745. prevLineMatches = false;
  1746. prevLineBrackets++;
  1747. }
  1748. break;
  1749. }
  1750. }
  1751. // This is a hack so that auto indent does not go haywire
  1752. // with explicit folding. Proper fix will be done later,
  1753. // when the auto indent is rewritten.
  1754. if(prevLineBrackets == 3)
  1755. prevLineBrackets = 0;
  1756. /*
  1757. * On the current line,
  1758. * } --> -1
  1759. * } else if(bob) { --> -1
  1760. * if(bob) { } --> 0
  1761. */
  1762. boolean lineStart = true; // False after initial indent
  1763. int lineIndent = 0; // Indent width (tab expanded)
  1764. int lineWidth = 0; // White space count
  1765. int lineBrackets = 0; // Additional bracket indent
  1766. int closeBracketIndex = -1; // For lining up closing
  1767. // and opening brackets
  1768. for(int i = 0; i < line.length(); i++)
  1769. {
  1770. char c = line.charAt(i);
  1771. switch(c)
  1772. {
  1773. case ' ':
  1774. if(lineStart)
  1775. {
  1776. lineIndent++;
  1777. lineWidth++;
  1778. }
  1779. break;
  1780. case '\t':
  1781. if(lineStart)
  1782. {
  1783. lineIndent += (tabSize
  1784. - (lineIndent
  1785. % tabSize));
  1786. lineWidth++;
  1787. }
  1788. break;
  1789. default:
  1790. lineStart = false;
  1791. if(closeBrackets.indexOf(c) != -1)
  1792. {
  1793. if(lineBrackets == 0)
  1794. closeBracketIndex = i;
  1795. else
  1796. lineBrackets--;
  1797. }
  1798. else if(openBrackets.indexOf(c) != -1)
  1799. {
  1800. if(!doubleBracketIndent)
  1801. prevLineMatches = false;
  1802. lineBrackets++;
  1803. }
  1804. break;
  1805. }
  1806. }
  1807. // This is a hack so that auto indent does not go haywire
  1808. // with explicit folding. Proper fix will be done later,
  1809. // when the auto indent is rewritten.
  1810. if(lineBrackets == 3)
  1811. {
  1812. closeBracketIndex = -1;
  1813. lineBrackets = 0;
  1814. }
  1815. if(closeBracketIndex != -1)
  1816. {
  1817. int offset = TextUtilities.findMatchingBracket(
  1818. this,lineIndex,closeBracketIndex);
  1819. if(offset != -1)
  1820. {
  1821. lineElement = map.getElement(map.getElementIndex(
  1822. offset));
  1823. int startOffset = lineElement.getStartOffset();
  1824. String closeLine = getText(startOffset,
  1825. lineElement.getEndOffset() - startOffset - 1);
  1826. prevLineIndent = MiscUtilities
  1827. .getLeadingWhiteSpaceWidth(
  1828. closeLine,tabSize);
  1829. }
  1830. else
  1831. return false;
  1832. }
  1833. else
  1834. {
  1835. prevLineIndent += (prevLineBrackets * indentSize);
  1836. }
  1837. if(prevLineMatches)
  1838. prevLineIndent += indentSize;
  1839. if(!canDecreaseIndent && prevLineIndent <= lineIndent)
  1840. return false;
  1841. if(!canIncreaseIndent && prevLineIndent >= lineIndent)
  1842. return false;
  1843. // Do it
  1844. try
  1845. {
  1846. beginCompoundEdit();
  1847. remove(start,lineWidth);
  1848. insert(start,MiscUtilities.createWhiteSpace(
  1849. prevLineIndent,(noTabs ? 0 : tabSize)));
  1850. }
  1851. finally
  1852. {
  1853. endCompoundEdit();
  1854. }
  1855. return true;
  1856. } //}}}
  1857. //{{{ indentLines() method
  1858. /**
  1859. * Indents all specified lines.
  1860. * @param start The first line to indent
  1861. * @param end The last line to indent
  1862. * @since jEdit 3.1pre3
  1863. */
  1864. public void indentLines(int start, int end)
  1865. {
  1866. try
  1867. {
  1868. beginCompoundEdit();
  1869. for(int i = start; i <= end; i++)
  1870. indentLine(i,true,true);
  1871. }
  1872. finally
  1873. {
  1874. endCompoundEdit();
  1875. }
  1876. } //}}}
  1877. //{{{ indentLines() method
  1878. /**
  1879. * Indents all specified lines.
  1880. * @param lines The line numbers
  1881. * @since jEdit 3.2pre1
  1882. */
  1883. public void indentLines(int[] lines)
  1884. {
  1885. try
  1886. {
  1887. beginCompoundEdit();
  1888. for(int i = 0; i < lines.length; i++)
  1889. indentLine(lines[i],true,true);
  1890. }
  1891. finally
  1892. {
  1893. endCompoundEdit();
  1894. }
  1895. } //}}}
  1896. //{{{ markTokens() method
  1897. /**
  1898. * Returns the syntax tokens for the specified line.
  1899. * @param lineIndex The line number
  1900. * @since jEdit 4.0pre1
  1901. */
  1902. public TokenList markTokens(int lineIndex)
  1903. {
  1904. try
  1905. {
  1906. writeLock();
  1907. if(lineIndex < 0 || lineIndex >= offsetMgr.getLineCount())
  1908. throw new ArrayIndexOutOfBoundsException(lineIndex);
  1909. /* If cached tokens are valid, return 'em */
  1910. if(lastTokenizedLine == lineIndex)
  1911. return tokenList;
  1912. /*
  1913. * Else, go up back, looking for a line with
  1914. * a valid line context.
  1915. */
  1916. int start, end;
  1917. if(parseFully)
  1918. {
  1919. start = -1;
  1920. end = 0;
  1921. }
  1922. else
  1923. {
  1924. start = Math.max(0,lineIndex - 100) - 1;
  1925. end = Math.max(0,lineIndex - 100);
  1926. }
  1927. for(int i = lineIndex - 1; i > end; i--)
  1928. {
  1929. if(offsetMgr.isLineContextValid(i))
  1930. {
  1931. start = i;
  1932. break;
  1933. }
  1934. }
  1935. for(int i = start + 1; i <= lineIndex; i++)
  1936. {
  1937. getLineText(i,seg);
  1938. TokenMarker.LineContext prevContext = (i == 0 ? null
  1939. : offsetMgr.getLineContext(i - 1));
  1940. /* Prepare tokenization */
  1941. tokenList.lastToken = null;
  1942. TokenMarker.LineContext context = offsetMgr.getLineContext(i);
  1943. ParserRule oldRule;
  1944. ParserRuleSet oldRules;
  1945. if(context == null)
  1946. {
  1947. oldRule = null;
  1948. oldRules = null;
  1949. }
  1950. else
  1951. {
  1952. oldRule = context.inRule;
  1953. oldRules = context.rules;
  1954. }
  1955. context = tokenMarker.markTokens(prevContext,tokenList,seg);
  1956. offsetMgr.setLineContext(i,context);
  1957. // Could incorrectly be set to 'false' with
  1958. // recursive delegates, where the chaining might
  1959. // have changed but not the rule set in question (?)
  1960. if(oldRule != context.inRule)
  1961. nextLineRequested = true;
  1962. else if(oldRules != context.rules)
  1963. nextLineRequested = true;
  1964. //else if(i != lastTokenizedLine)
  1965. // nextLineRequested = false;
  1966. }
  1967. lastTokenizedLine = lineIndex;
  1968. int lineCount = offsetMgr.getLineCount();
  1969. if(nextLineRequested && lineCount - lineIndex > 1)
  1970. {
  1971. offsetMgr.linesChanged(lineIndex + 1,lineCount - lineIndex - 1);
  1972. }
  1973. return tokenList;
  1974. }
  1975. finally
  1976. {
  1977. writeUnlock();
  1978. }
  1979. } //}}}
  1980. //{{{ isNextLineRequested() method
  1981. /**
  1982. * Returns true if the next line should be repainted. This
  1983. * will return true after a line has been tokenized that starts
  1984. * a multiline token that continues onto the next line.
  1985. */
  1986. public boolean isNextLineRequested()
  1987. {
  1988. boolean retVal = nextLineRequested;
  1989. nextLineRequested = false;
  1990. return retVal;
  1991. } //}}}
  1992. //{{{ getTokenMarker() method
  1993. /**
  1994. * This method is only public so that the <code>OffsetManager</code>
  1995. * class can use it.
  1996. * @since jEdit 4.0pre1
  1997. */
  1998. public TokenMarker getTokenMarker()
  1999. {
  2000. return tokenMarker;
  2001. } //}}}
  2002. //}}}
  2003. //{{{ Deprecated methods
  2004. //{{{ addDocumentListener() method
  2005. /**
  2006. * @deprecated Write a <code>BufferChangeListener</code> instead
  2007. */
  2008. public void addDocumentListener(DocumentListener l)
  2009. {
  2010. Log.log(Log.WARNING,this,"Document listeners not supported: "
  2011. + l.getClass().getName());
  2012. } //}}}
  2013. //{{{ removeDocumentListener() method
  2014. /**
  2015. * @deprecated Write a <code>BufferChangeListener</code> instead
  2016. */
  2017. public void removeDocumentListener(DocumentListener l)
  2018. {
  2019. Log.log(Log.WARNING,this,"Document listeners not supported: "
  2020. + l.getClass().getName());
  2021. } //}}}
  2022. //{{{ putProperty() method
  2023. /**
  2024. * @deprecated Call <code>setProperty()</code> instead.
  2025. */
  2026. public void putProperty(Object name, Object value)
  2027. {
  2028. if(value == null)
  2029. properties.remove(name);
  2030. else
  2031. properties.put(name,value);
  2032. } //}}}
  2033. //{{{ putBooleanProperty() method
  2034. /**
  2035. * @deprecated Call <code>setBooleanProperty()</code> instead
  2036. */
  2037. public void putBooleanProperty(String name, boolean value)
  2038. {
  2039. setBooleanProperty(name,value);
  2040. } //}}}
  2041. //{{{ isSaving() method
  2042. /**
  2043. * @deprecated Call isPerformingIO() instead
  2044. */
  2045. public final boolean isSaving()
  2046. {
  2047. return getFlag(IO);
  2048. } //}}}
  2049. //{{{ tokenizeLines() method
  2050. /**
  2051. * @deprecated Don't call this method.
  2052. */
  2053. public void tokenizeLines() {} //}}}
  2054. //{{{ tokenizeLines() method
  2055. /**
  2056. * @deprecated
  2057. */
  2058. public void tokenizeLines(int start, int len)
  2059. {
  2060. } //}}}
  2061. //{{{ isLineVisible() method
  2062. /**
  2063. * @deprecated Fold visibility is now stored on a per-text area
  2064. * basis. Call <code>textArea.getFoldVisibilityManager()</code>
  2065. * to get a visibility manager, and call the
  2066. * <code>isLineVisible()</code> method on that object instead.
  2067. */
  2068. public boolean isLineVisible(int line)
  2069. {
  2070. return true;
  2071. } //}}}
  2072. //{{{ getVirtualLineCount() method
  2073. /**
  2074. * @deprecated
  2075. */
  2076. public int getVirtualLineCount()
  2077. {
  2078. return offsetMgr.getLineCount();
  2079. } //}}}
  2080. //{{{ getPrevVisibleLine() method
  2081. /**
  2082. * @deprecated Fold visibility is now stored on a per-text area
  2083. * basis. Call <code>textArea.getFoldVisibilityManager()</code>
  2084. * to get a visibility manager, and call the
  2085. * <code>getPrevVisibleLine()</code> method on that object instead.
  2086. */
  2087. public int getPrevVisibleLine(int lineNo)
  2088. {
  2089. return lineNo - 1;
  2090. } //}}}
  2091. //{{{ getNextVisibleLine() method
  2092. /**
  2093. * @deprecated Fold visibility is now stored on a per-text area
  2094. * basis. Call <code>textArea.getFoldVisibilityManager()</code>
  2095. * to get a visibility manager, and call the
  2096. * <code>getNextVisibleLine()</code> method on that object instead.
  2097. */
  2098. public int getNextVisibleLine(int lineNo)
  2099. {
  2100. if(lineNo >= offsetMgr.getLineCount() - 1)
  2101. return -1;
  2102. else
  2103. return lineNo + 1;
  2104. } //}}}
  2105. //{{{ virtualToPhysical() method
  2106. /**
  2107. * @deprecated Fold visibility is now stored on a per-text area
  2108. * basis. Call <code>textArea.getFoldVisibilityManager()</code>
  2109. * to get a visibility manager, and call the
  2110. * <code>virtualToPhysical()</code> method on that object instead.
  2111. */
  2112. public int virtualToPhysical(int lineNo)
  2113. {
  2114. return lineNo;
  2115. } //}}}
  2116. //{{{ physicalToVirtual() method
  2117. /**
  2118. * @deprecated Fold visibility is now stored on a per-text area
  2119. * basis. Call <code>textArea.getFoldVisibilityManager()</code>
  2120. * to get a visibility manager, and call the
  2121. * <code>physicalToVirtual()</code> method on that object instead.
  2122. */
  2123. public int physicalToVirtual(int lineNo)
  2124. {
  2125. return lineNo;
  2126. } //}}}
  2127. //{{{ getRootElements() method
  2128. /**
  2129. * @deprecated
  2130. */
  2131. public Element[] getRootElements()
  2132. {
  2133. return new Element[] { getDefaultRootElement() };
  2134. } //}}}
  2135. //{{{ getParagraphElement() method
  2136. /**
  2137. * @deprecated
  2138. */
  2139. public Element getParagraphElement(int offset)
  2140. {
  2141. return new LineElement(this,getLineOfOffset(offset));
  2142. } //}}}
  2143. //{{{ getDefaultRootElement() method
  2144. /**
  2145. * @deprecated Use <code>getLineOfOffset()</code>,
  2146. * <code>getLineStartOffset()</code>, and
  2147. * <code>getLineEndOffset()</code> instead.
  2148. */
  2149. public Element getDefaultRootElement()
  2150. {
  2151. return new RootElement(this);
  2152. } //}}}
  2153. //{{{ insertString() method
  2154. /**
  2155. * @deprecated Call <code>insert()</code> instead.
  2156. */
  2157. public void insertString(int offset, String str, AttributeSet attr)
  2158. {
  2159. insert(offset,str);
  2160. } //}}}
  2161. //}}}
  2162. //{{{ Folding methods
  2163. //{{{ isFoldStart() method
  2164. /**
  2165. * Returns if the specified line begins a fold.
  2166. * @since jEdit 3.1pre1
  2167. */
  2168. public boolean isFoldStart(int line)
  2169. {
  2170. return (line != getLineCount() - 1
  2171. && getFoldLevel(line) < getFoldLevel(line + 1));
  2172. } //}}}
  2173. //{{{ getFoldLevel() method
  2174. /**
  2175. * Returns the fold level of the specified line.
  2176. * @param line A physical line index
  2177. * @since jEdit 3.1pre1
  2178. */
  2179. public int getFoldLevel(int line)
  2180. {
  2181. try
  2182. {
  2183. writeLock();
  2184. if(line < 0 || line >= offsetMgr.getLineCount())
  2185. throw new ArrayIndexOutOfBoundsException(line);
  2186. if(offsetMgr.isFoldLevelValid(line))
  2187. {
  2188. //System.err.println("level valid: " + line + ":"
  2189. // + offsetMgr.getFoldLevel(line));
  2190. return offsetMgr.getFoldLevel(line);
  2191. }
  2192. else
  2193. {
  2194. int start = 0;
  2195. for(int i = line - 1; i >= 0; i--)
  2196. {
  2197. if(offsetMgr.isFoldLevelValid(i))
  2198. {
  2199. start = i + 1;
  2200. break;
  2201. }
  2202. }
  2203. int newFoldLevel = 0;
  2204. boolean changed = false;
  2205. for(int i = start; i <= line; i++)
  2206. {
  2207. newFoldLevel = foldHandler.getFoldLevel(this,i,seg);
  2208. offsetMgr.setFoldLevel(i,newFoldLevel);
  2209. if(newFoldLevel != offsetMgr.getFoldLevel(i))
  2210. changed = true;
  2211. }
  2212. if(changed && !getFlag(INSIDE_INSERT))
  2213. {
  2214. //System.err.println("fold level changed: " + start + ":" + line);
  2215. fireFoldLevelChanged(start,line);
  2216. }
  2217. return newFoldLevel;
  2218. }
  2219. }
  2220. finally
  2221. {
  2222. writeUnlock();
  2223. }
  2224. } //}}}
  2225. //{{{ getFoldAtLine() method
  2226. /**
  2227. * Returns an array. The first element is the start line, the
  2228. * second element is the end line, of the fold containing the
  2229. * specified line number.
  2230. * @param line The line number
  2231. * @since jEdit 4.0pre3
  2232. */
  2233. public int[] getFoldAtLine(int line)
  2234. {
  2235. int start, end;
  2236. if(isFoldStart(line))
  2237. {
  2238. start = line;
  2239. int foldLevel = getFoldLevel(line);
  2240. line++;
  2241. while(getFoldLevel(line) > foldLevel)
  2242. {
  2243. line++;
  2244. if(line == getLineCount())
  2245. break;
  2246. }
  2247. end = line - 1;
  2248. }
  2249. else
  2250. {
  2251. start = line;
  2252. int foldLevel = getFoldLevel(line);
  2253. while(getFoldLevel(start) >= foldLevel)
  2254. {
  2255. if(start == 0)
  2256. break;
  2257. else
  2258. start--;
  2259. }
  2260. end = line;
  2261. while(getFoldLevel(end) >= foldLevel)
  2262. {
  2263. end++;
  2264. if(end == getLineCount())
  2265. break;
  2266. }
  2267. end--;
  2268. }
  2269. while(getLineLength(end) == 0 && end > start)
  2270. end--;
  2271. return new int[] { start, end };
  2272. } //}}}
  2273. //{{{ _getFoldVisibilityManager() method
  2274. /**
  2275. * Plugins and macros should call
  2276. * <code>textArea.getFoldVisibilityManager()</code>
  2277. * instead of this method.
  2278. * @param textArea The text area
  2279. * @since jEdit 4.0pre1
  2280. */
  2281. public FoldVisibilityManager _getFoldVisibilityManager(
  2282. JEditTextArea textArea)
  2283. {
  2284. FoldVisibilityManager mgr = new FoldVisibilityManager(this,
  2285. offsetMgr,textArea);
  2286. // find it a bit that it can set in line's 'visible' flag sets
  2287. for(int i = 0; i < inUseFVMs.length; i++)
  2288. {
  2289. if(inUseFVMs[i] == null)
  2290. {
  2291. inUseFVMs[i] = mgr;
  2292. mgr._grab(i);
  2293. return mgr;
  2294. }
  2295. }
  2296. //XXX
  2297. throw new InternalError("Too many text areas editing this buffer");
  2298. } //}}}
  2299. //{{{ _releaseFoldVisibilityManager() method
  2300. /**
  2301. * Plugins and macros should not call this method.
  2302. * @param mgr The fold visibility manager
  2303. * @since jEdit 4.0pre1
  2304. */
  2305. public void _releaseFoldVisibilityManager(FoldVisibilityManager mgr)
  2306. {
  2307. inUseFVMs[mgr._getIndex()] = null;
  2308. mgr._release();
  2309. } //}}}
  2310. //}}}
  2311. //{{{ Position methods
  2312. //{{{ createPosition() method
  2313. /**
  2314. * Creates a floating position.
  2315. * @param offset The offset
  2316. */
  2317. public Position createPosition(int offset)
  2318. {
  2319. try
  2320. {
  2321. readLock();
  2322. if(offset < 0 || offset > contentMgr.getLength())
  2323. throw new ArrayIndexOutOfBoundsException(offset);
  2324. return offsetMgr.createPosition(offset);
  2325. }
  2326. finally
  2327. {
  2328. readUnlock();
  2329. }
  2330. } //}}}
  2331. //}}}
  2332. //{{{ Marker methods
  2333. //{{{ getMarkers() method
  2334. /**
  2335. * Returns a vector of markers.
  2336. * @since jEdit 3.2pre1
  2337. */
  2338. public final Vector getMarkers()
  2339. {
  2340. return markers;
  2341. } //}}}
  2342. //{{{ addOrRemoveMarker() method
  2343. /**
  2344. * If a marker is set on the line of the position, it is removed. Otherwise
  2345. * a new marker with the specified shortcut is added.
  2346. * @param pos The position of the marker
  2347. * @param shortcut The shortcut ('\0' if none)
  2348. * @since jEdit 3.2pre5
  2349. */
  2350. public void addOrRemoveMarker(char shortcut, int pos)
  2351. {
  2352. int line = getLineOfOffset(pos);
  2353. if(getMarkerAtLine(line) != null)
  2354. removeMarker(line);
  2355. else
  2356. addMarker(shortcut,pos);
  2357. } //}}}
  2358. //{{{ addMarker() method
  2359. /**
  2360. * Adds a marker to this buffer.
  2361. * @param pos The position of the marker
  2362. * @param shortcut The shortcut ('\0' if none)
  2363. * @since jEdit 3.2pre1
  2364. */
  2365. public void addMarker(char shortcut, int pos)
  2366. {
  2367. if(!getFlag(READ_ONLY) && jEdit.getBooleanProperty("persistentMarkers"))
  2368. setDirty(true);
  2369. Marker markerN = new Marker(this,shortcut,pos);
  2370. boolean added = false;
  2371. // don't sort markers while buffer is being loaded
  2372. if(!getFlag(LOADING))
  2373. {
  2374. markerN.createPosition();
  2375. for(int i = 0; i < markers.size(); i++)
  2376. {
  2377. Marker marker = (Marker)markers.elementAt(i);
  2378. if(shortcut != '\0' && marker.getShortcut() == shortcut)
  2379. marker.setShortcut('\0');
  2380. if(marker.getPosition() == pos)
  2381. {
  2382. markers.removeElementAt(i);
  2383. i--;
  2384. }
  2385. }
  2386. for(int i = 0; i < markers.size(); i++)
  2387. {
  2388. Marker marker = (Marker)markers.elementAt(i);
  2389. if(marker.getPosition() > pos)
  2390. {
  2391. markers.insertElementAt(markerN,i);
  2392. added = true;
  2393. break;
  2394. }
  2395. }
  2396. }
  2397. if(!added)
  2398. markers.addElement(markerN);
  2399. if(!getFlag(LOADING) && !getFlag(TEMPORARY))
  2400. {
  2401. EditBus.send(new BufferUpdate(this,null,
  2402. BufferUpdate.MARKERS_CHANGED));
  2403. }
  2404. } //}}}
  2405. //{{{ getMarkerInRange() method
  2406. /**
  2407. * Returns the first marker within the specified range.
  2408. * @param start The start offset
  2409. * @param end The end offset
  2410. * @since jEdit 4.0pre4
  2411. */
  2412. public Marker getMarkerInRange(int start, int end)
  2413. {
  2414. for(int i = 0; i < markers.size(); i++)
  2415. {
  2416. Marker marker = (Marker)markers.elementAt(i);
  2417. int pos = marker.getPosition();
  2418. if(pos >= start && pos < end)
  2419. return marker;
  2420. }
  2421. return null;
  2422. } //}}}
  2423. //{{{ getMarkerAtLine() method
  2424. /**
  2425. * Returns the first marker at the specified line.
  2426. * @param line The line number
  2427. * @since jEdit 3.2pre2
  2428. */
  2429. public Marker getMarkerAtLine(int line)
  2430. {
  2431. for(int i = 0; i < markers.size(); i++)
  2432. {
  2433. Marker marker = (Marker)markers.elementAt(i);
  2434. if(getLineOfOffset(marker.getPosition()) == line)
  2435. return marker;
  2436. }
  2437. return null;
  2438. } //}}}
  2439. //{{{ removeMarker() method
  2440. /**
  2441. * Removes all markers at the specified line.
  2442. * @param line The line number
  2443. * @since jEdit 3.2pre2
  2444. */
  2445. public void removeMarker(int line)
  2446. {
  2447. for(int i = 0; i < markers.size(); i++)
  2448. {
  2449. Marker marker = (Marker)markers.elementAt(i);
  2450. if(getLineOfOffset(marker.getPosition()) == line)
  2451. {
  2452. if(!getFlag(READ_ONLY) && jEdit.getBooleanProperty("persistentMarkers"))
  2453. setDirty(true);
  2454. marker.removePosition();
  2455. markers.removeElementAt(i);
  2456. i--;
  2457. }
  2458. }
  2459. EditBus.send(new BufferUpdate(this,null,
  2460. BufferUpdate.MARKERS_CHANGED));
  2461. } //}}}
  2462. //{{{ removeAllMarkers() method
  2463. /**
  2464. * Removes all defined markers.
  2465. * @since jEdit 2.6pre1
  2466. */
  2467. public void removeAllMarkers()
  2468. {
  2469. if(!getFlag(READ_ONLY) && jEdit.getBooleanProperty("persistentMarkers"))
  2470. setDirty(true);
  2471. for(int i = 0; i < markers.size(); i++)
  2472. ((Marker)markers.elementAt(i)).removePosition();
  2473. markers.removeAllElements();
  2474. EditBus.send(new BufferUpdate(this,null,
  2475. BufferUpdate.MARKERS_CHANGED));
  2476. } //}}}
  2477. //{{{ getMarker() method
  2478. /**
  2479. * Returns the marker with the specified shortcut.
  2480. * @param shortcut The shortcut
  2481. * @since jEdit 3.2pre2
  2482. */
  2483. public Marker getMarker(char shortcut)
  2484. {
  2485. Enumeration enum = markers.elements();
  2486. while(enum.hasMoreElements())
  2487. {
  2488. Marker marker = (Marker)enum.nextElement();
  2489. if(marker.getShortcut() == shortcut)
  2490. return marker;
  2491. }
  2492. return null;
  2493. } //}}}
  2494. //}}}
  2495. //{{{ Miscellaneous methods
  2496. //{{{ getNext() method
  2497. /**
  2498. * Returns the next buffer in the list.
  2499. */
  2500. public final Buffer getNext()
  2501. {
  2502. return next;
  2503. } //}}}
  2504. //{{{ getPrev() method
  2505. /**
  2506. * Returns the previous buffer in the list.
  2507. */
  2508. public final Buffer getPrev()
  2509. {
  2510. return prev;
  2511. } //}}}
  2512. //{{{ getIndex() method
  2513. /**
  2514. * Returns the position of this buffer in the buffer list.
  2515. */
  2516. public final int getIndex()
  2517. {
  2518. int count = 0;
  2519. Buffer buffer = prev;
  2520. for(;;)
  2521. {
  2522. if(buffer == null)
  2523. break;
  2524. count++;
  2525. buffer = buffer.prev;
  2526. }
  2527. return count;
  2528. } //}}}
  2529. //{{{ toString() method
  2530. /**
  2531. * Returns a string representation of this buffer.
  2532. * This simply returns the path name.
  2533. */
  2534. public String toString()
  2535. {
  2536. return name + " (" + MiscUtilities.getParentOfPath(path) + ")";
  2537. } //}}}
  2538. //{{{ handleMessage() method
  2539. public void handleMessage(EBMessage msg)
  2540. {
  2541. if(msg instanceof PropertiesChanged)
  2542. propertiesChanged();
  2543. else if(msg instanceof EditPaneUpdate)
  2544. {
  2545. EditPaneUpdate emsg = (EditPaneUpdate)msg;
  2546. if(emsg.getWhat() == EditPaneUpdate.CREATED)
  2547. {
  2548. // see getFoldVisibilityManager()
  2549. }
  2550. else if(emsg.getWhat() == EditPaneUpdate.DESTROYED)
  2551. {
  2552. JEditTextArea textArea = emsg.getEditPane()
  2553. .getTextArea();
  2554. FoldVisibilityManager mgr = textArea
  2555. .getFoldVisibilityManager();
  2556. for(int i = 0; i < inUseFVMs.length; i++)
  2557. {
  2558. if(mgr == inUseFVMs[i])
  2559. {
  2560. mgr._release();
  2561. inUseFVMs[i] = null;
  2562. break;
  2563. }
  2564. }
  2565. }
  2566. }
  2567. } //}}}
  2568. //}}}
  2569. //{{{ Package-private members
  2570. Buffer prev;
  2571. Buffer next;
  2572. //{{{ Buffer constructor
  2573. Buffer(View view, String path, boolean newFile, boolean temp,
  2574. Hashtable props)
  2575. {
  2576. properties = ((Hashtable)props.clone());
  2577. // fill in defaults for these from system properties.
  2578. if(properties.get(ENCODING) == null)
  2579. properties.put(ENCODING,System.getProperty("file.encoding"));
  2580. if(properties.get(LINESEP) == null)
  2581. properties.put(LINESEP,System.getProperty("line.separator"));
  2582. lock = new ReadWriteLock();
  2583. contentMgr = new ContentManager();
  2584. offsetMgr = new OffsetManager(this);
  2585. integerArray = new IntegerArray();
  2586. undoMgr = new UndoManager(this);
  2587. bufferListeners = new Vector();
  2588. seg = new Segment();
  2589. lastTokenizedLine = -1;
  2590. tokenList = new TokenList();
  2591. inUseFVMs = new FoldVisibilityManager[8];
  2592. setFlag(TEMPORARY,temp);
  2593. markers = new Vector();
  2594. // this must be