diff options
Diffstat (limited to 'idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java')
-rw-r--r-- | idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java new file mode 100644 index 0000000..9d2d7a5 --- /dev/null +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingService.java @@ -0,0 +1,141 @@ +/* + * Copyright 2023 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.googlejavaformat.intellij; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Range; +import com.google.googlejavaformat.java.Formatter; +import com.google.googlejavaformat.java.FormatterException; +import com.google.googlejavaformat.java.JavaFormatterOptions; +import com.google.googlejavaformat.java.JavaFormatterOptions.Style; +import com.intellij.formatting.service.AsyncDocumentFormattingService; +import com.intellij.formatting.service.AsyncFormattingRequest; +import com.intellij.ide.highlighter.JavaFileType; +import com.intellij.lang.ImportOptimizer; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiFile; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import org.jetbrains.annotations.NotNull; + +/** Uses {@code google-java-format} to reformat code. */ +public class GoogleJavaFormatFormattingService extends AsyncDocumentFormattingService { + + public static final ImmutableSet<ImportOptimizer> IMPORT_OPTIMIZERS = + ImmutableSet.of(new GoogleJavaFormatImportOptimizer()); + + @Override + protected FormattingTask createFormattingTask(AsyncFormattingRequest request) { + Project project = request.getContext().getProject(); + + if (!JreConfigurationChecker.checkJreConfiguration(project)) { + return null; + } + + Style style = GoogleJavaFormatSettings.getInstance(project).getStyle(); + Formatter formatter = createFormatter(style, request.canChangeWhitespaceOnly()); + return new GoogleJavaFormatFormattingTask(formatter, request); + } + + @Override + protected String getNotificationGroupId() { + return Notifications.PARSING_ERROR_NOTIFICATION_GROUP; + } + + @Override + protected String getName() { + return "google-java-format"; + } + + private static Formatter createFormatter(Style style, boolean canChangeWhiteSpaceOnly) { + JavaFormatterOptions.Builder optBuilder = JavaFormatterOptions.builder().style(style); + if (canChangeWhiteSpaceOnly) { + optBuilder.formatJavadoc(false).reorderModifiers(false); + } + return new Formatter(optBuilder.build()); + } + + @Override + public @NotNull Set<Feature> getFeatures() { + return Set.of(Feature.FORMAT_FRAGMENTS, Feature.OPTIMIZE_IMPORTS); + } + + @Override + public boolean canFormat(@NotNull PsiFile file) { + return JavaFileType.INSTANCE.equals(file.getFileType()) + && GoogleJavaFormatSettings.getInstance(file.getProject()).isEnabled(); + } + + @Override + public @NotNull Set<ImportOptimizer> getImportOptimizers(@NotNull PsiFile file) { + return IMPORT_OPTIMIZERS; + } + + private static final class GoogleJavaFormatFormattingTask implements FormattingTask { + private final Formatter formatter; + private final AsyncFormattingRequest request; + + private GoogleJavaFormatFormattingTask(Formatter formatter, AsyncFormattingRequest request) { + this.formatter = formatter; + this.request = request; + } + + @Override + public void run() { + try { + String formattedText = formatter.formatSource(request.getDocumentText(), toRanges(request)); + request.onTextReady(formattedText); + } catch (FormatterException e) { + request.onError( + Notifications.PARSING_ERROR_TITLE, + Notifications.parsingErrorMessage(request.getContext().getContainingFile().getName())); + } + } + + private static Collection<Range<Integer>> toRanges(AsyncFormattingRequest request) { + if (isWholeFile(request)) { + // The IDE sometimes passes invalid ranges when the file is unsaved before invoking the + // formatter. So this is a workaround for that issue. + return ImmutableList.of(Range.closedOpen(0, request.getDocumentText().length())); + } + return request.getFormattingRanges().stream() + .map(textRange -> Range.closedOpen(textRange.getStartOffset(), textRange.getEndOffset())) + .collect(ImmutableList.toImmutableList()); + } + + private static boolean isWholeFile(AsyncFormattingRequest request) { + List<TextRange> ranges = request.getFormattingRanges(); + return ranges.size() == 1 + && ranges.get(0).getStartOffset() == 0 + // using greater than or equal because ranges are sometimes passed inaccurately + && ranges.get(0).getEndOffset() >= request.getDocumentText().length(); + } + + @Override + public boolean isRunUnderProgress() { + return true; + } + + @Override + public boolean cancel() { + return false; + } + } +} |