-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
use formatDocument as fallback for willSaveWaitUntil #783
use formatDocument as fallback for willSaveWaitUntil #783
Conversation
if server does not support willSaveWaitUntil the code can be formatted prior to save. fixes eclipse#761
42d70fb
to
96a3f92
Compare
The advantage of this proposal is, that the client can handle the operations which should be done prior to a document save. In this case formatting. The format enable and the range of the formatting can be modified on document level. |
@rubenporras and @mickaelistria WDYT? |
Hi @mickaelistria @rubenporras, could you please give me a feedback, if this proposal is acceptable? I would be happy to add this feature in the cdt-lsp release for Eclipse 23-09. |
So shouldn't it be called something more generic like
What are the possible values you're interested in "whole document", "modified lines"...? Can't this be made generic in LSP4E with some preference? (and CDT would propagate its own preference to LSP4E)
I would be happy as well, but I'm afraid it may be a bit short in time. Let's do our best and try anyway. |
A more generic name was my first intention as well, but currently it supports only formatting. When there is more stuff to do in the future, I think it's better to add new interfaces/services and do not mix them with the
Do you mean for the
Yes, that would be the preferences we want to start with.
It could. I would like to do this in a next step. But I think these preferences are located best under the editor. Since it controls the editor behavior. LSP4E is part of the implementation.
Thank you! |
So I don't get why using a service pattern if there is only 1 implementation, that is built in with the only consumer? |
Sorry. Maybe I don't understand which consumer do you mean? LSP4E ist the consumer, right? The idea is, that cdt-lsp provides this service to LSP4E. if (formatOnSave != null) {
formatOnSave.call(f -> formatDocument(f.isEnabledFor(document), f.getFormattingRegions(LSPEclipseUtils.toBuffer(document))));
} |
There is no OSGi service implementation needed in LSP4E. The service should be provided by external bundles.
Sorry my mistake, |
formatOnSave can never be null. When the ServiceCaller cannot find any service, it does not call formatDocument at all.
Ah OK, so there are 2 implementations: the default one built-in LSP4E, and another one provided by CDT. FYI, this does also relate to a bigger topic of per-editor preferences which has several issues open about. |
There is no OSGi service implementation needed in LSP4E. The service should be provided by external bundles.
Good point. I can add a LSP4E Preference setting to enable this feature.
I understand. The idea behind
Thank you. I'll have a look on that. |
Maybe we can put the format-on-save LSP4E preferences topic in a new issue? |
Could this be done in a next step in a separate interface/service like |
Sure, it's a hard one I believe which would also require change to Eclipse Text Framework. Not something we can fix soon. |
I removed the default implementation of |
Sure we can. What I want to avoid now is that consumers start to believe this OSGi service is safe for public usage. Discussions here has revealed that this is not necessarily the best approach to resolve the underlying problem so it's not one we will want to maintain; we will want to drastically change it later. We need a way to prevent people from building on top of "unstable" content. |
I'll try to add a commit for this. |
I do not have currently time to continue looking at this, so whatever you and Mickael agree should be fine. |
I added a public interface IFormatOnSave {
/**
* Returns the {@link FormatStrategy} for the given document.
* @param document
* @return FormatStrategy
*/
FormatStrategy getFormatStrategy(IDocument document);
}
public enum FormatStrategy {
/**
* No formatting. Disables the formatting for the given document.
*/
NO_FORMAT,
/**
* Format all lines
*/
ALL_LINES,
/**
* Format edited lines
*/
EDITED_LINES;
} @mickaelistria Is this what you had in mind as you mentioned this?:
|
I implemented the |
Still not happy with the enum |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See inline comments for a proposal to get rid of the enum: return 1 implementation instead of a enum value.
@Override | ||
public IRegion[] getFormattingRegions(IDocument document) throws CoreException { | ||
switch (formatOnSave.getFormatStrategy(document)) { | ||
case NO_FORMAT: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be 3 different service implementation: NoFormatRegionProvider, AllLinesRegionsProvider, ModifiedLinesRegionProvider
* Format strategy to be applied to a document. | ||
* | ||
*/ | ||
public enum FormatStrategy { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you split 3 different implementation from FormatRegionsProvider, then you can decide to return 1 of this implementation directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. But I am little unsure how to handle this public interface, which a foreign bundle has to implement:
public interface IFormatOnSave {
/**
* Returns the {@link FormatStrategy} for the given document.
* @param document
* @return FormatStrategy
*/
FormatStrategy getFormatStrategy(IDocument document);
}
What should be the retuern type of getFormatStrategy
when the foreign bundle should not implement IFormatRegionsProvider
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I get your point here.
9cb7330
to
41801bb
Compare
Yes, its an editor feature which relies on the corresponding LS. The link to the appropriate LS should be done via the I'll commit a new PR to choose the right |
The link to the LS is already known of LSP4E, but what we miss here is the link between the service implementation and the language server that contributes it. If we make it a field of the extension point, then we have this link and don't need to add |
That's true, but then we have fixed coupling to the bundle which implements the extension point. It could be that the format-on-save handling is implemented in the ui or editor bundle. With this OSGI service we are more flexible where to implement it. |
Separating the regions provider from the format regions implementors by adding a new interface IFormatRegions which will be extended by IFormatRegionsProvider.
because there is currently no preferences to disable the format-on-save feature when the LS does not support serverSupportsWillSaveWaitUntil
ok. so would you like it to be merged in the current state? |
- The OSGi IFormatRegionsProvider service is now filtered with the server id of the language server which is used for the document in the DocumentContentSynchronizer. This ensures that the used IFormatRegionsProvider matches the corresponding language server for the document. - In no bundle provides a IFormatRegionsProvider service, the LSP4E default provider (DefaultFormatRegionsProvider) is used to determine the ranges to be formatted on a file save event. - The LS wrapper in DocumentContentSynchronizer is now used to perform the format request.
@mickaelistria : This should be fixed with my last commit. If its okay, you can merge it :-) |
can be (re)added when LSP4E PR eclipse/lsp4e#783 has been merged
Are there any open topics here? |
* }</pre> | ||
*/ | ||
public interface IFormatRegionsProvider extends IFormatRegions { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inheritance quickly becomes a burden. I would instead suggest here
public interface IFormatRegionsStrategyProvider {
/**
* @return an IFormatRegionsComputer suitable for this document,
* or <code>null</code> if the current provider cannot equip the document
* for formatting regions.
*/
public IFormatRegionsComputer getFormatRegionsComputer(Document document);
}
and FormatAllLins and FormatEditedLines would implement IFormatRegionsComputer.
So consumers could just implement eg
public class FormatEditedLinesOnSaveForMyJSXFiles implements IFormatRegionsStrategyProvider {
public IFormatRegionsComputer getFormatRegionsComputer(Document document) {
if (!isJSXFile(document)) {
return null;
}
return new FormatEditedLines();
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The proposal above will lead to this call in DocumentContentSynchronizer
:
return formatRegionsStrategyProvider.getFormatRegionsComputer(document).getFormattingRegions(document);
This will fail if getFormatRegionsComputer(document)
returns null
, So a check for null
must be applied.
The only reason for:
public interface IFormatRegionsProvider extends IFormatRegions
is to distinguish between the provider (IFormatRegionsProvider
) and the computer (IFormatRegions
). The provider should be implemented as OSGI service . It contains the computer implementation (IFormatRegions
). In that case, the call is even simpler:
return formatRegionsProvider.getFormattingRegions(document);
/** | ||
* Get the formatting regions | ||
* @param document | ||
* @return region to be formatted or null if document should not be formatted on save. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This gives the impression that this particular provider can cause the document to not be formatted at all by returning null
even if some other suitable IFormatRegions for this document can contribute ranges to format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
even if some other suitable IFormatRegions for this document can contribute ranges to format.
The idea is, that null
shall be returned if no formatting is enabled for the document/project
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because of the contributive nature of Eclipse Platform and OSGi service, 1 provider returning null doesn't mean that there is no provider that can apply. This is related to my former questions about the case of multiple extensions to handle different LS or documents: when this happens, we need something to pick the right one and use it; the right one may depend on various things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 provider returning null doesn't mean that there is no provider that can apply.
There should be a single IFormatRegionsProvider
provider for a LS definition. The filtering is done by the serverDefinitionId
property in the OSGI component definition. This is my IFormatRegionsProvider
implementation for the CDT LS:
@Component(property = { "serverDefinitionId:String=org.eclipse.cdt.lsp.server" })
public class FormatOnSave implements IFormatRegionsProvider {
private final FormatEditedLines formatEditedLines = new FormatEditedLines();
@Reference
private EditorConfiguration configuration;
@Override
public IRegion[] getFormattingRegions(IDocument document) {
var file = LSPEclipseUtils.getFile(document);
if (file != null) {
var editorOptions = configuration.options(file);
if (editorOptions != null && editorOptions.formatOnSave()) {
if (editorOptions.formatAllLines()) {
return new IRegion[] { new Region(0, document.getLength()) };
}
if (editorOptions.formatEditedLines()) {
return formatEditedLines.getFormattingRegions(document);
}
}
}
return null;
}
}
This is related to my former questions about the case of multiple extensions to handle different LS or documents: when this happens, we need something to pick the right one and use it; the right one may depend on various things.
Please have a look at org.eclipse.lsp4e.DocumentContentSynchronizer.getFormatRegions()
. There we pick up the IFormatRegionsProvider
based on the languageServerWrapper.serverDefinition.id
as filter:
var serviceReferences = bundleContext.getAllServiceReferences(IFormatRegionsProvider.class.getName(), serverId);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently we pick up the first one, if there are several provider for the same LS id:
reference = serviceReferences[0];
the right one may depend on various things.
If there is a use case for more than one IFormatRegionsProvider
for a language server definition (e.g. for each LS instance) then the filter could be modified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe IFormatRegionsProvider
could implement a method which returns the serverID
for filtering purposes. IMO the filtering via OSGi component property is more charming, since its a one-liner:
var serviceReferences = bundleContext.getAllServiceReferences(IFormatRegionsProvider.class.getName(), serverId)
I think it provides also more flexibility in the future. A new filter property can be easily added to the component definition without breaking the API.
OK, I missed the service property to filter on the LS id. |
Or as |
|
I like this latest version a lot. @ghentschke Is it OK with you if we merge this one ? |
Yes! |
Thank you! |
because eclipse/lsp4e#783 has been merged
0.17.3 due to new format-on-save feature by PR eclipse#783
can be (re)added when LSP4E PR eclipse/lsp4e#783 has been merged
because eclipse/lsp4e#783 has been merged
0.17.3 due to new format-on-save feature by PR eclipse#783
0.17.3 due to new format-on-save feature by PR #783
if server does not support willSaveWaitUntil the code can be formatted prior to save
OSGi services are used to obtain the formatting enable and the regions to be formatted prior to buffer save.
fixes #761