Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can the Script Editor incorporate a Tooltips feature? #77

Closed
ajtruckle opened this issue May 17, 2024 · 10 comments
Closed

Can the Script Editor incorporate a Tooltips feature? #77

ajtruckle opened this issue May 17, 2024 · 10 comments
Assignees
Labels
enhancement New feature or request
Milestone

Comments

@ajtruckle
Copy link
Owner

Is your feature request related to a problem? Please describe.
Visual Studio 2022 includes a tooltip feature that is extremely useful for coding.

For example:

image

Describe the solution you'd like
Is it possible for the Script Editor to integrate a similar feature?

Additional context
The tooltips display when you hover the mouse over keywords.

@ajtruckle ajtruckle added the enhancement New feature or request label May 17, 2024
@ajtruckle
Copy link
Owner Author

This feature is called Call tips in the Scintilla control. To quote:

Call tips are small windows displaying the arguments to a function and are displayed after the user has typed the name of the function. They normally display characters using the font facename, size and character set defined by STYLE_DEFAULT. You can choose to use STYLE_CALLTIP to define the facename, size, foreground and background colours and character set with SCI_CALLTIPUSESTYLE. This also enables support for Tab characters. There is some interaction between call tips and autocompletion lists in that showing a call tip cancels any active autocompletion list, and vice versa.

So this looks like a big task to undertake.

@cengizu
Copy link

cengizu commented Jun 5, 2024

Yes, that would be great! This will certainly increase usability by reducing ambiguity and guiding users.

@ajtruckle
Copy link
Owner Author

@cengizu
I agree it would be a nice feature to implement. Maybe one day!

@ajtruckle
Copy link
Owner Author

@cengizu
I have been doing some research and found out how to display some text after you have selected an item from a autocomplete list.

The test code:

#pragma warning(suppress: 26440 26461)
void CScintillaHelper::OnAutoCCompleted(_Inout_ Scintilla::NotificationData* pSCNotification)
{
#ifdef DEBUG
	// Check if the notification data is valid
	if (pSCNotification && pSCNotification->text)
	{
		CString strSelectedText(pSCNotification->text);
		strSelectedText.AppendFormat(L": Display parameter list or help text for function");
		m_edit.CallTipShow(pSCNotification->position, strSelectedText);
	}
#endif // DEBUG
}

Here it is in action:

Recording.2025-01-18.182234.mp4

What we have to do now is agree what the tip text should be for each supported command:

  • apply-imports
  • apply-templates
  • attribute
  • call-template
  • choose
  • comment
  • copy
  • copy-of
  • element
  • fallback
  • for-each
  • if
  • include
  • key
  • message
  • namespace-alias
  • number
  • otherwise
  • output
  • param
  • preserve-space
  • processing-instruction
  • sort
  • strip-space
  • text
  • value-of
  • variable
  • when
  • with-param
  • template

@ajtruckle
Copy link
Owner Author

@cengizu
I asked ChatGPT to suggest some context help text for those XSLT functions. Here is the updated code snippet:

#pragma warning(suppress: 26440 26461)
void CScintillaHelper::OnAutoCCompleted(_Inout_ Scintilla::NotificationData* pSCNotification)
{
#ifdef DEBUG
	// Check if the notification data is valid
	if (pSCNotification && pSCNotification->text)
	{
		CString strSelectedText(pSCNotification->text);

		std::map<CString, CString> xsltFunctions = {
			{L"apply-imports", L"Applies the templates from imported stylesheets."},
			{L"apply-templates", L"Applies templates rules to elements in the current node-set."},
			{L"attribute", L"Creates an attribute for the current element."},
			{L"call-template", L"Calls a named template and applies it to the current context."},
			{L"choose", L"Evaluates one of a set of templates based on a condition."},
			{L"comment", L"Inserts a comment node in the output."},
			{L"copy", L"Copies the current node and applies templates to its children."},
			{L"copy-of", L"Creates a copy of the current node and applies templates to its children."},
			{L"element", L"Creates a new element node."},
			{L"fallback", L"Used within templates to provide alternative content."},
			{L"for-each", L"Iterates over a node-set and applies templates to each node."},
			{L"if", L"Evaluates a condition and applies templates based on the result."},
			{L"include", L"Includes another stylesheet into the current stylesheet."},
			{L"key", L"Generates a key from a node-set."},
			{L"message", L"Outputs a message to the XSLT processor."},
			{L"namespace-alias", L"Defines a namespace alias."},
			{L"number", L"Formats a number according to a specified pattern."},
			{L"otherwise", L"Defines the default template in a choose statement."},
			{L"output", L"Specifies the serialization properties of the output document."},
			{L"param", L"Declares a parameter for use in templates."},
			{L"preserve-space", L"Specifies that whitespace should be preserved in output."},
			{L"processing-instruction", L"Inserts a processing instruction into the output."},
			{L"sort", L"Sorts a node-set."},
			{L"strip-space", L"Specifies that whitespace should be stripped from output."},
			{L"text", L"Inserts text content into the output."},
			{L"value-of", L"Evaluates an XPath expression and inserts the result into the output."},
			{L"variable", L"Declares a variable for use in templates."},
			{L"when", L"Specifies conditions for template application in templates."},
			{L"with-param", L"Sets the value of a parameter for use in a template."},
			{L"template", L"Defines a template for transforming nodes."}
		};

		if (xsltFunctions.contains(strSelectedText)) {
			CString callTipText = strSelectedText;

			callTipText += L": ";
			callTipText += xsltFunctions.at(strSelectedText);

			m_edit.CallTipShow(pSCNotification->position, callTipText);
		}
	}
#endif // DEBUG
}

