📄 searchandreplace.java
字号:
repeat = true; } else break loop; } } catch(Exception e) { Log.log(Log.ERROR,SearchAndReplace.class,e); Object[] args = { e.getMessage() }; if(args[0] == null) args[0] = e.toString(); GUIUtilities.error(comp,"searcherror",args); } finally { view.hideWaitCursor(); } return false; } //}}} //{{{ find() method /** * Finds the next instance of the search string in the specified * buffer. * @param view The view * @param buffer The buffer * @param start Location where to start the search */ public static boolean find(View view, Buffer buffer, int start) throws Exception { return find(view,buffer,start,false,false); } //}}} //{{{ find() method /** * Finds the next instance of the search string in the specified * buffer. * @param view The view * @param buffer The buffer * @param start Location where to start the search * @param firstTime See {@link SearchMatcher#nextMatch(CharIndexed, * boolean,boolean,boolean,boolean)}. * @since jEdit 4.1pre7 */ public static boolean find(View view, Buffer buffer, int start, boolean firstTime, boolean reverse) throws Exception { SearchMatcher matcher = getSearchMatcher(); if(matcher == null) { view.getToolkit().beep(); return false; } Segment text = new Segment(); if(reverse) buffer.getText(0,start,text); else buffer.getText(start,buffer.getLength() - start,text); // the start and end flags will be wrong with reverse search enabled, // but they are only used by the regexp matcher, which doesn't // support reverse search yet. // // REMIND: fix flags when adding reverse regexp search. int[] match = matcher.nextMatch(new CharIndexedSegment(text,reverse), start == 0,true,firstTime,reverse); if(match != null) { jEdit.commitTemporary(buffer); view.setBuffer(buffer); JEditTextArea textArea = view.getTextArea(); if(reverse) { textArea.setSelection(new Selection.Range( start - match[1], start - match[0])); textArea.moveCaretPosition(start - match[1]); } else { textArea.setSelection(new Selection.Range( start + match[0], start + match[1])); textArea.moveCaretPosition(start + match[1]); } return true; } else return false; } //}}} //{{{ replace() method /** * Replaces the current selection with the replacement string. * @param view The view * @return True if the operation was successful, false otherwise */ public static boolean replace(View view) { // component that will parent any dialog boxes Component comp = SearchDialog.getSearchDialog(view); if(comp == null) comp = view; JEditTextArea textArea = view.getTextArea(); Buffer buffer = view.getBuffer(); if(!buffer.isEditable()) return false; boolean smartCaseReplace = (replace != null && TextUtilities.getStringCase(replace) == TextUtilities.LOWER_CASE); Selection[] selection = textArea.getSelection(); if(selection.length == 0) { view.getToolkit().beep(); return false; } record(view,"replace(view)",true,false); // a little hack for reverse replace and find int caret = textArea.getCaretPosition(); Selection s = textArea.getSelectionAtOffset(caret); if(s != null) caret = s.getStart(); try { buffer.beginCompoundEdit(); SearchMatcher matcher = getSearchMatcher(); if(matcher == null) return false; int retVal = 0; for(int i = 0; i < selection.length; i++) { s = selection[i]; /* if an occurence occurs at the beginning of the selection, the selection start will get moved. this sucks, so we hack to avoid it. */ int start = s.getStart(); if(s instanceof Selection.Range) { retVal += _replace(view,buffer,matcher, s.getStart(),s.getEnd(), smartCaseReplace); textArea.removeFromSelection(s); textArea.addToSelection(new Selection.Range( start,s.getEnd())); } else if(s instanceof Selection.Rect) { for(int j = s.getStartLine(); j <= s.getEndLine(); j++) { retVal += _replace(view,buffer,matcher, s.getStart(buffer,j),s.getEnd(buffer,j), smartCaseReplace); } textArea.addToSelection(new Selection.Rect( start,s.getEnd())); } } if(reverse) { // so that Replace and Find continues from // the right location textArea.moveCaretPosition(caret); } else { s = textArea.getSelectionAtOffset( textArea.getCaretPosition()); if(s != null) textArea.moveCaretPosition(s.getEnd()); } if(retVal == 0) { view.getToolkit().beep(); return false; } return true; } catch(Exception e) { Log.log(Log.ERROR,SearchAndReplace.class,e); Object[] args = { e.getMessage() }; if(args[0] == null) args[0] = e.toString(); GUIUtilities.error(comp,"searcherror",args); } finally { buffer.endCompoundEdit(); } return false; } //}}} //{{{ replace() method /** * Replaces text in the specified range with the replacement string. * @param view The view * @param buffer The buffer * @param start The start offset * @param end The end offset * @return True if the operation was successful, false otherwise */ public static boolean replace(View view, Buffer buffer, int start, int end) { if(!buffer.isEditable()) return false; // component that will parent any dialog boxes Component comp = SearchDialog.getSearchDialog(view); if(comp == null) comp = view; boolean smartCaseReplace = (replace != null && TextUtilities.getStringCase(replace) == TextUtilities.LOWER_CASE); try { buffer.beginCompoundEdit(); SearchMatcher matcher = getSearchMatcher(); if(matcher == null) return false; int retVal = 0; retVal += _replace(view,buffer,matcher,start,end, smartCaseReplace); if(retVal != 0) return true; } catch(Exception e) { Log.log(Log.ERROR,SearchAndReplace.class,e); Object[] args = { e.getMessage() }; if(args[0] == null) args[0] = e.toString(); GUIUtilities.error(comp,"searcherror",args); } finally { buffer.endCompoundEdit(); } return false; } //}}} //{{{ replaceAll() method /** * Replaces all occurances of the search string with the replacement * string. * @param view The view */ public static boolean replaceAll(View view) { // component that will parent any dialog boxes Component comp = SearchDialog.getSearchDialog(view); if(comp == null) comp = view; int fileCount = 0; int occurCount = 0; if(fileset.getFileCount(view) == 0) { GUIUtilities.error(comp,"empty-fileset",null); return false; } record(view,"replaceAll(view)",true,true); view.showWaitCursor(); boolean smartCaseReplace = (replace != null && TextUtilities.getStringCase(replace) == TextUtilities.LOWER_CASE); try { SearchMatcher matcher = getSearchMatcher(); if(matcher == null) return false; String path = fileset.getFirstFile(view);loop: while(path != null) { Buffer buffer = jEdit.openTemporary( view,null,path,false); /* this is stupid and misleading. * but 'path' is not used anywhere except * the above line, and if this is done * after the 'continue', then we will * either hang, or be forced to duplicate * it inside the buffer == null, or add * a 'finally' clause. you decide which one's * worse. */ path = fileset.getNextFile(view,path); if(buffer == null) continue loop; // Wait for buffer to finish loading if(buffer.isPerformingIO()) VFSManager.waitForRequests(); if(!buffer.isEditable()) continue loop; // Leave buffer in a consistent state if // an error occurs int retVal = 0; try { buffer.beginCompoundEdit(); retVal = _replace(view,buffer,matcher, 0,buffer.getLength(), smartCaseReplace); } finally { buffer.endCompoundEdit(); } if(retVal != 0) { fileCount++; occurCount += retVal; jEdit.commitTemporary(buffer); } } } catch(Exception e) { Log.log(Log.ERROR,SearchAndReplace.class,e); Object[] args = { e.getMessage() }; if(args[0] == null) args[0] = e.toString(); GUIUtilities.error(comp,"searcherror",args); } finally { view.hideWaitCursor(); } /* Don't do this when playing a macro, cos it's annoying */ if(!BeanShell.isScriptRunning()) { Object[] args = { new Integer(occurCount), new Integer(fileCount) }; view.getStatus().setMessageAndClear(jEdit.getProperty( "view.status.replace-all",args)); if(occurCount == 0) view.getToolkit().beep(); } return (fileCount != 0); } //}}} //}}} //{{{ load() method /** * Loads search and replace state from the properties. */ public static void load() { search = jEdit.getProperty("search.find.value"); replace = jEdit.getProperty("search.replace.value"); ignoreCase = jEdit.getBooleanProperty("search.ignoreCase.toggle"); regexp = jEdit.getBooleanProperty("search.regexp.toggle"); beanshell = jEdit.getBooleanProperty("search.beanshell.toggle"); wrap = jEdit.getBooleanProperty("search.wrap.toggle"); fileset = new CurrentBufferSet(); // Tags plugin likes to call this method at times other than // startup; so we need to fire a SearchSettingsChanged to // notify the search bar and so on. matcher = null; EditBus.send(new SearchSettingsChanged(null)); } //}}} //{{{ save() method /** * Saves search and replace state to the properties. */ public static void save() { jEdit.setProperty("search.find.value",search); jEdit.setProperty("search.replace.value",replace); jEdit.setBooleanProperty("search.ignoreCase.toggle",ignoreCase); jEdit.setBooleanProperty("search.regexp.toggle",regexp); jEdit.setBooleanProperty("search.beanshell.toggle",beanshell); jEdit.setBooleanProperty("search.wrap.toggle",wrap); } //}}} //{{{ Private members //{{{ Instance variables private static String search; private static String replace; private static boolean regexp; private static boolean ignoreCase; private static boolean reverse; private static boolean beanshell; private static boolean wrap; private static SearchMatcher matcher; private static SearchFileSet fileset; //}}} //{{{ record() method private static void record(View view, String action, boolean replaceAction, boolean recordFileSet) { Macros.Recorder recorder = view.getMacroRecorder(); if(recorder != null) { recorder.record("SearchAndReplace.setSearchString(\"" + MiscUtilities.charsToEscapes(search) + "\");"); if(replaceAction) { recorder.record("SearchAndReplace.setReplaceString(\"" + MiscUtilities.charsToEscapes(replace) + "\");"); recorder.record("SearchAndReplace.setBeanShellReplace(" + beanshell + ");"); } else { // only record this if doing a find next recorder.record("SearchAndReplace.setAutoWrapAround(" + wrap + ");"); recorder.record("SearchAndReplace.setReverseSearch(" + reverse + ");"); } recorder.record("SearchAndReplace.setIgnoreCase(" + ignoreCase + ");"); recorder.record("SearchAndReplace.setRegexp(" + regexp + ");"); if(recordFileSet) { recorder.record("SearchAndReplace.setSearchFileSet(" + fileset.getCode() + ");"); } recorder.record("SearchAndReplace." + action + ";"); } } //}}} //{{{ _replace() method /** * Replaces all occurances of the search string with the replacement * string. * @param view The view * @param buffer The buffer * @param start The start offset * @param end The end offset * @param matcher The search matcher to use * @param smartCaseReplace See user's guide * @return The number of occurrences replaced */ private static int _replace(View view, Buffer buffer, SearchMatcher matcher, int start, int end, boolean smartCaseReplace) throws Exception { int occurCount = 0; boolean endOfLine = (buffer.getLineEndOffset( buffer.getLineOfOffset(end)) - 1 == end); Segment text = new Segment(); int offset = start;loop: for(int counter = 0; ; counter++) { buffer.getText(offset,end - offset,text); boolean startOfLine = (buffer.getLineStartOffset( buffer.getLineOfOffset(offset)) == offset); int[] occur = matcher.nextMatch( new CharIndexedSegment(text,false), startOfLine,endOfLine,counter == 0, false); if(occur == null) break loop; int _start = occur[0]; int _length = occur[1] - occur[0]; String found = new String(text.array,text.offset + _start,_length); String subst = matcher.substitute(found); if(smartCaseReplace && ignoreCase) { int strCase = TextUtilities.getStringCase(found); if(strCase == TextUtilities.LOWER_CASE) subst = subst.toLowerCase(); else if(strCase == TextUtilities.UPPER_CASE) subst = subst.toUpperCase(); else if(strCase == TextUtilities.TITLE_CASE) subst = TextUtilities.toTitleCase(subst); } if(subst != null) { buffer.remove(offset + _start,_length); buffer.insert(offset + _start,subst); occurCount++; offset += _start + subst.length(); end += (subst.length() - found.length()); } else offset += _start + _length; } return occurCount; } //}}} //}}}}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -