| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:io' show Platform; |
| |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:dartdoc/src/charcode.dart' show $a, $z; |
| import 'package:dartdoc/src/failure.dart'; |
| import 'package:dartdoc/src/model/model.dart'; |
| import 'package:glob/glob.dart'; |
| import 'package:path/path.dart' as path; |
| |
| /// This will handle matching globs, including on Windows. |
| /// |
| /// On windows, globs are assumed to use absolute Windows paths with drive |
| /// letters in combination with globs, e.g. `C:\foo\bar\*.txt`. `fullName` |
| /// also is assumed to have a drive letter. |
| bool matchGlobs(List<String> globs, String fullName, {bool? isWindows}) { |
| var windows = isWindows ?? Platform.isWindows; |
| if (windows) { |
| // Drive letter (lower-cased). |
| var driveChar = _windowsDriveLetterOf(fullName); |
| // TODO(jcollins-g): port this special casing to the glob package. |
| if (driveChar < 0) { |
| throw DartdocFailure( |
| 'Unable to recognize drive letter on Windows in: $fullName'); |
| } |
| // Remove drive letter and colon. |
| fullName = fullName.substring(2); |
| // Remove same drive letter and colon from globs, |
| // discard any with different or no drive letter. |
| var filteredGlobs = <String>[ |
| for (var glob in globs) |
| // Starts with same drive letter. |
| if (driveChar == _windowsDriveLetterOf(glob)) |
| // Remove leading drive letter and colon, change `\` to `/`. |
| path.posix.joinAll(path.windows.split(glob.substring(2))) |
| ]; |
| globs = filteredGlobs; |
| } |
| |
| return globs.any((g) => |
| Glob(g, context: windows ? path.windows : path.posix).matches(fullName)); |
| } |
| |
| // The lower-case ASCII code of the drive letter of path, or -1 if none. |
| int _windowsDriveLetterOf(String path) { |
| if (path.length >= 3 && path.startsWith(r':\', 1)) { |
| // Drive letter, lower-cased. |
| var charCode = path.codeUnitAt(0) | 0x20; |
| if (charCode >= $a && charCode <= $z) return charCode; |
| } |
| return -1; |
| } |
| |
| Iterable<T> filterHasCanonical<T extends ModelElement>( |
| Iterable<T> maybeHasCanonicalItems) { |
| return maybeHasCanonicalItems.where((me) => me.canonicalModelElement != null); |
| } |
| |
| extension ElementExtension on Element { |
| bool get hasPrivateName { |
| final name = this.name; |
| if (name == null) return false; |
| if (name.startsWith('_')) return true; |
| |
| // GenericFunctionTypeElements have the name we care about in the enclosing |
| // element. |
| if (this case GenericFunctionTypeElement self) { |
| final enclosingElementName = self.enclosingElement?.name; |
| return enclosingElementName != null && |
| enclosingElementName.startsWith('_'); |
| } |
| return false; |
| } |
| } |
| |
| extension IterableOfDocumentableExtension<E extends Documentable> |
| on Iterable<E> { |
| /// The public items which are documented. |
| Iterable<E> get whereDocumented => where((e) => e.isDocumented).wherePublic; |
| } |
| |
| extension IterableOfReferableExtension<E extends Referable> on Iterable<E> { |
| Iterable<E> get wherePublic => where((e) => e.isPublic); |
| } |
| |
| extension IterableOfModelElementExtension<E extends ModelElement> |
| on Iterable<E> { |
| Iterable<E> whereDocumentedIn(Library library) => |
| whereDocumented.where((e) => e.canonicalLibrary == library); |
| } |