Draft for drift file discovery

This commit is contained in:
Simon Binder 2022-08-22 22:05:39 +02:00
parent 5e4a65eace
commit f82ea5936d
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
12 changed files with 191 additions and 164 deletions

View File

@ -1,79 +0,0 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:collection/collection.dart';
import 'package:sqlparser/sqlparser.dart';
import 'package:path/path.dart' show url;
import 'results/element.dart';
class DriftAnalysisCache {
final Map<Uri, FileState> knownFiles = {};
final Map<DriftElementId, PendingDriftElement> knownElements = {};
FileState notifyFileChanged(Uri uri) {
// todo: Mark references for files that import this one as stale.
// todo: Mark elements that reference an element in this file as stale.
return knownFiles.putIfAbsent(uri, () => FileState(uri))
..errors.clear()
..kind = null
..parsedDartFile = null
..parsedDriftFile = null;
}
void notifyFileDeleted(Uri uri) {}
}
/// A [DriftElement] that is known to exist, but perhaps hasn't fully been
/// resolved yet.
class PendingDriftElement {
final DriftElementId ownId;
final List<Dependency> dependencies;
bool cleanState = false;
bool isOnCircularReferencePath = false;
PendingDriftElement(this.ownId, List<Dependency> dependencies)
: dependencies = UnmodifiableListView(dependencies);
}
abstract class Dependency {}
class ReferencesElement extends Dependency {
final DriftElementId referencedElement;
ReferencesElement(this.referencedElement);
}
class ReferencesUnknownElement extends Dependency {
final String name;
ReferencesUnknownElement(this.name);
}
enum FileKind {
driftFile,
dartLibrary,
/// A Dart part file, a file with an unknown extension or a file that doesn't
/// exist.
invalid,
}
class FileState {
final Uri uri;
final List<DriftElementId> locallyDefinedElements = [];
final List<Uri> directImports = [];
final List<AnalysisError> errors = [];
bool contentsFresh = false;
bool referencesFresh = false;
FileKind? kind;
DriftFile? parsedDriftFile;
LibraryElement? parsedDartFile;
FileState(this.uri);
String get extension => url.extension(uri.path);
}

View File

@ -1,70 +0,0 @@
import 'package:sqlparser/sqlparser.dart';
import '../analyzer/options.dart';
import 'backend.dart';
import 'cache.dart';
class DriftAnalysisDriver {
final DriftBackend backend;
final DriftAnalysisCache cache = DriftAnalysisCache();
final DriftOptions options;
DriftAnalysisDriver(this.backend, this.options);
SqlEngine _newSqlEngine() {
return SqlEngine(
EngineOptions(
useDriftExtensions: true,
enabledExtensions: [
// todo: Map from options
],
version: options.sqliteVersion,
),
);
}
/// Identify all [PendingDriftElement]s in a file.
Future<void> _discover(FileState state) async {
final extension = state.extension;
switch (extension) {
case '.dart':
try {
state
..parsedDartFile = await backend.readDart(state.uri)
..kind = FileKind.dartLibrary;
} catch (e, s) {
backend.log
.fine('Could not read Dart library from ${state.uri}', e, s);
state.kind = FileKind.invalid;
}
break;
case '.drift':
case '.moor':
final engine = _newSqlEngine();
String contents;
try {
contents = await backend.readAsString(state.uri);
state.kind = FileKind.driftFile;
} catch (e, s) {
backend.log.fine('Could not read drift sources ${state.uri}', e, s);
state.kind = FileKind.invalid;
break;
}
// todo: Handle parse errors
final parsed = engine.parseDriftFile(contents);
state.parsedDriftFile = parsed.rootNode as DriftFile;
break;
}
}
Future<void> fullyAnalyze(Uri uri) async {
var known = cache.knownFiles[uri];
if (known == null || !known.contentsFresh) {
await _discover(cache.notifyFileChanged(uri));
}
}
}

View File

@ -0,0 +1,20 @@
import '../results/element.dart';
import 'state.dart';
class DriftAnalysisCache {
final Map<Uri, FileState> knownFiles = {};
final Map<DriftElementId, DiscoveredElement> knownElements = {};
FileState notifyFileChanged(Uri uri) {
// todo: Mark references for files that import this one as stale.
// todo: Mark elements that reference an element in this file as stale.
return knownFiles.putIfAbsent(uri, () => FileState(uri))
..errorsDuringDiscovery.clear()
..errorsDuringAnalysis.clear()
..results = null
..discovery = null;
}
void notifyFileDeleted(Uri uri) {}
}

View File

@ -0,0 +1,34 @@
import 'package:sqlparser/sqlparser.dart';
import '../../analyzer/options.dart';
import '../backend.dart';
import '../resolver/discover.dart';
import 'cache.dart';
class DriftAnalysisDriver {
final DriftBackend backend;
final DriftAnalysisCache cache = DriftAnalysisCache();
final DriftOptions options;
DriftAnalysisDriver(this.backend, this.options);
SqlEngine newSqlEngine() {
return SqlEngine(
EngineOptions(
useDriftExtensions: true,
enabledExtensions: [
// todo: Map from options
],
version: options.sqliteVersion,
),
);
}
Future<void> fullyAnalyze(Uri uri) async {
var known = cache.knownFiles[uri];
if (known == null || known.discovery == null) {
await DiscoverStep(this, cache.notifyFileChanged(uri)).discover();
}
}
}

View File

@ -0,0 +1,53 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:path/path.dart' show url;
import 'package:sqlparser/sqlparser.dart' hide AnalysisError;
import '../results/element.dart';
import '../results/file.dart';
import 'error.dart';
class FileState {
final Uri ownUri;
DiscoveredFileState? discovery;
AnalyzedFile? results;
final List<AnalysisError> errorsDuringDiscovery = [];
final List<AnalysisError> errorsDuringAnalysis = [];
FileState(this.ownUri);
String get extension => url.extension(ownUri.path);
}
abstract class DiscoveredFileState {
final List<DiscoveredElement> locallyDefinedElements;
DiscoveredFileState(this.locallyDefinedElements);
}
class DiscoveredDriftFile extends DiscoveredFileState {
final DriftFile ast;
DiscoveredDriftFile(this.ast, super.locallyDefinedElements);
}
class DiscoveredDartLibrary extends DiscoveredFileState {
final LibraryElement library;
DiscoveredDartLibrary(this.library, super.locallyDefinedElements);
}
class NotADartLibrary extends DiscoveredFileState {
NotADartLibrary() : super(const []);
}
class NoSuchFile extends DiscoveredFileState {
NoSuchFile() : super(const []);
}
abstract class DiscoveredElement {
final DriftElementId ownId;
DiscoveredElement(this.ownId);
}

View File

@ -0,0 +1 @@
class DependencyCollector {}

View File

@ -0,0 +1,61 @@
import 'package:sqlparser/sqlparser.dart';
import '../driver/driver.dart';
import '../driver/state.dart';
import '../results/element.dart';
import 'intermediate_state.dart';
class DiscoverStep {
final DriftAnalysisDriver _driver;
final FileState _file;
DiscoverStep(this._driver, this._file);
DriftElementId _id(String name) => DriftElementId(_file.ownUri, name);
Future<void> discover() async {
final extension = _file.extension;
final pendingElements = <DiscoveredElement>[];
switch (extension) {
case '.dart':
try {
final library = await _driver.backend.readDart(_file.ownUri);
_file.discovery = DiscoveredDartLibrary(library, []);
} catch (e, s) {
_driver.backend.log
.fine('Could not read Dart library from ${_file.ownUri}', e, s);
_file.discovery = NotADartLibrary();
}
break;
case '.drift':
case '.moor':
final engine = _driver.newSqlEngine();
String contents;
try {
contents = await _driver.backend.readAsString(_file.ownUri);
} catch (e, s) {
_driver.backend.log
.fine('Could not read drift sources ${_file.ownUri}', e, s);
_file.discovery = NoSuchFile();
break;
}
// todo: Handle parse errors
final parsed = engine.parseDriftFile(contents);
final ast = parsed.rootNode as DriftFile;
for (final node in ast.childNodes) {
if (node is TableInducingStatement) {
pendingElements
.add(DiscoveredDriftTable(_id(node.createdName), node));
} else if (node is CreateViewStatement) {
pendingElements
.add(DiscoveredDriftView(_id(node.createdName), node));
}
}
break;
}
}
}

View File

@ -0,0 +1,15 @@
import 'package:sqlparser/sqlparser.dart';
import '../driver/state.dart';
class DiscoveredDriftTable extends DiscoveredElement {
final TableInducingStatement createTable;
DiscoveredDriftTable(super.ownId, this.createTable);
}
class DiscoveredDriftView extends DiscoveredElement {
final CreateViewStatement createView;
DiscoveredDriftView(super.ownId, this.createView);
}

View File

@ -0,0 +1 @@
class AnalyzedFile {}

View File

@ -1,15 +0,0 @@
import 'package:sqlparser/sqlparser.dart';
abstract class TemporaryResult {}
class TemporaryDriftTable extends TemporaryResult {
final TableInducingStatement statement;
TemporaryDriftTable(this.statement);
}
class TemporaryDriftView extends TemporaryResult {
final CreateViewStatement statement;
TemporaryDriftView(this.statement);
}

View File

@ -1,3 +1,4 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:logging/logging.dart';
import 'package:build/build.dart';
import 'package:build/build.dart' as build;
@ -16,4 +17,9 @@ class DriftBuildBackend extends DriftBackend {
Future<String> readAsString(Uri uri) {
return _buildStep.readAsString(AssetId.resolve(uri));
}
@override
Future<LibraryElement> readDart(Uri uri) {
return _buildStep.resolver.libraryFor(AssetId.resolve(uri));
}
}