Skip to content

Commit

Permalink
Fix message show details on mouse hover (#24)
Browse files Browse the repository at this point in the history
* Fix message show details on mouse hover

* fix pipeline

* fix some qodana issues

---------

Co-authored-by: Dan Timofte <dantimofte@proton.me>
  • Loading branch information
dantimofte and Dan Timofte authored Oct 13, 2024
1 parent bf57a4c commit eef9822
Show file tree
Hide file tree
Showing 17 changed files with 24,353 additions and 28 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ jobs:
dpkg-query -Wf '${Installed-Size}\t${Package}\n' | sort -n | tail -n 100
df -h
echo "Removing large packages"
sudo apt-get update
sudo apt-get remove -y '^dotnet-.*'
sudo apt-get remove -y azure-cli microsoft-edge-stable google-chrome-stable postgresql-14 firefox powershell mono-devel libgl1-mesa-dri
sudo apt-get autoremove -y
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
- v0.3.0 Cmd click for fields and groups to go to the field definition
- v0.3.0 Mouse hovering over a field or group name will display the field definition
- v0.3.0 Add Inspection to replace component reference with the full component definition
- v0.4.0 Mouse hovering over a fix message will display the fix message details
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pluginGroup = ac.quant.quickfixspec
pluginName = quickfix-spec
pluginRepositoryUrl = https://github.com/dantimofte/quickfix-spec
# SemVer format -> https://semver.org
pluginVersion = 0.3.0
pluginVersion = 0.4.0

# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 233
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class QuickfixInlayHintsProvider implements InlayHintsProvider<NoSettings

@Override
public @NotNull String getName() {
return "Quickfix tag inlay hints";
return "Fix tag inlay hints";
}

@Override
Expand All @@ -44,6 +44,30 @@ public class QuickfixInlayHintsProvider implements InlayHintsProvider<NoSettings
return new MyCollector();
}

@Override
public boolean isLanguageSupported(@NotNull Language language) {
return language.isKindOf(XMLLanguage.INSTANCE);
}

public @NotNull SettingsKey<NoSettings> getSettingsKey() {
return KEY;
}

@Override
public @NotNull String getPreviewText() {
return "";
}

@Override
public @NotNull ImmediateConfigurable createConfigurable(@NotNull NoSettings settings) {
return new ImmediateConfigurable() {
@Override
public @NotNull JComponent createComponent(@NotNull ChangeListener changeListener) {
return new JPanel();
}
};
}

