<commit_before># Get current region selected by mouse.
# returns {fst: FIRST_ELEMENT, lst: LAST_ELEMENT}
getSelectionRange = ->
  sel = window.getSelection()
  if !sel.isCollapsed && range = sel.getRangeAt(0)
     fst: range.startContainer.parentNode
     lst: range.endContainer.parentNode

# Get Current line numbers selected by mouse.
# returns {fst: FIRST_LINE_NUMBER, lst: LAST_LINE_NUMBER}
getSelectionLineRange = ->
  return undefined if !(range = getSelectionRange())
  fst = Number findNearestLinenum($(range.fst), -1)
  lst = Number findNearestLinenum($(range.lst),  1)
  return undefined if isNaN(fst) || isNaN(lst)
  return fst: fst, lst: lst

# Get the nearest data-linenum attribute from ELEMENT
# including itself. DIRECTION should be +1 or -1
#
# returns: value of data-linenum attribute
#
findNearestLinenum = (element, direction = -1) ->
  name = 'data-linenum'
  sel = "[#{name}]"
  ele = $(element)
  return ele.attr(name) if ele.is(sel)

  # Since ele does not have data-linenum,
  # inject a dummy data-linenum to ele, and
  # find its index among the other data-lineum holders.
  ele.attr(name, '??')
  index = $(sel).index(ele)

  buddy = $(sel)[index + direction]
  ele.removeAttr(name)
  return $(buddy).attr(name)

# Get the JSON format of the current page.
#
# If the current page is /mintes/1, it will return /minutes/1.json.
# This function is supposed to be used to get a JSON-style content of
# currently displayed minute.
#
# returns JSON encoded structure.
#
getCurrentPageAsJSON = () ->
  res = $.ajax
    url: window.location.pathname + ".json"
    async: false
    dataType: 'json'
  json = res.responseJSON
  return json

# Convert markdown TEXT to html and update element UPDATE_ELEMENT.
#
renderMarkdown = (text, update_element) ->
  $.post '/minutes/preview', {text: text}, (data) ->
    $(update_element).html(data)

#
# Remove Headings of list item ``+ (A) ``
#
removeHeader = (string) ->
  string.replace(/^ *[*+-] */, '')

# Return a regexp to match a string in the array STRINGS.
# This is a JavaScript verion of Emacs regexp-opt
# https://gist.github.com/kawanet/5540864
#
array_to_regexp = (strings, regexp_option) ->
  re = strings
    .sort (a, b) ->
      b.length - a.length
    .map (str) ->
      str.replace /\W/g, (match) ->
        "\\" + match
    .join("|")
  return new RegExp(re, regexp_option)

#
# Remove trailing ``-->(...)'' from STRING.
#
removeTrailer = (string) ->
  string.replace(/(．)? *--(>|&gt;)\(.*\) */, '')


# Get the minimum indent level of LINES.
# LINES has multiple lines separated by "\n".
#
# For example, if LINES has these three lines:
#
# |    first line
# |      second line
# |  third line
#
# this function returns the minimum indent level as 2.
# calculated from the third line.
#
getIndentLevel = (lines) ->
  indent = 9999
  for line in lines.split("\n")
    match = /^ */.exec(line)
    if match && match[0].length < indent
      indent = match[0].length
  return indent

# Decrease indents of LINES by LEVEL.
# LINES has multiple lines separated by "\n".
#
# For example, if you call this function with LEVEL is 2 and LINES has
# these three lines:
#
# |    first line
# |      second line
# |  third line
#
# this function returns:
#
# |  first line
# |    second line
# |third line
#
chopIndentLevel = (lines, level) ->
  return lines if level == 0
  space = new RegExp("^#{' '.repeat(level)}")
  result = ''
  for line in lines.split("\n")
    result += line.replace(space, '') + "\n"
  return result

# Decrease indents of LINES to zero.
#
chopIndent = (lines) ->
  chopIndentLevel(lines, getIndentLevel(lines))

# Find words listed in KEYWORDS by scanning the LINES.
#
# If LINES has a string enumerated in KEYWORDS the sting will
# be counted up as a candidate.
#
# For example,
#
# LINES:
# | nomlab/foo is great, but nomlab/bar is not.
# | blah blah...
#
# KEYWORDS:
#   ["nomlab/foo", "nomlab/bar", "nomlab/baz"]
#
# returns:
#   ["nomlab/foo", "nomlab/bar"]
#
# FIXME: this function breaks original KEYWORDS
#
findKeywords = (lines, keywords) ->
  regexp = array_to_regexp(keywords, "g")
  repos = lines.match(regexp)
  keywords = repos.filter((x, i, self) ->
    self.indexOf(x) == i
  )
  keywords

# Extract lines from line number FST to LST.
#
extractLines = (lines, fst, lst) -><commit_msg>Fix bug that new issue button does not work<commit_after># Get current region selected by mouse.
# returns {fst: FIRST_ELEMENT, lst: LAST_ELEMENT}
getSelectionRange = ->
  sel = window.getSelection()
  if !sel.isCollapsed && range = sel.getRangeAt(0)
     fst: range.startContainer.parentNode
     lst: range.endContainer.parentNode

