result = new LinkedList<>();
PsiElement element = property.getPrevSibling();
while (element instanceof PsiComment || element instanceof PsiWhiteSpace) {
if (element instanceof PsiComment) {
String commentText = element.getText().replaceFirst("[# ]+", "");
result.add(commentText);
}
element = element.getPrevSibling();
}
return StringUtil.join(Lists.reverse(result),"\n ");
}
```
## Render the Documentation
With easy ways to access the key, the value, the file, and a possible documentation comment,
we now have everything in place to provide a useful implementation of `generateDoc()`.
```java
@Override
public @Nullable String generateDoc(PsiElement element, @Nullable PsiElement originalElement) {
if (element instanceof SimpleProperty) {
final String key = ((SimpleProperty) element).getKey();
final String value = ((SimpleProperty) element).getValue();
final String file = SymbolPresentationUtil.getFilePathPresentation(element.getContainingFile());
final String docComment = SimpleUtil.findDocumentationComment((SimpleProperty) element);
return renderFullDoc(key, value, file, docComment);
}
return null;
}
```
The creation of the rendered documentation is done in a separate method for clarity.
It uses `DocumentationMarkup` to align and format the contents.
```java
private String renderFullDoc(String key, String value, String file, String docComment) {
StringBuilder sb = new StringBuilder();
sb.append(DocumentationMarkup.DEFINITION_START);
sb.append("Simple Property");
sb.append(DocumentationMarkup.DEFINITION_END);
sb.append(DocumentationMarkup.CONTENT_START);
sb.append(value);
sb.append(DocumentationMarkup.CONTENT_END);
sb.append(DocumentationMarkup.SECTIONS_START);
addKeyValueSection("Key:", key, sb);
addKeyValueSection("Value:", value, sb);
addKeyValueSection("File:", file, sb);
addKeyValueSection("Comment:", docComment, sb);
sb.append(DocumentationMarkup.SECTIONS_END);
return sb.toString();
}
```
The `addKeyValueSection()` method used is just a small helper function to reduce repetition.
```java
private void addKeyValueSection(String key, String value, StringBuilder sb) {
sb.append(DocumentationMarkup.SECTION_HEADER_START);
sb.append(key);
sb.append(DocumentationMarkup.SECTION_SEPARATOR);
sb.append("");
sb.append(value);
sb.append(DocumentationMarkup.SECTION_END);
}
```
After implementing all the steps above, the IDE will show the rendered documentation for a Simple key when called with View | Quick Documentation.
## Implement Additional Functionality
We can provide implementations for additional functionality that comes with a `DocumentationProvider`.
For instance, when simply hovering the mouse over the code, it also shows documentation after a short delay.
It’s not necessary that this popup show the exact same information as when calling _Quick Documentation_, but for the purpose of this tutorial, we’ll do just that.
```java
public @Nullable String generateHoverDoc(@NotNull PsiElement element, @Nullable PsiElement originalElement) {
return generateDoc(element, originalElement);
}
```
When the mouse hovers over code with Ctrl/Cmd pressed, the IDE shows navigation information of the symbol under the cursor,
such as its namespace or package.
The implementation below will show the Simple key and the file where it is defined.
```java
@Override
public @Nullable String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) {
if (element instanceof SimpleProperty) {
final String key = ((SimpleProperty) element).getKey();
final String file = SymbolPresentationUtil.getFilePathPresentation(element.getContainingFile());
return "\"" + key + "\" in " + file;
}
return null;
}
```
Finally, View | Quick Documentation can also be called from a selected entry within the autocompletion popup.
In that case, language developers need to ensure that the correct PSI element for generating the documentation is provided.
In the case of Simple Language, the lookup element is already a `SimpleProperty` and no additional work needs to be done.
In other circumstances, you can override `getDocumentationElementForLookupItem() `and return the correct PSI element.
## Addendum: Choosing a Better Target Element
To be able to call View | Quick Documentation for Simple properties in all places of a Java string literal, two steps are required:
1. The extension point needs to be changed from `lang.documentationProvider` to `documentationProvider` because only then
the Simple DocumentationProvider is called for PSI elements with a different language.
2. The `getCustomDocumentationElement()` method needs to be implemented to find the correct target PSI element for creating the documentation.
Therefore, the current version of the code could be extended to check whether View | Quick Documentation was called from inside a Java string or a Simple file.
It then uses PSI and `PsiReference` functionalities to determine the correct target element.
This allows getting documentation for a Simple property no matter where it was called inside a Java string literal or a Simple property definition.
```java
@Override
public @Nullable PsiElement getCustomDocumentationElement(@NotNull Editor editor, @NotNull PsiFile file, @Nullable PsiElement contextElement, int targetOffset) {
if (contextElement != null) {
// In this part the SimpleProperty element is extracted from inside a Java string
if (contextElement instanceof PsiJavaToken && ((PsiJavaToken) contextElement).getTokenType().equals(JavaTokenType.STRING_LITERAL)) {
final PsiElement parent = contextElement.getParent();
final PsiReference[] references = parent.getReferences();
for (PsiReference ref : references) {
if (ref instanceof SimpleReference) {
final PsiElement property = ref.resolve();
if (property instanceof SimpleProperty) {
return property;
}
}
}
}
// In this part the SimpleProperty element is extracted when inside a .simple file
else if (contextElement.getLanguage() == SimpleLanguage.INSTANCE) {
final PsiElement property = PsiTreeUtil.getParentOfType(contextElement, SimpleProperty.class);
if (property != null) {
return property;
}
}
}
return super.getCustomDocumentationElement(editor, file, contextElement, targetOffset);
}
```