Rider - Taking ReSharper out of Process
- 16. ReSharper out of process
• Language server
• Headless. Command line process. IntelliJ provides the UI
Client/server communication
• Cross platform
.NET Framework on Windows. Mono on MacOS and Linux
• Removes Visual Studio in-process constraints
Memory usage. 64 bit
• Continued investment in ReSharper
- 17. Thick Client? Thin Client?
• IntelliJ provides high level UI elements, functionality and infrastructure
Editors, Alt+Enter, completion, Find Usages, test runner, debugging…
Searchable tree views, popup dialogs, settings pages…
• No knowledge of syntax trees or semantic model
Parsing, resolving, syntax highlighting, folding, inspections, refactoring,
code completion, etc. all owned by ReSharper
• (Some standalone functionality)
Find in path, REST client, Databases, VCS
• Optimisations
Lexing for initial syntax highlighting
- 19. Alt+Enter
• IntelliJ provides editor, text caret, and tracks Alt+Enter keypress
• Asks current language for items
• Current language is an IntelliJ facade for ReSharper out-of-proc
Asks ReSharper for items at current location
• ReSharper returns list of display names, icons and submenus
• IntelliJ displays items in Alt+Enter menu
- 20. Inspection highlights
• IntelliJ provides infrastructure to display “squigglies”
• Opposite direction, pushed from ReSharper
• Source file is opened, or modified
IntelliJ notifies ReSharper
• ReSharper analyses the file, runs inspections, gathers highlights
• ReSharper publishes list of range, severity and tooltip
• IntelliJ displays squiggles
- 21. Modifying source
• Bi-directional
• User typing
IntelliJ publishes changes as delta of typed characters at offset
• ReSharper rewriting code
Publishes delta as chunk of code
Renamed variable, new method, additional `using` statement, etc.
- 22. Observations
• Enabling functionality, rather than implementing it
Can show all Alt+Enter menus, run all inspections, rewrite code in
context actions and quick fixes
• As long as there is no UI…
• The data is very lightweight
- 23. IPC - RPC?
• Boilerplate - define calls and messages for each required action
• Imperative
• Conflict resolution?
Who wins? How to reset/resync state?
• JSON? Protobuf?
Client
(IntelliJ)
Server
(ReSharper)
- 24. MVVM
• Only send data required for UI components
• Lightweight View Model data
View
(IntelliJ)
Model
(ReSharper)
View
Model
- 26. Shared View Model
• Single view of state of entire IDE
Shared between front end and back end
Keep in sync. Only need to update changed fields
• Becomes declarative
No more boilerplate messages, just update View Model
• Reactive/observable. Composable
Subscribe for changes
• Two way
Client and server can both contribute to View Model
E.g. button click/refactoring results
• Tightly coupled? 🤔
- 27. Conflict resolution
• The client is always right
• Each value has a version
• Version increments only when client changes value
• If server changes value, no version update
• Only accept change with same or newer version
- 29. Wire protocol
• Becomes trivial - no messages, just deltas
Don’t change the protocol, just extend model
• Supports batching
• Serialisation by code generation via DSL
• Binary wire protocol, with logging
• Sockets
- 30. Rider Framework
• Two libraries, C# and Kotlin
Provides primitives and handles communication
• Kotlin based DSL to describe View Model
• Generates real code - C# and Kotlin
Interfaces, implementation and serialisation
• Business logic subscribes to and manipulates “real model”
Magic happens
- 31. View Model building blocks
• Lifetime
• Signals (events)
• Properties (observable value)
• Maps (observable collections)
• Fields (immutable)
• Call (async RPC)
• string
• int
• enum
• classdef (node)
• structdef (data)
- 32. Lifetime
class Lifetime {
static Lifetime Eternal;
void Add(Action action);
}
class LifetimeDef {
ctor(Lifetime parent);
Lifetime Lifetime;
void Terminate();
}
Dual of IDisposable
- 33. Signal
// Produce event
interface ISource<T> {
void Fire(T value);
}
// Subscribe to event
interface ISink<T> {
void Advise(Lifetime l, Action<T> handler);
}
// Composable event
interface ISignal<T> : ISource<T>, ISink<T> {
}
- 34. Properties
// Subscribe to event
interface ISink<T> {
void Advise(Lifetime l, Action<T> handler);
}
// Observable property
interface IProperty<T> : ISink<T> {
T Value { get; set; }
void View(Lifetime l, Action<Lifetime, T> action);
}
Stateful signal
- 35. Maps
class MapEvent<K,V> {
enum Kind { Add, Remove }
Kind kind;
K key;
V value;
}
// Observable collection
interface IViewableMap<K,V>
: IDictionary<K,V>, ISink<MapEvent<K,V >> {
void View(Lifetime l, Action<Lifetime, K, V> action);
}
- 36. Kotlin DSL
fun classdef (
name : String,
init : ClassdefNode.() -> Unit
)
classdef (
“Foo”,
{
myClassdef.map(…)
}
)
classdef (
“Foo”,
{
map(…)
}
)
- 37. Kotlin DSL
fun classdef (
name : String,
init : ClassdefNode.() -> Unit
)
classdef (
“Foo”,
{ lambda_expression }
)
classdef (“Foo”) {
lambda_expression
}
- 38. object Solution {
init {
map(“editors”, string, classdef(“Editor”) {
list(“document”, char)
property(“caret”, int)
map(“highlighters”, Range, Highlighter)
property(“completion”, Completion.nullable)
})
voidSource(“build”)
}
val Range = classdef(“Range”) {
field(“start”, int)
field(“length”, int)
}
val Highlighter = classdef(“Highlighter”) {
…
}
}
- 39. Challenges
• Rider’s Project Model very different to IntelliJ
Replace Project view with Solution Explorer
IntelliJ uses “project” where we expect “solution”
• What about duplicate language implementations?
E.g. JavaScript - WebStorm or ReSharper?
C++ CLion or ReSharper?
• Plugins are more complex
Front end and back end
• ReSharper out of process with Visual Studio? 🙊