private static class MyCollector implements InlayHintsCollector {

// Set the top and bottom insets using magic numbers for better alignment
Expand Down Expand Up @@ -170,28 +194,5 @@ private void maybePresent(

}

@Override
public boolean isLanguageSupported(@NotNull Language language) {
return language.isKindOf(XMLLanguage.INSTANCE);
}

public @NotNull SettingsKey<NoSettings> getSettingsKey() {
return KEY;
}

@Override
public @NotNull String getPreviewText() {
return "";
}

@Override
public @NotNull ImmediateConfigurable createConfigurable(@NotNull NoSettings settings) {
return new ImmediateConfigurable() {
@Override
public @NotNull JComponent createComponent(@NotNull ChangeListener changeListener) {
return new JPanel();
}
};
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ac.quant.quickfixspec.common

import com.intellij.lang.xml.XMLLanguage
import com.intellij.openapi.project.Project
import com.intellij.openapi.components.Service
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.xml.XmlFile
import java.net.URL

@Service(Service. Level. PROJECT)
class FixDataDictionaryService(private val project: Project) {

private var fields: FixFields? = null

init {
loadFixSpecs()
}

private fun loadFixSpecs() {
val filePath: URL? = this::class.java.getResource("/spec/FIX44.xml")

val fixSpecs : PsiFile? = PsiFileFactory.getInstance(project).createFileFromText(
"FIX40.xml",
XMLLanguage.INSTANCE,
filePath!!.readText()
)

fields = getFields(fixSpecs as XmlFile)
}

private fun getFields(file: XmlFile): FixFields {
val fieldsTag = file.rootTag?.findFirstSubTag("fields")
return FixFields(fieldsTag!!)
}

fun getTagName(tag: String): String {
return fields?.getTagName(tag) ?: ""
}

fun getTagValueDefinition(tag: String, value: String): String {
return fields?.getTagValueDefinition(tag, value) ?: ""
}

}
44 changes: 44 additions & 0 deletions src/main/kotlin/ac/quant/quickfixspec/common/FixField.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ac.quant.quickfixspec.common

import com.intellij.psi.xml.XmlTag
import lombok.Getter

@Getter
class FixField(tag: XmlTag) {
val name: String = parseName(tag)
val number: String = parseNumber(tag)
val fixType: String = parseFixType(tag)

Check warning on line 10 in src/main/kotlin/ac/quant/quickfixspec/common/FixField.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unused symbol

Property "fixType" is never used
val values: Map<String, String> = parseValues(tag)


private fun parseName(tag: XmlTag): String {
return getAttributeValue(tag, "name")
}

private fun parseNumber(tag: XmlTag): String {
return getAttributeValue(tag, "number")
}

private fun parseFixType(tag: XmlTag): String {
return getAttributeValue(tag, "type")
}

private fun parseValues(tag: XmlTag): Map<String, String> {
val valueTags = tag.findSubTags("value")
val valueTagsDict = mutableMapOf<String, String>()
for (valueTag in valueTags) {
try {
val enumValue = getAttributeValue(valueTag, "enum")
val description = getAttributeValue(valueTag, "description")
valueTagsDict[enumValue] = description
} catch (e: Exception) {
println("Error processing value tags. Error: ${e.message}")
}
}
return valueTagsDict
}

private fun getAttributeValue(tag: XmlTag, attributeName: String): String {
return tag.getAttribute(attributeName)?.value ?: ""
}
}
38 changes: 38 additions & 0 deletions src/main/kotlin/ac/quant/quickfixspec/common/FixFields.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package ac.quant.quickfixspec.common

import com.intellij.psi.xml.XmlTag

class FixFields(fieldsTag: XmlTag) {
private val fieldsByNum: Map<String, FixField>
private val fieldsByName: Map<String, FixField>

init {
val result = getFields(fieldsTag)
fieldsByNum = result[0]
fieldsByName = result[1]
}

private fun getFields(fieldsTag: XmlTag): List<MutableMap<String, FixField>> {
val mutableNum = mutableMapOf<String, FixField>()
val mutableName = mutableMapOf<String, FixField>()
try {
for (field in fieldsTag.subTags) {
val fixField = FixField(field)
mutableNum[fixField.number] = fixField
mutableName[fixField.name] = fixField
}
} catch (e: Exception) {
println("Error processing fields. Error: ${e.message}")
}
return listOf(mutableNum, mutableName)
}

fun getTagName(tagNumber: String): String {
return fieldsByNum[tagNumber]?.name ?: ""
}

fun getTagValueDefinition(tag: String, value: String): String {
return fieldsByNum[tag]?.values?.get(value) ?: ""
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package ac.quant.quickfixspec.documentation
import ac.quant.quickfixspec.common.FixDataDictionaryService
import com.intellij.model.Pointer
import com.intellij.platform.backend.documentation.DocumentationResult
import com.intellij.platform.backend.documentation.DocumentationTarget
import com.intellij.platform.backend.presentation.TargetPresentation

@Suppress("UnstableApiUsage")
class FixMessageDocumentationTarget(
private val fixMessage: String,
private val fixDelimiter: String,
private val fixDataDictionaryService: FixDataDictionaryService,
) : DocumentationTarget {


override fun createPointer(): Pointer<out DocumentationTarget> {
return Pointer.hardPointer(this)
}

override fun computePresentation(): TargetPresentation {
return TargetPresentation.builder(fixMessage).presentation()
}

override fun computeDocumentationHint(): String {
val docHint = computeDocumentation().toString()
return docHint
}

override fun computeDocumentation(): DocumentationResult {
val componentDetails = getMessageDetails()
return DocumentationResult.documentation(componentDetails)
}

private fun getMessageDetails(): String {
// split the message by the delimiter
val messageParts = fixMessage.split(fixDelimiter)
// build new string with the tag equals name on each line without the last index

val formattedMessage = messageParts.subList(0, messageParts.size - 1).joinToString("\n") {
val tagNumber = it.substringBefore("=")
val tagValue = it.substringAfter("=")
val tagName = fixDataDictionaryService.getTagName(tagNumber)
val tagValueDefinition = fixDataDictionaryService.getTagValueDefinition(tagNumber, tagValue)
"<tr><td>$tagNumber</td><td>$tagName</td><td>$tagValue</td><td>$tagValueDefinition</td></tr>"
}

val displayText = "<html><head><style> table { width: 100%; } td, th { border-bottom: 1px solid; text-align: left; padding: 1px; } </style></head><body><table>$formattedMessage</table></html>"

return displayText
}

}
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
package ac.quant.quickfixspec.documentation

import ac.quant.quickfixspec.common.FixDataDictionaryService
import ac.quant.quickfixspec.common.PsiUtils.NAME_ATTRIBUTE
import ac.quant.quickfixspec.common.PsiUtils.TAGS_WITH_DEFINITION
import ac.quant.quickfixspec.common.PsiUtils.DEFINITION_GROUP_NAME
import ac.quant.quickfixspec.common.PsiUtils.getRootTag
import com.intellij.platform.backend.documentation.DocumentationTarget
import com.intellij.platform.backend.documentation.DocumentationTargetProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.xml.XmlAttribute
import com.intellij.psi.xml.XmlTag
import com.intellij.psi.xml.XmlAttributeValue


class QuickfixComponentDocumentationTargetProvider : DocumentationTargetProvider {


var rootTag: XmlTag? = null

Check notice on line 20 in src/main/kotlin/ac/quant/quickfixspec/documentation/QuickfixComponentDocumentationTargetProvider.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Class member can have 'private' visibility

Property 'rootTag' could be private
private var fixDataDictionaryService: FixDataDictionaryService? = null


override fun documentationTargets(file: PsiFile, offset: Int): List<DocumentationTarget> {

val element = file.findElementAt(offset) ?: return emptyList()

return if (file.name.endsWith(".xml")) {
getTagDefinition(element)
} else {
getFixMessageDetails(file, element)
}

}

private fun getTagDefinition(element: PsiElement): List<DocumentationTarget> {
val attributeValue = element.parent as? XmlAttributeValue ?: return emptyList()
val attribute = attributeValue.parent as? XmlAttribute ?: return emptyList()

Expand All @@ -29,15 +45,14 @@ class QuickfixComponentDocumentationTargetProvider : DocumentationTargetProvider
return emptyList()
}

val tag = attribute.parent as? XmlTag ?: return emptyList()
val tag = attribute.parent ?: return emptyList()

if (tag.name !in TAGS_WITH_DEFINITION) {
return emptyList()
}

val tagParent = tag.parent as? XmlTag ?: return emptyList()


// skip definition when tagParent.name is "components" or "fields"

if (tagParent.name in DEFINITION_GROUP_NAME.values) {
Expand All @@ -46,8 +61,24 @@ class QuickfixComponentDocumentationTargetProvider : DocumentationTargetProvider

val attrNameValue = attribute.value ?: return emptyList()
return listOf(QuickfixComponentDocumentationTarget(attrNameValue, tag.name, rootTag))

}

// search for strings that are actually fix messages and return them with the actual name of the tag number based on the xml file
private fun getFixMessageDetails(file: PsiFile, element: PsiElement): List<DocumentationTarget> {
fixDataDictionaryService = file.project.getService(FixDataDictionaryService::class.java)

val fixMessages = mutableListOf<DocumentationTarget>()
val fixMessageRegex = Regex("8=FIX.*?10=[0-9]{3}(\\\\u0001)?(.)?")
val matches = fixMessageRegex.findAll(element.text)
for (match in matches) {
val fixMessage= match.value
// if group 0 is empty then try group 1
val fixDelimiter = match.groupValues[1].ifEmpty{ match.groupValues[2] }

fixMessages.add(FixMessageDocumentationTarget(fixMessage, fixDelimiter, fixDataDictionaryService!!))
}

return fixMessages
}

}
1 change: 1 addition & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<extensions defaultExtensionNs="com.intellij.codeInsight">
<inlayProvider
implementationClass="ac.quant.quickfixspec.inlay.QuickfixInlayHintsProvider"
isEnabledByDefault="true"
language="XML"
/>
</extensions>
Expand Down
Loading

0 comments on commit eef9822

Please sign in to comment.