Editor Setup

PSC produces diagnostics in the standard filename:line:col: severity: message [code] format. Every editor that can parse compiler output can run PSC. This page gives copy-paste configurations for the most common editors.

Each configuration runs psc check on the current file and displays the results as inline diagnostics. For background on what PSC catches, see the Type Checking guide.

Vim

ALE (recommended)

ALE runs linters asynchronously and displays results as you edit. Add to your .vimrc:

" Run PSC on Perl files
let g:ale_linters = { 'perl': ['psc'] }

" Define PSC as a linter
call ale#linter#Define('perl', {
\   'name': 'psc',
\   'executable': 'psc',
\   'command': 'psc check %s',
\   'callback': 'ale#handlers#gcc#HandleGCCFormat',
\ })

Save a Perl file and diagnostics appear in the sign column.

Syntastic

Syntastic checks files on save. Add to your .vimrc:

let g:syntastic_perl_checkers = ['psc']
let g:syntastic_perl_psc_exec = 'psc'
let g:syntastic_perl_psc_args = 'check'

Compiler plugin (no plugin required)

Vim's built-in :compiler mechanism works with PSC's output format. Create ~/.vim/compiler/psc.vim:

CompilerSet makeprg=psc\ check\ %
CompilerSet errorformat=%f:%l:%c:\ %t%*[^:]:\ %m

Then run :compiler psc and :make to populate the quickfix list.

Neovim

nvim-lint (recommended)

nvim-lint runs linters asynchronously. Add to your Lua config:

require('lint').linters.psc = {
  cmd = 'psc',
  args = { 'check' },
  stdin = false,
  append_fname = true,
  stream = 'stderr',
  parser = require('lint.parser').from_errorformat(
    '%f:%l:%c: %trror: %m,%f:%l:%c: %tarning: %m,%f:%l:%c: %tint: %m',
    { source = 'psc' }
  ),
}

require('lint').linters_by_ft = {
  perl = { 'psc' },
}

-- Lint on save
vim.api.nvim_create_autocmd({ 'BufWritePost' }, {
  callback = function() require('lint').try_lint() end,
})

efm-langserver

efm-langserver wraps command-line tools as LSP servers. Add to your efm config (~/.config/efm-langserver/config.yaml):

languages:
  perl:
    - lint-command: 'psc check ${INPUT}'
      lint-stdin: false
      lint-formats:
        - '%f:%l:%c: %trror: %m'
        - '%f:%l:%c: %tarning: %m'
        - '%f:%l:%c: %tint: %m'

Then register efm as a language server in your Neovim config:

require('lspconfig').efm.setup({
  filetypes = { 'perl' },
  init_options = { documentFormatting = false },
})

Compiler command (no plugin required)

vim.api.nvim_create_autocmd('FileType', {
  pattern = 'perl',
  callback = function()
    vim.bo.makeprg = 'psc check %'
    vim.bo.errorformat = '%f:%l:%c: %trror: %m,%f:%l:%c: %tint: %m'
  end,
})

Run :make and results appear in the quickfix list. Jump between them with :cnext and :cprev.

VS Code

Tasks + Problem Matcher

VS Code can run PSC as a build task and parse its output into the Problems panel. Add to .vscode/tasks.json:

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "PSC: Check current file",
      "type": "shell",
      "command": "psc",
      "args": ["check", "${file}"],
      "group": "build",
      "problemMatcher": {
        "owner": "psc",
        "fileLocation": ["relative", "${workspaceFolder}"],
        "pattern": {
          "regexp": "^(.+):(\\d+):(\\d+):\\s+(error|warning|hint):\\s+(.+)$",
          "file": 1,
          "line": 2,
          "column": 3,
          "severity": 4,
          "message": 5
        }
      }
    },
    {
      "label": "PSC: Check project",
      "type": "shell",
      "command": "psc",
      "args": ["check", "lib/"],
      "group": "build",
      "problemMatcher": {
        "owner": "psc",
        "fileLocation": ["relative", "${workspaceFolder}"],
        "pattern": {
          "regexp": "^(.+):(\\d+):(\\d+):\\s+(error|warning|hint):\\s+(.+)$",
          "file": 1,
          "line": 2,
          "column": 3,
          "severity": 4,
          "message": 5
        }
      }
    }
  ]
}

Run with Ctrl+Shift+B (or Cmd+Shift+B on macOS) and select the task. Diagnostics appear in the Problems panel with clickable file locations.

Run on Save (optional)

Install the Run on Save extension and add to .vscode/settings.json:

