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
47 changes: 15 additions & 32 deletions lib/database/code/relations_table.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'package:ariadne/database/code/code_database.dart';
import 'package:ariadne/shared/symbol_relation.dart';
import 'package:drift/drift.dart';
import 'package:lsp_server/lsp_server.dart';

class RelationsTable {
const RelationsTable(CodeDatabase db) : _db = db;
Expand All @@ -21,9 +20,15 @@ class RelationsTable {
Future<List<SymbolRelation>> getSymbolRelations({
required int symbolId,
}) async {
final target = await (_db.select(
_db.symbols,
)..where((s) => s.id.equals(symbolId))).getSingle();
late final Symbol target;

try {
target = await (_db.select(
_db.symbols,
)..where((s) => s.id.equals(symbolId))).getSingle();
} catch (e) {
return [];
}

const query = '''
WITH children AS (
Expand Down Expand Up @@ -66,38 +71,16 @@ class RelationsTable {
}).toList();
}

/// Returns enclosing class of the [symbol].
Future<Symbol> getOwningClass(Symbol symbol) async {
final classKind = SymbolKind.Class.toJson() as int;
if (symbol.kind == classKind) return symbol;

final query = _db.select(_db.symbols)
..where(
(s) =>
s.uri.equals(symbol.uri) &
s.kind.equals(classKind) &
s.rangeStartLine.isSmallerOrEqualValue(symbol.rangeStartLine) &
s.rangeEndLine.isBiggerOrEqualValue(symbol.rangeEndLine),
)
..orderBy([
(s) =>
OrderingTerm(expression: s.rangeStartLine, mode: OrderingMode.desc),
])
..limit(1);

return await query.getSingleOrNull() ?? symbol;
}

/// Returns enclosing enum of the [symbol].
Future<Symbol> getOwningEnum(Symbol symbol) async {
final enumKind = SymbolKind.Enum.toJson() as int;
if (symbol.kind == enumKind) return symbol;

/// Returns enclosing symbol with the [kind] of the [symbol].
Future<Symbol> getOwning({
required Symbol symbol,
required int owningKind,
}) async {
final query = _db.select(_db.symbols)
..where(
(s) =>
s.uri.equals(symbol.uri) &
s.kind.equals(enumKind) &
s.kind.equals(owningKind) &
s.rangeStartLine.isSmallerOrEqualValue(symbol.rangeStartLine) &
s.rangeEndLine.isBiggerOrEqualValue(symbol.rangeEndLine),
)
Expand Down
10 changes: 8 additions & 2 deletions lib/viewer/bloc/viewer_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ class ViewerBloc extends ReplayBloc<ViewerEvent, ViewerState> {

final effectiveSymbol =
event.focusedSymbol.kind == SymbolKind.Constructor.toJson()
? await _codeDB.projectRelations.getOwningClass(event.focusedSymbol)
? await _codeDB.projectRelations.getOwning(
symbol: event.focusedSymbol,
owningKind: SymbolKind.Class.toJson() as int,
)
: event.focusedSymbol;

final relations = await _codeDB.projectRelations.getSymbolRelations(
Expand Down Expand Up @@ -152,7 +155,10 @@ class ViewerBloc extends ReplayBloc<ViewerEvent, ViewerState> {
];

final snippetSymbol = effectiveSymbol.kind == SymbolKind.EnumMember.toJson()
? await _codeDB.projectRelations.getOwningEnum(effectiveSymbol)
? await _codeDB.projectRelations.getOwning(
symbol: effectiveSymbol,
owningKind: SymbolKind.Enum.toJson() as int,
)
: effectiveSymbol;

final snippet = await _analyzer.getHighlightedSnippet(
Expand Down
186 changes: 186 additions & 0 deletions test/database/code/relations_table_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import 'package:ariadne/database/code/code_database.dart';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:lsp_server/lsp_server.dart';

void main() {
late CodeDatabase db;

setUp(() {
db = CodeDatabase.forTesting(NativeDatabase.memory());
});

tearDown(() => db.close());

group('RelationsTable', () {
test('creates and returns symbol relations', () async {
var relations = await db.relations.select().get();
expect(relations, isEmpty);

final symbol1 = SymbolsCompanion.insert(
name: 'symbol1',
kind: 5,
uri: 'some/uri.dart',
rangeStartLine: 0,
rangeStartColumn: 0,
rangeEndLine: 0,
rangeEndColumn: 5,
selectionStartLine: 0,
selectionStartColumn: 0,
selectionEndLine: 0,
selectionEndColumn: 5,
);
final symbol2 = SymbolsCompanion.insert(
name: 'symbol2',
kind: 5,
uri: 'some/uri.dart',
rangeStartLine: 1,
rangeStartColumn: 0,
rangeEndLine: 1,
rangeEndColumn: 5,
selectionStartLine: 1,
selectionStartColumn: 0,
selectionEndLine: 1,
selectionEndColumn: 5,
);
await db.projectSymbols.insertBatch(symbols: [symbol1, symbol2]);

final relation1 = RelationsCompanion.insert(
fromId: 1,
toId: 2,
relationType: 'calls',
line: 0,
column: 0,
);
final relation2 = RelationsCompanion.insert(
fromId: 2,
toId: 1,
relationType: 'inherits',
line: 1,
column: 0,
);

await db.projectRelations.insertBatch(relations: [relation1, relation2]);
relations = await db.relations.select().get();
expect(relations.length, equals(2));

final symbolRelations = await db.projectRelations.getSymbolRelations(
symbolId: 1,
);

expect(symbolRelations.length, equals(2));
expect(symbolRelations.first.type, equals('calls'));
expect(symbolRelations.first.isIncoming, isFalse);
expect(symbolRelations.first.symbol.id, equals(2));
expect(symbolRelations.last.type, equals('inherits'));
expect(symbolRelations.last.isIncoming, isTrue);
expect(symbolRelations.last.symbol.id, equals(2));
});

test('returns empty list if no relations found', () async {
final symbolRelations = await db.projectRelations.getSymbolRelations(
symbolId: 1,
);

expect(symbolRelations, isEmpty);
});

test('returns owning class of a symbol', () async {
final symbol1 = SymbolsCompanion.insert(
name: 'symbol1',
kind: SymbolKind.Class.toJson() as int,
uri: 'some/uri.dart',
rangeStartLine: 0,
rangeStartColumn: 0,
rangeEndLine: 3,
rangeEndColumn: 5,
selectionStartLine: 0,
selectionStartColumn: 0,
selectionEndLine: 0,
selectionEndColumn: 5,
);
final symbol2 = SymbolsCompanion.insert(
name: 'symbol2',
kind: SymbolKind.Constructor.toJson() as int,
uri: 'some/uri.dart',
rangeStartLine: 1,
rangeStartColumn: 0,
rangeEndLine: 1,
rangeEndColumn: 5,
selectionStartLine: 1,
selectionStartColumn: 0,
selectionEndLine: 1,
selectionEndColumn: 5,
);
await db.projectSymbols.insertBatch(symbols: [symbol1, symbol2]);
final symbols = await db.projectSymbols.all();

final owningSymbol = await db.projectRelations.getOwning(
symbol: symbols.last,
owningKind: SymbolKind.Class.toJson() as int,
);
expect(owningSymbol, symbols.first);
});

test('returns owning enum of a symbol', () async {
final symbol1 = SymbolsCompanion.insert(
name: 'symbol1',
kind: SymbolKind.Enum.toJson() as int,
uri: 'some/uri.dart',
rangeStartLine: 0,
rangeStartColumn: 0,
rangeEndLine: 3,
rangeEndColumn: 5,
selectionStartLine: 0,
selectionStartColumn: 0,
selectionEndLine: 0,
selectionEndColumn: 5,
);
final symbol2 = SymbolsCompanion.insert(
name: 'symbol2',
kind: SymbolKind.EnumMember.toJson() as int,
uri: 'some/uri.dart',
rangeStartLine: 1,
rangeStartColumn: 0,
rangeEndLine: 1,
rangeEndColumn: 5,
selectionStartLine: 1,
selectionStartColumn: 0,
selectionEndLine: 1,
selectionEndColumn: 5,
);
await db.projectSymbols.insertBatch(symbols: [symbol1, symbol2]);
final symbols = await db.projectSymbols.all();

final owningSymbol = await db.projectRelations.getOwning(
symbol: symbols.last,
owningKind: SymbolKind.Enum.toJson() as int,
);
expect(owningSymbol, symbols.first);
});

test('returns passed symbol when no owning symbol found', () async {
const symbol = Symbol(
id: 1,
name: 'name',
kind: 5,
uri: 'some/uri.dart',
rangeStartLine: 0,
rangeStartColumn: 0,
rangeEndLine: 0,
rangeEndColumn: 0,
selectionStartLine: 0,
selectionStartColumn: 0,
selectionEndLine: 0,
selectionEndColumn: 0,
);

final owningSymbol = await db.projectRelations.getOwning(
symbol: symbol,
owningKind: SymbolKind.Class.toJson() as int,
);
expect(owningSymbol, equals(symbol));
});
});
}