{"record":{"id":"22b8e56fadc74ae1","repo":"neoclide/coc.nvim","slug":"range-cannot-span-multiple-lines","errorCode":null,"errorMessage":"`range` cannot span multiple lines","messagePattern":"`range` cannot span multiple lines","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/model/semanticTokensBuilder.ts","lineNumber":82,"sourceCode":"      if (typeof arg4 === 'undefined') {\n        arg4 = 0\n      }\n      // 1st overload\n      return this._pushEncoded(arg0, arg1, arg2, arg3, arg4)\n    }\n    if (Range.is(arg0) && typeof arg1 === 'string' && isStrArrayOrUndefined(arg2)) {\n      // 2nd overload\n      return this._push(arg0, arg1, arg2)\n    }\n    throw new Error('Illegal argument')\n  }\n\n  private _push(range: Range, tokenType: string, tokenModifiers?: string[]): void {\n    if (!this._hasLegend) {\n      throw new Error('Legend must be provided in constructor')\n    }\n    if (range.start.line !== range.end.line) {\n      throw new Error('`range` cannot span multiple lines')\n    }\n    if (!this._tokenTypeStrToInt.has(tokenType)) {\n      throw new Error('`tokenType` is not in the provided legend')\n    }\n    const line = range.start.line\n    const char = range.start.character\n    const length = range.end.character - range.start.character\n    const nTokenType = this._tokenTypeStrToInt.get(tokenType)!\n    let nTokenModifiers = 0\n    if (tokenModifiers) {\n      for (const tokenModifier of tokenModifiers) {\n        if (!this._tokenModifierStrToInt.has(tokenModifier)) {\n          throw new Error('`tokenModifier` is not in the provided legend')\n        }\n        const nTokenModifier = this._tokenModifierStrToInt.get(tokenModifier)!\n        nTokenModifiers |= (1 << nTokenModifier) >>> 0\n      }\n    }","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/neoclide/coc.nvim/blob/50e974d9692461a69147d5cab146a8d3e439abe4/src/model/semanticTokensBuilder.ts#L64-L100","documentation":"The semantic tokens protocol encodes tokens as single-line deltas, so every token range must start and end on the same line. _push validates range.start.line === range.end.line and throws when a range spans multiple lines.","triggerScenarios":"Calling push() with a Range whose start.line differs from end.line — e.g. tokenizing a multiline comment/string/region as one token instead of splitting it per line.","commonSituations":"Textmate/Tree-sitter grammars that yield multiline captures; highlighting block comments or heredocs; incorrectly computing a token range from a multi-line match.","solutions":["Split multi-line tokens into one token per line before pushing","Clamp the range to a single line: use range.start.line for both endpoints with the length limited to that line's content","Fix the token producer (grammar/matcher) so matches never cross lines"],"exampleFix":"// before\nbuilder.push(new Range(start, endMultiLine), 'comment')\n// after\nfor (let line = start.line; line <= end.line; line++) {\n  const lineEnd = line === end.line ? end : doc.lineAt(line).range.end\n  const lineStart = line === start.line ? start : new Position(line, 0)\n  if (!lineStart.isEqual(lineEnd)) builder.push(new Range(lineStart, lineEnd), 'comment')\n}","handlingStrategy":"validation","validationCode":"if (range.start.line !== range.end.line) {\n  // split into per-line ranges before pushing\n}","typeGuard":"function isSingleLine(r: Range): boolean {\n  return r.start.line === r.end.line\n}","tryCatchPattern":"try {\n  builder.push(range, tokenType)\n} catch (e) {\n  if (e.message.includes('cannot span multiple lines')) {\n    splitAndPushPerLine(range, tokenType, builder)\n  }\n}","preventionTips":["Normalize all ranges to single-line before pushing","Configure grammars/matchers so tokens never cross lines","Add an assertion in your token producer that start.line === end.line"],"tags":["semantic-tokens","range","protocol"],"backgroundTag":"multiline-token-range","analyzedSha":"50e974d9692461a69147d5cab146a8d3e439abe4","analyzedAt":"2026-08-31T11:17:23.966Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}