<commit_before>package com.reason.ide.format;

import com.intellij.openapi.application.*;
import com.intellij.openapi.application.ex.*;
import com.intellij.openapi.command.*;
import com.intellij.openapi.command.undo.*;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.project.*;
import com.intellij.openapi.util.*;
import com.intellij.openapi.vfs.*;
import com.intellij.psi.*;
import com.reason.*;
import com.reason.ide.settings.*;
import org.jetbrains.annotations.*;

public class ReformatOnSave {
  private static final Key<Integer> REFORMAT_COUNT = new Key<>("reasonml.format.count");
  private static final Log LOG = Log.create("format.auto");

  public static void apply(@NotNull Project project, @NotNull Document document) {
    ORSettings settings = ORSettings.getInstance(project);
    if (settings.isFormatOnSaveEnabled()) {
      PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
      if (psiFile != null && psiFile.isWritable()) {
        VirtualFile virtualFile = psiFile.getVirtualFile();
        if (virtualFile != null && virtualFile.exists()) {
          Integer count = psiFile.getUserData(REFORMAT_COUNT);
          if (LOG.isDebugEnabled()) {
            LOG.debug("Before document saving (" + project.getName() + ", autoSave=true, count=" + count + "): " + psiFile.getVirtualFile());
          }
          if (count != null && count > 2) {
            LOG.warn("   -> Too many saves (" + count + "), auto reformat is cancelled");
            psiFile.putUserData(REFORMAT_COUNT, 1);
            return;
          }

          String textToReformat = psiFile.getText();
          FormatterProcessor formatterProcessor = ORPostFormatProcessor.getFormatterProcessor(psiFile);
          String newText = formatterProcessor == null ? textToReformat : formatterProcessor.apply(textToReformat);

          if (newText == null || textToReformat.equals(newText)) {
            LOG.debug(" -> Text unchanged, abort format");
            psiFile.putUserData(REFORMAT_COUNT, 1);
          } else {
            ApplicationManagerEx.getApplicationEx()
                .invokeLater(() -> WriteAction.run(() -> {
                  PsiFile newFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
                  if (newFile != null) {
                    if (document.getText().equals(newText)) {
                      LOG.debug(" -> Text unchanged, abort format");
                      newFile.putUserData(REFORMAT_COUNT, 1);
                    } else {
                      CommandProcessor.getInstance().executeCommand(project, () -> {
                            document.setText(newText);
                            Integer newCount = newFile.getUserData(REFORMAT_COUNT);
                            newFile.putUserData(REFORMAT_COUNT, newCount == null ? 1 : newCount + 1);
                            newFile.putUserData(UndoConstants.FORCE_RECORD_UNDO, null);
                            FileDocumentManager.getInstance().saveDocument(document);
                          },
                          "or.reformat",
                          "CodeFormatGroup",
                          document);
                    }
                  }
                }));
          }
        }
      }
    }
  }<commit_msg>Fix reformat on save: using document.getText instead of psiFile.getText to get up to date edited text<commit_after>package com.reason.ide.format;

import com.intellij.openapi.application.*;
import com.intellij.openapi.application.ex.*;
import com.intellij.openapi.command.*;
import com.intellij.openapi.command.undo.*;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.project.*;
import com.intellij.openapi.util.*;
import com.intellij.openapi.vfs.*;
import com.intellij.psi.*;
import com.reason.*;
import com.reason.ide.settings.*;
import org.jetbrains.annotations.*;

/**
 * IntelliJ doesn't offer an easy way to change document text before it is saved.
 * <p>
 * When we detect that a document is about to be saved, we run an async write command
 * on the main thread that will change the edited document.
 * This write action is run after the initial save (We also use a command to have undo).
 * Then we save the document again, ie we will always generate a minimum of 2 document saves.
 */
public class ReformatOnSave {
    private static final Key<Integer> REFORMAT_COUNT = new Key<>("ReasonML.format.count");
    private static final Log LOG = Log.create("format.auto");

    public static void apply(@NotNull Project project, @NotNull Document document) {
        ORSettings settings = ORSettings.getInstance(project);
        if (settings.isFormatOnSaveEnabled()) {
            PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
            if (psiFile != null && psiFile.isWritable()) {
                VirtualFile virtualFile = psiFile.getVirtualFile();
                if (virtualFile != null && virtualFile.exists()) {
                    Integer psiCount = psiFile.getUserData(REFORMAT_COUNT);
                    int count = psiCount == null ? 1 : psiCount;
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Before document saving (" + project.getName() + ", autoSave=true, count=" + count + "): " + psiFile.getVirtualFile());
                    }
                    if (count > 2) {
                        LOG.warn(" -> Too many saves (" + count + "), auto reformat is cancelled");
                        psiFile.putUserData(REFORMAT_COUNT, 1);
                        return;
                    }

                    psiFile.putUserData(REFORMAT_COUNT, count + 1);

                    ApplicationManagerEx.getApplicationEx()
                            .invokeLater(() -> WriteAction.run(() -> {
                                PsiFile newFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
                                if (newFile != null) {
                                    String textToReformat = document.getText();
                                    FormatterProcessor formatterProcessor = ORPostFormatProcessor.getFormatterProcessor(newFile);
                                    String newText = formatterProcessor == null ? textToReformat : formatterProcessor.apply(textToReformat);

                                    if (newText == null || document.getText().equals(newText)) {
                                        LOG.debug(" -> Text null or unchanged, abort format");
                                        newFile.putUserData(REFORMAT_COUNT, 1);
                                    } else {
                                        CommandProcessor.getInstance().executeCommand(project, () -> {
                                                    LOG.debug(" -> Applying text formatting");
                                                    document.setText(newText);
                                                    newFile.putUserData(UndoConstants.FORCE_RECORD_UNDO, null);
                                                },
                                                "or.reformat",
                                                "CodeFormatGroup",
                                                document);

                                        if (count == 1) {
                                            // Only re-save first time, to avoid infinite loop
                                            FileDocumentManager.getInstance().saveDocument(document);
                                        }
                                    }
                                }
                            }));
                }
            }
        }
    }
{'fraction_non_alphanumeric': 0.10128369704749679, 'fraction_numerical': 0.001668806161745828, 'mean_word_length': 1.6828512396694215, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 31, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7147646', 'n_tokens_mistral': 1866, 'n_tokens_neox': 1789, 'n_words': 420}