#!/usr/bin/env bash

# USAGE: To run locally:
#   bash .github/ci/lint.sh origin/master HEAD

set -e

REF_BRANCH="$1"
PR_BRANCH="$2"

_fail() {
  echo
  printf "lint.sh: %s\n" "$@"
  exit 1
}

_check_generated_docs() {
  if ! git diff "${REF_BRANCH}"..."${PR_BRANCH}" --exit-code -- doc/configs.md doc/configs.txt; then
    _fail '`configs.md` or `configs.txt` will be regenerated by the docgen CI process. Edit the Lua source file instead.' \
      'For details on generating documentation, see: https://github.com/neovim/nvim-lspconfig/blob/master/CONTRIBUTING.md#generating-docs'
  fi
}

# Enforce buffer-local commands.
_check_cmd_buflocal() {
  if git grep -P 'nvim_create_user_command' -- 'lsp/*.lua' ; then
    _fail 'Define commands with nvim_buf_create_user_command (buffer-local), not nvim_create_user_command'
  fi
}

# Check that "@brief" docstring is the first line of each "lsp/*.lua" config.
_check_brief_placement() {
  if find ./lsp -type f -name "*.lua" -exec awk 'NR==1 && !/brief/{print FILENAME}' {} \; | grep --color=never '.' ; then
    _fail '`@brief` docstring must be at the top of the config source file'
  fi
}

# Returned object should have `---@type vim.lsp.Config` annotation.
# TODO: use luals/emmylua type checking in CI: https://github.com/neovim/neovim/pull/33344
_check_type() {
  if git grep --files-without-match '\-\-\-\@type vim\.lsp\.Config' -- 'lsp/*.lua' ; then
    _fail 'Missing `---@type vim.lsp.Config` annotation.'
  fi
}

# Enforce "Lsp" prefix on all user commands.
_check_lsp_cmd_prefix() {
  local exclude='tinymist'
  if git grep -P 'nvim_buf_create_user_command' -- 'lsp/*.lua' | grep -v "$exclude" | grep --color -v Lsp ; then
    _fail 'Command names must start with "Lsp" prefix'
  fi
}

# Enforce client:exec_cmd().
_check_exec_cmd() {
  local exclude='eslint\|pyright\|basedpyright'
  if git grep -P 'workspace.executeCommand' -- 'lsp/*.lua' | grep -v "$exclude"  ; then
    _fail 'Use client:exec_cmd() instead of calling request("workspace/executeCommand") directly. Example: lsp/pyright.lua'
  fi
}

# Disallow util functions in Nvim 0.11+ (lsp/) configs.
_check_deprecated_in_nvim_0_11() {
  if git grep -P 'is_descendant' -- 'lsp/*.lua' ; then
    _fail 'Use vim.fs.relpath() instead of util.path.is_descendant()'
  fi
  if git grep -P 'search_ancestors' -- 'lsp/*.lua' ; then
    _fail 'Use vim.iter(vim.fs.parents(fname)):find(…) instead of util.path.search_ancestors(fname,…)'
  fi
  if git grep -P 'validate_bufnr' -- 'lsp/*.lua' ; then
    _fail 'Do not use util.validate_bufnr(). Nvim stdlib already treats bufnr=0 as "current buffer".'
  fi
  if git grep -P 'single_file_support' -- 'lsp/*.lua' ; then
    _fail 'vim.lsp.config assumes "single-file support" by default. If the LS does not support that, set workspace_required=true.'
  fi
}

_check_deprecated_utils() {
  # checks for added lines that contain search pattern and prints them
  SEARCH_PATTERN='(path\.dirname|fn\.cwd)'

  if git diff --pickaxe-all -U0 -G "${SEARCH_PATTERN}" "${REF_BRANCH}" "${PR_BRANCH}" -- '*.lua' | grep -Ev '(configs|utils)\.lua$' | grep -E "^\+.*${SEARCH_PATTERN}" ; then
    _fail 'Do not use vim.fn.cwd or util.path.dirname in root_dir.' \
      "See: https://github.com/neovim/nvim-lspconfig/blob/master/CONTRIBUTING.md#new-config"
  fi

  SEARCH_PATTERN='(util\.path\.dirname|util\.path\.sanitize|util\.path\.exists|util\.path\.is_file|util\.path\.is_dir|util\.path\.join|util\.path\.iterate_parents|util\.find_mercurial_ancestor|util\.find_node_modules_ancestor|util\.find_package_json_ancestor|util\.find_git_ancestor|util\.get_lsp_clients|util\.get_active_client_by_name)'

  if git diff --pickaxe-all -U0 -G "${SEARCH_PATTERN}" "${REF_BRANCH}" "${PR_BRANCH}" -- '*.lua' | grep -Ev '\.lua$' | grep -E "^\+.*${SEARCH_PATTERN}" ; then
    _fail 'Do not use deprecated util functions: '"${SEARCH_PATTERN}"
  fi
}

_check_legacy_configs() {
  if ! git diff "${REF_BRANCH}"..."${PR_BRANCH}" --exit-code -- lua/lspconfig/configs/ ; then
    _fail 'Configs in `lua/lspconfig/configs/*` are deprecated. Add or update configs in `lsp/*` instead.'
  fi
}

_check_generated_docs
_check_cmd_buflocal
_check_brief_placement
_check_type
_check_lsp_cmd_prefix
_check_exec_cmd
_check_deprecated_in_nvim_0_11
_check_deprecated_utils
_check_legacy_configs
