From: <ez...@us...> - 2009-06-19 22:07:19
|
Revision: 15494 http://jedit.svn.sourceforge.net/jedit/?rev=15494&view=rev Author: ezust Date: 2009-06-19 22:07:17 +0000 (Fri, 19 Jun 2009) Log Message: ----------- Patch to improve XML Plugin completion popups - ID: 2809253 Also bumped version up to 2.5 And added changelog for this version. Modified Paths: -------------- plugins/XML/branches/relax-ng/XML.props plugins/XML/branches/relax-ng/docs/users-guide.xml plugins/XML/branches/relax-ng/test_data/import_schema/network.xml plugins/XML/branches/relax-ng/xml/parser/XmlParser.java Modified: plugins/XML/branches/relax-ng/XML.props =================================================================== --- plugins/XML/branches/relax-ng/XML.props 2009-06-19 20:58:47 UTC (rev 15493) +++ plugins/XML/branches/relax-ng/XML.props 2009-06-19 22:07:17 UTC (rev 15494) @@ -2,7 +2,7 @@ plugin.xml.XmlPlugin.activate=defer plugin.xml.XmlPlugin.name=XML plugin.xml.XmlPlugin.author=Slava Pestov, Dale Anson, Alan Ezust, Rob McKinnon, Martin Raspe, Jakub Roztocil -plugin.xml.XmlPlugin.version=2.0.9 +plugin.xml.XmlPlugin.version=2.5 plugin.xml.XmlPlugin.docs=index.html plugin.xml.XmlPlugin.depend.0=jdk 1.5 Modified: plugins/XML/branches/relax-ng/docs/users-guide.xml =================================================================== --- plugins/XML/branches/relax-ng/docs/users-guide.xml 2009-06-19 20:58:47 UTC (rev 15493) +++ plugins/XML/branches/relax-ng/docs/users-guide.xml 2009-06-19 22:07:17 UTC (rev 15494) @@ -594,7 +594,7 @@ </para> </section> </chapter> - + <appendix id="xmlindenter-changelog"> <title>XMLIndenter: Change log</title> <para> @@ -705,8 +705,20 @@ </appendix> <appendix id="changes"> + + <title>Change log</title> <itemizedlist> - <listitem><para><emphasis role="bold">Version 2.0.8</emphasis> + + <listitem><para><emphasis role="bold">Version 2.5</emphasis> + Requires jEdit 4.3pre16, Java 1.5, ErrorList 1.4, SideKick 0.7.7 and XercesPlugin 2.9.0. + </para> + <itemizedlist> + <listitem><para> Improved XML Completion (Greg Knittl - #2801814, 2809253) </para></listitem> + </itemizedlist> +</listitem> + + +<listitem><para><emphasis role="bold">Version 2.0.8</emphasis> Requires jEdit 4.3pre7, Java 1.5, ErrorList 1.4, SideKick 0.6.3 and XercesPlugin 2.8.0. </para> <itemizedlist> @@ -721,7 +733,7 @@ <listitem><para> From HTML sidekick, delegates to Ecmascript only when ecmascript is the preferred parser for the javascript edit mode. (Dale Anson) </para></listitem> </itemizedlist> </listitem> - + <listitem><para><emphasis role="bold">Version 2.0.6</emphasis> Requires jEdit 4.3pre7, Java 1.5, ErrorList 1.4, SideKick 0.6.3 and XercesPlugin 2.8.0. </para> <itemizedlist> @@ -731,7 +743,7 @@ </para> </listitem> <listitem><para> Un-broke characters to entities (1579232) and error highlighting (1597017) (Alan Ezust) </para></listitem> </itemizedlist> - </listitem> + </listitem> <listitem><para><emphasis role="bold">Version 2.0.5</emphasis> Requires jEdit 4.3pre7, Java 1.5, ErrorList 1.4, SideKick 0.6.3 and XercesPlugin 2.8.0. Modified: plugins/XML/branches/relax-ng/test_data/import_schema/network.xml =================================================================== --- plugins/XML/branches/relax-ng/test_data/import_schema/network.xml 2009-06-19 20:58:47 UTC (rev 15493) +++ plugins/XML/branches/relax-ng/test_data/import_schema/network.xml 2009-06-19 22:07:17 UTC (rev 15494) @@ -1,11 +1,9 @@ <section id="styletest" c="remark" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation='http://slackerdoc.tigris.org/xsd/slackerdoc.xsd' -> + xsi:noNamespaceSchemaLocation='http://slackerdoc.tigris.org/xsd/slackerdoc.xsd'> <title> Stylesheet Test </title> - - <abstract> <p> - This file is an example that uses all Slacker document elements, and a subset of common/popular elements from the <a href="@docbookdoc@/part2.html">Docbook</a> language. It's also a testcase for the scripts and stylesheets. </p> + <abstract> <p> + This file is an example that uses all Slacker document elements, and a subset of common/popular elements from the <a href="@docbookdoc@/part2.html">Docbook</a> language. It's also a testcase for the scripts and stylesheets. </p> </abstract> <question> @@ -34,108 +32,107 @@ <p condition="textbook"> This text is in a condition="textbook" element, so it only appears - in the textbook version. + in the textbook version. </p> <p c="textbook"> - <tt>ant bookhtml</tt> or <tt>ant pdf</tt> both generate versions that include textbook elements. An alternate way of describing textbook conditionals is with the <tag>textbook</tag> tag. This is equivalent to a <tag>p condition="textbook"</tag> + <tt>ant bookhtml</tt> or <tt>ant pdf</tt> both generate versions that include textbook elements. An alternate way of describing textbook conditionals is with the <tag>textbook</tag> tag. This is equivalent to a <tag>p condition="textbook"</tag> </p> - <figure id="examplefigure"> <title> A title for a figure </title> <img scale="50" src="./uml/entitymgr.png" alt="a beautiful UML diagram" /> </figure> <p condition="slides" > - <tt>ant slides</tt> generates a slides version of the document. Like <tag>textbook</tag>, <tag>slides</tag> is another conditional name and tag. + <tt>ant slides</tt> generates a slides version of the document. Like <tag>textbook</tag>, <tag>slides</tag> is another conditional name and tag. </p> - + <ol> - <li> Here is an ordered list. item. + <li> Here is an ordered list. item. It should look familiar since it's tag is borrowed from XHTML. </li> - <li> + <li> This ordered list shows you which version(s) you are reading: </li> <li condition="textbook"> You are reading textbook text. </li> <li condition="slides"> You are reading slides text. </li> <li condition="remark"> You are also reading draft remarks. These are notes to other authors and reviewers, and some sections under construction. </li> <li condition="solution"> Solutions to exercises </li> - <li condition="instructor"> Instructor's notes </li> + <li condition="instructor"> Instructor's notes </li> <li condition="junk"> This is a junk tag. It shouldn't appear at all since I haven't defined it as a condition in the script yet.</li> <li condition="todo"> Make it possible to detect new conditions? Or have "all" blindly include everything? - </li> + </li> <li> This listitem item appears in every version </li> </ol> - + <p> An <b>admonition</b> is a Docbook tag with a title and an associated icon. </p> - + <p> Here are the available admonitions. The original Docbook admonitions are all supported in Slacker's Docbook; however, we have added two new tags: <tag>prereq</tag> and <tag>question</tag>. - </p> - + </p> + <warning> <title> Warning </title> - <p> - Danger Will Robinson. Things to be careful about. </p> + <p> + Danger Will Robinson. Things to be careful about. </p> </warning> - <caution> <title> Caution </title> - <p> + <caution> <title> Caution </title> + <p> Cautions can be dangerous to your health. - They happen to be mapped to <tag>warning</tag> anyway. </p> + They happen to be mapped to <tag>warning</tag> anyway. </p> </caution> - + <note> <title> This is a note </title> - <p> - Take note of that. It's important! + <p> + Take note of that. It's important! </p> </note> - + <important> <title> Important </title> - <p> + <p> This is actually not so important, since it's mapped to <tag>note</tag>. </p> </important> - - <tip> <title> Tip </title> <p> + + <tip> <title> Tip </title> <p> This is a tip. It's very good advice. It could save you time. </p> - + </tip> - <question> - <p> - The Docbook meaning of <tag>question</tag> remains the same if it appears inside a <tag>qandaset</tag>. However, if it appears where a other admonitions are allowed, <tag>question</tag> is mapped to the docbook <tt>important</tt> admonition. </p> + <question> + <p> + The Docbook meaning of <tag>question</tag> remains the same if it appears inside a <tag>qandaset</tag>. However, if it appears where a other admonitions are allowed, <tag>question</tag> is mapped to the docbook <tt>important</tt> admonition. </p> </question> - + <prereq> - <p> - This is inside a <tag>prereq</tag>. It's mapped to the + <p> + This is inside a <tag>prereq</tag>. It's mapped to the <tag>caution</tag> admonition since we are not using it. </p> </prereq> - - <p> + + <p> The glyphs are taken from Bobco fonts, with copyrights held by the <a href="http://www.subgenius.com/">Subgenius Foundation</a> <footnote><para> Praise Bob! </para></footnote>.</p> - + <sidebar> - <title> This is the title of a sidebar </title> <p> + <title> This is the title of a sidebar </title> <p> This is a sidebar. Passages which elaborate further ideas in the slides belong in a sidebar. </p> </sidebar> - + <formalpara> <title> A formalpara about parabullet (<tag>pb</tag>) </title> <p> - This is a <tag>formalpara</tag> which is discussing the parabullet that follows. parabullets are very interesting because they are rendered quite differently depending on which version you are generating. In order to simultaneously write bullet-text for the slides version and paragraph text in the book version without repeating yourself, mark each paragraph as a <tag>pb</tag>, marking up each major idea in it as an <tag>li</tag>. </p> + This is a <tag>formalpara</tag> which is discussing the parabullet that follows. parabullets are very interesting because they are rendered quite differently depending on which version you are generating. In order to simultaneously write bullet-text for the slides version and paragraph text in the book version without repeating yourself, mark each paragraph as a <tag>pb</tag>, marking up each major idea in it as an <tag>li</tag>. </p> </formalpara> - + <pb> <li> This content is inside <tag>pb</tag>, which is short for <i>parabullet</i>. </li> - <li> When a <tag>pb</tag> is encountered, and condition['slides'] is not true, we translate <tag>pb</tag> into <tag>para</tag>, stripping all <tag>li</tag>s. + <li> When a <tag>pb</tag> is encountered, and condition['slides'] is not true, we translate <tag>pb</tag> into <tag>para</tag>, stripping all <tag>li</tag>s. </li> <li> Otherwise, the preprocessor translates each <tag>li</tag> into a <tag>listitem</tag> <tag>para</tag> and the <tag>pb</tag> maps to an <tag>unorderedlist</tag>. They appear as bullets in the slides version and paragraphs in the textbook. </li> <li> This means that unordered lists inside <tag>pb</tag>should be properly punctuated. </li> - <li> <tag>li</tag> is much easier to type than <tag>listitem</tag> <tag>para</tag>. </li> + <li> <tag>li</tag> is much easier to type than <tag>listitem</tag> <tag>para</tag>. </li> </pb> @@ -143,29 +140,29 @@ <li> This is a regular unordered list. </li> <li> It should appear always as a bullet in all versions. </li> </ul> - - <todo> + + <todo> The <tag>todo</tag> element is mapped to <tag>p</tag> but also has a bold heading that says "Todo Items". </todo> - + <ol> <li> Add keyword-processing with hyperlinks to the <a href="@docbookdoc@/ref-elements.html">Element Reference Guide</a>. We have it already in the C++ textbook. </li> </ol> - - - <p c="remark"> + + + <p c="remark"> This is a paragraph marked conditionally with remark, but using the abbreviated c= attribuite. Since it is a remark, it should only be visible in the development version, and is styled for remark. </p> <remark> The <tag>remark</tag> tag is just shorthand for a <tag>para class="remark"</tag> </remark> - - <solution> - We have a solution tag which creates a formalpara with a "solution" title, and wraps around whatever needs to be part of the solution. Content between solution tags can easily be excluded from a particular target if you want to control the time and place of its release (e.g., to students). + + <solution> + We have a solution tag which creates a formalpara with a "solution" title, and wraps around whatever needs to be part of the solution. Content between solution tags can easily be excluded from a particular target if you want to control the time and place of its release (e.g., to students). </solution> - + <p condition="remark"> <tt>ant dev</tt> Generates the HTML "development" version which contains every conditional included in the output, highlighted in different colors as specified in the css file. </p> @@ -177,19 +174,19 @@ <todo> Add conditional inclusion tags for XML inside proper XML comments. </todo> - + <p> When including C++, you can use //start and //end comments to switch on/off inclusion. </p> - + <p> In addition, c-style comments appear as callouts in the listings. </p> <p> Click on filename, or the "link" icon below the included code to see the original file before processing. Notice we only see a small piece. </p> - <include src="dialogs.cpp" mode="cpp" segid="menu"/> + <include src="dialogs.cpp" mode="cpp" segid="menu"/> <p> In this next example, there is no link, and in fact, the included file has restricted permissions, so you should not be able to see it even if you guessed at its url. </p> - - <include src="sample.cpp" mode="cpp" segid="main" link="false"/> - + + <include src="sample.cpp" mode="cpp" segid="main" link="false"/> + </section> Modified: plugins/XML/branches/relax-ng/xml/parser/XmlParser.java =================================================================== --- plugins/XML/branches/relax-ng/xml/parser/XmlParser.java 2009-06-19 20:58:47 UTC (rev 15493) +++ plugins/XML/branches/relax-ng/xml/parser/XmlParser.java 2009-06-19 22:07:17 UTC (rev 15494) @@ -5,6 +5,7 @@ * * Copyright (C) 2000, 2003 Slava Pestov * Portions copyright (C) 2001 David Walend + * Copyright (C) 2009 Greg Knittl * * The XML plugin is licensed under the GNU General Public License, with * the following exception: @@ -18,6 +19,7 @@ import java.util.*; import org.gjt.sp.jedit.*; +import org.gjt.sp.jedit.syntax.*; import sidekick.*; import xml.completion.*; import xml.completion.ElementDecl.AttributeDecl; @@ -79,7 +81,54 @@ //{{{ complete() method public SideKickCompletion complete(EditPane editPane, int caret) + // The challenge of realtime keystroke completion for xml is that + // the syntax may not be well formed at any given keystroke. + // It would be ideal to start from an incremental xml parser/validator. + // A Google search shows the nxml mode for EMACS is probably the + // most advanced implementation of this to date (I haven't tried it). + // complete() could check if SideKick parse on keystroke is + // enabled and use that xml parse tree if available. + // + // jEdit parses syntax per keystroke and just as it usually looks reasonable + // on the screen so it is usually a reasonable basis for suggesting completions + // + // This patch uses syntax information to improve the validity of + // completion popups in a number of ways, including reducing + // attribute completion popups in text areas. + // This relies on XML text areas being unparsed, Token.NULL. + // This true for the two xml modes I'm aware of: xml and xsl + // + // If a new xml-derived edit mode say, xhtml/css, parsed the css syntax + // and then used this complete method, attribute popups would appear in + // it's css text areas + // This code could add additional logic to handle such modes + // Better would be to have nested levels of syntax parsing + // so that this code could handle xml level completion for all + // xml derived syntaxes. Or process syntax in parallel to jEdit with a + // custom rules set + // + // jEdit syntax Token.END represents the newline character(s) at the end of each + // line. It is a visual marker that does not correspond to XML syntax. + // XML allows newlines in at least text areas, attribute values, comments, CDATA. + // I have treated Token.END as whitespace and attribute completion will pop up + // incorrectly in some circumstances at the start of lines after the first line, + // such as when a text area spans multiple lines + // + // Additional fixes: + // enable attribute completion after attribute values that contain / + // enable attribute completion for elements that span multiple lines + // + // For testing purposes, manually invoking completion from + // Plugins > SideKick > Show Completion Popup seems to display + // more exceptions in the code than keystroke activation + // + // Greg Knittl 2009-06-19 { + // caret can be at 0 when invoking completion from SideKick plugin menu + // or through backspace + // could pop up a pro forma xml declaration for caret = 0 + if (caret == 0) + return null; SideKickParsedData _data = SideKickParsedData .getParsedData(editPane.getView()); if(!(_data instanceof XmlParsedData)) @@ -90,57 +139,114 @@ XmlParsedData data = (XmlParsedData)_data; Buffer buffer = editPane.getBuffer(); - - // first, we get the word before the caret - List allowedCompletions = new ArrayList(20); - - int caretLine = buffer.getLineOfOffset(caret); + + int lastchar = caret - 1; + int lastcharLine = buffer.getLineOfOffset(lastchar); String text = buffer.getText(0,caret); - int lineStart = buffer.getLineStartOffset(caretLine); + int lineStart = buffer.getLineStartOffset(lastcharLine); + + // get syntax tokens for the line of character before the caret + // ghk not sure if this duplicates jEdits syntax tokenization + // since this is per keystroke, performance is of some importance + DefaultTokenHandler tokenHandler = new DefaultTokenHandler(); + buffer.markTokens(lastcharLine,tokenHandler); + Token token = tokenHandler.getTokens(); + while(token.id != Token.END) + { + int next = lineStart + token.length; + if (lineStart <= lastchar && next > lastchar) + break; + lineStart = next; + token = token.next; + } + + // could test for comments and return at this point + // continuing allows some completion within comments + // for example when comments contain lines of valid xml + + String modename = buffer.getMode().getName(); + int mode = -1; boolean insideQuote = false; int wordStart = -1; int attribStart = -1; - for(int i = caret - 1; i >= lineStart; i--) + // iterate backwards towards start of file to find a tag + // or the & indicating the start of an entity + // i >= 1 enables attribute completion for elements spanning multiple lines + for(int i = lastchar; i >= 1; i--) { char ch = text.charAt(i); - if(ch == '<' || ch == '&') + if(ch == '<') { wordStart = i; if (mode == -1) - mode = (ch == '<' ? ELEMENT_COMPLETE : ENTITY_COMPLETE); + mode = ELEMENT_COMPLETE; break; } + if(ch == '&') + { + // & and ATTRIBUTE_COMPLETE is invalid because it implies whitespace in the entity + // can occur with attributes enclosed by misinterpreted 's that contain an entity followed by a space + if (mode == ATTRIB_COMPLETE) + return null; + wordStart = i; + if (mode == -1) + mode = ENTITY_COMPLETE; + break; + } + // " in text area or ' delimiting attribute values break this logic + // xslt often uses multiple levels of quotes for XPath else if (ch == '"') { insideQuote = !insideQuote; } - else if (!insideQuote && attribStart == -1 && ch == ' ') { + // whitespace is not allowed in element tag names or entities; + // in xml mode attributes can only occur in Token.MARKUP or Token.END + // this solves the problem of attributes defined with ' for xml mode + // xsl mode parses the markup more finely so the logic gets more complex but probably could be done + else if (Character.isWhitespace(ch) && !(token.id == Token.MARKUP || token.id == Token.END) && modename.equals("xml")) { + return null; + } + // whitespace is not allowed in element tags or entities; + // no attributes allowed in text area (Token.NULL) or comments (Token.COMMENT1) so exit + else if (Character.isWhitespace(ch) && (token.id == Token.NULL || token.id == Token.COMMENT1)) { + return null; + } + // no break to allow loop to iterate back to find next < or & + // add test for not Token.NULL (text area) + else if (Character.isWhitespace(ch) && token.id != Token.NULL && !insideQuote && mode == -1) { attribStart = i+1; mode = ATTRIB_COMPLETE; } - else if(ch == '/' && (i == 0 || text.charAt(i - 1) != '<')) - return null; } if (insideQuote) mode=-1; String closingTag = null; String word; + List allowedCompletions = new ArrayList(20); + if(wordStart != -1 && mode != -1) { - int st = wordStart + 1; - word = text.substring(st, caret); - int firstSpace = word.indexOf(' '); - if (firstSpace > 0) - word = word.substring(0, firstSpace); + String tolastchar = text.substring(wordStart + 1, caret); + // avoid ArrayIndexOutOfBoundsException for < or & followed by one or more spaces + if (tolastchar.trim().length() == 0) + word = ""; + else + { + String firstSpace = tolastchar.split("\\s")[0]; + if (firstSpace.length() > 0) + word = firstSpace; + else + word = text.substring(wordStart + 1, caret); + } List completions = new ArrayList(); if(mode == ELEMENT_COMPLETE) { - completions = data.getAllowedElements(buffer, caret); - TagParser.Tag tag = TagParser.findLastOpenTag(text,caret - 2,data); + completions = data.getAllowedElements(buffer, lastchar); + TagParser.Tag tag = TagParser.findLastOpenTag(text,lastchar - 1,data); if(tag != null) closingTag = tag.tag; @@ -187,8 +293,7 @@ } else if (mode == ATTRIB_COMPLETE) { - int lastSpace = text.lastIndexOf(' ', caret); - String prefix = text.substring(lastSpace+1, caret); + String prefix = text.substring(attribStart, caret); ElementDecl decl = data.getElementDecl(word); if (decl != null) completions = decl.attributes; for (int i=0; i<completions.size(); ++i) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |