PageRenderTime 69ms CodeModel.GetById 31ms 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

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

  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(nextLineReq

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