Repository: atom/atom
File: src/tree-sitter-language-mode.js (JavaScript)

Code before:
  /*
  Section - Backward compatibility shims
  */

  tokenizedLineForRow (row) {
    return new TokenizedLine({
      openScopes: [],
      text: this.buffer.lineForRow(row),
      tags: [],
      ruleStack: [],
      lineEnding: this.buffer.lineEndingForRow(row),
      tokenIterator: null,
      grammar: this.grammar
    })
  }

  syntaxTreeScopeDescriptorForPosition (point) {
    const nodes = []
    point = Point.fromObject(point)

    if (point.column > 0 && point.column === this.buffer.lineLengthForRow(point.row)) {
      point.column--
    }

    this._forEachTreeWithRange(new Range(point, point), tree => {
      let node = tree.rootNode.descendantForPosition(point)
      while (node) {
        nodes.push(node)
        node = node.parent
      }
    })

    // The nodes are mostly already sorted from smallest to largest,
    // but for files with multiple syntax trees (e.g. ERB), each tree's
    // nodes are separate. Sort the nodes from largest to smallest.
    nodes.reverse()
    nodes.sort((a, b) =>
      a.startIndex - b.startIndex || b.endIndex - a.endIndex
    )

    const nodeTypes = nodes.map(node => node.type)
    nodeTypes.unshift(this.grammar.scopeName)
    return new ScopeDescriptor({scopes: nodeTypes})
  }

  scopeDescriptorForPosition (point) {
    point = Point.fromObject(point)

    if (point.column > 0 && point.column === this.buffer.lineLengthForRow(point.row)) {
      point.column--
    }

Review comment (suggestion):
Since we're mutating this point, we should pass `true` for [the `copy` parameter to `Point.fromObject`](https://github.com/atom/text-buffer/blob/d10f7595e600c775bd8bf505d11d5ba7a02a1290/src/point.coffee#L36-L45) above, to make sure we're not mutating a point that might get reused after the call to `scopeDescriptorForPosition`.

Code after:
  /*
  Section - Backward compatibility shims
  */

  tokenizedLineForRow (row) {
    return new TokenizedLine({
      openScopes: [],
      text: this.buffer.lineForRow(row),
      tags: [],
      ruleStack: [],
      lineEnding: this.buffer.lineEndingForRow(row),
      tokenIterator: null,
      grammar: this.grammar
    })
  }

  syntaxTreeScopeDescriptorForPosition (point) {
    const nodes = []
    point = this.buffer.clipPosition(Point.fromObject(point))

    // If the position is the end of a line, get node of left character instead of newline
    // This is to match TextMate behaviour, see https://github.com/atom/atom/issues/18463
    if (point.column > 0 && point.column === this.buffer.lineLengthForRow(point.row)) {
      point = point.copy()
      point.column--
    }

    this._forEachTreeWithRange(new Range(point, point), tree => {
      let node = tree.rootNode.descendantForPosition(point)
      while (node) {
        nodes.push(node)
        node = node.parent
      }
    })

    // The nodes are mostly already sorted from smallest to largest,
    // but for files with multiple syntax trees (e.g. ERB), each tree's
    // nodes are separate. Sort the nodes from largest to smallest.
    nodes.reverse()
    nodes.sort((a, b) =>
      a.startIndex - b.startIndex || b.endIndex - a.endIndex
    )

    const nodeTypes = nodes.map(node => node.type)
    nodeTypes.unshift(this.grammar.scopeName)
    return new ScopeDescriptor({scopes: nodeTypes})
  }

  scopeDescriptorForPosition (point) {
    point = this.buffer.clipPosition(Point.fromObject(point))