# Get Current line numbers selected by mouse.
# returns {fst: FIRST_LINE_NUMBER, lst: LAST_LINE_NUMBER}
getSelectionLineRange = ->
  return undefined if !(range = getSelectionRange())
  fst = Number findNearestLinenum($(range.fst), -1)
  lst = Number findNearestLinenum($(range.lst),  1)
  return undefined if isNaN(fst) || isNaN(lst)
  return fst: fst, lst: lst

# Get the nearest data-linenum attribute from ELEMENT
# including itself. DIRECTION should be +1 or -1
#
# returns: value of data-linenum attribute
#
findNearestLinenum = (element, direction = -1) ->
  name = 'data-linenum'
  sel = "[#{name}]"
  ele = $(element)
  return ele.attr(name) if ele.is(sel)

  # Since ele does not have data-linenum,
  # inject a dummy data-linenum to ele, and
  # find its index among the other data-lineum holders.
  ele.attr(name, '??')
  index = $(sel).index(ele)

  buddy = $(sel)[index + direction]
  ele.removeAttr(name)
  return $(buddy).attr(name)

# Get the JSON format of the current page.
#
# If the current page is /mintes/1, it will return /minutes/1.json.
# This function is supposed to be used to get a JSON-style content of
# currently displayed minute.
#
# returns JSON encoded structure.
#
getCurrentPageAsJSON = () ->
  res = $.ajax
    url: window.location.pathname + ".json"
    async: false
    dataType: 'json'
  json = res.responseJSON
  return json

# Convert markdown TEXT to html and update element UPDATE_ELEMENT.
#
renderMarkdown = (text, update_element) ->
  $.post '/minutes/preview', {text: text}, (data) ->
    $(update_element).html(data)

#
# Remove Headings of list item ``+ (A) ``
#
removeHeader = (string) ->
  string.replace(/^ *[*+-] */, '')

# Return a regexp to match a string in the array STRINGS.
# This is a JavaScript verion of Emacs regexp-opt
# https://gist.github.com/kawanet/5540864
#
array_to_regexp = (strings, regexp_option) ->
  re = strings
    .sort (a, b) ->
      b.length - a.length
    .map (str) ->
      str.replace /\W/g, (match) ->
        "\\" + match
    .join("|")
  return new RegExp(re, regexp_option)

#
# Remove trailing ``-->(...)'' from STRING.
#
removeTrailer = (string) ->
  string.replace(/(．)? *--(>|&gt;)\(.*\) */, '')


# Get the minimum indent level of LINES.
# LINES has multiple lines separated by "\n".
#
# For example, if LINES has these three lines:
#
# |    first line
# |      second line
# |  third line
#
# this function returns the minimum indent level as 2.
# calculated from the third line.
#
getIndentLevel = (lines) ->
  indent = 9999
  for line in lines.split("\n")
    match = /^ */.exec(line)
    if match && match[0].length < indent
      indent = match[0].length
  return indent

# Decrease indents of LINES by LEVEL.
# LINES has multiple lines separated by "\n".
#
# For example, if you call this function with LEVEL is 2 and LINES has
# these three lines:
#
# |    first line
# |      second line
# |  third line
#
# this function returns:
#
# |  first line
# |    second line
# |third line
#
chopIndentLevel = (lines, level) ->
  return lines if level == 0
  space = new RegExp("^#{' '.repeat(level)}")
  result = ''
  for line in lines.split("\n")
    result += line.replace(space, '') + "\n"
  return result

# Decrease indents of LINES to zero.
#
chopIndent = (lines) ->
  chopIndentLevel(lines, getIndentLevel(lines))

# Find words listed in KEYWORDS by scanning the LINES.
#
# If LINES has a string enumerated in KEYWORDS the sting will
# be counted up as a candidate.
#
# For example,
#
# LINES:
# | nomlab/foo is great, but nomlab/bar is not.
# | blah blah...
#
# KEYWORDS:
#   ["nomlab/foo", "nomlab/bar", "nomlab/baz"]
#
# returns:
#   ["nomlab/foo", "nomlab/bar"]
#
# FIXME: this function breaks original KEYWORDS
#
findKeywords = (lines, keywords) ->
  regexp = array_to_regexp(keywords, "g")
  repos = lines.match(regexp) || []
  keywords = repos.filter((x, i, self) ->
    self.indexOf(x) == i
  )
  keywords

# Extract lines from line number FST to LST.
#
extractLines = (lines, fst, lst) ->
{'fraction_non_alphanumeric': 0.1397126969416126, 'fraction_numerical': 0.005560704355885079, 'mean_word_length': 3.366717248356095, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 43, 'https://': 2, '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': '6523736', 'n_tokens_mistral': 3005, 'n_tokens_neox': 2920, 'n_words': 940}