How do I use the language server for error detection?

← Debugging and Troubleshooting · Ref: Q249

EK9 includes a full Language Server Protocol (LSP) implementation that provides real-time error detection, hover documentation, go-to-definition, and completions in your editor.

STARTING THE LSP

Two modes are available:

  ek9 -ls     Full LSP mode for IDE integration
  ek9 -lsh    Hover-help-only mode for lightweight editors

The LSP communicates over stdio using the standard LSP JSON-RPC protocol. Any editor that supports LSP can use it (VSCode, Neovim, Emacs, Sublime Text, etc.).

VSCODE EXTENSION

A VSCode extension is available that provides:

  Syntax highlighting for .ek9 files
  Real-time error detection as you type
  Hover documentation showing type information and method signatures
  Go-to-definition for navigating to symbol declarations
  Code completions for types, methods, and operators

REAL-TIME ERROR DETECTION

The LSP compiles your code through the PRE_IR_CHECKS phase (phase 9 of 22). This means ALL type errors, flow analysis errors, duplicate detection, and resolution errors appear instantly in your editor. You see errors before you save the file.

HOVER DOCUMENTATION

Hovering over any symbol shows its type, documentation, and available methods. For built-in types, this includes the full API. For your own types, it shows the declared operators and methods.

PROGRESSIVE ERROR DETECTION

The multi-phase pipeline means errors are detected progressively:

  Phase 2-3: Duplicate names and definitions
  Phase 4-5: Unresolved references and types
  Phase 6-7: Type hierarchy and generic resolution errors
  Phase 8-9: Flow analysis (unset variables, uninitialised returns)

Each phase builds on the previous, so fixing early errors often resolves later ones.

COMMAND LINE ALTERNATIVE

If you prefer the command line, use:

  ek9 -c myfile.ek9          compile and check for errors
  ek9 -E1 -c myfile.ek9      visual errors with source snippets
  ek9 -E2 -c myfile.ek9      add fuzzy suggestions

See Q246 for debugging strategies. See Q252 for verbose compilation modes. See Q253 for understanding compiler phases.

Example

defines module qa.debugging.languageserver

  defines trait

    Describable
      describe() as pure abstract
        <- rtn as String?

  defines class

    Point
      x <- Float()
      y <- Float()

      Point()
        ->
          px as Float
          py as Float
        this.x :=: px
        this.y :=: py

      distanceTo() as pure
        -> other as Point
        <- rtn as Float: Float()
        dx <- x - other.x
        dy <- y - other.y
        rtn: sqrt (dx * dx + dy * dy)

      operator $ as pure
        <- rtn as String: `(${x}, ${y})`

      override operator ? as pure
        <- rtn as Boolean: x? and y?

    Sensor with trait of Describable
      name <- String()
      reading <- Float()

      Sensor()
        ->
          sensorName as String
          sensorReading as Float
        this.name :=: sensorName
        this.reading :=: sensorReading

      override describe() as pure
        <- rtn as String: `Sensor ${name}: ${reading}`

      operator $ as pure
        <- rtn as String: `${name}=${reading}`

      override operator ? as pure
        <- rtn as Boolean: name? and reading?

  defines program

    LanguageServerDemo()
      stdout <- Stdout()

      // Types that the LSP would provide hover info for
      p1 <- Point(0.0, 0.0)
      p2 <- Point(3.0, 4.0)

      stdout.println(`Point 1: ${p1}`)
      stdout.println(`Point 2: ${p2}`)
      stdout.println(`Distance: ${p1.distanceTo(p2)}`)

      // Sensor with trait
      sensor <- Sensor("Temp", 22.5)
      stdout.println(`Sensor: ${sensor}`)
      stdout.println(`Description: ${sensor.describe()}`)

Common mistakes

E50060 — The method is named 'distanceTo' not 'distance'. Calling a method that does not exist on the type produces a resolution error. See ek9 -h E50060 for details.

Incorrect:

stdout.println(`Distance: ${p1.distance(p2)}`)

Correct:

stdout.println(`Distance: ${p1.distanceTo(p2)}`)

E08090 — If the variable 'sensor' is declared but never used in any expression, the compiler rejects it. Every declared variable must be referenced. See ek9 -h E50050 for details.

Incorrect:

sensorLabel <- `Sensor: ${sensor}`

Correct:

stdout.println(`Sensor: ${sensor}`)
Other ways to ask this
  • What is the EK9 language server?
  • How do I get real-time errors in my editor?
  • Does EK9 support IDE integration?

Coming from another language?

Java: IntelliJ/Eclipse provide rich IDE support, separate from compiler. Python: Pylance/Pyright LSP servers, type checking optional. Rust: rust-analyzer provides excellent LSP support. Go: gopls is the official LSP server. C++: clangd provides LSP, but C++ complexity limits accuracy. Kotlin: IntelliJ native support, kotlin-language-server for other editors. EK9: built-in LSP as part of the compiler itself, compiles through 10 phases for comprehensive error detection, hover documentation, go-to-definition.

Keywords: vscode, editor, error, realtime, ide, lsp, troubleshoot, detection, server, error-message, hover, completion, language, definition, integration, debug