Page 1 of 1

Finding the node at the caret positoin

Posted: Thu Sep 18, 2014 3:06 pm
by Hydrogen
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?

Re: Finding the node at the caret positoin

Posted: Thu Sep 18, 2014 3:24 pm
by Radu
Hi,

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

Regards,
Radu

Re: Finding the node at the caret positoin

Posted: Thu Sep 18, 2014 3:29 pm
by Hydrogen
The users are all using text mode.

Re: Finding the node at the caret positoin

Posted: Thu Sep 18, 2014 4:14 pm
by Radu
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

Re: Finding the node at the caret positoin

Posted: Thu Sep 18, 2014 5:08 pm
by Hydrogen
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()]

Re: Finding the node at the caret positoin

Posted: Fri Sep 19, 2014 2:09 pm
by Radu
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

Re: Finding the node at the caret positoin

Posted: Thu Jan 10, 2019 7:27 pm
by bk-one
hi,
are there any developments regarding this?
thanks,
barbara

Re: Finding the node at the caret positoin

Posted: Fri Jan 11, 2019 1:02 pm
by Radu
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