{
  "emeraldwalk.runonsave": {
    "commands": [
      {
        "match": "\\.(pl|pm|t)$",
        "cmd": "psc check ${file}"
      }
    ]
  }
}

Emacs

Flycheck (recommended)

Flycheck displays inline diagnostics as you edit. Add to your init file:

(require 'flycheck)

(flycheck-define-checker perl-psc
  "A Perl type checker using PSC."
  :command ("psc" "check" source)
  :error-patterns
  ((error line-start (file-name) ":" line ":" column ": error: " (message) line-end)
   (warning line-start (file-name) ":" line ":" column ": warning: " (message) line-end)
   (info line-start (file-name) ":" line ":" column ": hint: " (message) line-end))
  :modes (perl-mode cperl-mode))

(add-to-list 'flycheck-checkers 'perl-psc)

Flymake (built-in)

Flymake ships with Emacs 26+. No external package required:

(defun flymake-psc-init ()
  "Set up Flymake for PSC."
  (let ((temp-file (flymake-proc-init-create-temp-buffer-copy
                    'flymake-proc-create-temp-inplace)))
    (list "psc" (list "check" temp-file))))

(add-to-list 'flymake-proc-allowed-file-name-masks
             '("\\.p[lm]\\'" flymake-psc-init))

(add-hook 'perl-mode-hook #'flymake-mode)
(add-hook 'cperl-mode-hook #'flymake-mode)

Sublime Text

SublimeLinter

Install SublimeLinter via Package Control, then add to your SublimeLinter settings (Preferences > Package Settings > SublimeLinter > Settings):

{
  "linters": {
    "psc": {
      "cmd": "psc check ${file}",
      "regex": "^(?P<file>.+):(?P<line>\\d+):(?P<col>\\d+): (?P<error>error|warning|hint): (?P<message>.+)$",
      "selector": "source.perl"
    }
  }
}

Build System (no plugin required)

Create a build system via Tools > Build System > New Build System:

{
  "cmd": ["psc", "check", "$file"],
  "file_regex": "^(.+):(\\d+):(\\d+): (error|warning|hint): (.+)$",
  "selector": "source.perl"
}

Save as PSC.sublime-build. Run with Ctrl+B (or Cmd+B on macOS) and double-click results to jump to the source.

Helix

Helix supports external formatters and language servers. Until PSC's built-in LSP server ships, use efm-langserver as a bridge. Add to ~/.config/helix/languages.toml:

[[language]]
name = "perl"
language-servers = ["efm-lsp-perl"]

[language-server.efm-lsp-perl]
command = "efm-langserver"

Then configure efm-langserver to run PSC as described in the Neovim efm-langserver section above.

CI / command line

PSC works in any pipeline that can run a binary. It exits 0 on success and non-zero when diagnostics are found:

# Check a single file
psc check lib/MyModule.pm

# Check an entire project
psc check lib/

# Use in a Makefile
check:
	psc check lib/ t/

In GitHub Actions:

- name: Type-check Perl
  run: psc check lib/

LSP server

PSC includes a built-in LSP server that delivers hover tooltips, inline diagnostics, go-to-definition, and document symbols directly to any LSP-capable editor. Start it with psc lsp — no bridge tools or separate installations required.

Neovim (lspconfig)

require('lspconfig.configs').psc = {
  default_config = {
    cmd = { 'psc', 'lsp' },
    filetypes = { 'perl' },
    root_dir = require('lspconfig.util').root_pattern('.perl-version', 'cpanfile', '.git'),
  },
}
require('lspconfig').psc.setup({})

VS Code

Add to .vscode/settings.json (requires a generic LSP client extension like vscode-languageclient or Hax):

{
  "perl.lsp.serverCommand": ["psc", "lsp"]
}

Or configure it as a custom language server in your LSP client extension's settings.

Emacs (eglot)

Eglot ships with Emacs 29+:

(add-to-list 'eglot-server-programs '((perl-mode cperl-mode) "psc" "lsp"))
(add-hook 'perl-mode-hook #'eglot-ensure)
(add-hook 'cperl-mode-hook #'eglot-ensure)

Helix

Add to ~/.config/helix/languages.toml:

[[language]]
name = "perl"
language-servers = ["psc-lsp"]

[language-server.psc-lsp]
command = "psc"
args = ["lsp"]

Sublime Text (LSP package)

Install the LSP package, then add to LSP settings:

{
  "clients": {
    "psc": {
      "enabled": true,
      "command": ["psc", "lsp"],
      "selector": "source.perl"
    }
  }
}

The psc check linter integrations above remain available as an alternative for editors without LSP support or for CI pipelines.