Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions src/FileIndexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import {
import { Input } from './Input.js'
import { Packages } from './Packages.js'
import { formatByteSizeAsHumanReadable } from './parseHumanByteSizeIntoNumber.js'
import { Position } from './Position.js'
import { Range } from './Range.js'
import { ScipSymbol } from './ScipSymbol.js'
import { SourceInfo } from './SourceInfo.js'
import * as ts_inline from './TypeScriptInternal.js'
Expand Down Expand Up @@ -93,8 +95,13 @@ export class FileIndexer {
}
this.pushOccurrence(
create(OccurrenceSchema, {
range: [0, 0, 0],
enclosingRange: this.sourceInfo.range(this.sourceFile),
typedRange: new Range(
new Position(0, 0),
new Position(0, 0)
).toTypedRange(),
typedEnclosingRange: this.sourceInfo
.range(this.sourceFile)
?.toTypedEnclosingRange(),
symbol: symbol.value,
symbolRoles: SymbolRole.Definition,
})
Expand Down Expand Up @@ -260,7 +267,7 @@ export class FileIndexer {
for (const declaration of declarations) {
let scipSymbol = this.scipSymbol(declaration)

let enclosingRange: number[] | undefined
let enclosingRange: Range | undefined

if (!isDefinitionNode || scipSymbol.isEmpty() || scipSymbol.isLocal()) {
// Skip enclosing ranges for these cases
Expand Down Expand Up @@ -311,8 +318,8 @@ export class FileIndexer {
}
this.pushOccurrence(
create(OccurrenceSchema, {
enclosingRange: enclosingRange,
range,
typedEnclosingRange: enclosingRange?.toTypedEnclosingRange(),
typedRange: range.toTypedRange(),
symbol: scipSymbol.value,
symbolRoles: role,

Expand All @@ -339,7 +346,7 @@ export class FileIndexer {
* // ^^^^^^^^ references `Props.property` and defines a local parameter `property`
* ```
*/
private handleObjectBindingPattern(node: ts.Node, range: number[]): void {
private handleObjectBindingPattern(node: ts.Node, range: Range): void {
const isObjectBindingPatternProperty =
ts.isIdentifier(node) &&
ts.isBindingElement(node.parent) &&
Expand All @@ -356,7 +363,7 @@ export class FileIndexer {
}
this.pushOccurrence(
create(OccurrenceSchema, {
range,
typedRange: range.toTypedRange(),
symbol: scipSymbol.value,
})
)
Expand All @@ -378,7 +385,7 @@ export class FileIndexer {
*/
private handleShorthandPropertyValue(
declaration: ts.Node,
range: number[]
range: Range
): void {
if (!ts.isShorthandPropertyAssignment(declaration)) {
return
Expand All @@ -398,7 +405,7 @@ export class FileIndexer {
}
this.pushOccurrence(
create(OccurrenceSchema, {
range,
typedRange: range.toTypedRange(),
symbol: scipSymbol.value,
})
)
Expand Down Expand Up @@ -468,7 +475,7 @@ export class FileIndexer {
}

private pushOccurrence(occurrence: Occurrence): void {
const key = `${occurrence.range.join(':')} ${occurrence.symbolRoles} ${occurrence.symbol}`
const key = `${Range.fromOccurrence(occurrence).toString()} ${occurrence.symbolRoles} ${occurrence.symbol}`
if (this.occurrenceKeys.has(key)) {
return
}
Expand Down
15 changes: 11 additions & 4 deletions src/ProjectIndexer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ import { test } from 'uvu'
import * as assert from 'uvu/assert'

import { GlobalCache, ProjectOptions } from './CommandLineOptions.js'
import { Position } from './Position.js'
import {
deduplicateOccurrences,
languageForFileName,
prettyMilliseconds,
ProjectIndexer,
} from './ProjectIndexer.js'
import { Range } from './Range.js'

function minute(x: number): number {
return x * 60 * 1000
Expand Down Expand Up @@ -101,13 +103,15 @@ test('only deduplicates documents after successful emission', () => {
})

test('Svelte occurrence deduplication preserves definition metadata', () => {
const range = new Range(new Position(1, 2), new Position(1, 3))
const enclosingRange = new Range(new Position(1, 0), new Position(5, 0))
const reference = create(OccurrenceSchema, {
range: [1, 2, 3],
typedRange: range.toTypedRange(),
symbol: 'local 0',
})
const definition = create(OccurrenceSchema, {
range: [1, 2, 3],
enclosingRange: [1, 0, 5, 0],
typedRange: range.toTypedRange(),
typedEnclosingRange: enclosingRange.toTypedEnclosingRange(),
symbol: 'local 0',
symbolRoles: SymbolRole.Definition,
diagnostics: [
Expand All @@ -122,7 +126,10 @@ test('Svelte occurrence deduplication preserves definition metadata', () => {

assert.is(document.occurrences.length, 1)
assert.is(document.occurrences[0], definition)
assert.equal(document.occurrences[0].enclosingRange, [1, 0, 5, 0])
assert.equal(
Range.fromTypedRange(document.occurrences[0].typedEnclosingRange),
enclosingRange
)
assert.is(
document.occurrences[0].diagnostics[0].message,
'definition diagnostic'
Expand Down
11 changes: 6 additions & 5 deletions src/ProjectIndexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { GlobalCache, ProjectOptions } from './CommandLineOptions.js'
import { FileIndexer } from './FileIndexer.js'
import { Input } from './Input.js'
import { Packages } from './Packages.js'
import { Range } from './Range.js'
import { ScipSymbol } from './ScipSymbol.js'
import { SourceInfo, typescriptSourceInfo } from './SourceInfo.js'
import {
Expand Down Expand Up @@ -299,7 +300,7 @@ export function languageForFileName(fileName: string): string | undefined {
export function deduplicateOccurrences(document: Document): void {
const occurrences = new Map<string, Occurrence>()
for (const occurrence of document.occurrences) {
const key = `${occurrence.range.join(':')} ${occurrence.symbol}`
const key = `${Range.fromOccurrence(occurrence).toString()} ${occurrence.symbol}`
const existing = occurrences.get(key)
if (existing) {
const symbolRoles = existing.symbolRoles | occurrence.symbolRoles
Expand All @@ -312,17 +313,17 @@ export function deduplicateOccurrences(document: Document): void {
// the enclosing range and diagnostics associated with the declaration.
if (occurrenceIsDefinition && !existingIsDefinition) {
occurrence.symbolRoles = symbolRoles
if (occurrence.enclosingRange.length === 0) {
occurrence.enclosingRange = existing.enclosingRange
if (occurrence.typedEnclosingRange.case === undefined) {
occurrence.typedEnclosingRange = existing.typedEnclosingRange
}
if (occurrence.diagnostics.length === 0) {
occurrence.diagnostics = existing.diagnostics
}
occurrences.set(key, occurrence)
} else {
existing.symbolRoles = symbolRoles
if (existing.enclosingRange.length === 0) {
existing.enclosingRange = occurrence.enclosingRange
if (existing.typedEnclosingRange.case === undefined) {
existing.typedEnclosingRange = occurrence.typedEnclosingRange
}
if (existing.diagnostics.length === 0) {
existing.diagnostics = occurrence.diagnostics
Expand Down
72 changes: 57 additions & 15 deletions src/Range.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
import { create } from '@bufbuild/protobuf'
import {
type MultiLineRange,
MultiLineRangeSchema,
type Occurrence,
type SingleLineRange,
SingleLineRangeSchema,
} from '@scip-code/scip'
import * as ts from 'typescript'

import { Position } from './Position.js'
Expand All @@ -14,25 +22,59 @@ export class Range {
}
return this.end.compare(other.end)
}
public toPackedRange(): number[] {
if (this.isSingleLine()) {
return [this.start.line, this.start.character, this.end.character]
public toTypedRange(): Occurrence['typedRange'] {
return this.isSingleLine()
? { case: 'singleLineRange', value: this.toSingleLineRange() }
: { case: 'multiLineRange', value: this.toMultiLineRange() }
}
public toTypedEnclosingRange(): Occurrence['typedEnclosingRange'] {
return this.isSingleLine()
? { case: 'singleLineEnclosingRange', value: this.toSingleLineRange() }
: { case: 'multiLineEnclosingRange', value: this.toMultiLineRange() }
}
private toSingleLineRange(): SingleLineRange {
return create(SingleLineRangeSchema, {
line: this.start.line,
startCharacter: this.start.character,
endCharacter: this.end.character,
})
}
private toMultiLineRange(): MultiLineRange {
return create(MultiLineRangeSchema, {
startLine: this.start.line,
startCharacter: this.start.character,
endLine: this.end.line,
endCharacter: this.end.character,
})
}
public static fromTypedRange(
range: Occurrence['typedRange'] | Occurrence['typedEnclosingRange']
): Range | undefined {
const value = range.value
if (value === undefined) {
return undefined
}
if (value.$typeName === 'scip.SingleLineRange') {
return new Range(
new Position(value.line, value.startCharacter),
new Position(value.line, value.endCharacter)
)
}
return [
this.start.line,
this.start.character,
this.end.line,
this.end.character,
]
}
public static fromPackedRange(range: number[]): Range {
const endLine = range.length === 3 ? range[0] : range[2]
const endCharacter = range.length === 3 ? range[2] : range[3]
return new Range(
new Position(range[0], range[1]),
new Position(endLine, endCharacter)
new Position(value.startLine, value.startCharacter),
new Position(value.endLine, value.endCharacter)
)
}
public static fromOccurrence(occurrence: Occurrence): Range {
const range = Range.fromTypedRange(occurrence.typedRange)
if (!range) {
throw new Error(`occurrence of '${occurrence.symbol}' has no range`)
}
return range
}
public toString(): string {
return `${this.start.line}:${this.start.character}:${this.end.line}:${this.end.character}`
}

public static fromNode(node: ts.Node): Range {
const sourceFile = node.getSourceFile()
Expand Down
20 changes: 9 additions & 11 deletions src/SnapshotTesting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,9 @@ export function formatSnapshot(
symbolsWithDefinitions.add(occurrence.symbol)
}

if (formatOptions.showRanges && occurrence.enclosingRange.length > 0) {
enclosingRanges.push({
range: Range.fromPackedRange(occurrence.enclosingRange),
symbol: occurrence.symbol,
})
const enclosingRange = Range.fromTypedRange(occurrence.typedEnclosingRange)
if (formatOptions.showRanges && enclosingRange) {
enclosingRanges.push({ range: enclosingRange, symbol: occurrence.symbol })
}
}

Expand Down Expand Up @@ -244,7 +242,7 @@ export function formatSnapshot(
// This is the only case where we would need to do this.
if (occurrenceIndex === 0) {
const occurrence = doc.occurrences[occurrenceIndex]
const range = Range.fromPackedRange(occurrence.range)
const range = Range.fromOccurrence(occurrence)

// This is essentially a "file-based" item.
// This guarantees that this sits above everything else in the file.
Expand Down Expand Up @@ -273,7 +271,8 @@ export function formatSnapshot(
out.push('\n')
while (
occurrenceIndex < doc.occurrences.length &&
doc.occurrences[occurrenceIndex].range[0] === lineNumber
Range.fromOccurrence(doc.occurrences[occurrenceIndex]).start.line ===
lineNumber
) {
const occurrence = doc.occurrences[occurrenceIndex]
occurrenceIndex++
Expand All @@ -282,12 +281,11 @@ export function formatSnapshot(
continue
}

if (occurrence.range.length > 3) {
const range = Range.fromOccurrence(occurrence)
if (!range.isSingleLine()) {
throw new Error('not yet implemented, multi-line ranges')
}

const range = Range.fromPackedRange(occurrence.range)

out.push(commentSyntax)
const isStartOfLine = range.start.character === 0
if (!isStartOfLine && range.start.character > 2) {
Expand Down Expand Up @@ -336,7 +334,7 @@ export function formatSnapshot(
}

function occurrencesByLine(a: Occurrence, b: Occurrence): number {
return Range.fromPackedRange(a.range).compare(Range.fromPackedRange(b.range))
return Range.fromOccurrence(a).compare(Range.fromOccurrence(b))
}

function enclosingRangesByLine(
Expand Down
4 changes: 2 additions & 2 deletions src/SourceInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export interface SourceInfo {
readonly language?: string
readonly isSvelte: boolean

range(node: ts.Node): number[] | undefined
range(node: ts.Node): Range | undefined
/**
* Overrides symbol assignment for declarations in this source file.
* Return undefined to use normal TypeScript symbol assignment.
Expand All @@ -32,7 +32,7 @@ export function typescriptSourceInfo(sourceFile: ts.SourceFile): SourceInfo {
fileName: sourceFile.fileName,
originalText: sourceFile.getText(),
isSvelte: false,
range: node => Range.fromNode(node).toPackedRange(),
range: node => Range.fromNode(node),
declarationSymbol: () => undefined,
releaseCaches: () => {},
}
Expand Down
Loading
Loading