diff --git a/lib/database/code/relations_table.dart b/lib/database/code/relations_table.dart index 58cbaae..634b975 100644 --- a/lib/database/code/relations_table.dart +++ b/lib/database/code/relations_table.dart @@ -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; @@ -21,9 +20,15 @@ class RelationsTable { Future> 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 ( @@ -66,38 +71,16 @@ class RelationsTable { }).toList(); } - /// Returns enclosing class of the [symbol]. - Future 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 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 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), ) diff --git a/lib/viewer/bloc/viewer_bloc.dart b/lib/viewer/bloc/viewer_bloc.dart index dd8adc4..5e8c048 100644 --- a/lib/viewer/bloc/viewer_bloc.dart +++ b/lib/viewer/bloc/viewer_bloc.dart @@ -116,7 +116,10 @@ class ViewerBloc extends ReplayBloc { 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( @@ -152,7 +155,10 @@ class ViewerBloc extends ReplayBloc { ]; 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( diff --git a/test/database/code/relations_table_test.dart b/test/database/code/relations_table_test.dart new file mode 100644 index 0000000..2d38d43 --- /dev/null +++ b/test/database/code/relations_table_test.dart @@ -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)); + }); + }); +}