paulk-asert commented on PR #2976:
URL: https://github.com/apache/groovy/pull/2976#issuecomment-6305052468

   Thanks Daniel, there's a lot of good work here, and wiring Console/groovysh 
to the engine is the right direction. I went through it in some detail, 
including running a couple of probes against the PR head. I don't think it's 
ready to merge yet. Here's what I found, most important first.
   
   **Must fix before merge**
   
   1. **`ReflectionParameterNames` (core) pins class loaders.** `DEBUG_NAMES` 
is a static `ConcurrentHashMap` keyed strongly by `Class<?>`, and 
`Java8.fillParameterNames` now routes through it. So every reflection-backed 
ClassNode the compiler builds keeps its class, and that class's loader, 
reachable forever. A probe that defines a class in a throwaway loader and calls 
`names(...)` on one of its methods shows the loader is never collected; without 
the call it is. This is also a behaviour change to the core compiler for all 
users, and it makes parameter names differ between the reflection path (real 
names) and the ASM decompiler path (`param0`). Could we move it out of this PR? 
Either keep it inside groovy-lsp, or give it its own JIRA with a cache that 
doesn't hold classes strongly.
   
   2. **The completion repair compile ignores the configured 
`CompilerSettings`.** `CompletionService.repairedModule` builds a fresh 
`CompilerConfiguration` and compiles to SEMANTIC_ANALYSIS without the disabled 
global transforms or `withProcessFlags`. With this buffer:
   
      ```groovy
      import groovy.transform.ASTTest
      @ASTTest(value={ System.setProperty('probe.ran','yes') })
      def x = 'abc'
      x.
      ```
   
      `engine.complete(uri, 3, 2)` runs the `@ASTTest` closure, whereas the 
normal diagnostics compile of the same buffer (with the trailing `x.` completed 
to `x.size()`) does not. `@Grab` would be processed the same way. The repair 
compile should go through the same settings path as the main compile.
   
   3. **Console highlighting cost per keystroke.** 
`SmartDocumentFilter.parseDocument()` runs synchronously in the DocumentFilter 
(so on the EDT). On every insert/remove it re-lexes the whole buffer and resets 
the whole document to `defaultStyle`, and `offsetOf()` rescans from offset 0 
for each token, so the cost is roughly tokens × length. The old filter diffed 
token lists and restyled only the changed tokens. Tracking a running offset 
would make it linear, and restricting the reset to the dirty range would help 
too. Related: those whole-document `setCharacterAttributes` calls are undoable 
edits, so the undo manager grows by a full-document attribute edit per 
keystroke.
   
   **Please add an opt-out**
   
   Some users will prefer raw typing speed over compiler assistance, and at the 
moment they can only partly turn it off:
   
   - **Console:** View → Smart Highlighter falls back to `GroovyFilter`, but 
`installLanguageHost()` attaches `ConsoleLanguageHost` unconditionally. The 
background diagnostic compile after each typing pause, and the compile on 
File→Open, always happen.
   - **groovysh:** tail-tips are off by default (Alt-S), but `Main` always 
installs `GroovyshLanguageSession` as a completer, so every Tab compiles the 
remembered buffer. `noSyntaxCheck` is no longer read on the new SYNTAX path 
either.
   
   Could we have a Console preference/menu item that skips attaching the 
language host, and a groovysh option (reviving `noSyntaxCheck`, or a new one) 
that skips the language session? In both cases it should fall back to the 
previous behaviour (regex highlighting / the existing `scriptCompleter` and 
Inspector).
   
   **Should fix**
   
   - Each `SmartDocumentFilter` opens its own `GroovyLanguageClient`/server 
(with its own compile executor thread), and nothing closes it: not 
`Console.exit()`, and not toggling the smart highlighter, which builds a new 
filter. Embedders opening several Console windows leak one per window/toggle.
   - `ConsoleLanguageHost.refresh()` compiles on the calling thread, so 
File→Open now blocks the EDT for a full compile.
   - `GroovyLanguageClient`: `version`, `sent` and `diagnostics` are 
unsynchronized but used from both the EDT and the debounce thread. A load 
overlapping a pending compile can leave the client and server disagreeing about 
the current text.
   - groovysh: completion and tail-tips compile synchronously on the JLine 
thread with no timeout or cancellation. With tail-tips on, METHOD fires on 
every keystroke inside `(`, and each one is a full compile of the remembered 
buffer.
   - `withProcessFlags` flips JVM-wide 
`groovy.asttest.enable`/`groovy.grape.enable`. That was tolerable in a 
standalone server, but in-process it can race the host's own compiles (e.g. a 
Console run while a background compile is in flight).
   - Completion shortcut (matrei's comment): `isCompletionChord` requires 
`VK_SLASH` and rejects Shift, so on Swedish/German layouts there's no way to 
trigger it. A configurable binding would solve both that and the Ctrl+Space 
question.
   - Host config forwarding is partial: only classpath, plain/star imports and 
script base class. Static/aliased imports and AST customizers aren't forwarded, 
but the adoc says "imports and AST customizers". Reading `ImportCustomizer` 
private fields by reflection is also fragile; it would be better to add an 
accessor in core if we need it.
   - API surface/compat:
     - `SmartDocumentFilter` is public since 3.0, and this removes 
`HIGHLIGHTED_TOKEN_TYPE_LIST` and changes `getLatestTokenList()`'s return type.
     - `GroovyLanguageClient` is public in a non-internal package.
     - `GroovyLanguageEngine` gains a lot of public methods the hosts don't use.
   
     Can we trim/narrow these?
   - Packaging: groovysh no longer has groovy-console on its compile classpath, 
so a Maven user depending on groovy-groovysh alone loses `/console` 
(gracefully, but silently). groovysh and groovy-console now always pull 
groovy-lsp + lsp4j + gson, so groovy-lsp is optional in name only. Worth a line 
in the release notes at least.
   
   **Merge conflict**
   
   There's one conflict in `Console.groovy` with GROOVY-12436 (key bindings 
preserved across theme switch). It's mechanical, but note the PR removes 
`import java.awt.Container`, which master's 
`updateUIPreservingKeyBindings`/`saveKeyBindings` still use, so resolving just 
the marked hunk won't compile. (Alt+/ itself survives a theme switch since it's 
a KeyListener, so it doesn't need the 12436 mechanism.)
   
   The Copilot points on `positionOf`/`offsetOf` line handling look fixed, 
thanks. Happy to look again once the first three are addressed; splitting the 
core `ReflectionParameterNames` change out would also make this easier to 
review.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to