<commit_before>package com.intellij.bash.shellcheck;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.intellij.bash.psi.BashFile;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.ExternalAnnotator;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.io.StreamUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class BashShellcheckExternalAnnotator extends ExternalAnnotator<String, Collection<BashShellcheckExternalAnnotator.Result>> {
  class Result {
    int line;
    int endLine;
    int column;
    int endColumn;
    String level;
    String message;
    long code;
  }

  @Nullable
  @Override
  public String collectInformation(@NotNull PsiFile file) {
    return file instanceof BashFile ? file.getText() : null;
  }

  @Nullable
  @Override
  public String collectInformation(@NotNull PsiFile file, @NotNull Editor editor, boolean hasErrors) {
    return editor.getDocument().getText();
  }

  @Nullable
  @Override
  public Collection<BashShellcheckExternalAnnotator.Result> doAnnotate(String fileContent) {
    String shellcheckExecutable = Registry.stringValue("bash.shellcheck.path");
    if (StringUtil.isEmpty(shellcheckExecutable)) {
      return null;
    }
    try {
      GeneralCommandLine commandLine = new GeneralCommandLine()
          .withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE)
          .withExePath(shellcheckExecutable)
          .withParameters("--color=never", "--format=json", "--severity=style", "--shell=bash", "--wiki-link-count=10", "-");
      Process process = commandLine.createProcess();
      writeFileContentToStdin(process, fileContent, commandLine.getCharset());
      if (process.waitFor(10, TimeUnit.SECONDS)) {
        String output = StreamUtil.readText(process.getInputStream(), commandLine.getCharset());
        Type type = TypeToken.getParameterized(List.class, Result.class).getType();
        return new Gson().fromJson(output, type);
      }
      return null;
    }
    catch (IOException | ExecutionException | InterruptedException e) {
      // todo: add notification
      return null;
    }
  }

  @Override
  public void apply(@NotNull PsiFile file, Collection<Result> annotationResult, @NotNull AnnotationHolder holder) {
    super.apply(file, annotationResult, holder);
    Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
    if (document == null) {
      return;<commit_msg>shellcheck: exclude inspection with . and source inclusion<commit_after>package com.intellij.bash.shellcheck;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.intellij.bash.psi.BashFile;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.ExternalAnnotator;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.io.StreamUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class BashShellcheckExternalAnnotator extends ExternalAnnotator<String, Collection<BashShellcheckExternalAnnotator.Result>> {
  class Result {
    int line;
    int endLine;
    int column;
    int endColumn;
    String level;
    String message;
    long code;
  }

  @Nullable
  @Override
  public String collectInformation(@NotNull PsiFile file) {
    return file instanceof BashFile ? file.getText() : null;
  }

  @Nullable
  @Override
  public String collectInformation(@NotNull PsiFile file, @NotNull Editor editor, boolean hasErrors) {
    return editor.getDocument().getText();
  }

  @Nullable
  @Override
  public Collection<BashShellcheckExternalAnnotator.Result> doAnnotate(String fileContent) {
    String shellcheckExecutable = Registry.stringValue("bash.shellcheck.path");
    if (StringUtil.isEmpty(shellcheckExecutable)) {
      return null;
    }
    try {
      GeneralCommandLine commandLine = new GeneralCommandLine()
          .withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE)
          .withExePath(shellcheckExecutable)
          .withParameters(
              "--color=never", "--format=json", "--severity=style", "--shell=bash", "--wiki-link-count=10",
              "-exclude=SC1091",
              "-");
      Process process = commandLine.createProcess();
      writeFileContentToStdin(process, fileContent, commandLine.getCharset());
      if (process.waitFor(10, TimeUnit.SECONDS)) {
        String output = StreamUtil.readText(process.getInputStream(), commandLine.getCharset());
        Type type = TypeToken.getParameterized(List.class, Result.class).getType();
        return new Gson().fromJson(output, type);
      }
      return null;
    }
    catch (IOException | ExecutionException | InterruptedException e) {
      // todo: add notification
      return null;
    }
  }

  @Override
  public void apply(@NotNull PsiFile file, Collection<Result> annotationResult, @NotNull AnnotationHolder holder) {
    super.apply(file, annotationResult, holder);
    Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
    if (document == null) {
      return;
{'fraction_non_alphanumeric': 0.10468110892289047, 'fraction_numerical': 0.0018179063778215423, 'mean_word_length': 4.447194719471947, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2147160', 'n_tokens_mistral': 1930, 'n_tokens_neox': 1881, 'n_words': 371}