Path to current file in Dart - dart

How can I get the path to the current file? (similar to the __FILE__ constant in PHP or macro in C) For example:
a.dart:
import './b.dart';
void main() => print(path());
b.dart:
import 'dart:io';
String path() => Platform.script.toFilePath();
The above code prints the path to the invoking script, a.dart. How can I change b.dart to get the path to b.dart instead, such that wherever I call path() from within the project, I'll always get the correct path to b.dart?

This may not work in all cases (I means the platforms and executable formats), but it is as easy as shelling pears:
import 'package:stack_trace/stack_trace.dart';
void main(List<String> args) {
final frame = _frame(); // <==== /E:/prj/dart_test/bin/main.dart at line 4
print('file: ${frame.uri.path}');
print('lime: ${frame.line}');
}
Frame _frame() {
return Frame.caller(1);
}
Result:
file: /E:/prj/dart_test/bin/main.dart
lime: 4

Related

How to use Dart's analysis server method search.findTopLevelDeclarations?

I'm trying to get list of top level class declarations with Dart's analysis server. So, I'm sending search.findTopLevelDeclarations request, but search results are always empty.
It seems to me that analysis server don't know where to search. I've tried to set my project's root as execution context (execution.createContext) root and/or analysis root (analysis.setAnalysisRoots), but search results are still empty.
What should I do to make server understand where to search declarations?
Never played with this before so I got into quite a journey...
I don't know how you are interacting with the analysis server but I have made a working example using the analysis_server_client package. One problem doing that is that the version on pub.dev is quite old so I ended up fetching the version from the stable branch of Dart SDK:
https://github.com/dart-lang/sdk/tree/stable/pkg/analysis_server_client
You can then import the package in your pubspec.yaml by doing:
dependencies:
analysis_server_client:
path: /path/to/analysis_server_client
I then made a simplified version of the example code from:
https://github.com/dart-lang/sdk/blob/stable/pkg/analysis_server_client/example/example.dart
import 'dart:io' show exit;
import 'package:analysis_server_client/handler/connection_handler.dart';
import 'package:analysis_server_client/handler/notification_handler.dart';
import 'package:analysis_server_client/protocol.dart';
import 'package:analysis_server_client/server.dart';
final server = Server();
Future<void> main(List<String> args) async {
const targetDirPath = r'C:\tmp\simple_project';
const searchPattern = 'main';
// Launch the server
await server.start();
// Connect to the server
final handler = _Handler(server);
server.listenToOutput(notificationProcessor: handler.handleEvent);
if (!await handler.serverConnected(timeLimit: const Duration(seconds: 15))) {
exit(1);
}
await server.send(ANALYSIS_REQUEST_SET_ANALYSIS_ROOTS,
AnalysisSetAnalysisRootsParams([targetDirPath], const []).toJson());
await server.send(SEARCH_REQUEST_FIND_TOP_LEVEL_DECLARATIONS,
SearchFindTopLevelDeclarationsParams(searchPattern).toJson());
}
class _Handler with NotificationHandler, ConnectionHandler {
#override
final Server server;
_Handler(this.server);
#override
void onSearchResults(SearchResultsParams params) {
print('-- Start of result --');
params.results.forEach(print);
print('-- End of result --');
server.stop();
}
}
The project at C:\tmp\simple_project is a simple project created with the following which means it just contains a single main method:
dart create -t console-simple simple_project
When I run my analyzer program I get the following output:
-- Start of result --
{"location":{"file":"C:\\tmp\\simple_project\\bin\\simple_project.dart","offset":5,"length":4,"startLine":1,"startColumn":6,"endLine":1,"endColumn":10},"kind":"DECLARATION","isPotential":false,"path":[{"kind":"FUNCTION","name":"main","location":{"file":"C:\\tmp\\simple_project\\bin\\simple_project.dart","offset":5,"length":4,"startLine":1,"startColumn":6,"endLine":1,"endColumn":10},"flags":8,"parameters":"(List<String> arguments)","returnType":"void"},{"kind":"COMPILATION_UNIT","name":"simple_project.dart","location":{"file":"C:\\tmp\\simple_project\\bin\\simple_project.dart","offset":0,"length":0,"startLine":1,"startColumn":1,"endLine":1,"endColumn":1},"flags":16},{"kind":"LIBRARY","name":"","location":{"file":"C:\\tmp\\simple_project\\bin\\simple_project.dart","offset":0,"length":0,"startLine":1,"startColumn":1,"endLine":1,"endColumn":1},"flags":0}]}
-- End of result --
If I change searchPattern to an empty String, I gets a long list of top level declarations around the default included Dart SDK libraries. I am sure there are a way to exclude those.
But as far as I can see, the searchPattern is a regular expression tested against the name of each top level declaration and includes the declaration if its name contain any part of the regular expression.
I found the code responsible for the search here:
#override
Future<List<SearchMatch>> searchTopLevelDeclarations(String pattern) async {
var allElements = <Element>{};
var regExp = RegExp(pattern);
var drivers = _drivers.toList();
for (var driver in drivers) {
var elements = await driver.search.topLevelElements(regExp);
allElements.addAll(elements);
}
return allElements.map(SearchMatchImpl.forElement).toList();
}
https://github.com/dart-lang/sdk/blob/1278bd5adb6a857580f137e47bc521976222f7b9/pkg/analysis_server/lib/src/services/search/search_engine_internal.dart#L113-L123
Which calls into:
/// Returns top-level elements with names matching the given [regExp].
Future<List<Element>> topLevelElements(RegExp regExp) async {
List<Element> elements = <Element>[];
void addElement(Element element) {
if (!element.isSynthetic && regExp.hasMatch(element.displayName)) {
elements.add(element);
}
}
List<FileState> knownFiles = _driver.fsState.knownFiles.toList();
for (FileState file in knownFiles) {
var unitResult = await _driver.getUnitElement(file.path);
if (unitResult is UnitElementResult) {
CompilationUnitElement unitElement = unitResult.element;
unitElement.accessors.forEach(addElement);
unitElement.classes.forEach(addElement);
unitElement.enums.forEach(addElement);
unitElement.extensions.forEach(addElement);
unitElement.functions.forEach(addElement);
unitElement.mixins.forEach(addElement);
unitElement.topLevelVariables.forEach(addElement);
unitElement.typeAliases.forEach(addElement);
}
}
return elements;
}
https://github.com/dart-lang/sdk/blob/1278bd5adb6a857580f137e47bc521976222f7b9/pkg/analyzer/lib/src/dart/analysis/search.dart#L166-L192

Can't figure out how to load yaml document in Dart

According to the readme for the Dart-Yaml pub package, I should be able to do the following:
var db = loadYamlDocument("db.yaml");
with print(db['hostname']); giving me the value I have specified for port in that yaml, however I'm told that YamlDocument has no instance method []. Okay, I guess that object doesn't have map behaviors, which is something I would have expected it to have. Looking through the docs, there's mention of a YamlMap, but I don't see how to implement that at all.
If I simply try print(db); I actually get the string: 'db.yaml'.
So I tried using new File('db.yaml'); and then sending that variable into the loadYamlDocument method, but that doesn't work either.
Does anyone have any luck reading a yaml document and getting a map out of it in Dart?
import "dart:io";
import "package:yaml/yaml.dart";
main() {
File file = new File('pubspec.yaml');
String yamlString = file.readAsStringSync();
Map yaml = loadYaml(yamlString);
}
EDIT:
Map loadYamlFileSync(String path) {
File file = new File(path);
if (file?.existsSync() == true) {
return loadYaml(file.readAsStringSync());
}
return null;
}
Future<Map> loadYamlFile(String path) async{
File file = new File(path);
if ((await file?.exists()) == true) {
String content = await file.readAsString();
return loadYaml(content);
}
return null;
}
main(List<String> args){
print(loadYamlFileSync("pubspec.yaml"));
}
Check the documentation pages for the Yaml package.
loadYamlDocument() returns a YamlDocument which is a 'heavyweight' class that gives you access to all the features of a Yaml document.
You probably want to use loadYaml, which in most cases is going to return a Map. The description says that the actual implementation of the map is a YamlMap (the Yaml package's implementation of a Map, that they presumably need to use instead of a HashMap for some technical reason).

Cannot access var defined in multilayer imported file

Say i got 3 dart scripts in the same folder.
// a.dart
import 'b.dart';
void main(){
print(foo);
}
// b.dart
import 'c.dart';
// c.dart
var foo = 1;
and I got Cannot resolve 'foo' in a.dart
Import doesn't automatically reexport.
You can use one of these variants:
// b.dart
import 'c.dart';
export 'c.dart';
// foo is available in b.dart and a.dart
or
// b.dart
export 'c.dart';
// foo is not available in b.dart but in a.dart
This is the same as my answer to How can I import all files in a folder? was about ;-)
A few alternate options.
Option 1
Solely use import statements and reference the actually files you need. For example:
// a.dart
import 'b.dart';
import 'c.dart';
void main(){
print(foo);
print(bar);
}
// b.dart
import 'c.dart';
var bar = foo + 1;
// c.dart
var foo = 1;
This doesn't allow for inherited imports, but if you follow this pattern throughout your app, your dependencies are very easy to see (they won't be hidden in another file).
Option 2
Don't use imports, but rather just make everything part of the same library.
// a.dart
library myLib;
part 'b.dart';
part 'c.dart';
void main(){
print(foo);
print(bar);
}
// b.dart
part of myLib;
var bar = foo + 1;
// c.dart
part of myLib;
var foo = 1;
This option works well if your application isn't too large and all your classes and global variables have unique names (that is to say you don't nee namespacing). You can also implement this pattern into a small subsection of your applicaiton rather than the whole thing.

Why Logback/Slf4j logs wrong file and line numbers in Groovy?

I have noticed that sometimes Logback/Slf4j logs wrong file and line numbers in Groovy.
I have a lot of bad file/line number logs in my Grails application (more than 50% of all logs)
Is there any workaround?
Simplest example:
logback.groovy
appender("STDOUT", ConsoleAppender) {
encoder(PatternLayoutEncoder) {
pattern = '%d{HH:mm:ss.SSS} [%-5level] %msg \\(%file:%line\\)%n'
}
}
root(DEBUG, ["STDOUT"])
Test.groovy
#Slf4j
class Test {
static void main(String[] args) {
log.info("${'Wrong file and line number!'}")
}
}
Output
23:24:23.894 [INFO ] 0 Wrong file and line number! (NativeMethodAccessorImpl.java:-2)
Example of my grails log output with problem
10:16:44.881 [DEBUG] [org.grails.plugin.resource.ResourceProcessor] -------------------------------------------------- (null:-1)
The problem occurs when a GString is logged (any normal String logs the correct line number). I have no clue why it works like this but I have found two workarounds: Either convert GString to String by calling the toString() method (ugly) or use template format with parameters
import groovy.util.logging.Slf4j;
#Slf4j
class Test {
static void main(String[] args) {
def x = 1
log.info("Does not work: ${x}")
log.info("Works ${x}".toString())
log.info("Works {}", x)
}
}

Reading static files under a library in Dart?

I am writing a library in Dart and I have static files under the library folder. I want to be able to read those files, but I'm not sure how to retrieve the path to it... there is not __FILE__ or $0 like in some other languages.
Update: It seems that I was not clear enough. Let this help you understand me:
test.dart
import 'foo.dart';
void main() {
print(Foo.getMyPath());
}
foo.dart
library asd;
class Foo {
static Path getMyPath() => new Path('resources/');
}
It gives me the wrong folder location. It gives me the path to test.dart + resources/, but I want the path to foo.dart + resources/.
As mentioned, you can use mirrors. Here's an example using what you wanted to achieve:
test.dart
import 'foo.dart';
void main() {
print(Foo.getMyPath());
}
foo.dart
library asd;
import 'dart:mirrors';
class Foo {
static Path getMyPath() => new Path('${currentMirrorSystem().libraries['asd'].url}/resources/');
}
It should output something like:
/Users/Kai/test/lib/resources/
There will probably be a better way to do this in a future release. I will update the answer when this is the case.
Update: You could also define a private method in the library:
/**
* Returns the path to the root of this library.
*/
_getRootPath() {
var pathString = new Path(currentMirrorSystem().libraries['LIBNAME'].url).directoryPath.toString().replaceFirst('file:///', '');
return pathString;
}
The dart mirrors API (still experimental, and not available on all platforms such as dart2js yet) exposes a url getter on the LibraryMirror. This should give you what you want.
I'm not aware of any other way to get this information on a library.
#import('dart:mirrors');
#import('package:mylib/mylib.dart');
main(){
final urlOfLib = currentMirrorSystem().libraries['myLibraryName'].url;
}
Generally the usual method of accessing resources which are located at a static position with your library is by use using a relative path.
#import('dart:io');
...
var filePath = new Path('resources/cool.txt');
var file = new File.fromPath(filePath);
// And if you really wanted, you can then get the full path
// Note: below is for example only. It is missing various
// integrity checks like error handling.
file.fullPath.then((path_str) {
print(path_str);
});
See addition API information on Path and on File
As an aside.. If you absolutely wanted to get the same type of output as __FILE__ you can do something like the following:
#import('dart:io');
...
var opts = new Options();
var path = new Path(opts.script);
var file = new File.fromPath(path);
file.fullPath().then((path_str) {
print(path_str);
});

Resources