Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ framework-tests/.packed
framework-tests/.test-projects
.magent
eslint-output.txt

.idea/
*.iml
10 changes: 0 additions & 10 deletions .idea/.gitignore

This file was deleted.

10 changes: 0 additions & 10 deletions .idea/codeStyles/Project.xml

This file was deleted.

5 changes: 0 additions & 5 deletions .idea/codeStyles/codeStyleConfig.xml

This file was deleted.

6 changes: 0 additions & 6 deletions .idea/inspectionProfiles/Project_Default.xml

This file was deleted.

8 changes: 0 additions & 8 deletions .idea/modules.xml

This file was deleted.

8 changes: 0 additions & 8 deletions .idea/varlock.iml

This file was deleted.

6 changes: 0 additions & 6 deletions .idea/vcs.xml

This file was deleted.

2 changes: 1 addition & 1 deletion packages/intellij-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,26 @@ 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+:([^}]*)\}""")
private val TABSTOP_SNIPPET_RE = Regex("""\$\{\d+\}""")
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
val offset = params.offset
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
Expand All @@ -63,7 +66,7 @@ class EnvSpecCompletionContributor : CompletionContributor() {
return
}

if (commentStart >= 0) {
if (commentPrefix != null) {
val existingDecoratorNames = EnvSpecCompletionCore.getExistingDecoratorNames(lineDocument, line, commentPrefix)

// Type option completion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -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<String, Int>? {
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) {
Expand Down Expand Up @@ -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 {
Expand All @@ -118,8 +136,10 @@ object EnvSpecDiagnosticsCore {
}

fun getDecoratorOccurrences(lineText: String, lineNumber: Int): List<DecoratorOccurrence> {
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()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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*$")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}

Expand Down
35 changes: 3 additions & 32 deletions packages/vscode-plugin/src/completion-core.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { DataTypeInfo, DecoratorInfo } from './intellisense-catalog';
import { getDecoratorCommentText } from './diagnostics-core';

type LineDocument = {
lineCount: number;
Expand All @@ -15,39 +16,9 @@ const INCOMPATIBLE_DECORATORS = new Map<string, Set<string>>([
['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) {
Expand Down
2 changes: 2 additions & 0 deletions packages/vscode-plugin/src/diagnostics-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Loading