Parakeet is unofficial, community-made support for the Inko programming language in JetBrains IDEs. It's tested in IntelliJ IDEA, and should work in the others (RubyMine, WebStorm, RustRover, etc.), though they haven't been tried yet.
This plugin is not affiliated with or endorsed by the Inko project. Please don't report problems with this plugin to the Inko project; open an issue here instead. When Parakeet causes an error, the IDE's error dialog can do that for you: Open a GitHub Issue opens one in your browser, filled in with the error and your versions, for you to review and submit.
I use three words for how code comes to exist. Code I wrote, I typed by hand, with nothing generated. Code I wrought, an AI agent generated while I iterated with it, and I reviewed every change before committing it, so I own every diff. Code I requested, an agent built with little or no review from me, the way a dark factory runs without lights.
Versions 0.1.0 through 0.15.0 are requested.
- An Inko file type for
.inkofiles, with its own icon - Syntax highlighting, from the TextMate grammar in inko-lang/vscode
- Line comment toggling (
#), bracket matching, and auto-closing brackets and quotes - Two-space indentation as you type, like
inko fmt(Settings → Editor → Code Style → Inko) - Compiler errors and warnings as you type, from Cuttlebone (the same as
inko check), with quick fixes (Alt+Enter): import an undefined name from the module that defines it, remove an unused import, rename an unused local to_name, discard an unused result withlet _ =, add a missing field, implement missing methods or a missing required trait, and remove unreachable code - Errors across the whole project, as you type: Cuttlebone checks every module, so files with errors are marked in the Project view and the Problems tool window even when they aren't open, and Code → Inspect Code lists every module's errors and warnings
- Intentions (Alt+Enter anywhere): flip a comparison,
==,!=,and, oror; invert anif; replace anif/else ifchain over literals with amatch, and back; add or remove argument names; and specify or remove alet's type - Style-guide inspections for what
inko fmtdoesn't fix, with quick fixes: names in the wrong case, Bool methods not ending in?(or prefixed withis_), redundant argument names likestart: start, and empty parentheses in calls likepop(). They're weak warnings, configurable in Settings → Editor → Inspections → Inko, and# noinspection Nameon the line above suppresses one. - Code → Reformat Code (⌥⌘L / Ctrl+Alt+L) as
inko fmtformats, from Cuttlebone (which runs Inko's own formatter on the unsaved text). To format on save, turn on Settings → Tools → Actions on Save → Reformat code. - Go to definition (⌘-click / Ctrl-click, ⌘B / Ctrl+B) for types, traits, constants, functions, methods, fields, modules, and local variables and parameters, including the standard library and dependencies. A method call goes to the method the compiler calls, from Cuttlebone.
- Go to Type Declaration (⇧⌘B / Ctrl+Shift+B)
- Find Usages (⌥F7 / Alt+F7) and Rename (⇧F6 / Shift+F6) across the project, by the compiler's answers, including renaming a module's file, and a trait's method with the methods implementing it
- Highlight usages of the name at the caret within the file
(⌘⇧F7 / Ctrl+Shift+F7), with assignments (
=,:=,+=and the rest) as writes - Parameter info (⌘P / Ctrl+P) in calls, and parameter-name hints before positional arguments
- Type hints for
letbindings and closure arguments that don't write a type. Types and parameter names each have a switch in Settings → Editor → Inlay Hints - Trait gutter icons linking traits, their implementations, and the methods that implement them, with Go to Implementation (⌥⌘B / Ctrl+Alt+B) and Go to Super Method (⌘U / Ctrl+U)
- Implement Methods (⌃I / Ctrl+I) and Override Methods (⌃O / Ctrl+O)
in
impl Trait for Type, stubbing the trait's methods - Generate (⌘N / Alt+Insert) in a type: a Constructor (
fn static newfrom every field) and Implement Trait (animplfor a chosen trait, with its required methods stubbed and the trait imported) - Expression Type (⌃⇧P / Ctrl+Shift+P): the compiler's type
- Go to Test (⌘⇧T / Ctrl+Shift+T) between
src/app/util.inkoandtest/app/test_util.inko, and Create Test - Quick documentation (F1, or hover): a definition's signature, where it's defined, and its doc comment, and the type at the caret ("Type here"); ⇧F1 opens standard library definitions on docs.inko-lang.org
- Go to Class / Go to Symbol (⌘O / ⌥⌘O) and Search Everywhere for Inko types, traits, methods, fields, and constants, from Cuttlebone's workspace symbols: the project's, its dependencies' and the standard library's, including what isn't saved yet
- Structure view, breadcrumbs, and sticky lines for each file's declarations, from Cuttlebone's document symbols, through IntelliJ's LSP support
- Code folding for blocks, argument lists, comment runs, and imports, from Cuttlebone (also in code that doesn't parse)
- Optimize Imports (⌃⌥O / Ctrl+Alt+O): removes unused imports and sorts the rest
- Refactorings: Change Signature (⌘F6 / Ctrl+F6), Extract Method (⌥⌘M / Ctrl+Alt+M), Inline Method and Inline Variable (⌥⌘N / Ctrl+Alt+N), Introduce Variable (⌥⌘V / Ctrl+Alt+V), Introduce Parameter (⌥⌘P / Ctrl+Alt+P), Introduce Constant (⌥⌘C / Ctrl+Alt+C), Safe Delete (⌘⌫ / Alt+Delete), Move (F6) for a top-level type, trait, constant, or function to another module, and moving files or directories and renaming directories (which updates their modules' imports)
- Type Hierarchy (⌃H / Ctrl+H) for traits and the types implementing them, and Call Hierarchy (⌃⌥H / Ctrl+Alt+H) for callers and callees
- Usage counts ("N usages") above declarations
- Surround With (⌥⌘T / Ctrl+Alt+T) and Unwrap/Remove (⌘⇧⌫ / Ctrl+Shift+Delete), Move Statement (⇧⌘↑/↓ / Ctrl+Shift+↑/↓), Complete Current Statement (⇧⌘⏎ / Ctrl+Shift+Enter), and Extend Selection (⌥↑ / Ctrl+W) through calls, arguments, strings, brackets, statements, and declarations, from Cuttlebone (also in code that doesn't parse)
- Copy Reference (⌥⇧⌘C / Ctrl+Alt+Shift+C), like
app.util.Helper.new - New Inko File (File → New → Inko File): a module, a test, or a program
- Inko highlighting in Markdown
```inkocode fences - External Libraries: the standard library and each project's
dependencies, browsable and searchable, with
depandbuildexcluded from the project inko.pkg: highlighting and errors, plus Tools → Inko Dependencies to sync, update, add, and remove dependencies, and a Sync button wheninko.pkgchanges- Completion is Cuttlebone's: members after
.(the compiler's list), the names in scope (locals, the type's methods, the module's own and imported names, the prelude's), the public names of modules not imported yet, adding the import when you choose one, keywords where the grammar takes them, fields after@, and modules and symbols in animport. Where Cuttlebone can't answer for the code being edited, Parakeet offers the locals and parameters in scope, keywords, the file's fields, and after.members by name - Imports on paste: pasting Inko code into another module adds the imports it needs (Settings → Editor → General → Auto Import → Insert imports on paste)
- Live templates:
fn,fnp,type,typep,trait,impl,match,main,tests, andtest, and postfix completion:.if,.match,.let,.not,.return, and.parafter an expression - TODO highlighting and spell checking in comments
- Test runner: results per test file and test, failures with expected and actual values, jumping to (or rerunning) a single test, and rerunning the failed tests
- Run and test from the gutter: ▶ next to
fn async main, next tofn pub testsin test files, and next to eacht.test('…') - Run configurations for
inko run,inko test,inko build, andinko check, with release builds, other targets, and static linking, and clickablefile.inko:linelinks in the output - New Project wizard (File → New → Project → Inko) using
inko init, optionally as a library or with a GitHub Actions workflow. The main module gets a valid module name (e.g.from_intellijfor a project named "From IntelliJ"), which you can change. - Per-project toolchain in Settings → Languages & Frameworks → Inko:
auto-detect, ivm's default, any version ivm has installed, or a custom
executable. With ivm installed, you can also install and remove Inko versions
there, and banners offer to install Inko when it's missing, or a newer one
when
inko.pkgrequires it.
The plugin understands Inko through the inko command rather than its own
compiler, so it always agrees with your installed Inko version.
- Errors are Cuttlebone's pulled diagnostics (
textDocument/diagnosticfor the editor and Inspect Code,workspace/diagnosticfor the whole project): the same asinko checkreports, from a check of the whole project with your unsaved text, so imports in both directions and dependencies resolve. - Go to definition, quick documentation, parameter info and hints, and
Implement Methods use the definitions
Parakeet reads from the code: the project's
src/andtest/, its dependencies indep/, and the standard library (inko print std), with unsaved text in place of a file's, each file read again when it changes. Nothing is compiled for it, so it follows unsaved edits and code that doesn't compile. - Cuttlebone answers with the compiler's own types and definitions; see
Cuttlebone for which features ask it. Parakeet requires
it: without it, what needs the compiler's types (go to definition of
anything but a local or a module, completion, parameter info,
hints, usage highlighting, Expression Type, Find Usages, Rename and the
refactorings built on it) is unavailable, and Parakeet says so once when the
project opens. Each question has a time budget (0.4 s for resolving a name, 1.5 s for
completion, parameter info, documentation and Expression Type, 2 s for
usage highlighting and for a file's inlay hints, asked in one request, and
10 s for a search's references), and answers are kept until the code
changes. Highlighting only waits for resolving names (inspections and
intentions resolve them), the inlay hints and usage highlighting:
Cuttlebone answers questions about unchanged text from the check it kept,
in under a millisecond after a file's first check, which costs a whole
check (about 50 ms for a small project, more on a busy machine). A
question whose wait is cancelled because the caret moved isn't cancelled
itself: that would discard the checks Cuttlebone kept, and its answer is
kept for next time. An edit cancels (
$/cancelRequest) every question about the text before it, since Cuttlebone (from its seventh milestone) would otherwise answer each one for that text, at a check each, for nobody: except what's still waiting for its answer, a search or completion (whose items are filtered as you type on). Usage highlighting, which runs each time the caret moves, so on every keystroke, asks once the text has been still for as long as highlighting waits after an edit (IntelliJ's autoreparse delay, 0.3 s by default). In code being edited, Cuttlebone answers from a copy with the broken lines blank, or else from the last version that type checked ("stale", shown only where nothing changes code; see Answers in code being edited). - In code Cuttlebone can't check (it doesn't parse there, or a
declaration has an error), some features read the code themselves instead,
so they don't go dark mid-edit; see
Answers in code being edited for which.
That reading doesn't infer types: a method call like
x.to_stringnarrows tox's type when it's written or obvious (x: Foo,let x = Foo.new,self), and otherwise offers everyto_stringit can't rule out. It's never used where Cuttlebone has answered, or isn't running. - Find Usages and Rename search the IDE's word index for each name, then
keep the occurrences Cuttlebone's
textDocument/referenceslists: one check of the whole project, once per search, matched by the compiler's ids. For a declaration outside the project (the standard library, a dependency), a use of it in the project is found first, by resolving candidates a file at a time. A trait's method counts the calls to the methods implementing it, since the compiler gives each its own id, and renaming any of them renames them all. A compound assignment to a member (c.value += 1) calls the gettervalueand then the settervalue=, so it's a use of both. Renaming a getter or setter offers to rename the other beside it too (as IntelliJ does for Java's), in the same rename, so every usage is found while the code still type checks; renaming only one lists such a site as a conflict. Where references have no answer (a file in the project doesn't parse or imports a module that doesn't exist, or code a declaration's error may affect mentions the name), each candidate is resolved throughtextDocument/definitioninstead, a file's requests sent together. Only there, in code Cuttlebone can't check, does Find Usages show what Parakeet's own reading finds, as possible usages; Rename leaves those alone and lists them as conflicts first. Usage counts, Safe Delete, Change Signature, Inline Method, Move and Introduce Parameter build on the same search (usage counts stop at 99+), and like Rename use fresh answers only. Moving files and directories, and renaming directories, make Cuttlebone's edits (workspace/willRenameFiles, asked before the move, from one check of the whole project) to every import of the modules that move, and to their uses by their own name; without its answer, the move lists that as a conflict. - Type Hierarchy is Cuttlebone's, from the type database: the traits a type implements, the traits a trait requires, and what requires or implements it.
- Dependencies need syncing when
dep/hashisn't the BLAKE3 hash ofinko.pkg, which is howinkoitself decides. Errors, indexing, and test runs never sync (inkowould otherwise sync, and need the network, in each mirror): they use the project'sdep/as it is, and a banner says when it's out of date or was never synced. - Introduce and Inline Variable, Introduce Constant, Move Statement, and
Smart Enter work from the file's tokens, following Inko's syntax
reference: statements end at a line break unless the next line continues
them (e.g. starts with
.), and inlining parenthesizes values whose operators would otherwise bind differently (Inko's binary operators share one precedence and group left to right, andand/orbind more loosely). For the same reason, part of an operator chain (60 - 60intotal - 60 - 60) isn't offered as an expression. Inline Variable refuses a variable that's assigned again (=,:=, or a compound assignment like+=); one whose value calls a method, reads a field or makes an array, when it's used more than once (each use would make its own) or code runs before its last use (which may change it); and one whose value usestry,throworreturn, into a closure (where they'd leave the closure). And Introduce Variable doesn't replace an occurrence that's assigned, past an assignment to what the expression reads, or in a loop that assigns it. - Change Signature, Introduce Parameter, Extract Method, and Inline
Method rewrite calls from their tokens, following Inko's rules: no default
arguments (so new parameters need a value for calls to pass), named
arguments after positional ones, no empty parentheses, parameters that can't
be reassigned, and owned values that move unless borrowed. They update the
calls the search finds (above). Extract Method writes a local's type as
written, else as the compiler has it (Cuttlebone's fresh hover at the
local's name, as for "Specify type explicitly"), and leaves placeholders
for types it can't work out, which
inko checkpoints out if left behind. - Quick fixes are Cuttlebone's (
textDocument/codeAction), for the diagnostic IDs it fixes (invalid-symbol,unused-symbol,unused-result,invalid-assign,missing-field,invalid-implementation,missing-trait,unreachable): each one checked by Cuttlebone before it's offered, so it leaves fewer of its error and adds none, also in a file with other errors. They're asked for after highlighting, for the diagnostics on screen. - Implement and Override Methods stub a trait's required methods (those
without a body) or default ones, with bodies that call
panic('not implemented')(in Inko's prelude), which compiles for any return type. Go to Implementation and Super Method are Cuttlebone's:textDocument/implementation(what implements a trait or its method, from the type database),textDocument/declaration(the trait's method a type's method implements), and a type's traits from its type hierarchy. The trait gutter icons pointing down to implementations are Cuttlebone's too; those pointing up from animplcome from its header and the trait's declaration, as Implement Methods reads them (a request per method on every highlighting pass would be too many), and a type has no icon (⌘U finds its traits). Generate → Implement Trait leaves out the traits Cuttlebone says the type implements, in other modules too. - Style-guide inspections follow
Inko's style guide
where
inko fmtdoesn't. Removing an argument name is only offered where it keeps the meaning (the argument is in that parameter's position, after only positional ones), which needs the call to resolve to one method. - Tests run the way
inko testdoes (from the project root), but through a program Parakeet generates that reports each test to the IDE. It's built in a mirror of the project in the IDE's system directory (with its own build cache), so nothing is added to your project. It takes one name filter, so rerunning failed tests builds it once and runs it once per failed test. - Errors across the whole project come from Cuttlebone's workspace pull, which it answers when a check (after an edit, or when it starts) changes a file's errors; Parakeet asks again as soon as it has an answer.
- Intentions work from the file's tokens with the same operator rules as
the refactorings.
if⇄matchonly rewrites comparisons of a name or field to literals (so the value is the same each time it's read), and argument names are only removed when each named argument is in its parameter's position, since Inko accepts named arguments in any order. - Expression Type uses Cuttlebone's type when hover's range is exactly
the chosen expression. "Specify type explicitly" writes the compiler's
type for the variable, and "Remove explicit type" is offered only when
the value says its type itself (a literal, a cast,
Point(…), orType.methodreturning that type, as Cuttlebone's type hints count it), so the annotation can't be what decides it, and the compiler's type for the value is the one written. Both use fresh answers only, since they write code, and types the compiler couldn't infer fully (with a?) never. - Move rewrites imports from the usages Find Usages finds, and adds the imports the moved code needs the same way imports on paste does: copying records the imported and module-level names the copied code uses, and pasting adds the ones the target module lacks.
- Indentation, Unwrap, and postfix completion come from the file's own
tokens, so they follow unsaved edits and work without
inko.
-
A JetBrains IDE, version 2025.3 or newer
-
The bundled TextMate Bundles plugin enabled (it is by default)
-
The
inkoexecutable. By default the plugin usesinkoonPATH, then ivm's default version. To pick an ivm-installed version or another executable for a project, use Settings → Languages & Frameworks → Inko. -
Cuttlebone 0.15.0 or newer, for the compiler's types and definitions: Parakeet offers to download it
Parakeet is published on GitHub Releases, with a plugin repository your IDE can check for updates:
- In your IDE, open Settings → Plugins, click ⚙️, and choose Manage Plugin Repositories…
- Click +, enter
https://github.com/setdef/Parakeet/releases/latest/download/updatePlugins.xml, and click OK. - In the Marketplace tab, search for Parakeet for Inko, and install it.
The IDE then offers updates like any other plugin.
Alternatively, download parakeet-<version>-signed.zip from
Releases and use Settings →
Plugins → ⚙️ → Install Plugin from Disk… (without automatic updates). To try a
local build, run ./gradlew buildPlugin and install
build/distributions/parakeet-<version>.zip the same way.
Cuttlebone is a language server for Inko that type checks with Inko's own compiler. Parakeet requires it for everything that needs the compiler's types; without it, those features are unavailable rather than guessed.
Open an Inko file. With no Cuttlebone (none set, none on PATH, none
downloaded), Parakeet offers to download one, in one notification: the
build of the newest Cuttlebone release for your Inko (inko --version,
from the project's toolchain), OS and CPU. Click Download Cuttlebone
and it's downloaded, checked and started; nothing is downloaded without
that click. Parakeet then starts one cuttlebone per project, through
IntelliJ's own LSP support, and stops it when the project closes.
Cuttlebone has builds for macOS 15 or newer and Linux, on ARM64 and x86-64. Each build works with one Inko version, the compiler it links. When the newest release has none for your Inko, Parakeet looks for an older release from 0.15.0 on that has one (in Cuttlebone's release index), and if none does, says so instead of offering a build that wouldn't run.
- Downloads come from Cuttlebone's GitHub
releases, through the
IDE's own HTTP support (so its proxy settings apply), with progress and a
cancel button. Each is checked against its release's
manifest.json(size and SHA-256), unpacked, and run once (cuttlebone --versionmust name the Inko it's for) before it's moved into place, in one step, in JetBrains' shared data directory (parakeet/cuttlebone/, one directory per Cuttlebone and Inko version, used by each of your JetBrains IDEs). A failed, cancelled or partial download is never used. - When your Inko changes (ivm switched versions, say, noticed when you come back to the IDE), Parakeet uses the download for the new version, or offers to download it the same way.
- Updates are offered, never installed for you: when a newer Cuttlebone has a build for your Inko, Parakeet says so once.
To use a Cuttlebone of your own, put cuttlebone on your PATH (Parakeet
also checks Cargo's bin directory, where cargo install puts it), or set
Settings → Languages & Frameworks → Inko → Cuttlebone → Executable. The
setting comes first, then PATH, and either takes precedence over a
download. A cuttlebone on PATH or in Cargo's bin directory that
doesn't run, or whose --version doesn't print a Cuttlebone version (a
mise shim for a tool that isn't installed, say), or that's older than
0.15.0, isn't counted: Parakeet skips it, goes on to the next one or a
download, and says so on the settings page. One that old in the setting
is refused, saying why, and an older download is left unused, with the
newer one offered as for none. Building Cuttlebone yourself needs Rust and LLVM 18 (its README
says how), and it refuses any Inko but the one it links:
cuttlebone --version prints it (e.g. cuttlebone 0.10.0 (Inko 0.21.1)).
The settings page says which Cuttlebone is in use and where it came from
(the setting, PATH, Cargo's bin directory, or a download), whether it's
running, and if not, why: not found, a different Inko version, or stopped
after crashing. Beyond the one notification when there's no Cuttlebone at
all, or none that will run (with the download offer, when there's one),
Parakeet says so there (and in idea.log) and nowhere else. It restarts a crashed
Cuttlebone up to 5 times in a row, waiting 1, 2, 4, 8 and 16 s, and
IntelliJ's language services widget in the status bar can restart it by
hand.
Without Cuttlebone, every feature below is unavailable. With it:
| Feature | Cuttlebone's answer | Where Cuttlebone can't check the code being edited |
|---|---|---|
| Go to definition, and what's built on resolving a name (quick documentation, hierarchies, inspections, intentions, refactorings, …) | textDocument/definition: the declaration the compiler resolves |
for navigation, documentation and hierarchies, a stale answer, else Parakeet's reading of the code; for what changes code (refactorings, intentions, quick fixes, imports), nothing |
| Find Usages, Rename, usage counts, and the refactorings built on them | textDocument/references, once per search, for the whole project; a trait's method with its implementations' |
each candidate's textDocument/definition, a file's at once; Find Usages alone adds Parakeet's reading, as possible usages, and Rename lists them as conflicts |
| Call Hierarchy | callHierarchy/incomingCalls (from one check of the whole project, by the compiler's ids) and outgoingCalls, which Parakeet asks for (IntelliJ 2025.3's LSP support has none) |
a stale answer for callees; none for callers |
| Type Hierarchy | typeHierarchy/supertypes and subtypes, from the type database, which Parakeet asks for (IntelliJ 2025.3's LSP support has none) |
none |
| Go to Implementation and Super Method, trait gutter icons to implementations, a trait method's implementations in Find Usages and Rename | textDocument/implementation and textDocument/declaration, and a type's supertypes, which Parakeet asks for (IntelliJ 2025.3's LSP support has none); Rename takes fresh answers only |
none; Rename lists it as a conflict |
| Moving files and directories, and renaming directories | workspace/willRenameFiles, asked before the move and made after it (IntelliJ 2025.3's LSP support has none) |
none: a conflict says so |
| Errors and warnings, Inspect Code, Optimize Imports' unused imports | textDocument/diagnostic: the same as inko check, from a check of the documents as they are |
Cuttlebone's: it checks the code as it is |
| Quick fixes | textDocument/codeAction: each fix checked with its edits applied, which Parakeet asks for itself, for the errors it shows |
Cuttlebone's: a fix is offered where it leaves fewer of its error and adds none |
| Errors across the whole project (the Project view, Problems) | workspace/diagnostic, a long poll |
Cuttlebone's |
| Completion | textDocument/completion on the real buffer: members, names with what to import, keywords, fields, and an import's modules and symbols |
Parakeet's reading: locals and parameters, keywords, the file's fields, members by name |
| Parameter info | textDocument/signatureHelp: the resolved method's signature, or a type's constructor's (Point(x: 1), from its fields), with the receiver's type arguments, and the argument's parameter (named ones too) |
Parakeet's reading, for methods |
| Parameter-name and type hints | textDocument/inlayHint, once for the whole file |
none: hints only inform |
| Highlight usages | textDocument/documentHighlight: the same symbol by the compiler's ids, with assignments as writes |
Parakeet's reading |
| Quick documentation | the resolved definition, plus hover's type at the caret | the definition resolved as for navigation (above) |
| Expression Type | hover's type, when its range is the expression | none, and it says why |
| "Specify type explicitly" and "Remove explicit type" | hover's fresh type, of the variable or of a value that says its type itself | not offered |
| Go to Type Declaration | textDocument/typeDefinition, through IntelliJ's LSP support |
nothing |
| Go to Class and Go to Symbol | workspace/symbol, through IntelliJ's LSP support |
Cuttlebone's: it reads declarations from the syntax |
| Code folding | textDocument/foldingRange, which Parakeet's folding asks for (IntelliJ's own would restart all highlighting, inko check too, at each answer) |
Cuttlebone's: it folds from the tokens |
| Reformat Code | textDocument/formatting, through IntelliJ's LSP support |
none: inko fmt refuses code that doesn't parse too |
| Extend Selection | textDocument/selectionRange, which Parakeet's selection asks for (IntelliJ 2025.3's LSP support has none), waiting on the EDT up to 0.5 s: Cuttlebone answers from the tokens, without a check |
Cuttlebone's: it reads the tokens |
Where a search for what a refactoring changes has no fresh answer from Cuttlebone for a name that may be a use (its check crashed, it ran out of time, or the code there is being edited), the refactoring refuses, listing where, rather than leave the name as it is.
Where Cuttlebone answers that there's nothing there (a name that doesn't resolve, say), or has no answer for another reason (it's starting, or a question ran out of time), nothing is shown: Parakeet's reading stands in only for code being edited.
Errors and warnings are Cuttlebone's, which Parakeet pulls itself rather than through IntelliJ's display of them, which would have no quick fixes, wouldn't be in Inspect Code, and wouldn't mark files that aren't open; so without Cuttlebone there are none. Every other LSP feature IntelliJ offers is turned off, so nothing shows twice. Cuttlebone answers from each open file's unsaved text, and from saved text for the rest. A file changed with no editor open (by a refactoring, or by undoing one) is saved right after the change, since Cuttlebone would read its old text from disk; until it is, Parakeet uses no answer from Cuttlebone.
Since its fifth milestone, Cuttlebone answers in code that doesn't parse, or has an error in a declaration, where an answer still holds. It says how in each answer's status:
- Fresh, from a repaired copy: the documents as they are, with the lines in the way blank. Parakeet uses these like any fresh answer.
- Stale: from the last version that type checked, in text that hasn't changed since. Parakeet uses these only where an answer is shown and changes no code:
| Feature | Stale answer | Shown as |
|---|---|---|
| Parameter-name and type hints | used | the hint's tooltip says "possibly outdated" |
| Parameter info | used | "(possibly outdated)" after the signature |
| Quick documentation's "Type here" | used | "(possibly outdated)" after the type |
| Expression Type | used | "(possibly outdated)" after the type |
| Highlight usages | used | as a fresh answer: there's nowhere to mark it |
| Go to Type Declaration | used (IntelliJ's own) | as a fresh answer |
| Go to definition, documentation, Call and Type Hierarchy | used | as a fresh answer, but not one Rename at the caret takes |
| Rename, the refactorings, intentions, quick fixes and imports | not used: they change code from it | as no answer: nothing |
| Completion | not used: choosing an item inserts it (Cuttlebone never sends one) | as no answer: Parakeet's reading |
Where Cuttlebone has no answer in code being edited (the method being edited after the edit; a file importing a module that doesn't exist, before any good check, and since its eleventh milestone only what uses that import; a file importing a module that doesn't parse; and, since its eighth milestone, only the method or item with a declaration's error and those naming what it's in), the features in the table above read the code themselves. Elsewhere in a project with a declaration error, Cuttlebone's answers are fresh.
Tools are managed with mise (mise install).
| Command | What it does |
|---|---|
./gradlew runIde |
Launch a sandbox IDE with the plugin loaded |
./gradlew test |
Run all headless IDE tests, including integration tests against your installed inko and cuttlebone (-PnoCuttlebone skips the latter) |
./gradlew test -PunitOnly |
Run only the unit tests, which use a fake inko and a fake Cuttlebone and need nothing installed |
./gradlew typingReplay |
Type code into a std module at a person's speed against your installed inko and cuttlebone, and fail if typing responsiveness regresses (the limits, whose times are scaled by the machine's load unless PARAKEET_TIMING_STRICT is set, as CI sets it, and then by PARAKEET_TIMING_SCALE, the runner's baseline); PARAKEET_REPLAY_PROJECT adds a project of your own |
./gradlew verifyPlugin |
Check compatibility with the oldest and newest supported IDEs |
./gradlew buildPlugin |
Build the installable zip |
scripts/check.sh [--full] |
Every check a commit must pass (--full adds the real-world projects' tests and the Plugin Verifier); see AGENTS.md. Cuttlebone's integration tests use $CUTTLEBONE or cuttlebone on PATH, and are skipped (with a message) without one; --full requires it |
./gradlew test -PrealWorld --tests '*.RealWorldProjectsIntegrationTest' |
Open real Inko projects against your installed inko and cuttlebone: Yorick Peterse's public projects and Inko's own (idoc, website), cloned beside this repository (../yorickpeterse/<name>, ../inko-lang/<name>) or under PARAKEET_FIXTURES. Each is copied and synced, and skipped (with a message) when it isn't there or doesn't type check; nothing may log an error, and a sample of names must resolve, with hovers, documentation and completion |
./gradlew test -PrealWorld --tests '*.RealWorldRefactoringsIntegrationTest' |
On copies of the same projects, Find Usages of a sample of each kind of declaration must match Cuttlebone's references, and every refactoring, applied at a sample of sites, must leave the copy type checking with its tests passing, or refuse saying why (SESSION-real-world-refactorings.md). PARAKEET_SWEEP_ONLY=Rename,Inline runs only some, and PARAKEET_PROTOCOL=<dir> keeps, per project, what Cuttlebone was sent and answered, and the sources on disk as they changed |
scripts/install-hooks.sh |
Run scripts/check.sh on what's staged before each commit |
The files in bundles/inko/ are copied unchanged from inko-lang/vscode
(v0.7.0, commit 46243e5). To update them, copy package.json,
language-configuration.json, and syntaxes/inko.tmLanguage.json from a newer
release.
GitHub Actions runs build.yml on every push to
main and every pull request:
- Build and unit tests (Linux): builds the plugin zip (uploaded as an
artifact) and runs the unit tests, which use a fake
inko. - Plugin Verifier (Linux): checks compatibility with the oldest and newest supported IDEs.
- Integration tests on Linux and macOS, each against the newest Inko release
and the oldest supported one (0.20.0), installed with ivm by
ci/install-inko.sh. ivm builds Inko from source, so installed versions are cached. These skip Cuttlebone's integration tests. - Cuttlebone on Linux and macOS: the latest Cuttlebone release, downloaded
through its
manifest.jsonand checked against its size and SHA-256 byci/install-cuttlebone.sh, with the newest Inko it has a build for. It runs the integration tests against it and the typing replay, whose timing limits count here (AGENTS.md): as written for the reference machine, scaled by how fast the runner measures a fixed baseline workload (inko checkof std, byci/timing-baseline.py, in the job's summary), never below 1. Run by hand withslow, every answer from Cuttlebone is held that many milliseconds, to see the limits fail. Withreal-world(every release, or run by hand) it also runs the real-world projects' sweeps on Linux, on the commitsci/real-world-projects.txtpins, and fails if any project is missing or can't sync.
Dependabot proposes weekly updates to Gradle dependencies and GitHub Actions.
Pushing a tag like v0.11.0 (matching version in gradle.properties) runs
release.yml: the full build above, with the
real-world sweeps against the latest Cuttlebone, then it
signs the plugin and creates a GitHub Release with the signed zip and
updatePlugins.xml (the plugin repository file IDEs check for updates). If a
PUBLISH_TOKEN secret exists, it also publishes to the JetBrains Marketplace.
One-time setup:
-
Create a signing certificate (JetBrains' guide), kept outside the repository:
openssl genpkey -aes-256-cbc -algorithm RSA -out private_encrypted.pem -pkeyopt rsa_keygen_bits:4096 openssl rsa -in private_encrypted.pem -out private.pem openssl req -key private.pem -new -x509 -days 3650 -out chain.crt rm private.pem
-
Add repository secrets (Settings → Secrets and variables → Actions, or
gh secret set):CERTIFICATE_CHAIN(contents ofchain.crt),PRIVATE_KEY(contents ofprivate_encrypted.pem), andPRIVATE_KEY_PASSWORD.
Without the signing secrets, a release fails at its first step and publishes nothing.
To also publish to the JetBrains Marketplace later, upload the first version by
hand at plugins.jetbrains.com (it
only accepts automated uploads for plugins that already exist), then add a
PUBLISH_TOKEN secret (a
Marketplace token).
Audits and session reports, oldest first:
- Audit against Inko v0.21.1
- Guardrails, and trusting the compiler's output
- Fixing the rest of the audit
- Connecting to Cuttlebone
- Who owns each LSP feature
- Stale and repaired answers
- Requiring Cuttlebone
- Replaying typing against Cuttlebone
- Declaration errors, and 0.12
- Compound assignments
- Refactoring follow-ups
- Downloading Cuttlebone, and 0.13.0
- Hand-test fixes, and 0.12.1
- Find Usages and the refactorings on real-world code
- 0.13.1, and stale answers
- Slimming Parakeet
- Slimming Parakeet, part 2: Cuttlebone 0.14.0
- Late edits, and slimming Parakeet, part 3: Cuttlebone 0.15.0
Parakeet's icons (a flat parakeet on a branch) are original to this plugin and not derived from Inko's logo, which is licensed CC BY-NC-SA 4.0 and isn't used here.
MPL-2.0, the same license as inko-lang/vscode, whose grammar and language configuration this plugin bundles.