Skip to content

Completion using arbitrary langauge servers in Java text blocks#1536

Draft
danthe1st wants to merge 1 commit into
eclipse-lsp4e:mainfrom
danthe1st:java-text-block-completion
Draft

Completion using arbitrary langauge servers in Java text blocks#1536
danthe1st wants to merge 1 commit into
eclipse-lsp4e:mainfrom
danthe1st:java-text-block-completion

Conversation

@danthe1st

@danthe1st danthe1st commented May 16, 2026

Copy link
Copy Markdown
Contributor

I think it would be nice if we could use language servers from inside Java text blocks.

To demonstrate it, I implemented a prototype that can provide completions in the following way:

  • If completions are requested in a Java text block, it gets the logical contents of a text block.
  • It finds the position in the logical text block where the completion should be inserted
  • The language servers are asked for completion using the logical contents and insertion position
  • Completion proposals are created for both the normal LSP proposals and the text block-based proposals
  • If the new completions are executed, it performs the changes on the original source code.

With the HTML language server from WildWebDeveloper, this looks like this:

image
Screencast_20260516_151757.webm

When I say logical content, I mean the parsed String that Java will know about (which I can get from JDT) which is also shown when hovering over it:
image

Specifically, I have the following questions:

  • Would there be interest in such a feature (I could imagine that being especially useful in conjunction with an SQL langauge server)?
  • I created a TextBlockSourceContentMapper for converting positions within the Java code to positions within the logical text block content. Does anyone a better approach (e.g. getting it from JDT in some way) to this?
  • What should be used to determine what language is used in a text block? Currently, I'm thinking of the following ways:
    • Variable/parameter names (e.g. if the String is directly assigned to a variable/used as a parameter named html, it may want to treat it like an .html file)
    • Annotations like @Language("sql") or @Sql that are somehow associated with the string (e.g. by annotating parameters or variables)
    • IntelliJ seems to do something similar
    • If we do this, we might want to add some GUI/preference screen where users can specify the annotation they want to use
    • Similarly, plugins (especially the one providing the LSP) might want to set this as well
  • If the String is passed to certain "known" method (e.g. Connection#prepareStatement), the language server could be inferred.
    • It might make sense to configure that via preferences and allow the plugin providing the LSP to set this as well.
  • When whatever Oracle/OpenJDK might create as a successor to String Templates, that could have something useful for indicating that

Limitations

This requires the org.eclipse.lsp4e.resourceFallback.enabled preference being set to true (at least for now).

Note that this is everything but complete. I am creating a draft pull request to have a prototype as a base for discussions. For example, I hardcoded it using html for now.

@martinlippert

Copy link
Copy Markdown
Contributor

I think this would indeed be a very nice feature to have. How to do identify the type of language in the text block? The code creates a doc with .html, which probably causes the HTML language serve to jump in. How do you plan to do that when you can't hard-code the type of language?

@FlorianKroiss

Copy link
Copy Markdown
Contributor

I think this would be a nice addition.

Regarding content recognition:
IntelliJ also recognizes special comments, i.e.

// language=HTML
String html = "<body><h1>hi</h1></body>";

@danthe1st

Copy link
Copy Markdown
Contributor Author

How to do identify the type of language in the text block?

That is one of the main unanswered questions that I mentioned in the PR and that I'd like to get feedback on. I guess that it would be inferred from the source code in some way and ideally, that could be configured by both users (via a preference screen) and plugins which may have more information on that (e.g. plugins may know methods that expect Strings corresponding to the language server they are providing).

@danthe1st

Copy link
Copy Markdown
Contributor Author

IntelliJ also recognizes special comments

I guess it might be useful to define a (configurable) regex for comments right before text blocks containing one group for the language?

@martinlippert

martinlippert commented May 16, 2026

Copy link
Copy Markdown
Contributor

I don't really have a perfect solution in mind, but I usually don't like those approaches very much that require the developers to add things to the source code just to make the tooling happy (or working).

@danthe1st

danthe1st commented May 16, 2026

Copy link
Copy Markdown
Contributor Author

I don't really have a perfect solution in mind, but I usually don't like those approaches very much that require the developers to add things to the source code just to make the tooling happy (or working).

I think we can still provide default values (i.e. have a configuration that's somewhat compatible with IntelliJ's approach) where applicable.

@martinlippert

Copy link
Copy Markdown
Contributor

I don't really have a perfect solution in mind, but I usually don't like those approaches very much that require the developers to add things to the source code just to make the tooling happy (or working).

I think we can still provide default values (i.e. have a configuration that's somewhat compatible with IntelliJ's approach) where applicable.

What exactly do you have in mind here? Not sure I understand the "provide default values". Can you elaborate on that?

@danthe1st

danthe1st commented May 19, 2026

Copy link
Copy Markdown
Contributor Author

What exactly do you have in mind here? Not sure I understand the "provide default values". Can you elaborate on that?

My idea is maybe something like the following:

  • By default, a comment with the content matching language=([\w]+) means that a String right after is treated as a file with that extension
    • If someone wants to, they could change that regex?
  • If a text block is assigned to a variable with a known name (e.g. html, sql or similar), that language server is used
    • I am not sure about making this dependent on variable names
  • A plugin providing a language server could somehow provide a list of method (parameter)s that are known to correspond to that language server
    • Users add more method (parameter)s to that list via a preference screen
  • The preferences could also contain a list of annotations used for identifying a language (such as @Sql or @Language) in case a user wants to add some

I guess it might make sense to start with the first one or two and others can be added later if someone is interested/has a good idea on how to do that.

@martinlippert

Copy link
Copy Markdown
Contributor

It would be useful to extract the exact strategy that identifies the language into a distinct component and open that up for extensions. The use case that I have in mind here is:

In the Spring Tools, we know precisely for example when a SQL statement is used in a @Query annotation. We can even identify the exact dialect (SQL, HQL, JPQL, etc.). So for this case, it would be nice if the Spring Tools extension could contribute something as an extension that basically delivers the information about "this string literal or text block is SQL" to the LSP4E mechanism. In that case, the user does not need to add extra language ID to the source code.

My guess is that other tools would also be able to identify those languages, since they are usually not totally random and can be inferred from the wider context. In our case it is the Spring Data specific @Query annotation (among those embedded languages that we identify).

@danthe1st danthe1st force-pushed the java-text-block-completion branch from 233aca5 to a584177 Compare May 20, 2026 16:28
@danthe1st

danthe1st commented May 20, 2026

Copy link
Copy Markdown
Contributor Author

I updated the PR with some code that uses // language=... comments for now but I don't have a concrete plan on how to allow other plugins to do this.

@danthe1st danthe1st force-pushed the java-text-block-completion branch 8 times, most recently from 0f097d4 to 7f3184e Compare June 6, 2026 15:52
@danthe1st danthe1st force-pushed the java-text-block-completion branch 2 times, most recently from 7c274f5 to d7ff4ec Compare June 6, 2026 16:33
@danthe1st

danthe1st commented Jun 6, 2026

Copy link
Copy Markdown
Contributor Author

@martinlippert I added this interface to the the PR as an extension point (still work in progress as everything else):

/**
 * Allows plugins providing custom logic for which language servers should be
 * used within Java text blocks.
 */
public interface LSJavaTextBlockLanguageDetector {
	/**
	 * Creates a {@link URI} indicating the language server that should be used for
	 * a Java text block.
	 *
	 * @param compilationUnit
	 *                            The compilation unit corresponding to the Java
	 *                            file.
	 * @param textViewer
	 *                            The text viewer the Java file is opened in.
	 * @param textBlockRegion
	 *                            The source code region of the text block within
	 *                            the Java file.
	 * @return A {@link URI} where documents corresponding to that {@link URI} are
	 *         recognized by that language server. This {@link URI} does not need to
	 *         be resolvable.
	 */
	@Nullable
	URI createURIForTextBlock(ICompilationUnit compilationUnit, ITextViewer textViewer, IRegion textBlockRegion);
}

Do you think something along these lines would be useful (I am not yet sure what exactly this should return, I kept it as general as possible for now)?

Aside from that, do you think this feature would be useful for Spring Tools in some way (I think you might be doing something similar within your language server (providing things for Java files) but not providing language servers for your dialects)?

I am also thinking about possibly adding other capabilities (e.g. highlighting if it is easily possible) at some point but it would be useful to know about whether this would be actually useful for anyone.

@danthe1st danthe1st force-pushed the java-text-block-completion branch from d7ff4ec to 8e5c960 Compare June 6, 2026 18:36
@danthe1st danthe1st force-pushed the java-text-block-completion branch from 8e5c960 to b96e453 Compare June 6, 2026 18:37
@danthe1st

Copy link
Copy Markdown
Contributor Author

This requires the org.eclipse.lsp4e.resourceFallback.enabled preference being set to true (at least for now).

It seems like #1500 (and #1517) are about making that options available to users in some way. I don't know how to make sure lsp4e (specifically getting the URI) works with custom (non-file based) IDocuments without the nonbuffered approach introduced with #1501.

@martinlippert

Copy link
Copy Markdown
Contributor

@martinlippert I added this interface to the the PR as an extension point (still work in progress as everything else):

/**
 * Allows plugins providing custom logic for which language servers should be
 * used within Java text blocks.
 */
public interface LSJavaTextBlockLanguageDetector {
	/**
	 * Creates a {@link URI} indicating the language server that should be used for
	 * a Java text block.
	 *
	 * @param compilationUnit
	 *                            The compilation unit corresponding to the Java
	 *                            file.
	 * @param textViewer
	 *                            The text viewer the Java file is opened in.
	 * @param textBlockRegion
	 *                            The source code region of the text block within
	 *                            the Java file.
	 * @return A {@link URI} where documents corresponding to that {@link URI} are
	 *         recognized by that language server. This {@link URI} does not need to
	 *         be resolvable.
	 */
	@Nullable
	URI createURIForTextBlock(ICompilationUnit compilationUnit, ITextViewer textViewer, IRegion textBlockRegion);
}

Do you think something along these lines would be useful (I am not yet sure what exactly this should return, I kept it as general as possible for now)?

Aside from that, do you think this feature would be useful for Spring Tools in some way (I think you might be doing something similar within your language server (providing things for Java files) but not providing language servers for your dialects)?

I am also thinking about possibly adding other capabilities (e.g. highlighting if it is easily possible) at some point but it would be useful to know about whether this would be actually useful for anyone.

Yes, looks definitely useful fro our case. The return value would be something like:

URI.create("javatextblock:///" + UUID.randomUUID() + "." + language);

I think that should be documented somewhere, otherwise it is (at least to me) somewhat unclear what exactly the return value should be. Or could we also just return the identifier for the language instead of a URI ?

Also, what would be the exact identifier for a specific language? Would be good to have an example of how this matches in the end to a language server that gets selected for a specific lanaguge identifier.

@danthe1st

danthe1st commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

I think that should be documented somewhere, otherwise it is (at least to me) somewhat unclear what exactly the return value should be. Or could we also just return the identifier for the language instead of a URI ?

I am not exactly what it should return (or even what parameters would be ideal). One option would be the file extension as a String (e.g. html if it should be treated as a .html file). The URI is then used in LSContentAssistProcessor#computeCompletionProposals/LanguageServers.forDocument() to figure out the language servers to use.

URI.create("javatextblock:///" + UUID.randomUUID() + "." + language) is one option to do this but I think it could be any URI that the language server accepts (I don't know how exactly lsp4e decides which language server is used as I didn't take a look at that yet). I also thought about returning a custom class with constructors/factories providing different options.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants