diff --git a/.gitignore b/.gitignore
index b523d384..6fd493e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,6 @@ framework-tests/.packed
framework-tests/.test-projects
.magent
eslint-output.txt
+
+.idea/
+*.iml
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index ab1f4164..00000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Ignored default folder with query files
-/queries/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
-# Editor-based HTTP Client requests
-/httpRequests/
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
deleted file mode 100644
index dc3e940f..00000000
--- a/.idea/codeStyles/Project.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
deleted file mode 100644
index 79ee123c..00000000
--- a/.idea/codeStyles/codeStyleConfig.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
deleted file mode 100644
index 03d9549e..00000000
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 9b8317cd..00000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/varlock.iml b/.idea/varlock.iml
deleted file mode 100644
index c956989b..00000000
--- a/.idea/varlock.iml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1ddf..00000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/packages/intellij-plugin/README.md b/packages/intellij-plugin/README.md
index 1dd69a3a..036176c3 100644
--- a/packages/intellij-plugin/README.md
+++ b/packages/intellij-plugin/README.md
@@ -71,7 +71,7 @@ Inspired by the [VS Code / Open VSX extension](../../vscode-plugin):
### Troubleshooting
-**Build fails with "What went wrong: 25"** — You're using Java 25, which Gradle 8.x doesn't support. Use Java 17:
+**Build fails with "What went wrong: 25"** — You're using Java 25, which isn't supported by the Gradle/toolchain used for this plugin. Use Java 17:
```bash
# Homebrew (openjdk@17)
diff --git a/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecCompletionContributor.kt b/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecCompletionContributor.kt
index 9aa8de25..086fe788 100644
--- a/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecCompletionContributor.kt
+++ b/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecCompletionContributor.kt
@@ -26,7 +26,7 @@ class EnvSpecCompletionContributor : CompletionContributor() {
}
companion object {
- private val ENV_KEY_PATTERN = Pattern.compile("^\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*=")
+ private val ENV_KEY_PATTERN = Pattern.compile("^\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_.-]*)\\s*=")
private val KOTLIN_ESCAPED_DOLLAR_RE = Regex("\\$\\{'\\$'\\}")
private val CHOICE_SNIPPET_RE = Regex("""\$\{\d+\|([^}]*)\|\}""")
private val DEFAULT_SNIPPET_RE = Regex("""\$\{\d+:([^}]*)\}""")
@@ -34,6 +34,10 @@ class EnvSpecCompletionContributor : CompletionContributor() {
private val SIMPLE_TABSTOP_SNIPPET_RE = Regex("""\$\d+""")
}
+ private fun getDecoratorCommentPrefix(linePrefix: String): String? {
+ return EnvSpecDiagnosticsCore.getDecoratorCommentText(linePrefix)
+ }
+
private fun doAddCompletions(params: CompletionParameters, result: CompletionResultSet) {
val file = params.originalFile as? EnvSpecFile ?: return
val document = params.editor.document
@@ -41,8 +45,7 @@ class EnvSpecCompletionContributor : CompletionContributor() {
val line = document.getLineNumber(offset)
val lineStart = document.getLineStartOffset(line)
val linePrefix = document.getText(com.intellij.openapi.util.TextRange(lineStart, offset))
- val commentStart = linePrefix.indexOf('#')
- val commentPrefix = if (commentStart >= 0) linePrefix.substring(commentStart + 1) else ""
+ val commentPrefix = getDecoratorCommentPrefix(linePrefix)
val lineDocument = object : LineDocument {
override val lineCount: Int get() = document.lineCount
@@ -63,7 +66,7 @@ class EnvSpecCompletionContributor : CompletionContributor() {
return
}
- if (commentStart >= 0) {
+ if (commentPrefix != null) {
val existingDecoratorNames = EnvSpecCompletionCore.getExistingDecoratorNames(lineDocument, line, commentPrefix)
// Type option completion
diff --git a/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecDiagnosticsCore.kt b/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecDiagnosticsCore.kt
index 5c15428b..1a3e7e3c 100644
--- a/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecDiagnosticsCore.kt
+++ b/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecDiagnosticsCore.kt
@@ -4,7 +4,10 @@ import java.net.InetAddress
object EnvSpecDiagnosticsCore {
+ private val DECORATOR_LINE_PATTERN = Regex("""^\s*#\s*(@.*)$""")
private val DECORATOR_PATTERN = Regex("@([A-Za-z][\\w-]*)(?:\\([^)]*\\)|=[^\\s#]+)?")
+ private val NON_DECORATOR_LABEL_RE = Regex("^@[A-Za-z]+:")
+ private val NON_DECORATOR_PROSE_RE = Regex("^@[A-Za-z]+\\s+[^@#]")
private const val MAX_MATCHES_PATTERN_LENGTH = 200
private val INCOMPATIBLE_DECORATOR_PAIRS = listOf(
listOf("required", "optional"),
@@ -15,6 +18,20 @@ object EnvSpecDiagnosticsCore {
data class DecoratorOccurrence(val name: String, val line: Int, val start: Int, val end: Int)
data class CoreDiagnostic(val line: Int, val start: Int, val end: Int, val message: String)
+ fun getDecoratorCommentText(lineText: String): String? {
+ return getDecoratorCommentRange(lineText)?.first
+ }
+
+ private fun getDecoratorCommentRange(lineText: String): Pair? {
+ val match = DECORATOR_LINE_PATTERN.find(lineText) ?: return null
+ val commentText = match.groupValues[1]
+ if (NON_DECORATOR_LABEL_RE.containsMatchIn(commentText)) return null
+ if (NON_DECORATOR_PROSE_RE.containsMatchIn(commentText)) return null
+ val stripped = stripInlineComment(commentText)
+ val startIndex = match.groups[1]!!.range.first
+ return stripped to startIndex
+ }
+
fun stripInlineComment(value: String): String {
var quote: Char? = null
for (i in value.indices) {
@@ -107,7 +124,8 @@ object EnvSpecDiagnosticsCore {
fun getTypeInfoFromPrecedingComments(document: LineDocument, lineNumber: Int): TypeInfo? {
val commentBlock = getPrecedingCommentBlock(document, lineNumber)
for (index in commentBlock.indices.reversed()) {
- val match = Regex("@type=([A-Za-z][\\w-]*)(?:\\((.*)\\))?").find(commentBlock[index]) ?: continue
+ val decoratorText = getDecoratorCommentText(commentBlock[index]) ?: continue
+ val match = Regex("@type=([A-Za-z][\\w-]*)(?:\\((.*)\\))?").find(decoratorText) ?: continue
return if (match.groupValues[1] == "enum") {
TypeInfo("enum", splitEnumArgs(match.groupValues[2]), emptyMap())
} else {
@@ -118,8 +136,10 @@ object EnvSpecDiagnosticsCore {
}
fun getDecoratorOccurrences(lineText: String, lineNumber: Int): List {
- return DECORATOR_PATTERN.findAll(lineText).map { match ->
- DecoratorOccurrence(match.groupValues[1], lineNumber, match.range.first, match.range.last + 1)
+ val (decoratorText, startIndex) = getDecoratorCommentRange(lineText) ?: return emptyList()
+ return DECORATOR_PATTERN.findAll(decoratorText).map { match ->
+ val absStart = startIndex + (match.range.first)
+ DecoratorOccurrence(match.groupValues[1], lineNumber, absStart, absStart + match.value.length)
}.toList()
}
diff --git a/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecInspection.kt b/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecInspection.kt
index 93f20994..6267858a 100644
--- a/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecInspection.kt
+++ b/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecInspection.kt
@@ -69,6 +69,6 @@ class EnvSpecInspection : LocalInspectionTool() {
}
companion object {
- private val ENV_ASSIGNMENT = Pattern.compile("^\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*=\\s*(.*?)\\s*$")
+ private val ENV_ASSIGNMENT = Pattern.compile("^\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_.-]*)\\s*=\\s*(.*?)\\s*$")
}
}
diff --git a/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecLexer.kt b/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecLexer.kt
index 23e0c5fc..45a66a9e 100644
--- a/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecLexer.kt
+++ b/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecLexer.kt
@@ -226,13 +226,13 @@ class EnvSpecLexer : LexerBase() {
queue.add(QueuedToken(start + i, start + valueEnd, EnvSpecTokenTypes.DECORATOR_VALUE))
i = valueEnd
if (i < text.length && text[i] == '(') {
- queue.add(QueuedToken(start + i, start + i + 1, EnvSpecTokenTypes.EQUALS))
+ queue.add(QueuedToken(start + i, start + i + 1, EnvSpecTokenTypes.PAREN_OPEN))
val close = findClosingParen(text, i)
if (close > i + 1) {
enqueueDecoratorArgsTokens(start + i + 1, text.substring(i + 1, close))
}
if (close < text.length) {
- queue.add(QueuedToken(start + close, start + close + 1, EnvSpecTokenTypes.EQUALS))
+ queue.add(QueuedToken(start + close, start + close + 1, EnvSpecTokenTypes.PAREN_CLOSE))
i = close + 1
} else {
i = text.length
@@ -251,13 +251,13 @@ class EnvSpecLexer : LexerBase() {
}
if (i < text.length && text[i] == '(') {
- queue.add(QueuedToken(start + i, start + i + 1, EnvSpecTokenTypes.EQUALS))
+ queue.add(QueuedToken(start + i, start + i + 1, EnvSpecTokenTypes.PAREN_OPEN))
val close = findClosingParen(text, i)
if (close > i + 1) {
enqueueDecoratorArgsTokens(start + i + 1, text.substring(i + 1, close))
}
if (close < text.length) {
- queue.add(QueuedToken(start + close, start + close + 1, EnvSpecTokenTypes.EQUALS))
+ queue.add(QueuedToken(start + close, start + close + 1, EnvSpecTokenTypes.PAREN_CLOSE))
i = close + 1
} else {
i = text.length
diff --git a/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecSyntaxHighlighter.kt b/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecSyntaxHighlighter.kt
index b2022a4b..7e0e7ba5 100644
--- a/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecSyntaxHighlighter.kt
+++ b/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecSyntaxHighlighter.kt
@@ -24,6 +24,8 @@ class EnvSpecSyntaxHighlighter : SyntaxHighlighterBase() {
EnvSpecTokenTypes.EXPORT_KEYWORD -> KEYWORD_KEYS
EnvSpecTokenTypes.ENV_KEY -> INSTANCE_FIELD_KEYS
EnvSpecTokenTypes.EQUALS -> OPERATION_SIGN_KEYS
+ EnvSpecTokenTypes.PAREN_OPEN -> OPERATION_SIGN_KEYS
+ EnvSpecTokenTypes.PAREN_CLOSE -> OPERATION_SIGN_KEYS
EnvSpecTokenTypes.ENV_VALUE -> STRING_KEYS
EnvSpecTokenTypes.VALUE_FUNCTION -> FUNCTION_CALL_KEYS
EnvSpecTokenTypes.VALUE_REFERENCE -> VALUE_REFERENCE_KEYS
diff --git a/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecTokenTypes.kt b/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecTokenTypes.kt
index 8ed13475..77a77fdb 100644
--- a/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecTokenTypes.kt
+++ b/packages/intellij-plugin/src/main/kotlin/dev/dmno/envspec/EnvSpecTokenTypes.kt
@@ -43,6 +43,12 @@ object EnvSpecTokenTypes {
@JvmField
val EQUALS: IElementType = object : IElementType("ENV_SPEC_EQUALS", EnvSpecLanguage) {}
+ @JvmField
+ val PAREN_OPEN: IElementType = object : IElementType("ENV_SPEC_PAREN_OPEN", EnvSpecLanguage) {}
+
+ @JvmField
+ val PAREN_CLOSE: IElementType = object : IElementType("ENV_SPEC_PAREN_CLOSE", EnvSpecLanguage) {}
+
@JvmField
val ENV_VALUE: IElementType = object : IElementType("ENV_SPEC_VALUE", EnvSpecLanguage) {}
diff --git a/packages/vscode-plugin/src/completion-core.ts b/packages/vscode-plugin/src/completion-core.ts
index cb540f69..ad9a4b7d 100644
--- a/packages/vscode-plugin/src/completion-core.ts
+++ b/packages/vscode-plugin/src/completion-core.ts
@@ -1,4 +1,5 @@
import type { DataTypeInfo, DecoratorInfo } from './intellisense-catalog';
+import { getDecoratorCommentText } from './diagnostics-core';
type LineDocument = {
lineCount: number;
@@ -15,39 +16,9 @@ const INCOMPATIBLE_DECORATORS = new Map>([
['public', new Set(['sensitive'])],
]);
-function stripInlineComment(value: string) {
- let quote: '"' | '\'' | '' = '';
-
- for (let i = 0; i < value.length; i += 1) {
- const char = value[i];
- if (quote) {
- if (char === quote) quote = '';
- continue;
- }
-
- if (char === '"' || char === '\'') {
- quote = char;
- continue;
- }
-
- if (char === '#' && (i === 0 || /\s/.test(value[i - 1]))) {
- return value.slice(0, i).trimEnd();
- }
- }
-
- return value.trim();
-}
-
+/** Thin wrapper kept for API compatibility with existing callers and tests. */
export function getDecoratorCommentPrefix(lineText: string) {
- const match = lineText.match(/^\s*#\s*(@.*)$/);
- if (!match) return undefined;
-
- const commentText = match[1];
-
- if (/^@[A-Za-z]+:/.test(commentText)) return undefined;
- if (/^@[A-Za-z]+\s+[^@#]/.test(commentText)) return undefined;
-
- return stripInlineComment(commentText);
+ return getDecoratorCommentText(lineText);
}
function splitArgs(input: string) {
diff --git a/packages/vscode-plugin/src/diagnostics-core.ts b/packages/vscode-plugin/src/diagnostics-core.ts
index 1b5814cd..e7b1a198 100644
--- a/packages/vscode-plugin/src/diagnostics-core.ts
+++ b/packages/vscode-plugin/src/diagnostics-core.ts
@@ -169,6 +169,8 @@ function getDecoratorCommentText(lineText: string) {
return stripInlineComment(commentText);
}
+export { getDecoratorCommentText };
+
export function getTypeInfoFromPrecedingComments(document: LineDocument, lineNumber: number) {
const commentBlock = getPrecedingCommentBlock(document, lineNumber);