Finding the node at the caret positoin

Post here questions and problems related to oXygen frameworks/document types.
Hydrogen
Posts: 15
Joined: Fri Jan 27, 2012 11:40 am

Finding the node at the caret positoin

Post by Hydrogen » Thu Sep 18, 2014 3:06 pm

Hi,

In a plugin I need to extract data from at document relative to the current node, i.e. the node at which the caret is. First I need to find that node. I am using evaluateXPath(). The problem is crafting the Xpath expression that will yield the correct node. I have found that the . operator will select the current element. If the caret is in a text node the . operator will select the enclosing element node. node() and text() will sometimes select way to much for my need. I will illustrate with this very simplified example:

Code: Select all

<xml>
<bigelement>
<element> textinside </element> textoutside
<element> textinside </element> textoutside
<element> textinside </element> textoutside
</bigelement>
<bigelement>
<element> textinside </element> textoutside
<element> textinside </element> textoutside with caret
<element> textinside </element> textoutside
</bigelement>
</xml>
If I place the caret at "textinside" both node() and text() will select the current text node and nothing else. If however I place the caret at "textoutside" evaluateXPath() will return an array of nodes and I do not know how to pick the one with the caret in. node() will select everything inside "bigelement", both element nodes and text nodes. text() will select the four text nodes that are inside "bigelement" but not inside "element":

Code: Select all


                       /xml[1]/bigelement[2]/text()[1]
textoutside /xml[1]/bigelement[2]/text()[2]
textoutside with caret /xml[1]/bigelement[2]/text()[3]
textoutside /xml[1]/bigelement[2]/text()[4]
My question is: Can I craft an XPath expression that will return the text node with the caret in this case? If not, can I find (using Java code) which of the returned text nodes contain the caret?

Radu
Posts: 6325
Joined: Fri Jul 09, 2004 5:18 pm

Re: Finding the node at the caret positoin

Post by Radu » Thu Sep 18, 2014 3:24 pm

Hi,

One question, do you want this when editing the XML in the Text or in the visual Author editing mode?

Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com

Hydrogen
Posts: 15
Joined: Fri Jan 27, 2012 11:40 am

Re: Finding the node at the caret positoin

Post by Hydrogen » Thu Sep 18, 2014 3:29 pm

The users are all using text mode.

Radu
Posts: 6325
Joined: Fri Jul 09, 2004 5:18 pm

Re: Finding the node at the caret positoin

Post by Radu » Thu Sep 18, 2014 4:14 pm

Hi,

Unfortunately for the text page we do not have much API to help you navigate the nodes structure, we'll try to add more API for a future version.

My only suggestion would be something like this (I did not test the code below but with some small changes it should work):

Code: Select all

    WSXMLTextNodeRange[] elementsByXPath = xmlTextPage.findElementsByXPath(".");
if(elementsByXPath != null && elementsByXPath.length > 0){
//This should always be only one match, the entire XML element inside which the caret is placed.
WSXMLTextNodeRange range = elementsByXPath[0];
int startOffset = xmlTextPage.getOffsetOfLineStart(range.getStartLine()) + range.getStartColumn() - 1;
int endOffsetOffset = xmlTextPage.getOffsetOfLineStart(range.getEndLine()) + range.getEndColumn() - 1;
int caretOffset = xmlTextPage.getCaretOffset();

//This should be a string containing all the XML element content (including start and end tags).
String xmlText = xmlTextPage.getDocument().getText(startOffset, endOffsetOffset - startOffset);
int offsetInXMLTextRelativeToStartRange = caretOffset - startOffset;
int lastEndTagOffset = xmlText.substring(0, offsetInXMLTextRelativeToStartRange).lastIndexOf(">");
int firstStartTagOffset = xmlText.indexOf("<", offsetInXMLTextRelativeToStartRange);
if(firstStartTagOffset != -1 && lastEndTagOffset != -1 && firstStartTagOffset < lastEndTagOffset){
//The text node containing the caret should be between them
String textNodeContent = xmlText.substring(lastEndTagOffset + 1, firstStartTagOffset);
}
}
Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com

Hydrogen
Posts: 15
Joined: Fri Jan 27, 2012 11:40 am

Re: Finding the node at the caret positoin

Post by Hydrogen » Thu Sep 18, 2014 5:08 pm

Thank you for the reply. I see that I will have to create some kind of workaround rather than believe I can craft a super XPath. That is actually helpful to know.

In the real application I am actually looking for data that is relative to the node with the caret. For example something like this:

Code: Select all

(node()/preceding::*/@href)[last()]
A possible workaround could be to use getCaretOffset() to compare to the output from findElementsByXPath() to select which of the outputs from evaluateXPath()to use. This assumes the two functions returnes the same number and sequence of nodes.

Another workaround might be to use regexp rather than XPath.

Thanks for your help!

Re possible future API I may have two immediate suggestions:
1) Implement current() and let it represent the exact node at the caret (whatever node type that is). This will not break compatibility since current() is not currently implemented. The difference between what is returned from the . operator and current() may break peoples expectations.
2) Create an Oxygen proprietary function that returns the number of the caret containing node of a sequence. This would allow a construct like:

Code: Select all

node()[oxy_caretposition()]

Radu
Posts: 6325
Joined: Fri Jul 09, 2004 5:18 pm

Re: Finding the node at the caret positoin

Post by Radu » Fri Sep 19, 2014 2:09 pm

Hi,

Please see some comments below:
A possible workaround could be to use getCaretOffset() to compare to the output from findElementsByXPath() to select which of the outputs from evaluateXPath()to use. This assumes the two functions returnes the same number and sequence of nodes.
Right, so call for example:

Code: Select all

 WSXMLTextNodeRange[] ranges = access.findElementsByXPath("text()");
to get all the direct child text nodes of the current element in which the caret is, then use the text ranges to search for the range where the caret is placed. This could possibly work and be more elegant than my proposed workaround.
Another workaround might be to use regexp rather than XPath.
Yes, regexp is a possible fallback, using XML aware ways is always better if possible.
Re possible future API I may have two immediate suggestions:
1) Implement current() and let it represent the exact node at the caret (whatever node type that is). This will not break compatibility since current() is not currently implemented. The difference between what is returned from the . operator and current() may break peoples expectations.
2) Create an Oxygen proprietary function that returns the number of the caret containing node of a sequence. This would allow a construct like:
We'll take this into account, thanks for giving us feedback.

Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com

bk-one
Posts: 13
Joined: Mon Oct 23, 2017 3:42 pm

Re: Finding the node at the caret positoin

Post by bk-one » Thu Jan 10, 2019 7:27 pm

hi,
are there any developments regarding this?
thanks,
barbara

Radu
Posts: 6325
Joined: Fri Jul 09, 2004 5:18 pm

Re: Finding the node at the caret positoin

Post by Radu » Fri Jan 11, 2019 1:02 pm

Hi Barbara,

So you are interested in API for the Text editing mode, right?
I did not find any mention that the proposals were implemented. But there is this method:

Code: Select all

ro.sync.exml.workspace.api.editor.page.text.xml.WSXMLTextEditorPage.getDocumentController()
https://www.oxygenxml.com/InstData/Edit ... oller.html

which allows you to insert XML fragments relative to a certain XPath expression which could be "." for example.

Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com

Post Reply