[
https://issues.apache.org/jira/browse/GROOVY-12411?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18126066#comment-18126066
]
ASF GitHub Bot commented on GROOVY-12411:
-----------------------------------------
sonarqubecloud[bot] commented on PR #2936:
URL: https://github.com/apache/groovy/pull/2936#issuecomment-6109404048
## [](https://sonarcloud.io/dashboard?id=apache_groovy&pullRequest=2936)
**Quality Gate passed**
Issues
 [8 New
issues](https://sonarcloud.io/project/issues?id=apache_groovy&pullRequest=2936&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)
 [0 Accepted
issues](https://sonarcloud.io/project/issues?id=apache_groovy&pullRequest=2936&issueStatuses=ACCEPTED)
Measures
 [0 Security
Hotspots](https://sonarcloud.io/project/security_hotspots?id=apache_groovy&pullRequest=2936&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)
 [92.4% Coverage on New
Code](https://sonarcloud.io/component_measures?id=apache_groovy&pullRequest=2936&metric=new_coverage&view=list)
 [0.0% Duplication on New
Code](https://sonarcloud.io/component_measures?id=apache_groovy&pullRequest=2936&metric=new_duplicated_lines_density&view=list)
<!
> Add a first-party Language Server Protocol implementation (groovy-lsp)
> ----------------------------------------------------------------------
>
> Key: GROOVY-12411
> URL: https://issues.apache.org/jira/browse/GROOVY-12411
> Project: Groovy
> Issue Type: Improvement
> Reporter: Daniel Sun
> Priority: Major
>
> Apache Groovy needs a language server that uses the compiler in this
> repository. Community servers compile against a published Groovy jar, so
> parser and type-system changes reach an editor only after that jar is
> released. This change adds an optional {{groovy-lsp}} module and a
> {{groovylsp}} command. Editors, and later AI tools, share that language core.
> It is not a full IDE, and it does not replace Groovy-Eclipse or IntelliJ.
> h2. Why this shape
> The server compiles the open buffers with the in-tree compiler and answers
> Language Server Protocol (LSP) requests from that snapshot. A language change
> in this tree shows up in the server without forking a community project. The
> server refuses an edit that would guess. It does not rename an unbound
> dynamic call, and it does not rewrite every token that happens to spell the
> same name.
> There is no public extension SPI in this first cut. No ServiceLoader hook, no
> OSGi bundle, and no second hover or completion API. Static type checking of
> annotated members is core behavior, not a plugin. A later {{groovy-mcp}}
> wrapper may call the in-process engine. That wrapper is not part of this
> change.
> h2. What lands
> * Optional subproject {{{}groovy-lsp{}}}. It is not part of
> {{{}groovy-all{}}}. The binary distribution adds {{groovylsp}} and
> {{{}groovylsp.bat{}}}.
> * The default transport is stdio. {{--socket}} accepts one TCP client.
> {{--help}} and {{--version}} print and exit. On stdio, standard output is
> redirected to standard error, so a print from Groovy code cannot corrupt the
> protocol stream.
> * The protocol is LSP 3.18, carried by Eclipse LSP4J 1.0. Fields from a
> newer draft that the server does not understand are ignored. A capability is
> advertised only when it is implemented. LSP4J is an EPL 2.0 Category B
> binary. Its sources are not copied into Groovy.
> * Open Groovy buffers compile through semantic analysis. The server does not
> generate Groovy class files. Errors are collected, and a partial AST is still
> queried.
> * {{.java}} files in the workspace are joint-compiled with
> {{JavaAwareCompilationUnit}} ({{{}memStub{}}}) and {{javax.tools}} javac.
> Annotation processing is off.
> * After semantic analysis, {{StaticTypeCheckingPass}} type-checks members
> annotated {{@TypeChecked}} or {{{}@CompileStatic{}}}. It skips
> {{@CompileDynamic}} and {{{}TypeCheckingMode.SKIP{}}}. It does not force
> static checking on ordinary dynamic Groovy, and it does not raise the compile
> phase.
> * {{LocatedMessage.toDiagnostic()}} and {{write()}} report the message with
> no line or column when the parse context is null. They no longer throw
> {{NullPointerException}} in that case.
> * {{ASTNode}} column javadoc now matches the compiler. Columns are 1-based
> Unicode code points, the end column is exclusive, and a missing position is
> -1. Getters, setters, and stored values are unchanged. See GROOVY-12085.
> * User guide {{{}groovy-lsp.adoc{}}}. Contributor map
> {{{}subprojects/groovy-lsp/ARCHITECTURE.md{}}}.
> h2. In-process engine
> {{GroovyLanguageEngine}} answers the same questions as the LSP handlers and
> returns JDK records. Constructing it does not scan a workspace, so it does
> not compile a checkout root.
> * {{open(Path)}} reads one file from disk and returns its URI. Queries take
> that URI and use 0-based UTF-16 positions.
> * {{{}openBuffer{}}}, {{{}updateBuffer{}}}, and {{closeBuffer}} take any URI
> the caller chooses. A URI whose scheme is not {{file}} is not treated as a
> path. The engine does not reserve buffer names.
> * {{open}} and {{openBuffer}} compile immediately. {{updateBuffer}} only
> replaces the text. The next query compiles when the buffer has changed, and
> {{compileNow}} compiles at once. {{closeBuffer}} drops the buffer and
> compiles what remains.
> * {{setParentLoader}} sets the parent loader. {{setClasspath}} replaces the
> compile classpath. {{putExtra}} stores an opaque fingerprint so an in-place
> change to the same loader still forces a new compile.
> * Rename, format, and organize-imports return edits. They do not write files.
> Groovy Console and groovysh are not connected to this engine in this change.
> h2. What an editor gets
> Recognized sources are {{{}.groovy{}}}, {{{}.gvy{}}}, {{{}.gy{}}},
> {{{}.gsh{}}}, or language id {{{}groovy{}}}.
> * *Editing.* Incremental sync. Completion of keywords, snippets, members,
> workspace types with an import edit, and GDK members when a prefix is typed.
> Groovydoc is filled when the client resolves a completion item. Format and
> range format indent by braces and leave the text of a multiline string or a
> block comment unchanged. A typed newline indents the next line. A typed
> closing brace outdents, except inside a string, a comment, or a slashy regex.
> * *Imports and types.* Organize imports keeps star, static, and alias
> imports, and does not re-insert Groovy's default imports. {{source.fixAll}}
> only deletes unused imports. Other actions add one import, add every
> unambiguous missing import in one edit, add a package taken from the file
> path, and replace the {{def}} immediately before a field or local name with
> an inferred type.
> * *Generate.* Implement abstract methods by inserting an {{@Override}} stub
> that throws {{{}UnsupportedOperationException{}}}. Add {{@Override}} on a
> method that already overrides. Drop the {{private}} modifier on a field that
> has no accessor, so Groovy treats the field as a property. Insert
> {{{}@groovy.transform.ToString{}}},
> {{{}@groovy.transform.EqualsAndHashCode{}}}, or
> {{{}@groovy.transform.TupleConstructor{}}}. Those three actions insert the
> annotation only. They do not paste a method body. {{@ToString}} is skipped
> when that annotation, {{{}@Canonical{}}}, or {{toString}} is already present.
> {{@EqualsAndHashCode}} is skipped when that annotation, {{{}@Canonical{}}},
> {{{}equals{}}}, or {{hashCode}} is already present. {{@TupleConstructor}} is
> skipped when that annotation, {{{}@Canonical{}}}, {{{}@MapConstructor{}}}, or
> a declared constructor is already present, and when the class has no instance
> field.
> * *Create class.* For an unresolved type, offer a sibling {{Name.groovy}}
> next to the referencing file, using that file's package. A qualified name is
> accepted only when its package is the same. The action is skipped when that
> type is already defined, when the sibling is already open or on disk, or when
> the client did not advertise file creation ({{{}documentChanges{}}} plus the
> create resource operation). The stub is written only in that new file. The
> create neither overwrites nor ignores an existing file.
> * *Calls.* At a call site, complete a parameter name as a named-argument
> prefix when the call is bound or has one arity match. {{refactor.rewrite}}
> converts such a positional call to named arguments.
> * *Navigation.* Definition, type definition, implementation, declaration,
> and references. References match the declaration by owner, name, and
> descriptor, not by the bare spelling. Call hierarchy and type hierarchy.
> Document symbols and workspace symbols. A workspace-symbol query may be a
> case-insensitive substring or CamelHumps, so {{ASF}} finds
> {{{}AbstractServerFactory{}}}. Folding, selection ranges, document highlight,
> and document links on imports.
> * *Presentation.* Push and pull diagnostics: compiler messages, unused
> imports, static-type errors on annotated members, and javac messages from a
> joint compile. Semantic tokens for the whole file or a range. Inlay hints for
> declaration types and for parameter names. A parameter-name inlay is labelled
> inferred unless the compiler bound the call, and an inferred inlay does not
> insert text. Code lenses show a reference count.
> * *Workspace.* Workspace folders, watched files, configuration, and progress
> when the client asks for it. {{workspace/willRenameFiles}} updates one
> top-level Groovy class whose name matches the file name without its
> extension. It refuses a script, a file that declares several types, a class
> name that does not match, and {{{}.java{}}}.
> Commands:
> * {{groovy.lsp.organizeImports}}
> * {{groovy.lsp.showReferences}}
> * {{{}groovy.lsp.gotoSuperMethod{}}}, the nearest overridden method that has
> source. It skips {{Object}} and {{{}GroovyObject{}}}.
> h2. Rename
> {{methodTarget}} is often empty on a dynamic call. A match by name and arity
> is enough to display, and not enough to edit.
> ||Feature||When the compiler bound the call||When it did not||
> |Hover|The bound signature|One arity match on a resolved receiver, marked
> {{{}_inferred_{}}}. Otherwise the name only|
> |Definition|That method|Every arity match that has source. Never an arbitrary
> first hit|
> |Rename and prepare-rename|Allowed|Refused|
> A class, method, field, parameter, or bound local is renamed by identity.
> Renaming a class also renames the file when the class name matches the file
> name without its extension.
> h2. Groovy and Java in one workspace
> Joint compilation lets Groovy and Java sources in the workspace see each
> other's types. Go to definition can cross that boundary, and javac
> diagnostics are published with the Groovy ones. javac writes class files to a
> private temporary directory whose name starts with {{{}groovy-lsp-javac-{}}},
> not under the user home directory. On POSIX the directory is owner-only. It
> is removed on the next compile and when the compiler closes. If that
> directory cannot be created, the compile continues with Groovy only.
> This is not a Java language server. Java completion, Java formatting, and
> Java-only rename stay with a Java language server. Eclipse JDT and
> groovy-eclipse remain the stack for a full Java IDE. No source is copied from
> groovy-language-server-edt, and its identifier-scan rename is not used.
> ||Question||groovy-lsp||Other Groovy language servers||
> |Which compiler|The compiler in this repository|A published Groovy jar, or
> JDT plus groovy-eclipse|
> |Java IDE features|Joint type resolution, and definition from Groovy into
> Java|groovy-language-server-edt also completes, formats, and renames Java|
> |GDSL and dependency download|Not included|groovyls adds Jenkins GDSL and
> dependency download|
> |Unbound-call rename|Refused|groovy-language-server-edt can rename by
> scanning the identifier text|
> h2. Configuration and safety
> On {{{}initialized{}}}, if the client advertised
> {{{}workspace/configuration{}}}, the server asks for the {{groovy}} section
> without blocking the JSON-RPC reader. The client can also push
> {{{}workspace/didChangeConfiguration{}}}.
> ||Key||Meaning||
> |classpath|Extra jars and directories. The server does not run Gradle or
> Maven|
> |sourcePaths|Extra source roots. A relative path is resolved from a workspace
> folder|
> |grapeEnabled|Allow {{@Grab}} while compiling. Default is off|
> |astTestEnabled|Allow {{@ASTTest}} while compiling. Default is off|
> Any other key under {{groovy}} is stored as text. The server does not
> interpret it and does not download anything for it. Changing such a key
> builds a new class loader and recompiles.
> An empty source-path list and an empty classpath are filled separately, and
> only when the client left that list empty. Source roots are every existing
> directory among {{{}src/main/groovy{}}}, {{{}src/test/groovy{}}},
> {{{}src/main/java{}}}, and {{{}src/test/java{}}}. If none of those four
> exists, the server uses {{src}} when that directory exists, and otherwise the
> workspace folder itself. The classpath gains jars that sit directly under
> {{lib}} or {{{}libs{}}}.
> Diagnostics for one file stay on that file. Unchanged buffers are not
> compiled again. {{@Grab}} stays off unless the client turns it on, both by
> omitting its global transform and by the process property
> {{{}groovy.grape.enable{}}}. {{@ASTTest}} is a local transform, so it is
> turned off only by {{{}groovy.asttest.enable{}}}. Both properties are
> process-wide for the duration of a compile, then restored. One compile holds
> them so another compile cannot restore them early.
> h2. Out of scope
> * A full Java language server.
> * GDSL and Jenkins pipeline descriptors.
> * Invoking Gradle or Maven, including the Tooling API.
> * A debugger or a test runner.
> * Renaming by identifier text.
> * Name-span fields on {{{}AnnotatedNode{}}}. Identifier ranges stay inside
> groovy-lsp.
> * A public plugin SPI.
> * Wiring Groovy Console or groovysh.
> h2. Positions
> This is not an LSP break. Compiler columns are already 1-based Unicode code
> points (GROOVY-12085). LSP positions stay 0-based in the encoding chosen at
> initialize: {{utf-16}} unless the client offers {{utf-8}} or {{{}utf-32{}}}.
> The server converts both ways, including characters outside the Basic
> Multilingual Plane. Clients keep sending positions as they do today.
> h2. Running
> {code:java}
> groovylsp
> groovylsp --stdio
> groovylsp --socket PORT
> groovylsp --help
> groovylsp --version
> {code}
> An editor starts that process and speaks JSON-RPC on its standard input and
> output. After shutdown, {{exit}} ends the process with code 0. {{exit}}
> without a preceding shutdown ends it with code 1.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)