A Solargraph plugin that gives LSP editors autocomplete, hover docs and go-to-definition for games built with Talon, a scene/game object/component framework for DragonRuby.
Solargraph already indexes Talon's source when it's in your game. This plugin teaches it the parts of Talon that are defined by metaprogramming:
class Mover < Talon::Component
prop :speed, default: 2 # speed / speed= (Integer), from prop and state
state :x, default: 0 # prop, state, on_update, ... complete in the class body
on_update do |args| # hook blocks run on the instance: self is a Mover
self.x += speed # so props, state, go, scene and attr_dr helpers complete
args.outputs.sprites << { ... } # args is GTK::Args in Talon's built-in hooks
end
end
module Visible
extend Talon::Concern
include Talon::Configurable
included do
prop :visible, default: true # Lamp.new.visible completes
end
class_methods do
def hidden_by_default # Lamp.hidden_by_default completes
prop_defaults[:visible] = false
end
end
end
class Lamp < Talon::Component
include Visible
end- A CRuby install. DragonRuby doesn't need one, but Solargraph does.
- Solargraph 0.61.x:
gem install solargraph -v '~> 0.61.0' - Talon's source in your Solargraph workspace, for example vendored in
mygame/lib/talon. Vendor Talon from commit 6625c07 or later so hook block parameters are typed asGTK::Args. - solargraph-dragonruby, recommended, for the DragonRuby
API (
args,outputs,attr_dr, ...). This plugin works without it, butargs,outputsand the other DragonRuby helpers won't complete.
Install the gems into the same Ruby that runs solargraph:
gem install solargraph-dragonruby solargraph-dr_talonThen add both plugins to the .solargraph.yml in your game's root directory (solargraph config generates a
default one), and make sure include covers Talon's source:
include:
- "mygame/**/*.rb"
plugins:
- solargraph-dragonruby
- solargraph-dr_talonRestart your editor's language server. See solargraph-dragonruby's README for editor setup.
Talon::Concern- Methods in a
class_methods do ... endblock are defined in the concern'sClassMethodsmodule, which is how they exist at runtime. - Including or prepending a concern adds its class methods to the host, along with those of every concern it
includes (its dependencies). Subclasses inherit them, so
prop,state,set_callback,prop_defaults, ... complete in any subclass ofTalon::Component,Talon::Scene,Talon::GameObjectorTalon::Game. - Inside
included do ... end(andprepended do ... end),selfis the class that includes the concern, which can't be known statically. So the concern's own class methods and those of the concerns it includes complete there. To usepropin anincludedblock, have the concerninclude Talon::Configurable(forstate,Talon::Stateful, and foron_*hooks,Talon::Callbacks). Talon then also includes them into the host.
- Methods in a
propandstatedefine a reader and a writer.-
The type comes from a
@returntag in the comment above the call, or is inferred from a literaldefault:(2is anInteger,"a"aString,trueaBoolean,Vector.new(...)aVector, ...). -
The comment becomes the hover docs, and go-to-definition jumps to the declaration.
-
A declaration in a concern's
includedblock defines the methods on the concern, so every host gets them. -
Declarations in a loop over a list of symbols define one reader and writer per symbol. The list can be a literal array or a constant assigned one earlier in the same file:
AXES = %i[dx dy].freeze AXES.each { |axis| state axis, default: 0 } # dx, dx=, dy, dy=
This is how
Talon::StateSpritedeclares the sprite attributes of everyTalon::GameObject(x,y,w,h,path,angle, ...), so they complete on game objects (from Talon c4b670c).
-
- Lifecycle hook blocks in a class body are evaluated on the instance, as Talon runs them.
-
This applies to
on_start,on_update,on_draw,on_destroy,on_enterandon_exit, and toset_callbackanddefine_callback. -
It also covers any other
on_*call, so custom hooks (def self.on_hit(&b) = set_callback(:hit, &b)) work too. -
Block parameters are typed by the hook method's
@yieldparamtag. Talon's built-in hooks document theirs asGTK::Args(since Talon 6625c07; older copies leaveargsuntyped). Document a custom hook the same way:# @yieldparam enemy [Enemy] def self.on_hit(*method_names, &block) = set_callback(:hit, *method_names, &block)
-
- The plugin recognizes Talon's DSLs by name, so it doesn't check that a class actually descends from a Talon
class:
prop :nameandstate :namecalled in any class body define accessors.- A block passed to any bare
on_*call in a class body is evaluated on the instance.
- Only
eachloops are unrolled, with a single block parameter passed straight toproporstate. A list that comes from a method call, another file, or contains anything but symbols isn't known statically, so its attributes don't complete. - A concern's own class side (
Visible.hidden_by_default) also completes itsClassMethods. This is what makes them complete insideincluded do ... end, but calling them on the concern itself fails at runtime. - The Talon methods behind a hook block (such as
updatefromon_update) are generated at runtime and don't complete.
bin/setup # install dependencies
bundle exec rake # tests + standardrb
bundle exec rake fixtures:sync # re-vendor Talon's lib/ into test/fixtures from main
bundle exec rake fixtures:sync[<ref>] # ...or from a specific branch, tag or committest/fixtures/talonis a snapshot of Talon'slib/that the tests run against.UPSTREAMrecords its commit. After a Talon change, sync it and re-run the tests.- The Solargraph dependency is pinned to
~> 0.61.0, because the node processor and convention internals this plugin hooks into change between minor releases. Re-run the tests before widening it.
Add changes to the ## [Unreleased] section of CHANGELOG.md as you go. To release:
bundle exec rake release:prepare[minor] # or major, patch, or an explicit version like 1.2.3
git push origin main v0.2.0 # the command it printsrelease:prepare runs the tests, bumps version.rb and Gemfile.lock, moves the Unreleased notes under a
dated heading, and commits and tags the release. The first release can use the current version:
rake release:prepare[0.1.0]. Pushing the tag runs .github/workflows/release.yml. It checks that the tag,
version and changelog agree, publishes the gem to RubyGems.org with
Trusted Publishing, and creates the GitHub release from that
version's changelog notes.
One-time setup: on RubyGems.org, add a trusted publisher for this repository with the workflow release.yml and
the environment release. The gem has never been pushed, so add it as a "pending" trusted publisher.
The gem is available as open source under the terms of the MIT License.
The vendored Talon source in test/fixtures/talon is also MIT licensed, by Austin Simmons.