// :collapseFolds=1:folding=explicit: // // Date Tag Buddy v0.2 // - a BeanShell macro script for the jEdit text editor - // // {{{ What does it do? // Updates certain date tags in an HTML buffer (the current edit mode must be "html"): // 1. Updates the date value of a modification date tag:
Last modified: 01 Mar 2006
// 2. Updates the year in a copyright text: © Me 2006 // Updates all "includes" in an HTML buffer. An include is defined as two HTML comments where // the first one contains the path to a file that should be inserted between the comments: // // // The path can be relative, as in the example, or absolute. Included files may contain date tags. // // The date tag and "include" updates can be performed automatically when the buffer is saved. Therefore install // Ollie Rutherfurd's ActionHooks plugin (http://plugins.jedit.org/plugins/?ActionHooks) and set it up: // 1. Add the Date Tag Buddy macro to the "BufferUpdate.SAVED" event list. // 2. Configure the ActionHooks plugin to start up with JEdit. // This ensures that date tags and "includes" are automatically updated whenever the HTML buffer is saved. // // A modification tag is identified by
and
. A copyright text is identified by © and . // The macro assumes that the date value is located at the end of each tag. To change the default behavior of the // macro you can modify the constants at its end. // }}} // // Copyright (c) 2006. Ralf Kintrup [r.kintrup@web.de] // // 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 the jEdit program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // {{{ History // 2006-02-05 Created. Implemented modification date and copyright text // update functionality. // Checked for jEdit 4.2 and 4.3pre3 API. [rk] // 2006-03-15 Implemented "include" update functionality. // Checked for jEdit 4.3pre3 API. [rk] // }}} // {{{ imports import gnu.regexp.*; import java.text.SimpleDateFormat; // }}} // {{{ include methods // {{{ updateIncludes() method /** * Updates all "includes" within the buffer if they need to get updated. * An include is defined as two HTML comments where the first one contains * the path to a file that should be inserted between the comments: * * * The path can be relative as in the example or absolute. * * @return 'true' if the text area was modified, otherwise 'false'. */ boolean updateIncludes() { boolean modified = false; boolean modifiedInclude; // Construct a regular expression that filters out the file path and // the content between the HTML comments StringBuffer stbRegExp = new StringBuffer(100); stbRegExp.append(""); stbRegExp.append("([\\s\\S]*?)"); // Subexpression 2: the content between the two comments stbRegExp.append(""); RE regExp = new gnu.regexp.RE(stbRegExp.toString()); // Iterate through the regexp matches and insert the file contents. REMatch[] matchArray = regExp.getAllMatches(textArea.getText()); REMatch match; String matchFilePath; int count = matchArray.length; //Macros.message(view, "Found " + count + " includes."); // Debug for (int i = 0; i < count; i++) { match = matchArray[i]; matchFilePath = match.toString(1); //Macros.message(view, "Found include " + (i + 1) + " from " + match.getStartIndex(2) + " to " + match.getEndIndex(2) + ":\n" + match.toString(2)); // Debug modifiedInclude = insertFile(matchFilePath, match.getStartIndex(2), match.getEndIndex(2)); if (modifiedInclude) { modified = true; // The matches moved due to the insert. Rescan the buffer. matchArray = regExp.getAllMatches(textArea.getText()); } } return modified; } // }}} // {{{ insertFile() method /** * Inserts the content of the specified file to the buffer replacing the * provided buffer substring. The insert is only performed if the file * differs from the substring. If the file is already open in jEdit it * inserts the content of the open buffer, even if it was modified. * Part of the code is taken from Ollie Rutherfurds "Insert Selection" * macro. * * @param filePath relative or absolute path with the file name of the * file to include. * @param start the start position of the substring to replace. * @param end the end position of the substring to replace. * @return 'true' if the text area was modified, otherwise 'false'. */ boolean insertFile(String filePath, int start, int end) { boolean isDifferent = false; boolean closeBuffer = false; // Try to get the buffer in case it's already open. String absolutePath = MiscUtilities.constructPath(view.getBuffer().getDirectory(), filePath); Buffer buffer = jEdit.getBuffer(absolutePath); if (buffer == null) { // Read the file content into temporary buffer buffer = jEdit.openTemporary(view, null, filePath, false); closeBuffer = true; } try { if (buffer == null) { Macros.message(view, "Could not open temporary buffer:\n" + filePath + "\n\n"); return isDifferent; } // Continue after the buffer was fully loaded. while (!buffer.isLoaded()) { VFSManager.waitForRequests(); } // Compare the selected text with the file content. To prevent // an infite loop when date tags are used in the "includes" // together with the BufferUpdate.SAVED event, the date tags // need to be taken out of the equal comparison. String oldInclude = view.getTextArea().getText(start, end - start); String fileContent = buffer.getText(0, buffer.getLength()); if (!fileContent.equals("") && !equalsWithoutDateTags(oldInclude, fileContent)) { isDifferent = true; view.getTextArea().setSelectedText(new Selection.Range(start, end), fileContent); } if (fileContent.equals("")) { Macros.message(view, "Could not find include file or file is empty:\n" + filePath + "\n\n"); } } finally { if (closeBuffer && (buffer != null)) { setAccessibility(true); buffer.close(); buffer = null; setAccessibility(false); } } return isDifferent; } // }}} // {{{ equalsWithoutDateTags() method /** * Returns whether the passed two texts are equal when date tag values * in them are ignored. * * @param text1 first text to compare. * @param text1 second text to compare. * @return 'true' if the two texts are equal, otherwise 'false'. */ boolean equalsWithoutDateTags(String text1, String text2) { String newText1 = new String(text1); String newText2 = new String(text2); newText1 = deleteDateTagValue(newText1, MODIFICATION_DATE_START, MODIFICATION_DATE_END); newText1 = deleteDateTagValue(newText1, COPYRIGHT_START, COPYRIGHT_END); newText2 = deleteDateTagValue(newText2, MODIFICATION_DATE_START, MODIFICATION_DATE_END); newText2 = deleteDateTagValue(newText2, COPYRIGHT_START, COPYRIGHT_END); return newText1.equals(newText2); } // }}} // {{{ deleteDateTagValue() method /** * Deletes the tag value from the passed text. Only deletes the value of * the first occurrence of the tag. * * @param text the text to work on * @param tagBegin the opening tag * @param tagEnd the closing tag * @return a new String without the tag value. */ String deleteDateTagValue(String text, String tagBegin, String tagEnd) { String newText = new String(text); RE regExp = new gnu.regexp.RE(tagBegin + "([\\s\\S]*?)" + tagEnd); REMatchEnumeration matchEnum = regExp.getMatchEnumeration(text); // Delete the value of the first occurrence of the tag. if (matchEnum.hasMoreElements()) { REMatch match = matchEnum.nextMatch(); String matchValue = match.toString(1); newText = text.substring(0, match.getStartIndex(1)).concat(text.substring(match.getEndIndex(1))); } return newText; } // }}} // }}} // {{{ date tag methods // {{{ updateModificationDateTag() method /** * Updates the value of the first occurence of a modification date tag * in the passed text area. The tag value is set to the current date * according to the passed pattern and locale. The pattern must deliver * a date value with a fixed length for all possible dates. * * The method call updateModificationDateTag("
", "
", * "dd MMM yyyy", Locale.US, textArea) will update the date of the * following modification date tag: *
Last modified: 7 Feb 2006
* * @param tagBegin the modification date begin tag * @param tagEnd the modification date end tag * @param pattern the pattern describing the date and time * format. See the SimpleDateFormat class for details. * @param locale the locale whose date format symbols should be used * @param textArea the text area to search * @return 'true' if the text area was modified, otherwise 'false'. */ boolean updateModificationDateTag(String tagBegin, String tagEnd, String pattern, Locale locale, JEditTextArea textArea) { String dateString = new SimpleDateFormat(pattern, locale).format(new Date()); return updateTag(tagBegin, tagEnd, dateString, textArea); } // }}} // {{{ updateCopyrightYear() method /** * Updates the value of the first occurence of a copyright tag in the * passed text area. The copyright tag value is set to the current year. * * The method call updateCopyrightTag("©", "") will update the * year in the following copyright text: * Copyright © Me 2006 * * @param tagBegin the copyright begin text * @param tagEnd the copyright end text * @param textArea the text area to search * @return 'true' if the text area was modified, otherwise 'false'. */ boolean updateCopyrightYear(String tagBegin, String tagEnd, JEditTextArea textArea) { String year = new SimpleDateFormat("yyyy", Locale.US).format(new Date()); return updateTag(tagBegin, tagEnd, year, textArea); } // }}} // {{{ updateTag() method /** * Searches for a begin and an end tag in the entire text area and * replaces the last characters of its value with the passed new value. * Only updates the value of the first occurence of the tag and only * modifies the text area if the new value differs from the old one. * * @param String tagBegin the begin tag * @param String tagEnd the end tag * @param String newValue the last characters of the new tag value * @param JEditTextArea textArea the text area to search * @return 'true' if the text area was modified, otherwise 'false'. */ boolean updateTag(String tagBegin, String tagEnd, String newValue, JEditTextArea textArea) { boolean isDifferent = false; // Create pattern to match a text with the length of the new value. StringBuffer valuePattern = new StringBuffer(""); int patternLength = newValue.length(); for (int i = 0; i < patternLength; i++) { valuePattern.append("[\\s\\S]"); } // Search for the tag in the current buffer. RE regExp = new gnu.regexp.RE("(" + tagBegin + ")([\\s\\S]*?)(" + valuePattern.toString() + ")(" + tagEnd + ")"); REMatchEnumeration matchEnum = regExp.getMatchEnumeration(textArea.getText()); if (matchEnum.hasMoreElements()) { REMatch match = matchEnum.nextMatch(); String matchStart = match.toString(1); String matchPrefix = match.toString(2); String matchValue = match.toString(3); String matchEnd = match.toString(4); String matchComplete = match.toString(); isDifferent = !matchValue.equals(newValue); if (isDifferent) { // Write start and end tag with the new value between the start // and the end of the match, replacing the old value. textArea.setSelection(new Selection.Range(match.getStartIndex(), match.getEndIndex())); textArea.setSelectedText(matchStart + matchPrefix + newValue + matchEnd); } } return isDifferent; } // }}} // }}} // {{{ displayHelp() method /** * Display a help dialog. */ void displayHelp() { StringBuffer helpMsg = new StringBuffer(""); helpMsg.append("DateTagBuddy Help\n"); helpMsg.append("(only displayed when the buffer is not in html mode and the ActionHooks plugin is disabled)\n"); helpMsg.append("\n"); helpMsg.append("Updates certain date tags in an HTML buffer (the edit mode must be ''html''):\n"); helpMsg.append("1. Updates the date value of a modification date tag:
Last modified: 01 Mar 2006
\n"); helpMsg.append("2. Updates the year in a copyright text: © Me 2006\n"); helpMsg.append("Updates all ''includes'' in an HTML buffer. An include is defined as two HTML comments where\n"); helpMsg.append("the first one contains the path to a file that should be inserted between the comments:\n"); helpMsg.append("\n"); helpMsg.append("\n"); helpMsg.append("The path can be relative, as in the example, or absolute. Include files may contain date tags.\n"); helpMsg.append("\n"); helpMsg.append("The date tag and ''include'' updates can be performed automatically when the buffer is saved. Therefore\n"); helpMsg.append("install Ollie Rutherfurd's ActionHooks plugin (http://plugins.jedit.org/plugins/?ActionHooks) and set it up:\n"); helpMsg.append("1. Add the Date Tag Buddy macro to the ''BufferUpdate.SAVED'' event list.\n"); helpMsg.append("2. Configure the ActionHooks plugin to start up with JEdit.\n"); helpMsg.append("This ensures that date tags and ''includes'' are automatically updated whenever the HTML buffer is saved. \n"); helpMsg.append("\n"); Macros.message(view, helpMsg.toString()); } // }}} // TO CHANGE THE MATCHED TAGS MODIFY THE CONSTANTS BELOW. final String MODIFICATION_DATE_START = "
"; final String MODIFICATION_DATE_END = "
"; final String MODIFICATION_DATE_PATTERN = "dd MMM yyyy"; // Must have a fixed width for all possible date values. final Locale MODIFICATION_DATE_LOCALE = Locale.US; // Choose between Java Locales for the date format. final String COPYRIGHT_START = "©"; final String COPYRIGHT_END = ""; // {{{ main Buffer buffer = editPane.getBuffer(); String mode = buffer.getMode().getName(); JEditTextArea textArea = editPane.getTextArea(); EditPlugin actionHooksPlugin = jEdit.getPlugin("actionhooks.ActionHooksPlugin"); // Display a help dialog if the macro is not used the right way. if ((!mode.equals("html") && actionHooksPlugin == null) || (!mode.equals("html") && !actionhooks.ActionHooksPlugin.getEnabled())) { displayHelp(); // Update tags only if the buffer is in HTML mode and if the text area is editable. } else if (mode.equals("html") && textArea.isEditable()) { // Save the caret position and the selection. int savedCaretPosition = textArea.getCaretPosition(); Selection[] savedSelection = textArea.getSelection(); // Update the buffer. boolean modifiedIncludes = updateIncludes(); boolean modifiedDateTag = updateModificationDateTag(MODIFICATION_DATE_START, MODIFICATION_DATE_END, MODIFICATION_DATE_PATTERN, MODIFICATION_DATE_LOCALE, textArea); boolean modifiedCopyright = updateCopyrightYear(COPYRIGHT_START, COPYRIGHT_END, textArea); // Restore the caret position and the selection. textArea.setCaretPosition(savedCaretPosition, false); textArea.setSelection(savedSelection); // Only save the buffer if something was changed in order to prevent an // infinite loop when hooked on the BufferUpdate.SAVED event with the // ActionHooks plugin. Save the buffer if the ActionHooks plugin is // enabled, assuming that the user added the BufferUpdate.SAVED event // hook. In all other cases let the user decide when to save the buffer. if ((modifiedIncludes || modifiedDateTag || modifiedCopyright) && (actionHooksPlugin != null) && (actionhooks.ActionHooksPlugin.getEnabled())) { buffer.save(view, null); } } // }}} /* Date_Tag_Buddy.bsh Updates certain date tags in an HTML buffer (the current edit mode must be "html"): 1. Updates the date value of a modification date tag:
Last modified: 01 Mar 2006
2. Updates the year in a copyright text: © Me 2006 Updates all "includes" in an HTML buffer. An include is defined as two HTML comments where the first one contains the path to a file that should be inserted between the comments: The path can be relative, as in the example, or absolute. Included files may contain date tags. The date tag and "include" updates can be automatically updated when the buffer is saved. Therefore install Ollie Rutherfurd's ActionHooks plugin (http://plugins.jedit.org/plugins/?ActionHooks) and set it up: 1. Add the Date Tag Buddy macro to the "BufferUpdate.SAVED" event list. 2. Configure the ActionHooks plugin to start up with JEdit. This ensures that date tags and "includes" are automatically updated whenever the HTML buffer is saved. A modification tag is identified by
and
. A copyright text is identified by © and . The macro assumes that the date value is located at the end of each tag. To change the default behavior of the macro you can modify the constants at its end. Checked for 4.3pre3 API.
*/