Therefore, if one were to type in xsl: and subsequently select value-of from the autocomplete list, the following calltip will be displayed:

Image

We need determine whether to display the context help text or the parameter list for the function.

@ajtruckle
Copy link
Owner Author

@cengizu

We need determine whether to display the context help text or the parameter list for the function.

I have a great idea! 💡 Continue to show the context help whenever the user selects a function from the autocomplete list. Additionally, leverage the editor's ability to detect when the cursor is hovering over text. When this happens, we can identify the function under the cursor and display a calltip with the function's parameter list.

@ajtruckle ajtruckle self-assigned this Jan 19, 2025
@ajtruckle ajtruckle added this to the 25.3.0 milestone Jan 19, 2025
@ajtruckle
Copy link
Owner Author

@cengizu
This is a useful resource that lists all the XSLT Elements.

Here is an example of hovering the mouse over the elements in the XSLT file:

Recording.2025-01-19.102716.mp4

@ajtruckle
Copy link
Owner Author

ajtruckle commented Jan 20, 2025

@cengizu
I have decided to based the call tips on this XSLT Elements Reference.

  • Additional XSLT Elements
    • attribute-set
    • decimal-format
    • import
    • transform

Attributes

I am thinking of extending the call tips to include the attributes for each XSLT element. The linked XSLT reference has good explanations.

For example: <xsl:apply-templates>

Attribute Value Description
select expression Optional. Specifies the nodes to be processed. An asterisk selects the entire node-set. If this attribute is omitted, all child nodes of the current node will be selected
mode name Optional. If there are multiple ways of processing defined for the same element, distinguishes among them

I am assuming that all XSLT elements sharing the same attributes will convey the same meaning. To be confirmed.

@ajtruckle
Copy link
Owner Author

ajtruckle commented Jan 20, 2025

@cengizu

I am assuming that all XSLT elements sharing the same attributes will convey the same meaning. To be confirmed.

I made an incorrect assumption. The explanations differ slightly. Here are some examples for select:

  • apply-templates: Optional. Specifies the nodes to be processed. An asterisk selects the entire node-set. If this attribute is omitted, all child nodes of the current node will be selected.
  • value-of: Required. An XPath expression that specifies which node/attribute to extract the value from. It works like navigating a file system where a forward slash (/) selects subdirectories.

Therefore, I am unable to include calltips for the attributes. I must find a robust method to trace backwards until I locate the XSLT element associated with the attribute. Perhaps we implement this in a future release. 💪

@ajtruckle
Copy link
Owner Author

ajtruckle commented Jan 21, 2025

@cengizu
I'm adding some code here for my reference. This code checks the last 100 characters to determine the function associated with the attribute. I have tested it, and it works as intended. This allows us to create a lookup map of context help strings for each element's supported attributes, as I originally envisioned.

const static std::vector<CString> g_xsltAttributes = {
	L"case-order",
	L"cdata-section-elements",
	L"collation",
	L"data-type",
	L"decimal-separator",
	L"disable-output-escaping",
	L"digit",
	L"doctype-public",
	L"doctype-system",
	L"elements",
	L"encoding",
	L"exclude-result-prefixes",
	L"extension-element-prefixes",
	L"format",
	L"grouping-separator",
	L"grouping-size",
	L"href",
	L"id",
	L"indent",
	L"infinity",
	L"lang",
	L"letter-value",
	L"match",
	L"media-type",
	L"method",
	L"mode",
	L"name",
	L"NaN",
	L"namespace",
	L"omit-xml-declaration",
	L"order",
	L"pattern-separator",
	L"per-mille",
	L"percent",
	L"priority",
	L"result-prefix",
	L"select",
	L"standalone",
	L"stylesheet-prefix",
	L"terminate",
	L"test",
	L"use",
	L"use-attribute-sets",
	L"value",
	L"version",
	L"zero-digit"
};
auto itAttribute = std::find(g_xsltAttributes.begin(), g_xsltAttributes.end(), strText);
if (itAttribute != g_xsltAttributes.end()) {
	// Step 1: Determine the starting position of the element
	auto startPos = m_edit.PositionRelative(pSCNotification->position, -100); // 100 characters back
	if (startPos < 0) startPos = 0; // Ensure we don't go out of bounds
	const auto length = pSCNotification->position - startPos;

	// Step 2: Retrieve the text range
	if (length > 0) {
		std::string buffer(length, '\0'); // Allocate a buffer of the required size
		Scintilla::TextRangeFull tr{ startPos, pSCNotification->position, buffer.data() };
		m_edit.GetTextRangeFull(&tr);

		// Step 3: Search for <xsl:xxx ...> using regex
		std::regex xslRegex("<xsl:([a-zA-Z0-9_-]+)");

		auto match = std::sregex_iterator(buffer.begin(), buffer.end(), xslRegex);
		auto end = std::sregex_iterator();

		std::string elementName;
		for (auto& it = match; it != end; ++it) {
			elementName = (*it)[1].str(); // Keep updating to the last match
		}
		if (!elementName.empty()) {
			m_edit.CallTipShow(pSCNotification->position, elementName.c_str());
		}
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants