Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 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
17 changes: 17 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,20 @@
run: bun run build:libs
- name: Run tests
run: bun run test:ci

intellij-plugin:
runs-on: ubuntu-latest
defaults:
run:
working-directory: packages/intellij-plugin
steps:
- uses: actions/checkout@v6
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build IntelliJ plugin
run: ./gradlew build
Comment on lines +43 to +57

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium test

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 21 days ago

To fix the problem, explicitly restrict the GITHUB_TOKEN permissions in the workflow. The simplest approach is to add a workflow-level permissions: block that applies to all jobs. Since the jobs only need to check out code and do local builds/tests, contents: read is sufficient in most cases and matches the recommended minimal starting point.

The best fix without changing existing behavior is:

  • Add a permissions: section near the top of .github/workflows/test.yaml, at the workflow root (same level as name: and on:), specifying contents: read.
  • This will apply to both build and intellij-plugin jobs, because they do not define their own permissions blocks.
  • No additional imports or methods are needed; this is a pure YAML configuration adjustment.

Concretely:

  • In .github/workflows/test.yaml, insert:
permissions:
  contents: read

between the existing on: block and jobs: block (e.g., after line 5, before line 7). This resolves the CodeQL warning by explicitly limiting the GITHUB_TOKEN.

Suggested changeset 1
.github/workflows/test.yaml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -3,6 +3,8 @@
   pull_request:
   push:
 
+permissions:
+  contents: read
 
 jobs:
   build:
EOF
@@ -3,6 +3,8 @@
pull_request:
push:

permissions:
contents: read

jobs:
build:
Copilot is powered by AI and may make mistakes. Always verify output.
10 changes: 10 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

876 changes: 19 additions & 857 deletions bun.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export default tseslint.config(
'.magent',
'framework-tests/.test-projects',
'framework-tests/.packed',
'packages/intellij-plugin/build',
],
},

Expand Down
19 changes: 19 additions & 0 deletions packages/intellij-plugin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Gradle
.gradle/
build/

# Kotlin (incremental compilation / daemon metadata)
.kotlin/

# IntelliJ Platform Gradle Plugin (IDE dependency cache, optional project cache)
.intellijPlatform/

# IntelliJ IDEA / Android Studio
.idea/
*.iml
*.ipr
*.iws
out/

# Local SDK or machine-specific paths (if present)
local.properties
42 changes: 42 additions & 0 deletions packages/intellij-plugin/CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Compressed context — Env Spec IntelliJ plugin

## Purpose
JetBrains plugin for [@env-spec](https://varlock.dev/env-spec) on `.env` / `.env.*`: completion, diagnostics, hover, comments, syntax, tests, CI. Parallels the [VS Code extension](../../vscode-plugin).

## Build / toolchain
- **Gradle** 9.0+ (wrapper in repo); **IntelliJ Platform Gradle Plugin** 2.13.x requires Gradle ≥ 9.0.
- **JDK 17** for `./gradlew` (e.g. `JAVA_HOME=/opt/homebrew/opt/openjdk@17`). Java 25 + older Gradle showed a useless `What went wrong: 25` message.
- **Plugin ZIP**: `./gradlew buildPlugin` (or `./gradlew build` — `build` depends on `buildPlugin` in `build.gradle.kts`). Output: `build/distributions/*.zip`.
- **`./gradlew runIde`**: launches a sandbox IDE with the plugin for manual testing and opens the monorepo root (`../..` from `packages/intellij-plugin`) automatically.

## UX / editor (recent)
- **Syntax (comments/decorators)**: Comment lines now tokenize decorators with structure (`@name`, `=`, function name, arg keys/values, commas/parens) instead of a single flat comment token. New token families include `DECORATOR`, `DECORATOR_VALUE`, `DECORATOR_ARG_KEY`, and `DECORATOR_ARG_VALUE`.
- **Syntax (assignment values)**: Assignment values now tokenize resolver function names (`if`, `eq`, etc.) and refs (`$ENV`, `${ENV}`) separately from generic value text, improving parity with VS Code highlighting.
- **Incremental lexing stability**: Lexer now re-tokenizes from true line start (then trims to current offset), preventing state drift where highlights looked correct initially but degraded after incremental rehighlight.
- **Color scheme page**: Added descriptors for decorator subparts, value function calls, and value references in **Settings → Editor → Color Scheme → Env Spec**.
- **Icon**: `src/main/resources/icons/env-spec.svg`; `EnvSpecFileType.getIcon()` via `IconLoader`.
- **Enter on `#` lines**: `EnvSpecCommentEnterHandler` (`EnterHandlerDelegate`) inserts newline + indent + `# `; registered in `plugin.xml` as `enterHandlerDelegate`.
- **Completion insertion behavior**:
- Fixed duplicate/append issues on accept (Tab/Enter) by replacing `startOffset..tailOffset` instead of static line ranges.
- Prevents duplicate `@` when accepting decorator suggestions after typing `@`.
- Type-option completions now insert `optionName=` and place caret directly after `=`.
- Added snippet normalization for catalog insert text so VS Code-style placeholders are not inserted literally.

## Tests (recent additions)
- Added `EnvSpecLexerTest` coverage for:
- Decorator segmentation (`@type=enum(...)`, `@generateTypes(...)`)
- Arg key/value tokenization across multi-arg decorators
- Incomplete vs closed paren forms
- Mid-line incremental lexing start offsets
- Assignment value function/reference tokenization (`if(eq($ENV,...))`)
- Added `EnvSpecCompletionContributorTest` coverage for:
- Completion match contexts immediately after `=`
- Snippet text normalization paths

## Key paths
| Area | Path |
|------------|------|
| Plugin src | `src/main/kotlin/dev/dmno/envspec/` |
| Plugin XML | `src/main/resources/META-INF/plugin.xml` |
| Build | `build.gradle.kts`, `gradle/wrapper/` |
| Docs | `README.md` (this file is additive only) |
86 changes: 86 additions & 0 deletions packages/intellij-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Env Spec Language - IntelliJ / WebStorm Plugin

Adds syntax highlighting and IntelliSense for [@env-spec](https://varlock.dev/env-spec) enabled `.env` files in IntelliJ IDEA and WebStorm.

## Features

Inspired by the [VS Code / Open VSX extension](../../vscode-plugin):

- **Completion (IntelliSense)** for:
- Decorators (`@required`, `@type=`, etc.)
- `@type=` data types (string, number, enum, url, etc.)
- Type options (e.g. `string(minLength=5)`)
- Resolver functions (`concat`, `fallback`, `forEnv`, etc.)
- `$KEY` references
- Enum values when `@type=enum(...)` is used

- **Inline diagnostics** for:
- Duplicate single-use decorators
- Incompatible decorators (`@required` + `@optional`, etc.)
- Invalid enum values
- Static value validation (url prependHttps, string length, number range, etc.)

- **Documentation** on hover for decorators

- **Syntax highlighting** for .env and .env.* files:
- Comment lines (`# …`) vs assignments (`KEY=value`, optional `export`)
- Colors follow **Settings → Editor → Color Scheme → Env Spec** (defaults match line comments, keywords, keys, `=`, and string-like values)

- **Project view icon** for registered `.env` / `.env.*` files

- **Toggle line comment** (`# `) support

- **Enter on a `#` line** inserts a new line with the same indent and `# ` (block comment continuation)

## Installation

### From JetBrains Marketplace

1. Open **Settings** | **Plugins** | **Marketplace**
2. Search for "Env Spec" or "@env-spec"
3. Install and restart the IDE

### From disk (development)

1. Build the plugin: `./gradlew buildPlugin` (or `./gradlew build`, which includes it)
2. The plugin ZIP is in `build/distributions/`
3. Install via **Settings** | **Plugins** | **⚙️** | **Install Plugin from Disk...**

## Supported file patterns

- `.env`
- `.env.*` (e.g. `.env.schema`, `.env.local`, `.env.example`)

## Requirements

- IntelliJ IDEA 2024.3+ or WebStorm 2024.3+
- **Java 17** (for building) — use a supported JDK for the Gradle version in this repo (see Troubleshooting if you see a cryptic `25` error with Java 25)

## Development

```bash
# Build
./gradlew build

# Run IDE with plugin
./gradlew runIde

# Run tests
./gradlew test
```

### Troubleshooting

**Build fails with "What went wrong: 25"** — You're using Java 25, which Gradle 8.x doesn't support. Use Java 17:

```bash
# Homebrew (openjdk@17)
export JAVA_HOME="/opt/homebrew/opt/openjdk@17"
./gradlew build
```

You can add that `export` to your `~/.zshrc` or run it before each build.

## License

MIT
55 changes: 55 additions & 0 deletions packages/intellij-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
plugins {
id("java")
id("org.jetbrains.kotlin.jvm") version "2.0.21"
id("org.jetbrains.intellij.platform") version "2.13.1"
}

group = "dev.dmno"
version = "0.1.0"

repositories {
mavenCentral()
intellijPlatform {
defaultRepositories()
}
}

dependencies {
intellijPlatform {
intellijIdea("2024.3.6")
}
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testRuntimeOnly("junit:junit:4.13.2") // IntelliJ test framework needs JUnit4 classes (IJPL-159134)
}

tasks.test {
useJUnitPlatform()
}

tasks.named<JavaExec>("runIde") {
// Open the varlock repository root when the sandbox IDE starts.
val repoRoot = layout.projectDirectory.dir("../..").asFile.absolutePath
args(repoRoot)
}

// Ensure `build` produces the plugin zip (buildPlugin is not included by default)
tasks.named("build") {
dependsOn(tasks.named("buildPlugin"))
}

intellijPlatform {
buildSearchableOptions.set(false)
pluginConfiguration {
name.set("Env Spec Language")
description.set("Adds syntax highlighting and IntelliSense for @env-spec enabled .env files")
vendor {
name.set("dmno-dev")
url.set("https://varlock.dev")
}
}
}

kotlin {
jvmToolchain(17)
}
5 changes: 5 additions & 0 deletions packages/intellij-plugin/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
org.gradle.jvmargs=-Xmx2048m
org.gradle.parallel=true

# Suppress "no searchable options" warning (plugin has no Settings UI)
org.jetbrains.intellij.platform.noSearchableOptionsWarning=false
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading
Loading