📄 buffer.java
字号:
/* * Buffer.java - jEdit buffer * :tabSize=8:indentSize=8:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 1998, 2003 Slava Pestov * Portions copyright (C) 1999, 2000 mike dillon * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */package org.gjt.sp.jedit;//{{{ Importsimport gnu.regexp.*;import javax.swing.*;import javax.swing.text.*;import java.awt.Toolkit;import java.io.File;import java.io.IOException;import java.net.Socket;import java.util.*;import org.gjt.sp.jedit.browser.VFSBrowser;import org.gjt.sp.jedit.buffer.*;import org.gjt.sp.jedit.io.*;import org.gjt.sp.jedit.msg.*;import org.gjt.sp.jedit.search.RESearchMatcher;import org.gjt.sp.jedit.syntax.*;import org.gjt.sp.jedit.textarea.*;import org.gjt.sp.util.*;//}}}/** * A <code>Buffer</code> represents the contents of an open text * file as it is maintained in the computer's memory (as opposed to * how it may be stored on a disk).<p> * * In a BeanShell script, you can obtain the current buffer instance from the * <code>buffer</code> variable.<p> * * This class does not have a public constructor. * Buffers can be opened and closed using methods in the <code>jEdit</code> * class.<p> * * This class is partially thread-safe, however you must pay attention to two * very important guidelines: * <ul> * <li>Changes to a buffer can only be made from the AWT thread. * <li>When accessing the buffer from another thread, you must * grab a read lock if you plan on performing more than one call, to ensure that * the buffer contents are not changed by the AWT thread for the duration of the * lock. Only methods whose descriptions specify thread safety can be invoked * from other threads. * </ul> * * @author Slava Pestov * @version $Id: Buffer.java,v 1.213 2004/02/27 23:59:09 spestov Exp $ */public class Buffer{ //{{{ Some constants /** * Line separator property. */ public static final String LINESEP = "lineSeparator"; /** * Backed up property. * @since jEdit 3.2pre2 */ public static final String BACKED_UP = "Buffer__backedUp"; /** * Caret info properties. * @since jEdit 3.2pre1 */ public static final String CARET = "Buffer__caret"; public static final String SELECTION = "Buffer__selection"; /** * This should be a physical line number, so that the scroll * position is preserved correctly across reloads (which will * affect virtual line numbers, due to fold being reset) */ public static final String SCROLL_VERT = "Buffer__scrollVert"; public static final String SCROLL_HORIZ = "Buffer__scrollHoriz"; /** * Character encoding used when loading and saving. * @since jEdit 3.2pre4 */ public static final String ENCODING = "encoding"; /** * Should jEdit try to set the encoding based on a UTF8, UTF16 or * XML signature at the beginning of the file? */ public static final String ENCODING_AUTODETECT = "encodingAutodetect"; /** * This property is set to 'true' if the file has a trailing newline. * @since jEdit 4.0pre1 */ public static final String TRAILING_EOL = "trailingEOL"; /** * This property is set to 'true' if the file should be GZipped. * @since jEdit 4.0pre4 */ public static final String GZIPPED = "gzipped"; //}}} //{{{ Input/output methods //{{{ reload() method /** * Reloads the buffer from disk, asking for confirmation if the buffer * has unsaved changes. * @param view The view * @since jEdit 2.7pre2 */ public void reload(View view) { if(getFlag(DIRTY)) { String[] args = { name }; int result = GUIUtilities.confirm(view,"changedreload", args,JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if(result != JOptionPane.YES_OPTION) return; } view.getEditPane().saveCaretInfo(); load(view,true); } //}}} //{{{ load() method /** * Loads the buffer from disk, even if it is loaded already. * @param view The view * @param reload If true, user will not be asked to recover autosave * file, if any * * @since 2.5pre1 */ public boolean load(final View view, final boolean reload) { if(isPerformingIO()) { GUIUtilities.error(view,"buffer-multiple-io",null); return false; } setBooleanProperty(BufferIORequest.ERROR_OCCURRED,false); setFlag(LOADING,true); // view text areas temporarily blank out while a buffer is // being loaded, to indicate to the user that there is no // data available yet. if(!getFlag(TEMPORARY)) EditBus.send(new BufferUpdate(this,view,BufferUpdate.LOAD_STARTED)); final boolean loadAutosave; if(reload || !getFlag(NEW_FILE)) { if(file != null) modTime = file.lastModified(); // Only on initial load if(!reload && autosaveFile != null && autosaveFile.exists()) loadAutosave = recoverAutosave(view); else { if(autosaveFile != null) autosaveFile.delete(); loadAutosave = false; } if(!loadAutosave) { VFS vfs = VFSManager.getVFSForPath(path); if(!checkFileForLoad(view,vfs,path)) { setFlag(LOADING,false); return false; } // have to check again since above might set // NEW_FILE flag if(reload || !getFlag(NEW_FILE)) { if(!vfs.load(view,this,path)) { setFlag(LOADING,false); return false; } } } } else loadAutosave = false; //{{{ Do some stuff once loading is finished Runnable runnable = new Runnable() { public void run() { String newPath = getStringProperty( BufferIORequest.NEW_PATH); Segment seg = (Segment)getProperty( BufferIORequest.LOAD_DATA); IntegerArray endOffsets = (IntegerArray) getProperty(BufferIORequest.END_OFFSETS); if(seg == null) seg = new Segment(new char[1024],0,0); if(endOffsets == null) { endOffsets = new IntegerArray(); endOffsets.add(1); } try { writeLock(); // For `reload' command firePreContentRemoved(0,0,getLineCount() - 1,getLength()); contentMgr.remove(0,getLength()); lineMgr.contentRemoved(0,0,getLineCount() - 1,getLength()); positionMgr.contentRemoved(0,getLength()); fireContentRemoved(0,0,getLineCount() - 1,getLength()); // theoretically a segment could // have seg.offset != 0 but // SegmentBuffer never does that contentMgr._setContent(seg.array,seg.count); lineMgr._contentInserted(endOffsets); positionMgr.contentInserted(0,seg.count); fireContentInserted(0,0, endOffsets.getSize() - 1, seg.count - 1); } finally { writeUnlock(); } unsetProperty(BufferIORequest.LOAD_DATA); unsetProperty(BufferIORequest.END_OFFSETS); unsetProperty(BufferIORequest.NEW_PATH); undoMgr.clear(); undoMgr.setLimit(jEdit.getIntegerProperty( "buffer.undoCount",100)); if(!getFlag(TEMPORARY)) finishLoading(); setFlag(LOADING,false); // if reloading a file, clear dirty flag if(reload) setDirty(false); if(!loadAutosave && newPath != null) setPath(newPath); // if loadAutosave is false, we loaded an // autosave file, so we set 'dirty' to true // note that we don't use setDirty(), // because a) that would send an unnecessary // message, b) it would also set the // AUTOSAVE_DIRTY flag, which will make // the autosave thread write out a // redundant autosave file if(loadAutosave) setFlag(DIRTY,true); // send some EditBus messages if(!getFlag(TEMPORARY)) { EditBus.send(new BufferUpdate(Buffer.this, view,BufferUpdate.LOADED)); //EditBus.send(new BufferUpdate(Buffer.this, // view,BufferUpdate.MARKERS_CHANGED)); } } }; //}}} if(getFlag(TEMPORARY)) runnable.run(); else VFSManager.runInAWTThread(runnable); return true; } //}}} //{{{ insertFile() method /** * Loads a file from disk, and inserts it into this buffer. * @param view The view * * @since 4.0pre1 */ public boolean insertFile(final View view, String path) { if(isPerformingIO()) { GUIUtilities.error(view,"buffer-multiple-io",null); return false; } setBooleanProperty(BufferIORequest.ERROR_OCCURRED,false); path = MiscUtilities.constructPath(this.path,path); Buffer buffer = jEdit.getBuffer(path); if(buffer != null) { view.getTextArea().setSelectedText( buffer.getText(0,buffer.getLength())); return true; } VFS vfs = VFSManager.getVFSForPath(path); // this returns false if initial sanity // checks (if the file is a directory, etc) // fail return vfs.insert(view,this,path); } //}}} //{{{ autosave() method /** * Autosaves this buffer. */ public void autosave() { if(autosaveFile == null || !getFlag(AUTOSAVE_DIRTY) || !getFlag(DIRTY) || getFlag(LOADING) || getFlag(IO)) return; setFlag(AUTOSAVE_DIRTY,false); VFSManager.runInWorkThread(new BufferIORequest( BufferIORequest.AUTOSAVE,null,this,null, VFSManager.getFileVFS(),autosaveFile.getPath())); } //}}} //{{{ saveAs() method /** * Prompts the user for a file to save this buffer to. * @param view The view * @param rename True if the buffer's path should be changed, false * if only a copy should be saved to the specified filename * @since jEdit 2.6pre5 */ public boolean saveAs(View view, boolean rename) { String[] files = GUIUtilities.showVFSFileDialog(view,path, VFSBrowser.SAVE_DIALOG,false); // files[] should have length 1, since the dialog type is // SAVE_DIALOG if(files == null) return false; return save(view,files[0],rename); } //}}} //{{{ save() method /** * Saves this buffer to the specified path name, or the current path * name if it's null. * @param view The view * @param path The path name to save the buffer to, or null to use * the existing path */ public boolean save(View view, String path) { return save(view,path,true); } //}}} //{{{ save() method /** * Saves this buffer to the specified path name, or the current path * name if it's null. * @param view The view * @param path The path name to save the buffer to, or null to use * the existing path * @param rename True if the buffer's path should be changed, false * if only a copy should be saved to the specified filename * @since jEdit 2.6pre5 */ public boolean save(final View view, String path, final boolean rename) { if(isPerformingIO()) { GUIUtilities.error(view,"buffer-multiple-io",null); return false; } setBooleanProperty(BufferIORequest.ERROR_OCCURRED,false); if(path == null && getFlag(NEW_FILE)) return saveAs(view,rename); if(path == null && file != null) { long newModTime = file.lastModified(); if(newModTime != modTime && jEdit.getBooleanProperty("view.checkModStatus")) { Object[] args = { this.path }; int result = GUIUtilities.confirm(view, "filechanged-save",args, JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if(result != JOptionPane.YES_OPTION) return false; } } EditBus.send(new BufferUpdate(this,view,BufferUpdate.SAVING)); setFlag(IO,true); final String oldPath = this.path; final String oldSymlinkPath = this.symlinkPath; final String newPath = (path == null ? this.path : path); VFS vfs = VFSManager.getVFSForPath(newPath); if(!checkFileForSave(view,vfs,newPath)) { setFlag(IO,false); return false; } if(!vfs.save(view,this,newPath)) { setFlag(IO,false); return false; } // Once save is complete, do a few other things VFSManager.runInAWTThread(new Runnable() { public void run() { setFlag(IO,false); finishSaving(view,oldPath,oldSymlinkPath, newPath,rename,getBooleanProperty( BufferIORequest.ERROR_OCCURRED)); } }); return true; } //}}} //{{{ checkFileStatus() method public static final int FILE_NOT_CHANGED = 0; public static final int FILE_CHANGED = 1; public static final int FILE_DELETED = 2; /** * Check if the buffer has changed on disk. * @return One of <code>NOT_CHANGED</code>, <code>CHANGED</code>, or * <code>DELETED</code>. * * @since jEdit 4.2pre1 */ public int checkFileStatus(View view) { // - don't do these checks while a save is in progress, // because for a moment newModTime will be greater than // oldModTime, due to the multithreading // - only supported on local file system if(!getFlag(IO) && !getFlag(LOADING) && file != null && !getFlag(NEW_FILE)) { boolean newReadOnly = (file.exists() && !file.canWrite()); if(newReadOnly != getFlag(READ_ONLY)) { setFlag(READ_ONLY,newReadOnly); EditBus.send(new BufferUpdate(this,null, BufferUpdate.DIRTY_CHANGED)); } long oldModTime = modTime; long newModTime = file.lastModified(); if(newModTime != oldModTime) { modTime = newModTime; if(!file.exists()) { setFlag(NEW_FILE,true); setDirty(true); return FILE_DELETED; } else { return FILE_CHANGED; } } } return FILE_NOT_CHANGED; } //}}} //}}} //{{{ Getters/setter methods for various buffer meta-data //{{{ getLastModified() method /** * Returns the last time jEdit modified the file on disk. * This method is thread-safe. */ public long getLastModified() { return modTime; } //}}} //{{{ setLastModified() method /** * Sets the last time jEdit modified the file on disk. * @param modTime The new modification time */ public void setLastModified(long modTime) { this.modTime = modTime; } //}}} //{{{ getVFS() method /** * Returns the virtual filesystem responsible for loading and * saving this buffer. This method is thread-safe. */ public VFS getVFS() { return VFSManager.getVFSForPath(path); } //}}}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -