├── .atom
└── launches
│ ├── all.yaml
│ ├── generate_analysis.yaml
│ ├── grind.yaml
│ ├── source_map.yaml
│ ├── test.yaml
│ └── utils_test.yaml
├── .gitattributes
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── assets
└── dart-plugin-atom-screenshot.png
├── coffeelint.json
├── doc
├── getting_started.md
└── images
│ └── screenshot.png
├── grammars
├── dart.cson
├── yaml-ext-license.md
└── yaml-ext.cson
├── images
├── gear.svg
└── loading.svg
├── keymaps
└── atom-dart.cson
├── lib
├── analysis
│ ├── analysis_options.dart
│ ├── buffer_observer.dart
│ ├── completions.dart
│ ├── dartdoc.dart
│ ├── declaration_nav.dart
│ ├── find_type.dart
│ ├── formatting.dart
│ ├── organize_file.dart
│ ├── quick_fixes.dart
│ ├── refactor.dart
│ ├── references.dart
│ └── type_hierarchy.dart
├── analysis_server.dart
├── atom_autocomplete.dart
├── atom_linter.dart
├── atom_package_deps.dart
├── atom_statusbar.dart
├── atom_treeview.dart
├── atom_utils.dart
├── browser.dart
├── dartino
│ ├── dartino.dart
│ ├── dartino_project_settings.dart
│ ├── dartino_util.dart
│ ├── device
│ │ ├── dartuino_board.dart
│ │ ├── device.dart
│ │ ├── local_device.dart
│ │ └── stm32f.dart
│ ├── launch_dartino.dart
│ └── sdk
│ │ ├── dartino_sdk.dart
│ │ └── sdk.dart
├── debug
│ ├── breakpoints.dart
│ ├── chrome.dart
│ ├── chrome_debugger.dart
│ ├── debugger.dart
│ ├── debugger_tooltip.dart
│ ├── debugger_ui.dart
│ ├── evaluator.dart
│ ├── model.dart
│ ├── observatory.dart
│ ├── observatory_debugger.dart
│ ├── utils.dart
│ └── websocket.dart
├── editors.dart
├── elements.dart
├── error_repository.dart
├── flutter
│ ├── flutter.dart
│ ├── flutter_connect.dart
│ ├── flutter_daemon.dart
│ ├── flutter_devices.dart
│ ├── flutter_ext.dart
│ ├── flutter_launch.dart
│ ├── flutter_sdk.dart
│ ├── flutter_tools.dart
│ ├── flutter_ui.dart
│ └── mojo_launch.dart
├── impl
│ ├── changelog.dart
│ ├── debounce.dart
│ ├── editing.dart
│ ├── navigation.dart
│ ├── outline.dart
│ ├── pub.dart
│ ├── rebuild.dart
│ ├── smoketest.dart
│ ├── status.dart
│ ├── status_display.dart
│ ├── testing.dart
│ ├── testing_utils.dart
│ ├── toolbar.dart
│ ├── toolbar_impl.dart
│ └── tooltip.dart
├── jobs.dart
├── launch
│ ├── console.dart
│ ├── launch.dart
│ ├── launch_cli.dart
│ ├── launch_configs.dart
│ ├── launch_serve.dart
│ ├── launch_shell.dart
│ ├── launch_web.dart
│ └── run.dart
├── linter.dart
├── material.dart
├── plugin.dart
├── projects.dart
├── sdk.dart
├── state.dart
├── usage.dart
├── utils.dart
└── views.dart
├── menus
└── atom-dart.cson
├── package.json
├── pubspec.yaml
├── settings
├── dart.cson
└── yaml-ext.cson
├── snippets
└── dart.cson
├── spec
├── _spec
│ ├── animals_test.dart
│ ├── jasmine.dart
│ ├── sample-spec.dart
│ └── test.dart
├── all-spec.dart
├── flutter
│ └── launch_flutter_test.dart
├── projects_test.dart
└── sdk_test.dart
├── styles
├── console.less
├── dartdoc.less
├── dartlang.less
├── debugger.less
├── layout.css
├── material.less
├── outline.less
├── status.less
├── tooltip.less
└── views.less
├── test
├── all.dart
├── dartino_util_test.dart
├── dependencies_test.dart
├── evaluator_test.dart
├── testing_utils_test.dart
└── utils_test.dart
├── tool
├── grind.dart
├── source_map.dart
├── src
│ ├── parser.dart
│ └── src_gen.dart
├── test.dart
└── travis.sh
└── web
├── entry.dart
├── entry.dart.js
└── entry.dart.js.map
/.atom/launches/all.yaml:
--------------------------------------------------------------------------------
1 | # Cli launch configuration for test/all.dart.
2 | type: cli
3 | path: test/all.dart
4 |
5 | cli:
6 | # Additional args for the application.
7 | args:
8 | # The working directory to use for the launch.
9 | cwd:
10 | # Enable or disable checked mode.
11 | checked: true
12 | # Enable or disable debugging.
13 | debug: true
14 |
--------------------------------------------------------------------------------
/.atom/launches/generate_analysis.yaml:
--------------------------------------------------------------------------------
1 | # Cli launch configuration for tool/analysis/generate_analysis.dart.
2 | type: cli
3 | path: tool/analysis/generate_analysis.dart
4 |
5 | cli:
6 | args:
7 | checked: true
8 | debug: true
9 |
--------------------------------------------------------------------------------
/.atom/launches/grind.yaml:
--------------------------------------------------------------------------------
1 | # Cli launch configuration for tool/grind.dart.
2 | type: cli
3 | path: tool/grind.dart
4 |
5 | cli:
6 | args:
7 | checked: true
8 |
--------------------------------------------------------------------------------
/.atom/launches/source_map.yaml:
--------------------------------------------------------------------------------
1 | # Cli launch configuration for tool/source_map.dart.
2 | type: cli
3 | path: tool/source_map.dart
4 |
5 | cli:
6 | # Additional args for the application.
7 | args:
8 | # The working directory to use for the launch.
9 | cwd:
10 | # Enable or disable checked mode.
11 | checked: true
12 | # Enable or disable debugging.
13 | debug: true
14 |
--------------------------------------------------------------------------------
/.atom/launches/test.yaml:
--------------------------------------------------------------------------------
1 | # Cli launch configuration for tool/test.dart.
2 | type: cli
3 | path: tool/test.dart
4 |
5 | cli:
6 | # Additional args for the application.
7 | args:
8 | # The working directory to use for the launch.
9 | cwd:
10 | # Enable or disable checked mode.
11 | checked: true
12 | # Enable or disable debugging.
13 | debug: true
14 |
--------------------------------------------------------------------------------
/.atom/launches/utils_test.yaml:
--------------------------------------------------------------------------------
1 | # Cli launch configuration for test/utils_test.dart.
2 | type: cli
3 | path: test/utils_test.dart
4 |
5 | cli:
6 | args:
7 | checked: true
8 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Line endings: enforce LF in GitHub, convert to native on checkout.
2 | * text=auto
3 | *.dart text
4 | *.md text
5 | *.sh text
6 | *.yaml text
7 | *.png binary
8 |
9 | web/entry.dart.js linguist-vendored
10 | web/entry_dart_results.html linguist-documentation
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .buildlog
2 | .DS_Store
3 | .idea
4 | .packages
5 | .pub/
6 | .settings/
7 | build/
8 | doc/api/
9 | packages
10 | pubspec.lock
11 | npm-debug.log
12 | node_modules
13 |
14 | spec/all-spec.js
15 | spec/all-spec.js.map
16 |
17 | *.dart.js
18 | *.dart.js.deps
19 | *.dart.js.map
20 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: dart
2 | dart: stable
3 |
4 | script: ./tool/travis.sh
5 |
6 | branches:
7 | only:
8 | - master
9 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | First up, please join our [dart-atom-dev mailing list][list].
2 |
3 | Contributions welcome! You can help out by testing and filing issues, helping
4 | with docs, or writing code.
5 |
6 | ## Developing the plugin
7 | To work on `dartlang` plugin:
8 |
9 | - install [Atom](https://atom.io/)
10 | - clone this repo
11 | - from the command line, run `pub get`
12 | - to use `grind` :
13 | - from the command line, run `pub global activate grinder`
14 | - then run `grind build`; this will re-compile the javascript
15 | - from the repo directory, type `apm link` (you can install `apm` via the
16 | `Atom > Install Shell Commands` menu item)
17 | - restart Atom
18 |
19 | The plugin will be active in your copy of Atom. When making changes:
20 |
21 | - `type type type...`
22 |
23 | and:
24 |
25 | - run `grind build`
26 | - from atom, hit `ctrl-option-command-l`; this will re-start atom
27 |
28 | ## Publishing a new release
29 |
30 | - `git pull` the latest
31 | - rev the changelog version from 'unreleased' to the next version
32 | - rev the pubspec version to the same
33 | - commit, push to master
34 | - verify that the package.json version is one minor patch older; apm will rev
35 | the last number - we want that to match the changelog and pubspec versions
36 | - from the CLI: `apm publish patch`
37 |
38 | ## Docs
39 |
40 | Some of our docs are in the main readme.md file. We try and keep that file short
41 | and sweet.
42 |
43 | Most of our docs are in the `gh-pages` branch on the repo. We author the docs in
44 | markdown, and github's gh-pages system automatically converts it the html (we're
45 | using the 'SinglePaged' jekyll template). Changes pushed to the `gh-pages`
46 | branch go live automatically. When working on the docs, run `jekyll serve -w
47 | --force_polling` to see a preview version of the rendered docs.
48 |
49 | [list]: https://groups.google.com/forum/#!forum/dart-atom-dev
50 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2015, the Dart project authors. All rights reserved.
2 | Redistribution and use in source and binary forms, with or without
3 | modification, are permitted provided that the following conditions are
4 | met:
5 |
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above
9 | copyright notice, this list of conditions and the following
10 | disclaimer in the documentation and/or other materials provided
11 | with the distribution.
12 | * Neither the name of Google Inc. nor the names of its
13 | contributors may be used to endorse or promote products derived
14 | from this software without specific prior written permission.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dart plugin for Atom
2 |
3 | A [Dart](https://www.dartlang.org) plugin for [Atom](https://atom.io).
4 |
5 | ## DEPRECATED
6 |
7 | **Note**: This plugin is deprecated, and is no longer actively maintained.
8 |
9 | It's recommended that people investigate more actively maintained Dart plugins, such as those for
10 | [IntelliJ](https://dart.dev/tools/jetbrains-plugin) and
11 | [VS Code](https://dart.dev/tools/vs-code).
12 |
13 | ## Getting started
14 |
15 | See our [getting started guide](https://dart-atom.github.io/dart/)! This is
16 | useful for users new to the plugin and users new to Atom as well.
17 |
18 | ## What is it?
19 |
20 | This package is a lightweight, streamlined Dart development plugin for Atom. It
21 | supports features like auto-discovery of the Dart SDK, errors and warnings shown
22 | as you type, code completion, refactorings, debugging, and integration with Pub
23 | and other tools.
24 |
25 | 
26 |
27 | ## Installing
28 |
29 | - install Atom [atom.io](https://atom.io/)
30 | - install this [dart][] package (with `apm install dart` or through the
31 | Atom UI)
32 |
33 | ## Terms and Privacy
34 |
35 | The Dart plugin for Atom (the "Plugin") is an open-source project, contributed
36 | to by Google. By using the Plugin, you consent to be bound by Google's general
37 | Terms of Service and Google's general
38 | [Privacy Policy](http://www.google.com/intl/en/policies/privacy/).
39 |
40 | ## License
41 |
42 | See the [LICENSE](https://github.com/dart-atom/dart/blob/master/LICENSE)
43 | file.
44 |
45 | [dart]: https://atom.io/packages/dart
46 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | analyzer:
2 | strong-mode: true
3 | linter:
4 | rules:
5 | - directives_ordering
6 |
--------------------------------------------------------------------------------
/assets/dart-plugin-atom-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-atom/dart/b37d3f916904e6f7564fcd794288d83c137af10b/assets/dart-plugin-atom-screenshot.png
--------------------------------------------------------------------------------
/coffeelint.json:
--------------------------------------------------------------------------------
1 | {
2 | "max_line_length": {
3 | "level": "ignore"
4 | },
5 | "no_empty_param_list": {
6 | "level": "error"
7 | },
8 | "arrow_spacing": {
9 | "level": "error"
10 | },
11 | "no_interpolation_in_single_quotes": {
12 | "level": "error"
13 | },
14 | "no_debugger": {
15 | "level": "error"
16 | },
17 | "colon_assignment_spacing": {
18 | "spacing": {
19 | "left": 0,
20 | "right": 1
21 | },
22 | "level": "error"
23 | },
24 | "braces_spacing": {
25 | "spaces": 0,
26 | "level": "error"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/doc/getting_started.md:
--------------------------------------------------------------------------------
1 | The getting started guide has [moved](http://dart-atom.github.io/dartlang/).
2 |
--------------------------------------------------------------------------------
/doc/images/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-atom/dart/b37d3f916904e6f7564fcd794288d83c137af10b/doc/images/screenshot.png
--------------------------------------------------------------------------------
/grammars/yaml-ext-license.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 GitHub Inc.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
22 | --------------------------------------------------------------------
23 |
24 | This package was derived from a TextMate bundle located at
25 | https://github.com/textmate/yaml.tmbundle and distributed under the following
26 | license, located in `README.mdown`:
27 |
28 | Permission to copy, use, modify, sell and distribute this
29 | software is granted. This software is provided "as is" without
30 | express or implied warranty, and with no claim as to its
31 | suitability for any purpose.
32 |
--------------------------------------------------------------------------------
/images/gear.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/images/loading.svg:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/keymaps/atom-dart.cson:
--------------------------------------------------------------------------------
1 | # Keybindings require three things to be fully defined: A selector that is
2 | # matched against the focused element, the keystroke and the command to execute.
3 | # For more detailed documentation see
4 | # https://atom.io/docs/latest/behind-atom-keymaps-in-depth.
5 |
6 | # Global commands
7 | 'atom-workspace':
8 | 'f5': 'dart:debug-run'
9 | 'shift-f5': 'dart:debug-terminate'
10 | 'f9': 'dart:debug-toggle-breakpoint'
11 | 'f10': 'dart:debug-step'
12 | 'f11': 'dart:debug-stepin'
13 | 'shift-f11': 'dart:debug-stepout'
14 |
15 | # Global commands (mac)
16 | 'body.platform-darwin':
17 | 'ctrl-alt-cmd-t': 'dart:run-tests'
18 |
19 | # Global commands (windows and linux)
20 | 'body.platform-win32, body.platform-linux':
21 | 'ctrl-alt-t': 'dart:run-tests'
22 |
23 | # Dart editor commands (common to all platforms)
24 | 'atom-text-editor[data-grammar~="dart"]:not([mini])':
25 | 'ctrl-1': 'dart:quick-fix'
26 | 'alt-enter': 'dart:quick-fix'
27 | 'f1': 'dart:show-dartdoc'
28 | 'f4': 'dart:type-hierarchy'
29 | 'alt-shift-i': 'dart:refactor-inline-local'
30 | 'alt-shift-l': 'dart:refactor-extract-local'
31 | 'alt-shift-r': 'dart:refactor-rename'
32 | 'enter': 'dart:newline'
33 |
34 | # Dart editor commands (mac)
35 | 'body.platform-darwin atom-text-editor[data-grammar~="dart"]:not([mini])':
36 | 'cmd-f4': 'dart:find-references'
37 | 'cmd-r': 'dart:run-application'
38 | 'cmd-shift-r': 'dart:app-full-restart'
39 | 'cmd-alt-b': 'dart:dart-format'
40 | 'cmd-alt-o': 'dart:organize-directives'
41 | 'cmd-alt-down': 'dart:jump-to-declaration'
42 | 'cmd-alt-enter': 'dart:jump-to-declaration'
43 | 'shift-cmd-t': 'dart:find-type'
44 |
45 | # Handle cmd-r for launch config files.
46 | 'body.platform-darwin atom-text-editor[data-grammar~="yaml"]:not([mini])':
47 | 'cmd-r': 'dart:run-application'
48 | 'cmd-shift-r': 'dart:app-full-restart'
49 |
50 | # Dart editor commands (windows and linux)
51 | 'body.platform-linux atom-text-editor[data-grammar~="dart"]:not([mini]), body.platform-win32 atom-text-editor[data-grammar~="dart"]:not([mini])':
52 | 'ctrl-alt-enter': 'dart:jump-to-declaration'
53 | 'ctrl-f4': 'dart:find-references'
54 | 'ctrl-r': 'dart:run-application'
55 | 'ctrl-shift-r': 'dart:app-full-restart'
56 | 'ctrl-alt-b': 'dart:dart-format'
57 | 'ctrl-alt-o': 'dart:organize-directives'
58 | 'ctrl-shift-t': 'dart:find-type'
59 |
60 | # Handle ctrl-r for launch config files.
61 | 'body.platform-linux atom-text-editor[data-grammar~="yaml"]:not([mini]), body.platform-win32 atom-text-editor[data-grammar~="yaml"]:not([mini])':
62 | 'ctrl-r': 'dart:run-application'
63 | 'ctrl-shift-r': 'dart:app-full-restart'
64 |
65 | # Dart editor commands (linux)
66 | 'body.platform-linux atom-text-editor[data-grammar~="dart"]:not([mini])':
67 | 'ctrl-alt-down': 'dart:jump-to-declaration'
68 |
--------------------------------------------------------------------------------
/lib/analysis/find_type.dart:
--------------------------------------------------------------------------------
1 | import 'package:analysis_server_lib/analysis_server_lib.dart' show FindTopLevelDeclarationsResult, SearchResult, Location;
2 | import 'package:atom/atom.dart';
3 | import 'package:atom/node/notification.dart';
4 | import 'package:atom/node/workspace.dart';
5 | import 'package:atom/utils/disposable.dart';
6 | import 'package:logging/logging.dart';
7 |
8 | import '../analysis_server.dart';
9 | import '../state.dart';
10 | import 'references.dart';
11 |
12 | final Logger _logger = new Logger('find_type');
13 |
14 | class FindTypeHelper implements Disposable {
15 | Disposables disposables = new Disposables();
16 |
17 | String _lastSearchTerm;
18 |
19 | FindTypeHelper() {
20 | disposables.add(atom.commands.add(
21 | 'atom-text-editor', 'dart:find-type', (event) => _handleFindType(event.editor)
22 | ));
23 | }
24 |
25 | void _handleFindType(TextEditor editor) {
26 | promptUser(
27 | 'Find type:',
28 | defaultText: _lastSearchTerm,
29 | selectText: true,
30 | isDart: true
31 | ).then((String searchTerm) {
32 | try {
33 | // Focus the current editor.
34 | editor?.getElement()?.focused();
35 | } catch (e) {
36 | _logger.info('Error focusing editor in _handleFindType', e);
37 | }
38 |
39 | // Abort if user cancels the operation or there's nothing to do.
40 | if (searchTerm == null) return;
41 |
42 | searchTerm = searchTerm.trim();
43 | if (searchTerm.isEmpty) {
44 | atom.beep();
45 | return;
46 | }
47 |
48 | _lastSearchTerm = searchTerm;
49 |
50 | AnalysisRequestJob job = new AnalysisRequestJob('Find type', () async {
51 | String term = _createInsensitiveRegex(searchTerm);
52 | FindTopLevelDeclarationsResult result =
53 | await analysisServer.server.search.findTopLevelDeclarations(term);
54 |
55 | if (result?.id == null) {
56 | atom.beep();
57 | return;
58 | }
59 |
60 | List results =
61 | await analysisServer.getSearchResults(result.id);
62 |
63 | if (results.isEmpty) {
64 | atom.beep();
65 | return;
66 | }
67 |
68 | List exact = results.where((SearchResult result) {
69 | return result.path.first.name.toLowerCase() == searchTerm.toLowerCase();
70 | });
71 |
72 | if (exact.length == 1) {
73 | Location location = exact.first.location;
74 | navigationManager.jumpToLocation(location.file,
75 | location.startLine - 1, location.startColumn - 1, location.length);
76 | } else {
77 | deps[FindReferencesHelper].openView(new ReferencesSearch('Find Type',
78 | searchTerm, results: results));
79 | }
80 | });
81 | job.schedule();
82 | });
83 | }
84 |
85 | String _createInsensitiveRegex(String searchTerm) {
86 | StringBuffer buf = new StringBuffer();
87 |
88 | for (int i = 0; i < searchTerm.length; i++) {
89 | String s = searchTerm[i];
90 | buf.write('[${s.toLowerCase()}${s.toUpperCase()}]');
91 | }
92 |
93 | return buf.toString();
94 | }
95 |
96 | void dispose() => disposables.dispose();
97 | }
98 |
--------------------------------------------------------------------------------
/lib/analysis/formatting.dart:
--------------------------------------------------------------------------------
1 | library atom.formatting;
2 |
3 | import 'dart:async';
4 |
5 | import 'package:atom/atom.dart';
6 | import 'package:atom/node/process.dart';
7 | import 'package:atom/node/workspace.dart';
8 | import 'package:atom/utils/disposable.dart';
9 | import 'package:logging/logging.dart';
10 |
11 | import '../analysis_server.dart';
12 | import '../editors.dart';
13 | import '../state.dart';
14 |
15 | final Logger _logger = new Logger('formatting');
16 |
17 | // Get the current preferred line length for Dart files.
18 | int get _prefLineLength =>
19 | atom.config.getValue('editor.preferredLineLength', scope: ['source.dart']);
20 |
21 | class FormattingManager implements Disposable {
22 | Disposables _commands = new Disposables();
23 |
24 | FormattingManager() {
25 | _commands.add(atom.commands.add('.tree-view', 'dart:dart-format', (e) {
26 | formatFile(e.targetFilePath);
27 | }));
28 | _commands.add(atom.commands.add('atom-text-editor', 'dart:dart-format', (e) {
29 | formatEditor(e.editor);
30 | }));
31 | }
32 |
33 | void dispose() => _commands.dispose();
34 |
35 | static void formatFile(String path) {
36 | if (!sdkManager.hasSdk) {
37 | atom.beep();
38 | return;
39 | }
40 |
41 | // dartfmt -l90 -w lib/analysis/formatting.dart
42 | List args = [];
43 | args.add('-l${_prefLineLength}');
44 | args.add('-w');
45 | args.add(path);
46 | sdkManager.sdk.execBinSimple('dartfmt', args).then((ProcessResult result) {
47 | if (result.exit == 0) {
48 | atom.notifications.addSuccess('Formatting successful.');
49 | } else {
50 | atom.notifications.addError('Error while formatting', description: result.stderr);
51 | }
52 | });
53 | }
54 |
55 | /// Formats a [TextEditor]. Returns false if the editor was not formatted;
56 | /// true if it was.
57 | static Future formatEditor(TextEditor editor, {bool quiet: false}) {
58 | String path = editor.getPath();
59 |
60 | if (projectManager.getProjectFor(path) == null) {
61 | atom.beep();
62 | return new Future.value(false);
63 | }
64 |
65 | if (!analysisServer.isActive) {
66 | atom.beep();
67 | return new Future.value(false);
68 | }
69 |
70 | Range range = editor.getSelectedBufferRange();
71 | TextBuffer buffer = editor.getBuffer();
72 | int offset = buffer.characterIndexForPosition(range.start);
73 | int end = buffer.characterIndexForPosition(range.end);
74 |
75 | // TODO: If range.isNotEmpty, just format the given selection?
76 | return analysisServer
77 | .format(path, offset, end - offset, lineLength: _prefLineLength)
78 | .then((FormatResult result) {
79 | if (result.edits.isEmpty) {
80 | if (!quiet) atom.notifications.addSuccess('No formatting changes.');
81 | return false;
82 | } else {
83 | if (!quiet) atom.notifications.addSuccess('Formatting successful.');
84 | applyEdits(editor, result.edits);
85 | editor.setSelectedBufferRange(new Range.fromPoints(
86 | buffer.positionForCharacterIndex(result.selectionOffset),
87 | buffer.positionForCharacterIndex(
88 | result.selectionOffset + result.selectionLength)));
89 | return true;
90 | }
91 | }).catchError((e) {
92 | if (e is RequestError) {
93 | if (!quiet) {
94 | atom.notifications
95 | .addError('Error while formatting', description: e.message);
96 | }
97 | } else {
98 | atom.beep();
99 | _logger.warning('error when formatting: ${e}');
100 | }
101 | return false;
102 | });
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/lib/analysis/organize_file.dart:
--------------------------------------------------------------------------------
1 | library atom.analysis.organize_file;
2 |
3 | import 'package:atom/atom.dart';
4 | import 'package:atom/node/workspace.dart';
5 | import 'package:atom/utils/disposable.dart';
6 |
7 | import '../analysis_server.dart';
8 | import '../editors.dart';
9 | import '../state.dart';
10 |
11 | class OrganizeFileManager implements Disposable {
12 | Disposables disposables = new Disposables();
13 |
14 | OrganizeFileManager() {
15 | _addEditorCommand('dart:sort-members', _handleSortMembers);
16 | _addEditorCommand('dart:organize-directives', _handleOrganizeDirectives);
17 | }
18 |
19 | void _addEditorCommand(String command, void impl(TextEditor editor)) {
20 | disposables.add(atom.commands.add('atom-text-editor', command, (e) {
21 | if (!analysisServer.isActive) {
22 | atom.beep();
23 | return;
24 | }
25 |
26 | TextEditor editor = e.editor;
27 |
28 | if (projectManager.getProjectFor(editor.getPath()) == null) {
29 | atom.beep();
30 | return;
31 | }
32 |
33 | impl(editor);
34 | }));
35 | }
36 |
37 | void dispose() => disposables.dispose();
38 |
39 | void _handleSortMembers(TextEditor editor) {
40 | String path = editor.getPath();
41 |
42 | AnalysisRequestJob job = new AnalysisRequestJob('Sort members', () {
43 | return analysisServer.server.edit.sortMembers(path).then((result) {
44 | SourceFileEdit edit = result.edit;
45 |
46 | if (edit.edits.isEmpty) {
47 | atom.notifications.addSuccess('No changes from sort members.');
48 | } else {
49 | atom.notifications.addSuccess('Sort members successful.');
50 | applyEdits(editor, edit.edits);
51 | }
52 | });
53 | });
54 |
55 | job.schedule();
56 | }
57 |
58 | void _handleOrganizeDirectives(TextEditor editor) {
59 | String path = editor.getPath();
60 |
61 | AnalysisRequestJob job = new AnalysisRequestJob('Organize directives', () {
62 | return analysisServer.server.edit.organizeDirectives(path).then((result) {
63 | SourceFileEdit edit = result.edit;
64 |
65 | if (edit.edits.isEmpty) {
66 | atom.notifications.addSuccess('No changes from organize directives.');
67 | } else {
68 | atom.notifications.addSuccess('Organize directives successful.');
69 | applyEdits(editor, edit.edits);
70 | }
71 | }).catchError((e) {
72 | if (e is RequestError && e.code == 'UNKNOWN_REQUEST') {
73 | atom.notifications.addWarning(
74 | 'Organize directives is not supported in this version of the analysis server.');
75 | } else {
76 | atom.notifications
77 | .addError('Error running organize directives.', detail: '${e}');
78 | }
79 | });
80 | });
81 |
82 | job.schedule();
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/lib/analysis/quick_fixes.dart:
--------------------------------------------------------------------------------
1 |
2 | library atom.quick_fixes;
3 |
4 | import 'dart:async';
5 |
6 | import 'package:analysis_server_lib/analysis_server_lib.dart';
7 | import 'package:atom/atom.dart';
8 | import 'package:atom/node/workspace.dart';
9 | import 'package:atom/utils/disposable.dart';
10 | import 'package:atom/utils/string_utils.dart';
11 |
12 | import '../analysis_server.dart';
13 | import '../atom_autocomplete.dart';
14 | import '../editors.dart';
15 | import '../state.dart';
16 |
17 | class QuickFixHelper implements Disposable {
18 | Disposables disposables = new Disposables();
19 |
20 | QuickFixHelper() {
21 | disposables.add(atom.commands.add('atom-text-editor',
22 | 'dart:quick-fix', (event) => _handleQuickFix(event.editor)));
23 | }
24 |
25 | /// Open the list of available quick fixes for the given editor at the current
26 | /// location. The editor should be visible and active.
27 | void displayQuickFixes(TextEditor editor) =>
28 | _handleQuickFix(editor, autoFix: false);
29 |
30 | void dispose() => disposables.dispose();
31 |
32 | void _handleQuickFix(TextEditor editor, {bool autoFix: true}) {
33 | String path = editor.getPath();
34 | Range range = editor.getSelectedBufferRange();
35 | int offset = editor.getBuffer().characterIndexForPosition(range.start);
36 | int length = editor.getBuffer().characterIndexForPosition(range.end) - offset;
37 |
38 | Job job = new AnalysisRequestJob('quick fix', () async {
39 | Future assistsFuture = analysisServer.getAssists(path, offset, length);
40 | FixesResult fixes = await analysisServer.getFixes(path, offset);
41 | AssistsResult assists = await assistsFuture;
42 |
43 | _handleFixesResult(fixes, assists, editor, autoFix: autoFix);
44 | });
45 | job.schedule();
46 | }
47 |
48 | void _handleFixesResult(FixesResult result, AssistsResult assists,
49 | TextEditor editor, {bool autoFix: true}) {
50 | List fixes = result.fixes;
51 |
52 | if (fixes.isEmpty && assists.assists.isEmpty) {
53 | atom.beep();
54 | return;
55 | }
56 |
57 | List<_Change> changes = new List.from(
58 | fixes.expand((fix) => fix.fixes.map(
59 | (SourceChange change) => new _Change(change, fix.error))));
60 |
61 | changes.addAll(
62 | assists.assists.map((SourceChange change) => new _Change(change)));
63 |
64 | if (autoFix && changes.length == 1 && assists.assists.isEmpty) {
65 | // Apply the fix.
66 | _applyChange(editor, changes.first.change);
67 | } else {
68 | int i = 0;
69 | var renderer = (_Change change) {
70 | // We need to create suggestions with unique text replacements.
71 | return new Suggestion(
72 | text: 'fix_${++i}',
73 | replacementPrefix: '',
74 | displayText: change.change.message,
75 | rightLabel: change.isAssist ? 'assist' : 'quick-fix',
76 | description: change.isAssist ? null : change.error.message,
77 | type: change.isAssist ? 'attribute' : 'function'
78 | );
79 | };
80 |
81 | // Show a selection dialog.
82 | chooseItemUsingCompletions(editor, changes, renderer).then((_Change choice) {
83 | editor.undo();
84 | _applyChange(editor, choice.change);
85 | });
86 | }
87 | }
88 | }
89 |
90 | class _Change {
91 | final SourceChange change;
92 | final AnalysisError error;
93 |
94 | _Change(this.change, [this.error]);
95 |
96 | bool get isAssist => error == null;
97 |
98 | String toString() {
99 | return error == null ? change.message : '${error.message}: ${change.message}';
100 | }
101 | }
102 |
103 | void _applyChange(TextEditor currentEditor, SourceChange change) {
104 | List sourceFileEdits = change.edits;
105 | List linkedEditGroups = change.linkedEditGroups;
106 |
107 | Future.forEach(sourceFileEdits, (SourceFileEdit edit) {
108 | return atom.workspace.open(edit.file,
109 | options: {'searchAllPanes': true}).then((TextEditor editor) {
110 | applyEdits(editor, edit.edits);
111 | int index = sourceFileEdits.indexOf(edit);
112 | if (index >= 0 && index < linkedEditGroups.length) {
113 | selectEditGroup(editor, linkedEditGroups[index]);
114 | }
115 | });
116 | }).then((_) {
117 | String fileSummary = sourceFileEdits.map((edit) => edit.file).join('\n');
118 | if (sourceFileEdits.length == 1) fileSummary = null;
119 | atom.notifications.addSuccess(
120 | 'Executed quick fix: ${toStartingLowerCase(change.message)}',
121 | detail: fileSummary);
122 |
123 | // atom.workspace.open(currentEditor.getPath(),
124 | // options: {'searchAllPanes': true}).then((TextEditor editor) {
125 | // if (change.selection != null) {
126 | // editor.setCursorBufferPosition(
127 | // editor.getBuffer().positionForCharacterIndex(change.selection.offset));
128 | // } else if (linkedEditGroups.isNotEmpty) {
129 | // selectEditGroups(currentEditor, linkedEditGroups);
130 | // }
131 | // });
132 | }).catchError((e) {
133 | atom.notifications.addError('Error Performing Rename', detail: '${e}');
134 | });
135 | }
136 |
--------------------------------------------------------------------------------
/lib/atom_linter.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | /// See the `linter` API [here](https://github.com/AtomLinter/linter).
6 | library atom.linter;
7 |
8 | import 'dart:async';
9 | import 'dart:js';
10 |
11 | import 'package:atom/atom.dart';
12 | import 'package:atom/node/workspace.dart';
13 | import 'package:atom/src/js.dart';
14 |
15 | abstract class LinterProvider {
16 | final List grammarScopes;
17 | final String scope;
18 | final bool lintOnFly;
19 |
20 | final JsObject _key = jsify({'scope': 'project'});
21 |
22 | static void registerLinterProvider(
23 | String methodName, LinterProvider provider) {
24 | final JsObject exports = context['module']['exports'];
25 | exports[methodName] = () => provider.toProxy();
26 | }
27 |
28 | /// [grammarScopes] is a list of scopes, e.g. `['source.js', 'source.php']`.
29 | /// [scope] is one of either `file` or `project`. [lintOnFly] must be false
30 | /// for the scope `project`.
31 | LinterProvider({this.grammarScopes, this.scope, this.lintOnFly: false});
32 |
33 | // A unique identifier for the provider; JS will store this in a hashmap as a
34 | // map key;
35 | Object get key => _key;
36 |
37 | Future> lint(TextEditor editor);
38 |
39 | JsObject toProxy() {
40 | Map map = {
41 | 'grammarScopes': grammarScopes,
42 | 'scope': scope,
43 | 'lintOnFly': lintOnFly,
44 | 'lint': _lint
45 | };
46 |
47 | return jsify(map);
48 | }
49 |
50 | JsObject _lint(jsEditor) => jsify([]);
51 | }
52 |
53 | abstract class LinterConsumer {
54 | void consume(LinterService linterService);
55 | }
56 |
57 | class LinterService extends ProxyHolder {
58 | ProxyHolder _linter;
59 |
60 | LinterService(obj) : super(obj) {
61 | _linter = new ProxyHolder((obj as JsFunction).apply([jsify({'name': 'dart'})]));
62 | }
63 |
64 | void deleteMessages(LinterProvider provider) {
65 | _linter.invoke('clearMessages');
66 | }
67 |
68 | void setMessages(LinterProvider provider, List messages) {
69 | var list = messages.map((m) => m.toMap()).toList();
70 | _linter.invoke('setAllMessages', list);
71 | }
72 | }
73 |
74 | typedef void LintSolutionVoidFunc();
75 |
76 | class LintSolution {
77 |
78 | final String title;
79 | final num priority;
80 | final Rn position;
81 | LintSolutionVoidFunc apply;
82 |
83 | LintSolution({this.title, this.priority: 0, this.position, this.apply});
84 |
85 | Map toMap() {
86 | Map m = {};
87 | if (title != null) m['title'] = title;
88 | if (priority != null) m['priority'] = priority;
89 | if (position != null) m['position'] = position;
90 | if (apply != null) m['apply'] = apply;
91 | return m;
92 | }
93 | }
94 |
95 | class LintMessage {
96 | static const String ERROR = 'Error';
97 | static const String WARNING = 'Warning';
98 | static const String INFO = 'Info';
99 |
100 | final String type;
101 | final String text;
102 | final String correction;
103 | final String filePath;
104 | final Rn range;
105 | final bool hasFix;
106 | final List solutions;
107 |
108 | LintMessage({this.type, this.text, this.correction, this.hasFix,
109 | this.filePath, this.range, this.solutions});
110 |
111 | Map toMap() {
112 | Map m = {};
113 | if (type != null) m['severity'] = type.toLowerCase();
114 | if (text != null) m['excerpt'] = text;
115 | if (correction != null) m['description'] = correction;
116 | if (filePath != null && range != null) {
117 | m['location'] = {
118 | 'file': filePath,
119 | 'position': range.toArray()
120 | };
121 | }
122 | if (solutions != null) {
123 | m['solutions'] = solutions.map((e) => e.toMap()).toList();
124 | }
125 | return m;
126 | }
127 | }
128 |
129 | class Rn {
130 | final Pt start;
131 | final Pt end;
132 |
133 | Rn(this.start, this.end);
134 |
135 | List toArray() => [start.toArray(), end.toArray()];
136 | }
137 |
138 | class Pt {
139 | final int row;
140 | final int column;
141 |
142 | Pt(this.row, this.column);
143 |
144 | List toArray() => [row, column];
145 | }
146 |
--------------------------------------------------------------------------------
/lib/atom_package_deps.dart:
--------------------------------------------------------------------------------
1 | library atom.atom_package_deps;
2 |
3 | import 'dart:async';
4 |
5 | import 'package:atom/atom.dart';
6 | import 'package:atom/node/notification.dart';
7 | import 'package:atom/node/package.dart';
8 | import 'package:atom/node/process.dart';
9 | import 'package:logging/logging.dart';
10 |
11 | import 'jobs.dart';
12 |
13 | final Logger _logger = new Logger('atom.atom_package_deps');
14 |
15 | Future install() {
16 | return atomPackage.loadPackageJson().then((Map info) {
17 | List installedPackages = atom.packages.getAvailablePackageNames();
18 | List requiredPackages = new List.from(info['required-packages']);
19 |
20 | if (requiredPackages == null || requiredPackages.isEmpty) {
21 | return null;
22 | }
23 |
24 | Set toInstall = new Set.from(requiredPackages);
25 | toInstall.removeAll(installedPackages);
26 |
27 | if (toInstall.isEmpty) return null;
28 |
29 | _logger.info('installing ${toInstall}');
30 |
31 | return new _InstallJob(toInstall.toList()).schedule();
32 | });
33 | }
34 |
35 | class _InstallJob extends Job {
36 | final List packages;
37 | bool quitRequested = false;
38 | int errorCount = 0;
39 |
40 | _InstallJob(this.packages) : super("Installing Packages");
41 |
42 | bool get quiet => true;
43 |
44 | Future run() {
45 | packages.sort();
46 |
47 | Notification notification = atom.notifications.addInfo(name,
48 | detail: '', description: 'Installing…', dismissable: true);
49 |
50 | NotificationHelper helper = new NotificationHelper(notification.view);
51 | helper.setNoWrap();
52 | helper.setRunning();
53 |
54 | helper.appendText('Installing packages ${packages.join(', ')}.');
55 |
56 | notification.onDidDismiss.listen((_) => quitRequested = true);
57 |
58 | return Future.forEach(packages, (String name) {
59 | return _install(helper, name);
60 | }).whenComplete(() {
61 | if (errorCount == 0) {
62 | helper.showSuccess();
63 | helper.setSummary('Finished.');
64 | } else {
65 | helper.showError();
66 | helper.setSummary('Errors installing packages.');
67 | }
68 | });
69 | }
70 |
71 | Future _install(NotificationHelper helper, String name) {
72 | final String apm = atom.packages.getApmPath();
73 |
74 | ProcessRunner runner = new ProcessRunner(
75 | apm, args: ['--no-color', 'install', name]);
76 | return runner.execSimple().then((ProcessResult result) {
77 | if (result.stdout != null && result.stdout.isNotEmpty) {
78 | helper.appendText(result.stdout.trim());
79 | }
80 | if (result.stderr != null && result.stderr.isNotEmpty) {
81 | helper.appendText(result.stderr.trim(), stderr: true);
82 | }
83 | if (result.exit != 0) {
84 | errorCount++;
85 | } else {
86 | atom.packages.activatePackage(name);
87 | }
88 | });
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/lib/atom_statusbar.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | /// See the `status-bar` API [here](https://github.com/atom/status-bar).
6 | library atom.statusbar;
7 |
8 | import 'dart:js';
9 |
10 | import 'package:atom/src/js.dart';
11 |
12 | /// A wrapper around the `status-bar` API.
13 | class StatusBar extends ProxyHolder {
14 | StatusBar(JsObject obj) : super(obj);
15 |
16 | /// Add a tile to the left side of the status bar. Lower priority tiles are
17 | /// placed further to the left. The [item] parameter to these methods can be a
18 | /// DOM element, a jQuery object, or a model object for which a view provider
19 | /// has been registered in the the view registry.
20 | Tile addLeftTile({dynamic item, int priority}) {
21 | Map m = {'item': item};
22 | if (priority != null) m['priority'] = priority;
23 | return new Tile(invoke('addLeftTile', m));
24 | }
25 |
26 | /// Add a tile to the right side of the status bar. Lower priority tiles are
27 | /// placed further to the right. The [item] parameter to these methods can be
28 | /// a DOM element, a jQuery object, or a model object for which a view
29 | /// provider has been registered in the the view registry.
30 | Tile addRightTile({dynamic item, int priority}) {
31 | Map m = {'item': item};
32 | if (priority != null) m['priority'] = priority;
33 | return new Tile(invoke('addRightTile', m));
34 | }
35 |
36 | /// Retrieve all of the tiles on the left side of the status bar.
37 | List getLeftTiles() => new List.from(invoke('getLeftTiles').map((t) => new Tile(t)));
38 |
39 | /// Retrieve all of the tiles on the right side of the status bar.
40 | List getRightTiles() => new List.from(invoke('getRightTiles').map((t) => new Tile(t)));
41 | }
42 |
43 | class Tile extends ProxyHolder {
44 | Tile(JsObject obj) : super(obj);
45 |
46 | /// Retrieve the priority that was assigned to the Tile when it was created.
47 | int getPriority() => invoke('getPriority');
48 |
49 | /// Retrieve the Tile's item.
50 | dynamic getItem() => invoke('getItem');
51 |
52 | /// Remove the Tile from the status bar.
53 | void destroy() => invoke('destroy');
54 | }
55 |
--------------------------------------------------------------------------------
/lib/atom_treeview.dart:
--------------------------------------------------------------------------------
1 | library atom.treeview;
2 |
3 | import 'dart:js';
4 |
5 | import 'package:atom/src/js.dart';
6 | import 'package:atom/utils/disposable.dart';
7 |
8 | // TODO: Dispatch back to the original service?
9 |
10 | abstract class FileIconsService implements Disposable {
11 | Function _onWillDeactivate;
12 |
13 | /// Returns a CSS class name to add to the file view
14 | String iconClassForPath(String path);
15 |
16 | /// An event that lets the tree view return to its default icon strategy.
17 | void onWillDeactivate(Function fn) {
18 | _onWillDeactivate = fn;
19 | }
20 |
21 | void dispose() {
22 | if (_onWillDeactivate != null) _onWillDeactivate();
23 | }
24 |
25 | JsObject toProxy() {
26 | return jsify({
27 | 'iconClassForPath': iconClassForPath,
28 | 'onWillDeactivate': onWillDeactivate
29 | });
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/atom_utils.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library atom.atom_utils;
6 |
7 | import 'dart:async';
8 | import 'dart:html' show Element, NodeValidator;
9 |
10 | import 'package:atom/atom.dart';
11 | import 'package:atom/node/package.dart';
12 | import 'package:atom/node/process.dart';
13 |
14 | import 'state.dart';
15 |
16 | /// Return a description of Atom, the plugin, and the OS.
17 | Future getSystemDescription({bool sdkPath: false}) async {
18 | // 'Atom 1.0.11, atom-dart 0.4.3, SDK 1.12 running on Windows.'
19 | String atomVer = atom.getVersion();
20 | String os = isMac ? 'macos' : process.platform;
21 | String pluginVer = await atomPackage.getPackageVersion();
22 | String sdkVer = sdkManager.hasSdk ? await sdkManager.sdk.getVersion() : null;
23 |
24 | String description = '\n\nAtom ${atomVer}, atom-dart ${pluginVer}';
25 | if (sdkVer != null) description += ', SDK ${sdkVer}';
26 | description += ' running on ${os}.';
27 |
28 | if (sdkPath) {
29 | if (sdkManager.hasSdk) {
30 | description += '\nSDK at ${sdkManager.sdk.path}.';
31 | } else {
32 | description += '\nNo SDK configured.';
33 | }
34 | }
35 |
36 | return description;
37 | }
38 |
39 | /// A [NodeValidator] which allows everything.
40 | class PermissiveNodeValidator implements NodeValidator {
41 | bool allowsElement(Element element) => true;
42 | bool allowsAttribute(Element element, String attributeName, String value) {
43 | return true;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/lib/browser.dart:
--------------------------------------------------------------------------------
1 | library atom.browser;
2 |
3 | import 'dart:async';
4 |
5 | import 'package:atom/atom.dart';
6 | import 'package:atom/node/fs.dart';
7 | import 'package:atom/node/process.dart';
8 | import 'package:atom/utils/disposable.dart';
9 |
10 | import 'impl/debounce.dart';
11 | import 'state.dart';
12 |
13 | class BrowserManager implements Disposable {
14 | final String browserKey = '${pluginId}.browserLocation';
15 | final List subs = [];
16 |
17 | String get browserPath => atom.config.getValue(browserKey);
18 |
19 | Browser _browser;
20 | Browser get browser => _browser;
21 |
22 | String _version;
23 | String get version => _version;
24 |
25 | StreamController _onBrowserChangeController =
26 | new StreamController.broadcast(sync: true);
27 | StreamController _onVersionChangeController =
28 | new StreamController.broadcast(sync: true);
29 |
30 | Stream get onBrowserChange => _onBrowserChangeController.stream;
31 | Stream get onBrowserVersionChange =>
32 | _onVersionChangeController.stream;
33 |
34 | BrowserManager() {
35 | void update(path) {
36 | _browser = new Browser(path);
37 | _onBrowserChangeController.add(_browser);
38 | // Now get version if possible.
39 | _browser.getVersion().then((version) {
40 | _version = version;
41 | _onVersionChangeController.add(version);
42 | });
43 | }
44 |
45 | subs.add(atom.config
46 | .onDidChange(browserKey)
47 | .transform(new Debounce(new Duration(seconds: 1)))
48 | .listen(update));
49 | if (browserPath != null) {
50 | update(browserPath);
51 | }
52 | }
53 |
54 | void dispose() {
55 | subs.forEach((sub) => sub.cancel());
56 | }
57 | }
58 |
59 | class Browser {
60 | static const singletonBoolParameters = const [
61 | 'no-default-browser-check',
62 | 'no-first-run'
63 | ];
64 |
65 | final String path;
66 |
67 | Browser(this.path);
68 |
69 | Future getVersion() {
70 | if (!fs.existsSync(path) || !fs.statSync(path).isFile()) {
71 | return new Future.value(null);
72 | }
73 | if (isWindows) {
74 | return new Future.value('windows');
75 | }
76 | ProcessRunner runner =
77 | new ProcessRunner.underShell(path, args: ['--version']);
78 | runner.execStreaming();
79 |
80 | StringBuffer buf = new StringBuffer();
81 | runner.onStdout.listen((str) => buf.write(str));
82 |
83 | return runner.onExit.then((_) => buf.toString());
84 | }
85 |
86 | List execArgsFromYaml(yamlArgs, {List exceptKeys: const []}) {
87 | List execArgs = [];
88 | if (yamlArgs is Map) {
89 | yamlArgs.forEach((k, v) {
90 | if (exceptKeys.contains(k)) return;
91 | if (singletonBoolParameters.contains(k)) {
92 | if (v) execArgs.add('--$k');
93 | } else {
94 | execArgs.add('--$k=$v');
95 | }
96 | });
97 | }
98 | return execArgs;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/lib/dartino/dartino_project_settings.dart:
--------------------------------------------------------------------------------
1 | import 'package:atom/node/fs.dart';
2 | import 'package:yaml/yaml.dart';
3 |
4 | const _checkDartinoProject = 'checkDartinoProject';
5 |
6 | class DartinoProjectSettings {
7 | final Directory projectDirectory;
8 | Map _settings;
9 |
10 | DartinoProjectSettings(this.projectDirectory);
11 |
12 | /// Return `true` if the user should be prompted when checking
13 | /// to see if a project is a well formed Dartino project.
14 | bool get checkDartinoProject {
15 | return this[_checkDartinoProject] != 'false';
16 | }
17 |
18 | void set checkDartinoProject(bool value) {
19 | this[_checkDartinoProject] = value ? 'true' : 'false';
20 | }
21 |
22 | String operator [](String key) {
23 | if (_settings == null) {
24 | try {
25 | var parsed = loadYaml(_settingsFile.readSync(true));
26 | _settings = parsed is Map ? parsed : {};
27 | } catch (e) {
28 | _settings = {};
29 | }
30 | }
31 | var value = _settings[key];
32 | return value is String ? value : null;
33 | }
34 |
35 | void operator []=(String key, String value) {
36 | if (this[key] == value) return;
37 | if (value != null) {
38 | _settings[key] = value;
39 | } else {
40 | _settings.remove(key);
41 | }
42 | var buf = new StringBuffer();
43 | buf.writeln('# Dartino settings');
44 | for (String key in _settings.keys.toList()..sort()) {
45 | buf.writeln('$key: ${_settings[key]}');
46 | }
47 | _settingsFile.create().then((_) {
48 | _settingsFile.writeSync(buf.toString());
49 | });
50 | }
51 |
52 | File get _settingsFile => new File.fromPath(
53 | fs.join(projectDirectory.path, '.atom', 'dartino', 'settings.yaml'));
54 | }
55 |
--------------------------------------------------------------------------------
/lib/dartino/dartino_util.dart:
--------------------------------------------------------------------------------
1 |
2 | /// Return `true` if the specified packages file content
3 | /// contains references to Dartino packages.
4 | bool containsDartinoReferences(String content, String sdkPath) {
5 | if (content == null || sdkPath == null) return false;
6 | if (content.isEmpty || sdkPath.isEmpty) return false;
7 | String path = new Uri.file(sdkPath).toString();
8 | if (!path.startsWith('file://')) return false;
9 | for (String line in content.split('\n')) {
10 | if (line.contains(path)) return true;
11 | }
12 | return false;
13 | }
14 |
--------------------------------------------------------------------------------
/lib/dartino/device/dartuino_board.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:convert';
3 |
4 | import 'package:atom/atom.dart';
5 | import 'package:atom/node/process.dart';
6 |
7 | import '../launch_dartino.dart';
8 | import '../sdk/dartino_sdk.dart';
9 | import '../sdk/sdk.dart';
10 | import 'device.dart';
11 |
12 | /// An Dartuino board
13 | class DartuinoBoard extends Device {
14 | /// Return a target device for the given launch or `null` if none.
15 | static Future forLaunch(Sdk sdk, DartinoLaunch launch) async {
16 | //TODO(danrubel) add Windows support
17 |
18 | if (isMac || isLinux) {
19 | String ttyPath;
20 | // Old style interaction with device via TTY
21 | var stdout = await exec('ls', ['-1', '/dev']);
22 | if (stdout == null) return null;
23 | for (String line in LineSplitter.split(stdout)) {
24 | // TODO(danrubel) move this out of the plugin into dartino
25 | // and SOD command line utilities - dartino show usb devices
26 | if (line.startsWith('tty.usb') || line.startsWith('ttyUSB')) {
27 | ttyPath = '/dev/$line';
28 | // This board surfaces 2 tty ports... and only the 2nd one works
29 | // so continue looping to pick up the 2nd tty port
30 | }
31 | }
32 | if (ttyPath != null) return new DartuinoBoard(ttyPath);
33 | }
34 | return null;
35 | }
36 |
37 | final String ttyPath;
38 |
39 | DartuinoBoard(this.ttyPath);
40 |
41 | @override
42 | Future launchDartino(DartinoSdk sdk, DartinoLaunch launch) async {
43 | atom.notifications.addError('Dartino not yet supported on this board');
44 | return false;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/lib/dartino/device/device.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:atom/atom.dart';
4 | import 'package:atom/node/notification.dart';
5 | import 'package:atom_dart/dartino/sdk/sdk.dart';
6 |
7 | import '../dartino.dart';
8 | import '../launch_dartino.dart';
9 | import '../sdk/dartino_sdk.dart';
10 | import 'dartuino_board.dart';
11 | import 'local_device.dart';
12 | import 'stm32f.dart';
13 |
14 | /// The connected device on which the application is executed.
15 | abstract class Device {
16 | /// Return a target device for the given launch.
17 | /// If there is a problem or a compatible device cannot be found
18 | /// then notify the user and return `null`.
19 | static Future forLaunch(Sdk sdk, DartinoLaunch launch) async {
20 | if (dartino.devicePath == 'local') {
21 | return LocalDevice.forLaunch(sdk, launch);
22 | }
23 | Device device = await Stm32f.forLaunch(sdk, launch);
24 | if (device == null) device = await DartuinoBoard.forLaunch(sdk, launch);
25 | if (device == null) {
26 | if (dartino.devicePath.isEmpty) {
27 | atom.notifications.addError('No connected devices found.',
28 | detail: 'Please connect the device and try again.\n'
29 | ' \n'
30 | 'If the device is already connected, please set the device\n'
31 | 'path in Settings > Packages > dartino > Device Path,\n'
32 | 'and/or disconnect and reconnect the device.',
33 | buttons: [
34 | new NotificationButton('Open settings', dartino.openSettings)
35 | ]);
36 | } else {
37 | atom.notifications.addError('Device not found',
38 | detail: 'Could not find specified device:\n'
39 | '${dartino.devicePath}\n'
40 | ' \n'
41 | 'Please connect the device and try again\n'
42 | 'or change/remove the device path in\n'
43 | 'Settings > Packages > dartino > Device Path',
44 | buttons: [
45 | new NotificationButton('Open settings', dartino.openSettings)
46 | ]);
47 | }
48 | }
49 | return device;
50 | }
51 |
52 | /// Launch the specified application on the device and return `true`.
53 | /// If there is a problem, notify the user and return `false`.
54 | Future launchDartino(DartinoSdk sdk, DartinoLaunch launch);
55 | }
56 |
--------------------------------------------------------------------------------
/lib/dartino/device/local_device.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:atom/atom.dart';
4 |
5 | import '../launch_dartino.dart';
6 | import '../sdk/dartino_sdk.dart';
7 | import '../sdk/sdk.dart';
8 | import 'device.dart';
9 |
10 | /// A device for running a Dartino app on the local host/developer machine.
11 | class LocalDevice extends Device {
12 | /// Return a target device for the given launch or `null` if none.
13 | static Future forLaunch(Sdk sdk, DartinoLaunch launch) async {
14 | return new LocalDevice();
15 | }
16 |
17 | @override
18 | Future launchDartino(DartinoSdk sdk, DartinoLaunch launch) async {
19 | if (!await launch.debug(sdk, null)) {
20 | atom.notifications.addError('Failed to start debug session',
21 | detail: 'Failed to start debug session on local machine.\n'
22 | '${launch.primaryResource}\n'
23 | 'See console for more.');
24 | return false;
25 | }
26 | return true;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lib/dartino/device/stm32f.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:convert';
3 |
4 | import 'package:atom/atom.dart';
5 | import 'package:atom/node/fs.dart';
6 | import 'package:atom/node/process.dart';
7 |
8 | import '../launch_dartino.dart';
9 | import '../sdk/dartino_sdk.dart';
10 | import '../sdk/sdk.dart';
11 | import 'device.dart';
12 |
13 | /// An STM32F Discovery or Nucleo board
14 | class Stm32f extends Device {
15 | //TODO(danrubel) generalize STM boards and hopefully connected devices in general
16 |
17 | /// Return a target device for the given launch or `null` if none.
18 | static Future forLaunch(Sdk sdk, DartinoLaunch launch) async {
19 | //TODO(danrubel) move this into the command line utility
20 | //TODO(danrubel) add Windows support
21 | String ttyPath;
22 | String mediaPath;
23 |
24 | if (isMac || isLinux) {
25 | var stdout = await exec('ls', ['-1', '/dev']);
26 | if (stdout == null) return null;
27 | for (String line in LineSplitter.split(stdout)) {
28 | // TODO(danrubel) move this out of the plugin into dartino
29 | // and SOD command line utilities - dartino show usb devices
30 | if (line.startsWith('tty.usb') || line.startsWith('ttyACM')) {
31 | ttyPath = '/dev/$line';
32 | break;
33 | }
34 | }
35 | }
36 |
37 | for (String mediaName in [
38 | 'DIS_F746NG', // STM32F746 Discovery
39 | 'NODE_F411RE', // STM32F411 Nucleo
40 | ]) {
41 | if (isMac) {
42 | mediaPath = '/Volumes/$mediaName';
43 | }
44 | if (isLinux) {
45 | var stdout = await exec('df');
46 | if (stdout == null) return null;
47 | for (String line in LineSplitter.split(stdout)) {
48 | if (line.endsWith('/$mediaName')) {
49 | mediaPath = line.substring(line.lastIndexOf(' /') + 1);
50 | break;
51 | }
52 | }
53 | }
54 |
55 | if (mediaPath != null ||
56 | !fs.existsSync('$mediaPath/MBED.HTM') ||
57 | !fs.existsSync('$mediaPath/mbed.htm')) {
58 | return new Stm32f(ttyPath, mediaPath);
59 | }
60 | }
61 | return null;
62 | }
63 |
64 | final String ttyPath;
65 | final String mediaPath;
66 |
67 | Stm32f(this.ttyPath, this.mediaPath);
68 |
69 | @override
70 | Future launchDartino(DartinoSdk sdk, DartinoLaunch launch) async {
71 | //TODO(danrubel) add windows support and move this into cmdline util
72 | if (isWindows) {
73 | atom.notifications.addError('Platform not supported');
74 | return false;
75 | }
76 |
77 | // TODO(danrubel) use the code below
78 | // rather than `launch.launchConfiguration.debug`
79 | // because we want `debug` to default `false`
80 | // until this new feature is ready.
81 | var debug = launch.launchConfiguration.typeArgs['debug'];
82 | if (debug is! bool) debug = false;
83 |
84 | // Compile, deploy, and run
85 | var args = ['flash', launch.primaryResource];
86 | if (debug) args.insert(1, '--debugging-mode');
87 | var exitCode = await launch.run(sdk.dartinoBinary,
88 | args: args,
89 | message: 'Compile and deploy to connected device ...',
90 | isLast: !debug);
91 | if (exitCode != 0) {
92 | atom.notifications.addError('Failed to deploy application',
93 | detail: 'Failed to deploy to device.\n'
94 | '${launch.primaryResource}\n'
95 | 'See console for more.');
96 | launch.launchTerminated(1, quiet: true);
97 | return false;
98 | }
99 |
100 | // If debugging then connect and start a debug session
101 | if (debug && !await launch.debug(sdk, ttyPath)) {
102 | atom.notifications.addError('Failed to start debug session',
103 | detail: 'Failed to start debug session on device.\n'
104 | '${launch.primaryResource}\n'
105 | 'See console for more.');
106 | return false;
107 | }
108 | return true;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/lib/dartino/sdk/sdk.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:atom/atom.dart';
4 | import 'package:atom/node/fs.dart';
5 | import 'package:atom/node/notification.dart';
6 | import 'package:atom/node/process.dart';
7 |
8 | import '../launch_dartino.dart';
9 |
10 | /// Abstract SDK implementation used by Dartino.
11 | /// Clients should call .forPath to instantiate a new SDK
12 | /// then further call [validate] to verify that the SDK is valid.
13 | abstract class Sdk {
14 | /// The root path of the sdk
15 | final String sdkRoot;
16 |
17 | Sdk(this.sdkRoot);
18 |
19 | String get name;
20 |
21 | /// Return a string representing the Sdk version, or `null` if unknown.
22 | Future get version => null;
23 |
24 | /// Return the path to the Dart SDK
25 | /// that is shipped as part of the Dartino SDK
26 | String get dartSdkPath => fs.join(sdkRoot, 'internal', 'dart-sdk');
27 |
28 | /// Return the path to the root directory of the samples
29 | /// or `null` if none.
30 | String get samplesRoot;
31 |
32 | /// Create a new project at the specified location.
33 | /// Return a [Future] that indicates whether the project was created.
34 | Future createNewProject(String projectPath);
35 |
36 | /// Execute the given SDK binary (a command in the `bin/` folder). [cwd] can
37 | /// be either a [String] or a [Directory].
38 | ProcessRunner execBin(String binName, List args,
39 | {cwd, bool startProcess: true}) {
40 | if (cwd is Directory) cwd = cwd.path;
41 | String osBinName = isWindows ? '${binName}.bat' : binName;
42 | String command = fs.join(sdkRoot, 'bin', osBinName);
43 |
44 | ProcessRunner runner =
45 | new ProcessRunner.underShell(command, args: args, cwd: cwd);
46 | if (startProcess) runner.execStreaming();
47 | return runner;
48 | }
49 |
50 | /// Return `true` if the specified file exists in the SDK
51 | bool existsSync(String relativePosixPath) {
52 | var path = resolvePath(relativePosixPath);
53 | return path != null && fs.existsSync(path);
54 | }
55 |
56 | /// Return a path to the `.packages` file used to analyze the specified
57 | /// project or `null` if none,
58 | /// where [projDir] may be a [Directory] or a directory path.
59 | String packageRoot(projDir);
60 |
61 | /// Compile, deploy, and launch the specified application.
62 | /// Return a [Future] that completes when the application has been launched.
63 | Future launch(DartinoLaunch launch);
64 |
65 | /// Return the absolute OS specific path for the file or directory specified by
66 | /// [relativePosixPath] in the SDK, or `null` if there is a problem.
67 | String resolvePath(String relativePosixPath) {
68 | if (sdkRoot == null || sdkRoot.trim().isEmpty) return null;
69 | return fs.join(sdkRoot, relativePosixPath.replaceAll('/', fs.separator));
70 | }
71 |
72 | /// Return `true` if this is a valid SDK installation,
73 | /// otherwise notify the user of the problem and return `false`.
74 | /// Set `quiet: true` to supress any user notifications.
75 | bool validate({bool quiet: false});
76 |
77 | /// Prompt the user for and return a location to install the SDK.
78 | /// The default text will be the user's home directory plus [relPosixPath]
79 | /// where [relPosixPath] is translated into an OS specific path.
80 | /// If the selected directory already exists,
81 | /// then notify the user an return `null`.
82 | static Future promptInstallPath(
83 | String sdkName, String relPosixPath) async {
84 | var relPath = relPosixPath.replaceAll('/', fs.separator);
85 | String path = await promptUser('Enter $sdkName installation path',
86 | defaultText: fs.join(fs.homedir, relPath), selectLastWord: true);
87 | if (path == null) return null;
88 | path = path.trim();
89 | if (path.isEmpty) return null;
90 | if (fs.existsSync(path)) {
91 | atom.notifications.addError('Invalid installation location',
92 | detail: 'The installation directory already exists.\n$path');
93 | return null;
94 | }
95 | return path;
96 | }
97 |
98 | /// If the user has not already choosen to opt into (or out of) analytics
99 | /// then prompt the user to do so.
100 | void promptOptIntoAnalytics();
101 |
102 | /// Show documentation for the installed SDK.
103 | void showDocs();
104 | }
105 |
--------------------------------------------------------------------------------
/lib/debug/debugger_tooltip.dart:
--------------------------------------------------------------------------------
1 | library atom.debugger_tooltip;
2 |
3 | import 'dart:async';
4 | import 'dart:math' as math;
5 |
6 | import 'package:atom/node/workspace.dart';
7 | import 'package:atom/utils/disposable.dart';
8 | import 'package:logging/logging.dart';
9 |
10 | import '../analysis_server.dart';
11 | import '../elements.dart';
12 | import '../impl/tooltip.dart';
13 | import '../material.dart';
14 | import '../state.dart';
15 |
16 | import './debugger_ui.dart';
17 | import './evaluator.dart';
18 | import './model.dart';
19 |
20 | final Logger _logger = new Logger('atom.debugger_tooltip');
21 |
22 | class DebugTooltipManager implements Disposable {
23 | EvaluatorReverseParser parser = new EvaluatorReverseParser();
24 |
25 | DebugTooltipManager();
26 |
27 | // This is how many characters back at most we go to get the full context.
28 | static const backwardBuffer = 160;
29 |
30 | Future check(TooltipElement tooltip) async {
31 | HoverInformation info = tooltip.info;
32 | TextEditor editor = tooltip.editor;
33 |
34 | int startOffset = math.max(0, info.offset - backwardBuffer);
35 | int endOffset = info.offset + info.length;
36 | String input = parser.reverseString(editor.getTextInBufferRange(
37 | new Range.fromPoints(
38 | editor.getBuffer().positionForCharacterIndex(startOffset),
39 | editor.getBuffer().positionForCharacterIndex(endOffset))));
40 |
41 | for (var connection in debugManager.connections) {
42 | if (!connection.isAlive) continue;
43 |
44 | DebugVariable result;
45 | try {
46 | // Parse in reverse from cursor, then let the evaluator unparse it
47 | // adding custom context information if needed.
48 | var expression = parser.parse(input, endOffset);
49 | // Let actual debugger to the eval.
50 | result = await connection
51 | .eval(new EvalExpression(editor.getPath(), expression));
52 | if (result == null) return;
53 | } catch (e) {
54 | _logger.warning(e);
55 | break;
56 | }
57 |
58 | // If we have anything we create the MTree to display it in the tooltip.
59 | MTree row =
60 | new MTree(new LocalTreeModel(), ExecutionTab.renderVariable)
61 | ..flex()
62 | ..toggleClass('has-debugger-data');
63 |
64 | row.selectedItem.onChanged.listen((variable) async {
65 | if (variable == null) return;
66 | await variable.value.invokeToString();
67 | await row.updateItem(variable);
68 | });
69 |
70 | await row.update([result]);
71 |
72 | String clazz = 'debugger-data';
73 | // TODO: this might change after eval of a getter
74 | if (!result?.value?.isPrimitive ?? false) clazz += ' expandable';
75 | tooltip.expand(div(c: clazz)..add(row));
76 | }
77 | }
78 |
79 | bool get hasOpenedConnection => debugManager.connections.any((c) => c.isAlive);
80 |
81 | void dispose() {}
82 | }
83 |
--------------------------------------------------------------------------------
/lib/debug/evaluator.dart:
--------------------------------------------------------------------------------
1 | library atom.evaluator;
2 |
3 | import 'dart:async';
4 |
5 | import 'package:petitparser/petitparser.dart';
6 |
7 | import '../utils.dart';
8 |
9 | class EvaluatorReverseParser {
10 | final Property endOffset = new Property();
11 |
12 | Parser reverseContextParser;
13 |
14 | EvaluatorReverseParser() {
15 | SettableParser expression = undefined();
16 | SettableParser index = undefined();
17 | SettableParser ref = undefined();
18 | SettableParser id = undefined();
19 |
20 | Parser trim(String c) => char(c).trim();
21 | Parser tagged(Parser p) => new TagPositionParser(p, this.endOffset);
22 |
23 | // This is the simple dart sub-grammar we handle for now, backward:
24 | // expression :: ref ('.' ref)*
25 | // backward -> (ref '.')* ref
26 | expression.set((ref & trim('.')).star() & ref);
27 | // index :: '[' expression | number ']' e
28 | // backward -> ']' expression | number '['
29 | index.set(trim(']') & (expression | digit()) & trim('['));
30 | // ref :: identifier [ index ]
31 | // backward -> [ index ] identifier
32 | ref.set(index.optional() & tagged(id));
33 |
34 | id.set(((letter() | char('_')) & (letter() | digit() | char('_')).star())
35 | .flatten());
36 |
37 | reverseContextParser = expression;
38 | }
39 |
40 | // Put it back in flowing order.
41 | dynamic reverseResult(dynamic value) {
42 | if (value is String) {
43 | return reverseString(value);
44 | } else if (value is List) {
45 | return value.reversed.map((v) => reverseResult(v)).toList();
46 | } else {
47 | return value;
48 | }
49 | }
50 |
51 | String reverseString(String input) =>
52 | new String.fromCharCodes(input.codeUnits.reversed);
53 |
54 | dynamic parse(String input, int endOffset) {
55 | this.endOffset.value = endOffset;
56 | return reverseResult(reverseContextParser.parse(input).value);
57 | }
58 | }
59 |
60 | class EvalExpression {
61 | String filePath;
62 |
63 | /// This is the simple dart sub-grammar we handle for now:
64 | /// expression :: ref ('.' ref)*
65 | /// ref :: identifier [ index ]
66 | /// index :: '[' expression | number ']'
67 | /// expression is in a List tree generate by petitparser.
68 | dynamic expression;
69 |
70 | EvalExpression(this.filePath, this.expression);
71 | }
72 |
73 | class Evaluator {
74 | final EvalExpression expression;
75 |
76 | Evaluator(this.expression);
77 |
78 | Future eval() async => visitExpression(expression.expression);
79 |
80 | Future visitExpression(dynamic expression) async {
81 | if (expression is String) return expression;
82 | if (expression is! List || expression.isEmpty) return '';
83 | List parts = [];
84 | parts.add(await visitFirstReference(expression[0]));
85 | for (var sub in expression[1]) {
86 | parts.add(await visitNextReference(sub));
87 | }
88 | return parts.join();
89 | }
90 |
91 | Future visitFirstReference(dynamic expression) async =>
92 | visitReference(true, expression);
93 |
94 | Future visitNextReference(dynamic expression) async {
95 | if (expression is String) return expression;
96 | if (expression is! List || expression.isEmpty) return '';
97 | String right = await visitReference(false, expression[1]);
98 | return '.$right';
99 | }
100 |
101 | Future visitReference(bool first, dynamic expression) async {
102 | if (expression is String) return expression;
103 | if (expression is! List || expression.isEmpty) return '';
104 | String left = await visitReferenceIdentifier(first, expression[0]);
105 | String right = await visitIndex(expression[1]);
106 | return '$left$right';
107 | }
108 |
109 | Future visitReferenceIdentifier(
110 | bool first, dynamic expression) async {
111 | if (expression is String) return expression;
112 | if (expression is! List || expression.isEmpty) return '';
113 | return mapReferenceIdentifier(first, expression[1], expression[0]);
114 | }
115 |
116 | /// For example, here we would override this in a js debugger to add 'this'
117 | /// to the first (leftmost) identifier if needed.
118 | Future mapReferenceIdentifier(
119 | bool first, int offset, String identifier) async {
120 | return identifier;
121 | }
122 |
123 | Future visitIndex(dynamic expression) async {
124 | if (expression is String) return expression;
125 | if (expression is! List || expression.isEmpty) return '';
126 | String inner = await visitExpression(expression[1]);
127 | return '[$inner]';
128 | }
129 | }
130 |
131 | class TagPositionParser extends DelegateParser {
132 | final Property endOffset;
133 |
134 | TagPositionParser(Parser delegate, this.endOffset) : super(delegate);
135 |
136 | @override
137 | Result parseOn(Context context) {
138 | var result = delegate.parseOn(context);
139 | if (result.isSuccess) {
140 | return result.success([
141 | endOffset.value - context.position - result.value.length + 1,
142 | result.value
143 | ]);
144 | } else {
145 | return result;
146 | }
147 | }
148 |
149 | @override
150 | Parser copy() => new TagPositionParser(delegate, endOffset);
151 | }
152 |
--------------------------------------------------------------------------------
/lib/debug/model.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import '../launch/launch.dart';
4 | import '../utils.dart';
5 | import './evaluator.dart';
6 |
7 | export './evaluator.dart' show EvalExpression;
8 |
9 | abstract class DebugConnection {
10 | final Launch launch;
11 | final Property metadata = new Property();
12 |
13 | final SelectionGroup isolates = new SelectionGroup();
14 |
15 | DebugConnection(this.launch);
16 |
17 | List get options => [];
18 |
19 | bool get isAlive;
20 |
21 | Stream get onPaused;
22 | Stream get onResumed;
23 |
24 | // Optional
25 | Stream> get onLibrariesChanged => null;
26 |
27 | Future terminate();
28 |
29 | Future get onTerminated;
30 |
31 | Future resume();
32 | stepIn();
33 | stepOver();
34 | stepOut();
35 | stepOverAsyncSuspension();
36 | autoStepOver();
37 |
38 | void dispose();
39 |
40 | Future eval(EvalExpression expression);
41 | }
42 |
43 | abstract class DebugOption {
44 | String get label;
45 |
46 | bool get checked;
47 | set checked(bool state);
48 | }
49 |
50 | // TODO: Add an IsolateState class.
51 |
52 | /// A representation of a VM Isolate.
53 | abstract class DebugIsolate extends MItem {
54 | DebugIsolate();
55 |
56 | String get id => name;
57 |
58 | String get name;
59 |
60 | /// Return a more human readable name for the Isolate.
61 | String get displayName => name;
62 |
63 | String get detail;
64 |
65 | bool get suspended;
66 |
67 | bool get hasFrames => frames != null && frames.isNotEmpty;
68 |
69 | List get frames;
70 |
71 | List get libraries;
72 |
73 | pause();
74 | Future resume();
75 | stepIn();
76 | stepOver();
77 | stepOut();
78 | stepOverAsyncSuspension();
79 | autoStepOver();
80 | }
81 |
82 | abstract class DebugFrame extends MItem {
83 | DebugFrame();
84 |
85 | String get id => title;
86 |
87 | String get title;
88 |
89 | bool get isSystem;
90 | bool get isExceptionFrame;
91 |
92 | List get locals;
93 |
94 | Future> resolveLocals() => new Future.value(locals);
95 |
96 | DebugLocation get location;
97 |
98 | Future eval(String expression);
99 |
100 | String toString() => title;
101 | }
102 |
103 | abstract class DebugVariable extends MItem {
104 | String get id => name;
105 |
106 | String get name;
107 | DebugValue get value;
108 |
109 | String toString() => name;
110 | }
111 |
112 | abstract class DebugValue {
113 | String get className;
114 |
115 | String get valueAsString;
116 |
117 | bool get isPrimitive;
118 | bool get isString;
119 | bool get isPlainInstance;
120 | bool get isList;
121 | bool get isMap;
122 |
123 | bool get valueIsTruncated;
124 |
125 | int get itemsLength;
126 |
127 | bool get replaceValueOnEval;
128 |
129 | String get hint {
130 | if (isString) {
131 | // We choose not to escape double quotes here; it doesn't work well visually.
132 | String str = valueAsString;
133 | return valueIsTruncated ? '"$str…' : '"$str"';
134 | } else if (isList) {
135 | return '[ $itemsLength ]';
136 | } else if (isMap) {
137 | return '{ $itemsLength }';
138 | } else if (itemsLength != null) {
139 | return '$className [ $itemsLength ]';
140 | } else if (isPlainInstance) {
141 | return className;
142 | } else {
143 | return valueAsString;
144 | }
145 | }
146 |
147 | Future> getChildren();
148 |
149 | Future invokeToString();
150 |
151 | String toString() => valueAsString;
152 | }
153 |
154 | abstract class DebugLocation {
155 | /// A file path.
156 | String get path;
157 |
158 | /// 1-based line number.
159 | int get line;
160 |
161 | /// 1-based column number.
162 | int get column;
163 |
164 | /// A display file path.
165 | String get displayPath;
166 |
167 | bool resolved = false;
168 |
169 | DebugLocation();
170 |
171 | Future resolve();
172 |
173 | String toString() => '${path} ${line}:${column}';
174 | }
175 |
176 | abstract class DebugLibrary extends MItem implements Comparable {
177 |
178 | String get id;
179 |
180 | String get name;
181 | String get uri;
182 |
183 | String get displayUri;
184 |
185 | bool get private;
186 |
187 | DebugLocation get location;
188 |
189 | int compareTo(other) {
190 | return displayUri.compareTo(other.displayUri);
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/lib/debug/observatory.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:vm_service_lib/vm_service_lib.dart';
4 |
5 | import 'observatory_debugger.dart';
6 |
7 | export 'observatory_debugger.dart' show ObservatoryIsolate;
8 |
9 | abstract class ServiceWrapper {
10 | VmService get service;
11 |
12 | Iterable get allIsolates;
13 |
14 | Stream get onIsolateCreated;
15 | Stream get onIsolateFinished;
16 | }
17 |
--------------------------------------------------------------------------------
/lib/debug/utils.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:atom/atom.dart';
3 | import 'package:atom/node/fs.dart';
4 | import 'package:atom/node/workspace.dart';
5 |
6 | /// [line] and [column] are 1-based.
7 | Range debuggerCoordsToEditorRange(int line, int column) {
8 | int l = line - 1;
9 | int c = column == null ? 0 : column - 1;
10 |
11 | return new Range.fromPoints(
12 | new Point.coords(l, c), new Point.coords(l, c + 1)
13 | );
14 | }
15 |
16 | LineColumn editorRangeToDebuggerCoords(Range range) {
17 | Point p = range.start;
18 | return new LineColumn(p.row + 1, p.column + 1);
19 | }
20 |
21 | String getDisplayUri(String uri) {
22 | if (uri == null) return null;
23 |
24 | if (uri.startsWith('file:')) {
25 | String path = Uri.parse(uri).toFilePath();
26 | return atom.project.relativizePath(path)[1];
27 | } else if (fs.existsSync(uri)) {
28 | return atom.project.relativizePath(uri)[1];
29 | } else if (uri.startsWith('packages/')) {
30 | return 'package:${uri.substring(9)}';
31 | }
32 |
33 | return uri;
34 | }
35 |
36 | class LineColumn {
37 | final int line;
38 | final int column;
39 |
40 | LineColumn(this.line, this.column);
41 | }
42 |
--------------------------------------------------------------------------------
/lib/debug/websocket.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:js';
3 |
4 | import 'package:atom/node/node.dart';
5 |
6 | // TODO: This class should move into `package:atom`.
7 |
8 | class WebSocket {
9 | static JsFunction _WebSocket = require('ws');
10 |
11 | JsObject _ws;
12 |
13 | WebSocket(String url) {
14 | // TODO(devoncarew): We could also pass in the origin here.
15 | _ws = _WebSocket.apply([url]);
16 | }
17 |
18 | Stream get onOpen {
19 | StreamController controller = new StreamController.broadcast();
20 | _ws.callMethod('on', ['open', () => controller.add(null)]);
21 | return controller.stream;
22 | }
23 |
24 | Stream get onMessage {
25 | StreamController controller = new StreamController.broadcast();
26 | _ws.callMethod('on', ['message', (data, flags) {
27 | controller.add(new MessageEvent(data, flags));
28 | }]);
29 | return controller.stream;
30 | }
31 |
32 | Stream get onError {
33 | StreamController controller = new StreamController.broadcast();
34 | _ws.callMethod('on', ['error', (event) => controller.add(event)]);
35 | return controller.stream;
36 | }
37 |
38 | Stream get onClose {
39 | StreamController controller = new StreamController.broadcast();
40 | _ws.callMethod('on', ['close', (code, message) => controller.add(code)]);
41 | return controller.stream;
42 | }
43 |
44 | void send(String data) {
45 | _ws.callMethod('send', [data]);
46 | }
47 |
48 | void close() {
49 | _ws.callMethod('close');
50 | }
51 | }
52 |
53 | class MessageEvent {
54 | final dynamic data;
55 | final dynamic flags;
56 |
57 | MessageEvent(this.data, this.flags);
58 | }
59 |
--------------------------------------------------------------------------------
/lib/error_repository.dart:
--------------------------------------------------------------------------------
1 | library atom.error_repository;
2 |
3 | import 'dart:async';
4 |
5 | import 'package:atom/node/fs.dart';
6 | import 'package:atom/utils/disposable.dart';
7 | import 'package:logging/logging.dart';
8 |
9 | import 'package:analysis_server_lib/analysis_server_lib.dart'
10 | show AnalysisErrors, AnalysisError, AnalysisFlushResults;
11 | import 'utils.dart';
12 |
13 | final Logger _logger = new Logger('error_repository');
14 |
15 | /// Repository of errors generated by `analysis.errors` events.
16 | ///
17 | /// One-stop shop for getting the status of errors the analyzer has reported.
18 | /// Source agonostic.
19 | class ErrorRepository implements Disposable {
20 | static const List _emptyErrors = const [];
21 |
22 | /// A collection of all known errors that the analysis_server has provided us,
23 | /// organized by filename.
24 | final Map> knownErrors = {};
25 |
26 | final StreamSubscriptions subs = new StreamSubscriptions();
27 |
28 | StreamController _changeController = new StreamController.broadcast();
29 | Stream _errorStream;
30 | Stream _flushStream;
31 |
32 | ErrorRepository();
33 |
34 | Stream get onChange => _changeController.stream;
35 |
36 | void initStreams(Stream errorStream,
37 | Stream flushStream) {
38 | this._errorStream = errorStream;
39 | this._flushStream = flushStream;
40 |
41 | subs.cancel();
42 |
43 | subs.add(_errorStream.listen(_handleAddErrors));
44 | subs.add(_flushStream.listen(_handleFlushErrors));
45 | }
46 |
47 | /// Clear all known errors. This is useful for situations like when the
48 | /// analysis server goes down.
49 | void clearAll() {
50 | knownErrors.clear();
51 | _changeController.add(null);
52 | }
53 |
54 | /// Clear all errors for files contained within the given directory.
55 | void clearForDirectory(Directory dir) {
56 | List paths = knownErrors.keys.toList();
57 | for (String path in paths) {
58 | if (dir.contains(path)) knownErrors.remove(path);
59 | }
60 | }
61 |
62 | List getForPath(String path) => knownErrors[path];
63 |
64 | void dispose() => subs.cancel();
65 |
66 | void _handleAddErrors(AnalysisErrors analysisErrors) {
67 | String path = analysisErrors.file;
68 | File file = new File.fromPath(path);
69 |
70 | // We use statSync() here and not file.isFile() as File.isFile() always
71 | // returns true.
72 | if (file.existsSync() && fs.statSync(path).isFile()) {
73 | var oldErrors = knownErrors[path];
74 | var newErrors = analysisErrors.errors;
75 |
76 | if (oldErrors == null) oldErrors = _emptyErrors;
77 | if (newErrors == null) newErrors = _emptyErrors;
78 |
79 | knownErrors[path] = analysisErrors.errors;
80 |
81 | if (!listIdentical(oldErrors, newErrors)) {
82 | _changeController.add(null);
83 | }
84 | } else {
85 | _logger.info('received an error event for a non-existent file: ${path}');
86 | }
87 | }
88 |
89 | void _handleFlushErrors(AnalysisFlushResults analysisFlushResults) {
90 | analysisFlushResults.files.forEach(knownErrors.remove);
91 | _changeController.add(null);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/lib/flutter/flutter.dart:
--------------------------------------------------------------------------------
1 | import 'package:atom/atom.dart';
2 | import 'package:atom_dart/sdk.dart';
3 | import 'package:logging/logging.dart';
4 | import 'package:pub_semver/pub_semver.dart';
5 |
6 | final Logger _logger = new Logger('flutter');
7 |
8 | final Flutter flutter = new Flutter();
9 |
10 | class Flutter {
11 | static bool hasFlutterPlugin() {
12 | return atom.packages.getAvailablePackageNames().contains('flutter');
13 | }
14 |
15 | static void setMinSdkVersion() {
16 | if (hasFlutterPlugin()) {
17 | SdkManager.minVersion = new Version.parse('1.15.0');
18 | }
19 | }
20 |
21 | /// Called by the Flutter plugin to enable Flutter specific behavior.
22 | void enable([AtomEvent _]) {
23 | if (!hasFlutterPlugin()) return;
24 | _logger.info('Flutter features enabled');
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/flutter/flutter_connect.dart:
--------------------------------------------------------------------------------
1 | import 'package:atom/atom.dart';
2 | import 'package:atom/node/fs.dart';
3 | import 'package:atom/utils/disposable.dart';
4 | import 'package:atom/utils/string_utils.dart';
5 |
6 | import '../elements.dart';
7 | import '../launch/launch.dart';
8 | import '../launch/launch_configs.dart';
9 | import '../projects.dart';
10 | import '../state.dart';
11 | import 'flutter_daemon.dart';
12 | import 'flutter_devices.dart';
13 | import 'flutter_launch.dart';
14 |
15 | /// Connect the tools to a Flutter app that is already running on a device.
16 | class FlutterConnectManager implements Disposable {
17 | Disposables _disposables = new Disposables();
18 |
19 | ConnectDialog connectDialog;
20 |
21 | void showConnectDialog() {
22 | if (connectDialog == null) {
23 | connectDialog = new ConnectDialog();
24 | _disposables.add(connectDialog);
25 | }
26 | connectDialog.show();
27 | }
28 |
29 | void dispose() => _disposables.dispose();
30 | }
31 |
32 | class ConnectDialog implements Disposable {
33 | TitledModelDialog dialog;
34 | CoreElement _listGroup;
35 | CoreElement itemCount;
36 |
37 | ConnectDialog() {
38 | dialog = new TitledModelDialog('Connect Debugger to Remote Flutter App:', classes: 'list-dialog');
39 | dialog.content.add([
40 | div(c: 'select-list')..add([_listGroup = ol(c: 'list-group')]),
41 | itemCount = div(text: 'Looking for apps…')
42 | ]);
43 | }
44 |
45 | void show() {
46 | _listGroup.clear();
47 | dialog.show();
48 |
49 | FlutterDaemon daemon = deps[FlutterDaemonManager].daemon;
50 | FlutterDeviceManager flutterDeviceManager = deps[FlutterDeviceManager];
51 |
52 | if (flutterDeviceManager.currentSelectedDevice == null) {
53 | dialog.hide();
54 | atom.notifications.addInfo('No Flutter devices found.');
55 | return;
56 | }
57 |
58 | String deviceId = flutterDeviceManager.currentSelectedDevice.id;
59 |
60 | DaemonRequestJob job = new DaemonRequestJob('Discovering Flutter Apps', () {
61 | itemCount.text = 'Looking for apps…';
62 |
63 | return daemon.app.discover(deviceId)
64 | .then(_updateApps)
65 | .catchError((e) {
66 | itemCount.text = 'No apps detected.';
67 | throw e;
68 | });
69 | });
70 | job.schedule();
71 | }
72 |
73 | void _handleAppClick(DiscoveredApp app) {
74 | dialog.hide();
75 |
76 | DartProject project = projectManager.getProjectFor(
77 | atom.workspace.getActiveTextEditor()?.getPath());
78 | if (project == null) {
79 | atom.notifications.addWarning('No active project.');
80 | return;
81 | }
82 |
83 | FlutterLaunchType launchType = launchManager.getLaunchType('flutter');
84 | List configs = project == null ?
85 | [] : launchConfigurationManager.getConfigsForProject(project.path);
86 |
87 | if (configs.isNotEmpty) {
88 | launchType.connectToApp(project, configs.first, app.observatoryPort);
89 | } else {
90 | // Find lib/main.dart; create a launch config for it.
91 | String mainPath = fs.join(project.path, 'lib/main.dart');
92 | if (fs.existsSync(mainPath)) {
93 | LaunchData data = new LaunchData(fs.readFileSync(mainPath));
94 |
95 | if (launchType.canLaunch(project.path, data)) {
96 | LaunchConfiguration config = launchConfigurationManager.createNewConfig(
97 | project.path,
98 | launchType.type,
99 | 'lib/main.dart',
100 | launchType.getDefaultConfigText()
101 | );
102 | launchType.connectToApp(project, config, app.observatoryPort);
103 | } else {
104 | atom.notifications.addWarning('The current project is not a runnable Flutter project.');
105 | }
106 | } else {
107 | atom.notifications.addWarning('The current project is not a runnable Flutter project.');
108 | }
109 | }
110 | }
111 |
112 | void _updateApps(List apps) {
113 | for (DiscoveredApp app in apps) {
114 | CoreElement item = li(c: 'item-container select-item')
115 | ..layoutHorizontal()
116 | ..add([
117 | div()
118 | ..inlineBlock()
119 | ..flex()
120 | ..text = app.id
121 | ..click(() => _handleAppClick(app))
122 | ]);
123 | _listGroup.add(item);
124 | }
125 |
126 | if (apps.isEmpty) {
127 | itemCount.text = 'No apps detected.';
128 | } else {
129 | itemCount.text = '${apps.length} ${pluralize('app', apps.length)} detected.';
130 | }
131 | }
132 |
133 | void dispose() => dialog.dispose();
134 | }
135 |
--------------------------------------------------------------------------------
/lib/flutter/flutter_devices.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'dart:async';
3 | import 'dart:collection' show LinkedHashSet;
4 |
5 | import 'package:atom/atom.dart';
6 | import 'package:atom/utils/dependencies.dart';
7 | import 'package:atom/utils/disposable.dart';
8 |
9 | import '../state.dart';
10 | import 'flutter_daemon.dart';
11 |
12 | export 'flutter_daemon.dart' show Device;
13 |
14 | class FlutterDeviceManager implements Disposable {
15 | /// Flutter run / build modes.
16 | static List runModes = [
17 | new BuildMode('debug', startPaused: true),
18 | new BuildMode('profile'),
19 | new BuildMode('release', supportsDebugging: false)
20 | ];
21 |
22 | StreamSubscriptions subs = new StreamSubscriptions();
23 |
24 | StreamController _selectedController = new StreamController.broadcast();
25 | StreamController> _devicesController = new StreamController.broadcast();
26 | StreamController _modeController = new StreamController.broadcast();
27 |
28 | Device _selectedDevice;
29 | LinkedHashSet _devices = new LinkedHashSet();
30 |
31 | BuildMode _runMode = runModes.first;
32 |
33 | FlutterDeviceManager() {
34 | _updateForDaemon(_daemonManager.daemon);
35 | subs.add(_daemonManager.onDaemonAvailable.listen(_updateForDaemon));
36 | }
37 |
38 | BuildMode get runMode => _runMode;
39 |
40 | set runMode(BuildMode mode) {
41 | _runMode = mode;
42 | _modeController.add(_runMode);
43 | }
44 |
45 | void _updateForDaemon(FlutterDaemon daemon) {
46 | if (daemon == null) {
47 | // Clear devices.
48 | _devices.clear();
49 | _devicesController.add(devices);
50 |
51 | _validateSelection();
52 | } else {
53 | // query devices.
54 | _daemonManager.getDevices().then((List result) {
55 | _devices.clear();
56 | _devices.addAll(result);
57 | _devicesController.add(devices);
58 |
59 | _validateSelection();
60 | });
61 |
62 | // Listen for changes.
63 | subs.add(_daemonManager.onDeviceAdded.listen(_handleDeviceAdd));
64 | subs.add(_daemonManager.onDeviceChanged.listen(_handleDeviceChanged));
65 | subs.add(_daemonManager.onDeviceRemoved.listen(_handleDeviceRemoved));
66 | }
67 | }
68 |
69 | bool get isManagerActive => _daemonManager.daemon != null;
70 |
71 | Stream get onManagerActiveChanged => _daemonManager.onDaemonAvailable.map((daemon) => daemon != null);
72 |
73 | Stream get onSelectedChanged => _selectedController.stream;
74 |
75 | Stream> get onDevicesChanged => _devicesController.stream;
76 |
77 | Stream get onModeChanged => _modeController.stream;
78 |
79 | Device get currentSelectedDevice => _selectedDevice;
80 |
81 | List get devices => _devices.toList();
82 |
83 | void setSelectedDeviceIndex(int index) {
84 | if (index >= 0 && index < _devices.length) {
85 | _selectedDevice = _devices.toList()[index];
86 | _selectedController.add(_selectedDevice);
87 | }
88 | }
89 |
90 | void dispose() {
91 | subs.cancel();
92 | }
93 |
94 | void _handleDeviceAdd(Device device) {
95 | atom.notifications.addSuccess("Found ${device.getLabel()}.");
96 |
97 | _devices.add(device);
98 | _devicesController.add(devices);
99 |
100 | _validateSelection();
101 | }
102 |
103 | void _handleDeviceChanged(Device device) {
104 | _devices.add(device);
105 |
106 | // If the IDs are the same, we replace the device object as the new one might
107 | // have updated information.
108 | if (_selectedDevice == device) {
109 | _selectedDevice = device;
110 | }
111 |
112 | _validateSelection();
113 | }
114 |
115 | void _handleDeviceRemoved(Device device) {
116 | atom.notifications.addInfo("${device.getLabel()} removed.");
117 |
118 | _devices.remove(device);
119 | _devicesController.add(devices);
120 |
121 | _validateSelection();
122 | }
123 |
124 | void _validateSelection() {
125 | Device selected = _selectedDevice;
126 |
127 | if (_devices.isEmpty) selected = null;
128 | if (!_devices.contains(selected)) selected = null;
129 | if (selected == null && _devices.isNotEmpty) selected = _devices.first;
130 |
131 | if (selected != _selectedDevice) {
132 | _selectedDevice = selected;
133 | _selectedController.add(selected);
134 | }
135 | }
136 |
137 | FlutterDaemonManager get _daemonManager => deps[FlutterDaemonManager];
138 | }
139 |
140 | class BuildMode {
141 | final String name;
142 | final bool supportsDebugging;
143 | final bool startPaused;
144 |
145 | BuildMode(this.name, { this.supportsDebugging: true, this.startPaused: false });
146 |
147 | String toString() => name;
148 | }
149 |
--------------------------------------------------------------------------------
/lib/flutter/flutter_ext.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'dart:async';
3 |
4 | import 'package:logging/logging.dart';
5 | import 'package:vm_service_lib/vm_service_lib.dart';
6 |
7 | import '../debug/observatory.dart';
8 | import '../utils.dart';
9 |
10 | const String _flutterPrefix = 'ext.flutter';
11 |
12 | final Logger _logger = new Logger('atom.flutter_ext');
13 |
14 | /// Flutter specific debugging extensions.
15 | ///
16 | /// This includes things like adjusting the `debugPaint` and `timeDilation`
17 | /// values.
18 | class FlutterExt {
19 | final ServiceWrapper serviceWrapper;
20 | final Property enabled = new Property(false);
21 |
22 | String isolateId;
23 | Set services = new Set();
24 |
25 | Map _reapply = {};
26 |
27 | FlutterExt(this.serviceWrapper) {
28 | _init();
29 | }
30 |
31 | VmService get service => serviceWrapper.service;
32 |
33 | bool get isFlutter => enabled.value;
34 |
35 | Future debugPaint(bool enabled) {
36 | const String key = '$_flutterPrefix.debugPaint';
37 |
38 | if (enabled) {
39 | _reapply[key] = () => debugPaint(true);
40 | } else {
41 | _reapply.remove(key);
42 | }
43 |
44 | return service.callServiceExtension(
45 | key,
46 | isolateId: isolateId,
47 | args: { 'enabled': enabled }
48 | );
49 | }
50 |
51 | Future repaintRainbow(bool enabled) {
52 | const String key = '$_flutterPrefix.repaintRainbow';
53 |
54 | if (enabled) {
55 | _reapply[key] = () => repaintRainbow(true);
56 | } else {
57 | _reapply.remove(key);
58 | }
59 |
60 | return service.callServiceExtension(
61 | key,
62 | isolateId: isolateId,
63 | args: { 'enabled': enabled }
64 | );
65 | }
66 |
67 | Future timeDilation(double dilation) {
68 | const String key = '$_flutterPrefix.timeDilation';
69 |
70 | if (dilation == 1.0) {
71 | _reapply.remove(key);
72 | } else {
73 | _reapply[key] = () => timeDilation(dilation);
74 | }
75 |
76 | return service.callServiceExtension(
77 | key,
78 | isolateId: isolateId,
79 | args: { 'timeDilation': dilation }
80 | );
81 | }
82 |
83 | Future performanceOverlay(bool enabled) {
84 | const String key = '$_flutterPrefix.showPerformanceOverlay';
85 |
86 | if (enabled) {
87 | _reapply[key] = () => performanceOverlay(true);
88 | } else {
89 | _reapply.remove(key);
90 | }
91 |
92 | return service.callServiceExtension(
93 | key,
94 | isolateId: isolateId,
95 | args: { 'enabled': enabled }
96 | );
97 | }
98 |
99 | void _init() {
100 | serviceWrapper.allIsolates.forEach(_checkIsolate);
101 | serviceWrapper.onIsolateCreated.listen(_checkIsolate);
102 |
103 | serviceWrapper.service.onIsolateEvent.listen((Event event) {
104 | if (event.kind == EventKind.kServiceExtensionAdded) {
105 | if (event.extensionRPC.startsWith('$_flutterPrefix.')) {
106 | _registerExtension(event.isolate.id, event.extensionRPC);
107 | }
108 | }
109 | });
110 | }
111 |
112 | void _checkIsolate(ObservatoryIsolate isolate) {
113 | if (isolate.isolate.extensionRPCs == null) return;
114 |
115 | isolate.isolate.extensionRPCs.forEach((String ext) {
116 | if (ext.startsWith('$_flutterPrefix.')) {
117 | _registerExtension(isolate.id, ext);
118 | }
119 | });
120 | }
121 |
122 | void _registerExtension(String isolateId, String extension) {
123 | if (!isFlutter) {
124 | enabled.value = true;
125 | }
126 |
127 | this.isolateId = isolateId;
128 |
129 | _logger.finer('Found ${extension}.');
130 |
131 | if (services.contains(extension)) {
132 | if (_reapply.containsKey(extension)) {
133 | _reapply[extension]();
134 | }
135 | } else {
136 | services.add(extension);
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/lib/flutter/flutter_tools.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:atom/atom.dart';
3 | import 'package:atom/node/command.dart';
4 | import 'package:atom/node/fs.dart';
5 | import 'package:atom/node/notification.dart';
6 | import 'package:atom/node/workspace.dart';
7 | import 'package:atom/utils/disposable.dart';
8 | import 'package:haikunator/haikunator.dart';
9 |
10 | import '../projects.dart';
11 | import '../state.dart';
12 | import 'flutter.dart';
13 | import 'flutter_connect.dart';
14 | import 'flutter_devices.dart';
15 | import 'flutter_sdk.dart';
16 |
17 | FlutterSdkManager _flutterSdk = deps[FlutterSdkManager];
18 |
19 | class FlutterToolsManager implements Disposable {
20 | Disposables disposables = new Disposables();
21 |
22 | FlutterConnectManager connectManager;
23 |
24 | FlutterToolsManager() {
25 | if (Flutter.hasFlutterPlugin()) {
26 | disposables.add(atom.commands.add(
27 | 'atom-workspace',
28 | 'flutter:screenshot',
29 | _screenshot
30 | ));
31 | disposables.add(atom.commands.add(
32 | 'atom-workspace',
33 | 'flutter:create-project',
34 | _createProject
35 | ));
36 | disposables.add(atom.commands.add(
37 | 'atom-workspace',
38 | 'flutter:doctor',
39 | _doctor
40 | ));
41 | disposables.add(atom.commands.add(
42 | 'atom-workspace',
43 | 'flutter:upgrade',
44 | _upgrade
45 | ));
46 | disposables.add(atom.commands.add(
47 | 'atom-workspace',
48 | 'flutter:connect-remote-debugger',
49 | _connect
50 | ));
51 |
52 | connectManager = new FlutterConnectManager();
53 | disposables.add(connectManager);
54 | }
55 | }
56 |
57 | void _screenshot(AtomEvent _) {
58 | DartProject project = projectManager.getProjectFor(
59 | atom.workspace.getActiveTextEditor()?.getPath());
60 |
61 | if (project == null) {
62 | atom.notifications.addWarning('No active project.');
63 | return;
64 | }
65 |
66 | // Find the currently selected device.
67 | FlutterDeviceManager deviceManager = deps[FlutterDeviceManager];
68 | Device device = deviceManager.currentSelectedDevice;
69 |
70 | // flutter screenshot [-d device.id]
71 | FlutterTool flutter = _flutterSdk.sdk.flutterTool;
72 | flutter.runInJob(device == null ? ['screenshot'] : ['screenshot', '-d', device.id],
73 | title: 'Running Flutter screenshot…',
74 | cwd: project.directory.path
75 | );
76 | }
77 |
78 | void _createProject(AtomEvent _) {
79 | if (!_flutterSdk.hasSdk) {
80 | _flutterSdk.showInstallationInfo();
81 | return;
82 | }
83 |
84 | String projectName = Haikunator.haikunate(delimiter: '_');
85 | String parentPath = fs.dirname(_flutterSdk.sdk.path);
86 | String projectPath = fs.join(parentPath, projectName);
87 |
88 | String _response;
89 | FlutterTool flutter = _flutterSdk.sdk.flutterTool;
90 |
91 | promptUser(
92 | 'Enter the path to the project to create:',
93 | defaultText: projectPath,
94 | selectLastWord: true
95 | ).then((String response) {
96 | _response = response;
97 |
98 | if (_response != null) {
99 | return flutter.runInJob(
100 | ['create', _response], title: 'Creating Flutter Project'
101 | );
102 | }
103 | }).then((_) {
104 | if (_response != null) {
105 | atom.project.addPath(_response);
106 | String path = fs.join(_response, 'lib', 'main.dart');
107 | atom.workspace.open(path).then((TextEditor editor) {
108 | // Focus the file in the files view 'tree-view:reveal-active-file'.
109 | atom.commands.dispatch(
110 | atom.views.getView(editor), 'tree-view:reveal-active-file');
111 | });
112 | }
113 | });
114 | }
115 |
116 | void _upgrade(AtomEvent _) {
117 | if (!_flutterSdk.hasSdk) {
118 | _flutterSdk.showInstallationInfo();
119 | return;
120 | }
121 |
122 | TextEditor editor = atom.workspace.getActiveTextEditor();
123 | if (editor == null) {
124 | atom.notifications.addWarning('No active editor.');
125 | return;
126 | }
127 |
128 | DartProject project = projectManager.getProjectFor(editor.getPath());
129 | if (project == null) {
130 | atom.notifications.addWarning('The current project is not a Dart project.');
131 | return;
132 | }
133 |
134 | atom.workspace.saveAll();
135 |
136 | FlutterTool flutter = _flutterSdk.sdk.flutterTool;
137 | flutter.runInJob(['upgrade'],
138 | title: 'Running Flutter upgrade…',
139 | cwd: project.directory.path
140 | );
141 | }
142 |
143 | void _doctor(AtomEvent _) {
144 | if (!_flutterSdk.hasSdk) {
145 | _flutterSdk.showInstallationInfo();
146 | return;
147 | }
148 |
149 | FlutterTool flutter = _flutterSdk.sdk.flutterTool;
150 |
151 | flutter.runInJob(['doctor'],
152 | title: 'Running Flutter doctor…',
153 | cwd: _flutterSdk.sdk.path
154 | );
155 | }
156 |
157 | void _connect(AtomEvent _) {
158 | if (!_flutterSdk.hasSdk) {
159 | _flutterSdk.showInstallationInfo();
160 | return;
161 | }
162 |
163 | connectManager.showConnectDialog();
164 | }
165 |
166 | void dispose() => disposables.dispose();
167 | }
168 |
--------------------------------------------------------------------------------
/lib/flutter/flutter_ui.dart:
--------------------------------------------------------------------------------
1 |
2 | import '../debug/debugger.dart';
3 | import '../debug/observatory_debugger.dart';
4 | import '../elements.dart';
5 | import '../flutter/flutter_ext.dart';
6 |
7 | // TODO(devoncarew): We need to re-do the UI for the Flutter section to better
8 | // fit more elements (like a FPS label and the current route text).
9 |
10 | class FlutterSection {
11 | final DebugConnection connection;
12 |
13 | CoreElement infoElement;
14 | bool isDebugDrawing = false;
15 | bool isRepaintRainbow = false;
16 | bool isSlowAnimations = false;
17 | bool isPerformanceOverlay = false;
18 |
19 | FlutterSection(this.connection, CoreElement element) {
20 | element.add([
21 | div().add([
22 | span(text: 'Flutter', c: 'overflow-hidden-ellipsis'),
23 | infoElement = span(c: 'debugger-secondary-info')
24 | ]),
25 | table()..add([
26 | tr()..add([
27 | td()..add([
28 | new CoreElement('label')..add([
29 | new CoreElement('input')
30 | ..setAttribute('type', 'checkbox')
31 | ..click(_toggleDrawing),
32 | span(text: 'Debug drawing', c: 'text-subtle')
33 | ])
34 | ]),
35 | td()..add([
36 | new CoreElement('label')..add([
37 | new CoreElement('input')
38 | ..setAttribute('type', 'checkbox')
39 | ..click(_toggleRepaintRainbow),
40 | span(text: 'Repaint rainbow', c: 'text-subtle')
41 | ])
42 | ])
43 | ]),
44 | tr()..add([
45 | td()..add([
46 | new CoreElement('label')..add([
47 | new CoreElement('input')
48 | ..setAttribute('type', 'checkbox')
49 | ..click(_togglePerformanceOverlay),
50 | span(text: 'Performance overlay', c: 'text-subtle')
51 | ])
52 | ]),
53 | td()..add([
54 | new CoreElement('label')..add([
55 | new CoreElement('input')
56 | ..setAttribute('type', 'checkbox')
57 | ..click(_toggleSlowAnimations),
58 | span(text: 'Slow animations', c: 'text-subtle')
59 | ])
60 | ])
61 | ])
62 | ])
63 | ]);
64 |
65 | element.hidden(true);
66 |
67 | if (connection is ObservatoryConnection) {
68 | ObservatoryConnection obs = connection;
69 | obs.flutterExtension.enabled.observe((value) {
70 | if (value) {
71 | element.hidden(false);
72 | }
73 | });
74 | }
75 | }
76 |
77 | FlutterExt get flutterExtension =>
78 | (connection as ObservatoryConnection).flutterExtension;
79 |
80 | void _toggleDrawing() {
81 | isDebugDrawing = !isDebugDrawing;
82 | flutterExtension.debugPaint(isDebugDrawing);
83 | }
84 |
85 | void _toggleRepaintRainbow() {
86 | isRepaintRainbow = !isRepaintRainbow;
87 | flutterExtension.repaintRainbow(isRepaintRainbow);
88 | }
89 |
90 | void _toggleSlowAnimations() {
91 | isSlowAnimations = !isSlowAnimations;
92 | flutterExtension.timeDilation(isSlowAnimations ? 5.0 : 1.0);
93 | }
94 |
95 | void _togglePerformanceOverlay() {
96 | isPerformanceOverlay = !isPerformanceOverlay;
97 | flutterExtension.performanceOverlay(isPerformanceOverlay);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/lib/flutter/mojo_launch.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'dart:async';
3 |
4 | import 'package:atom/node/fs.dart';
5 | import 'package:atom/node/process.dart';
6 |
7 | import '../flutter/flutter_devices.dart';
8 | import '../launch/launch.dart';
9 | import '../projects.dart';
10 | import '../state.dart';
11 | import 'flutter_sdk.dart';
12 |
13 | FlutterSdkManager _flutterSdk = deps[FlutterSdkManager];
14 | FlutterDeviceManager get deviceManager => deps[FlutterDeviceManager];
15 |
16 | class MojoLaunchType extends LaunchType {
17 | static void register(LaunchManager manager) => manager.registerLaunchType(new MojoLaunchType());
18 |
19 | MojoLaunchType() : super('mojo');
20 |
21 | // Don't advertise the mojo launch configuration as much as the flutter one.
22 | bool canLaunch(String path, LaunchData data) => false;
23 |
24 | String getDefaultConfigText() {
25 | return 'checked: true\n# args:\n# - --mojo-path=path/to/mojo';
26 | }
27 |
28 | _LaunchInstance _lastLaunch;
29 |
30 | Future performLaunch(LaunchManager manager, LaunchConfiguration configuration) async {
31 | String path = configuration.primaryResource;
32 | DartProject project = projectManager.getProjectFor(path);
33 | if (project == null) throw "File not in a Dart project.";
34 |
35 | if (!_flutterSdk.hasSdk) {
36 | _flutterSdk.showInstallationInfo();
37 | throw "Unable to launch ${configuration.shortResourceName}; no Flutter SDK found.";
38 | }
39 |
40 | await _killLastLaunch();
41 |
42 | _lastLaunch = new _LaunchInstance(project, configuration, this);
43 | return _lastLaunch.launch();
44 | }
45 |
46 | Future _killLastLaunch() {
47 | if (_lastLaunch == null) return new Future.value();
48 | Launch launch = _lastLaunch._launch;
49 | return launch.isTerminated ? new Future.value() : launch.kill();
50 | }
51 | }
52 |
53 | class _LaunchInstance {
54 | final DartProject project;
55 |
56 | Launch _launch;
57 | ProcessRunner _runner;
58 | List _args;
59 | Device _device;
60 |
61 | _LaunchInstance(
62 | this.project,
63 | LaunchConfiguration configuration,
64 | MojoLaunchType launchType
65 | ) {
66 | List flutterArgs = configuration.argsAsList;
67 |
68 | _args = ['run_mojo'];
69 |
70 | var route = configuration.typeArgs['route'];
71 | if (route is String && route.isNotEmpty) {
72 | _args.add('--route');
73 | _args.add(route);
74 | }
75 |
76 | _device = _currentSelectedDevice;
77 | if (_device != null) {
78 | _args.add('--device-id');
79 | _args.add(_device.id);
80 | }
81 |
82 | String relPath = fs.relativize(project.path, configuration.primaryResource);
83 | if (relPath != 'lib/main.dart') {
84 | _args.add('-t');
85 | _args.add(relPath);
86 | }
87 |
88 | _args.addAll(flutterArgs);
89 |
90 | _launch = new Launch(
91 | launchManager,
92 | launchType,
93 | configuration,
94 | configuration.shortResourceName,
95 | killHandler: _kill,
96 | cwd: project.path,
97 | title: 'flutter ${_args.join(' ')}',
98 | targetName: _device?.name
99 | );
100 |
101 | launchManager.addLaunch(_launch);
102 | }
103 |
104 | Future launch() async {
105 | FlutterTool flutter = _flutterSdk.sdk.flutterTool;
106 |
107 | _runner = _flutter(flutter, _args, cwd: project.path);
108 | _runner.execStreaming();
109 | _runner.onStdout.listen((String str) => _launch.pipeStdio(str));
110 | _runner.onStderr.listen((String str) => _launch.pipeStdio(str, error: true));
111 | _runner.onExit.then((code) => _launch.launchTerminated(code));
112 |
113 | return _launch;
114 | }
115 |
116 | Future _kill() {
117 | if (_runner == null) {
118 | _launch.launchTerminated(1);
119 | return new Future.value();
120 | } else {
121 | return new Future.delayed(new Duration(milliseconds: 250), () {
122 | _runner?.kill();
123 | _runner = null;
124 | });
125 | }
126 | }
127 |
128 | Device get _currentSelectedDevice => deviceManager.currentSelectedDevice;
129 | }
130 |
131 | ProcessRunner _flutter(FlutterTool flutter, List args, {String cwd}) {
132 | return flutter.runRaw(args, cwd: cwd, startProcess: false);
133 | }
134 |
--------------------------------------------------------------------------------
/lib/impl/changelog.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'dart:async';
3 | import 'dart:html' show HttpRequest;
4 |
5 | import 'package:atom/atom.dart';
6 | import 'package:atom/node/fs.dart';
7 | import 'package:atom/node/package.dart';
8 | import 'package:atom/node/shell.dart';
9 | import 'package:atom/utils/disposable.dart';
10 | import 'package:logging/logging.dart';
11 |
12 | import '../state.dart';
13 |
14 | final Logger _logger = new Logger('changelog');
15 |
16 | Future checkChangelog() => atomPackage.getPackageVersion().then(_checkChangelog);
17 |
18 | class ChangelogManager implements Disposable {
19 | Disposables disposables = new Disposables();
20 |
21 | File _changeLogFile;
22 |
23 | ChangelogManager() {
24 | disposables.add(atom.commands.add('atom-workspace', '${pluginId}:release-notes', (_) {
25 | _handleReleaseNotes();
26 | }));
27 | disposables.add(atom.commands.add('atom-workspace', '${pluginId}:getting-started', (_) {
28 | _handleGettingStarted();
29 | }));
30 | }
31 |
32 | void _handleReleaseNotes() {
33 | Future f;
34 |
35 | if (_changeLogFile != null) {
36 | f = new Future.value(_changeLogFile);
37 | } else {
38 | f = HttpRequest
39 | .getString('atom://dart/CHANGELOG.md')
40 | .then((contents) {
41 | Directory dir = new Directory.fromPath(fs.tmpdir);
42 | _changeLogFile = dir.getFile('CHANGELOG.md');
43 | _changeLogFile.writeSync(contents);
44 | return _changeLogFile;
45 | });
46 | }
47 |
48 | f.then((File file) {
49 | atom.workspace.open(file.path, options: {'split': 'right'});
50 | });
51 | }
52 |
53 | void _handleGettingStarted() {
54 | shell.openExternal('https://dart-atom.github.io/dart/');
55 | }
56 |
57 | void dispose() => disposables.dispose();
58 | }
59 |
60 | void _checkChangelog(String currentVersion) {
61 | String lastVersion = atom.config.getValue('_dart._version');
62 |
63 | if (lastVersion != currentVersion) {
64 | _logger.info("upgraded from ${lastVersion} to ${currentVersion}");
65 | atom.config.setValue('_dart._version', currentVersion);
66 |
67 | if (lastVersion != null) {
68 | atom.notifications.addSuccess(
69 | 'Upgraded to dart plugin version ${currentVersion}.');
70 | }
71 |
72 | // HttpRequest.getString('atom://dart/CHANGELOG.md').then((str) {
73 | // String changes;
74 | // if (lastVersion != null) {
75 | // changes = _extractVersion(str, lastVersion, inclusive: false);
76 | // } else {
77 | // changes = _extractVersion(str, currentVersion, inclusive: true);
78 | // }
79 | // if (changes != null && changes.isNotEmpty) {
80 | // atom.notifications.addSuccess(
81 | // 'Upgraded to dart plugin version ${currentVersion}.',
82 | // description: changes,
83 | // dismissable: true);
84 | // }
85 | // });
86 | } else {
87 | _logger.info("dart version ${currentVersion}");
88 | }
89 | }
90 |
91 | // String _extractVersion(String changelog, String last, {bool inclusive: true}) {
92 | // Version lastVersion = new Version.parse(last);
93 | // List changes = changelog.split('\n');
94 | // Iterable itor = changes.skipWhile((line) => !line.startsWith('##'));
95 | // changes = itor.takeWhile((line) {
96 | // if (line.startsWith('## ')) {
97 | // try {
98 | // line = line.substring(3);
99 | // Version ver = new Version.parse(line);
100 | // if (inclusive) return ver >= lastVersion;
101 | // return ver > lastVersion;
102 | // } catch (_) {
103 | // return true;
104 | // }
105 | // }
106 | // return true;
107 | // }).toList();
108 | // return changes.join('\n').trim();
109 | // }
110 |
--------------------------------------------------------------------------------
/lib/impl/debounce.dart:
--------------------------------------------------------------------------------
1 | library atom.debounce;
2 |
3 | import 'dart:async';
4 |
5 | class Debounce implements StreamTransformer {
6 | final Duration duration;
7 |
8 | Timer _timer;
9 |
10 | Debounce(this.duration);
11 |
12 | Stream bind(Stream stream) {
13 | StreamController controller = new StreamController();
14 |
15 | StreamSubscription sub;
16 |
17 | sub = stream.listen((T data) {
18 | _timer?.cancel();
19 | _timer = new Timer(duration, () => controller.add(data));
20 | }, onDone: () => sub.cancel());
21 |
22 | return controller.stream;
23 | }
24 |
25 | void cancel() => _timer?.cancel();
26 | }
27 |
--------------------------------------------------------------------------------
/lib/impl/navigation.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:atom/atom.dart';
4 | import 'package:atom/node/command.dart';
5 | import 'package:atom/node/workspace.dart';
6 | import 'package:atom/utils/disposable.dart';
7 | import 'package:logging/logging.dart';
8 |
9 | import '../state.dart';
10 | import '../usage.dart' show trackCommand;
11 |
12 | final Logger _logger = new Logger('atom.navigation');
13 |
14 | // TODO: Finish this class.
15 |
16 | class NavigationManager implements Disposable {
17 | Disposables _commands = new Disposables();
18 |
19 | StreamController _navController =
20 | new StreamController.broadcast();
21 | List _history = [];
22 | List _future = [];
23 |
24 | NavigationManager() {
25 | _commands.add(atom.commands.add('atom-text-editor',
26 | 'dart:return-from-declaration', _handleNavigateReturn));
27 | _commands.add(atom.commands.add('atom-text-editor[data-grammar~="dart"]',
28 | 'symbols-view:return-from-declaration', _handleNavigateReturn));
29 | }
30 |
31 | // TODO:
32 | Stream get onNavigate => _navController.stream;
33 |
34 | Future jumpToLocation(
35 | String path, [int line, int column, int length]
36 | ) {
37 | _pushCurrentLocation();
38 | return editorManager.jumpToLocation(path, line, column, length);
39 | }
40 |
41 | void goBack() {
42 | if (_history.isNotEmpty) {
43 | // TODO:
44 |
45 | }
46 | }
47 |
48 | void goForward() {
49 | if (_future.isNotEmpty) {
50 |
51 | }
52 | }
53 |
54 | bool canGoBack() => _history.isNotEmpty;
55 |
56 | bool canGoForward() => _future.isNotEmpty;
57 |
58 | void _handleNavigateReturn(AtomEvent _) {
59 | // TODO: rework this
60 |
61 | trackCommand('return-from-declaration');
62 |
63 | if (_history.isEmpty) {
64 | _beep();
65 | _logger.info('No navigation positions on the stack.');
66 | } else {
67 | NavigationPosition pos = _history.removeLast();
68 | editorManager.jumpToLocation(pos.path, pos.line, pos.column, pos.length);
69 | }
70 | }
71 |
72 | void _pushCurrentLocation() {
73 | TextEditor editor = atom.workspace.getActiveTextEditor();
74 |
75 | if (editor != null) {
76 | Range range = editor.getSelectedBufferRange();
77 | if (range == null) return;
78 |
79 | int length = range.isSingleLine() ? range.end.column - range.start.column : null;
80 | if (length == 0) length = null;
81 |
82 | Point start = range.start;
83 | _history.add(
84 | new NavigationPosition(editor.getPath(), start.row, start.column, length));
85 | }
86 | }
87 |
88 | void _beep() => atom.beep();
89 |
90 | void dispose() => _commands.dispose();
91 | }
92 |
93 | class NavigationPosition {
94 | final String path;
95 | final int line;
96 | final int column;
97 | final int length;
98 |
99 | NavigationPosition(this.path, this.line, this.column, [this.length]);
100 |
101 | String toString() => '[${path} ${line}:${column}]';
102 | }
103 |
--------------------------------------------------------------------------------
/lib/impl/rebuild.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | /// A library used for rebuilding the `dart` project.
6 | library atom.rebuild;
7 |
8 | import 'dart:async';
9 |
10 | import 'package:atom/atom.dart';
11 | import 'package:atom/node/fs.dart';
12 | import 'package:atom/utils/disposable.dart';
13 |
14 | import '../jobs.dart';
15 | import '../state.dart';
16 | import 'pub.dart';
17 |
18 | class RebuildManager implements Disposable {
19 | Disposables disposables = new Disposables();
20 |
21 | RebuildManager() {
22 | disposables.add(atom.commands
23 | .add('atom-workspace', 'dart:rebuild-restart-dev', (_) {
24 | if (_projectsToBuild().isNotEmpty) {
25 | new RebuildJob("Rebuilding Atom plugins").schedule();
26 | }
27 | }));
28 | }
29 |
30 | void dispose() => disposables.dispose();
31 | }
32 |
33 | class RebuildJob extends Job {
34 | RebuildJob(String title) : super(title, RebuildJob);
35 |
36 | Future run() {
37 | // Validate that there's an sdk.
38 | if (!sdkManager.hasSdk) {
39 | sdkManager.showNoSdkMessage();
40 | return new Future.value();
41 | }
42 |
43 | // Save any dirty editors.
44 | atom.workspace.getTextEditors().forEach((editor) {
45 | if (editor.isModified()) editor.save();
46 | });
47 |
48 | // Build plugins and aggregate the results
49 | var builds = _projectsToBuild().map((String name) => _runBuild(name));
50 | Future result = Future.wait(builds).then((List results) =>
51 | results.reduce((bool value, bool success) => value && success));
52 |
53 | return result.then((bool success) {
54 | if (success) {
55 | new Future.delayed(new Duration(seconds: 2)).then((_) => atom.reload());
56 | }
57 | });
58 | }
59 |
60 | /// Locate and build the specified project.
61 | Future _runBuild(String projName) {
62 | // Find the project to be built.
63 | Directory proj = atom.project.getDirectories().firstWhere(
64 | (d) => d.getBaseName().endsWith(projName), orElse: () => null
65 | );
66 | if (proj == null) {
67 | atom.notifications.addWarning("Unable to find project '${projName}'.");
68 | return new Future.value(false);
69 | }
70 |
71 | List args = ['grinder', 'build'];
72 |
73 | // Run the build and check for an exit code of `0` from grind build.
74 | return new PubRunJob.local(proj.getPath(), args, title: projName)
75 | .schedule().then((JobStatus status) => status.isOk && status.result == 0);
76 | }
77 | }
78 |
79 | List _projectsToBuild() =>
80 | atom.config.getValue('$pluginId.buildAtomPlugins') as List ?? [];
81 |
--------------------------------------------------------------------------------
/lib/impl/status_display.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library atom.status;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:atom/atom.dart';
10 | import 'package:atom/utils/disposable.dart';
11 | import 'package:atom/utils/string_utils.dart';
12 |
13 | import '../atom_statusbar.dart';
14 | import '../elements.dart';
15 | import '../jobs.dart';
16 | import '../state.dart';
17 |
18 | const Duration _showDelay = const Duration(milliseconds: 200);
19 |
20 | const Duration _hideDelay = const Duration(milliseconds: 400);
21 |
22 | class StatusDisplay implements Disposable {
23 | final Disposables _disposables = new Disposables();
24 | StreamSubscription _subscription;
25 |
26 | JobsDialog dialog;
27 |
28 | Tile _statusbarTile;
29 |
30 | Timer _showTimer;
31 | Timer _hideTimer;
32 |
33 | StatusDisplay(StatusBar statusBar) {
34 | CoreElement spinner;
35 | CoreElement textLabel;
36 | CoreElement countBadge;
37 |
38 | CoreElement statusElement = div(c: 'job-status-bar dart')
39 | ..inlineBlock()
40 | ..click(_showJobsDialog)
41 | ..add([
42 | spinner = img()
43 | ..inlineBlockTight()
44 | ..clazz('status-spinner')
45 | ..src = 'atom://dart/images/gear.svg',
46 | textLabel = div(c: 'text-label')..inlineBlockTight(), // text-highlight
47 | countBadge = span(c: 'badge badge-info badge-count')
48 | ]);
49 |
50 | _statusbarTile =
51 | statusBar.addRightTile(item: statusElement.element, priority: 1000);
52 |
53 | _subscription = jobs.onQueueChanged.listen((_) {
54 | Job job = jobs.activeJob;
55 |
56 | bool shouldShow = job != null;
57 | bool isShowing = statusElement.element.classes.contains('showing');
58 |
59 | if (shouldShow && !isShowing) {
60 | _hideTimer?.cancel();
61 | _hideTimer = null;
62 |
63 | // Show it.
64 | if (_showTimer == null) {
65 | _showTimer = new Timer(_showDelay, () {
66 | statusElement.toggleClass('showing', true);
67 | _showTimer = null;
68 | });
69 | }
70 | } else if (!shouldShow && isShowing) {
71 | _showTimer?.cancel();
72 | _showTimer = null;
73 |
74 | // Hide it.
75 | if (_hideTimer == null) {
76 | _hideTimer = new Timer(_hideDelay, () {
77 | textLabel.text = '';
78 | statusElement.toggleClass('showing', false);
79 | _hideTimer = null;
80 | });
81 | }
82 | }
83 |
84 | if (job != null) {
85 | textLabel.text = '${job.name}…';
86 | }
87 |
88 | int jobsLength = jobs.allJobs.length;
89 | countBadge.text = jobsLength == 0 ? '' : '${jobsLength} ${pluralize('job', jobsLength)}';
90 |
91 | spinner.toggleClass('showing', shouldShow);
92 | textLabel.toggleClass('showing', shouldShow);
93 | countBadge.toggleClass('showing', jobsLength > 1);
94 |
95 | _updateJobsDialog();
96 | });
97 |
98 | _disposables.add(atom.commands.add(
99 | 'atom-workspace', 'dart:show-jobs', (_) => _showJobsDialog()));
100 | }
101 |
102 | void dispose() {
103 | _subscription.cancel();
104 | _statusbarTile.destroy();
105 | _disposables.dispose();
106 | }
107 |
108 | void _showJobsDialog() {
109 | if (dialog == null) {
110 | dialog = new JobsDialog();
111 | _disposables.add(dialog);
112 | }
113 | dialog.show();
114 | dialog.updateJobsDialog();
115 | }
116 |
117 | void _updateJobsDialog() {
118 | if (dialog != null) dialog.updateJobsDialog();
119 | }
120 | }
121 |
122 | class JobsDialog implements Disposable {
123 | TitledModelDialog dialog;
124 | CoreElement _listGroup;
125 |
126 | JobsDialog() {
127 | dialog = new TitledModelDialog('', classes: 'list-dialog');
128 | dialog.content.add([
129 | div(c: 'select-list')..add([_listGroup = ol(c: 'list-group')])
130 | ]);
131 | }
132 |
133 | void show() => dialog.show();
134 |
135 | void updateJobsDialog() {
136 | dialog.title.text = jobs.allJobs.isEmpty ?
137 | 'No running jobs.' :
138 | '${jobs.allJobs.length} running ${pluralize('job', jobs.allJobs.length)}';
139 | _listGroup.element.children.clear();
140 |
141 | for (JobInstance jobInstance in jobs.allJobs) {
142 | Job job = jobInstance.job;
143 |
144 | CoreElement item = li(c: 'item-container')
145 | ..layoutHorizontal()
146 | ..add([
147 | div()
148 | ..inlineBlock()
149 | ..flex()
150 | ..text = jobInstance.isRunning ? '${job.name}…' : job.name
151 | ]);
152 |
153 | if (job.infoAction != null) {
154 | item.add([
155 | div(c: 'info')
156 | ..inlineBlock()
157 | ..icon('question')
158 | ..click(job.infoAction)
159 | ]);
160 | }
161 |
162 | if (jobInstance.isRunning) {
163 | item.add([
164 | div(c: 'jobs-progress')
165 | ..inlineBlock()
166 | ..add([new ProgressElement()])
167 | ]);
168 | }
169 |
170 | _listGroup.add(item);
171 | }
172 | }
173 |
174 | void dispose() => dialog.dispose();
175 | }
176 |
--------------------------------------------------------------------------------
/lib/impl/testing_utils.dart:
--------------------------------------------------------------------------------
1 |
2 | /// Return the list of possible test paths. The given [path] must be in the
3 | /// `lib/` directory.
4 | List getPossibleTestPaths(String path, String separator) {
5 | assert(separator.length == 1);
6 |
7 | if (!path.startsWith('lib${separator}')) return [];
8 | path = path.substring(4);
9 |
10 | List result = [];
11 |
12 | // Remove '.dart'; add '_test.dart'.
13 | path = path.substring(0, path.length - 5) + '_test.dart';
14 |
15 | // Look for test/path/file_test.dart.
16 | String testPath = 'test' + separator + path;
17 | result.add(testPath);
18 |
19 | // Look for test/path-w/o-src/file_test.dart.
20 | if (path.startsWith('src${separator}')) {
21 | path = path.substring(4);
22 | testPath = 'test' + separator + path;
23 | result.add(testPath);
24 | }
25 |
26 | // Look for test/file_test.dart.
27 | if (path.contains('/')) {
28 | testPath = 'test' + separator + _basename(path, separator: separator);
29 | result.add(testPath);
30 | }
31 |
32 | return result;
33 | }
34 |
35 | String _basename(String name, { String separator}) {
36 | int index = name.lastIndexOf(separator);
37 | return index == -1 ? name : name.substring(index + 1);
38 | }
39 |
--------------------------------------------------------------------------------
/lib/impl/toolbar.dart:
--------------------------------------------------------------------------------
1 | import 'dart:js';
2 |
3 | import 'package:atom/atom.dart';
4 |
5 | /// A wrapper around the `toolbar` API.
6 | class Toolbar extends ProxyHolder {
7 | Toolbar(JsObject obj) : super(obj);
8 |
9 | ToolbarTile addLeftTile({dynamic item, int priority}) {
10 | Map m = {'item': item};
11 | if (priority != null) m['priority'] = priority;
12 | return new ToolbarTile(invoke('addLeftTile', m));
13 | }
14 |
15 | ToolbarTile addRightTile({dynamic item, int priority}) {
16 | Map m = {'item': item};
17 | if (priority != null) m['priority'] = priority;
18 | return new ToolbarTile(invoke('addRightTile', m));
19 | }
20 |
21 | List getLeftTiles() =>
22 | new List.from(invoke('getLeftTiles').map((t) => new ToolbarTile(t)));
23 |
24 | List getRightTiles() =>
25 | new List.from(invoke('getRightTiles').map((t) => new ToolbarTile(t)));
26 | }
27 |
28 | class ToolbarTile extends ProxyHolder {
29 | ToolbarTile(JsObject obj) : super(obj);
30 |
31 | int getPriority() => invoke('getPriority');
32 | dynamic getItem() => invoke('getItem');
33 | void destroy() => invoke('destroy');
34 | }
35 |
--------------------------------------------------------------------------------
/lib/launch/launch_cli.dart:
--------------------------------------------------------------------------------
1 | library atom.launch_cli;
2 |
3 | import 'dart:async';
4 |
5 | import 'package:atom/atom.dart';
6 | import 'package:atom/node/fs.dart';
7 | import 'package:atom/node/process.dart';
8 | import 'package:atom_dart/dartino/dartino.dart' show dartino;
9 |
10 | import '../debug/observatory_debugger.dart' show ObservatoryDebugger;
11 | import '../projects.dart';
12 | import '../sdk.dart';
13 | import '../state.dart';
14 | import 'launch.dart';
15 |
16 | final String _observatoryPrefix = 'Observatory listening on ';
17 |
18 | class CliLaunchType extends LaunchType {
19 | static void register(LaunchManager manager) =>
20 | manager.registerLaunchType(new CliLaunchType());
21 |
22 | CliLaunchType() : super('cli');
23 |
24 | bool canLaunch(String path, LaunchData data) {
25 | if (!path.endsWith('.dart')) return false;
26 |
27 | DartProject project = projectManager.getProjectFor(path);
28 |
29 | if (project == null) {
30 | return data.hasMain;
31 | } else {
32 | // Check that the file is not in lib/.
33 | String relativePath = fs.relativize(project.path, path);
34 | if (relativePath.startsWith('lib${fs.separator}')) return false;
35 |
36 | if (dartino.isProject(project.directory.path)) return false;
37 | return data.hasMain;
38 | }
39 | }
40 |
41 | Future performLaunch(LaunchManager manager, LaunchConfiguration configuration) {
42 | Sdk sdk = sdkManager.sdk;
43 |
44 | if (sdk == null) new Future.error('No Dart SDK configured');
45 |
46 | bool withDebug = configuration.debug;
47 | String path = configuration.primaryResource;
48 | String cwd = configuration.cwd;
49 | List args = configuration.argsAsList;
50 |
51 | DartProject project = projectManager.getProjectFor(path);
52 |
53 | // Determine the best cwd.
54 | if (cwd == null) {
55 | if (project == null) {
56 | List paths = atom.project.relativizePath(path);
57 | if (paths[0] != null) {
58 | cwd = paths[0];
59 | path = paths[1];
60 | }
61 | } else {
62 | cwd = project.path;
63 | path = fs.relativize(cwd, path);
64 | }
65 | } else {
66 | path = fs.relativize(cwd, path);
67 | }
68 |
69 | List _args = [];
70 |
71 | int observatoryPort;
72 |
73 | if (withDebug) {
74 | observatoryPort = getOpenPort();
75 | _args.add('--enable-vm-service:${observatoryPort}');
76 | _args.add('--pause_isolates_on_start=true');
77 | }
78 |
79 | if (configuration.checked) _args.add('--checked');
80 |
81 | _args.add(path);
82 | if (args != null) _args.addAll(args);
83 |
84 | String description = (args == null || args.isEmpty) ? path : '${path} ${args.join(' ')}';
85 |
86 | // Run in `underShell` to capture environment variables on the mac.
87 | ProcessRunner runner = new ProcessRunner.underShell(
88 | sdk.dartVm.path,
89 | args: _args,
90 | cwd: cwd
91 | );
92 |
93 | Launch launch = new _CliLaunch(manager, this, configuration, path,
94 | killHandler: () => runner.kill(),
95 | cwd: cwd,
96 | project: project,
97 | title: description
98 | );
99 | manager.addLaunch(launch);
100 |
101 | runner.execStreaming();
102 | runner.onStdout.listen((str) {
103 | // "Observatory listening on http://127.0.0.1:xxx\n"
104 | if (str.startsWith(_observatoryPrefix)) {
105 | // str is 'http://127.0.0.1:xxx'.
106 | str = str.substring(_observatoryPrefix.length).trim();
107 |
108 | launch.servicePort.value = observatoryPort;
109 |
110 | ObservatoryDebugger.connect(launch, 'localhost', observatoryPort).catchError((e) {
111 | launch.pipeStdio(
112 | 'Unable to connect to the observatory (port ${observatoryPort}).\n',
113 | error: true
114 | );
115 | });
116 | } else {
117 | launch.pipeStdio(str);
118 | }
119 | });
120 | runner.onStderr.listen((str) => launch.pipeStdio(str, error: true));
121 | runner.onExit.then((int code) => launch.launchTerminated(code));
122 |
123 | return new Future.value(launch);
124 | }
125 |
126 | String getDefaultConfigText() {
127 | return '''
128 | # Additional args for the application.
129 | args:
130 | # The working directory to use for the launch.
131 | cwd:
132 | # Enable or disable checked mode.
133 | checked: true
134 | # Enable or disable debugging.
135 | debug: true
136 | ''';
137 | }
138 | }
139 |
140 | // TODO: Move more launching functionality into this class.
141 | class _CliLaunch extends Launch {
142 | CachingServerResolver _resolver;
143 |
144 | _CliLaunch(
145 | LaunchManager manager,
146 | LaunchType launchType,
147 | LaunchConfiguration launchConfiguration,
148 | String name,
149 | { Function killHandler, String cwd, DartProject project, String title }
150 | ) : super(
151 | manager,
152 | launchType,
153 | launchConfiguration,
154 | name,
155 | killHandler: killHandler,
156 | cwd: cwd,
157 | title: title
158 | ) {
159 | _resolver = new CachingServerResolver(
160 | cwd: project?.path,
161 | server: analysisServer
162 | );
163 |
164 | exitCode.onChanged.first.then((_) => _resolver.dispose());
165 | }
166 |
167 | Future resolve(String url) => _resolver.resolve(url);
168 | }
169 |
--------------------------------------------------------------------------------
/lib/launch/launch_serve.dart:
--------------------------------------------------------------------------------
1 | library atom.launch_serve;
2 |
3 | import 'dart:async';
4 |
5 | import 'package:atom/atom.dart';
6 | import 'package:atom/node/fs.dart';
7 | import 'package:atom/node/process.dart';
8 |
9 | import '../projects.dart';
10 | import '../state.dart';
11 | import 'launch.dart';
12 |
13 | const singletonBoolParameters = const ['all', 'force-poll', 'no-force-poll'];
14 |
15 | /// Pub serve launch.
16 | class ServeLaunchType extends LaunchType {
17 | static void register(LaunchManager manager) =>
18 | manager.registerLaunchType(new ServeLaunchType());
19 |
20 | ServeLaunchType() : super('serve');
21 |
22 | bool canLaunch(String path, LaunchData data) {
23 | return path.endsWith('pubspec.yaml');
24 | }
25 |
26 | bool get supportsChecked => false;
27 |
28 | Future performLaunch(
29 | LaunchManager manager, LaunchConfiguration configuration) {
30 | String cwd = configuration.cwd;
31 | var args = configuration.typeArgs['args'] ?? {};
32 |
33 | String launchName = 'pub serve';
34 |
35 | // Determine the best cwd.
36 | if (cwd == null) {
37 | List paths =
38 | atom.project.relativizePath(configuration.primaryResource);
39 | if (paths[0] != null) {
40 | cwd = paths[0];
41 | launchName = paths[1];
42 | }
43 | } else {
44 | launchName = fs.relativize(cwd, launchName);
45 | }
46 |
47 | List execArgs = ['serve'];
48 | if (args is Map) {
49 | args.forEach((k, v) {
50 | if (singletonBoolParameters.contains(k)) {
51 | if (v) execArgs.add('--$k');
52 | } else {
53 | execArgs.add('--$k=$v');
54 | }
55 | });
56 | }
57 | ProcessRunner runner =
58 | sdkManager.sdk.execBin('pub', execArgs, cwd: cwd, startProcess: false);
59 |
60 | String root =
61 | 'http://${args['hostname'] ?? 'localhost'}:${args['port'] ?? 'port'}';
62 | Launch launch = new ServeLaunch(
63 | manager, this, configuration, launchName, root,
64 | killHandler: () => runner.kill(), cwd: cwd, title: launchName);
65 | manager.addLaunch(launch);
66 |
67 | runner.execStreaming();
68 | runner.onStdout.listen((str) => launch.pipeStdio(str));
69 | runner.onStderr.listen((str) => launch.pipeStdio(str, error: true));
70 | runner.onExit.then((code) => launch.launchTerminated(code));
71 |
72 | return new Future.value(launch);
73 | }
74 |
75 | String getDefaultConfigText() {
76 | return '''
77 | # Additional args for pub serve
78 | args:
79 | # Mode to run transformers in. (defaults to "debug")
80 | #mode: debug
81 | # Use all default source directories.
82 | #all: true
83 | # The JavaScript compiler to use to build the app. [dart2js, dartdevc, none]
84 | #web-compiler: dartdevc
85 | # Defines an environment constant for dart2js.
86 | #define: variable=value[,variable=value]
87 | # The hostname to listen on. (defaults to "localhost")
88 | #hostname: localhost
89 | # The base port to listen on. (defaults to "8084")
90 | port: 8084
91 | # Force the use of a polling filesystem watcher.
92 | #force-poll: true
93 | #no-force-poll: true
94 | ''';
95 | }
96 | }
97 |
98 | class ServeLaunch extends Launch {
99 | String _root;
100 | String get root => _root;
101 |
102 | ServeLaunch(LaunchManager manager, LaunchType launchType,
103 | LaunchConfiguration launchConfiguration, String name, String root,
104 | {Function killHandler, String cwd, DartProject project, String title})
105 | : _root = root,
106 | super(manager, launchType, launchConfiguration, name,
107 | killHandler: killHandler, cwd: cwd, title: title);
108 | }
109 |
--------------------------------------------------------------------------------
/lib/launch/launch_shell.dart:
--------------------------------------------------------------------------------
1 | library atom.launch_shell;
2 |
3 | import 'dart:async';
4 |
5 | import 'package:atom/atom.dart';
6 | import 'package:atom/node/fs.dart';
7 | import 'package:atom/node/process.dart';
8 |
9 | import 'launch.dart';
10 |
11 | class ShellLaunchType extends LaunchType {
12 | static void register(LaunchManager manager) =>
13 | manager.registerLaunchType(new ShellLaunchType());
14 |
15 | ShellLaunchType() : super('shell');
16 |
17 | bool canLaunch(String path, LaunchData data) {
18 | return path.endsWith('.sh') || path.endsWith('.bat');
19 | }
20 |
21 | bool get supportsChecked => false;
22 |
23 | Future performLaunch(LaunchManager manager, LaunchConfiguration configuration) {
24 | String script = configuration.primaryResource;
25 | String cwd = configuration.cwd;
26 | List args = configuration.argsAsList;
27 |
28 | String launchName = script;
29 |
30 | // Determine the best cwd.
31 | if (cwd == null) {
32 | List paths = atom.project.relativizePath(script);
33 | if (paths[0] != null) {
34 | cwd = paths[0];
35 | launchName = paths[1];
36 | }
37 | } else {
38 | launchName = fs.relativize(cwd, launchName);
39 | }
40 |
41 | ProcessRunner runner = new ProcessRunner(script, args: args, cwd: cwd);
42 | String description = (args == null || args.isEmpty) ? launchName : '${launchName} ${args.join(' ')}';
43 | Launch launch = new Launch(manager, this, configuration, launchName,
44 | killHandler: () => runner.kill(),
45 | cwd: cwd,
46 | title: description
47 | );
48 | manager.addLaunch(launch);
49 |
50 | runner.execStreaming();
51 | runner.onStdout.listen((str) => launch.pipeStdio(str));
52 | runner.onStderr.listen((str) => launch.pipeStdio(str, error: true));
53 | runner.onExit.then((code) => launch.launchTerminated(code));
54 |
55 | return new Future.value(launch);
56 | }
57 |
58 | String getDefaultConfigText() {
59 | return '''
60 | # Additional args for the application.
61 | args:
62 | ''';
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/lib/launch/launch_web.dart:
--------------------------------------------------------------------------------
1 | library atom.launch_web;
2 |
3 | import 'dart:async';
4 |
5 | import 'package:atom/atom.dart';
6 | import 'package:atom/node/fs.dart';
7 | import 'package:atom/node/process.dart';
8 |
9 | import '../browser.dart';
10 | import '../debug/chrome_debugger.dart';
11 | import '../state.dart';
12 | import 'launch.dart';
13 | import 'launch_serve.dart';
14 |
15 | const launchOptionKeys = const [
16 | 'debugging',
17 | 'local_pub_serve',
18 | 'pub_serve_host'
19 | ];
20 |
21 | class WebLaunchType extends LaunchType {
22 | static void register(LaunchManager manager) =>
23 | manager.registerLaunchType(new WebLaunchType());
24 |
25 | WebLaunchType() : super('web');
26 |
27 | bool canLaunch(String path, LaunchData data) => path.endsWith('.html');
28 |
29 | Future performLaunch(
30 | LaunchManager manager, LaunchConfiguration configuration) {
31 | Browser browser = deps[BrowserManager].browser;
32 | if (browser == null) {
33 | atom.notifications.addWarning('No browser configured.');
34 | return new Future.value();
35 | }
36 |
37 | Map yamlArgs = configuration.typeArgs['args'] ?? {};
38 | bool debugging = yamlArgs['debugging'] == true;
39 | bool pub_serve_check = yamlArgs['local_pub_serve'] == true;
40 |
41 | String root;
42 | if (pub_serve_check) {
43 | // Find pub serve for 'me'.
44 | ServeLaunch pubServe = manager.launches.firstWhere(
45 | (l) =>
46 | l is ServeLaunch &&
47 | l.isRunning &&
48 | l.launchConfiguration.projectPath == configuration.projectPath,
49 | orElse: () => null);
50 |
51 | if (pubServe == null) {
52 | atom.notifications.addWarning('No pub serve launch found.');
53 | return new Future.value();
54 | }
55 | root = pubServe.root;
56 | } else {
57 | root = yamlArgs['pub_serve_host'] ?? 'http://localhost:8084';
58 | }
59 |
60 | List args =
61 | browser.execArgsFromYaml(yamlArgs, exceptKeys: launchOptionKeys);
62 | String htmlFile = configuration.shortResourceName;
63 | if (htmlFile.startsWith('web/') || htmlFile.startsWith('web\\')) {
64 | htmlFile = htmlFile.substring(4);
65 | }
66 |
67 | if (!debugging) {
68 | args.add('$root/$htmlFile');
69 | }
70 |
71 | print(browser.path);
72 | print(args);
73 |
74 | ProcessRunner runner =
75 | new ProcessRunner.underShell(browser.path, args: args);
76 |
77 | Launch launch = new Launch(
78 | manager, this, configuration, configuration.shortResourceName,
79 | killHandler: () => runner.kill(),
80 | title: configuration.shortResourceName);
81 | manager.addLaunch(launch);
82 |
83 | runner.execStreaming();
84 | runner.onStdout.listen((str) => launch.pipeStdio(str));
85 | runner.onStderr.listen((str) => launch.pipeStdio(str, error: true));
86 | runner.onExit.then((code) => launch.launchTerminated(code));
87 |
88 | if (debugging) {
89 | String debugHost = 'localhost:${yamlArgs['remote-debugging-port']}';
90 | ChromeDebugger
91 | .connect(launch, configuration, debugHost, root, htmlFile)
92 | .catchError((e) {
93 | launch.pipeStdio('Unable to connect to chrome.\n', error: true);
94 | });
95 | }
96 |
97 | return new Future.value(launch);
98 | }
99 |
100 | String getDefaultConfigText() => '''
101 | # Additional args for browser.
102 | args:
103 | # options
104 | debugging: true
105 | local_pub_serve: true
106 | # if local_pub_serve is false, specify pub serve endpoint
107 | pub_serve_host: http://localhost:8084
108 |
109 | # chrome
110 | remote-debugging-port: 9222
111 | user-data-dir: ${fs.tmpdir}/dart-dbg-host
112 | no-default-browser-check: true
113 | no-first-run: true
114 | ''';
115 | }
116 |
--------------------------------------------------------------------------------
/lib/state.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library atom.state;
6 |
7 | import 'dart:async';
8 | import 'dart:convert' show JSON;
9 |
10 | import 'package:atom/utils/dependencies.dart';
11 |
12 | import 'analysis_server.dart';
13 | import 'debug/breakpoints.dart';
14 | import 'debug/debugger.dart';
15 | import 'debug/debugger_tooltip.dart';
16 | import 'editors.dart';
17 | import 'error_repository.dart';
18 | import 'impl/navigation.dart';
19 | import 'impl/status.dart';
20 | import 'jobs.dart';
21 | import 'launch/launch.dart';
22 | import 'launch/launch_configs.dart';
23 | import 'projects.dart';
24 | import 'sdk.dart';
25 |
26 | export 'package:atom/utils/dependencies.dart' show deps;
27 |
28 | final String pluginId = 'dart';
29 |
30 | AtomAnalysisServer get analysisServer => deps[AtomAnalysisServer];
31 | EditorManager get editorManager => deps[EditorManager];
32 | ErrorRepository get errorRepository => deps[ErrorRepository];
33 | JobManager get jobs => deps[JobManager];
34 | LaunchManager get launchManager => deps[LaunchManager];
35 | LaunchConfigurationManager get launchConfigurationManager => deps[LaunchConfigurationManager];
36 | DebugManager get debugManager => deps[DebugManager];
37 | DebugTooltipManager get debugTooltipManager => deps[DebugTooltipManager];
38 | BreakpointManager get breakpointManager => deps[BreakpointManager];
39 | ProjectManager get projectManager => deps[ProjectManager];
40 | SdkManager get sdkManager => deps[SdkManager];
41 | NavigationManager get navigationManager => deps[NavigationManager];
42 | StatusViewManager get statusViewManager => deps[StatusViewManager];
43 |
44 | final State state = new State();
45 |
46 | class State {
47 | dynamic _pluginState = {};
48 | Map _storables = {};
49 |
50 | Map _controllers = {};
51 |
52 | State();
53 |
54 | dynamic operator[](String key) => _pluginState[key];
55 |
56 | void operator[]=(String key, dynamic value) {
57 | _pluginState[key] = value;
58 | if (_controllers[key] != null) _controllers[key].add(value);
59 | }
60 |
61 | /// Register the given [StateStorable]. This will call [StateStorable.fromStored]
62 | /// before it returns.
63 | void registerStorable(String key, StateStorable storable) {
64 | try {
65 | _storables[key] = storable;
66 | var data = this[key];
67 | storable.initFromStored(data is String ? JSON.decode(data) : null);
68 | } catch (e) {
69 | print('Exception restoring state: ${e}');
70 | }
71 | }
72 |
73 | void loadFrom(dynamic inState) {
74 | _pluginState = inState ?? {};
75 | }
76 |
77 | Stream onValueChanged(String key) {
78 | if (_controllers[key] != null) {
79 | return _controllers[key].stream;
80 | } else {
81 | StreamController controller = new StreamController.broadcast(
82 | sync: true,
83 | onCancel: () => _controllers.remove(key));
84 | _controllers[key] = controller;
85 | return controller.stream;
86 | }
87 | }
88 |
89 | dynamic saveState() {
90 | _storables.forEach((String key, StateStorable storable) {
91 | _pluginState[key] = JSON.encode(storable.toStorable());
92 | });
93 | return _pluginState;
94 | }
95 | }
96 |
97 | abstract class StateStorable {
98 | StateStorable();
99 |
100 | /// Initialize the state from a previously stored JSON encodable value.
101 | void initFromStored(dynamic storedData);
102 |
103 | /// Write the current state to a JSON encodable value.
104 | dynamic toStorable();
105 | }
106 |
--------------------------------------------------------------------------------
/lib/usage.dart:
--------------------------------------------------------------------------------
1 | library atom.usage;
2 |
3 | import 'dart:async';
4 |
5 | import 'package:atom/atom.dart';
6 | import 'package:atom/node/package.dart';
7 | import 'package:atom/node/workspace.dart';
8 | import 'package:atom/utils/disposable.dart';
9 | import 'package:logging/logging.dart';
10 | import 'package:usage/usage_html.dart';
11 |
12 | import 'projects.dart';
13 | import 'state.dart';
14 |
15 | // TODO: If re-enabling analytics, replace with a real UA ID.
16 | final String _UA = 'UA-0000';
17 |
18 | // TODO: Remove this constant if re-enabling analytics. Also, see plugin.dart,
19 | // 'sendUsage'.
20 | final bool kAnalyticsEnabled = false;
21 |
22 | Analytics _ga = new AnalyticsMock();
23 |
24 | class UsageManager implements Disposable {
25 | StreamSubscriptions _subs = new StreamSubscriptions();
26 | Disposable _editorObserve;
27 |
28 | UsageManager() {
29 | _init().then((_) => trackCommand('auto-startup'));
30 | }
31 |
32 | Future _init() {
33 | return atomPackage.getPackageVersion().then((String version) {
34 | atom.config.observe('${pluginId}.sendUsage', null, (value) {
35 | // Disable Google Analytics if the UA is the placeholder one.
36 | if (_UA.startsWith('UA-0000')) value = false;
37 |
38 | if (kAnalyticsEnabled && value == true) {
39 | _ga = new AnalyticsHtml(_UA, pluginId, version);
40 | _ga.optIn = true;
41 | _ga.sendScreenView('editor');
42 | } else {
43 | _ga = new AnalyticsMock();
44 | }
45 | });
46 |
47 | _subs.add(Logger.root.onRecord.listen(_handleLogRecord));
48 | _subs.add(atom.commands.onDidDispatch.listen(trackCommand));
49 |
50 | _editorObserve = atom.workspace.observeActivePaneItem(_activePaneItemChanged);
51 |
52 | analysisServer.onActive.listen((val) {
53 | trackCommand(val ? 'auto-analysis-server-start' : 'auto-analysis-server-stop');
54 | });
55 | });
56 | }
57 |
58 | void dispose() {
59 | trackCommand('auto-shutdown');
60 | _subs.cancel();
61 | if (_editorObserve != null) _editorObserve.dispose();
62 | }
63 | }
64 |
65 | void trackCommand(String command) {
66 | String category = 'dart';
67 |
68 | List list = command.split(':');
69 | if (list.length >= 2) {
70 | category = list[0];
71 | command = list[1];
72 | }
73 |
74 | // Ignore `core:` commands (core:confirm, core:cancel, ...).
75 | if (category == 'core') return;
76 |
77 | // Ignore `dart:newline`.
78 | if (command == 'newline') return;
79 |
80 | _ga.sendEvent(category, command);
81 | }
82 |
83 | void _activePaneItemChanged(_) {
84 | TextEditor editor = atom.workspace.getActiveTextEditor();
85 | if (editor == null || editor.getPath() == null) return;
86 |
87 | String path = editor.getPath();
88 |
89 | // Ensure that the file is Dart related.
90 | if (isDartFile(path) || projectManager.getProjectFor(path) != null) {
91 | int index = path.lastIndexOf('.');
92 | if (index == -1) {
93 | _ga.sendScreenView('editor');
94 | } else {
95 | String extension = path.substring(index + 1);
96 | _ga.sendScreenView('editor/${extension.toLowerCase()}');
97 | }
98 | }
99 | }
100 |
101 | void _handleLogRecord(LogRecord log) {
102 | if (log.level >= Level.WARNING) {
103 | bool fatal = log.level >= Level.SEVERE;
104 | String message = log.message;
105 | if (message.contains('/Users/')) {
106 | message = message.substring(0, message.indexOf('/Users/'));
107 | }
108 | String desc = '${log.loggerName}:${message}';
109 | if (log.error != null) desc += ',${log.error.runtimeType}';
110 | if (log.stackTrace != null) desc += ',${sanitizeStacktrace(log.stackTrace)}';
111 | _ga.sendException(desc, fatal: fatal);
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/lib/utils.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library atom.utils;
6 |
7 | import 'dart:async';
8 |
9 | final String loremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing "
10 | "elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "
11 | "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi "
12 | "ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit"
13 | " in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur"
14 | " sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
15 | "mollit anim id est laborum.";
16 |
17 | /// A value that fires events when it changes.
18 | class Property {
19 | T _value;
20 | StreamController _controller = new StreamController.broadcast();
21 |
22 | Property([T initialValue]) {
23 | _value = initialValue;
24 | }
25 |
26 | T get value => _value;
27 | set value(T v) {
28 | if (_value != v) {
29 | _value = v;
30 | _controller.add(_value);
31 | }
32 | }
33 |
34 | bool get hasValue => _value != null;
35 |
36 | Stream get onChanged => _controller.stream;
37 |
38 | StreamSubscription observe(callback(T t)) {
39 | callback(value);
40 | return onChanged.listen(callback);
41 | }
42 |
43 | String toString() => '${_value}';
44 | }
45 |
46 | /// A SelectionGroup:
47 | /// - manages a set of items
48 | /// - fires notifications when the set changes
49 | /// - has a notion of a 'selected' or active item
50 | class SelectionGroup {
51 | T _selection;
52 | List _items = [];
53 |
54 | StreamController _addedController = new StreamController.broadcast();
55 | StreamController _selectionChangedController = new StreamController.broadcast();
56 | StreamController _removedController = new StreamController.broadcast();
57 | StreamController _mutationController = new StreamController.broadcast();
58 |
59 | SelectionGroup();
60 |
61 | T get selection => _selection;
62 |
63 | List get items => _items;
64 |
65 | bool get isEmpty => _items.isEmpty;
66 | bool get isNotEmpty => _items.isNotEmpty;
67 | int get length => _items.length;
68 |
69 | Stream get onAdded => _addedController.stream;
70 | Stream get onSelectionChanged => _selectionChangedController.stream;
71 | Stream get onRemoved => _removedController.stream;
72 |
73 | StreamSubscription> observeMutation(callback(List list)) {
74 | callback(items);
75 | return _mutationController.stream.map((_) => items).listen(callback);
76 | }
77 |
78 | void add(T item) {
79 | _items.add(item);
80 | _addedController.add(item);
81 | _mutationController.add(item);
82 |
83 | if (_selection == null) {
84 | _selection = item;
85 | _selectionChangedController.add(selection);
86 | }
87 | }
88 |
89 | void setSelection(T sel) {
90 | if (_selection != sel) {
91 | _selection = sel;
92 | _selectionChangedController.add(selection);
93 | }
94 | }
95 |
96 | void remove(T item) {
97 | if (!_items.contains(item)) return;
98 |
99 | _items.remove(item);
100 | _removedController.add(item);
101 | _mutationController.add(item);
102 |
103 | if (_selection == item) {
104 | _selection = null;
105 | _selectionChangedController.add(null);
106 | }
107 | }
108 | }
109 |
110 | typedef Future ReturnsFuture();
111 |
112 | class FutureSerializer {
113 | List _operations = [];
114 | List> _completers = [];
115 |
116 | Future perform(ReturnsFuture operation) {
117 | Completer completer = new Completer();
118 |
119 | _operations.add(operation);
120 | _completers.add(completer);
121 |
122 | if (_operations.length == 1) {
123 | _serviceQueue();
124 | }
125 |
126 | return completer.future;
127 | }
128 |
129 | void _serviceQueue() {
130 | ReturnsFuture operation = _operations.first;
131 | Completer completer = _completers.first;
132 |
133 | Future future = operation();
134 | future.then((value) {
135 | completer.complete(value);
136 | }).catchError((e) {
137 | completer.completeError(e);
138 | }).whenComplete(() {
139 | _operations.removeAt(0);
140 | _completers.removeAt(0);
141 |
142 | if (_operations.isNotEmpty) _serviceQueue();
143 | });
144 | }
145 | }
146 |
147 | abstract class MItem {
148 | String get id;
149 | String key;
150 | }
151 |
152 | class Pair {
153 | final L left;
154 | final R right;
155 | Pair(this.left, this.right);
156 | }
157 |
158 | bool listIdentical(List a, List b) {
159 | if (a.length != b.length) return false;
160 |
161 | for (int i = 0; i < a.length; i++) {
162 | var _a = a[i];
163 | var _b = b[i];
164 | if (_a == null && _b != null) return false;
165 | if (_a != null && _b == null) return false;
166 | if (_a != _b) return false;
167 | }
168 |
169 | return true;
170 | }
171 |
--------------------------------------------------------------------------------
/menus/atom-dart.cson:
--------------------------------------------------------------------------------
1 | 'menu': [
2 | {
3 | label: 'Packages'
4 | submenu: [
5 | {
6 | label: 'Dart'
7 | submenu: [
8 | { label: 'Package Settings…', command: 'dart:settings' }
9 | { type: 'separator' }
10 | { label: 'Re-analyze Sources', command: 'dart:reanalyze-sources' }
11 | { label: 'Analysis Server Status', command: 'dart:analysis-server-status' }
12 | { type: 'separator' }
13 | { label: 'Getting Started…', command: 'dart:getting-started' }
14 | { label: 'Release Notes', command: 'dart:release-notes' }
15 | { label: 'Send Feedback…', command: 'dart:send-feedback'}
16 | ]
17 | }
18 | ]
19 | }
20 | {
21 | label: 'View'
22 | submenu: [
23 | { label: 'Toggle Outline View', command: 'dart:toggle-outline-view'}
24 | ]
25 | }
26 | ]
27 |
28 | 'context-menu':
29 | 'atom-pane[data-active-item-path$="pubspec.yaml"] atom-text-editor[data-grammar~="yaml"]': [
30 | { label: 'Pub Get', command: 'dart:pub-get'}
31 | { label: 'Pub Upgrade', command: 'dart:pub-upgrade'}
32 | { label: 'Pub Serve', command: 'dart:pub-serve'}
33 | { type: 'separator'}
34 | ]
35 | 'atom-pane[data-active-item-path$=".dart"] atom-text-editor[data-grammar~="dart"]': [
36 | { label: 'Find References…', command: 'dart:find-references' }
37 | { label: 'Format', command: 'dart:dart-format' }
38 | { label: 'Organize Directives', command: 'dart:organize-directives' }
39 | # TODO(danrubel) should rename be moved into the Refactor submenu ?
40 | { label: 'Rename…', command: 'dart:refactor-rename' }
41 | { label: 'Refactor', submenu: [
42 | { label: 'Extract Local…', command: 'dart:refactor-extract-local' }
43 | { label: 'Inline Local…', command: 'dart:refactor-inline-local' }
44 | ]}
45 | { label: 'Sort Members', command: 'dart:sort-members' }
46 | { label: 'Type Hierarchy', command: 'dart:type-hierarchy' }
47 | { label: 'Toggle Breakpoint', command: 'dart:debug-toggle-breakpoint' }
48 | { type: 'separator'}
49 | ]
50 | '.tree-view.full-menu li[is="tree-view-file"] span[data-name$=".dart"]': [
51 | { label: 'Format', command: 'dart:dart-format' }
52 | { type: 'separator'}
53 | ]
54 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dart",
3 | "main": "./web/entry.dart.js",
4 | "version": "1.0.1",
5 | "description": "A Dart plugin for Atom.",
6 | "keywords": [
7 | "dart",
8 | "dartlang"
9 | ],
10 | "repository": "https://github.com/dart-atom/dart",
11 | "license": "BSD",
12 | "engines": {
13 | "atom": ">=1.0.0 <2.0.0"
14 | },
15 | "dependencies": {
16 | "chrome-remote-interface": "0.24.3",
17 | "ws": "1.1.1"
18 | },
19 | "required-packages": [
20 | "atom-toolbar",
21 | "linter"
22 | ],
23 | "consumedServices": {
24 | "atom-toolbar": {
25 | "versions": {
26 | "^1.0.0": "consumeToolbar"
27 | }
28 | },
29 | "linter-indie": {
30 | "versions": {
31 | "2.0.0": "consumeLinter"
32 | }
33 | },
34 | "status-bar": {
35 | "versions": {
36 | "^1.0.0": "consumeStatusBar"
37 | }
38 | }
39 | },
40 | "providedServices": {
41 | "autocomplete.provider": {
42 | "versions": {
43 | "2.0.0": "provideAutocomplete"
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: atom_dart
2 | version: 1.0.1
3 | description: A Dart plugin for Atom.
4 | homepage: https://github.com/dart-atom/dart
5 |
6 | environment:
7 | sdk: '>=1.9.0 <2.0.0'
8 |
9 | dependencies:
10 | analysis_server_lib: ^0.1.2+2
11 | atom:
12 | git: https://github.com/dart-atom/atom.dart.git
13 | # path: ../atom.dart
14 | haikunator: ^0.1.0
15 | logging: ^0.11.0
16 | markdown: ^0.11.0
17 | pub_semver: ^1.2.1
18 | usage: ^1.1.0
19 | vm_service_lib: ^0.3.0
20 | yaml: ^2.0.0
21 | source_maps: "^0.10.4"
22 | petitparser: "^1.6.1"
23 |
24 | dev_dependencies:
25 | grinder: ^0.8.0+1
26 | html: ^0.12.0
27 | test: ^0.12.0
28 | which: ^0.1.0
29 |
--------------------------------------------------------------------------------
/settings/dart.cson:
--------------------------------------------------------------------------------
1 | '.source.dart':
2 | 'editor':
3 | 'commentStart': '// '
4 | 'increaseIndentPattern': '(?x)
5 | \\{ [^}"\']* $
6 | | \\[ [^\\]"\']* $
7 | | \\( [^)"\']* $
8 | '
9 | 'decreaseIndentPattern': '(?x)
10 | ^ \\s* (\\s* /[*] .* [*]/ \\s*)* [}\\])]
11 | '
12 |
--------------------------------------------------------------------------------
/settings/yaml-ext.cson:
--------------------------------------------------------------------------------
1 | '.source.yaml-ext':
2 | 'editor':
3 | 'autoIndentOnPaste': false
4 | 'commentStart': '# '
5 | 'foldEndPattern': '^\\s*$|^\\s*\\}|^\\s*\\]|^\\s*\\)'
6 | 'increaseIndentPattern': '^\\s*.*(:|-) ?(&\\w+)?(\\{[^}"\']*|\\([^)"\']*)?$'
7 | 'decreaseIndentPattern': '^\\s+\\}$'
8 |
--------------------------------------------------------------------------------
/snippets/dart.cson:
--------------------------------------------------------------------------------
1 | '.source.dart':
2 | 'main':
3 | 'prefix': 'main'
4 | 'body': """
5 | main(List args) {
6 | $1
7 | }
8 |
9 | """
10 | 'try':
11 | 'prefix': 'try'
12 | 'body': """
13 | try {
14 | $2
15 | } catch (${1:e}) {
16 |
17 | }
18 |
19 | """
20 | 'if':
21 | 'prefix': 'if'
22 | 'body': """
23 | if ($1) {
24 |
25 | }
26 |
27 | """
28 | 'if Else':
29 | 'prefix': 'ife'
30 | 'body': """
31 | if ($1) {
32 |
33 | } else {
34 |
35 | }
36 | """
37 | 'assert':
38 | 'prefix': 'assert'
39 | 'body': """
40 | assert($1);
41 |
42 | """
43 | 'print':
44 | 'prefix': 'print'
45 | 'body': """
46 | print($1);
47 | """
48 | 'for':
49 | 'prefix': 'for'
50 | 'body': """
51 | for (var i = 0; i < count; i++) {
52 |
53 | }
54 | """
55 | 'while':
56 | 'prefix': 'while'
57 | 'body': """
58 | while ($1) {
59 |
60 | }
61 | """
62 | 'void':
63 | 'prefix': 'void'
64 | 'body': """
65 | void ${1:name} () {
66 |
67 | }
68 | """
69 | 'import':
70 | 'prefix': 'import'
71 | 'body': """
72 | import '$1';
73 | """
74 | 'class':
75 | 'prefix': 'class'
76 | 'body': """
77 | class ${1:name} {
78 |
79 | }
80 | """
81 | 'typedef':
82 | 'prefix': 'typedef'
83 | 'body': """
84 | typedef ${1:type} ${2:name};
85 | """
86 |
--------------------------------------------------------------------------------
/spec/_spec/animals_test.dart:
--------------------------------------------------------------------------------
1 | import 'test.dart';
2 |
3 | register() {
4 | registerSuites([
5 | new CatTest(),
6 | new DogTest()
7 | ]);
8 | }
9 |
10 | class CatTest extends TestSuite {
11 | setUp() {
12 | print('I was set up!');
13 | }
14 |
15 | tearDown() {
16 | print('I was torn down.');
17 | }
18 |
19 | Map getTests() => {
20 | 'hasPaws': _hasPaws,
21 | 'has4Paws': _has4Paws
22 | };
23 |
24 | _hasPaws() {
25 | expect(true, true);
26 | }
27 |
28 | _has4Paws() {
29 | expect(2, 4);
30 | }
31 | }
32 |
33 | class DogTest extends TestSuite {
34 | Map getTests() => {
35 | 'fooBar': _fooBar
36 | };
37 |
38 | _fooBar() {
39 | expect(4, 4);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/spec/_spec/jasmine.dart:
--------------------------------------------------------------------------------
1 | library jasmine;
2 |
3 | //import 'dart:async';
4 | import 'dart:js';
5 |
6 | // describe("A suite", function(done) {
7 | // it("contains spec with an expectation", function() {
8 | // expect(true).toBe(true);
9 | // });
10 | // });
11 |
12 | final JsFunction _describe = context['describe'];
13 | final JsFunction _it = context['it'];
14 | final JsFunction _expect = context['expect'];
15 |
16 | typedef dynamic Callback();
17 |
18 | describe(String name, describeClosure) {
19 | return _describe.apply([name, describeClosure]);
20 | }
21 |
22 | it(String description, Callback test) {
23 | return _it.apply([description, test]);
24 | }
25 |
26 | Expectation expect(dynamic value) {
27 | return new Expectation(_expect.apply([value]));
28 | }
29 |
30 | beforeAll(Callback callback) {
31 | context.callMethod('beforeAll', [callback]);
32 | }
33 |
34 | // TODO: Re-do this to support Jasmine 1.3
35 | // (http://jasmine.github.io/1.3/introduction.html) - runs(), waitsFor(), runs()
36 | beforeEach(Callback callback) {
37 | context.callMethod('beforeEach', [callback]);
38 | // _beforeEach.apply([(_done) {
39 | // Done done = new Done(_done);
40 | // dynamic result = callback();
41 | // if (result is Future) {
42 | // result
43 | // .then((_) => done.finished())
44 | // .catchError((e) => done.fail(e));
45 | // } else {
46 | // done.finished();
47 | // }
48 | // }]);
49 | }
50 |
51 | afterEach(Callback callback) {
52 | context.callMethod('afterEach', [callback]);
53 | // _afterEach.apply([(_done) {
54 | // Done done = new Done(_done);
55 | // dynamic result = callback();
56 | // if (result is Future) {
57 | // result
58 | // .then((_) => done.finished())
59 | // .catchError((e) => done.fail(e));
60 | // } else {
61 | // done.finished();
62 | // }
63 | // }]);
64 | }
65 |
66 | afterAll(Callback callback) {
67 | context.callMethod('afterAll', [callback]);
68 | }
69 |
70 | class Expectation {
71 | final JsObject obj;
72 |
73 | Expectation(this.obj);
74 |
75 | toBe(dynamic value) {
76 | return obj.callMethod('toBe', [value]);
77 | }
78 | }
79 |
80 | // Jasmine 2.0
81 | // class Done {
82 | // final JsObject obj;
83 | //
84 | // Done(this.obj);
85 | //
86 | // finished() {
87 | // (obj as JsFunction).apply([]);
88 | // }
89 | //
90 | // fail([dynamic error]) {
91 | // if (error != null) {
92 | // obj.callMethod('fail', [error]);
93 | // } else {
94 | // obj.callMethod('fail');
95 | // }
96 | // }
97 | // }
98 |
--------------------------------------------------------------------------------
/spec/_spec/sample-spec.dart:
--------------------------------------------------------------------------------
1 |
2 | import '../_spec/jasmine.dart';
3 |
4 | void main() {
5 | for (int i in [1, 2, 3]) {
6 | describe('foo sample ${i}', () {
7 | it('is cool', () {
8 | expect(true).toBe(true);
9 | });
10 |
11 | it('so cool', () {
12 | expect(false).toBe(true);
13 | });
14 |
15 | for (String str in ['foo', 'bar', 'baz']) {
16 | it('more ${str} cool', () {
17 | expect(true).toBe(true);
18 | });
19 | }
20 | });
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/spec/_spec/test.dart:
--------------------------------------------------------------------------------
1 | library test;
2 |
3 | import 'jasmine.dart' as jasmine;
4 |
5 | typedef Test();
6 |
7 | /// TODO: doc
8 | registerSuite(TestSuite testSuite) => _registerSuite(testSuite);
9 |
10 | /// TODO: doc
11 | registerSuites(List testSuites) => testSuites.forEach(registerSuite);
12 |
13 | /// TODO: doc
14 | abstract class TestSuite {
15 | // /// Called once before any test in the test suite is run.
16 | // setUpSuite() {
17 | //
18 | // }
19 |
20 | /// Called before each test in the test suite has run. Can return a Future to
21 | /// indicate that the setup is async.
22 | setUp() {
23 |
24 | }
25 |
26 | /// Return the `` tests for this test suite.
27 | Map getTests();
28 |
29 | /// Called after each test in the test suite has run. Can return a Future to
30 | /// indicate that the tearDown is async.
31 | tearDown() {
32 |
33 | }
34 |
35 | // /// Called once after all the tests in a test suite has run.
36 | // tearDownSuite() {
37 | //
38 | // }
39 |
40 | /// Used to validate test expectations.
41 | expect(Object actual, Object expected) {
42 | jasmine.expect(actual).toBe(expected);
43 | }
44 | }
45 |
46 | // Impl.
47 |
48 | _registerSuite(TestSuite suite) {
49 | String suiteName = suite.runtimeType.toString();
50 |
51 | jasmine.describe(suiteName, () {
52 | // // Call setUpSuite.
53 | // jasmine.beforeAll(() {
54 | // return reflect(suite).invoke(#setUpSuite, []);
55 | // });
56 |
57 | // Call setUp.
58 | jasmine.beforeEach(() => suite.setUp());
59 |
60 | // Call tearDown.
61 | jasmine.afterEach(() => suite.tearDown());
62 |
63 | // // Call tearDownSuite.
64 | // jasmine.afterAll(() {
65 | // return reflect(suite).invoke(#tearDownSuite, []);
66 | // });
67 |
68 | Map tests = suite.getTests();
69 |
70 | for (String testName in tests.keys) {
71 | Test test = tests[testName];
72 | print("${suiteName} - ${testName}");
73 | jasmine.it(testName, () => test());
74 | }
75 | });
76 | }
77 |
--------------------------------------------------------------------------------
/spec/all-spec.dart:
--------------------------------------------------------------------------------
1 | //import '_spec/animals_test.dart' as animals_test;
2 | //import '_spec/sample-spec.dart' as sample_spec;
3 |
4 | import 'flutter/launch_flutter_test.dart' as launch_flutter_test;
5 | import 'projects_test.dart' as projects_test;
6 | import 'sdk_test.dart' as sdk_test;
7 |
8 | main() {
9 | //animals_test.register();
10 | //sample_spec.main();
11 | launch_flutter_test.register();
12 | projects_test.register();
13 | sdk_test.register();
14 | }
15 |
--------------------------------------------------------------------------------
/spec/flutter/launch_flutter_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:atom_dart/flutter/flutter_launch.dart';
2 |
3 | import '../_spec/test.dart';
4 |
5 | void register() {
6 | registerSuite(new FlutterUriTranslatorTest());
7 | }
8 |
9 | class FlutterUriTranslatorTest extends TestSuite {
10 | FlutterUriTranslator x = new FlutterUriTranslator('/projects/foo_bar');
11 |
12 | Map getTests() => {
13 | 'targetToClient_package': _targetToClient_package,
14 | 'targetToClient_file': _targetToClient_file,
15 | 'targetToClient_dart': _targetToClient_dart,
16 | 'clientToTarget_package': _clientToTarget_package,
17 | 'clientToTarget_file': _clientToTarget_file,
18 | 'clientToTarget_dart': _clientToTarget_dart
19 | };
20 |
21 | _targetToClient_package() {
22 | expect(
23 | x.targetToClient('package:flutter/src/material/dialog.dart'),
24 | 'package:flutter/src/material/dialog.dart'
25 | );
26 | }
27 |
28 | _targetToClient_file() {
29 | expect(
30 | x.targetToClient('/projects/foo_bar/lib/main.dart'),
31 | '/projects/foo_bar/lib/main.dart'
32 | );
33 | }
34 |
35 | _targetToClient_dart() {
36 | expect(x.targetToClient('dart:core/core.dart'), 'dart:core/core.dart');
37 | }
38 |
39 | _clientToTarget_package() {
40 | expect(
41 | x.clientToTarget('package:flutter/src/material/dialog.dart'),
42 | 'package:flutter/src/material/dialog.dart'
43 | );
44 | }
45 |
46 | _clientToTarget_file() {
47 | expect(
48 | x.clientToTarget('/projects/foo_bar/lib/main.dart'),
49 | '/projects/foo_bar/lib/main.dart'
50 | );
51 | }
52 |
53 | _clientToTarget_dart() {
54 | expect(x.clientToTarget('dart:core/core.dart'), 'dart:core/core.dart');
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/spec/projects_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:atom_dart/projects.dart';
2 | import 'package:atom/node/fs.dart';
3 |
4 | import '_spec/test.dart';
5 |
6 | void register() => registerSuite(new ProjectsTest());
7 |
8 | class ProjectsTest extends TestSuite {
9 | Map getTests() => {
10 | // 'isDartBuildFile_findsProject': isDartBuildFile_findsProject,
11 | 'isDartBuildFile_noFalsePositive': isDartBuildFile_noFalsePositives
12 | };
13 |
14 | isDartBuildFile_findsProject() {
15 | String path = _createTempFile('BUILD', '\n/dart/build_defs\n');
16 | expect(isDartBuildFile(path), true);
17 |
18 | path = _createTempFile('BUILD', '\ndart_library(\n');
19 | expect(isDartBuildFile(path), true);
20 |
21 | path = _createTempFile('BUILD', '\ndart_analyzed_library\n');
22 | expect(isDartBuildFile(path), true);
23 | }
24 |
25 | isDartBuildFile_noFalsePositives() {
26 | String path = _createTempFile('BUILD', '\ndarty\n');
27 | expect(isDartBuildFile(path), false);
28 | }
29 | }
30 |
31 | String _createTempFile(String name, String contents) {
32 | String path = fs.join(fs.tmpdir, name);
33 | fs.writeFileSync(path, contents);
34 | return path;
35 | }
36 |
--------------------------------------------------------------------------------
/spec/sdk_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:atom_dart/sdk.dart';
2 |
3 | import '_spec/test.dart';
4 |
5 | void register() => registerSuite(new SdkTest());
6 |
7 | class SdkTest extends TestSuite {
8 | Map getTests() => {
9 | '_autoConfigure': autoConfigure,
10 | '_sdkDiscovery': sdkDiscovery
11 | };
12 |
13 | SdkManager _manager;
14 |
15 | setUp() => _manager = new SdkManager();
16 | tearDown() => _manager?.dispose();
17 |
18 | autoConfigure() {
19 | return _manager.tryToAutoConfigure().then((result) {
20 | print(result);
21 | expect(result, true);
22 | });
23 | }
24 |
25 | sdkDiscovery() {
26 | return new SdkDiscovery().discoverSdk().then((String foundSdk) {
27 | print('discoverSdk: ${foundSdk}');
28 | expect(foundSdk is String, true);
29 | });
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/styles/console.less:
--------------------------------------------------------------------------------
1 | @import "ui-variables";
2 | @import "syntax-variables";
3 |
4 | .atom-view .console-view .tab-scrollable {
5 | overflow-x: auto;
6 | }
7 |
8 | .console-view {
9 | margin: 4px;
10 |
11 | .trace-link {
12 | cursor: pointer;
13 | color: @text-color-highlight;
14 | }
15 |
16 | .trace-link:hover {
17 | text-decoration: underline;
18 | }
19 |
20 | .console-line {
21 | background-color: inherit;
22 | -webkit-user-select: text;
23 | }
24 |
25 | .console-line .badge {
26 | font-size: 12px;
27 | }
28 |
29 | .console-error {
30 | color: @text-color-error;
31 |
32 | .trace-link {
33 | color: @text-color-error;
34 | }
35 | }
36 |
37 | .console-header {
38 | margin-bottom: 0.5em;
39 | }
40 |
41 | .console-footer {
42 | margin-top: 0.5em;
43 | font-style: italic;
44 | color: @text-color-subtle;
45 | }
46 | }
47 |
48 | .atom-view .launch-terminated .title {
49 | font-style: italic;
50 | }
51 |
52 | .process-status-bar {
53 | .badge {
54 | color: @text-color;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/styles/dartdoc.less:
--------------------------------------------------------------------------------
1 | @import "ui-variables";
2 | @import "syntax-variables";
3 |
4 | @font-face {
5 | font-family: 'Material Icons';
6 | font-style: normal;
7 | font-weight: 400;
8 | src: local('Material Icons'), local('MaterialIcons-Regular'), url('https://fonts.gstatic.com/s/materialicons/v13/2fcrYFNaTjcS6g4U3t-Y5UEw0lE80llgEseQY3FEmqw.woff2') format('woff2');
9 | }
10 |
11 | // Allow the material icons font in the doc tooltips and code completions.
12 | #dartdoc-tooltip,
13 | autocomplete-suggestion-list {
14 | .material-icons {
15 | font-family: 'Material Icons';
16 | font-weight: normal;
17 | font-style: normal;
18 | font-size: 24px;
19 | line-height: 1;
20 | vertical-align: bottom;
21 | letter-spacing: normal;
22 | text-transform: none;
23 | display: inline-block;
24 | white-space: nowrap;
25 | word-wrap: normal;
26 | direction: ltr;
27 | -webkit-font-feature-settings: 'liga';
28 | -webkit-font-smoothing: antialiased;
29 | }
30 | }
31 |
32 | #dartdoc-tooltip {
33 | position: absolute;
34 | top: 8px;
35 | right: 8px;
36 | left: inherit;
37 | bottom: inherit;
38 | width: 445px;
39 | padding: 0;
40 | color: @text-color;
41 | border-radius: 2px;
42 | z-index: 100;
43 | font-size: 110%;
44 | border: none;
45 |
46 | .dartdoc-title {
47 | color: @text-color-highlight;
48 | background-color: @panel-heading-background-color;
49 | border-top-left-radius: 2px;
50 | border-top-right-radius: 2px;
51 | padding: 4px 8px;
52 | }
53 |
54 | .dartdoc-footer {
55 | color: @text-color-highlight;
56 | background-color: @panel-heading-background-color;
57 | border-bottom-left-radius: 2px;
58 | border-bottom-right-radius: 2px;
59 | padding: 4px 8px;
60 | border-bottom: 0px;
61 | }
62 |
63 | .dartdoc-body {
64 | padding: 0 8px;
65 | min-height: 100px;
66 | max-height: 300px;
67 | overflow-y: scroll;
68 | position: relative;
69 | background-color: @tree-view-background-color;
70 |
71 | p {
72 | margin: 8px 0;
73 | }
74 |
75 | li p {
76 | margin: 0;
77 | }
78 |
79 | pre {
80 | margin: 0 16px;
81 | }
82 |
83 | h1, h2, h3 {
84 | margin-bottom: 0;
85 | }
86 |
87 | pre {
88 | color: @syntax-text-color;
89 | background-color: @syntax-background-color;
90 | text-shadow: none;
91 | }
92 |
93 | pre code {
94 | word-wrap: normal;
95 | white-space: pre;
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/styles/debugger.less:
--------------------------------------------------------------------------------
1 | @import "ui-variables";
2 | @import "syntax-variables";
3 |
4 | .debugger {
5 | margin: 4px;
6 |
7 | .button-bar {
8 | min-height: 22px;
9 | }
10 |
11 | .debugger-execution-toolbar {
12 | display: flex;
13 | margin-left: -3px;
14 | overflow-x: hidden;
15 | margin-top: 6px;
16 |
17 | button.btn {
18 | margin-left: 3px;
19 | }
20 | }
21 |
22 | .debugger-section {
23 | flex-shrink: 0;
24 | border-bottom: 1px solid @base-border-color;
25 | position: relative;
26 | padding-top: 8px;
27 | padding-bottom: 6px;
28 |
29 | .debugger-section-subtitle {
30 | color: @text-color-subtle;
31 | overflow: hidden;
32 | white-space: nowrap;
33 | text-overflow: ellipsis;
34 | margin-top: 5px;
35 | }
36 |
37 | label {
38 | margin-right: 5px;
39 | }
40 | }
41 |
42 | .debugger-section.view-header {
43 | padding-top: 0;
44 | }
45 |
46 | .debugger-section.debugger-section-last {
47 | border-bottom: none;
48 | padding-bottom: 0;
49 | }
50 |
51 | .debugger-section.resizable {
52 | min-width: 100px;
53 | min-height: 100px;
54 | }
55 |
56 | .debug-tab-container {
57 | flex-shrink: 0;
58 |
59 | .debug-tab-title {
60 | color: @text-color-subtle;
61 | align-self: flex-end;
62 | line-height: 22px;
63 | min-height: 22px;
64 | }
65 |
66 | .debug-tab-toolbar {
67 | min-height: 22px;
68 | }
69 | }
70 |
71 | .debugger-breakpoint-path {
72 |
73 | }
74 |
75 | .debugger-breakpoint-icon {
76 | color: @syntax-gutter-text-color;
77 | opacity: 0.6;
78 | }
79 |
80 | .debugger-secondary-info {
81 | margin-left: 0.5em;
82 | color: @text-color-subtle;
83 | }
84 |
85 | .material-list-selected .debugger-secondary-info {
86 | color: @text-color;
87 | }
88 |
89 | .debugger-frame-area {
90 | height: 156px;
91 | margin-bottom: 6px;
92 | border-bottom: 1px solid @base-border-color;
93 | }
94 |
95 | .debugger-local-area {
96 | transition: background-color 400ms;
97 | }
98 |
99 | .debugger-locked {
100 | pointer-events: none;
101 | background-color: @base-border-color;
102 | }
103 |
104 | .debugger-object-details {
105 | min-height: 1em;
106 | max-height: 5em;
107 | overflow: auto;
108 | }
109 |
110 | .right-aligned {
111 | text-align: right;
112 | }
113 |
114 | label + label {
115 | margin-left: 10px;
116 | }
117 |
118 | label {
119 | input + span {
120 | margin-left: 8px;
121 | }
122 | }
123 | }
124 |
125 | atom-text-editor.editor .debugger-breakpoint {
126 |
127 | }
128 |
129 | atom-text-editor.editor .debugger-breakpoint::before {
130 | font-family: 'Octicons Regular';
131 | font-weight: normal;
132 | font-style: normal;
133 | display: inline-block;
134 | line-height: 1;
135 | text-decoration: none;
136 | font-size: 16px;
137 | width: 16px;
138 | height: 16px;
139 | content: "\f052";
140 | position: absolute;
141 | top: 3px;
142 | right: 5px;
143 | -webkit-font-smoothing: antialiased;
144 | }
145 |
146 | atom-text-editor.editor .debugger-executionpoint-line {
147 | background-color: @syntax-selection-color;
148 | }
149 |
150 | atom-text-editor.editor .debugger-executionpoint-highlight div.region {
151 | z-index: 1;
152 | }
153 |
154 | atom-text-editor.editor .debugger-executionpoint-highlight div.region::before {
155 | font-family: 'Octicons Regular';
156 | font-weight: normal;
157 | font-style: normal;
158 | display: inline-block;
159 | line-height: 1;
160 | text-decoration: none;
161 | font-size: 12px;
162 | width: 16px;
163 | height: 16px;
164 | content: "\f0aa";
165 | position: absolute;
166 | top: 16px;
167 | right: -4px;
168 | -webkit-font-smoothing: antialiased;
169 | }
170 |
171 | atom-text-editor.editor .debugger-executionpoint-linenumber {
172 |
173 | }
174 |
175 | atom-text-editor.editor .debugger-executionpoint-linenumber::before {
176 | font-family: 'Octicons Regular';
177 | font-weight: normal;
178 | font-style: normal;
179 | display: inline-block;
180 | line-height: 1;
181 | text-decoration: none;
182 | font-size: 18px;
183 | width: 16px;
184 | height: 16px;
185 | content: "\f03e";
186 | position: absolute;
187 | top: 1px;
188 | right: 2px;
189 | -webkit-font-smoothing: antialiased;
190 | }
191 |
--------------------------------------------------------------------------------
/styles/material.less:
--------------------------------------------------------------------------------
1 | @import "ui-variables";
2 |
3 | .material-icon-button {
4 | display: inline-block;
5 |
6 | width: 22px;
7 | margin: 1px;
8 | text-align: center;
9 | border-radius: 50%;
10 | transition: background-color 40ms ease;
11 |
12 | span {
13 | display: inline-block;
14 | margin: 3px;
15 | }
16 | }
17 |
18 | .material-icon-button:hover {
19 | background-color: @background-color-highlight;
20 | color: @text-color-highlight;
21 | }
22 |
23 | .material-icon-button:active {
24 | background-color: @background-color-selected;
25 | }
26 |
27 | .material-icon-button[disabled] {
28 | color: @text-color-subtle;
29 | background-color: inherit !important;
30 | }
31 |
32 | .material-tabgroup {
33 | .material-tab-container {
34 | overflow-x: hidden;
35 | display: flex;
36 | margin-bottom: 2px;
37 | }
38 |
39 | .material-tab {
40 | font-size: 1.1em;
41 | text-transform: uppercase;
42 | cursor: default;
43 | display: inline;
44 | }
45 |
46 | .material-tab + .material-tab {
47 | margin-left: 0.8em;
48 | }
49 |
50 | .material-tab.tab-selected {
51 | color: @text-color-highlight;
52 | }
53 |
54 | .material-tab:hover {
55 | color: @text-color-highlight;
56 | text-decoration: underline;
57 | }
58 |
59 | .material-tab[disabled] {
60 | color: @text-color-subtle;
61 | text-decoration: none;
62 | background-color: inherit;
63 | }
64 | }
65 |
66 | .material-list {
67 | ul,
68 | ol {
69 | margin-bottom: 0;
70 | padding-left: 0;
71 | overflow-y: auto;
72 | overflow-x: hidden;
73 | cursor: default;
74 | }
75 |
76 | li {
77 | cursor: pointer;
78 | display: block;
79 | line-height: @component-line-height;
80 | min-height: @component-line-height;
81 | padding: 0 4px;
82 | white-space: nowrap;
83 | text-overflow: ellipsis;
84 | overflow-x: hidden;
85 |
86 | .material-icon-button span {
87 | margin: 0;
88 | }
89 | }
90 |
91 | li:hover {
92 | background-color: @button-background-color-hover;
93 | }
94 |
95 | li.material-list-selected {
96 | color: @text-color-selected;
97 | background-color: @background-color-selected;
98 | }
99 |
100 | ul.material-list-indent {
101 | margin-left: @component-icon-padding + @component-icon-size;
102 | }
103 |
104 | .icon-triangle-right::before {
105 | position: relative;
106 | left: 3px;
107 | }
108 | }
109 |
110 | .hideable {
111 | opacity: 1;
112 | transition: opacity 100ms ease;
113 | }
114 |
115 | .hideable.hiding {
116 | opacity: 0;
117 | }
118 |
--------------------------------------------------------------------------------
/styles/outline.less:
--------------------------------------------------------------------------------
1 | @import "ui-variables";
2 | @import "syntax-variables";
3 |
4 | .outline-view {
5 | border-left: 1px solid @tool-panel-border-color;
6 |
7 | order: 2;
8 | min-width: 175px;
9 | overflow-x: hidden;
10 |
11 | display: flex;
12 | position: relative;
13 | flex-direction: column;
14 |
15 | .title-container {
16 | border-bottom: 1px solid @tool-panel-border-color;
17 | margin: 6px;
18 | padding-bottom: 6px;
19 | display: flex;
20 | flex-direction: row;
21 | flex-shrink: 0;
22 | }
23 |
24 | .title-text {
25 | cursor: default;
26 | text-overflow: ellipsis;
27 | overflow-x: hidden;
28 | flex: 20;
29 | white-space: pre;
30 | }
31 |
32 | .outline-tree {
33 | flex: 20;
34 | padding: 0 6px 6px 6px;
35 | overflow-y: auto;
36 | }
37 |
38 | .outline-errors {
39 | margin: 6px;
40 | border-top: 1px solid @tool-panel-border-color;
41 | padding-top: 6px;
42 | }
43 |
44 | .errors-list {
45 | max-height: 82px;
46 | overflow-x: hidden;
47 | overflow-y: auto;
48 |
49 | .outline-error-item {
50 | cursor: pointer;
51 | overflow-x: hidden;
52 | white-space: nowrap;
53 | text-overflow: ellipsis;
54 | display: flex;
55 |
56 | .item-info {
57 | color: @syntax-color-renamed;
58 | flex: 0 0 auto;
59 | }
60 |
61 | .item-warning {
62 | color: @syntax-color-modified;
63 | flex: 0 0 auto;
64 | }
65 |
66 | .item-error {
67 | color: @syntax-color-removed;
68 | flex: 0 0 auto;
69 | }
70 |
71 | .item-text {
72 | flex: 1 1 auto;
73 | overflow-x: inherit;
74 | text-overflow: inherit;
75 | padding-left: 8px;
76 | }
77 |
78 | .item-icon {
79 | flex: 0 0 auto;
80 | }
81 | }
82 | }
83 |
84 | ul,
85 | ol {
86 | padding-left: 12px;
87 | }
88 |
89 | ol,
90 | ul,
91 | li {
92 | list-style: none;
93 | margin-top: 0;
94 | margin-bottom: 0;
95 | }
96 |
97 | li {
98 | cursor: default;
99 | text-overflow: ellipsis;
100 | overflow-x: hidden;
101 | }
102 |
103 | .list-item {
104 | white-space: nowrap;
105 | }
106 |
107 | .list-item.region {
108 | font-weight: bold;
109 | background-color: @background-color-selected;
110 | }
111 |
112 | .list-item:hover {
113 | background-color: @background-color-highlight;
114 | }
115 |
116 | .outline-deprecated {
117 | text-decoration: line-through;
118 | }
119 |
120 | .outline-static {
121 | font-style: italic;
122 | }
123 |
124 | .muted {
125 | color: @text-color-subtle;
126 | }
127 |
128 | a {
129 | cursor: pointer;
130 | }
131 |
132 | a:hover {
133 | text-decoration: underline;
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/styles/status.less:
--------------------------------------------------------------------------------
1 | @import "ui-variables";
2 |
3 | .plugin-status {
4 | margin: 4px;
5 |
6 | .view-section {
7 | transition: color 0.4s, background-color 0.4s;
8 |
9 | .status-header {
10 | margin-bottom: 6px;
11 | overflow-x: hidden;
12 | text-overflow: ellipsis;
13 | display: flex;
14 | align-items: baseline;
15 |
16 | .view-subtitle {
17 | margin-left: 0.3em;
18 | }
19 | }
20 |
21 | button {
22 | margin-left: 6px;
23 | }
24 |
25 | div.bottom-margin {
26 | margin-bottom: 6px;
27 | }
28 |
29 | .diagnostics-title {
30 | display: inline-block;
31 | min-width: 8em;
32 | text-align: right;
33 | }
34 |
35 | .diagnostics-data {
36 | margin-left: 0.5em;
37 | color: @text-color-subtle;
38 | }
39 | }
40 |
41 | .view-section.status-emphasis {
42 | background-color: @background-color-highlight;
43 |
44 | .status-header {
45 | color: @text-color-highlight;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/styles/tooltip.less:
--------------------------------------------------------------------------------
1 | #hover-tooltip {
2 | background-color: #226AEF;
3 | border-radius: 2px;
4 | bottom: 0px;
5 | box-shadow: 0px 0px 8px #13213C;
6 | color: #FFF;
7 | font-size: 14px;
8 | left: 0px;
9 | padding: 5px 10px;
10 | position: absolute;
11 | right: inherit;
12 | top: inherit;
13 | z-index: 10000;
14 | display: flex;
15 | flex-direction: column;
16 | }
17 |
18 | #hover-tooltip .debugger-data {
19 | display: flex;
20 | flex-direction: column;
21 | }
22 |
23 | #hover-tooltip .debugger-data {
24 | flex-basis: 26px;
25 | }
26 |
27 | #hover-tooltip .debugger-data.expandable {
28 | flex-basis: 148px;
29 | }
30 |
31 | #hover-tooltip ::-webkit-scrollbar-track {
32 | background: transparent;
33 | }
34 |
35 | #hover-tooltip .material-list {
36 | ul,
37 | ol {
38 | margin-bottom: 0;
39 | padding-left: 0;
40 | overflow-y: auto;
41 | overflow-x: hidden;
42 | cursor: default;
43 | }
44 |
45 | li {
46 | cursor: pointer;
47 | display: block;
48 | line-height: @component-line-height;
49 | min-height: @component-line-height;
50 | padding: 0 4px;
51 | white-space: nowrap;
52 | text-overflow: ellipsis;
53 | overflow-x: hidden;
54 |
55 | .material-icon-button span {
56 | margin: 0;
57 | }
58 | }
59 |
60 | li:hover {
61 | background-color: transparent;
62 | }
63 |
64 | li.material-list-selected {
65 | color: @text-color-selected;
66 | background-color: transparent;
67 | }
68 |
69 | ul.material-list-indent {
70 | margin-left: @component-icon-padding + @component-icon-size;
71 | }
72 |
73 | .icon-triangle-right::before {
74 | position: relative;
75 | left: 3px;
76 | }
77 |
78 | .debugger-secondary-info {
79 | margin-left: 0.5em;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/styles/views.less:
--------------------------------------------------------------------------------
1 | @import "ui-variables";
2 |
3 | div.atom-view {
4 | min-width: 100px;
5 | min-height: 64px;
6 | height: 100%;
7 | padding-left: 0;
8 | padding-right: 0;
9 | }
10 |
11 | .atom-view {
12 | // Contains zero or more tab contents.
13 | .tab-container {
14 | padding: 0 8px;
15 | display: flex;
16 | }
17 |
18 | // The style for content contained in a tab.
19 | .tab-content {
20 | position: relative;
21 | flex: 1;
22 | display: flex;
23 | flex-direction: column;
24 | width: 100%;
25 | }
26 |
27 | .tab-scrollable-container {
28 | display: flex;
29 | flex-direction: column;
30 | overflow: hidden;
31 | }
32 |
33 | // A scrollable area in a tab view.
34 | .tab-scrollable {
35 | overflow-y: auto;
36 | overflow-x: hidden;
37 | flex: 1;
38 | padding: 8px 0;
39 | }
40 |
41 | .tab-non-scrollable {
42 | flex: 1;
43 | padding: 8px 0;
44 | }
45 |
46 | .button-bar {
47 | position: absolute;
48 | top: 8px;
49 | right: 16px;
50 | display: flex;
51 | align-items: center;
52 | z-index: 1;
53 |
54 | .icon::before {
55 | font-size: 14px;
56 | width: 14px;
57 | height: 14px;
58 | }
59 | }
60 |
61 | .view-header {
62 | padding-bottom: 6px;
63 | border-bottom: 1px solid @base-border-color;
64 | flex-shrink: 0;
65 |
66 | .view-subtitle {
67 | margin-top: 2px;
68 | }
69 | }
70 |
71 | .view-title {
72 | font-size: 1.25em;
73 | overflow: hidden;
74 | white-space: nowrap;
75 | text-overflow: ellipsis;
76 | }
77 |
78 | .view-subtitle {
79 | color: @text-color-subtle;
80 | overflow-x: hidden;
81 | white-space: nowrap;
82 | text-overflow: ellipsis;
83 | }
84 |
85 | .view-header-static {
86 | margin-top: 8px;
87 | }
88 |
89 | .view-resize[vertical] {
90 | cursor: ew-resize;
91 | z-index: 3;
92 | position: absolute;;
93 | top: 0;
94 | bottom: 0;
95 | width: 8px;
96 | }
97 |
98 | .view-resize[horizontal] {
99 | cursor: ns-resize;
100 | z-index: 3;
101 | position: absolute;;
102 | left: 0;
103 | right: 0;
104 | top: 0;
105 | height: 8px;
106 | }
107 |
108 | .view-resize[horizontal][top] {
109 | top: inherit;
110 | bottom: 0;
111 | }
112 |
113 | .view-section {
114 | padding-bottom: 8px;
115 | margin-bottom: 8px;
116 | border-bottom: 1px solid @base-border-color;
117 |
118 | .view-title {
119 | font-size: 1.1em;
120 | }
121 |
122 | .view-section-buttons {
123 | text-align: end;
124 | margin-top: 6px;
125 |
126 | button {
127 | margin-left: 6px;
128 | }
129 | }
130 | }
131 |
132 | .view-section.view-section-last {
133 | padding-bottom: 0;
134 | margin-bottom: 0;
135 | border-bottom: none;
136 | }
137 | }
138 |
139 | .atom-view[compact] {
140 | .tab-container {
141 | padding: 0 4px;
142 | }
143 |
144 | .tab-scrollable {
145 | padding: 4px 0;
146 | }
147 | }
148 |
149 | .atom-view {
150 | .list-tree {
151 | .list-item,
152 | li.list-nested-item div.list-item {
153 | text-overflow: ellipsis;
154 | overflow-x: hidden;
155 | white-space: nowrap;
156 | line-height: 22px;
157 | }
158 |
159 | li.list-nested-item.collapsed > div.list-item::before {
160 | content: "\f078";
161 | }
162 |
163 | li.list-nested-item.collapsed > ul.list-tree {
164 | display: none;
165 | }
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/test/all.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library atom.all_test;
6 |
7 | import 'dartino_util_test.dart' as dartino_util_test;
8 | import 'dependencies_test.dart' as dependencies_test;
9 | import 'evaluator_test.dart' as evaluator_test;
10 | import 'testing_utils_test.dart' as testing_utils_test;
11 | import 'utils_test.dart' as utils_test;
12 |
13 | main() {
14 | dartino_util_test.defineTests();
15 | dependencies_test.defineTests();
16 | evaluator_test.defineTests();
17 | testing_utils_test.defineTests();
18 | utils_test.defineTests();
19 | }
20 |
--------------------------------------------------------------------------------
/test/dartino_util_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library atom.dartino_util_test;
6 |
7 | import 'package:atom_dart/dartino/dartino_util.dart';
8 | import 'package:test/test.dart';
9 |
10 | main() => defineTests();
11 |
12 | defineTests() {
13 | group('dartino', () {
14 | group('packages file', () {
15 | test('random', () {
16 | expect(containsDartinoReferences(null, null), isFalse);
17 | expect(containsDartinoReferences(null, ''), isFalse);
18 | expect(containsDartinoReferences('', null), isFalse);
19 | expect(containsDartinoReferences(null, ''), isFalse);
20 | expect(containsDartinoReferences(null, 'foo'), isFalse);
21 | expect(containsDartinoReferences('foo', 'bar'), isFalse);
22 | expect(containsDartinoReferences('asd aesfse', 'asd'), isFalse);
23 | });
24 | test('non Dartino package file', () {
25 | bool actual = containsDartinoReferences(
26 | '''# Generated by pub
27 | analyzer:file:///Users/foo/bar/lib/
28 | ansicolor:file:///Users/foo/two/lib/
29 | args:file:///Users/foo/three/lib/
30 | async:file:///Users/foo/four/lib/
31 | ''',
32 | '/path/to/dartino-sdk');
33 | expect(actual, isFalse);
34 | });
35 | test('Dartino package file', () {
36 | bool actual = containsDartinoReferences(
37 | '''# Generated by pub
38 | analyzer:file:///Users/foo/bar/lib/
39 | ansicolor:file:///path/to/dartino-sdk/pkg/dartino/lib/
40 | args:file:///Users/foo/three/lib/
41 | async:file:///Users/foo/four/lib/
42 | ''',
43 | '/path/to/dartino-sdk');
44 | expect(actual, isTrue);
45 | });
46 | });
47 | });
48 | }
49 |
--------------------------------------------------------------------------------
/test/dependencies_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library atom.dependencies_test;
6 |
7 | import 'package:atom/utils/dependencies.dart';
8 | import 'package:test/test.dart';
9 |
10 | main() => defineTests();
11 |
12 | defineTests() {
13 | group('dependencies', () {
14 | test('retrieve dependency', () {
15 | Dependencies dependency = new Dependencies();
16 | expect(dependency[String], isNull);
17 | dependency[String] = 'foo';
18 | expect(dependency[String], isNotNull);
19 | expect(dependency[String], 'foo');
20 | });
21 |
22 | test('runInZone', () {
23 | expect(Dependencies.instance, isNull);
24 | Dependencies dependency = new Dependencies();
25 | expect(Dependencies.instance, isNull);
26 | dependency[String] = 'foo';
27 | dependency.runInZone(() {
28 | expect(Dependencies.instance, isNotNull);
29 | expect(dependency[String], 'foo');
30 | });
31 | expect(Dependencies.instance, isNull);
32 | });
33 | });
34 | }
35 |
--------------------------------------------------------------------------------
/test/evaluator_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library atom.evaluator_test;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:test/test.dart';
10 | import 'package:petitparser/petitparser.dart';
11 |
12 | import '../lib/debug/evaluator.dart';
13 |
14 | main() => defineTests();
15 |
16 | defineTests() {
17 | group('Evaluator', () {
18 | Future testExpression(String input, [String expected]) async {
19 | EvaluatorReverseParser parser = new EvaluatorReverseParser();
20 | dynamic eval = parser.parse(parser.reverseString(input), 1000);
21 | EvalExpression expression = new EvalExpression('file.dart', eval);
22 | Evaluator evaluator = new Evaluator(expression);
23 | String result = await evaluator.eval();
24 | expect(result, expected ?? input);
25 | }
26 |
27 | Future failParse(String input) async {
28 | EvaluatorReverseParser parser = new EvaluatorReverseParser();
29 | expect(parser.reverseContextParser.parse(parser.reverseString(input)),
30 | new isInstanceOf());
31 | }
32 |
33 | test('Testing a', () => testExpression('a'));
34 | test('Testing !a', () => testExpression('!a', 'a'));
35 | test('Testing a.b', () => testExpression('a.b'));
36 | test('Testing a.b.c', () => testExpression('a.b.c'));
37 | test('Testing a. b . c', () => testExpression('a. b . c', 'a.b.c'));
38 | test('Testing a . !b . c', () => testExpression('a . !b . c', 'b.c'));
39 | test('Testing a[c].b', () => testExpression('a[c].b'));
40 | test('Testing a[1].b', () => testExpression('a[1].b'));
41 | test('Testing a[1].b[1]', () => testExpression('a[1].b[1]'));
42 | test('Testing a[b[2]].c', () => testExpression('a[b[2]].c'));
43 |
44 | test('Failing on !', () => failParse('!'));
45 | // reverse parser we should never have a non id at the right
46 | test('Failing on a!', () => failParse('a!'));
47 | });
48 | }
49 |
--------------------------------------------------------------------------------
/test/testing_utils_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:atom_dart/impl/testing_utils.dart';
2 | import 'package:test/test.dart';
3 |
4 | main() => defineTests();
5 |
6 | defineTests() {
7 | group('test_utils', () {
8 | test('getPossibleTestPaths', () {
9 | expect(getPossibleTestPaths('bin/foo.dart', '/'), equals([]));
10 | expect(
11 | getPossibleTestPaths('lib/foo.dart', '/'),
12 | equals(['test/foo_test.dart'])
13 | );
14 | expect(
15 | getPossibleTestPaths('lib/bar/foo.dart', '/'),
16 | equals(['test/bar/foo_test.dart', 'test/foo_test.dart'])
17 | );
18 | expect(
19 | getPossibleTestPaths('lib/src/foo.dart', '/'),
20 | equals(['test/src/foo_test.dart', 'test/foo_test.dart'])
21 | );
22 | expect(
23 | getPossibleTestPaths('lib/src/bar/foo.dart', '/'),
24 | equals(['test/src/bar/foo_test.dart', 'test/bar/foo_test.dart', 'test/foo_test.dart'])
25 | );
26 | });
27 | });
28 | }
29 |
--------------------------------------------------------------------------------
/test/utils_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library atom.utils_test;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:atom/utils/string_utils.dart';
10 | import 'package:atom_dart/utils.dart';
11 | import 'package:test/test.dart';
12 |
13 | main() => defineTests();
14 |
15 | defineTests() {
16 | group('utils', () {
17 | test('toStartingLowerCase', () {
18 | expect(toStartingLowerCase(''), '');
19 | expect(toStartingLowerCase('a'), 'a');
20 | expect(toStartingLowerCase('A'), 'a');
21 | expect(toStartingLowerCase('ABC'), 'aBC');
22 | expect(toStartingLowerCase('abc'), 'abc');
23 | });
24 |
25 | test('simpleDiff 1', () {
26 | _checkDiff(simpleDiff('aabcc', 'aacc'), new Edit(2, 1, ''));
27 | });
28 |
29 | test('simpleDiff 2', () {
30 | _checkDiff(simpleDiff('aaa', 'bbb'), new Edit(0, 3, 'bbb'));
31 | });
32 |
33 | test('simpleDiff 3', () {
34 | _checkDiff(simpleDiff('aabb', 'aabbc'), new Edit(4, 0, 'c'));
35 | });
36 |
37 | test('simpleDiff 4', () {
38 | _checkDiff(simpleDiff('abbb', 'bbb'), new Edit(0, 1, ''));
39 | });
40 |
41 | test('simpleDiff 5', () {
42 | _checkDiff(simpleDiff('aabb', 'aabb'), new Edit(0, 0, ''));
43 | });
44 |
45 | test('simpleDiff 6', () {
46 | _checkDiff(simpleDiff('', 'aabb'), new Edit(0, 0, 'aabb'));
47 | });
48 |
49 | test('simpleDiff 7', () {
50 | _checkDiff(simpleDiff('aabb', ''), new Edit(0, 4, ''));
51 | });
52 | });
53 |
54 | group('Property', () {
55 | test('mutate value', () {
56 | Property p = new Property();
57 | expect(p.value, null);
58 | p.value = 123;
59 | expect(p.value, 123);
60 | });
61 |
62 | test('mutation fires event', () {
63 | Property p = new Property();
64 | expect(p.value, null);
65 | Future f = p.onChanged.first;
66 | p.value = '123';
67 | expect(p.value, '123');
68 | return f.then((val) => expect(val, '123'));
69 | });
70 | });
71 |
72 | group('SelectionGroup', () {
73 | test('adding changes selection', () {
74 | SelectionGroup group = new SelectionGroup();
75 | Future f = group.onSelectionChanged.first.then((sel) {
76 | expect(sel, 'foo');
77 | });
78 | group.add('foo');
79 | return f;
80 | });
81 |
82 | test('removing changes selection', () {
83 | SelectionGroup group = new SelectionGroup();
84 | group.add('foo');
85 | Future f = group.onSelectionChanged.first.then((sel) {
86 | expect(sel, null);
87 | });
88 | group.remove('foo');
89 | return f;
90 | });
91 | });
92 | }
93 |
94 | _checkDiff(List edits, Edit expectEdit) {
95 | expect(edits.length, 1);
96 | expect(edits.first, expectEdit);
97 | }
98 |
--------------------------------------------------------------------------------
/tool/grind.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library atom.grind;
6 |
7 | import 'dart:io';
8 |
9 | import 'package:grinder/grinder.dart';
10 | import 'package:which/which.dart';
11 |
12 | import 'package:atom/build/build.dart';
13 | import 'package:atom/build/publish.dart';
14 |
15 | main(List args) => grind(args);
16 |
17 | @Task()
18 | analyze() => new PubApp.global('tuneup').runAsync(['check', '--ignore-infos']);
19 |
20 | @DefaultTask()
21 | build() async {
22 | File inputFile = getFile('web/entry.dart');
23 | File outputFile = getFile('web/entry.dart.js');
24 |
25 | // --trust-type-annotations? --trust-primitives?
26 | await Dart2js.compileAsync(inputFile, csp: true, extraArgs: ['--show-package-warnings']);
27 | outputFile.writeAsStringSync(patchDart2JSOutput(outputFile.readAsStringSync()));
28 | }
29 |
30 | @Task('Build the Atom tests')
31 | buildAtomTests() async {
32 | final String base = 'spec/all-spec';
33 | File inputFile = getFile('${base}.dart');
34 | File outputFile = getFile('${base}.js');
35 | await Dart2js.compileAsync(inputFile, csp: true, outFile: outputFile);
36 | delete(getFile('${base}.js.deps'));
37 | }
38 |
39 | @Task('Run the Atom tests')
40 | @Depends(buildAtomTests)
41 | runAtomTests() async {
42 | String apmPath = whichSync('apm', orElse: () => null);
43 |
44 | if (apmPath != null) {
45 | await runAsync('apm', arguments: ['test']);
46 | } else {
47 | log("warning: command 'apm' not found");
48 | }
49 | }
50 |
51 | @Task()
52 | @Depends(build) //analyze, build, test, runAtomTests)
53 | publish() => publishAtomPlugin();
54 |
55 | @Task()
56 | test() => Dart.runAsync('test/all.dart');
57 |
58 | @Task()
59 | @Depends(analyze, build, test, runAtomTests)
60 | bot() => null;
61 |
62 | @Task()
63 | clean() {
64 | delete(getFile('web/entry.dart.js'));
65 | delete(getFile('web/entry.dart.js.deps'));
66 | delete(getFile('web/entry.dart.js.map'));
67 | }
68 |
69 | @Task('generate the analysis server API')
70 | analysisApi() {
71 | // https://github.com/dart-lang/sdk/blob/master/pkg/analysis_server/tool/spec/spec_input.html
72 | Dart.run('tool/analysis/generate_analysis.dart', packageRoot: 'packages');
73 | DartFmt.format('lib/analysis/analysis_server_lib.dart', lineLength: 90);
74 | }
75 |
76 | @Task()
77 | @Depends(analysisApi)
78 | generate() => null;
79 |
--------------------------------------------------------------------------------
/tool/source_map.dart:
--------------------------------------------------------------------------------
1 | library source_map;
2 |
3 | import 'dart:async';
4 | import 'dart:convert' show LineSplitter, UTF8;
5 | import 'dart:io';
6 |
7 | import 'package:source_maps/source_maps.dart';
8 |
9 | void main(List args) {
10 | Map> files = {};
11 | Map maps = {};
12 |
13 | List futures = [];
14 | //for (var file in ['main.dart', 'main.dart.js', 'main.dart.js.map']) {
15 | for (var file in ['main.dart', 'web__main.js', 'web__main.js.map']) {
16 | futures.add(getFile(file).then((lines) => files[file] = lines));
17 | }
18 | Future.wait(futures).then((_) {
19 | for (var file in files.keys) {
20 | if (file.endsWith('.map')) {
21 | SingleMapping map = parse(files[file].join());
22 | maps[file.substring(0, file.length - 4)] = map;
23 | }
24 | }
25 | for (var file in maps.keys) {
26 | SingleMapping map = maps[file];
27 | List source = files[file];
28 | Set unknownUrls = new Set();
29 |
30 | for (var line in map.lines) {
31 | if (line.entries.isEmpty) continue;
32 | bool output = false;
33 | for (var entry in line.entries) {
34 | if (entry.sourceUrlId == null || entry.sourceUrlId < 0) continue;
35 | String destinationFile = map.urls[entry.sourceUrlId];
36 | if (files[destinationFile] != null) {
37 | if (!output) {
38 | print('${line.line}:${source[line.line]}');
39 | output = true;
40 | }
41 | List destination = files[destinationFile];
42 | print('->@${entry.column}[$destinationFile:${entry.sourceLine},${entry.sourceColumn}]'
43 | '${destination[entry.sourceLine]}');
44 | if (entry.sourceNameId != null && entry.sourceNameId >= 0) {
45 | print(' ${map.names[entry.sourceNameId]}');
46 | }
47 | } else {
48 | unknownUrls.add(destinationFile);
49 | }
50 | }
51 | }
52 | print('UNKNOWN URLS: $unknownUrls');
53 | }
54 | }).catchError((e) {
55 | print(e);
56 | });
57 | }
58 |
59 | Future> getFile(String filename) {
60 | HttpClient client = new HttpClient();
61 | return client.get('localhost', 8081, filename).then((request) {
62 | request.headers.contentType
63 | = new ContentType("text", "plain", charset: "utf-8");
64 | return request.close();
65 | }).then((response) {
66 | return UTF8.decodeStream(response);
67 | }).then((t) {
68 | var ret = LineSplitter.split(t).toList();
69 | print('loaded: $filename (${ret.length})');
70 | return ret;
71 | });
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/tool/src/parser.dart:
--------------------------------------------------------------------------------
1 |
2 | library parser;
3 |
4 | import 'src_gen.dart';
5 |
6 | class Token {
7 | static final RegExp _alpha = new RegExp(r'^[0-9a-zA-Z_\-@]+$');
8 |
9 | final String text;
10 | Token next;
11 |
12 | Token(this.text);
13 |
14 | bool get eof => text == null;
15 |
16 | bool get isName {
17 | if (text == null || text.isEmpty) return false;
18 | return _alpha.hasMatch(text);
19 | }
20 |
21 | bool get isComment => text != null && text.startsWith('//');
22 |
23 | String toString() => text == null ? 'EOF' : text;
24 | }
25 |
26 | class Tokenizer {
27 | static final alphaNum =
28 | '@abcdefghijklmnopqrstuvwxyz-_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
29 | static final whitespace = ' \n\t\r';
30 |
31 | String text;
32 | Token _head;
33 | Token _last;
34 |
35 | Tokenizer(this.text);
36 |
37 | Token tokenize() {
38 | _emit(null);
39 |
40 | for (int i = 0; i < text.length; i++) {
41 | String c = text[i];
42 |
43 | if (whitespace.contains(c)) {
44 | // skip
45 | } else if (c == '/' && _peek(i) == '/') {
46 | int index = text.indexOf('\n', i);
47 | if (index == -1) index = text.length;
48 | _emit(text.substring(i, index));
49 | i = index;
50 | } else if (alphaNum.contains(c)) {
51 | int start = i;
52 |
53 | while (alphaNum.contains(_peek(i))) {
54 | i++;
55 | }
56 |
57 | _emit(text.substring(start, i + 1));
58 | } else {
59 | _emit(c);
60 | }
61 | }
62 |
63 | _emit(null);
64 |
65 | _head = _head.next;
66 |
67 | return _head;
68 | }
69 |
70 | void _emit(String value) {
71 | Token token = new Token(value);
72 | if (_head == null) _head = token;
73 | if (_last != null) _last.next = token;
74 | _last = token;
75 | }
76 |
77 | String _peek(int i) {
78 | i += 1;
79 | return i < text.length ? text[i] :new String.fromCharCodes([0]);
80 | }
81 |
82 | String toString() {
83 | StringBuffer buf = new StringBuffer();
84 |
85 | Token t = _head;
86 |
87 | buf.write('[${t}]\n');
88 |
89 | while (!t.eof) {
90 | t = t.next;
91 | buf.write('[${t}]\n');
92 | }
93 |
94 | return buf.toString().trim();
95 | }
96 | }
97 |
98 | abstract class Parser {
99 | final Token startToken;
100 |
101 | Token current;
102 |
103 | Parser(this.startToken);
104 |
105 | Token expect(String text) {
106 | Token t = advance();
107 | if (text != t.text) fail('expected ${text}, got ${t}');
108 | return t;
109 | }
110 |
111 | bool consume(String text) {
112 | if (peek().text == text) {
113 | advance();
114 | return true;
115 | } else {
116 | return false;
117 | }
118 | }
119 |
120 | Token peek() => current.eof ? current : current.next;
121 |
122 | Token expectName() {
123 | Token t = advance();
124 | if (!t.isName) fail('expected name token, got ${t}');
125 | return t;
126 | }
127 |
128 | Token advance() {
129 | if (current == null) {
130 | current = startToken;
131 | } else if (!current.eof) {
132 | current = current.next;
133 | }
134 |
135 | return current;
136 | }
137 |
138 | String collectComments() {
139 | StringBuffer buf = new StringBuffer();
140 |
141 | while (peek().isComment) {
142 | Token t = advance();
143 | String str = t.text.substring(2);
144 | buf.write(' ${str}');
145 | }
146 |
147 | if (buf.isEmpty) return null;
148 | return collapseWhitespace(buf.toString()).trim();
149 | }
150 |
151 | void validate(bool result, String message) {
152 | if (!result) throw 'expected ${message}';
153 | }
154 |
155 | void fail(String message) => throw message;
156 | }
157 |
--------------------------------------------------------------------------------
/tool/src/src_gen.dart:
--------------------------------------------------------------------------------
1 |
2 | /// A library to generate Dart source code.
3 | library src_gen;
4 |
5 | const int RUNE_SPACE = 32;
6 | const int RUNE_EOL = 10;
7 | const int RUNE_LEFT_CURLY = 123;
8 | const int RUNE_RIGHT_CURLY = 125;
9 |
10 | final RegExp _wsRegexp = new RegExp(r'\s+');
11 |
12 | String collapseWhitespace(String str) => str.replaceAll(_wsRegexp, ' ');
13 |
14 | /// foo ==> Foo
15 | String titleCase(String str) =>
16 | str.substring(0, 1).toUpperCase() + str.substring(1);
17 |
18 | /// FOO ==> Foo
19 | String forceTitleCase(String str) {
20 | if (str == null || str.isEmpty) return str;
21 | return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
22 | }
23 |
24 | String joinLast(Iterable strs, String join, [String last]) {
25 | if (strs.isEmpty) return '';
26 | List list = strs.toList();
27 | if (list.length == 1) return list.first;
28 | StringBuffer buf = new StringBuffer();
29 | for (int i = 0; i < list.length; i++) {
30 | if (i > 0) {
31 | if (i + 1 == list.length && last != null) {
32 | buf.write(last);
33 | } else {
34 | buf.write(join);
35 | }
36 | }
37 | buf.write(list[i]);
38 | }
39 | return buf.toString();
40 | }
41 |
42 | /**
43 | * A class used to generate Dart source code. This class facilitates writing out
44 | * dartdoc comments, automatically manages indent by counting curly braces, and
45 | * automatically wraps doc comments on 80 char column boundaries.
46 | */
47 | class DartGenerator {
48 | static const DEFAULT_COLUMN_BOUNDARY = 80;
49 |
50 | final int colBoundary;
51 |
52 | String _indent = "";
53 | final StringBuffer _buf = new StringBuffer();
54 |
55 | bool _previousWasEol = false;
56 |
57 | DartGenerator({this.colBoundary: DEFAULT_COLUMN_BOUNDARY});
58 |
59 | /**
60 | * Write out the given dartdoc text, wrapping lines as necessary to flow
61 | * along the column boundary. If [preferSingle] is true, and the docs would
62 | * fit on a single line, use `///` dartdoc style.
63 | */
64 | void writeDocs(String docs) {
65 | if (docs == null) return;
66 |
67 | docs = wrap(docs.trim(), colBoundary - _indent.length - 4);
68 | // docs = docs.replaceAll('*/', '/');
69 | // docs = docs.replaceAll('/*', r'/\*');
70 |
71 | docs.split('\n').forEach((line) => _writeln('/// ${line}'));
72 |
73 | // if (!docs.contains('\n') && preferSingle) {
74 | // _writeln("/// ${docs}", true);
75 | // } else {
76 | // _writeln("/**", true);
77 | // _writeln(" * ${docs.replaceAll("\n", "\n * ")}", true);
78 | // _writeln(" */", true);
79 | // }
80 | }
81 |
82 | /**
83 | * Write out the given Dart statement and terminate it with an eol. If the
84 | * statement will overflow the column boundary, attempt to wrap it at
85 | * reasonable places.
86 | */
87 | void writeStatement(String str) {
88 | if (_indent.length + str.length > colBoundary) {
89 | // Split the line on the first '('. Currently, we don't do anything
90 | // fancier then that. This takes the edge off the long lines.
91 | int index = str.indexOf('(');
92 |
93 | if (index == -1) {
94 | writeln(str);
95 | } else {
96 | writeln(str.substring(0, index + 1));
97 | writeln(" ${str.substring(index + 1)}");
98 | }
99 | } else {
100 | writeln(str);
101 | }
102 | }
103 |
104 | void writeln([String str = ""]) => _write("${str}\n");
105 |
106 | void write(String str) => _write(str);
107 |
108 | void out(String str) => _buf.write(str);
109 |
110 | void _writeln([String str = "", bool ignoreCurlies = false]) =>
111 | _write("${str}\n", ignoreCurlies);
112 |
113 | void _write(String str, [bool ignoreCurlies = false]) {
114 | for (final int rune in str.runes) {
115 | if (!ignoreCurlies) {
116 | if (rune == RUNE_LEFT_CURLY) {
117 | _indent = "${_indent} ";
118 | } else if (rune == RUNE_RIGHT_CURLY && _indent.length >= 2) {
119 | _indent = _indent.substring(2);
120 | }
121 | }
122 |
123 | if (_previousWasEol && rune != RUNE_EOL) {
124 | _buf.write(_indent);
125 | }
126 |
127 | _buf.write(new String.fromCharCode(rune));
128 |
129 | _previousWasEol = rune == RUNE_EOL;
130 | }
131 | }
132 |
133 | String toString() => _buf.toString();
134 | }
135 |
136 | /// Wrap a string on column boundaries.
137 | String wrap(String str, [int col = 80]) {
138 | // The given string could contain newlines.
139 | List lines = str.split('\n');
140 | return lines.map((l) => _simpleWrap(l, col)).join('\n');
141 | }
142 |
143 | /// Wrap a string ignoring newlines.
144 | String _simpleWrap(String str, [int col = 80]) {
145 | List lines = [];
146 |
147 | while (str.length > col) {
148 | int index = col;
149 |
150 | while (index > 0 && str.codeUnitAt(index) != RUNE_SPACE) {
151 | index--;
152 | }
153 |
154 | if (index == 0) {
155 | index = str.indexOf(' ');
156 |
157 | if (index == -1) {
158 | lines.add(str);
159 | str = '';
160 | } else {
161 | lines.add(str.substring(0, index).trim());
162 | str = str.substring(index).trim();
163 | }
164 | } else {
165 | lines.add(str.substring(0, index).trim());
166 | str = str.substring(index).trim();
167 | }
168 | }
169 |
170 | if (str.length > 0) lines.add(str);
171 |
172 | return lines.join('\n');
173 | }
174 |
--------------------------------------------------------------------------------
/tool/test.dart:
--------------------------------------------------------------------------------
1 | library foo_test;
2 |
3 | import 'dart:async';
4 | import 'dart:convert' show JSON;
5 | import 'dart:developer' as dev;
6 | import 'dart:io';
7 | import 'dart:isolate';
8 | import 'dart:typed_data';
9 |
10 | void main(List args) {
11 | print('args: ${args}');
12 | print(Directory.current);
13 |
14 | String abc = 'abd_def';
15 | String longText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, '
16 | 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut '
17 | 'enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut '
18 | 'aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit '
19 | 'in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur '
20 | 'sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt '
21 | 'mollit anim id est laborum.';
22 | int count = longText.length;
23 |
24 | Cat pebbles;
25 | Dog fido = new Dog(Dog.FIDO_NAME, parent: new Dog('Sam'));
26 |
27 | Map pets = {
28 | 'pebbles': pebbles,
29 | fido.name: fido,
30 | 'type': fido.runtimeType
31 | };
32 |
33 | Timer.run(() => print('timer 1'));
34 | Timer.run(_handleTimer);
35 |
36 | // dev.registerExtension('foo', fooHandler);
37 | //
38 | // dev.log('log from test');
39 |
40 | // dev.Timeline.timeSync('frame', _mockFrame);
41 | // dev.inspect(fido);
42 |
43 | // int i = 0;
44 | //
45 | // new Timer.periodic(new Duration(milliseconds: 10), (t) {
46 | // print('foo ${i}');
47 | // i++;
48 | // if (i > 300) t.cancel();
49 | // });
50 |
51 | print('foo 1');
52 | print('foo 2');
53 | print('foo 3');
54 |
55 | var typedList = new Int32List.fromList([1, 2, 3, 23476234]);
56 |
57 | dev.debugger();
58 |
59 | print('calcRecursive: ${calcRecursive(300)}');
60 |
61 | // startIsolates(4);
62 |
63 | // dev.log('log from test', name: 'test', level: 1);
64 | // dev.Timeline.timeSync('frame', _mockFrame);
65 | // dev.Timeline.timeSync('frame', _mockFrame);
66 |
67 | print('${abc} ${count}, ${pets.length}, ${typedList.length}');
68 |
69 | pebbles = new Cat('Pebbles');
70 |
71 | List animals = [
72 | pebbles, fido, pebbles, fido, pebbles, fido, pebbles, fido, pebbles, fido
73 | ];
74 |
75 | print(pebbles);
76 | print(fido);
77 |
78 | // if (pebbles.scratches()) {
79 | // throw 'no scratching';
80 | // }
81 |
82 | print(animals);
83 |
84 | fido.bark();
85 |
86 | // Demonstrates a game with 3 discs on pegs labeled '1', '2' and '3'.
87 | hanoi(4, '1', '2', '3');
88 | }
89 |
90 | abstract class Animal {
91 | final String name;
92 | final Animal parent;
93 |
94 | Animal(this.name, {this.parent});
95 |
96 | String toString() => '[${runtimeType} ${name}]';
97 | }
98 |
99 | class Cat extends Animal {
100 | Cat(String name) : super(name);
101 |
102 | bool scratches() => true;
103 | }
104 |
105 | class Dog extends Animal {
106 | static String FIDO_NAME = 'Fido';
107 |
108 | Dog(String name, {Dog parent}) : super(name, parent: parent);
109 |
110 | void bark() {
111 | print('woof!');
112 | }
113 | }
114 |
115 | String say(String from, String to) => "move $from -> $to";
116 |
117 | // Makes a move and recursively triggers next moves, if any.
118 | void hanoi(int discs, String a, String b, String c) {
119 | // Makes a move only if there are discs.
120 | if (discs > 0) {
121 | // if (discs == 1 && a == '1') dev.debugger();
122 |
123 | // Announces this move, from A to C.
124 | print('[${discs}] ${say(a, c)}');
125 |
126 | // Triggers the next step: from A to B.
127 | hanoi(discs - 1, a, c, b);
128 |
129 | // Triggers the last step: from B to C.
130 | hanoi(discs - 1, b, a, c);
131 | }
132 | }
133 |
134 | // dynamic _mockFrame() {
135 | // final List names = [
136 | // 'Fido', 'Sparky', 'Chips', 'Scooter'
137 | // ];
138 | //
139 | // return names.map((name) => new Dog(name)).toList();
140 | // }
141 |
142 | void _handleTimer() {
143 | print('timer 2');
144 | }
145 |
146 | Future fooHandler(String method, Map parameters) {
147 | String result = JSON.encode({
148 | 'type': '_extensionType',
149 | 'method': method,
150 | 'parameters': parameters,
151 | });
152 | return new Future.value(new dev.ServiceExtensionResponse.result(result));
153 | }
154 |
155 | void startIsolates(int count) {
156 | if (count == 0) return;
157 |
158 | startIsolate(count * 4);
159 |
160 | startIsolates(count - 1);
161 | startIsolates(count - 1);
162 | }
163 |
164 | Future startIsolate(int seconds) {
165 | return Isolate.spawn(isolateEntryPoint, seconds);
166 | }
167 |
168 | void isolateEntryPoint(seconds) {
169 | print('[${Isolate.current}] starting');
170 | print('[${Isolate.current}] running for ${seconds} seconds...');
171 | new Timer(new Duration(seconds: seconds), () {
172 | print('[${Isolate.current}] exiting');
173 | });
174 | }
175 |
176 | int calcRecursive(int depth) {
177 | if (depth == 0) {
178 | return 1;
179 | } else {
180 | return depth + calcRecursive(depth - 1);
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/tool/travis.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
4 | # for details. All rights reserved. Use of this source code is governed by a
5 | # BSD-style license that can be found in the LICENSE file.
6 |
7 | # Fast fail the script on failures.
8 | set -e
9 |
10 | # Analyze, build and test.
11 | # TODO: Re-enable the CI.
12 | # Disable analysis and tests for now, until the codebase works under Dart 2.0.
13 | #pub run grinder bot
14 |
--------------------------------------------------------------------------------
/web/entry.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library atom.entry;
6 |
7 | import 'package:atom/node/package.dart';
8 | import 'package:atom_dart/plugin.dart';
9 | import 'package:logging/logging.dart';
10 |
11 | main() {
12 | Logger.root.level = Level.WARNING;
13 | Logger.root.onRecord.listen((LogRecord r) {
14 | String tag = '${r.level.name.toLowerCase()} • ${r.loggerName}:';
15 | print('${tag} ${r.message}');
16 |
17 | if (r.error != null) print('${tag} ${r.error}');
18 | if (r.stackTrace != null) print('${tag} ${r.stackTrace}');
19 | });
20 |
21 | registerPackage(new AtomDartPackage());
22 | }
23 |
--------------------------------------------------------------------------------