From 02873f5116565a4cb7377ce79de569870f7821d8 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 26 Mar 2013 15:02:48 -0700 Subject: [PATCH 01/87] drop in nak for search --- package.json | 3 ++- src/app/project.coffee | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 8f1d8129a..4f7be7832 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "coffee-cache": "0.1.0", "pegjs": "0.7.0", "async": "0.2.6", - "plist": "git://github.com/nathansobo/node-plist.git" + "plist": "git://github.com/nathansobo/node-plist.git", + "nak": "0.2.4" }, "private": true, diff --git a/src/app/project.coffee b/src/app/project.coffee index d6651e727..a3da8a483 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -192,8 +192,9 @@ class Project readPath(line) if state is 'readingPath' readLine(line) if state is 'readingLines' - command = require.resolve('ag') - args = ['--ackmate', regex.source, @getPath()] + command = require.resolve 'nak' + args = ['--ackmate', "#{regex.source}", @getPath()] + args.unshift("--addVCSIgnores") if config.get('nak.addVCSIgnores') new BufferedProcess({command, args, stdout, exit}) deferred From 280e527f5a6b174124b3ac4d6fc7087272f4c217 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 26 Mar 2013 15:03:10 -0700 Subject: [PATCH 02/87] Remove unneeded vendor binarys --- vendor/ack | 2784 ---------------------------------------------------- vendor/ag | Bin 226672 -> 0 bytes 2 files changed, 2784 deletions(-) delete mode 100755 vendor/ack delete mode 100755 vendor/ag diff --git a/vendor/ack b/vendor/ack deleted file mode 100755 index a20f9f603..000000000 --- a/vendor/ack +++ /dev/null @@ -1,2784 +0,0 @@ -#!/usr/bin/env perl -# -# This file, ack, is generated code. -# Please DO NOT EDIT or send patches for it. -# -# Please take a look at the source from -# http://github.com/petdance/ack -# and submit patches against the individual files -# that build ack. -# - -use warnings; -use strict; - -our $VERSION = '1.96'; -# Check http://betterthangrep.com/ for updates - -# These are all our globals. - - -MAIN: { - if ( $App::Ack::VERSION ne $main::VERSION ) { - App::Ack::die( "Program/library version mismatch\n\t$0 is $main::VERSION\n\t$INC{'App/Ack.pm'} is $App::Ack::VERSION" ); - } - - # Do preliminary arg checking; - my $env_is_usable = 1; - for ( @ARGV ) { - last if ( $_ eq '--' ); - - # Priorities! Get the --thpppt checking out of the way. - /^--th[pt]+t+$/ && App::Ack::_thpppt($_); - - # See if we want to ignore the environment. (Don't tell Al Gore.) - if ( /^--(no)?env$/ ) { - $env_is_usable = defined $1 ? 0 : 1; - } - } - if ( $env_is_usable ) { - unshift( @ARGV, App::Ack::read_ackrc() ); - } - else { - my @keys = ( 'ACKRC', grep { /^ACK_/ } keys %ENV ); - delete @ENV{@keys}; - } - App::Ack::load_colors(); - - if ( exists $ENV{ACK_SWITCHES} ) { - App::Ack::warn( 'ACK_SWITCHES is no longer supported. Use ACK_OPTIONS.' ); - } - - if ( !@ARGV ) { - App::Ack::show_help(); - exit 1; - } - - main(); -} - -sub main { - my $opt = App::Ack::get_command_line_options(); - - $| = 1 if $opt->{flush}; # Unbuffer the output if flush mode - - if ( App::Ack::input_from_pipe() ) { - # We're going into filter mode - for ( qw( f g l ) ) { - $opt->{$_} and App::Ack::die( "Can't use -$_ when acting as a filter." ); - } - $opt->{show_filename} = 0; - $opt->{regex} = App::Ack::build_regex( defined $opt->{regex} ? $opt->{regex} : shift @ARGV, $opt ); - if ( my $nargs = @ARGV ) { - my $s = $nargs == 1 ? '' : 's'; - App::Ack::warn( "Ignoring $nargs argument$s on the command-line while acting as a filter." ); - } - - my $res = App::Ack::Resource::Basic->new( '-' ); - my $nmatches; - if ( $opt->{count} ) { - $nmatches = App::Ack::search_and_list( $res, $opt ); - } - else { - # normal searching - $nmatches = App::Ack::search_resource( $res, $opt ); - } - $res->close(); - App::Ack::exit_from_ack( $nmatches ); - } - - my $file_matching = $opt->{f} || $opt->{lines}; - if ( $file_matching ) { - App::Ack::die( "Can't specify both a regex ($opt->{regex}) and use one of --line, -f or -g." ) if $opt->{regex}; - } - else { - $opt->{regex} = App::Ack::build_regex( defined $opt->{regex} ? $opt->{regex} : shift @ARGV, $opt ); - } - - # check that all regexes do compile fine - App::Ack::check_regex( $_ ) for ( $opt->{regex}, $opt->{G} ); - - my $what = App::Ack::get_starting_points( \@ARGV, $opt ); - my $iter = App::Ack::get_iterator( $what, $opt ); - App::Ack::filetype_setup(); - - my $nmatches = 0; - - App::Ack::set_up_pager( $opt->{pager} ) if defined $opt->{pager}; - if ( $opt->{f} ) { - $nmatches = App::Ack::print_files( $iter, $opt ); - } - elsif ( $opt->{l} || $opt->{count} ) { - $nmatches = App::Ack::print_files_with_matches( $iter, $opt ); - } - else { - $nmatches = App::Ack::print_matches( $iter, $opt ); - } - close $App::Ack::fh; - App::Ack::exit_from_ack( $nmatches ); -} - -=head1 NAME - -ack - grep-like text finder - -=head1 SYNOPSIS - - ack [options] PATTERN [FILE...] - ack -f [options] [DIRECTORY...] - -=head1 DESCRIPTION - -Ack is designed as a replacement for 99% of the uses of F. - -Ack searches the named input FILEs (or standard input if no files are -named, or the file name - is given) for lines containing a match to the -given PATTERN. By default, ack prints the matching lines. - -Ack can also list files that would be searched, without actually searching -them, to let you take advantage of ack's file-type filtering capabilities. - -=head1 FILE SELECTION - -I is intelligent about the files it searches. It knows about -certain file types, based on both the extension on the file and, -in some cases, the contents of the file. These selections can be -made with the B<--type> option. - -With no file selections, I only searches files of types that -it recognizes. If you have a file called F, and I -doesn't know what a .wango file is, I won't search it. - -The B<-a> option tells I to select all files, regardless of -type. - -Some files will never be selected by I, even with B<-a>, -including: - -=over 4 - -=item * Backup files: Files matching F<#*#> or ending with F<~>. - -=item * Coredumps: Files matching F - -=back - -However, I always searches the files given on the command line, -no matter what type. Furthermore, by specifying the B<-u> option all -files will be searched. - -=head1 DIRECTORY SELECTION - -I descends through the directory tree of the starting directories -specified. However, it will ignore the shadow directories used by -many version control systems, and the build directories used by the -Perl MakeMaker system. You may add or remove a directory from this -list with the B<--[no]ignore-dir> option. The option may be repeated -to add/remove multiple directories from the ignore list. - -For a complete list of directories that do not get searched, run -F. - -=head1 WHEN TO USE GREP - -I trumps I as an everyday tool 99% of the time, but don't -throw I away, because there are times you'll still need it. - -E.g., searching through huge files looking for regexes that can be -expressed with I syntax should be quicker with I. - -If your script or parent program uses I C<--quiet> or -C<--silent> or needs exit 2 on IO error, use I. - -=head1 OPTIONS - -=over 4 - -=item B<-a>, B<--all> - -Operate on all files, regardless of type (but still skip directories -like F, F, etc.) - -=item B<-A I>, B<--after-context=I> - -Print I lines of trailing context after matching lines. - -=item B<-B I>, B<--before-context=I> - -Print I lines of leading context before matching lines. - -=item B<-C [I]>, B<--context[=I]> - -Print I lines (default 2) of context around matching lines. - -=item B<-c>, B<--count> - -Suppress normal output; instead print a count of matching lines for -each input file. If B<-l> is in effect, it will only show the -number of lines for each file that has lines matching. Without -B<-l>, some line counts may be zeroes. - -If combined with B<-h> (B<--no-filename>) ack outputs only one total count. - -=item B<--color>, B<--nocolor> - -B<--color> highlights the matching text. B<--nocolor> supresses -the color. This is on by default unless the output is redirected. - -On Windows, this option is off by default unless the -L module is installed or the C -environment variable is used. - -=item B<--color-filename=I> - -Sets the color to be used for filenames. - -=item B<--color-match=I> - -Sets the color to be used for matches. - -=item B<--color-lineno=I> - -Sets the color to be used for line numbers. - -=item B<--column> - -Show the column number of the first match. This is helpful for editors -that can place your cursor at a given position. - -=item B<--env>, B<--noenv> - -B<--noenv> disables all environment processing. No F<.ackrc> is read -and all environment variables are ignored. By default, F considers -F<.ackrc> and settings in the environment. - -=item B<--flush> - -B<--flush> flushes output immediately. This is off by default -unless ack is running interactively (when output goes to a pipe -or file). - -=item B<-f> - -Only print the files that would be searched, without actually doing -any searching. PATTERN must not be specified, or it will be taken as -a path to search. - -=item B<--follow>, B<--nofollow> - -Follow or don't follow symlinks, other than whatever starting files -or directories were specified on the command line. - -This is off by default. - -=item B<-G I> - -Only paths matching I are included in the search. The entire -path and filename are matched against I, and I is a -Perl regular expression, not a shell glob. - -The options B<-i>, B<-w>, B<-v>, and B<-Q> do not apply to this I. - -=item B<-g I> - -Print files where the relative path + filename matches I. This option is -a convenience shortcut for B<-f> B<-G I>. - -The options B<-i>, B<-w>, B<-v>, and B<-Q> do not apply to this I. - -=item B<--group>, B<--nogroup> - -B<--group> groups matches by file name with. This is the default when -used interactively. - -B<--nogroup> prints one result per line, like grep. This is the default -when output is redirected. - -=item B<-H>, B<--with-filename> - -Print the filename for each match. - -=item B<-h>, B<--no-filename> - -Suppress the prefixing of filenames on output when multiple files are -searched. - -=item B<--help> - -Print a short help statement. - -=item B<-i>, B<--ignore-case> - -Ignore case in the search strings. - -This applies only to the PATTERN, not to the regexes given for the B<-g> -and B<-G> options. - -=item B<--[no]ignore-dir=I> - -Ignore directory (as CVS, .svn, etc are ignored). May be used multiple times -to ignore multiple directories. For example, mason users may wish to include -B<--ignore-dir=data>. The B<--noignore-dir> option allows users to search -directories which would normally be ignored (perhaps to research the contents -of F<.svn/props> directories). - -The I must always be a simple directory name. Nested directories like -F are NOT supported. You would need to specify B<--ignore-dir=foo> and -then no files from any foo directory are taken into account by ack unless given -explicitly on the command line. - -=item B<--line=I> - -Only print line I of each file. Multiple lines can be given with multiple -B<--line> options or as a comma separated list (B<--line=3,5,7>). B<--line=4-7> -also works. The lines are always output in ascending order, no matter the -order given on the command line. - -=item B<-l>, B<--files-with-matches> - -Only print the filenames of matching files, instead of the matching text. - -=item B<-L>, B<--files-without-matches> - -Only print the filenames of files that do I match. This is equivalent -to specifying B<-l> and B<-v>. - -=item B<--match I> - -Specify the I explicitly. This is helpful if you don't want to put the -regex as your first argument, e.g. when executing multiple searches over the -same set of files. - - # search for foo and bar in given files - ack file1 t/file* --match foo - ack file1 t/file* --match bar - -=item B<-m=I>, B<--max-count=I> - -Stop reading a file after I matches. - -=item B<--man> - -Print this manual page. - -=item B<-n>, B<--no-recurse> - -No descending into subdirectories. - -=item B<-o> - -Show only the part of each line matching PATTERN (turns off text -highlighting) - -=item B<--output=I> - -Output the evaluation of I for each line (turns off text -highlighting) - -=item B<--pager=I> - -Direct ack's output through I. This can also be specified -via the C and C environment variables. - -Using --pager does not suppress grouping and coloring like piping -output on the command-line does. - -=item B<--passthru> - -Prints all lines, whether or not they match the expression. Highlighting -will still work, though, so it can be used to highlight matches while -still seeing the entire file, as in: - - # Watch a log file, and highlight a certain IP address - $ tail -f ~/access.log | ack --passthru 123.45.67.89 - -=item B<--print0> - -Only works in conjunction with -f, -g, -l or -c (filename output). The filenames -are output separated with a null byte instead of the usual newline. This is -helpful when dealing with filenames that contain whitespace, e.g. - - # remove all files of type html - ack -f --html --print0 | xargs -0 rm -f - -=item B<-Q>, B<--literal> - -Quote all metacharacters in PATTERN, it is treated as a literal. - -This applies only to the PATTERN, not to the regexes given for the B<-g> -and B<-G> options. - -=item B<-r>, B<-R>, B<--recurse> - -Recurse into sub-directories. This is the default and just here for -compatibility with grep. You can also use it for turning B<--no-recurse> off. - -=item B<--smart-case>, B<--no-smart-case> - -Ignore case in the search strings if PATTERN contains no uppercase -characters. This is similar to C in vim. This option is -off by default. - -B<-i> always overrides this option. - -This applies only to the PATTERN, not to the regexes given for the -B<-g> and B<-G> options. - -=item B<--sort-files> - -Sorts the found files lexically. Use this if you want your file -listings to be deterministic between runs of I. - -=item B<--show-types> - -Outputs the filetypes that ack associates with each file. - -Works with B<-f> and B<-g> options. - -=item B<--thpppt> - -Display the all-important Bill The Cat logo. Note that the exact -spelling of B<--thpppppt> is not important. It's checked against -a regular expression. - -=item B<--type=TYPE>, B<--type=noTYPE> - -Specify the types of files to include or exclude from a search. -TYPE is a filetype, like I or I. B<--type=perl> can -also be specified as B<--perl>, and B<--type=noperl> can be done -as B<--noperl>. - -If a file is of both type "foo" and "bar", specifying --foo and ---nobar will exclude the file, because an exclusion takes precedence -over an inclusion. - -Type specifications can be repeated and are ORed together. - -See I for a list of valid types. - -=item B<--type-add I=I<.EXTENSION>[,I<.EXT2>[,...]]> - -Files with the given EXTENSION(s) are recognized as being of (the -existing) type TYPE. See also L. - - -=item B<--type-set I=I<.EXTENSION>[,I<.EXT2>[,...]]> - -Files with the given EXTENSION(s) are recognized as being of type -TYPE. This replaces an existing definition for type TYPE. See also -L. - -=item B<-u>, B<--unrestricted> - -All files and directories (including blib/, core.*, ...) are searched, -nothing is skipped. When both B<-u> and B<--ignore-dir> are used, the -B<--ignore-dir> option has no effect. - -=item B<-v>, B<--invert-match> - -Invert match: select non-matching lines - -This applies only to the PATTERN, not to the regexes given for the B<-g> -and B<-G> options. - -=item B<--version> - -Display version and copyright information. - -=item B<-w>, B<--word-regexp> - -Force PATTERN to match only whole words. The PATTERN is wrapped with -C<\b> metacharacters. - -This applies only to the PATTERN, not to the regexes given for the B<-g> -and B<-G> options. - -=item B<-1> - -Stops after reporting first match of any kind. This is different -from B<--max-count=1> or B<-m1>, where only one match per file is -shown. Also, B<-1> works with B<-f> and B<-g>, where B<-m> does -not. - -=back - -=head1 THE .ackrc FILE - -The F<.ackrc> file contains command-line options that are prepended -to the command line before processing. Multiple options may live -on multiple lines. Lines beginning with a # are ignored. A F<.ackrc> -might look like this: - - # Always sort the files - --sort-files - - # Always color, even if piping to a another program - --color - - # Use "less -r" as my pager - --pager=less -r - -Note that arguments with spaces in them do not need to be quoted, -as they are not interpreted by the shell. Basically, each I -in the F<.ackrc> file is interpreted as one element of C<@ARGV>. - -F looks in your home directory for the F<.ackrc>. You can -specify another location with the F variable, below. - -If B<--noenv> is specified on the command line, the F<.ackrc> file -is ignored. - -=head1 Defining your own types - -ack allows you to define your own types in addition to the predefined -types. This is done with command line options that are best put into -an F<.ackrc> file - then you do not have to define your types over and -over again. In the following examples the options will always be shown -on one command line so that they can be easily copy & pasted. - -I searches for foo in all perl files. I -tells you, that perl files are files ending -in .pl, .pm, .pod or .t. So what if you would like to include .xs -files as well when searching for --perl files? I -does this for you. B<--type-add> appends -additional extensions to an existing type. - -If you want to define a new type, or completely redefine an existing -type, then use B<--type-set>. I defines the type I to include files with -the extensions .e or .eiffel. So to search for all eiffel files -containing the word Bertrand use I. -As usual, you can also write B<--type=eiffel> -instead of B<--eiffel>. Negation also works, so B<--noeiffel> excludes -all eiffel files from a search. Redefining also works: I -and I<.xs> files no longer belong to the type I. - -When defining your own types in the F<.ackrc> file you have to use -the following: - - --type-set=eiffel=.e,.eiffel - -or writing on separate lines - - --type-set - eiffel=.e,.eiffel - -The following does B work in the F<.ackrc> file: - - --type-set eiffel=.e,.eiffel - - -In order to see all currently defined types, use I<--help types>, e.g. -I - -Restrictions: - -=over 4 - -=item - -The types 'skipped', 'make', 'binary' and 'text' are considered "builtin" and -cannot be altered. - -=item - -The shebang line recognition of the types 'perl', 'ruby', 'php', 'python', -'shell' and 'xml' cannot be redefined by I<--type-set>, it is always -active. However, the shebang line is only examined for files where the -extension is not recognised. Therefore it is possible to say -I and -only find your shiny new I<.perl> files (and all files with unrecognized extension -and perl on the shebang line). - -=back - -=head1 ENVIRONMENT VARIABLES - -For commonly-used ack options, environment variables can make life much easier. -These variables are ignored if B<--noenv> is specified on the command line. - -=over 4 - -=item ACKRC - -Specifies the location of the F<.ackrc> file. If this file doesn't -exist, F looks in the default location. - -=item ACK_OPTIONS - -This variable specifies default options to be placed in front of -any explicit options on the command line. - -=item ACK_COLOR_FILENAME - -Specifies the color of the filename when it's printed in B<--group> -mode. By default, it's "bold green". - -The recognized attributes are clear, reset, dark, bold, underline, -underscore, blink, reverse, concealed black, red, green, yellow, -blue, magenta, on_black, on_red, on_green, on_yellow, on_blue, -on_magenta, on_cyan, and on_white. Case is not significant. -Underline and underscore are equivalent, as are clear and reset. -The color alone sets the foreground color, and on_color sets the -background color. - -This option can also be set with B<--color-filename>. - -=item ACK_COLOR_MATCH - -Specifies the color of the matching text when printed in B<--color> -mode. By default, it's "black on_yellow". - -This option can also be set with B<--color-match>. - -See B for the color specifications. - -=item ACK_COLOR_LINENO - -Specifies the color of the line number when printed in B<--color> -mode. By default, it's "bold yellow". - -This option can also be set with B<--color-lineno>. - -See B for the color specifications. - -=item ACK_PAGER - -Specifies a pager program, such as C, C or C, to which -ack will send its output. - -Using C does not suppress grouping and coloring like -piping output on the command-line does, except that on Windows -ack will assume that C does not support color. - -C overrides C if both are specified. - -=item ACK_PAGER_COLOR - -Specifies a pager program that understands ANSI color sequences. -Using C does not suppress grouping and coloring -like piping output on the command-line does. - -If you are not on Windows, you never need to use C. - -=back - -=head1 ACK & OTHER TOOLS - -=head2 Vim integration - -F integrates easily with the Vim text editor. Set this in your -F<.vimrc> to use F instead of F: - - set grepprg=ack\ -a - -That examples uses C<-a> to search through all files, but you may -use other default flags. Now you can search with F and easily -step through the results in Vim: - - :grep Dumper perllib - -=head2 Emacs integration - -Phil Jackson put together an F extension that "provides a -simple compilation mode ... has the ability to guess what files you -want to search for based on the major-mode." - -L - -=head2 TextMate integration - -Pedro Melo is a TextMate user who writes "I spend my day mostly -inside TextMate, and the built-in find-in-project sucks with large -projects. So I hacked a TextMate command that was using find + -grep to use ack. The result is the Search in Project with ack, and -you can find it here: -L" - -=head2 Shell and Return Code - -For greater compatibility with I, I in normal use returns -shell return or exit code of 0 only if something is found and 1 if -no match is found. - -(Shell exit code 1 is C<$?=256> in perl with C or backticks.) - -The I code 2 for errors is not used. - -If C<-f> or C<-g> are specified, then 0 is returned if at least one -file is found. If no files are found, then 1 is returned. - -=cut - -=head1 DEBUGGING ACK PROBLEMS - -If ack gives you output you're not expecting, start with a few simple steps. - -=head2 Use B<--noenv> - -Your environment variables and F<.ackrc> may be doing things you're -not expecting, or forgotten you specified. Use B<--noenv> to ignore -your environment and F<.ackrc>. - -=head2 Use B<-f> to see what files you're scanning - -The reason I created B<-f> in the first place was as a debugging -tool. If ack is not finding matches you think it should find, run -F to see what files are being checked. - -=head1 TIPS - -=head2 Use the F<.ackrc> file. - -The F<.ackrc> is the place to put all your options you use most of -the time but don't want to remember. Put all your --type-add and ---type-set definitions in it. If you like --smart-case, set it -there, too. I also set --sort-files there. - -=head2 Use F<-f> for working with big codesets - -Ack does more than search files. C will create a -list of all the Perl files in a tree, ideal for sending into F. -For example: - - # Change all "this" to "that" in all Perl files in a tree. - ack -f --perl | xargs perl -p -i -e's/this/that/g' - -or if you prefer: - - perl -p -i -e's/this/thatg/' $(ack -f --perl) - -=head2 Use F<-Q> when in doubt about metacharacters - -If you're searching for something with a regular expression -metacharacter, most often a period in a filename or IP address, add -the -Q to avoid false positives without all the backslashing. See -the following example for more... - -=head2 Use ack to watch log files - -Here's one I used the other day to find trouble spots for a website -visitor. The user had a problem loading F, so I -took the access log and scanned it with ack twice. - - ack -Q aa.bb.cc.dd /path/to/access.log | ack -Q -B5 troublesome.gif - -The first ack finds only the lines in the Apache log for the given -IP. The second finds the match on my troublesome GIF, and shows -the previous five lines from the log in each case. - -=head2 Share your knowledge - -Join the ack-users mailing list. Send me your tips and I may add -them here. - -=head1 FAQ - -=head2 Why isn't ack finding a match in (some file)? - -Probably because it's of a type that ack doesn't recognize. ack's -searching behavior is driven by filetype. B - -Use the C<-f> switch to see a list of files that ack will search -for you. - -If you want ack to search files that it doesn't recognize, use the -C<-a> switch. - -If you want ack to search every file, even ones that it always -ignores like coredumps and backup files, use the C<-u> switch. - -=head2 Why does ack ignore unknown files by default? - -ack is designed by a programmer, for programmers, for searching -large trees of code. Most codebases have a lot files in them which -aren't source files (like compiled object files, source control -metadata, etc), and grep wastes a lot of time searching through all -of those as well and returning matches from those files. - -That's why ack's behavior of not searching things it doesn't recognize -is one of its greatest strengths: the speed you get from only -searching the things that you want to be looking at. - -=head2 Wouldn't it be great if F did search & replace? - -No, ack will always be read-only. Perl has a perfectly good way -to do search & replace in files, using the C<-i>, C<-p> and C<-n> -switches. - -You can certainly use ack to select your files to update. For -example, to change all "foo" to "bar" in all PHP files, you can do -this from the Unix shell: - - $ perl -i -p -e's/foo/bar/g' $(ack -f --php) - -=head2 Can you make ack recognize F<.xyz> files? - -That's an enhancement. Please see the section in the manual about -enhancements. - -=head2 There's already a program/package called ack. - -Yes, I know. - -=head2 Why is it called ack if it's called ack-grep? - -The name of the program is "ack". Some packagers have called it -"ack-grep" when creating packages because there's already a package -out there called "ack" that has nothing to do with this ack. - -I suggest you make a symlink named F that points to F -because one of the crucial benefits of ack is having a name that's -so short and simple to type. - -To do that, run this with F or as root: - - ln -s /usr/bin/ack-grep /usr/bin/ack - -=head2 What does F mean? - -Nothing. I wanted a name that was easy to type and that you could -pronounce as a single syllable. - -=head2 Can I do multi-line regexes? - -No, ack does not support regexes that match multiple lines. Doing -so would require reading in the entire file at a time. - -If you want to see lines near your match, use the C<--A>, C<--B> -and C<--C> switches for displaying context. - -=head1 AUTHOR - -Andy Lester, C<< >> - -=head1 BUGS - -Please report any bugs or feature requests to the issues list at -Github: L - -=head1 ENHANCEMENTS - -All enhancement requests MUST first be posted to the ack-users -mailing list at L. I -will not consider a request without it first getting seen by other -ack users. This includes requests for new filetypes. - -There is a list of enhancements I want to make to F in the ack -issues list at Github: L - -Patches are always welcome, but patches with tests get the most -attention. - -=head1 SUPPORT - -Support for and information about F can be found at: - -=over 4 - -=item * The ack homepage - -L - -=item * The ack issues list at Github - -L - -=item * AnnoCPAN: Annotated CPAN documentation - -L - -=item * CPAN Ratings - -L - -=item * Search CPAN - -L - -=item * Git source repository - -L - -=back - -=head1 ACKNOWLEDGEMENTS - -How appropriate to have Inowledgements! - -Thanks to everyone who has contributed to ack in any way, including -Matthew Wild, -Scott Kyle, -Nick Hooey, -Bo Borgerson, -Mark Szymanski, -Marq Schneider, -Packy Anderson, -JR Boyens, -Dan Sully, -Ryan Niebur, -Kent Fredric, -Mike Morearty, -Ingmar Vanhassel, -Eric Van Dewoestine, -Sitaram Chamarty, -Adam James, -Richard Carlsson, -Pedro Melo, -AJ Schuster, -Phil Jackson, -Michael Schwern, -Jan Dubois, -Christopher J. Madsen, -Matthew Wickline, -David Dyck, -Jason Porritt, -Jjgod Jiang, -Thomas Klausner, -Uri Guttman, -Peter Lewis, -Kevin Riggle, -Ori Avtalion, -Torsten Blix, -Nigel Metheringham, -GEbor SzabE, -Tod Hagan, -Michael Hendricks, -Evar ArnfjErE Bjarmason, -Piers Cawley, -Stephen Steneker, -Elias Lutfallah, -Mark Leighton Fisher, -Matt Diephouse, -Christian Jaeger, -Bill Sully, -Bill Ricker, -David Golden, -Nilson Santos F. Jr, -Elliot Shank, -Merijn Broeren, -Uwe Voelker, -Rick Scott, -Ask BjErn Hansen, -Jerry Gay, -Will Coleda, -Mike O'Regan, -Slaven ReziE<0x107>, -Mark Stosberg, -David Alan Pisoni, -Adriano Ferreira, -James Keenan, -Leland Johnson, -Ricardo Signes -and Pete Krawczyk. - -=head1 COPYRIGHT & LICENSE - -Copyright 2005-2011 Andy Lester. - -This program is free software; you can redistribute it and/or modify -it under the terms of the Artistic License v2.0. - -=cut -package File::Next; - -use strict; -use warnings; - - -our $VERSION = '1.06'; - - - -use File::Spec (); - - -our $name; # name of the current file -our $dir; # dir of the current file - -our %files_defaults; -our %skip_dirs; - -BEGIN { - %files_defaults = ( - file_filter => undef, - descend_filter => undef, - error_handler => sub { CORE::die @_ }, - sort_files => undef, - follow_symlinks => 1, - ); - %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir); -} - - -sub files { - ($_[0] eq __PACKAGE__) && die 'File::Next::files must not be invoked as File::Next->files'; - - my ($parms,@queue) = _setup( \%files_defaults, @_ ); - my $filter = $parms->{file_filter}; - - return sub { - while (@queue) { - my ($dir,$file,$fullpath) = splice( @queue, 0, 3 ); - if ( -f $fullpath ) { - if ( $filter ) { - local $_ = $file; - local $File::Next::dir = $dir; - local $File::Next::name = $fullpath; - next if not $filter->(); - } - return wantarray ? ($dir,$file,$fullpath) : $fullpath; - } - elsif ( -d _ ) { - unshift( @queue, _candidate_files( $parms, $fullpath ) ); - } - } # while - - return; - }; # iterator -} - - - - - - - -sub sort_standard($$) { return $_[0]->[1] cmp $_[1]->[1] } -sub sort_reverse($$) { return $_[1]->[1] cmp $_[0]->[1] } - -sub reslash { - my $path = shift; - - my @parts = split( /\//, $path ); - - return $path if @parts < 2; - - return File::Spec->catfile( @parts ); -} - - - -sub _setup { - my $defaults = shift; - my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash - - my %passed_parms = %{$passed_parms}; - - my $parms = {}; - for my $key ( keys %{$defaults} ) { - $parms->{$key} = - exists $passed_parms{$key} - ? delete $passed_parms{$key} - : $defaults->{$key}; - } - - # Any leftover keys are bogus - for my $badkey ( keys %passed_parms ) { - my $sub = (caller(1))[3]; - $parms->{error_handler}->( "Invalid option passed to $sub(): $badkey" ); - } - - # If it's not a code ref, assume standard sort - if ( $parms->{sort_files} && ( ref($parms->{sort_files}) ne 'CODE' ) ) { - $parms->{sort_files} = \&sort_standard; - } - my @queue; - - for ( @_ ) { - my $start = reslash( $_ ); - if (-d $start) { - push @queue, ($start,undef,$start); - } - else { - push @queue, (undef,$start,$start); - } - } - - return ($parms,@queue); -} - - -sub _candidate_files { - my $parms = shift; - my $dir = shift; - - my $dh; - if ( !opendir $dh, $dir ) { - $parms->{error_handler}->( "$dir: $!" ); - return; - } - - my @newfiles; - my $descend_filter = $parms->{descend_filter}; - my $follow_symlinks = $parms->{follow_symlinks}; - my $sort_sub = $parms->{sort_files}; - - for my $file ( grep { !exists $skip_dirs{$_} } readdir $dh ) { - my $has_stat; - - # Only do directory checking if we have a descend_filter - my $fullpath = File::Spec->catdir( $dir, $file ); - if ( !$follow_symlinks ) { - next if -l $fullpath; - $has_stat = 1; - } - - if ( $descend_filter ) { - if ( $has_stat ? (-d _) : (-d $fullpath) ) { - local $File::Next::dir = $fullpath; - local $_ = $file; - next if not $descend_filter->(); - } - } - if ( $sort_sub ) { - push( @newfiles, [ $dir, $file, $fullpath ] ); - } - else { - push( @newfiles, $dir, $file, $fullpath ); - } - } - closedir $dh; - - if ( $sort_sub ) { - return map { @{$_} } sort $sort_sub @newfiles; - } - - return @newfiles; -} - - -1; # End of File::Next -package App::Ack; - -use warnings; -use strict; - - - - -our $VERSION; -our $COPYRIGHT; -BEGIN { - $VERSION = '1.96'; - $COPYRIGHT = 'Copyright 2005-2011 Andy Lester.'; -} - -our $fh; - -BEGIN { - $fh = *STDOUT; -} - - -our %types; -our %type_wanted; -our %mappings; -our %ignore_dirs; - -our $input_from_pipe; -our $output_to_pipe; - -our $dir_sep_chars; -our $is_cygwin; -our $is_windows; - -use File::Spec (); -use File::Glob ':glob'; -use Getopt::Long (); - -BEGIN { - %ignore_dirs = ( - '.bzr' => 'Bazaar', - '.cdv' => 'Codeville', - '~.dep' => 'Interface Builder', - '~.dot' => 'Interface Builder', - '~.nib' => 'Interface Builder', - '~.plst' => 'Interface Builder', - '.git' => 'Git', - '.hg' => 'Mercurial', - '.pc' => 'quilt', - '.svn' => 'Subversion', - _MTN => 'Monotone', - blib => 'Perl module building', - CVS => 'CVS', - RCS => 'RCS', - SCCS => 'SCCS', - _darcs => 'darcs', - _sgbak => 'Vault/Fortress', - 'autom4te.cache' => 'autoconf', - 'cover_db' => 'Devel::Cover', - _build => 'Module::Build', - ); - - %mappings = ( - actionscript => [qw( as mxml )], - ada => [qw( ada adb ads )], - asm => [qw( asm s )], - batch => [qw( bat cmd )], - binary => q{Binary files, as defined by Perl's -B op (default: off)}, - cc => [qw( c h xs )], - cfmx => [qw( cfc cfm cfml )], - clojure => [qw( clj )], - cpp => [qw( cpp cc cxx m hpp hh h hxx )], - csharp => [qw( cs )], - css => [qw( css )], - delphi => [qw( pas int dfm nfm dof dpk dproj groupproj bdsgroup bdsproj )], - elisp => [qw( el )], - erlang => [qw( erl hrl )], - fortran => [qw( f f77 f90 f95 f03 for ftn fpp )], - go => [qw( go )], - groovy => [qw( groovy gtmpl gpp grunit )], - haskell => [qw( hs lhs )], - hh => [qw( h )], - html => [qw( htm html shtml xhtml )], - java => [qw( java properties )], - js => [qw( js )], - jsp => [qw( jsp jspx jhtm jhtml )], - lisp => [qw( lisp lsp )], - lua => [qw( lua )], - make => q{Makefiles (including *.mk and *.mak)}, - mason => [qw( mas mhtml mpl mtxt )], - objc => [qw( m h )], - objcpp => [qw( mm h )], - ocaml => [qw( ml mli )], - parrot => [qw( pir pasm pmc ops pod pg tg )], - perl => [qw( pl pm pm6 pod t )], - php => [qw( php phpt php3 php4 php5 phtml)], - plone => [qw( pt cpt metadata cpy py )], - python => [qw( py )], - rake => q{Rakefiles}, - ruby => [qw( rb rhtml rjs rxml erb rake spec )], - scala => [qw( scala )], - scheme => [qw( scm ss )], - shell => [qw( sh bash csh tcsh ksh zsh )], - skipped => q{Files, but not directories, normally skipped by ack (default: off)}, - smalltalk => [qw( st )], - sql => [qw( sql ctl )], - tcl => [qw( tcl itcl itk )], - tex => [qw( tex cls sty )], - text => q{Text files, as defined by Perl's -T op (default: off)}, - tt => [qw( tt tt2 ttml )], - vb => [qw( bas cls frm ctl vb resx )], - verilog => [qw( v vh sv )], - vhdl => [qw( vhd vhdl )], - vim => [qw( vim )], - yaml => [qw( yaml yml )], - xml => [qw( xml dtd xsl xslt ent )], - ); - - while ( my ($type,$exts) = each %mappings ) { - if ( ref $exts ) { - for my $ext ( @{$exts} ) { - push( @{$types{$ext}}, $type ); - } - } - } - # add manually Makefile extensions - push @{$types{$_}}, 'make' for qw{ mk mak }; - - # These have to be checked before any filehandle diddling. - $output_to_pipe = not -t *STDOUT; - $input_from_pipe = -p STDIN; - - $is_cygwin = ($^O eq 'cygwin'); - $is_windows = ($^O =~ /MSWin32/); - $dir_sep_chars = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) ); -} - - -sub read_ackrc { - my @files = ( $ENV{ACKRC} ); - my @dirs = - $is_windows - ? ( $ENV{HOME}, $ENV{USERPROFILE} ) - : ( '~', $ENV{HOME} ); - for my $dir ( grep { defined } @dirs ) { - for my $file ( '.ackrc', '_ackrc' ) { - push( @files, bsd_glob( "$dir/$file", GLOB_TILDE ) ); - } - } - for my $filename ( @files ) { - if ( defined $filename && -e $filename ) { - open( my $fh, '<', $filename ) or App::Ack::die( "$filename: $!\n" ); - my @lines = grep { /./ && !/^\s*#/ } <$fh>; - chomp @lines; - close $fh or App::Ack::die( "$filename: $!\n" ); - - # get rid of leading and trailing whitespaces - for ( @lines ) { - s/^\s+//; - s/\s+$//; - } - - return @lines; - } - } - - return; -} - - -sub get_command_line_options { - my %opt = ( - pager => $ENV{ACK_PAGER_COLOR} || $ENV{ACK_PAGER}, - ); - - my $getopt_specs = { - 1 => sub { $opt{1} = $opt{m} = 1 }, - 'A|after-context=i' => \$opt{after_context}, - 'B|before-context=i' => \$opt{before_context}, - 'C|context:i' => sub { shift; my $val = shift; $opt{before_context} = $opt{after_context} = ($val || 2) }, - 'a|all-types' => \$opt{all}, - 'break!' => \$opt{break}, - c => \$opt{count}, - 'color|colour!' => \$opt{color}, - 'color-match=s' => \$ENV{ACK_COLOR_MATCH}, - 'color-filename=s' => \$ENV{ACK_COLOR_FILENAME}, - 'color-lineno=s' => \$ENV{ACK_COLOR_LINENO}, - 'column!' => \$opt{column}, - count => \$opt{count}, - 'env!' => sub { }, # ignore this option, it is handled beforehand - f => \$opt{f}, - flush => \$opt{flush}, - 'follow!' => \$opt{follow}, - 'g=s' => sub { shift; $opt{G} = shift; $opt{f} = 1 }, - 'G=s' => \$opt{G}, - 'group!' => sub { shift; $opt{heading} = $opt{break} = shift }, - 'heading!' => \$opt{heading}, - 'h|no-filename' => \$opt{h}, - 'H|with-filename' => \$opt{H}, - 'i|ignore-case' => \$opt{i}, - 'invert-file-match' => \$opt{invert_file_match}, - 'lines=s' => sub { shift; my $val = shift; push @{$opt{lines}}, $val }, - 'l|files-with-matches' => \$opt{l}, - 'L|files-without-matches' => sub { $opt{l} = $opt{v} = 1 }, - 'm|max-count=i' => \$opt{m}, - 'match=s' => \$opt{regex}, - 'n|no-recurse' => \$opt{n}, - o => sub { $opt{output} = '$&' }, - 'output=s' => \$opt{output}, - 'pager=s' => \$opt{pager}, - 'nopager' => sub { $opt{pager} = undef }, - 'passthru' => \$opt{passthru}, - 'print0' => \$opt{print0}, - 'Q|literal' => \$opt{Q}, - 'r|R|recurse' => sub { $opt{n} = 0 }, - 'show-types' => \$opt{show_types}, - 'smart-case!' => \$opt{smart_case}, - 'sort-files' => \$opt{sort_files}, - 'u|unrestricted' => \$opt{u}, - 'v|invert-match' => \$opt{v}, - 'w|word-regexp' => \$opt{w}, - - 'ignore-dirs=s' => sub { shift; my $dir = remove_dir_sep( shift ); $ignore_dirs{$dir} = '--ignore-dirs' }, - 'noignore-dirs=s' => sub { shift; my $dir = remove_dir_sep( shift ); delete $ignore_dirs{$dir} }, - - 'version' => sub { print_version_statement(); exit; }, - 'help|?:s' => sub { shift; show_help(@_); exit; }, - 'help-types'=> sub { show_help_types(); exit; }, - 'man' => sub { - require Pod::Usage; - Pod::Usage::pod2usage({ - -verbose => 2, - -exitval => 0, - }); - }, - - 'type=s' => sub { - # Whatever --type=xxx they specify, set it manually in the hash - my $dummy = shift; - my $type = shift; - my $wanted = ($type =~ s/^no//) ? 0 : 1; # must not be undef later - - if ( exists $type_wanted{ $type } ) { - $type_wanted{ $type } = $wanted; - } - else { - App::Ack::die( qq{Unknown --type "$type"} ); - } - }, # type sub - }; - - # Stick any default switches at the beginning, so they can be overridden - # by the command line switches. - unshift @ARGV, split( ' ', $ENV{ACK_OPTIONS} ) if defined $ENV{ACK_OPTIONS}; - - # first pass through options, looking for type definitions - def_types_from_ARGV(); - - for my $i ( filetypes_supported() ) { - $getopt_specs->{ "$i!" } = \$type_wanted{ $i }; - } - - - my $parser = Getopt::Long::Parser->new(); - $parser->configure( 'bundling', 'no_ignore_case', ); - $parser->getoptions( %{$getopt_specs} ) or - App::Ack::die( 'See ack --help, ack --help-types or ack --man for options.' ); - - my $to_screen = not output_to_pipe(); - my %defaults = ( - all => 0, - color => $to_screen, - follow => 0, - break => $to_screen, - heading => $to_screen, - before_context => 0, - after_context => 0, - ); - if ( $is_windows && $defaults{color} && not $ENV{ACK_PAGER_COLOR} ) { - if ( $ENV{ACK_PAGER} || not eval { require Win32::Console::ANSI } ) { - $defaults{color} = 0; - } - } - if ( $to_screen && $ENV{ACK_PAGER_COLOR} ) { - $defaults{color} = 1; - } - - while ( my ($key,$value) = each %defaults ) { - if ( not defined $opt{$key} ) { - $opt{$key} = $value; - } - } - - if ( defined $opt{m} && $opt{m} <= 0 ) { - App::Ack::die( '-m must be greater than zero' ); - } - - for ( qw( before_context after_context ) ) { - if ( defined $opt{$_} && $opt{$_} < 0 ) { - App::Ack::die( "--$_ may not be negative" ); - } - } - - if ( defined( my $val = $opt{output} ) ) { - $opt{output} = eval qq[ sub { "$val" } ]; - } - if ( defined( my $l = $opt{lines} ) ) { - # --line=1 --line=5 is equivalent to --line=1,5 - my @lines = split( /,/, join( ',', @{$l} ) ); - - # --line=1-3 is equivalent to --line=1,2,3 - @lines = map { - my @ret; - if ( /-/ ) { - my ($from, $to) = split /-/, $_; - if ( $from > $to ) { - App::Ack::warn( "ignoring --line=$from-$to" ); - @ret = (); - } - else { - @ret = ( $from .. $to ); - } - } - else { - @ret = ( $_ ); - }; - @ret - } @lines; - - if ( @lines ) { - my %uniq; - @uniq{ @lines } = (); - $opt{lines} = [ sort { $a <=> $b } keys %uniq ]; # numerical sort and each line occurs only once! - } - else { - # happens if there are only ignored --line directives - App::Ack::die( 'All --line options are invalid.' ); - } - } - - return \%opt; -} - - -sub def_types_from_ARGV { - my @typedef; - - my $parser = Getopt::Long::Parser->new(); - # pass_through => leave unrecognized command line arguments alone - # no_auto_abbrev => otherwise -c is expanded and not left alone - $parser->configure( 'no_ignore_case', 'pass_through', 'no_auto_abbrev' ); - $parser->getoptions( - 'type-set=s' => sub { shift; push @typedef, ['c', shift] }, - 'type-add=s' => sub { shift; push @typedef, ['a', shift] }, - ) or App::Ack::die( 'See ack --help or ack --man for options.' ); - - for my $td (@typedef) { - my ($type, $ext) = split /=/, $td->[1]; - - if ( $td->[0] eq 'c' ) { - # type-set - if ( exists $mappings{$type} ) { - # can't redefine types 'make', 'skipped', 'text' and 'binary' - App::Ack::die( qq{--type-set: Builtin type "$type" cannot be changed.} ) - if ref $mappings{$type} ne 'ARRAY'; - - delete_type($type); - } - } - else { - # type-add - - # can't append to types 'make', 'skipped', 'text' and 'binary' - App::Ack::die( qq{--type-add: Builtin type "$type" cannot be changed.} ) - if exists $mappings{$type} && ref $mappings{$type} ne 'ARRAY'; - - App::Ack::warn( qq{--type-add: Type "$type" does not exist, creating with "$ext" ...} ) - unless exists $mappings{$type}; - } - - my @exts = split /,/, $ext; - s/^\.// for @exts; - - if ( !exists $mappings{$type} || ref($mappings{$type}) eq 'ARRAY' ) { - push @{$mappings{$type}}, @exts; - for my $e ( @exts ) { - push @{$types{$e}}, $type; - } - } - else { - App::Ack::die( qq{Cannot append to type "$type".} ); - } - } - - return; -} - - -sub delete_type { - my $type = shift; - - App::Ack::die( qq{Internal error: Cannot delete builtin type "$type".} ) - unless ref $mappings{$type} eq 'ARRAY'; - - delete $mappings{$type}; - delete $type_wanted{$type}; - for my $ext ( keys %types ) { - $types{$ext} = [ grep { $_ ne $type } @{$types{$ext}} ]; - } -} - - -sub ignoredir_filter { - return !exists $ignore_dirs{$_} && !exists $ignore_dirs{$File::Next::dir}; -} - - -sub remove_dir_sep { - my $path = shift; - $path =~ s/[$dir_sep_chars]$//; - - return $path; -} - - -use constant TEXT => 'text'; - -sub filetypes { - my $filename = shift; - - my $basename = $filename; - $basename =~ s{.*[$dir_sep_chars]}{}; - - return 'skipped' unless is_searchable( $basename ); - - my $lc_basename = lc $basename; - return ('make',TEXT) if $lc_basename eq 'makefile' || $lc_basename eq 'gnumakefile'; - return ('rake','ruby',TEXT) if $lc_basename eq 'rakefile'; - - # If there's an extension, look it up - if ( $filename =~ m{\.([^\.$dir_sep_chars]+)$}o ) { - my $ref = $types{lc $1}; - return (@{$ref},TEXT) if $ref; - } - - # At this point, we can't tell from just the name. Now we have to - # open it and look inside. - - return unless -e $filename; - # From Elliot Shank: - # I can't see any reason that -r would fail on these-- the ACLs look - # fine, and no program has any of them open, so the busted Windows - # file locking model isn't getting in there. If I comment the if - # statement out, everything works fine - # So, for cygwin, don't bother trying to check for readability. - if ( !$is_cygwin ) { - if ( !-r $filename ) { - App::Ack::warn( "$filename: Permission denied" ); - return; - } - } - - return 'binary' if -B $filename; - - # If there's no extension, or we don't recognize it, check the shebang line - my $fh; - if ( !open( $fh, '<', $filename ) ) { - App::Ack::warn( "$filename: $!" ); - return; - } - my $header = <$fh>; - close $fh; - - if ( $header =~ /^#!/ ) { - return ($1,TEXT) if $header =~ /\b(ruby|lua|p(?:erl|hp|ython))-?(\d[\d.]*)?\b/; - return ('shell',TEXT) if $header =~ /\b(?:ba|t?c|k|z)?sh\b/; - } - else { - return ('xml',TEXT) if $header =~ /\Q{Q}; - if ( $opt->{w} ) { - $str = "\\b$str" if $str =~ /^\w/; - $str = "$str\\b" if $str =~ /\w$/; - } - - my $regex_is_lc = $str eq lc $str; - if ( $opt->{i} || ($opt->{smart_case} && $regex_is_lc) ) { - $str = "(?i)$str"; - } - - return $str; -} - - -sub check_regex { - my $regex = shift; - - return unless defined $regex; - - eval { qr/$regex/ }; - if ($@) { - (my $error = $@) =~ s/ at \S+ line \d+.*//; - chomp($error); - App::Ack::die( "Invalid regex '$regex':\n $error" ); - } - - return; -} - - - - -sub warn { - return CORE::warn( _my_program(), ': ', @_, "\n" ); -} - - -sub die { - return CORE::die( _my_program(), ': ', @_, "\n" ); -} - -sub _my_program { - require File::Basename; - return File::Basename::basename( $0 ); -} - - - -sub filetypes_supported { - return keys %mappings; -} - -sub _get_thpppt { - my $y = q{_ /|,\\'!.x',=(www)=, U }; - $y =~ tr/,x!w/\nOo_/; - return $y; -} - -sub _thpppt { - my $y = _get_thpppt(); - App::Ack::print( "$y ack $_[0]!\n" ); - exit 0; -} - -sub _key { - my $str = lc shift; - $str =~ s/[^a-z]//g; - - return $str; -} - - -sub show_help { - my $help_arg = shift || 0; - - return show_help_types() if $help_arg =~ /^types?/; - - my $ignore_dirs = _listify( sort { _key($a) cmp _key($b) } keys %ignore_dirs ); - - App::Ack::print( <<"END_OF_HELP" ); -Usage: ack [OPTION]... PATTERN [FILE] - -Search for PATTERN in each source file in the tree from cwd on down. -If [FILES] is specified, then only those files/directories are checked. -ack may also search STDIN, but only if no FILE are specified, or if -one of FILES is "-". - -Default switches may be specified in ACK_OPTIONS environment variable or -an .ackrc file. If you want no dependency on the environment, turn it -off with --noenv. - -Example: ack -i select - -Searching: - -i, --ignore-case Ignore case distinctions in PATTERN - --[no]smart-case Ignore case distinctions in PATTERN, - only if PATTERN contains no upper case - Ignored if -i is specified - -v, --invert-match Invert match: select non-matching lines - -w, --word-regexp Force PATTERN to match only whole words - -Q, --literal Quote all metacharacters; PATTERN is literal - -Search output: - --line=NUM Only print line(s) NUM of each file - -l, --files-with-matches - Only print filenames containing matches - -L, --files-without-matches - Only print filenames with no matches - -o Show only the part of a line matching PATTERN - (turns off text highlighting) - --passthru Print all lines, whether matching or not - --output=expr Output the evaluation of expr for each line - (turns off text highlighting) - --match PATTERN Specify PATTERN explicitly. - -m, --max-count=NUM Stop searching in each file after NUM matches - -1 Stop searching after one match of any kind - -H, --with-filename Print the filename for each match - -h, --no-filename Suppress the prefixing filename on output - -c, --count Show number of lines matching per file - --column Show the column number of the first match - - -A NUM, --after-context=NUM - Print NUM lines of trailing context after matching - lines. - -B NUM, --before-context=NUM - Print NUM lines of leading context before matching - lines. - -C [NUM], --context[=NUM] - Print NUM lines (default 2) of output context. - - --print0 Print null byte as separator between filenames, - only works with -f, -g, -l, -L or -c. - -File presentation: - --pager=COMMAND Pipes all ack output through COMMAND. For example, - --pager="less -R". Ignored if output is redirected. - --nopager Do not send output through a pager. Cancels any - setting in ~/.ackrc, ACK_PAGER or ACK_PAGER_COLOR. - --[no]heading Print a filename heading above each file's results. - (default: on when used interactively) - --[no]break Print a break between results from different files. - (default: on when used interactively) - --group Same as --heading --break - --nogroup Same as --noheading --nobreak - --[no]color Highlight the matching text (default: on unless - output is redirected, or on Windows) - --[no]colour Same as --[no]color - --color-filename=COLOR - --color-match=COLOR - --color-lineno=COLOR Set the color for filenames, matches, and line numbers. - --flush Flush output immediately, even when ack is used - non-interactively (when output goes to a pipe or - file). - -File finding: - -f Only print the files found, without searching. - The PATTERN must not be specified. - -g REGEX Same as -f, but only print files matching REGEX. - --sort-files Sort the found files lexically. - --invert-file-match Print/search handle files that do not match -g/-G. - --show-types Show which types each file has. - -File inclusion/exclusion: - -a, --all-types All file types searched; - Ignores CVS, .svn and other ignored directories - -u, --unrestricted All files and directories searched - --[no]ignore-dir=name Add/Remove directory from the list of ignored dirs - -r, -R, --recurse Recurse into subdirectories (ack's default behavior) - -n, --no-recurse No descending into subdirectories - -G REGEX Only search files that match REGEX - - --perl Include only Perl files. - --type=perl Include only Perl files. - --noperl Exclude Perl files. - --type=noperl Exclude Perl files. - See "ack --help type" for supported filetypes. - - --type-set TYPE=.EXTENSION[,.EXT2[,...]] - Files with the given EXTENSION(s) are recognized as - being of type TYPE. This replaces an existing - definition for type TYPE. - --type-add TYPE=.EXTENSION[,.EXT2[,...]] - Files with the given EXTENSION(s) are recognized as - being of (the existing) type TYPE - - --[no]follow Follow symlinks. Default is off. - - Directories ignored by default: - $ignore_dirs - - Files not checked for type: - /~\$/ - Unix backup files - /#.+#\$/ - Emacs swap files - /[._].*\\.swp\$/ - Vi(m) swap files - /core\\.\\d+\$/ - core dumps - /[.-]min\\.js\$/ - Minified javascript files - -Miscellaneous: - --noenv Ignore environment variables and ~/.ackrc - --help This help - --man Man page - --version Display version & copyright - --thpppt Bill the Cat - -Exit status is 0 if match, 1 if no match. - -This is version $VERSION of ack. -END_OF_HELP - - return; - } - - - -sub show_help_types { - App::Ack::print( <<'END_OF_HELP' ); -Usage: ack [OPTION]... PATTERN [FILES] - -The following is the list of filetypes supported by ack. You can -specify a file type with the --type=TYPE format, or the --TYPE -format. For example, both --type=perl and --perl work. - -Note that some extensions may appear in multiple types. For example, -.pod files are both Perl and Parrot. - -END_OF_HELP - - my @types = filetypes_supported(); - my $maxlen = 0; - for ( @types ) { - $maxlen = length if $maxlen < length; - } - for my $type ( sort @types ) { - next if $type =~ /^-/; # Stuff to not show - my $ext_list = $mappings{$type}; - - if ( ref $ext_list ) { - $ext_list = join( ' ', map { ".$_" } @{$ext_list} ); - } - App::Ack::print( sprintf( " --[no]%-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) ); - } - - return; -} - -sub _listify { - my @whats = @_; - - return '' if !@whats; - - my $end = pop @whats; - my $str = @whats ? join( ', ', @whats ) . " and $end" : $end; - - no warnings 'once'; - require Text::Wrap; - $Text::Wrap::columns = 75; - return Text::Wrap::wrap( '', ' ', $str ); -} - - -sub get_version_statement { - require Config; - - my $copyright = get_copyright(); - my $this_perl = $Config::Config{perlpath}; - if ($^O ne 'VMS') { - my $ext = $Config::Config{_exe}; - $this_perl .= $ext unless $this_perl =~ m/$ext$/i; - } - my $ver = sprintf( '%vd', $^V ); - - return <<"END_OF_VERSION"; -ack $VERSION -Running under Perl $ver at $this_perl - -$copyright - -This program is free software. You may modify or distribute it -under the terms of the Artistic License v2.0. -END_OF_VERSION -} - - -sub print_version_statement { - App::Ack::print( get_version_statement() ); - - return; -} - - -sub get_copyright { - return $COPYRIGHT; -} - - -sub load_colors { - eval 'use Term::ANSIColor ()'; - - $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow'; - $ENV{ACK_COLOR_FILENAME} ||= 'bold green'; - $ENV{ACK_COLOR_LINENO} ||= 'bold yellow'; - - return; -} - - -sub is_interesting { - return if /^\./; - - my $include; - - for my $type ( filetypes( $File::Next::name ) ) { - if ( defined $type_wanted{$type} ) { - if ( $type_wanted{$type} ) { - $include = 1; - } - else { - return; - } - } - } - - return $include; -} - - - -# print subs added in order to make it easy for a third party -# module (such as App::Wack) to redefine the display methods -# and show the results in a different way. -sub print { print {$fh} @_ } -sub print_first_filename { App::Ack::print( $_[0], "\n" ) } -sub print_blank_line { App::Ack::print( "\n" ) } -sub print_separator { App::Ack::print( "--\n" ) } -sub print_filename { App::Ack::print( $_[0], $_[1] ) } -sub print_line_no { App::Ack::print( $_[0], $_[1] ) } -sub print_column_no { App::Ack::print( $_[0], $_[1] ) } -sub print_count { - my $filename = shift; - my $nmatches = shift; - my $ors = shift; - my $count = shift; - my $show_filename = shift; - - if ($show_filename) { - App::Ack::print( $filename ); - App::Ack::print( ':', $nmatches ) if $count; - } - else { - App::Ack::print( $nmatches ) if $count; - } - App::Ack::print( $ors ); -} - -sub print_count0 { - my $filename = shift; - my $ors = shift; - my $show_filename = shift; - - if ($show_filename) { - App::Ack::print( $filename, ':0', $ors ); - } - else { - App::Ack::print( '0', $ors ); - } -} - - - -{ - my $filename; - my $regex; - my $display_filename; - - my $keep_context; - - my $last_output_line; # number of the last line that has been output - my $any_output; # has there been any output for the current file yet - my $context_overall_output_count; # has there been any output at all - -sub search_resource { - my $res = shift; - my $opt = shift; - - $filename = $res->name(); - - my $v = $opt->{v}; - my $passthru = $opt->{passthru}; - my $max = $opt->{m}; - my $nmatches = 0; - - $display_filename = undef; - - # for --line processing - my $has_lines = 0; - my @lines; - if ( defined $opt->{lines} ) { - $has_lines = 1; - @lines = ( @{$opt->{lines}}, -1 ); - undef $regex; # Don't match when printing matching line - } - else { - $regex = qr/$opt->{regex}/; - } - - # for context processing - $last_output_line = -1; - $any_output = 0; - my $before_context = $opt->{before_context}; - my $after_context = $opt->{after_context}; - - $keep_context = ($before_context || $after_context) && !$passthru; - - my @before; - my $before_starts_at_line; - my $after = 0; # number of lines still to print after a match - - while ( $res->next_text ) { - # XXX Optimize away the case when there are no more @lines to find. - # XXX $has_lines, $passthru and $v never change. Optimize. - if ( $has_lines - ? $. != $lines[0] # $lines[0] should be a scalar - : $v ? m/$regex/ : !m/$regex/ ) { - if ( $passthru ) { - App::Ack::print( $_ ); - next; - } - - if ( $keep_context ) { - if ( $after ) { - print_match_or_context( $opt, 0, $., $-[0], $+[0], $_ ); - $after--; - } - elsif ( $before_context ) { - if ( @before ) { - if ( @before >= $before_context ) { - shift @before; - ++$before_starts_at_line; - } - } - else { - $before_starts_at_line = $.; - } - push @before, $_; - } - last if $max && ( $nmatches >= $max ) && !$after; - } - next; - } # not a match - - ++$nmatches; - - # print an empty line as a divider before first line in each file (not before the first file) - if ( !$any_output && $opt->{show_filename} && $opt->{break} && defined( $context_overall_output_count ) ) { - App::Ack::print_blank_line(); - } - - shift @lines if $has_lines; - - if ( $res->is_binary ) { - App::Ack::print( "Binary file $filename matches\n" ); - last; - } - if ( $keep_context ) { - if ( @before ) { - print_match_or_context( $opt, 0, $before_starts_at_line, $-[0], $+[0], @before ); - @before = (); - $before_starts_at_line = 0; - } - if ( $max && $nmatches > $max ) { - --$after; - } - else { - $after = $after_context; - } - } - print_match_or_context( $opt, 1, $., $-[0], $+[0], $_ ); - - last if $max && ( $nmatches >= $max ) && !$after; - } # while - - return $nmatches; -} # search_resource() - - - -sub print_match_or_context { - my $opt = shift; # opts array - my $is_match = shift; # is there a match on the line? - my $line_no = shift; - my $match_start = shift; - my $match_end = shift; - - my $color = $opt->{color}; - my $heading = $opt->{heading}; - my $show_filename = $opt->{show_filename}; - my $show_column = $opt->{column}; - - if ( $show_filename ) { - if ( not defined $display_filename ) { - $display_filename = - $color - ? Term::ANSIColor::colored( $filename, $ENV{ACK_COLOR_FILENAME} ) - : $filename; - if ( $heading && !$any_output ) { - App::Ack::print_first_filename($display_filename); - } - } - } - - # Modified for Atom by Nathan Sobo to print a "\0" instead of a ":" as a separator - my $sep = $is_match ? "\0" : '-'; - my $output_func = $opt->{output}; - for ( @_ ) { - if ( $keep_context && !$output_func ) { - if ( ( $last_output_line != $line_no - 1 ) && - ( $any_output || ( !$heading && defined( $context_overall_output_count ) ) ) ) { - App::Ack::print_separator(); - } - # to ensure separators between different files when --noheading - - $last_output_line = $line_no; - } - - if ( $show_filename ) { - App::Ack::print_filename($display_filename, $sep) if not $heading; - my $display_line_no = - $color - ? Term::ANSIColor::colored( $line_no, $ENV{ACK_COLOR_LINENO} ) - : $line_no; - App::Ack::print_line_no($display_line_no, $sep); - } - - if ( $output_func ) { - while ( /$regex/go ) { - App::Ack::print( $output_func->() . "\n" ); - } - } - else { - if ( $color && $is_match && $regex && - s/$regex/Term::ANSIColor::colored( substr($_, $-[0], $+[0] - $-[0]), $ENV{ACK_COLOR_MATCH} )/eg ) { - # At the end of the line reset the color and remove newline - s/[\r\n]*\z/\e[0m\e[K/; - } - else { - # remove any kind of newline at the end of the line - s/[\r\n]*\z//; - } - if ( $show_column ) { - App::Ack::print_column_no( $match_start+1, $sep ); - } - App::Ack::print($_ . "\n"); - } - $any_output = 1; - ++$context_overall_output_count; - ++$line_no; - } - - return; -} # print_match_or_context() - -} # scope around search_resource() and print_match_or_context() - - -TOTAL_COUNT_SCOPE: { -my $total_count; - -sub get_total_count { - return $total_count; -} - -sub reset_total_count { - $total_count = 0; -} - - -sub search_and_list { - my $res = shift; - my $opt = shift; - - my $nmatches = 0; - my $count = $opt->{count}; - my $ors = $opt->{print0} ? "\0" : "\n"; # output record separator - my $show_filename = $opt->{show_filename}; - - my $regex = qr/$opt->{regex}/; - - if ( $opt->{v} ) { - while ( $res->next_text ) { - if ( /$regex/ ) { - return 0 unless $count; - } - else { - ++$nmatches; - } - } - } - else { - while ( $res->next_text ) { - if ( /$regex/ ) { - ++$nmatches; - last unless $count; - } - } - } - - if ( $opt->{show_total} ) { - $total_count += $nmatches; - } - else { - if ( $nmatches ) { - App::Ack::print_count( $res->name, $nmatches, $ors, $count, $show_filename ); - } - elsif ( $count && !$opt->{l} ) { - App::Ack::print_count0( $res->name, $ors, $show_filename ); - } - } - - return $nmatches ? 1 : 0; -} # search_and_list() - -} # scope around $total_count - - - -sub filetypes_supported_set { - return grep { defined $type_wanted{$_} && ($type_wanted{$_} == 1) } filetypes_supported(); -} - - - -sub print_files { - my $iter = shift; - my $opt = shift; - - my $ors = $opt->{print0} ? "\0" : "\n"; - - my $nmatches = 0; - while ( defined ( my $file = $iter->() ) ) { - App::Ack::print $file, $opt->{show_types} ? " => " . join( ',', filetypes( $file ) ) : (), $ors; - $nmatches++; - last if $opt->{1}; - } - - return $nmatches; -} - - -sub print_files_with_matches { - my $iter = shift; - my $opt = shift; - - # if we have -l and only 1 file given on command line (this means - # show_filename is set to 0), we want to see the filename nevertheless - $opt->{show_filename} = 1 if $opt->{l}; - - $opt->{show_filename} = 0 if $opt->{h}; - $opt->{show_filename} = 1 if $opt->{H}; - - # abuse options to hand in the show_total parameter to search_and_list - $opt->{show_total} = $opt->{count} && !$opt->{show_filename}; - reset_total_count(); - - my $nmatches = 0; - while ( defined ( my $filename = $iter->() ) ) { - my $repo = App::Ack::Repository::Basic->new( $filename ); - my $res; - while ( $res = $repo->next_resource() ) { - $nmatches += search_and_list( $res, $opt ); - $res->close(); - last if $nmatches && $opt->{1}; - } - $repo->close(); - } - - if ( $nmatches && $opt->{show_total} ) { - App::Ack::print_count('', get_total_count(), "\n", 1, 0 ) - } - - return $nmatches; -} - - -sub print_matches { - my $iter = shift; - my $opt = shift; - - $opt->{show_filename} = 0 if $opt->{h}; - $opt->{show_filename} = 1 if $opt->{H}; - - my $nmatches = 0; - while ( defined ( my $filename = $iter->() ) ) { - my $repo; - my $tarballs_work = 0; - if ( $tarballs_work && $filename =~ /\.tar\.gz$/ ) { - App::Ack::die( 'Not working here yet' ); - require App::Ack::Repository::Tar; # XXX Error checking - $repo = App::Ack::Repository::Tar->new( $filename ); - } - else { - $repo = App::Ack::Repository::Basic->new( $filename ); - } - $repo or next; - - while ( my $res = $repo->next_resource() ) { - my $needs_line_scan; - if ( $opt->{regex} && !$opt->{passthru} ) { - $needs_line_scan = $res->needs_line_scan( $opt ); - if ( $needs_line_scan ) { - $res->reset(); - } - } - else { - $needs_line_scan = 1; - } - if ( $needs_line_scan ) { - $nmatches += search_resource( $res, $opt ); - } - $res->close(); - } - last if $nmatches && $opt->{1}; - $repo->close(); - } - return $nmatches; -} - - -sub filetype_setup { - my $filetypes_supported_set = filetypes_supported_set(); - # If anyone says --no-whatever, we assume all other types must be on. - if ( !$filetypes_supported_set ) { - for my $i ( keys %type_wanted ) { - $type_wanted{$i} = 1 unless ( defined( $type_wanted{$i} ) || $i eq 'binary' || $i eq 'text' || $i eq 'skipped' ); - } - } - return; -} - - -EXPAND_FILENAMES_SCOPE: { - my $filter; - - sub expand_filenames { - my $argv = shift; - - my $attr; - my @files; - - foreach my $pattern ( @{$argv} ) { - my @results = bsd_glob( $pattern ); - - if (@results == 0) { - @results = $pattern; # Glob didn't match, pass it thru unchanged - } - elsif ( (@results > 1) or ($results[0] ne $pattern) ) { - if (not defined $filter) { - eval 'require Win32::File;'; - if ($@) { - $filter = 0; - } - else { - $filter = Win32::File::HIDDEN()|Win32::File::SYSTEM(); - } - } # end unless we've tried to load Win32::File - if ( $filter ) { - # Filter out hidden and system files: - @results = grep { not(Win32::File::GetAttributes($_, $attr) and $attr & $filter) } @results; - App::Ack::warn( "$pattern: Matched only hidden files" ) unless @results; - } # end if we can filter by file attributes - } # end elsif this pattern got expanded - - push @files, @results; - } # end foreach pattern - - return \@files; - } # end expand_filenames -} # EXPAND_FILENAMES_SCOPE - - - -sub get_starting_points { - my $argv = shift; - my $opt = shift; - - my @what; - - if ( @{$argv} ) { - @what = @{ $is_windows ? expand_filenames($argv) : $argv }; - $_ = File::Next::reslash( $_ ) for @what; - - # Show filenames unless we've specified one single file - $opt->{show_filename} = (@what > 1) || (!-f $what[0]); - } - else { - @what = '.'; # Assume current directory - $opt->{show_filename} = 1; - } - - for my $start_point (@what) { - App::Ack::warn( "$start_point: No such file or directory" ) unless -e $start_point; - } - return \@what; -} - -sub _match { - my ( $target, $expression, $invert_flag ) = @_; - - if ( $invert_flag ) { - return $target !~ $expression; - } - else { - return $target =~ $expression; - } -} - - -sub get_iterator { - my $what = shift; - my $opt = shift; - - # Starting points are always searched, no matter what - my %starting_point = map { ($_ => 1) } @{$what}; - - my $g_regex = defined $opt->{G} ? qr/$opt->{G}/ : undef; - my $file_filter; - - if ( $g_regex ) { - $file_filter - = $opt->{u} ? sub { _match( $File::Next::name, qr/$g_regex/, $opt->{invert_file_match} ) } # XXX Maybe this should be a 1, no? - : $opt->{all} ? sub { $starting_point{ $File::Next::name } || ( _match( $File::Next::name, qr/$g_regex/, $opt->{invert_file_match} ) && is_searchable( $_ ) ) } - : sub { $starting_point{ $File::Next::name } || ( _match( $File::Next::name, qr/$g_regex/, $opt->{invert_file_match} ) && is_interesting( @ _) ) } - ; - } - else { - $file_filter - = $opt->{u} ? sub {1} - : $opt->{all} ? sub { $starting_point{ $File::Next::name } || is_searchable( $_ ) } - : sub { $starting_point{ $File::Next::name } || is_interesting( @_ ) } - ; - } - - my $descend_filter - = $opt->{n} ? sub {0} - : $opt->{u} ? sub {1} - : \&ignoredir_filter; - - my $iter = - File::Next::files( { - file_filter => $file_filter, - descend_filter => $descend_filter, - error_handler => sub { my $msg = shift; App::Ack::warn( $msg ) }, - sort_files => $opt->{sort_files}, - follow_symlinks => $opt->{follow}, - }, @{$what} ); - return $iter; -} - - -sub set_up_pager { - my $command = shift; - - return if App::Ack::output_to_pipe(); - - my $pager; - if ( not open( $pager, '|-', $command ) ) { - App::Ack::die( qq{Unable to pipe to pager "$command": $!} ); - } - $fh = $pager; - - return; -} - - -sub input_from_pipe { - return $input_from_pipe; -} - - - -sub output_to_pipe { - return $output_to_pipe; -} - - -sub exit_from_ack { - my $nmatches = shift; - - my $rc = $nmatches ? 0 : 1; - exit $rc; -} - - - -1; # End of App::Ack -package App::Ack::Repository; - - -use warnings; -use strict; - -sub FAIL { - require Carp; - Carp::confess( 'Must be overloaded' ); -} - - -sub new { - FAIL(); -} - - -sub next_resource { - FAIL(); -} - - -sub close { - FAIL(); -} - -1; -package App::Ack::Resource; - - -use warnings; -use strict; - -sub FAIL { - require Carp; - Carp::confess( 'Must be overloaded' ); -} - - -sub new { - FAIL(); -} - - -sub name { - FAIL(); -} - - -sub is_binary { - FAIL(); -} - - - -sub needs_line_scan { - FAIL(); -} - - -sub reset { - FAIL(); -} - - -sub next_text { - FAIL(); -} - - -sub close { - FAIL(); -} - -1; -package App::Ack::Plugin::Basic; - - - -package App::Ack::Resource::Basic; - - -use warnings; -use strict; - - -our @ISA = qw( App::Ack::Resource ); - - -sub new { - my $class = shift; - my $filename = shift; - - my $self = bless { - filename => $filename, - fh => undef, - could_be_binary => undef, - opened => undef, - id => undef, - }, $class; - - if ( $self->{filename} eq '-' ) { - $self->{fh} = *STDIN; - $self->{could_be_binary} = 0; - } - else { - if ( !open( $self->{fh}, '<', $self->{filename} ) ) { - App::Ack::warn( "$self->{filename}: $!" ); - return; - } - $self->{could_be_binary} = 1; - } - - return $self; -} - - -sub name { - my $self = shift; - - return $self->{filename}; -} - - -sub is_binary { - my $self = shift; - - if ( $self->{could_be_binary} ) { - return -B $self->{filename}; - } - - return 0; -} - - - -sub needs_line_scan { - my $self = shift; - my $opt = shift; - - return 1 if $opt->{v}; - - my $size = -s $self->{fh}; - if ( $size == 0 ) { - return 0; - } - elsif ( $size > 100_000 ) { - return 1; - } - - my $buffer; - my $rc = sysread( $self->{fh}, $buffer, $size ); - if ( not defined $rc ) { - App::Ack::warn( "$self->{filename}: $!" ); - return 1; - } - return 0 unless $rc && ( $rc == $size ); - - my $regex = $opt->{regex}; - return $buffer =~ /$regex/m; -} - - -sub reset { - my $self = shift; - - seek( $self->{fh}, 0, 0 ) - or App::Ack::warn( "$self->{filename}: $!" ); - - return; -} - - -sub next_text { - if ( defined ($_ = readline $_[0]->{fh}) ) { - $. = ++$_[0]->{line}; - return 1; - } - - return; -} - - -sub close { - my $self = shift; - - if ( not close $self->{fh} ) { - App::Ack::warn( $self->name() . ": $!" ); - } - - return; -} - -package App::Ack::Repository::Basic; - - -our @ISA = qw( App::Ack::Repository ); - - -use warnings; -use strict; - -sub new { - my $class = shift; - my $filename = shift; - - my $self = bless { - filename => $filename, - nexted => 0, - }, $class; - - return $self; -} - - -sub next_resource { - my $self = shift; - - return if $self->{nexted}; - $self->{nexted} = 1; - - return App::Ack::Resource::Basic->new( $self->{filename} ); -} - - -sub close { -} - - - -1; diff --git a/vendor/ag b/vendor/ag deleted file mode 100755 index 63e16c228b9f49ca1257e9b516c651a187356e16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 226672 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

JEt#ZZM?@a_}J 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 Y*i^38YG6%JX~kD(BrHtAM;Jo Date: Wed, 27 Mar 2013 01:06:34 -0700 Subject: [PATCH 03/87] Start figuring out where the pieces go --- src/app/project.coffee | 1 + .../command-panel/lib/command-panel-view.coffee | 11 +++++------ .../commands/select-all-matches-in-project.coffee | 12 ++++++++---- src/packages/command-panel/lib/preview-list.coffee | 3 ++- .../fuzzy-finder/lib/fuzzy-finder-view.coffee | 1 + 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/app/project.coffee b/src/app/project.coffee index a3da8a483..a70c548ff 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -177,6 +177,7 @@ class Project for [column, length] in matchPositions range = new Range([row, column], [row, column + length]) match = lineText.substr(column, length) + console.log(path) iterator({path, range, match}) deferred = $.Deferred() diff --git a/src/packages/command-panel/lib/command-panel-view.coffee b/src/packages/command-panel/lib/command-panel-view.coffee index ff574caa6..980df48a5 100644 --- a/src/packages/command-panel/lib/command-panel-view.coffee +++ b/src/packages/command-panel/lib/command-panel-view.coffee @@ -11,8 +11,8 @@ module.exports = class CommandPanelView extends View @content: -> @div class: 'command-panel tool-panel', => - @div class: 'loading is-loading', outlet: 'loadingMessage', 'Searching...' @div class: 'header', outlet: 'previewHeader', => + @span outlet: 'searchLoadingMessage', class: 'loading', 'Searching...' @ul outlet: 'expandCollapse', class: 'expand-collapse', => @li class: 'expand', 'Expand All' @li class: 'collapse', 'Collapse All' @@ -53,7 +53,7 @@ class CommandPanelView extends View @previewList.hide() @previewHeader.hide() @errorMessages.hide() - @loadingMessage.hide() + @searchLoadingMessage.hide() @prompt.iconSize(@miniEditor.getFontSize()) @history = state.history ? [] @@ -116,12 +116,13 @@ class CommandPanelView extends View @miniEditor.getText() execute: (command=@escapedCommand()) -> - @loadingMessage.show() + @previewHeader.show() + @searchLoadingMessage.show() @errorMessages.empty() try @commandInterpreter.eval(command, rootView.getActivePaneItem()).done ({operationsToPreview, errorMessages}) => - @loadingMessage.hide() + @searchLoadingMessage.hide() @history.push(command) @historyIndex = @history.length @@ -131,8 +132,6 @@ class CommandPanelView extends View @errorMessages.append $$ -> @li errorMessage for errorMessage in errorMessages else if operationsToPreview?.length - @previewHeader.show() - @previewList.populate(operationsToPreview) @previewList.focus() @previewCount.text("#{_.pluralize(operationsToPreview.length, 'match', 'matches')} in #{_.pluralize(@previewList.getPathCount(), 'file')}").show() else diff --git a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee index 619378ed3..0bb1480b0 100644 --- a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee +++ b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee @@ -8,17 +8,21 @@ class SelectAllMatchesInProject extends Command previewOperations: true constructor: (pattern) -> - @regex = new RegExp(pattern, 'g') + debugger; + @regex = new RegExp(pattern) compile: (project, buffer, range) -> deferred = $.Deferred() operations = [] promise = project.scan @regex, ({path, range}) -> - operations.push(new Operation( + debugger; + op = new Operation( project: project path: path bufferRange: range - )) + ) + deferred.resolve([op]) + operations.push(op) - promise.done -> deferred.resolve(operations) + promise.done# -> deferred.resolve(operations) deferred.promise() diff --git a/src/packages/command-panel/lib/preview-list.coffee b/src/packages/command-panel/lib/preview-list.coffee index 7f70db89d..28169abe6 100644 --- a/src/packages/command-panel/lib/preview-list.coffee +++ b/src/packages/command-panel/lib/preview-list.coffee @@ -39,7 +39,8 @@ class PreviewList extends ScrollView hasOperations: -> @operations? populate: (operations) -> - @destroyOperations() if @operations + debugger; + #@destroyOperations() if @operations @operations = operations @lastRenderedOperationIndex = 0 @empty() diff --git a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee index 538988957..0748ac8d2 100644 --- a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee +++ b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee @@ -147,6 +147,7 @@ class FuzzyFinderView extends SelectList path.indexOf(options.filter) >= 0 else @projectPaths + console.log listedItems @setArray(listedItems) options.done(listedItems) if options.done? else From 416f654d2c16c936e8b44014052670409aa6460c Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 27 Mar 2013 15:19:35 -0700 Subject: [PATCH 04/87] Restore operations pushing --- .../lib/commands/select-all-matches-in-project.coffee | 10 +++------- src/packages/command-panel/lib/preview-list.coffee | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee index 0bb1480b0..c9255a4fc 100644 --- a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee +++ b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee @@ -8,21 +8,17 @@ class SelectAllMatchesInProject extends Command previewOperations: true constructor: (pattern) -> - debugger; @regex = new RegExp(pattern) compile: (project, buffer, range) -> deferred = $.Deferred() operations = [] promise = project.scan @regex, ({path, range}) -> - debugger; - op = new Operation( + operations.push(new Operation( project: project path: path bufferRange: range - ) - deferred.resolve([op]) - operations.push(op) + )) - promise.done# -> deferred.resolve(operations) + promise.done -> deferred.resolve(operations) deferred.promise() diff --git a/src/packages/command-panel/lib/preview-list.coffee b/src/packages/command-panel/lib/preview-list.coffee index 28169abe6..0f7bea1d3 100644 --- a/src/packages/command-panel/lib/preview-list.coffee +++ b/src/packages/command-panel/lib/preview-list.coffee @@ -40,7 +40,7 @@ class PreviewList extends ScrollView populate: (operations) -> debugger; - #@destroyOperations() if @operations + @destroyOperations() if @operations @operations = operations @lastRenderedOperationIndex = 0 @empty() From 6a08827830fd2fbc4f050048efe5df0d0e7de0e1 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 27 Mar 2013 16:18:25 -0700 Subject: [PATCH 05/87] Properly append and count matches --- src/app/project.coffee | 1 - .../lib/command-panel-view.coffee | 3 ++- .../select-all-matches-in-project.coffee | 6 ++++-- .../command-panel/lib/preview-list.coffee | 20 ++++++++++++++++--- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/app/project.coffee b/src/app/project.coffee index a70c548ff..a3da8a483 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -177,7 +177,6 @@ class Project for [column, length] in matchPositions range = new Range([row, column], [row, column + length]) match = lineText.substr(column, length) - console.log(path) iterator({path, range, match}) deferred = $.Deferred() diff --git a/src/packages/command-panel/lib/command-panel-view.coffee b/src/packages/command-panel/lib/command-panel-view.coffee index 980df48a5..bd72f93ee 100644 --- a/src/packages/command-panel/lib/command-panel-view.coffee +++ b/src/packages/command-panel/lib/command-panel-view.coffee @@ -119,6 +119,7 @@ class CommandPanelView extends View @previewHeader.show() @searchLoadingMessage.show() @errorMessages.empty() + project.previewList = @previewList try @commandInterpreter.eval(command, rootView.getActivePaneItem()).done ({operationsToPreview, errorMessages}) => @@ -133,7 +134,7 @@ class CommandPanelView extends View @li errorMessage for errorMessage in errorMessages else if operationsToPreview?.length @previewList.focus() - @previewCount.text("#{_.pluralize(operationsToPreview.length, 'match', 'matches')} in #{_.pluralize(@previewList.getPathCount(), 'file')}").show() + @previewCount.text("#{_.pluralize(operationsToPreview.length, 'match', 'matches')} in #{_.pluralize(@previewList.getPathCount(operationsToPreview), 'file')}").show() else @detach() catch error diff --git a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee index c9255a4fc..155d792cf 100644 --- a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee +++ b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee @@ -14,11 +14,13 @@ class SelectAllMatchesInProject extends Command deferred = $.Deferred() operations = [] promise = project.scan @regex, ({path, range}) -> - operations.push(new Operation( + op = new Operation( project: project path: path bufferRange: range - )) + ) + project.previewList.populateSingle(op) + operations.push(op) promise.done -> deferred.resolve(operations) deferred.promise() diff --git a/src/packages/command-panel/lib/preview-list.coffee b/src/packages/command-panel/lib/preview-list.coffee index 0f7bea1d3..8569868fd 100644 --- a/src/packages/command-panel/lib/preview-list.coffee +++ b/src/packages/command-panel/lib/preview-list.coffee @@ -39,7 +39,6 @@ class PreviewList extends ScrollView hasOperations: -> @operations? populate: (operations) -> - debugger; @destroyOperations() if @operations @operations = operations @lastRenderedOperationIndex = 0 @@ -51,6 +50,14 @@ class PreviewList extends ScrollView @find('.operation:first').addClass('selected') + populateSingle: (operation) -> + @viewsForPath = {} + + @show() + @renderOperation(operation) + + @find('.operation:first').addClass('selected') + renderOperations: ({renderAll}={}) -> renderAll ?= false startingScrollHeight = @prop('scrollHeight') @@ -60,6 +67,13 @@ class PreviewList extends ScrollView @lastRenderedOperationIndex++ break if not renderAll and @prop('scrollHeight') >= startingScrollHeight + @pixelOverdraw and @prop('scrollHeight') > @height() + @pixelOverdraw + renderOperation: (operation, {renderAll}={}) -> + renderAll ?= false + startingScrollHeight = @prop('scrollHeight') + pathView = @pathViewForPath(operation.getPath()) + pathView.addOperation(operation) + + pathViewForPath: (path) -> pathView = @viewsForPath[path] if not pathView @@ -97,8 +111,8 @@ class PreviewList extends ScrollView previousView.addClass('selected') previousView.scrollTo() - getPathCount: -> - _.keys(_.groupBy(@operations, (operation) -> operation.getPath())).length + getPathCount: (operations=@operations)-> + _.keys(_.groupBy(operations, (operation) -> operation.getPath())).length getOperations: -> new Array(@operations...) From a585df6d2c09c7668d64ba89016443a9aadd3079 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 27 Mar 2013 16:45:11 -0700 Subject: [PATCH 06/87] Use nak for fuzzyfinder, too --- .../fuzzy-finder/lib/fuzzy-finder-view.coffee | 2 +- .../fuzzy-finder/lib/load-paths-task.coffee | 36 ++++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee index 0748ac8d2..330e50fb3 100644 --- a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee +++ b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee @@ -147,7 +147,7 @@ class FuzzyFinderView extends SelectList path.indexOf(options.filter) >= 0 else @projectPaths - console.log listedItems + @setArray(listedItems) options.done(listedItems) if options.done? else diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index 60a716688..12c80c327 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -1,5 +1,6 @@ _ = require 'underscore' -fs = require 'fs-utils' +BufferedProcess = require 'buffered-process' +$ = require 'jquery' module.exports = class LoadPathsTask @@ -13,21 +14,24 @@ class LoadPathsTask ignoredNames = ignoredNames.concat(config.get('core.ignoredNames') ? []) ignoreGitIgnoredFiles = config.get('core.hideGitIgnoredFiles') - paths = [] - isIgnored = (path) -> - for segment in path.split('/') - return true if _.contains(ignoredNames, segment) - ignoreGitIgnoredFiles and git?.isPathIgnored(fs.join(rootPath, path)) - onFile = (path) -> - return if @aborted - path = path.substring(rootPath.length + 1) - paths.push(path) unless isIgnored(path) - onDirectory = (path) => - not @aborted and not isIgnored(path.substring(rootPath.length + 1)) - onDone = => - @callback(paths) unless @aborted + command = require.resolve 'nak' + args = ['-l', rootPath] + args.unshift("--addVCSIgnores") if config.get('nak.addVCSIgnores') + args.unshift("-d", "#{ignoredNames.join(',')}") if ignoredNames.length > 0 - fs.traverseTree(rootPath, onFile, onDirectory, onDone) + paths = [] + deferred = $.Deferred() + exit = (code) => + if code is -1 + deferred.reject({command, code}) + else + @callback(paths) + deferred.resolve() + stdout = (data) -> + paths = paths.concat(data.split("\n")) + + new BufferedProcess({command, args, stdout, exit}) + deferred abort: -> - @aborted = true + @aborted = true \ No newline at end of file From 12bc89ca5006b2e4b638b06afa4c5fbaade5ab3b Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 27 Mar 2013 16:48:22 -0700 Subject: [PATCH 07/87] Add newline --- src/packages/fuzzy-finder/lib/load-paths-task.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index 12c80c327..b6ce1a368 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -34,4 +34,5 @@ class LoadPathsTask deferred abort: -> - @aborted = true \ No newline at end of file + @aborted = true + \ No newline at end of file From af78a6b50f81d59945243bb959c6f15a1fa8b9f0 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 27 Mar 2013 19:40:20 -0700 Subject: [PATCH 08/87] Remove unneeded quotes --- src/app/project.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/project.coffee b/src/app/project.coffee index a3da8a483..d404495bc 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -193,7 +193,7 @@ class Project readLine(line) if state is 'readingLines' command = require.resolve 'nak' - args = ['--ackmate', "#{regex.source}", @getPath()] + args = ['--ackmate', regex.source, @getPath()] args.unshift("--addVCSIgnores") if config.get('nak.addVCSIgnores') new BufferedProcess({command, args, stdout, exit}) deferred From 9e3935f34970d92e9f366a54b38d572f299e56ad Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Thu, 28 Mar 2013 12:09:14 -0700 Subject: [PATCH 09/87] Fix scan specs --- spec/app/project-spec.coffee | 4 ++-- src/app/project.coffee | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/spec/app/project-spec.coffee b/spec/app/project-spec.coffee index 48f483fc5..53d17d5e0 100644 --- a/spec/app/project-spec.coffee +++ b/spec/app/project-spec.coffee @@ -251,12 +251,12 @@ describe "Project", -> expect(iterator.argsForCall[0][0]).toEqual path: project.resolve('a') - match: 'aaa' + match: 'aa ' range: [[0, 0], [0, 3]] expect(iterator.argsForCall[1][0]).toEqual path: project.resolve('a') - match: 'aa' + match: 'a ' range: [[1, 3], [1, 5]] describe "serialization", -> diff --git a/src/app/project.coffee b/src/app/project.coffee index d404495bc..04413e126 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -176,7 +176,7 @@ class Project for [column, length] in matchPositions range = new Range([row, column], [row, column + length]) - match = lineText.substr(column, length) + match = lineText.substr(column + 1, length) iterator({path, range, match}) deferred = $.Deferred() @@ -195,6 +195,7 @@ class Project command = require.resolve 'nak' args = ['--ackmate', regex.source, @getPath()] args.unshift("--addVCSIgnores") if config.get('nak.addVCSIgnores') + #console.log(args) new BufferedProcess({command, args, stdout, exit}) deferred From 226611d2ec54aa86558e4b80f53667f696f0a4fa Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Thu, 28 Mar 2013 13:04:25 -0700 Subject: [PATCH 10/87] Fix fuzzy specs --- package.json | 2 +- src/packages/fuzzy-finder/lib/load-paths-task.coffee | 4 ++-- src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a1152052b..03ebffbd5 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "coffee-cache": "0.1.0", "pegjs": "0.7.0", "async": "0.2.6", - "nak": "0.2.4", + "nak": "0.2.5", "spellchecker": "0.2.0", "plist": "git://github.com/nathansobo/node-plist.git", "space-pen": "git://github.com/nathansobo/space-pen.git" diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index b6ce1a368..9e5c9489d 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -18,7 +18,7 @@ class LoadPathsTask args = ['-l', rootPath] args.unshift("--addVCSIgnores") if config.get('nak.addVCSIgnores') args.unshift("-d", "#{ignoredNames.join(',')}") if ignoredNames.length > 0 - + paths = [] deferred = $.Deferred() exit = (code) => @@ -28,7 +28,7 @@ class LoadPathsTask @callback(paths) deferred.resolve() stdout = (data) -> - paths = paths.concat(data.split("\n")) + paths = paths.concat(_.compact(data.split("\n"))) new BufferedProcess({command, args, stdout, exit}) deferred diff --git a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee index 15c89721e..d93b82264 100644 --- a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee +++ b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee @@ -365,7 +365,7 @@ describe 'FuzzyFinder', -> expect(finderView).toBeVisible() expect(rootView.find('.fuzzy-finder input:focus')).toExist() - it "opens a file directly when there is a single match", -> + fit "opens a file directly when there is a single match", -> editor.setText("sample.txt") jasmine.unspy(window, "setTimeout") rootView.trigger 'fuzzy-finder:find-under-cursor' @@ -379,7 +379,7 @@ describe 'FuzzyFinder', -> runs -> expect(finderView).not.toBeVisible() - expect(openedPath).toBe "sample.txt" + expect(openedPath).toBe "#{project.getPath()}/sample.txt" it "displays an error when the word under the cursor doesn't match any files", -> editor.setText("moogoogaipan") From daf5b9cb14bfa7bab2575232c50ea277dccc11c9 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 28 Mar 2013 22:12:03 -0400 Subject: [PATCH 11/87] Un-f fuzzy finder spec --- src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee index d93b82264..8e75f8d9c 100644 --- a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee +++ b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee @@ -365,7 +365,7 @@ describe 'FuzzyFinder', -> expect(finderView).toBeVisible() expect(rootView.find('.fuzzy-finder input:focus')).toExist() - fit "opens a file directly when there is a single match", -> + it "opens a file directly when there is a single match", -> editor.setText("sample.txt") jasmine.unspy(window, "setTimeout") rootView.trigger 'fuzzy-finder:find-under-cursor' From 346a3c94aa24cf698e99653aea2d4b5a53b838ea Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 28 Mar 2013 22:12:16 -0400 Subject: [PATCH 12/87] Bring back a newline --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 03ebffbd5..c5ba41b71 100644 --- a/package.json +++ b/package.json @@ -24,4 +24,4 @@ "scripts": { "preinstall": "true" } -} \ No newline at end of file +} From e5b89e559ef520ea56caa86dc214a853363646bb Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 28 Mar 2013 22:15:12 -0400 Subject: [PATCH 13/87] :lipstick: --- src/packages/fuzzy-finder/lib/load-paths-task.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index 9e5c9489d..545c5bc9a 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -18,7 +18,7 @@ class LoadPathsTask args = ['-l', rootPath] args.unshift("--addVCSIgnores") if config.get('nak.addVCSIgnores') args.unshift("-d", "#{ignoredNames.join(',')}") if ignoredNames.length > 0 - + paths = [] deferred = $.Deferred() exit = (code) => @@ -35,4 +35,3 @@ class LoadPathsTask abort: -> @aborted = true - \ No newline at end of file From 44cd588bf9b1d4ec012cd66d7ed113eafbd6c892 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 28 Mar 2013 22:16:48 -0400 Subject: [PATCH 14/87] Drop unneeded quotes --- src/packages/fuzzy-finder/lib/load-paths-task.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index 545c5bc9a..ad3b81ab0 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -17,7 +17,7 @@ class LoadPathsTask command = require.resolve 'nak' args = ['-l', rootPath] args.unshift("--addVCSIgnores") if config.get('nak.addVCSIgnores') - args.unshift("-d", "#{ignoredNames.join(',')}") if ignoredNames.length > 0 + args.unshift("-d", ignoredNames.join(',')) if ignoredNames.length > 0 paths = [] deferred = $.Deferred() From 95218ca9f54fe88b0cd94d0a8263ec533892c40f Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 28 Mar 2013 22:29:05 -0400 Subject: [PATCH 15/87] Remove commented out logging --- src/app/project.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/project.coffee b/src/app/project.coffee index 04413e126..886e7a059 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -195,7 +195,6 @@ class Project command = require.resolve 'nak' args = ['--ackmate', regex.source, @getPath()] args.unshift("--addVCSIgnores") if config.get('nak.addVCSIgnores') - #console.log(args) new BufferedProcess({command, args, stdout, exit}) deferred From 5f9c643ae9f0939762a4436ec8de0b5f42284180 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 28 Mar 2013 22:51:47 -0400 Subject: [PATCH 16/87] Kill process when task is aborted --- .../fuzzy-finder/lib/load-paths-task.coffee | 5 ++++- src/stdlib/buffered-process.coffee | 21 ++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index ad3b81ab0..538aac6ba 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -30,8 +30,11 @@ class LoadPathsTask stdout = (data) -> paths = paths.concat(_.compact(data.split("\n"))) - new BufferedProcess({command, args, stdout, exit}) + @process = new BufferedProcess({command, args, stdout, exit}) deferred abort: -> @aborted = true + if @process? + @process.kill() + @process = null diff --git a/src/stdlib/buffered-process.coffee b/src/stdlib/buffered-process.coffee index 04fcf3ce9..eda128601 100644 --- a/src/stdlib/buffered-process.coffee +++ b/src/stdlib/buffered-process.coffee @@ -2,32 +2,36 @@ ChildProcess = require 'child_process' module.exports = class BufferedProcess + process: null + killed: false + constructor: ({command, args, options, stdout, stderr, exit}={}) -> - process = ChildProcess.spawn(command, args, options) + @process = ChildProcess.spawn(command, args, options) stdoutClosed = true stderrClosed = true processExited = true exitCode = 0 triggerExitCallback = -> + return if @killed if stdoutClosed and stderrClosed and processExited exit?(exitCode) if stdout stdoutClosed = false - @bufferStream process.stdout, stdout, -> + @bufferStream @process.stdout, stdout, -> stdoutClosed = true triggerExitCallback() if stderr stderrClosed = false - @bufferStream process.stderr, stderr, -> + @bufferStream @process.stderr, stderr, -> stderrClosed = true triggerExitCallback() if exit processExited = false - process.on 'exit', (code) -> + @process.on 'exit', (code) -> exitCode = code processExited = true triggerExitCallback() @@ -36,7 +40,8 @@ class BufferedProcess stream.setEncoding('utf8') buffered = '' - stream.on 'data', (data) -> + stream.on 'data', (data) => + return if @killed buffered += data lastNewlineIndex = buffered.lastIndexOf('\n') if lastNewlineIndex isnt -1 @@ -44,5 +49,11 @@ class BufferedProcess buffered = buffered.substring(lastNewlineIndex + 1) stream.on 'close', => + return if @killed onLines(buffered) if buffered.length > 0 onDone() + + kill: -> + @killed = true + @process.kill() + @process = null From 1ccfbe22dc83716092d9fe983c4eba7b987e1129 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 28 Mar 2013 23:12:53 -0400 Subject: [PATCH 17/87] Remove unneeded unspy calls for setTimeout --- src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee index 8e75f8d9c..fb6c8ceb3 100644 --- a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee +++ b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee @@ -43,7 +43,6 @@ describe 'FuzzyFinder', -> it "shows all relative file paths for the current project and selects the first", -> rootView.attachToDom() finderView.maxItems = Infinity - jasmine.unspy(window, "setTimeout") rootView.trigger 'fuzzy-finder:toggle-file-finder' paths = null expect(finderView.find(".loading")).toBeVisible() @@ -279,7 +278,6 @@ describe 'FuzzyFinder', -> describe "cached file paths", -> it "caches file paths after first time", -> spyOn(LoadPathsTask.prototype, "start").andCallThrough() - jasmine.unspy(window, "setTimeout") rootView.trigger 'fuzzy-finder:toggle-file-finder' waitsFor -> @@ -299,7 +297,6 @@ describe 'FuzzyFinder', -> it "doesn't cache buffer paths", -> spyOn(project, "getEditSessions").andCallThrough() - jasmine.unspy(window, "setTimeout") rootView.trigger 'fuzzy-finder:toggle-buffer-finder' waitsFor -> @@ -319,7 +316,6 @@ describe 'FuzzyFinder', -> it "busts the cache when the window gains focus", -> spyOn(LoadPathsTask.prototype, "start").andCallThrough() - jasmine.unspy(window, "setTimeout") rootView.trigger 'fuzzy-finder:toggle-file-finder' waitsFor -> @@ -336,7 +332,6 @@ describe 'FuzzyFinder', -> describe "path ignoring", -> it "ignores paths that match entries in config.fuzzyFinder.ignoredNames", -> config.set("fuzzyFinder.ignoredNames", ["tree-view.js"]) - jasmine.unspy(window, "setTimeout") rootView.trigger 'fuzzy-finder:toggle-file-finder' finderView.maxItems = Infinity @@ -355,7 +350,6 @@ describe 'FuzzyFinder', -> it "opens the fuzzy finder window when there are multiple matches", -> editor.setText("sample") - jasmine.unspy(window, "setTimeout") rootView.trigger 'fuzzy-finder:find-under-cursor' waitsFor -> @@ -367,7 +361,6 @@ describe 'FuzzyFinder', -> it "opens a file directly when there is a single match", -> editor.setText("sample.txt") - jasmine.unspy(window, "setTimeout") rootView.trigger 'fuzzy-finder:find-under-cursor' openedPath = null @@ -385,7 +378,6 @@ describe 'FuzzyFinder', -> editor.setText("moogoogaipan") editor.setCursorBufferPosition([0,5]) - jasmine.unspy(window, "setTimeout") rootView.trigger 'fuzzy-finder:find-under-cursor' waitsFor -> From 327b49797bec365562c364105ccc1228927f2e01 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 28 Mar 2013 23:14:15 -0400 Subject: [PATCH 18/87] Relativize paths to project for folder label --- src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee index b49f096da..5ad9be5e2 100644 --- a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee +++ b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee @@ -60,7 +60,7 @@ class FuzzyFinderView extends SelectList typeClass = 'text-name' @span fs.base(path), class: "file label #{typeClass}" - if folder = fs.directory(path) + if folder = fs.directory(project.relativize(path)) @span " - #{folder}/", class: 'directory' openPath: (path) -> From 082acf438665027a7b3c7fd554b0e2481e0127b7 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Fri, 29 Mar 2013 09:35:40 -0700 Subject: [PATCH 19/87] Stash filestream work --- src/app/project.coffee | 2 +- .../lib/commands/select-all-matches-in-project.coffee | 3 ++- src/packages/command-panel/lib/operation.coffee | 11 +++++------ src/packages/command-panel/lib/preview-list.coffee | 2 -- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/app/project.coffee b/src/app/project.coffee index 886e7a059..6c5ad78c6 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -177,7 +177,7 @@ class Project for [column, length] in matchPositions range = new Range([row, column], [row, column + length]) match = lineText.substr(column + 1, length) - iterator({path, range, match}) + iterator({path, range, match, lineText}) deferred = $.Deferred() exit = (code) -> diff --git a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee index 155d792cf..65737f9cd 100644 --- a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee +++ b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee @@ -13,11 +13,12 @@ class SelectAllMatchesInProject extends Command compile: (project, buffer, range) -> deferred = $.Deferred() operations = [] - promise = project.scan @regex, ({path, range}) -> + promise = project.scan @regex, ({path, range, match, lineText}) -> op = new Operation( project: project path: path bufferRange: range + lineText: lineText ) project.previewList.populateSingle(op) operations.push(op) diff --git a/src/packages/command-panel/lib/operation.coffee b/src/packages/command-panel/lib/operation.coffee index 9be9ad7f6..c0adac184 100644 --- a/src/packages/command-panel/lib/operation.coffee +++ b/src/packages/command-panel/lib/operation.coffee @@ -1,6 +1,6 @@ module.exports = class Operation - constructor: ({@project, @path, @buffer, @bufferRange, @newText, @preserveSelection, @errorMessage}) -> + constructor: ({@project, @path, @buffer, @bufferRange, @lineText, @newText, @preserveSelection, @errorMessage}) -> if @buffer? @buffer.retain() @getMarker() @@ -23,11 +23,10 @@ class Operation @getBufferRange() unless @preserveSelection preview: -> - range = @getBuffer().getMarkerRange(@getMarker()) - line = @getBuffer().lineForRow(range.start.row) - prefix = line[0...range.start.column] - match = line[range.start.column...range.end.column] - suffix = line[range.end.column..] + range = @bufferRange + prefix = @lineText[0...range.start.column] + match = @lineText[range.start.column + 1...range.end.column] + suffix = @lineText[range.end.column..] {prefix, suffix, match, range} diff --git a/src/packages/command-panel/lib/preview-list.coffee b/src/packages/command-panel/lib/preview-list.coffee index 8569868fd..fe4c8a6b6 100644 --- a/src/packages/command-panel/lib/preview-list.coffee +++ b/src/packages/command-panel/lib/preview-list.coffee @@ -1,7 +1,6 @@ $ = require 'jquery' ScrollView = require 'scroll-view' _ = require 'underscore' -fs = require 'fs-utils' PathView = require './path-view' OperationView = require './operation-view' @@ -73,7 +72,6 @@ class PreviewList extends ScrollView pathView = @pathViewForPath(operation.getPath()) pathView.addOperation(operation) - pathViewForPath: (path) -> pathView = @viewsForPath[path] if not pathView From b8d054451790c8fbdd771743911e1326d93050ee Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Sat, 30 Mar 2013 11:20:27 -0700 Subject: [PATCH 20/87] Improve UX Properly compress all results to one heading Merge Expand/Collapse Button into one --- .../command-panel/lib/command-panel-view.coffee | 14 +++++++++++--- src/packages/command-panel/lib/preview-list.coffee | 9 +++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/packages/command-panel/lib/command-panel-view.coffee b/src/packages/command-panel/lib/command-panel-view.coffee index 9ed6533d5..878b11399 100644 --- a/src/packages/command-panel/lib/command-panel-view.coffee +++ b/src/packages/command-panel/lib/command-panel-view.coffee @@ -6,6 +6,7 @@ PreviewList = require './preview-list' Editor = require 'editor' {SyntaxError} = require('pegjs').parser _ = require 'underscore' +$ = require 'jquery' module.exports = class CommandPanelView extends View @@ -14,7 +15,6 @@ class CommandPanelView extends View @div class: 'header', outlet: 'previewHeader', => @span outlet: 'searchLoadingMessage', class: 'loading', 'Searching...' @ul outlet: 'expandCollapse', class: 'expand-collapse', => - @li class: 'expand', 'Expand All' @li class: 'collapse', 'Collapse All' @span outlet: 'previewCount', class: 'preview-count' @@ -54,6 +54,7 @@ class CommandPanelView extends View @previewHeader.hide() @errorMessages.hide() @searchLoadingMessage.hide() + @expandCollapse.hide() @prompt.iconSize(@miniEditor.getFontSize()) @history = state.history ? [] @@ -92,11 +93,17 @@ class CommandPanelView extends View @miniEditor.focus() onExpandAll: (event) => - @previewList.expandAllPaths() + elButton = $(event.currentTarget) + @previewList.expandAllPaths() + elButton.removeClass("expand").addClass("collapse") + elButton.text("Collapse All") @previewList.focus() onCollapseAll: (event) => + elButton = $(event.currentTarget) @previewList.collapseAllPaths() + elButton.removeClass("collapse").addClass("expand") + elButton.text("Expand All") @previewList.focus() attach: (text='', options={}) -> @@ -123,10 +130,11 @@ class CommandPanelView extends View @searchLoadingMessage.show() @errorMessages.empty() project.previewList = @previewList - + try @commandInterpreter.eval(command, rootView.getActivePaneItem()).done ({operationsToPreview, errorMessages}) => @searchLoadingMessage.hide() + @expandCollapse.show() @history.push(command) @historyIndex = @history.length diff --git a/src/packages/command-panel/lib/preview-list.coffee b/src/packages/command-panel/lib/preview-list.coffee index fe4c8a6b6..36a40cc79 100644 --- a/src/packages/command-panel/lib/preview-list.coffee +++ b/src/packages/command-panel/lib/preview-list.coffee @@ -19,8 +19,6 @@ class PreviewList extends ScrollView @on 'core:move-down', => @selectNextOperation(); false @on 'core:move-up', => @selectPreviousOperation(); false - @on 'scroll', => - @renderOperations() if @scrollBottom() >= (@prop('scrollHeight')) @command 'command-panel:collapse-all', => @collapseAllPaths() @command 'command-panel:expand-all', => @expandAllPaths() @@ -29,7 +27,6 @@ class PreviewList extends ScrollView @children().each (index, element) -> $(element).view().expand() collapseAllPaths: -> - @renderOperations(renderAll: true) @children().each (index, element) -> $(element).view().collapse() destroy: -> @@ -50,7 +47,7 @@ class PreviewList extends ScrollView @find('.operation:first').addClass('selected') populateSingle: (operation) -> - @viewsForPath = {} + @viewsForPath ||= {} @show() @renderOperation(operation) @@ -66,14 +63,14 @@ class PreviewList extends ScrollView @lastRenderedOperationIndex++ break if not renderAll and @prop('scrollHeight') >= startingScrollHeight + @pixelOverdraw and @prop('scrollHeight') > @height() + @pixelOverdraw - renderOperation: (operation, {renderAll}={}) -> - renderAll ?= false + renderOperation: (operation) -> startingScrollHeight = @prop('scrollHeight') pathView = @pathViewForPath(operation.getPath()) pathView.addOperation(operation) pathViewForPath: (path) -> pathView = @viewsForPath[path] + console.log(path) if not pathView pathView = new PathView({path: path, previewList: this}) @viewsForPath[path] = pathView From 209eceee6af9307e73ee617b07fea8d5fa11ba19 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Sat, 30 Mar 2013 18:36:50 -0700 Subject: [PATCH 21/87] stash --- src/packages/command-panel/lib/command-panel-view.coffee | 4 ++-- src/packages/command-panel/lib/preview-list.coffee | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/packages/command-panel/lib/command-panel-view.coffee b/src/packages/command-panel/lib/command-panel-view.coffee index 878b11399..311b5b7f2 100644 --- a/src/packages/command-panel/lib/command-panel-view.coffee +++ b/src/packages/command-panel/lib/command-panel-view.coffee @@ -130,7 +130,7 @@ class CommandPanelView extends View @searchLoadingMessage.show() @errorMessages.empty() project.previewList = @previewList - + try @commandInterpreter.eval(command, rootView.getActivePaneItem()).done ({operationsToPreview, errorMessages}) => @searchLoadingMessage.hide() @@ -149,7 +149,7 @@ class CommandPanelView extends View else @detach() catch error - @loadingMessage.hide() + @searchLoadingMessage.hide() if error.name is "SyntaxError" @flashError() return diff --git a/src/packages/command-panel/lib/preview-list.coffee b/src/packages/command-panel/lib/preview-list.coffee index 36a40cc79..cf700af2d 100644 --- a/src/packages/command-panel/lib/preview-list.coffee +++ b/src/packages/command-panel/lib/preview-list.coffee @@ -70,7 +70,6 @@ class PreviewList extends ScrollView pathViewForPath: (path) -> pathView = @viewsForPath[path] - console.log(path) if not pathView pathView = new PathView({path: path, previewList: this}) @viewsForPath[path] = pathView From 7f8e2aba1b15ced53121b2aadca055be6bca6259 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Sun, 31 Mar 2013 00:22:27 -0700 Subject: [PATCH 22/87] Stash changes --- src/app/project.coffee | 4 ++-- src/packages/command-panel/lib/command-panel-view.coffee | 1 + src/packages/command-panel/lib/operation.coffee | 2 +- src/packages/command-panel/lib/preview-list.coffee | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/app/project.coffee b/src/app/project.coffee index 6c5ad78c6..dea39f3c1 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -176,7 +176,7 @@ class Project for [column, length] in matchPositions range = new Range([row, column], [row, column + length]) - match = lineText.substr(column + 1, length) + match = lineText.substr(column , length) iterator({path, range, match, lineText}) deferred = $.Deferred() @@ -192,7 +192,7 @@ class Project readPath(line) if state is 'readingPath' readLine(line) if state is 'readingLines' - command = require.resolve 'nak' + command = require.resolve('nak') args = ['--ackmate', regex.source, @getPath()] args.unshift("--addVCSIgnores") if config.get('nak.addVCSIgnores') new BufferedProcess({command, args, stdout, exit}) diff --git a/src/packages/command-panel/lib/command-panel-view.coffee b/src/packages/command-panel/lib/command-panel-view.coffee index 311b5b7f2..7893f1145 100644 --- a/src/packages/command-panel/lib/command-panel-view.coffee +++ b/src/packages/command-panel/lib/command-panel-view.coffee @@ -144,6 +144,7 @@ class CommandPanelView extends View @errorMessages.append $$ -> @li errorMessage for errorMessage in errorMessages else if operationsToPreview?.length + @previewList.show() @previewList.focus() @previewCount.text("#{_.pluralize(operationsToPreview.length, 'match', 'matches')} in #{_.pluralize(@previewList.getPathCount(operationsToPreview), 'file')}").show() else diff --git a/src/packages/command-panel/lib/operation.coffee b/src/packages/command-panel/lib/operation.coffee index c0adac184..af963d8ca 100644 --- a/src/packages/command-panel/lib/operation.coffee +++ b/src/packages/command-panel/lib/operation.coffee @@ -25,7 +25,7 @@ class Operation preview: -> range = @bufferRange prefix = @lineText[0...range.start.column] - match = @lineText[range.start.column + 1...range.end.column] + match = @lineText[range.start.column...range.end.column] suffix = @lineText[range.end.column..] {prefix, suffix, match, range} diff --git a/src/packages/command-panel/lib/preview-list.coffee b/src/packages/command-panel/lib/preview-list.coffee index cf700af2d..101eb99ae 100644 --- a/src/packages/command-panel/lib/preview-list.coffee +++ b/src/packages/command-panel/lib/preview-list.coffee @@ -49,7 +49,7 @@ class PreviewList extends ScrollView populateSingle: (operation) -> @viewsForPath ||= {} - @show() + @lastRenderedOperationIndex ||= 0 @renderOperation(operation) @find('.operation:first').addClass('selected') @@ -67,6 +67,7 @@ class PreviewList extends ScrollView startingScrollHeight = @prop('scrollHeight') pathView = @pathViewForPath(operation.getPath()) pathView.addOperation(operation) + @lastRenderedOperationIndex++ pathViewForPath: (path) -> pathView = @viewsForPath[path] From ea65421120bea48a55836ae85f8186c4910a3a2a Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Sun, 31 Mar 2013 00:30:39 -0700 Subject: [PATCH 23/87] Remove streaming changes --- .../lib/command-panel-view.coffee | 5 ++--- .../select-all-matches-in-project.coffee | 4 +--- .../command-panel/lib/preview-list.coffee | 22 +++++-------------- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/src/packages/command-panel/lib/command-panel-view.coffee b/src/packages/command-panel/lib/command-panel-view.coffee index 7893f1145..b87aff9d2 100644 --- a/src/packages/command-panel/lib/command-panel-view.coffee +++ b/src/packages/command-panel/lib/command-panel-view.coffee @@ -129,7 +129,6 @@ class CommandPanelView extends View @previewHeader.show() @searchLoadingMessage.show() @errorMessages.empty() - project.previewList = @previewList try @commandInterpreter.eval(command, rootView.getActivePaneItem()).done ({operationsToPreview, errorMessages}) => @@ -144,9 +143,9 @@ class CommandPanelView extends View @errorMessages.append $$ -> @li errorMessage for errorMessage in errorMessages else if operationsToPreview?.length - @previewList.show() + @previewList.populate(operationsToPreview) @previewList.focus() - @previewCount.text("#{_.pluralize(operationsToPreview.length, 'match', 'matches')} in #{_.pluralize(@previewList.getPathCount(operationsToPreview), 'file')}").show() + @previewCount.text("#{_.pluralize(operationsToPreview.length, 'match', 'matches')} in #{_.pluralize(@previewList.getPathCount(), 'file')}").show() else @detach() catch error diff --git a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee index 65737f9cd..b0dd65e9e 100644 --- a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee +++ b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee @@ -14,14 +14,12 @@ class SelectAllMatchesInProject extends Command deferred = $.Deferred() operations = [] promise = project.scan @regex, ({path, range, match, lineText}) -> - op = new Operation( + operations.push(new Operation( project: project path: path bufferRange: range lineText: lineText ) - project.previewList.populateSingle(op) - operations.push(op) promise.done -> deferred.resolve(operations) deferred.promise() diff --git a/src/packages/command-panel/lib/preview-list.coffee b/src/packages/command-panel/lib/preview-list.coffee index 101eb99ae..49cb95835 100644 --- a/src/packages/command-panel/lib/preview-list.coffee +++ b/src/packages/command-panel/lib/preview-list.coffee @@ -19,7 +19,8 @@ class PreviewList extends ScrollView @on 'core:move-down', => @selectNextOperation(); false @on 'core:move-up', => @selectPreviousOperation(); false - + @on 'scroll', => + @renderOperations() if @scrollBottom() >= (@prop('scrollHeight')) @command 'command-panel:collapse-all', => @collapseAllPaths() @command 'command-panel:expand-all', => @expandAllPaths() @@ -27,6 +28,7 @@ class PreviewList extends ScrollView @children().each (index, element) -> $(element).view().expand() collapseAllPaths: -> + @renderOperations(renderAll: true) @children().each (index, element) -> $(element).view().collapse() destroy: -> @@ -46,14 +48,6 @@ class PreviewList extends ScrollView @find('.operation:first').addClass('selected') - populateSingle: (operation) -> - @viewsForPath ||= {} - - @lastRenderedOperationIndex ||= 0 - @renderOperation(operation) - - @find('.operation:first').addClass('selected') - renderOperations: ({renderAll}={}) -> renderAll ?= false startingScrollHeight = @prop('scrollHeight') @@ -62,12 +56,6 @@ class PreviewList extends ScrollView pathView.addOperation(operation) @lastRenderedOperationIndex++ break if not renderAll and @prop('scrollHeight') >= startingScrollHeight + @pixelOverdraw and @prop('scrollHeight') > @height() + @pixelOverdraw - - renderOperation: (operation) -> - startingScrollHeight = @prop('scrollHeight') - pathView = @pathViewForPath(operation.getPath()) - pathView.addOperation(operation) - @lastRenderedOperationIndex++ pathViewForPath: (path) -> pathView = @viewsForPath[path] @@ -106,8 +94,8 @@ class PreviewList extends ScrollView previousView.addClass('selected') previousView.scrollTo() - getPathCount: (operations=@operations)-> - _.keys(_.groupBy(operations, (operation) -> operation.getPath())).length + getPathCount: -> + _.keys(_.groupBy(@operations, (operation) -> operation.getPath())).length getOperations: -> new Array(@operations...) From 5e5437502f4f823aeebb980ab81ff6de39fc4a5e Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Sun, 31 Mar 2013 00:45:05 -0700 Subject: [PATCH 24/87] reset --- src/app/project.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/project.coffee b/src/app/project.coffee index d6651e727..bb0d30ecf 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -192,7 +192,7 @@ class Project readPath(line) if state is 'readingPath' readLine(line) if state is 'readingLines' - command = require.resolve('ag') + command = require.resolve('nak') args = ['--ackmate', regex.source, @getPath()] new BufferedProcess({command, args, stdout, exit}) deferred From 4cd181022d6105020efda3e2c1d1f582a5505b4d Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Sun, 31 Mar 2013 01:02:53 -0700 Subject: [PATCH 25/87] Restore old UI --- src/app/project.coffee | 3 +-- .../command-panel/lib/command-panel-view.coffee | 15 +++++++-------- .../commands/select-all-matches-in-project.coffee | 6 +++--- src/packages/command-panel/lib/operation.coffee | 6 +++--- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/app/project.coffee b/src/app/project.coffee index dea39f3c1..fa8f0d864 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -176,8 +176,7 @@ class Project for [column, length] in matchPositions range = new Range([row, column], [row, column + length]) - match = lineText.substr(column , length) - iterator({path, range, match, lineText}) + iterator({path, range, lineText}) deferred = $.Deferred() exit = (code) -> diff --git a/src/packages/command-panel/lib/command-panel-view.coffee b/src/packages/command-panel/lib/command-panel-view.coffee index b87aff9d2..0c8a8ccd0 100644 --- a/src/packages/command-panel/lib/command-panel-view.coffee +++ b/src/packages/command-panel/lib/command-panel-view.coffee @@ -12,8 +12,8 @@ module.exports = class CommandPanelView extends View @content: -> @div class: 'command-panel tool-panel', => + @div class: 'loading is-loading', outlet: 'loadingMessage', 'Searching...' @div class: 'header', outlet: 'previewHeader', => - @span outlet: 'searchLoadingMessage', class: 'loading', 'Searching...' @ul outlet: 'expandCollapse', class: 'expand-collapse', => @li class: 'collapse', 'Collapse All' @span outlet: 'previewCount', class: 'preview-count' @@ -53,8 +53,7 @@ class CommandPanelView extends View @previewList.hide() @previewHeader.hide() @errorMessages.hide() - @searchLoadingMessage.hide() - @expandCollapse.hide() + @loadingMessage.hide() @prompt.iconSize(@miniEditor.getFontSize()) @history = state.history ? [] @@ -126,14 +125,13 @@ class CommandPanelView extends View @miniEditor.getText() execute: (command=@escapedCommand()) -> - @previewHeader.show() - @searchLoadingMessage.show() + @loadingMessage.show() + @previewList.hide() @errorMessages.empty() try @commandInterpreter.eval(command, rootView.getActivePaneItem()).done ({operationsToPreview, errorMessages}) => - @searchLoadingMessage.hide() - @expandCollapse.show() + @loadingMessage.hide() @history.push(command) @historyIndex = @history.length @@ -143,13 +141,14 @@ class CommandPanelView extends View @errorMessages.append $$ -> @li errorMessage for errorMessage in errorMessages else if operationsToPreview?.length + @previewHeader.show() @previewList.populate(operationsToPreview) @previewList.focus() @previewCount.text("#{_.pluralize(operationsToPreview.length, 'match', 'matches')} in #{_.pluralize(@previewList.getPathCount(), 'file')}").show() else @detach() catch error - @searchLoadingMessage.hide() + @loadingMessage.hide() if error.name is "SyntaxError" @flashError() return diff --git a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee index b0dd65e9e..35742ef46 100644 --- a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee +++ b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee @@ -13,13 +13,13 @@ class SelectAllMatchesInProject extends Command compile: (project, buffer, range) -> deferred = $.Deferred() operations = [] - promise = project.scan @regex, ({path, range, match, lineText}) -> + promise = project.scan @regex, ({path, range, lineText}) -> operations.push(new Operation( project: project path: path bufferRange: range lineText: lineText - ) + )) promise.done -> deferred.resolve(operations) - deferred.promise() + deferred.promise() \ No newline at end of file diff --git a/src/packages/command-panel/lib/operation.coffee b/src/packages/command-panel/lib/operation.coffee index af963d8ca..f9b22973c 100644 --- a/src/packages/command-panel/lib/operation.coffee +++ b/src/packages/command-panel/lib/operation.coffee @@ -24,9 +24,9 @@ class Operation preview: -> range = @bufferRange - prefix = @lineText[0...range.start.column] - match = @lineText[range.start.column...range.end.column] - suffix = @lineText[range.end.column..] + prefix = @lineText[0...range.start.column + 1] + match = @lineText[range.start.column + 1...range.end.column + 1] + suffix = @lineText[range.end.column + 1..] {prefix, suffix, match, range} From 6f982c7c4cc1d91a74d69d960fa10256995ae853 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Sun, 31 Mar 2013 01:11:19 -0700 Subject: [PATCH 26/87] Fix spec --- spec/app/project-spec.coffee | 4 +++- src/app/project.coffee | 3 ++- .../lib/commands/select-all-matches-in-project.coffee | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/spec/app/project-spec.coffee b/spec/app/project-spec.coffee index 53d17d5e0..cc3d3879e 100644 --- a/spec/app/project-spec.coffee +++ b/spec/app/project-spec.coffee @@ -188,7 +188,7 @@ describe "Project", -> runs -> expect(paths).not.toContain('ignored/ignored.txt') - describe ".scan(options, callback)", -> + fdescribe ".scan(options, callback)", -> describe "when called with a regex", -> it "calls the callback with all regex matches in all files in the project", -> matches = [] @@ -253,11 +253,13 @@ describe "Project", -> path: project.resolve('a') match: 'aa ' range: [[0, 0], [0, 3]] + lineText: 'aaa bbb' expect(iterator.argsForCall[1][0]).toEqual path: project.resolve('a') match: 'a ' range: [[1, 3], [1, 5]] + lineText: 'cc aa cc' describe "serialization", -> it "restores the project path", -> diff --git a/src/app/project.coffee b/src/app/project.coffee index fa8f0d864..3c10f744a 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -176,7 +176,8 @@ class Project for [column, length] in matchPositions range = new Range([row, column], [row, column + length]) - iterator({path, range, lineText}) + match = lineText.substr(column + 1, length) + iterator({path, range, match, lineText}) deferred = $.Deferred() exit = (code) -> diff --git a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee index 35742ef46..20df613a3 100644 --- a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee +++ b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee @@ -13,7 +13,7 @@ class SelectAllMatchesInProject extends Command compile: (project, buffer, range) -> deferred = $.Deferred() operations = [] - promise = project.scan @regex, ({path, range, lineText}) -> + promise = project.scan @regex, ({path, range, match, lineText}) -> operations.push(new Operation( project: project path: path From da2cea4a24dc8fe95f2a0416cf47f25c9926f773 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Sun, 31 Mar 2013 01:20:50 -0700 Subject: [PATCH 27/87] Hide header on subsequent search --- src/packages/command-panel/lib/command-panel-view.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/packages/command-panel/lib/command-panel-view.coffee b/src/packages/command-panel/lib/command-panel-view.coffee index 0c8a8ccd0..6709db163 100644 --- a/src/packages/command-panel/lib/command-panel-view.coffee +++ b/src/packages/command-panel/lib/command-panel-view.coffee @@ -127,6 +127,7 @@ class CommandPanelView extends View execute: (command=@escapedCommand()) -> @loadingMessage.show() @previewList.hide() + @previewHeader.hide() @errorMessages.empty() try From ae544e0c7e5a8822403592ee8eacec8a56dbbbc4 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Sun, 31 Mar 2013 01:20:56 -0700 Subject: [PATCH 28/87] Bump nak --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 61c45b630..55518bde9 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "coffee-cache": "0.1.0", "pegjs": "0.7.0", "async": "0.2.6", - "nak": "0.2.5", + "nak": "0.2.6", "spellchecker": "0.2.0", "plist": "git://github.com/nathansobo/node-plist.git", "space-pen": "git://github.com/nathansobo/space-pen.git" From a4d8ed6ce68826e574851b76ca63fa769da24a7b Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Sun, 31 Mar 2013 17:07:30 -0700 Subject: [PATCH 29/87] Bump nak --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 55518bde9..adb5638b0 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "coffee-cache": "0.1.0", "pegjs": "0.7.0", "async": "0.2.6", - "nak": "0.2.6", + "nak": "0.2.7", "spellchecker": "0.2.0", "plist": "git://github.com/nathansobo/node-plist.git", "space-pen": "git://github.com/nathansobo/space-pen.git" From 2ae5cacc6ffef82b32bda4a9b01c9d399d0b4063 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 2 Apr 2013 13:25:21 -0700 Subject: [PATCH 30/87] Un-f project spec --- spec/app/project-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/project-spec.coffee b/spec/app/project-spec.coffee index cc3d3879e..c7b420650 100644 --- a/spec/app/project-spec.coffee +++ b/spec/app/project-spec.coffee @@ -188,7 +188,7 @@ describe "Project", -> runs -> expect(paths).not.toContain('ignored/ignored.txt') - fdescribe ".scan(options, callback)", -> + describe ".scan(options, callback)", -> describe "when called with a regex", -> it "calls the callback with all regex matches in all files in the project", -> matches = [] From d64c3e773de9ca33f11c4775259f3a762a6ea62e Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 2 Apr 2013 13:30:39 -0700 Subject: [PATCH 31/87] :lipstick: --- .../lib/commands/select-all-matches-in-project.coffee | 2 +- src/packages/command-panel/lib/preview-list.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee index 20df613a3..6cf583596 100644 --- a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee +++ b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee @@ -22,4 +22,4 @@ class SelectAllMatchesInProject extends Command )) promise.done -> deferred.resolve(operations) - deferred.promise() \ No newline at end of file + deferred.promise() diff --git a/src/packages/command-panel/lib/preview-list.coffee b/src/packages/command-panel/lib/preview-list.coffee index 49cb95835..58cb0f799 100644 --- a/src/packages/command-panel/lib/preview-list.coffee +++ b/src/packages/command-panel/lib/preview-list.coffee @@ -56,7 +56,7 @@ class PreviewList extends ScrollView pathView.addOperation(operation) @lastRenderedOperationIndex++ break if not renderAll and @prop('scrollHeight') >= startingScrollHeight + @pixelOverdraw and @prop('scrollHeight') > @height() + @pixelOverdraw - + pathViewForPath: (path) -> pathView = @viewsForPath[path] if not pathView From d2f2011ea3ea08fb54a8681f8946942ad91bea42 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 2 Apr 2013 13:50:48 -0700 Subject: [PATCH 32/87] Skip leading space after colon in nak output This was causing a spec to fail since the line text now had a leading space for all results since the separator before the line text is now ': ' instead of just ':'. --- src/app/project.coffee | 6 +++--- src/packages/command-panel/lib/operation.coffee | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/project.coffee b/src/app/project.coffee index 3c10f744a..e2ad125d3 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -164,9 +164,9 @@ class Project state = 'readingPath' path = null else - colonIndex = line.indexOf(':') + colonIndex = line.indexOf(': ') matchInfo = line.substring(0, colonIndex) - lineText = line.substring(colonIndex + 1) + lineText = line.substring(colonIndex + 2) readMatches(matchInfo, lineText) readMatches = (matchInfo, lineText) -> @@ -176,7 +176,7 @@ class Project for [column, length] in matchPositions range = new Range([row, column], [row, column + length]) - match = lineText.substr(column + 1, length) + match = lineText.substr(column, length) iterator({path, range, match, lineText}) deferred = $.Deferred() diff --git a/src/packages/command-panel/lib/operation.coffee b/src/packages/command-panel/lib/operation.coffee index f9b22973c..af963d8ca 100644 --- a/src/packages/command-panel/lib/operation.coffee +++ b/src/packages/command-panel/lib/operation.coffee @@ -24,9 +24,9 @@ class Operation preview: -> range = @bufferRange - prefix = @lineText[0...range.start.column + 1] - match = @lineText[range.start.column + 1...range.end.column + 1] - suffix = @lineText[range.end.column + 1..] + prefix = @lineText[0...range.start.column] + match = @lineText[range.start.column...range.end.column] + suffix = @lineText[range.end.column..] {prefix, suffix, match, range} From 9b19b4512f39d6b29ccdb414dbce8a11e3f4eef2 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 2 Apr 2013 13:53:49 -0700 Subject: [PATCH 33/87] Use getBufferRange() when previewing This is for the case where the marker may move between construction and previewing. --- src/packages/command-panel/lib/operation.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/command-panel/lib/operation.coffee b/src/packages/command-panel/lib/operation.coffee index af963d8ca..3dc17b29e 100644 --- a/src/packages/command-panel/lib/operation.coffee +++ b/src/packages/command-panel/lib/operation.coffee @@ -23,7 +23,7 @@ class Operation @getBufferRange() unless @preserveSelection preview: -> - range = @bufferRange + range = @getBufferRange() prefix = @lineText[0...range.start.column] match = @lineText[range.start.column...range.end.column] suffix = @lineText[range.end.column..] From 7caddc6c10f86a74c6f23d28ed6bc3fcc0dbd7ad Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 2 Apr 2013 14:04:38 -0700 Subject: [PATCH 34/87] Put the Expand/Collapse buttons back --- .../command-panel/lib/command-panel-view.coffee | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/packages/command-panel/lib/command-panel-view.coffee b/src/packages/command-panel/lib/command-panel-view.coffee index 6709db163..487af1730 100644 --- a/src/packages/command-panel/lib/command-panel-view.coffee +++ b/src/packages/command-panel/lib/command-panel-view.coffee @@ -6,7 +6,6 @@ PreviewList = require './preview-list' Editor = require 'editor' {SyntaxError} = require('pegjs').parser _ = require 'underscore' -$ = require 'jquery' module.exports = class CommandPanelView extends View @@ -15,6 +14,7 @@ class CommandPanelView extends View @div class: 'loading is-loading', outlet: 'loadingMessage', 'Searching...' @div class: 'header', outlet: 'previewHeader', => @ul outlet: 'expandCollapse', class: 'expand-collapse', => + @li class: 'expand', 'Expand All' @li class: 'collapse', 'Collapse All' @span outlet: 'previewCount', class: 'preview-count' @@ -92,17 +92,11 @@ class CommandPanelView extends View @miniEditor.focus() onExpandAll: (event) => - elButton = $(event.currentTarget) - @previewList.expandAllPaths() - elButton.removeClass("expand").addClass("collapse") - elButton.text("Collapse All") - @previewList.focus() + @previewList.expandAllPaths() + @previewList.focus() onCollapseAll: (event) => - elButton = $(event.currentTarget) @previewList.collapseAllPaths() - elButton.removeClass("collapse").addClass("expand") - elButton.text("Expand All") @previewList.focus() attach: (text='', options={}) -> From 1ee5eb32b418cc6aa90cb1303441604ada5332bf Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 2 Apr 2013 14:10:19 -0700 Subject: [PATCH 35/87] Restore scan spec asserts now that leading space is gone --- spec/app/project-spec.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/app/project-spec.coffee b/spec/app/project-spec.coffee index c7b420650..ab7e944de 100644 --- a/spec/app/project-spec.coffee +++ b/spec/app/project-spec.coffee @@ -247,17 +247,17 @@ describe "Project", -> stdout = BufferedProcess.prototype.bufferStream.argsForCall[0][1] stdout ":#{fs.resolveOnLoadPath('fixtures/dir/a')}\n" - stdout "1;0 3:aaa bbb\n2;3 2:cc aa cc\n" + stdout "1;0 3: aaa bbb\n2;3 2: cc aa cc\n" expect(iterator.argsForCall[0][0]).toEqual path: project.resolve('a') - match: 'aa ' + match: 'aaa' range: [[0, 0], [0, 3]] lineText: 'aaa bbb' expect(iterator.argsForCall[1][0]).toEqual path: project.resolve('a') - match: 'a ' + match: 'aa' range: [[1, 3], [1, 5]] lineText: 'cc aa cc' From 699212a13e3fc88806f93b6d020c1151d7a2d7e7 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 2 Apr 2013 14:17:47 -0700 Subject: [PATCH 36/87] Always pull line text from buffer This accounts for positional changes that may occur if the marker the operation is tracking moves. --- spec/app/project-spec.coffee | 2 -- src/app/project.coffee | 2 +- .../lib/commands/select-all-matches-in-project.coffee | 3 +-- src/packages/command-panel/lib/operation.coffee | 9 +++++---- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/spec/app/project-spec.coffee b/spec/app/project-spec.coffee index ab7e944de..5faf96952 100644 --- a/spec/app/project-spec.coffee +++ b/spec/app/project-spec.coffee @@ -253,13 +253,11 @@ describe "Project", -> path: project.resolve('a') match: 'aaa' range: [[0, 0], [0, 3]] - lineText: 'aaa bbb' expect(iterator.argsForCall[1][0]).toEqual path: project.resolve('a') match: 'aa' range: [[1, 3], [1, 5]] - lineText: 'cc aa cc' describe "serialization", -> it "restores the project path", -> diff --git a/src/app/project.coffee b/src/app/project.coffee index 6b29bab09..220c468a9 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -178,7 +178,7 @@ class Project for [column, length] in matchPositions range = new Range([row, column], [row, column + length]) match = lineText.substr(column, length) - iterator({path, range, match, lineText}) + iterator({path, range, match}) deferred = $.Deferred() exit = (code) -> diff --git a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee index 6cf583596..c9255a4fc 100644 --- a/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee +++ b/src/packages/command-panel/lib/commands/select-all-matches-in-project.coffee @@ -13,12 +13,11 @@ class SelectAllMatchesInProject extends Command compile: (project, buffer, range) -> deferred = $.Deferred() operations = [] - promise = project.scan @regex, ({path, range, match, lineText}) -> + promise = project.scan @regex, ({path, range}) -> operations.push(new Operation( project: project path: path bufferRange: range - lineText: lineText )) promise.done -> deferred.resolve(operations) diff --git a/src/packages/command-panel/lib/operation.coffee b/src/packages/command-panel/lib/operation.coffee index 3dc17b29e..7f70b75e0 100644 --- a/src/packages/command-panel/lib/operation.coffee +++ b/src/packages/command-panel/lib/operation.coffee @@ -1,6 +1,6 @@ module.exports = class Operation - constructor: ({@project, @path, @buffer, @bufferRange, @lineText, @newText, @preserveSelection, @errorMessage}) -> + constructor: ({@project, @path, @buffer, @bufferRange, @newText, @preserveSelection, @errorMessage}) -> if @buffer? @buffer.retain() @getMarker() @@ -24,9 +24,10 @@ class Operation preview: -> range = @getBufferRange() - prefix = @lineText[0...range.start.column] - match = @lineText[range.start.column...range.end.column] - suffix = @lineText[range.end.column..] + line = @getBuffer().lineForRow(range.start.row) + prefix = line[0...range.start.column] + match = line[range.start.column...range.end.column] + suffix = line[range.end.column..] {prefix, suffix, match, range} From f33a8538e3ab2144d1cc836870d837e7ac530653 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 2 Apr 2013 14:57:20 -0700 Subject: [PATCH 37/87] Bump nak version to allow for multiple matches --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bed01f2da..437fcb20a 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "coffee-cache": "0.1.0", "pegjs": "0.7.0", "async": "0.2.6", - "nak": "0.2.7", + "nak": "0.2.8", "spellchecker": "0.2.0", "plist": "git://github.com/nathansobo/node-plist.git", "space-pen": "git://github.com/nathansobo/space-pen.git" From 79ac5d606d523512e69a67d2f433591f1bff0ce0 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 2 Apr 2013 15:39:40 -0700 Subject: [PATCH 38/87] Bump nak again --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 437fcb20a..babfbd378 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "coffee-cache": "0.1.0", "pegjs": "0.7.0", "async": "0.2.6", - "nak": "0.2.8", + "nak": "0.2.9", "spellchecker": "0.2.0", "plist": "git://github.com/nathansobo/node-plist.git", "space-pen": "git://github.com/nathansobo/space-pen.git" From 955e1571a2677a84e8e7883d1abf3b98f1331680 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 2 Apr 2013 16:40:32 -0700 Subject: [PATCH 39/87] Bump nak version to add @kevinsawicki changes --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index babfbd378..9cae1eb37 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "coffee-cache": "0.1.0", "pegjs": "0.7.0", "async": "0.2.6", - "nak": "0.2.9", + "nak": "0.2.10", "spellchecker": "0.2.0", "plist": "git://github.com/nathansobo/node-plist.git", "space-pen": "git://github.com/nathansobo/space-pen.git" From 34ca5ce9490c57faf33d100a82dfcbb98749f73a Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 2 Apr 2013 16:49:17 -0700 Subject: [PATCH 40/87] nak no longer includes a space after the colon --- spec/app/project-spec.coffee | 2 +- src/app/project.coffee | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/app/project-spec.coffee b/spec/app/project-spec.coffee index 5faf96952..48f483fc5 100644 --- a/spec/app/project-spec.coffee +++ b/spec/app/project-spec.coffee @@ -247,7 +247,7 @@ describe "Project", -> stdout = BufferedProcess.prototype.bufferStream.argsForCall[0][1] stdout ":#{fs.resolveOnLoadPath('fixtures/dir/a')}\n" - stdout "1;0 3: aaa bbb\n2;3 2: cc aa cc\n" + stdout "1;0 3:aaa bbb\n2;3 2:cc aa cc\n" expect(iterator.argsForCall[0][0]).toEqual path: project.resolve('a') diff --git a/src/app/project.coffee b/src/app/project.coffee index 220c468a9..0ad04ed57 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -165,9 +165,9 @@ class Project state = 'readingPath' path = null else - colonIndex = line.indexOf(': ') + colonIndex = line.indexOf(':') matchInfo = line.substring(0, colonIndex) - lineText = line.substring(colonIndex + 2) + lineText = line.substring(colonIndex + 1) readMatches(matchInfo, lineText) readMatches = (matchInfo, lineText) -> From 22af597c429e9d718a1147a8436695856068636c Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 2 Apr 2013 16:50:53 -0700 Subject: [PATCH 41/87] :lipstick: --- src/packages/command-panel/lib/command-panel-view.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/command-panel/lib/command-panel-view.coffee b/src/packages/command-panel/lib/command-panel-view.coffee index 487af1730..13d773e41 100644 --- a/src/packages/command-panel/lib/command-panel-view.coffee +++ b/src/packages/command-panel/lib/command-panel-view.coffee @@ -92,8 +92,8 @@ class CommandPanelView extends View @miniEditor.focus() onExpandAll: (event) => - @previewList.expandAllPaths() - @previewList.focus() + @previewList.expandAllPaths() + @previewList.focus() onCollapseAll: (event) => @previewList.collapseAllPaths() From acf538cf4b49d10b09ab32acbb1d36f8c8d3aa62 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 08:18:00 -0700 Subject: [PATCH 42/87] Remove unused aborted ivar --- src/packages/fuzzy-finder/lib/load-paths-task.coffee | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index 538aac6ba..038da2f5f 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -4,8 +4,6 @@ $ = require 'jquery' module.exports = class LoadPathsTask - aborted: false - constructor: (@callback) -> start: -> @@ -34,7 +32,6 @@ class LoadPathsTask deferred abort: -> - @aborted = true if @process? @process.kill() @process = null From 320d177038579c7ba7a6e8a8e8fa19b27cfc4d38 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 08:19:04 -0700 Subject: [PATCH 43/87] Use consistent quotes --- src/packages/fuzzy-finder/lib/load-paths-task.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index 038da2f5f..0849f5a87 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -14,8 +14,8 @@ class LoadPathsTask command = require.resolve 'nak' args = ['-l', rootPath] - args.unshift("--addVCSIgnores") if config.get('nak.addVCSIgnores') - args.unshift("-d", ignoredNames.join(',')) if ignoredNames.length > 0 + args.unshift('--addVCSIgnores') if config.get('nak.addVCSIgnores') + args.unshift('-d', ignoredNames.join(',')) if ignoredNames.length > 0 paths = [] deferred = $.Deferred() @@ -26,7 +26,7 @@ class LoadPathsTask @callback(paths) deferred.resolve() stdout = (data) -> - paths = paths.concat(_.compact(data.split("\n"))) + paths = paths.concat(_.compact(data.split('\n'))) @process = new BufferedProcess({command, args, stdout, exit}) deferred From 2dc1817807e4aff179a15624af603a5151bcc9d8 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 08:24:31 -0700 Subject: [PATCH 44/87] Push new paths onto existing array --- src/packages/fuzzy-finder/lib/load-paths-task.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index 0849f5a87..6342a9f9e 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -26,7 +26,7 @@ class LoadPathsTask @callback(paths) deferred.resolve() stdout = (data) -> - paths = paths.concat(_.compact(data.split('\n'))) + paths.push.apply(paths, _.compact(data.split('\n'))) @process = new BufferedProcess({command, args, stdout, exit}) deferred From 917fbaadad5b038e5149fcebd93e761aa87635b3 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 08:27:36 -0700 Subject: [PATCH 45/87] Use splat instead of apply --- src/packages/fuzzy-finder/lib/load-paths-task.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index 6342a9f9e..cc10cb2a2 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -26,7 +26,7 @@ class LoadPathsTask @callback(paths) deferred.resolve() stdout = (data) -> - paths.push.apply(paths, _.compact(data.split('\n'))) + paths.push(_.compact(data.split('\n'))...) @process = new BufferedProcess({command, args, stdout, exit}) deferred From aa8fb3eb4bee8f450638e146d2c75753d5f8557f Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 08:33:16 -0700 Subject: [PATCH 46/87] Add failing symlink spec --- .../fuzzy-finder/spec/fuzzy-finder-spec.coffee | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee index c71a090c5..9b9a5b983 100644 --- a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee +++ b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee @@ -60,6 +60,17 @@ describe 'FuzzyFinder', -> expect(finderView.list.children().first()).toHaveClass 'selected' expect(finderView.find(".loading")).not.toBeVisible() + it "includes symlinked file paths", -> + rootView.attachToDom() + finderView.maxItems = Infinity + rootView.trigger 'fuzzy-finder:toggle-file-finder' + + waitsFor "all project paths to load", 5000, -> + not finderView.reloadProjectPaths + + runs -> + expect(finderView.list.find("li:contains(symlink-to-file)")).toExist() + describe "when root view's project has no path", -> beforeEach -> project.setPath(null) From ec93dc38b7dea14635d6dd5c48c91765956b20e8 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Apr 2013 10:21:56 -0600 Subject: [PATCH 47/87] Version serialized state of EditSession Since buffers are now serialized directly, previous versions of the EditSession state which used the buffer's path as the `buffer` key are no longer valid. --- src/app/edit-session.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index 36884284a..c1bb8022d 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -14,6 +14,8 @@ module.exports = class EditSession registerDeserializer(this) + @version: 1 + @deserialize: (state) -> session = project.buildEditSessionForBuffer(Buffer.deserialize(state.buffer)) if !session? @@ -87,6 +89,7 @@ class EditSession serialize: -> deserializer: 'EditSession' + version: @constructor.version buffer: @buffer.serialize() scrollTop: @getScrollTop() scrollLeft: @getScrollLeft() From 11f140ac5aab107fa26ba382996232d452f6169b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Apr 2013 10:27:57 -0600 Subject: [PATCH 48/87] Don't save/load window state to/from disk when pathToOpen is undefined This fixes a bug where `meta-n` was not opening a new buffer in the opened window if window state was previously saved for an `undefined` path. --- src/app/atom.coffee | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/atom.coffee b/src/app/atom.coffee index 6294b8961..05eeea60d 100644 --- a/src/app/atom.coffee +++ b/src/app/atom.coffee @@ -233,10 +233,12 @@ _.extend atom, null getSavedWindowState: -> - localStorage[window.location.params.pathToOpen] + if pathToOpen = window.location.params.pathToOpen + localStorage[pathToOpen] saveWindowState: -> - localStorage[@getPathToOpen()] = JSON.stringify(@getWindowState()) + if pathToOpen = @getPathToOpen() + localStorage[pathToOpen] = JSON.stringify(@getWindowState()) update: -> @sendMessageToBrowserProcess('update') From 68a02fe0094fb4c92cdb9269d54d36db9d260edb Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Apr 2013 11:03:45 -0600 Subject: [PATCH 49/87] Use `backwardsScanInRange` instead of passing `true` --- src/packages/bracket-matcher/lib/bracket-matcher.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/packages/bracket-matcher/lib/bracket-matcher.coffee b/src/packages/bracket-matcher/lib/bracket-matcher.coffee index 11398aad2..e5cc0c16c 100644 --- a/src/packages/bracket-matcher/lib/bracket-matcher.coffee +++ b/src/packages/bracket-matcher/lib/bracket-matcher.coffee @@ -116,14 +116,13 @@ module.exports = regex = new RegExp("[#{_.escapeRegExp(startPair + endPair)}]", 'g') startPairPosition = null unpairedCount = 0 - scanner = (match, range, {stop}) => + buffer.backwardsScanInRange regex, scanRange, (match, range, {stop}) => if match[0] is endPair unpairedCount++ else if match[0] is startPair unpairedCount-- startPairPosition = range.start stop() if unpairedCount < 0 - buffer.scanInRange(regex, scanRange, scanner, true) startPairPosition updateMatch: (editor) -> From 59a5a5bc8f00f657b236f1c6c08069da611cdb0b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Apr 2013 11:09:26 -0600 Subject: [PATCH 50/87] Always pass a hash to TextBuffer.scanInRange iterators This makes it easy to only assign variables for the information you need in the iterator. Before, we always forced you to take a match and a range as the first two arguments even if you weren't using them. --- spec/app/text-buffer-spec.coffee | 24 ++++++++-------- src/app/cursor.coffee | 28 +++++++++---------- src/app/edit-session.coffee | 2 +- src/app/text-buffer.coffee | 2 +- .../autocomplete/lib/autocomplete-view.coffee | 2 +- .../lib/bracket-matcher.coffee | 4 +-- .../lib/commands/regex-address.coffee | 4 +-- .../lib/commands/select-all-matches.coffee | 6 ++-- .../lib/commands/substitution.coffee | 6 ++-- src/packages/whitespace/lib/whitespace.coffee | 2 +- 10 files changed, 40 insertions(+), 40 deletions(-) diff --git a/spec/app/text-buffer-spec.coffee b/spec/app/text-buffer-spec.coffee index 72233ba16..dbf75502d 100644 --- a/spec/app/text-buffer-spec.coffee +++ b/spec/app/text-buffer-spec.coffee @@ -546,7 +546,7 @@ describe 'Buffer', -> describe "when given a regex with a ignore case flag", -> it "does a case-insensitive search", -> matches = [] - buffer.scanInRange /cuRRent/i, [[0,0], [12,0]], (match, range) -> + buffer.scanInRange /cuRRent/i, [[0,0], [12,0]], ({match, range}) -> matches.push(match) expect(matches.length).toBe 1 @@ -554,7 +554,7 @@ describe 'Buffer', -> it "calls the iterator with the first match for the given regex in the given range", -> matches = [] ranges = [] - buffer.scanInRange /cu(rr)ent/, [[4,0], [6,44]], (match, range) -> + buffer.scanInRange /cu(rr)ent/, [[4,0], [6,44]], ({match, range}) -> matches.push(match) ranges.push(range) @@ -569,7 +569,7 @@ describe 'Buffer', -> it "calls the iterator with each match for the given regex in the given range", -> matches = [] ranges = [] - buffer.scanInRange /cu(rr)ent/g, [[4,0], [6,59]], (match, range) -> + buffer.scanInRange /cu(rr)ent/g, [[4,0], [6,59]], ({match, range}) -> matches.push(match) ranges.push(range) @@ -593,7 +593,7 @@ describe 'Buffer', -> it "calls the iterator with the truncated match", -> matches = [] ranges = [] - buffer.scanInRange /cu(r*)/g, [[4,0], [6,9]], (match, range) -> + buffer.scanInRange /cu(r*)/g, [[4,0], [6,9]], ({match, range}) -> matches.push(match) ranges.push(range) @@ -612,7 +612,7 @@ describe 'Buffer', -> it "calls the iterator with the truncated match", -> matches = [] ranges = [] - buffer.scanInRange /cu(r*)e/g, [[4,0], [6,9]], (match, range) -> + buffer.scanInRange /cu(r*)e/g, [[4,0], [6,9]], ({match, range}) -> matches.push(match) ranges.push(range) @@ -626,7 +626,7 @@ describe 'Buffer', -> describe "when the iterator calls the 'replace' control function with a replacement string", -> it "replaces each occurrence of the regex match with the string", -> ranges = [] - buffer.scanInRange /cu(rr)ent/g, [[4,0], [6,59]], (match, range, { replace }) -> + buffer.scanInRange /cu(rr)ent/g, [[4,0], [6,59]], ({range, replace}) -> ranges.push(range) replace("foo") @@ -638,7 +638,7 @@ describe 'Buffer', -> expect(buffer.lineForRow(6)).toBe ' foo < pivot ? left.push(foo) : right.push(current);' it "allows the match to be replaced with the empty string", -> - buffer.scanInRange /current/g, [[4,0], [6,59]], (match, range, { replace }) -> + buffer.scanInRange /current/g, [[4,0], [6,59]], ({replace}) -> replace("") expect(buffer.lineForRow(5)).toBe ' = items.shift();' @@ -647,7 +647,7 @@ describe 'Buffer', -> describe "when the iterator calls the 'stop' control function", -> it "stops the traversal", -> ranges = [] - buffer.scanInRange /cu(rr)ent/g, [[4,0], [6,59]], (match, range, { stop }) -> + buffer.scanInRange /cu(rr)ent/g, [[4,0], [6,59]], ({range, stop}) -> ranges.push(range) stop() if ranges.length == 2 @@ -658,7 +658,7 @@ describe 'Buffer', -> it "calls the iterator with the last match for the given regex in the given range", -> matches = [] ranges = [] - buffer.backwardsScanInRange /cu(rr)ent/, [[4,0], [6,44]], (match, range) -> + buffer.backwardsScanInRange /cu(rr)ent/, [[4,0], [6,44]], ({match, range}) -> matches.push(match) ranges.push(range) @@ -673,7 +673,7 @@ describe 'Buffer', -> it "calls the iterator with each match for the given regex in the given range, starting with the last match", -> matches = [] ranges = [] - buffer.backwardsScanInRange /cu(rr)ent/g, [[4,0], [6,59]], (match, range) -> + buffer.backwardsScanInRange /cu(rr)ent/g, [[4,0], [6,59]], ({match, range}) -> matches.push(match) ranges.push(range) @@ -695,7 +695,7 @@ describe 'Buffer', -> describe "when the iterator calls the 'replace' control function with a replacement string", -> it "replaces each occurrence of the regex match with the string", -> ranges = [] - buffer.backwardsScanInRange /cu(rr)ent/g, [[4,0], [6,59]], (match, range, { replace }) -> + buffer.backwardsScanInRange /cu(rr)ent/g, [[4,0], [6,59]], ({range, replace}) -> ranges.push(range) replace("foo") unless range.start.isEqual([6,6]) @@ -709,7 +709,7 @@ describe 'Buffer', -> describe "when the iterator calls the 'stop' control function", -> it "stops the traversal", -> ranges = [] - buffer.backwardsScanInRange /cu(rr)ent/g, [[4,0], [6,59]], (match, range, { stop }) -> + buffer.backwardsScanInRange /cu(rr)ent/g, [[4,0], [6,59]], ({range, stop}) -> ranges.push(range) stop() if ranges.length == 2 diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index 5708d4bf7..f12a78c65 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -133,20 +133,20 @@ class Cursor moveToFirstCharacterOfLine: -> position = @getBufferPosition() - range = @getCurrentLineBufferRange() + scanRange = @getCurrentLineBufferRange() newPosition = null - @editSession.scanInRange /^\s*/, range, (match, matchRange) => - newPosition = matchRange.end + @editSession.scanInRange /^\s*/, scanRange, ({range}) => + newPosition = range.end return unless newPosition newPosition = [position.row, 0] if newPosition.isEqual(position) @setBufferPosition(newPosition) skipLeadingWhitespace: -> position = @getBufferPosition() - range = @getCurrentLineBufferRange() + scanRange = @getCurrentLineBufferRange() endOfLeadingWhitespace = null - @editSession.scanInRange /^[ \t]*/, range, (match, matchRange) => - endOfLeadingWhitespace = matchRange.end + @editSession.scanInRange /^[ \t]*/, scanRange, ({range}) => + endOfLeadingWhitespace = range.end @setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position) @@ -164,12 +164,12 @@ class Cursor allowPrevious = options.allowPrevious ? true currentBufferPosition = @getBufferPosition() previousNonBlankRow = @editSession.buffer.previousNonBlankRow(currentBufferPosition.row) - range = [[previousNonBlankRow, 0], currentBufferPosition] + scanRange = [[previousNonBlankRow, 0], currentBufferPosition] beginningOfWordPosition = null - @editSession.backwardsScanInRange (options.wordRegex ? @wordRegExp()), range, (match, matchRange, { stop }) => - if matchRange.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious - beginningOfWordPosition = matchRange.start + @editSession.backwardsScanInRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) => + if range.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious + beginningOfWordPosition = range.start if not beginningOfWordPosition?.isEqual(currentBufferPosition) stop() @@ -178,12 +178,12 @@ class Cursor getEndOfCurrentWordBufferPosition: (options = {}) -> allowNext = options.allowNext ? true currentBufferPosition = @getBufferPosition() - range = [currentBufferPosition, @editSession.getEofBufferPosition()] + scanRange = [currentBufferPosition, @editSession.getEofBufferPosition()] endOfWordPosition = null - @editSession.scanInRange (options.wordRegex ? @wordRegExp()), range, (match, matchRange, { stop }) => - if matchRange.start.isLessThanOrEqual(currentBufferPosition) or allowNext - endOfWordPosition = matchRange.end + @editSession.scanInRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) => + if range.start.isLessThanOrEqual(currentBufferPosition) or allowNext + endOfWordPosition = range.end if not endOfWordPosition?.isEqual(currentBufferPosition) stop() diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index c1bb8022d..64f6a2a6c 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -245,7 +245,7 @@ class EditSession normalizeTabsInBufferRange: (bufferRange) -> return unless @softTabs - @scanInRange /\t/, bufferRange, (match, range, {replace}) => replace(@getTabText()) + @scanInRange /\t/, bufferRange, ({replace}) => replace(@getTabText()) cutToEndOfLine: -> maintainPasteboard = false diff --git a/src/app/text-buffer.coffee b/src/app/text-buffer.coffee index 36f93724c..803e009bf 100644 --- a/src/app/text-buffer.coffee +++ b/src/app/text-buffer.coffee @@ -404,7 +404,7 @@ class Buffer range = new Range(startPosition, endPosition) keepLooping = true replacementText = null - iterator(match, range, { stop, replace }) + iterator({match, range, stop, replace }) if replacementText? @change(range, replacementText) diff --git a/src/packages/autocomplete/lib/autocomplete-view.coffee b/src/packages/autocomplete/lib/autocomplete-view.coffee index 2123c442f..8da239fc2 100644 --- a/src/packages/autocomplete/lib/autocomplete-view.coffee +++ b/src/packages/autocomplete/lib/autocomplete-view.coffee @@ -142,7 +142,7 @@ class AutocompleteView extends SelectList lineRange = [[selectionRange.start.row, 0], [selectionRange.end.row, @editor.lineLengthForBufferRow(selectionRange.end.row)]] [prefix, suffix] = ["", ""] - @currentBuffer.scanInRange @wordRegex, lineRange, (match, range, {stop}) -> + @currentBuffer.scanInRange @wordRegex, lineRange, ({match, range, stop}) -> stop() if range.start.isGreaterThan(selectionRange.end) if range.intersectsWith(selectionRange) diff --git a/src/packages/bracket-matcher/lib/bracket-matcher.coffee b/src/packages/bracket-matcher/lib/bracket-matcher.coffee index e5cc0c16c..4d80300fa 100644 --- a/src/packages/bracket-matcher/lib/bracket-matcher.coffee +++ b/src/packages/bracket-matcher/lib/bracket-matcher.coffee @@ -102,7 +102,7 @@ module.exports = regex = new RegExp("[#{_.escapeRegExp(startPair + endPair)}]", 'g') endPairPosition = null unpairedCount = 0 - buffer.scanInRange regex, scanRange, (match, range, {stop}) => + buffer.scanInRange regex, scanRange, ({match, range, stop}) => if match[0] is startPair unpairedCount++ else if match[0] is endPair @@ -116,7 +116,7 @@ module.exports = regex = new RegExp("[#{_.escapeRegExp(startPair + endPair)}]", 'g') startPairPosition = null unpairedCount = 0 - buffer.backwardsScanInRange regex, scanRange, (match, range, {stop}) => + buffer.backwardsScanInRange regex, scanRange, ({match, range, stop}) => if match[0] is endPair unpairedCount++ else if match[0] is startPair diff --git a/src/packages/command-panel/lib/commands/regex-address.coffee b/src/packages/command-panel/lib/commands/regex-address.coffee index d2828a6be..9764d0cae 100644 --- a/src/packages/command-panel/lib/commands/regex-address.coffee +++ b/src/packages/command-panel/lib/commands/regex-address.coffee @@ -24,12 +24,12 @@ class RegexAddress extends Address rangeToReturn = null scanMethodName = if @isReversed then "backwardsScanInRange" else "scanInRange" - buffer[scanMethodName] @regex, rangeToSearch, (match, range) -> + buffer[scanMethodName] @regex, rangeToSearch, ({range}) -> rangeToReturn = range if not rangeToReturn rangeToSearch = if @isReversed then rangeAfter else rangeBefore - buffer[scanMethodName] @regex, rangeToSearch, (match, range) -> + buffer[scanMethodName] @regex, rangeToSearch, ({range}) -> rangeToReturn = range if not rangeToReturn diff --git a/src/packages/command-panel/lib/commands/select-all-matches.coffee b/src/packages/command-panel/lib/commands/select-all-matches.coffee index ad1b0bca9..dc0eaef35 100644 --- a/src/packages/command-panel/lib/commands/select-all-matches.coffee +++ b/src/packages/command-panel/lib/commands/select-all-matches.coffee @@ -12,12 +12,12 @@ class SelectAllMatches extends Command compile: (project, buffer, ranges) -> deferred = $.Deferred() operations = [] - for range in ranges - buffer.scanInRange @regex, range, (match, matchRange) -> + for scanRange in ranges + buffer.scanInRange @regex, scanRange, ({range}) -> operations.push(new Operation( project: project buffer: buffer - bufferRange: matchRange + bufferRange: range )) deferred.resolve(operations) deferred.promise() diff --git a/src/packages/command-panel/lib/commands/substitution.coffee b/src/packages/command-panel/lib/commands/substitution.coffee index 66a621bc2..71a229c32 100644 --- a/src/packages/command-panel/lib/commands/substitution.coffee +++ b/src/packages/command-panel/lib/commands/substitution.coffee @@ -15,12 +15,12 @@ class Substitution extends Command compile: (project, buffer, ranges) -> deferred = $.Deferred() operations = [] - for range in ranges - buffer.scanInRange @regex, range, (match, matchRange) => + for scanRange in ranges + buffer.scanInRange @regex, scanRange, ({range}) => operations.push(new Operation( project: project buffer: buffer - bufferRange: matchRange + bufferRange: range newText: @replacementText preserveSelection: true )) diff --git a/src/packages/whitespace/lib/whitespace.coffee b/src/packages/whitespace/lib/whitespace.coffee index 087bfabd3..f5c954ba5 100644 --- a/src/packages/whitespace/lib/whitespace.coffee +++ b/src/packages/whitespace/lib/whitespace.coffee @@ -8,7 +8,7 @@ module.exports = whitespaceBeforeSave: (buffer) -> buffer.on 'will-be-saved', -> buffer.transact -> - buffer.scan /[ \t]+$/g, (match, range, { replace }) -> replace('') + buffer.scan /[ \t]+$/g, ({replace}) -> replace('') if config.get('whitespace.ensureSingleTrailingNewline') if buffer.getLastLine() is '' From 5df78812ef2c5589b765c1da952a064fdee57d72 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Apr 2013 11:28:55 -0600 Subject: [PATCH 51/87] Rename EditSession.scanInRange to scanInBufferRange This is more consistent with other range-oriented methods on EditSession. At this layer, we need to be explicit about what kind of range we are talking about. --- src/app/cursor.coffee | 8 ++++---- src/app/edit-session.coffee | 6 +++--- src/app/editor.coffee | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index f12a78c65..8f1a21d67 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -135,7 +135,7 @@ class Cursor position = @getBufferPosition() scanRange = @getCurrentLineBufferRange() newPosition = null - @editSession.scanInRange /^\s*/, scanRange, ({range}) => + @editSession.scanInBufferRange /^\s*/, scanRange, ({range}) => newPosition = range.end return unless newPosition newPosition = [position.row, 0] if newPosition.isEqual(position) @@ -145,7 +145,7 @@ class Cursor position = @getBufferPosition() scanRange = @getCurrentLineBufferRange() endOfLeadingWhitespace = null - @editSession.scanInRange /^[ \t]*/, scanRange, ({range}) => + @editSession.scanInBufferRange /^[ \t]*/, scanRange, ({range}) => endOfLeadingWhitespace = range.end @setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position) @@ -167,7 +167,7 @@ class Cursor scanRange = [[previousNonBlankRow, 0], currentBufferPosition] beginningOfWordPosition = null - @editSession.backwardsScanInRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) => + @editSession.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) => if range.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious beginningOfWordPosition = range.start if not beginningOfWordPosition?.isEqual(currentBufferPosition) @@ -181,7 +181,7 @@ class Cursor scanRange = [currentBufferPosition, @editSession.getEofBufferPosition()] endOfWordPosition = null - @editSession.scanInRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) => + @editSession.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) => if range.start.isLessThanOrEqual(currentBufferPosition) or allowNext endOfWordPosition = range.end if not endOfWordPosition?.isEqual(currentBufferPosition) diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index 64f6a2a6c..b420f7875 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -163,8 +163,8 @@ class EditSession bufferRangeForBufferRow: (row, options) -> @buffer.rangeForRow(row, options) lineForBufferRow: (row) -> @buffer.lineForRow(row) lineLengthForBufferRow: (row) -> @buffer.lineLengthForRow(row) - scanInRange: (args...) -> @buffer.scanInRange(args...) - backwardsScanInRange: (args...) -> @buffer.backwardsScanInRange(args...) + scanInBufferRange: (args...) -> @buffer.scanInRange(args...) + backwardsScanInBufferRange: (args...) -> @buffer.backwardsScanInRange(args...) isModified: -> @buffer.isModified() hasEditors: -> @buffer.hasEditors() @@ -245,7 +245,7 @@ class EditSession normalizeTabsInBufferRange: (bufferRange) -> return unless @softTabs - @scanInRange /\t/, bufferRange, ({replace}) => replace(@getTabText()) + @scanInBufferRange /\t/, bufferRange, ({replace}) => replace(@getTabText()) cutToEndOfLine: -> maintainPasteboard = false diff --git a/src/app/editor.coffee b/src/app/editor.coffee index a9583b713..a3bed0f00 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -315,8 +315,8 @@ class Editor extends View lineForBufferRow: (row) -> @getBuffer().lineForRow(row) lineLengthForBufferRow: (row) -> @getBuffer().lineLengthForRow(row) rangeForBufferRow: (row) -> @getBuffer().rangeForRow(row) - scanInRange: (args...) -> @getBuffer().scanInRange(args...) - backwardsScanInRange: (args...) -> @getBuffer().backwardsScanInRange(args...) + scanInBufferRange: (args...) -> @getBuffer().scanInRange(args...) + backwardsScanInBufferRange: (args...) -> @getBuffer().backwardsScanInRange(args...) configure: -> @observeConfig 'editor.showLineNumbers', (showLineNumbers) => @gutter.setShowLineNumbers(showLineNumbers) From a99e4ef5a97a80f61e88f2fc9ee7a428b01e0d51 Mon Sep 17 00:00:00 2001 From: Jeremy McAnally Date: Wed, 3 Apr 2013 14:44:30 -0300 Subject: [PATCH 52/87] Making an edit to .github To make the docs build... --- .github | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github b/.github index fc652aa74..f9562bce3 100644 --- a/.github +++ b/.github @@ -1,2 +1,2 @@ [docs] - title = The Guide to Atom +title = The Guide to Atom From f03b6207de9180e005d46be057edf8a86b72ccb1 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Apr 2013 12:01:01 -0600 Subject: [PATCH 53/87] Make all requires of 'fs-utils' assign to fsUtils var instead of fs --- benchmark/benchmark-suite.coffee | 1 - spec/app/atom-spec.coffee | 20 +-- spec/app/config-spec.coffee | 50 +++---- spec/app/directory-spec.coffee | 24 ++-- spec/app/editor-spec.coffee | 38 +++--- spec/app/file-spec.coffee | 34 ++--- spec/app/git-spec.coffee | 110 +++++++-------- spec/app/project-spec.coffee | 50 +++---- spec/app/root-view-spec.coffee | 6 +- spec/app/syntax-spec.coffee | 8 +- spec/app/text-buffer-spec.coffee | 64 ++++----- spec/app/text-mate-grammar-spec.coffee | 6 +- spec/app/text-mate-theme-spec.coffee | 4 +- spec/app/theme-spec.coffee | 4 +- spec/app/window-spec.coffee | 14 +- spec/jasmine-helper.coffee | 6 +- spec/spec-helper.coffee | 10 +- spec/spec-suite.coffee | 8 +- spec/stdlib/fs-utils-spec.coffee | 96 ++++++------- src/app/atom-theme.coffee | 12 +- src/app/atom.coffee | 22 +-- src/app/binding-set.coffee | 4 +- src/app/config.coffee | 42 +++--- src/app/edit-session.coffee | 10 +- src/app/editor.coffee | 6 +- src/app/git.coffee | 4 +- src/app/keymap.coffee | 8 +- src/app/package.coffee | 4 +- src/app/project.coffee | 12 +- src/app/repository-status-handler.coffee | 4 +- src/app/root-view.coffee | 3 +- src/app/syntax.coffee | 4 +- src/app/text-buffer.coffee | 4 +- src/app/text-mate-grammar.coffee | 8 +- src/app/text-mate-theme.coffee | 2 +- src/app/theme.coffee | 6 +- src/app/window.coffee | 26 ++-- .../lib/command-interpreter.coffee | 4 +- .../command-panel/lib/path-view.coffee | 4 +- .../command-panel/lib/preview-list.coffee | 2 +- .../fuzzy-finder/lib/fuzzy-finder-view.coffee | 22 +-- .../fuzzy-finder/lib/load-paths-task.coffee | 6 +- .../spec/fuzzy-finder-spec.coffee | 18 +-- .../lib/markdown-preview-view.coffee | 1 - .../lib/package-generator-view.coffee | 30 ++--- .../spec/package-generator-spec.coffee | 16 +-- .../snippets/lib/snippet-body-parser.coffee | 4 +- .../snippets/spec/snippets-spec.coffee | 1 - .../status-bar/spec/status-bar-spec.coffee | 32 ++--- .../symbols-view/lib/load-tags-handler.coffee | 10 +- .../symbols-view/lib/symbols-view.coffee | 12 +- .../symbols-view/lib/tag-generator.coffee | 4 +- .../symbols-view/lib/tag-reader.coffee | 4 +- .../spec/symbols-view-spec.coffee | 14 +- src/packages/tabs/lib/tab-view.coffee | 4 +- src/packages/tabs/spec/tabs-spec.coffee | 1 - src/packages/tree-view/lib/dialog.coffee | 6 +- src/packages/tree-view/lib/file-view.coffee | 14 +- src/packages/tree-view/lib/tree-view.coffee | 22 +-- .../tree-view/spec/tree-view-spec.coffee | 126 +++++++++--------- .../whitespace/spec/whitespace-spec.coffee | 9 +- src/stdlib/task.coffee | 2 +- 62 files changed, 548 insertions(+), 554 deletions(-) diff --git a/benchmark/benchmark-suite.coffee b/benchmark/benchmark-suite.coffee index 8f549b061..fd5fd0533 100644 --- a/benchmark/benchmark-suite.coffee +++ b/benchmark/benchmark-suite.coffee @@ -1,5 +1,4 @@ require 'benchmark-helper' -fs = require 'fs-utils' $ = require 'jquery' _ = require 'underscore' TokenizedBuffer = require 'tokenized-buffer' diff --git a/spec/app/atom-spec.coffee b/spec/app/atom-spec.coffee index df2ff94d0..1da757d0f 100644 --- a/spec/app/atom-spec.coffee +++ b/spec/app/atom-spec.coffee @@ -1,7 +1,7 @@ $ = require 'jquery' RootView = require 'root-view' {$$} = require 'space-pen' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' describe "the `atom` global", -> beforeEach -> @@ -129,9 +129,9 @@ describe "the `atom` global", -> describe "stylesheet loading", -> describe "when the metadata contains a 'stylesheets' manifest", -> it "loads stylesheets from the stylesheets directory as specified by the manifest", -> - one = fs.resolveOnLoadPath("package-with-stylesheets-manifest/stylesheets/1.css") - two = fs.resolveOnLoadPath("package-with-stylesheets-manifest/stylesheets/2.less") - three = fs.resolveOnLoadPath("package-with-stylesheets-manifest/stylesheets/3.css") + one = fsUtils.resolveOnLoadPath("package-with-stylesheets-manifest/stylesheets/1.css") + two = fsUtils.resolveOnLoadPath("package-with-stylesheets-manifest/stylesheets/2.less") + three = fsUtils.resolveOnLoadPath("package-with-stylesheets-manifest/stylesheets/3.css") expect(stylesheetElementForId(one)).not.toExist() expect(stylesheetElementForId(two)).not.toExist() expect(stylesheetElementForId(three)).not.toExist() @@ -145,9 +145,9 @@ describe "the `atom` global", -> describe "when the metadata does not contain a 'stylesheets' manifest", -> it "loads all stylesheets from the stylesheets directory", -> - one = fs.resolveOnLoadPath("package-with-stylesheets/stylesheets/1.css") - two = fs.resolveOnLoadPath("package-with-stylesheets/stylesheets/2.less") - three = fs.resolveOnLoadPath("package-with-stylesheets/stylesheets/3.css") + one = fsUtils.resolveOnLoadPath("package-with-stylesheets/stylesheets/1.css") + two = fsUtils.resolveOnLoadPath("package-with-stylesheets/stylesheets/2.less") + three = fsUtils.resolveOnLoadPath("package-with-stylesheets/stylesheets/3.css") expect(stylesheetElementForId(one)).not.toExist() expect(stylesheetElementForId(two)).not.toExist() expect(stylesheetElementForId(three)).not.toExist() @@ -215,9 +215,9 @@ describe "the `atom` global", -> it "removes the package's stylesheets", -> atom.activatePackage('package-with-stylesheets') atom.deactivatePackage('package-with-stylesheets') - one = fs.resolveOnLoadPath("package-with-stylesheets/stylesheets/1.css") - two = fs.resolveOnLoadPath("package-with-stylesheets/stylesheets/2.less") - three = fs.resolveOnLoadPath("package-with-stylesheets/stylesheets/3.css") + one = fsUtils.resolveOnLoadPath("package-with-stylesheets/stylesheets/1.css") + two = fsUtils.resolveOnLoadPath("package-with-stylesheets/stylesheets/2.less") + three = fsUtils.resolveOnLoadPath("package-with-stylesheets/stylesheets/3.css") expect(stylesheetElementForId(one)).not.toExist() expect(stylesheetElementForId(two)).not.toExist() expect(stylesheetElementForId(three)).not.toExist() diff --git a/spec/app/config-spec.coffee b/spec/app/config-spec.coffee index 2daa360cd..797923af1 100644 --- a/spec/app/config-spec.coffee +++ b/spec/app/config-spec.coffee @@ -1,4 +1,4 @@ -fs = require 'fs-utils' +fsUtils = require 'fs-utils' describe "Config", -> describe ".get(keyPath) and .set(keyPath, value)", -> @@ -19,38 +19,38 @@ describe "Config", -> describe ".save()", -> beforeEach -> - spyOn(fs, 'write') + spyOn(fsUtils, 'write') jasmine.unspy config, 'save' describe "when ~/.atom/config.json exists", -> it "writes any non-default properties to ~/.atom/config.json", -> - config.configFilePath = fs.join(config.configDirPath, "config.json") + config.configFilePath = fsUtils.join(config.configDirPath, "config.json") config.set("a.b.c", 1) config.set("a.b.d", 2) config.set("x.y.z", 3) config.setDefaults("a.b", e: 4, f: 5) - fs.write.reset() + fsUtils.write.reset() config.save() - expect(fs.write.argsForCall[0][0]).toBe(fs.join(config.configDirPath, "config.json")) - writtenConfig = JSON.parse(fs.write.argsForCall[0][1]) + expect(fsUtils.write.argsForCall[0][0]).toBe(fsUtils.join(config.configDirPath, "config.json")) + writtenConfig = JSON.parse(fsUtils.write.argsForCall[0][1]) expect(writtenConfig).toEqual config.settings describe "when ~/.atom/config.json doesn't exist", -> it "writes any non-default properties to ~/.atom/config.cson", -> - config.configFilePath = fs.join(config.configDirPath, "config.cson") + config.configFilePath = fsUtils.join(config.configDirPath, "config.cson") config.set("a.b.c", 1) config.set("a.b.d", 2) config.set("x.y.z", 3) config.setDefaults("a.b", e: 4, f: 5) - fs.write.reset() + fsUtils.write.reset() config.save() - expect(fs.write.argsForCall[0][0]).toBe(fs.join(config.configDirPath, "config.cson")) + expect(fsUtils.write.argsForCall[0][0]).toBe(fsUtils.join(config.configDirPath, "config.cson")) CoffeeScript = require 'coffee-script' - writtenConfig = CoffeeScript.eval(fs.write.argsForCall[0][1], bare: true) + writtenConfig = CoffeeScript.eval(fsUtils.write.argsForCall[0][1], bare: true) expect(writtenConfig).toEqual config.settings describe ".setDefaults(keyPath, defaults)", -> @@ -113,40 +113,40 @@ describe "Config", -> describe "initializeConfigDirectory()", -> beforeEach -> config.configDirPath = '/tmp/dot-atom-dir' - expect(fs.exists(config.configDirPath)).toBeFalsy() + expect(fsUtils.exists(config.configDirPath)).toBeFalsy() afterEach -> - fs.remove('/tmp/dot-atom-dir') if fs.exists('/tmp/dot-atom-dir') + fsUtils.remove('/tmp/dot-atom-dir') if fsUtils.exists('/tmp/dot-atom-dir') describe "when the configDirPath doesn't exist", -> it "copies the contents of dot-atom to ~/.atom", -> config.initializeConfigDirectory() - expect(fs.exists(config.configDirPath)).toBeTruthy() - expect(fs.exists(fs.join(config.configDirPath, 'packages'))).toBeTruthy() - expect(fs.exists(fs.join(config.configDirPath, 'snippets'))).toBeTruthy() - expect(fs.exists(fs.join(config.configDirPath, 'themes'))).toBeTruthy() - expect(fs.isFile(fs.join(config.configDirPath, 'config.cson'))).toBeTruthy() + expect(fsUtils.exists(config.configDirPath)).toBeTruthy() + expect(fsUtils.exists(fsUtils.join(config.configDirPath, 'packages'))).toBeTruthy() + expect(fsUtils.exists(fsUtils.join(config.configDirPath, 'snippets'))).toBeTruthy() + expect(fsUtils.exists(fsUtils.join(config.configDirPath, 'themes'))).toBeTruthy() + expect(fsUtils.isFile(fsUtils.join(config.configDirPath, 'config.cson'))).toBeTruthy() it "copies the bundles themes to ~/.atom", -> config.initializeConfigDirectory() - expect(fs.isFile(fs.join(config.configDirPath, 'themes/atom-dark-ui/package.cson'))).toBeTruthy() - expect(fs.isFile(fs.join(config.configDirPath, 'themes/atom-light-ui/package.cson'))).toBeTruthy() - expect(fs.isFile(fs.join(config.configDirPath, 'themes/atom-dark-syntax.css'))).toBeTruthy() - expect(fs.isFile(fs.join(config.configDirPath, 'themes/atom-light-syntax.css'))).toBeTruthy() + expect(fsUtils.isFile(fsUtils.join(config.configDirPath, 'themes/atom-dark-ui/package.cson'))).toBeTruthy() + expect(fsUtils.isFile(fsUtils.join(config.configDirPath, 'themes/atom-light-ui/package.cson'))).toBeTruthy() + expect(fsUtils.isFile(fsUtils.join(config.configDirPath, 'themes/atom-dark-syntax.css'))).toBeTruthy() + expect(fsUtils.isFile(fsUtils.join(config.configDirPath, 'themes/atom-light-syntax.css'))).toBeTruthy() describe "when the config file is not parseable", -> beforeEach -> config.configDirPath = '/tmp/dot-atom-dir' - config.configFilePath = fs.join(config.configDirPath, "config.cson") - expect(fs.exists(config.configDirPath)).toBeFalsy() + config.configFilePath = fsUtils.join(config.configDirPath, "config.cson") + expect(fsUtils.exists(config.configDirPath)).toBeFalsy() afterEach -> - fs.remove('/tmp/dot-atom-dir') if fs.exists('/tmp/dot-atom-dir') + fsUtils.remove('/tmp/dot-atom-dir') if fsUtils.exists('/tmp/dot-atom-dir') it "logs an error to the console and does not overwrite the config file", -> config.save.reset() spyOn(console, 'error') - fs.write(config.configFilePath, "{{{{{") + fsUtils.write(config.configFilePath, "{{{{{") config.loadUserConfig() config.set("hair", "blonde") # trigger a save expect(console.error).toHaveBeenCalled() diff --git a/spec/app/directory-spec.coffee b/spec/app/directory-spec.coffee index 4bbd8ea02..a56eee671 100644 --- a/spec/app/directory-spec.coffee +++ b/spec/app/directory-spec.coffee @@ -1,11 +1,11 @@ Directory = require 'directory' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' describe "Directory", -> directory = null beforeEach -> - directory = new Directory(fs.resolveOnLoadPath('fixtures')) + directory = new Directory(fsUtils.resolveOnLoadPath('fixtures')) afterEach -> directory.off() @@ -14,11 +14,11 @@ describe "Directory", -> temporaryFilePath = null beforeEach -> - temporaryFilePath = fs.join(fs.resolveOnLoadPath('fixtures'), 'temporary') - fs.remove(temporaryFilePath) if fs.exists(temporaryFilePath) + temporaryFilePath = fsUtils.join(fsUtils.resolveOnLoadPath('fixtures'), 'temporary') + fsUtils.remove(temporaryFilePath) if fsUtils.exists(temporaryFilePath) afterEach -> - fs.remove(temporaryFilePath) if fs.exists(temporaryFilePath) + fsUtils.remove(temporaryFilePath) if fsUtils.exists(temporaryFilePath) it "triggers 'contents-changed' event handlers", -> changeHandler = null @@ -26,13 +26,13 @@ describe "Directory", -> runs -> changeHandler = jasmine.createSpy('changeHandler') directory.on 'contents-changed', changeHandler - fs.write(temporaryFilePath, '') + fsUtils.write(temporaryFilePath, '') waitsFor "first change", -> changeHandler.callCount > 0 runs -> changeHandler.reset() - fs.remove(temporaryFilePath) + fsUtils.remove(temporaryFilePath) waitsFor "second change", -> changeHandler.callCount > 0 @@ -40,11 +40,11 @@ describe "Directory", -> temporaryFilePath = null beforeEach -> - temporaryFilePath = fs.join(directory.path, 'temporary') - fs.remove(temporaryFilePath) if fs.exists(temporaryFilePath) + temporaryFilePath = fsUtils.join(directory.path, 'temporary') + fsUtils.remove(temporaryFilePath) if fsUtils.exists(temporaryFilePath) afterEach -> - fs.remove(temporaryFilePath) if fs.exists(temporaryFilePath) + fsUtils.remove(temporaryFilePath) if fsUtils.exists(temporaryFilePath) it "no longer triggers events", -> changeHandler = null @@ -52,7 +52,7 @@ describe "Directory", -> runs -> changeHandler = jasmine.createSpy('changeHandler') directory.on 'contents-changed', changeHandler - fs.write(temporaryFilePath, '') + fsUtils.write(temporaryFilePath, '') waitsFor "change event", -> changeHandler.callCount > 0 @@ -61,7 +61,7 @@ describe "Directory", -> directory.off() waits 20 - runs -> fs.remove(temporaryFilePath) + runs -> fsUtils.remove(temporaryFilePath) waits 20 runs -> expect(changeHandler.callCount).toBe 0 diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index bbfa1ba99..4424d9d73 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -6,7 +6,7 @@ Project = require 'project' $ = require 'jquery' {$$} = require 'space-pen' _ = require 'underscore' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' describe "Editor", -> [buffer, editor, editSession, cachedLineHeight, cachedCharWidth] = [] @@ -83,7 +83,7 @@ describe "Editor", -> describe "when the activeEditSession's file is modified on disk", -> it "triggers an alert", -> path = "/tmp/atom-changed-file.txt" - fs.write(path, "") + fsUtils.write(path, "") editSession = project.buildEditSession(path) editor.edit(editSession) editor.insertText("now the buffer is modified") @@ -93,7 +93,7 @@ describe "Editor", -> spyOn(atom, "confirm") - fs.write(path, "a file change") + fsUtils.write(path, "a file change") waitsFor "file to trigger contents-changed event", -> fileChangeHandler.callCount > 0 @@ -147,7 +147,7 @@ describe "Editor", -> it "triggers alert if edit session's buffer goes into conflict with changes on disk", -> path = "/tmp/atom-changed-file.txt" - fs.write(path, "") + fsUtils.write(path, "") tempEditSession = project.buildEditSession(path) editor.edit(tempEditSession) tempEditSession.insertText("a buffer change") @@ -156,7 +156,7 @@ describe "Editor", -> contentsConflictedHandler = jasmine.createSpy("contentsConflictedHandler") tempEditSession.on 'contents-conflicted', contentsConflictedHandler - fs.write(path, "a file change") + fsUtils.write(path, "a file change") waitsFor -> contentsConflictedHandler.callCount > 0 @@ -228,10 +228,10 @@ describe "Editor", -> path = null beforeEach -> path = "/tmp/something.txt" - fs.write(path, path) + fsUtils.write(path, path) afterEach -> - fs.remove(path) if fs.exists(path) + fsUtils.remove(path) if fsUtils.exists(path) it "emits event when buffer's path is changed", -> eventHandler = jasmine.createSpy('eventHandler') @@ -390,7 +390,7 @@ describe "Editor", -> editor.clearFontFamily() it "positions the cursor to the clicked row and column", -> - {top, left} = editor.pixelOffsetForScreenPosition([3, 30]) + {top, left} = editor.pixelOffsUtilsetForScreenPosition([3, 30]) editor.renderedLines.trigger mousedownEvent(pageX: left, pageY: top) expect(editor.getCursorScreenPosition()).toEqual [3, 30] @@ -786,7 +786,7 @@ describe "Editor", -> setEditorHeightInLines(editor, 4) describe "if autoscroll is true", -> - it "centers the viewport on the selection if its vertical center is currently offscreen", -> + it "centers the viewport on the selection if its vertical center is currently offsUtilscreen", -> editor.setSelectedBufferRange([[2, 0], [4, 0]], autoscroll: true) expect(editor.scrollTop()).toBe 0 @@ -1197,7 +1197,7 @@ describe "Editor", -> expect(editor.renderedLines.find('.line:last').text()).toBe buffer.lineForRow(7) describe "when scrolling more than the editors height", -> - it "removes lines that are offscreen and not in range of the overdraw and builds lines that become visible", -> + it "removes lines that are offsUtilscreen and not in range of the overdraw and builds lines that become visible", -> editor.scrollTop(editor.scrollView.prop('scrollHeight') - editor.scrollView.height()) expect(editor.renderedLines.find('.line').length).toBe 8 expect(editor.renderedLines.find('.line:first').text()).toBe buffer.lineForRow(5) @@ -2003,11 +2003,11 @@ describe "Editor", -> beforeEach -> path = project.resolve('git/working-dir/file.txt') - originalPathText = fs.read(path) + originalPathText = fsUtils.read(path) editor.edit(project.buildEditSession(path)) afterEach -> - fs.write(path, originalPathText) + fsUtils.write(path, originalPathText) it "restores the contents of the editor to the HEAD revision", -> editor.setText('') @@ -2103,11 +2103,11 @@ describe "Editor", -> [path] = [] beforeEach -> - path = fs.join(fs.absolute("/tmp"), "grammar-change.txt") - fs.write(path, "var i;") + path = fsUtils.join(fsUtils.absolute("/tmp"), "grammar-change.txt") + fsUtils.write(path, "var i;") afterEach -> - fs.remove(path) if fs.exists(path) + fsUtils.remove(path) if fsUtils.exists(path) it "updates all the rendered lines when the grammar changes", -> editor.edit(project.buildEditSession(path)) @@ -2451,12 +2451,12 @@ describe "Editor", -> it "saves the state of the rendered lines, the display buffer, and the buffer to a file of the user's choosing", -> saveDialogCallback = null spyOn(atom, 'showSaveDialog').andCallFake (callback) -> saveDialogCallback = callback - spyOn(fs, 'write') + spyOn(fsUtils, 'write') editor.trigger 'editor:save-debug-snapshot' expect(atom.showSaveDialog).toHaveBeenCalled() saveDialogCallback('/tmp/state') - expect(fs.write).toHaveBeenCalled() - expect(fs.write.argsForCall[0][0]).toBe '/tmp/state' - expect(typeof fs.write.argsForCall[0][1]).toBe 'string' + expect(fsUtils.write).toHaveBeenCalled() + expect(fsUtils.write.argsForCall[0][0]).toBe '/tmp/state' + expect(typeof fsUtils.write.argsForCall[0][1]).toBe 'string' diff --git a/spec/app/file-spec.coffee b/spec/app/file-spec.coffee index 21f57833f..9cbd613c0 100644 --- a/spec/app/file-spec.coffee +++ b/spec/app/file-spec.coffee @@ -1,33 +1,33 @@ File = require 'file' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' describe 'File', -> [path, file] = [] beforeEach -> - path = fs.join(fs.resolveOnLoadPath('fixtures'), "atom-file-test.txt") # Don't put in /tmp because /tmp symlinks to /private/tmp and screws up the rename test - fs.remove(path) if fs.exists(path) - fs.write(path, "this is old!") + path = fsUtils.join(fsUtils.resolveOnLoadPath('fixtures'), "atom-file-test.txt") # Don't put in /tmp because /tmp symlinks to /private/tmp and screws up the rename test + fsUtils.remove(path) if fsUtils.exists(path) + fsUtils.write(path, "this is old!") file = new File(path) file.read() afterEach -> file.off() - fs.remove(path) if fs.exists(path) + fsUtils.remove(path) if fsUtils.exists(path) describe "when the contents of the file change", -> it "triggers 'contents-changed' event handlers", -> changeHandler = null changeHandler = jasmine.createSpy('changeHandler') file.on 'contents-changed', changeHandler - fs.write(file.getPath(), "this is new!") + fsUtils.write(file.getPath(), "this is new!") waitsFor "change event", -> changeHandler.callCount > 0 runs -> changeHandler.reset() - fs.write(file.getPath(), "this is newer!") + fsUtils.write(file.getPath(), "this is newer!") waitsFor "second change event", -> changeHandler.callCount > 0 @@ -37,7 +37,7 @@ describe 'File', -> removeHandler = null removeHandler = jasmine.createSpy('removeHandler') file.on 'removed', removeHandler - fs.remove(file.getPath()) + fsUtils.remove(file.getPath()) waitsFor "remove event", -> removeHandler.callCount > 0 @@ -46,11 +46,11 @@ describe 'File', -> newPath = null beforeEach -> - newPath = fs.join(fs.directory(path), "atom-file-was-moved-test.txt") + newPath = fsUtils.join(fsUtils.directory(path), "atom-file-was-moved-test.txt") afterEach -> - if fs.exists(newPath) - fs.remove(newPath) + if fsUtils.exists(newPath) + fsUtils.remove(newPath) waitsFor "remove event", (done) -> file.on 'removed', done it "it updates its path", -> @@ -58,7 +58,7 @@ describe 'File', -> moveHandler = jasmine.createSpy('moveHandler') file.on 'moved', moveHandler - fs.move(path, newPath) + fsUtils.move(path, newPath) waitsFor "move event", -> moveHandler.callCount > 0 @@ -74,14 +74,14 @@ describe 'File', -> changeHandler = jasmine.createSpy('changeHandler') file.on 'contents-changed', changeHandler - fs.move(path, newPath) + fsUtils.move(path, newPath) waitsFor "move event", -> moveHandler.callCount > 0 runs -> expect(changeHandler).not.toHaveBeenCalled() - fs.write(file.getPath(), "this is new!") + fsUtils.write(file.getPath(), "this is new!") waitsFor "change event", -> changeHandler.callCount > 0 @@ -98,12 +98,12 @@ describe 'File', -> expect(changeHandler).not.toHaveBeenCalled() - fs.remove(path) + fsUtils.remove(path) expect(changeHandler).not.toHaveBeenCalled() waits 20 runs -> - fs.write(path, "HE HAS RISEN!") + fsUtils.write(path, "HE HAS RISEN!") expect(changeHandler).not.toHaveBeenCalled() waitsFor "resurrection change event", -> @@ -111,7 +111,7 @@ describe 'File', -> runs -> expect(removeHandler).not.toHaveBeenCalled() - fs.write(path, "Hallelujah!") + fsUtils.write(path, "Hallelujah!") changeHandler.reset() waitsFor "post-resurrection change event", -> diff --git a/spec/app/git-spec.coffee b/spec/app/git-spec.coffee index 921d88cc5..ade848136 100644 --- a/spec/app/git-spec.coffee +++ b/spec/app/git-spec.coffee @@ -1,12 +1,12 @@ Git = require 'git' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' Task = require 'task' describe "Git", -> repo = null beforeEach -> - fs.remove('/tmp/.git') if fs.isDirectory('/tmp/.git') + fsUtils.remove('/tmp/.git') if fsUtils.isDirectory('/tmp/.git') afterEach -> repo.destroy() if repo?.repo? @@ -21,55 +21,55 @@ describe "Git", -> describe ".getPath()", -> it "returns the repository path for a .git directory path", -> - repo = new Git(fs.resolveOnLoadPath('fixtures/git/master.git/HEAD')) - expect(repo.getPath()).toBe fs.resolveOnLoadPath('fixtures/git/master.git') + repo = new Git(fsUtils.resolveOnLoadPath('fixtures/git/master.git/HEAD')) + expect(repo.getPath()).toBe fsUtils.resolveOnLoadPath('fixtures/git/master.git') it "returns the repository path for a repository path", -> - repo = new Git(fs.resolveOnLoadPath('fixtures/git/master.git')) - expect(repo.getPath()).toBe fs.resolveOnLoadPath('fixtures/git/master.git') + repo = new Git(fsUtils.resolveOnLoadPath('fixtures/git/master.git')) + expect(repo.getPath()).toBe fsUtils.resolveOnLoadPath('fixtures/git/master.git') describe ".getHead()", -> it "returns a branch name for a non-empty repository", -> - repo = new Git(fs.resolveOnLoadPath('fixtures/git/master.git')) + repo = new Git(fsUtils.resolveOnLoadPath('fixtures/git/master.git')) expect(repo.getHead()).toBe 'refs/heads/master' describe ".getShortHead()", -> it "returns a branch name for a non-empty repository", -> - repo = new Git(fs.resolveOnLoadPath('fixtures/git/master.git')) + repo = new Git(fsUtils.resolveOnLoadPath('fixtures/git/master.git')) expect(repo.getShortHead()).toBe 'master' describe ".isPathIgnored(path)", -> it "returns true for an ignored path", -> - repo = new Git(fs.resolveOnLoadPath('fixtures/git/ignore.git')) + repo = new Git(fsUtils.resolveOnLoadPath('fixtures/git/ignore.git')) expect(repo.isPathIgnored('a.txt')).toBeTruthy() it "returns false for a non-ignored path", -> - repo = new Git(fs.resolveOnLoadPath('fixtures/git/ignore.git')) + repo = new Git(fsUtils.resolveOnLoadPath('fixtures/git/ignore.git')) expect(repo.isPathIgnored('b.txt')).toBeFalsy() describe ".isPathModified(path)", -> [repo, path, newPath, originalPathText] = [] beforeEach -> - repo = new Git(fs.resolveOnLoadPath('fixtures/git/working-dir')) - path = fs.resolveOnLoadPath('fixtures/git/working-dir/file.txt') - newPath = fs.join(fs.resolveOnLoadPath('fixtures/git/working-dir'), 'new-path.txt') - originalPathText = fs.read(path) + repo = new Git(fsUtils.resolveOnLoadPath('fixtures/git/working-dir')) + path = fsUtils.resolveOnLoadPath('fixtures/git/working-dir/file.txt') + newPath = fsUtils.join(fsUtils.resolveOnLoadPath('fixtures/git/working-dir'), 'new-path.txt') + originalPathText = fsUtils.read(path) afterEach -> - fs.write(path, originalPathText) - fs.remove(newPath) if fs.exists(newPath) + fsUtils.write(path, originalPathText) + fsUtils.remove(newPath) if fsUtils.exists(newPath) describe "when the path is unstaged", -> it "returns false if the path has not been modified", -> expect(repo.isPathModified(path)).toBeFalsy() it "returns true if the path is modified", -> - fs.write(path, "change") + fsUtils.write(path, "change") expect(repo.isPathModified(path)).toBeTruthy() it "returns true if the path is deleted", -> - fs.remove(path) + fsUtils.remove(path) expect(repo.isPathModified(path)).toBeTruthy() it "returns false if the path is new", -> @@ -79,13 +79,13 @@ describe "Git", -> [path, newPath] = [] beforeEach -> - repo = new Git(fs.resolveOnLoadPath('fixtures/git/working-dir')) - path = fs.resolveOnLoadPath('fixtures/git/working-dir/file.txt') - newPath = fs.join(fs.resolveOnLoadPath('fixtures/git/working-dir'), 'new-path.txt') - fs.write(newPath, "i'm new here") + repo = new Git(fsUtils.resolveOnLoadPath('fixtures/git/working-dir')) + path = fsUtils.resolveOnLoadPath('fixtures/git/working-dir/file.txt') + newPath = fsUtils.join(fsUtils.resolveOnLoadPath('fixtures/git/working-dir'), 'new-path.txt') + fsUtils.write(newPath, "i'm new here") afterEach -> - fs.remove(newPath) if fs.exists(newPath) + fsUtils.remove(newPath) if fsUtils.exists(newPath) describe "when the path is unstaged", -> it "returns true if the path is new", -> @@ -98,37 +98,37 @@ describe "Git", -> [path1, path2, originalPath1Text, originalPath2Text] = [] beforeEach -> - repo = new Git(fs.resolveOnLoadPath('fixtures/git/working-dir')) - path1 = fs.resolveOnLoadPath('fixtures/git/working-dir/file.txt') - originalPath1Text = fs.read(path1) - path2 = fs.resolveOnLoadPath('fixtures/git/working-dir/other.txt') - originalPath2Text = fs.read(path2) + repo = new Git(fsUtils.resolveOnLoadPath('fixtures/git/working-dir')) + path1 = fsUtils.resolveOnLoadPath('fixtures/git/working-dir/file.txt') + originalPath1Text = fsUtils.read(path1) + path2 = fsUtils.resolveOnLoadPath('fixtures/git/working-dir/other.txt') + originalPath2Text = fsUtils.read(path2) afterEach -> - fs.write(path1, originalPath1Text) - fs.write(path2, originalPath2Text) + fsUtils.write(path1, originalPath1Text) + fsUtils.write(path2, originalPath2Text) it "no longer reports a path as modified after checkout", -> expect(repo.isPathModified(path1)).toBeFalsy() - fs.write(path1, '') + fsUtils.write(path1, '') expect(repo.isPathModified(path1)).toBeTruthy() expect(repo.checkoutHead(path1)).toBeTruthy() expect(repo.isPathModified(path1)).toBeFalsy() it "restores the contents of the path to the original text", -> - fs.write(path1, '') + fsUtils.write(path1, '') expect(repo.checkoutHead(path1)).toBeTruthy() - expect(fs.read(path1)).toBe(originalPath1Text) + expect(fsUtils.read(path1)).toBe(originalPath1Text) it "only restores the path specified", -> - fs.write(path2, 'path 2 is edited') + fsUtils.write(path2, 'path 2 is edited') expect(repo.isPathModified(path2)).toBeTruthy() expect(repo.checkoutHead(path1)).toBeTruthy() - expect(fs.read(path2)).toBe('path 2 is edited') + expect(fsUtils.read(path2)).toBe('path 2 is edited') expect(repo.isPathModified(path2)).toBeTruthy() it "fires a status-changed event if the checkout completes successfully", -> - fs.write(path1, '') + fsUtils.write(path1, '') repo.getPathStatus(path1) statusHandler = jasmine.createSpy('statusHandler') repo.on 'status-changed', statusHandler @@ -141,7 +141,7 @@ describe "Git", -> describe ".destroy()", -> it "throws an exception when any method is called after it is called", -> - repo = new Git(fs.resolveOnLoadPath('fixtures/git/master.git/HEAD')) + repo = new Git(fsUtils.resolveOnLoadPath('fixtures/git/master.git/HEAD')) repo.destroy() expect(-> repo.getHead()).toThrow() @@ -149,38 +149,38 @@ describe "Git", -> [path, originalPathText] = [] beforeEach -> - repo = new Git(fs.resolveOnLoadPath('fixtures/git/working-dir')) - path = fs.resolveOnLoadPath('fixtures/git/working-dir/file.txt') - originalPathText = fs.read(path) + repo = new Git(fsUtils.resolveOnLoadPath('fixtures/git/working-dir')) + path = fsUtils.resolveOnLoadPath('fixtures/git/working-dir/file.txt') + originalPathText = fsUtils.read(path) afterEach -> - fs.write(path, originalPathText) + fsUtils.write(path, originalPathText) it "returns the number of lines added and deleted", -> expect(repo.getDiffStats(path)).toEqual {added: 0, deleted: 0} - fs.write(path, "#{originalPathText} edited line") + fsUtils.write(path, "#{originalPathText} edited line") expect(repo.getDiffStats(path)).toEqual {added: 1, deleted: 1} describe ".getPathStatus(path)", -> [path, originalPathText] = [] beforeEach -> - repo = new Git(fs.resolveOnLoadPath('fixtures/git/working-dir')) - path = fs.resolveOnLoadPath('fixtures/git/working-dir/file.txt') - originalPathText = fs.read(path) + repo = new Git(fsUtils.resolveOnLoadPath('fixtures/git/working-dir')) + path = fsUtils.resolveOnLoadPath('fixtures/git/working-dir/file.txt') + originalPathText = fsUtils.read(path) afterEach -> - fs.write(path, originalPathText) + fsUtils.write(path, originalPathText) it "trigger a status-changed event when the new status differs from the last cached one", -> statusHandler = jasmine.createSpy("statusHandler") repo.on 'status-changed', statusHandler - fs.write(path, '') + fsUtils.write(path, '') status = repo.getPathStatus(path) expect(statusHandler.callCount).toBe 1 expect(statusHandler.argsForCall[0][0..1]).toEqual [path, status] - fs.write(path, 'abc') + fsUtils.write(path, 'abc') status = repo.getPathStatus(path) expect(statusHandler.callCount).toBe 1 @@ -188,19 +188,19 @@ describe "Git", -> [newPath, modifiedPath, cleanPath, originalModifiedPathText] = [] beforeEach -> - repo = new Git(fs.resolveOnLoadPath('fixtures/git/working-dir')) + repo = new Git(fsUtils.resolveOnLoadPath('fixtures/git/working-dir')) modifiedPath = project.resolve('git/working-dir/file.txt') - originalModifiedPathText = fs.read(modifiedPath) + originalModifiedPathText = fsUtils.read(modifiedPath) newPath = project.resolve('git/working-dir/untracked.txt') cleanPath = project.resolve('git/working-dir/other.txt') - fs.write(newPath, '') + fsUtils.write(newPath, '') afterEach -> - fs.write(modifiedPath, originalModifiedPathText) - fs.remove(newPath) if fs.exists(newPath) + fsUtils.write(modifiedPath, originalModifiedPathText) + fsUtils.remove(newPath) if fsUtils.exists(newPath) it "returns status information for all new and modified files", -> - fs.write(modifiedPath, 'making this path modified') + fsUtils.write(modifiedPath, 'making this path modified') statusHandler = jasmine.createSpy('statusHandler') repo.on 'statuses-changed', statusHandler repo.refreshStatus() @@ -215,7 +215,7 @@ describe "Git", -> expect(repo.isStatusModified(statuses[modifiedPath])).toBeTruthy() it "only starts a single web worker at a time and schedules a restart if one is already running", => - fs.write(modifiedPath, 'making this path modified') + fsUtils.write(modifiedPath, 'making this path modified') statusHandler = jasmine.createSpy('statusHandler') repo.on 'statuses-changed', statusHandler diff --git a/spec/app/project-spec.coffee b/spec/app/project-spec.coffee index 48f483fc5..14e0af834 100644 --- a/spec/app/project-spec.coffee +++ b/spec/app/project-spec.coffee @@ -1,5 +1,5 @@ Project = require 'project' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' _ = require 'underscore' BufferedProcess = require 'buffered-process' @@ -29,12 +29,12 @@ describe "Project", -> editSession = project.buildEditSession() editSession.saveAs('/tmp/atom-test-save-sets-project-path') expect(project.getPath()).toBe '/tmp' - fs.remove('/tmp/atom-test-save-sets-project-path') + fsUtils.remove('/tmp/atom-test-save-sets-project-path') describe ".buildEditSession(path)", -> [absolutePath, newBufferHandler, newEditSessionHandler] = [] beforeEach -> - absolutePath = fs.resolveOnLoadPath('fixtures/dir/a') + absolutePath = fsUtils.resolveOnLoadPath('fixtures/dir/a') newBufferHandler = jasmine.createSpy('newBufferHandler') project.on 'buffer-created', newBufferHandler newEditSessionHandler = jasmine.createSpy('newEditSessionHandler') @@ -86,30 +86,30 @@ describe "Project", -> describe ".resolve(path)", -> it "returns an absolute path based on the project's root", -> - absolutePath = fs.resolveOnLoadPath('fixtures/dir/a') + absolutePath = fsUtils.resolveOnLoadPath('fixtures/dir/a') expect(project.resolve('a')).toBe absolutePath expect(project.resolve(absolutePath + '/../a')).toBe absolutePath expect(project.resolve('a/../a')).toBe absolutePath describe ".relativize(path)", -> it "returns an relative path based on the project's root", -> - absolutePath = fs.resolveOnLoadPath('fixtures/dir') - expect(project.relativize(fs.join(absolutePath, "b"))).toBe "b" - expect(project.relativize(fs.join(absolutePath, "b/file.coffee"))).toBe "b/file.coffee" - expect(project.relativize(fs.join(absolutePath, "file.coffee"))).toBe "file.coffee" + absolutePath = fsUtils.resolveOnLoadPath('fixtures/dir') + expect(project.relativize(fsUtils.join(absolutePath, "b"))).toBe "b" + expect(project.relativize(fsUtils.join(absolutePath, "b/file.coffee"))).toBe "b/file.coffee" + expect(project.relativize(fsUtils.join(absolutePath, "file.coffee"))).toBe "file.coffee" describe ".setPath(path)", -> describe "when path is a file", -> it "sets its path to the files parent directory and updates the root directory", -> - project.setPath(fs.resolveOnLoadPath('fixtures/dir/a')) - expect(project.getPath()).toEqual fs.resolveOnLoadPath('fixtures/dir') - expect(project.getRootDirectory().path).toEqual fs.resolveOnLoadPath('fixtures/dir') + project.setPath(fsUtils.resolveOnLoadPath('fixtures/dir/a')) + expect(project.getPath()).toEqual fsUtils.resolveOnLoadPath('fixtures/dir') + expect(project.getRootDirectory().path).toEqual fsUtils.resolveOnLoadPath('fixtures/dir') describe "when path is a directory", -> it "sets its path to the directory and updates the root directory", -> - project.setPath(fs.resolveOnLoadPath('fixtures/dir/a-dir')) - expect(project.getPath()).toEqual fs.resolveOnLoadPath('fixtures/dir/a-dir') - expect(project.getRootDirectory().path).toEqual fs.resolveOnLoadPath('fixtures/dir/a-dir') + project.setPath(fsUtils.resolveOnLoadPath('fixtures/dir/a-dir')) + expect(project.getPath()).toEqual fsUtils.resolveOnLoadPath('fixtures/dir/a-dir') + expect(project.getRootDirectory().path).toEqual fsUtils.resolveOnLoadPath('fixtures/dir/a-dir') describe "when path is null", -> it "sets its path and root directory to null", -> @@ -127,7 +127,7 @@ describe "Project", -> expect(paths.length).toBeGreaterThan 0 it "ignores files that return true from atom.ignorePath(path)", -> - spyOn(project, 'isPathIgnored').andCallFake (path) -> fs.base(path).match /a$/ + spyOn(project, 'isPathIgnored').andCallFake (path) -> fsUtils.base(path).match /a$/ paths = null waitsForPromise -> @@ -140,7 +140,7 @@ describe "Project", -> describe "when config.core.hideGitIgnoredFiles is true", -> it "ignores files that are present in .gitignore if the project is a git repo", -> config.set "core.hideGitIgnoredFiles", true - project.setPath(fs.resolveOnLoadPath('fixtures/git/working-dir')) + project.setPath(fsUtils.resolveOnLoadPath('fixtures/git/working-dir')) paths = null waitsForPromise -> project.getFilePaths().done (foundPaths) -> paths = foundPaths @@ -152,11 +152,11 @@ describe "Project", -> ignoredFile = null beforeEach -> - ignoredFile = fs.join(fs.resolveOnLoadPath('fixtures/dir'), 'ignored.txt') - fs.write(ignoredFile, "") + ignoredFile = fsUtils.join(fsUtils.resolveOnLoadPath('fixtures/dir'), 'ignored.txt') + fsUtils.write(ignoredFile, "") afterEach -> - fs.remove(ignoredFile) + fsUtils.remove(ignoredFile) it "ignores ignored.txt file", -> paths = null @@ -172,11 +172,11 @@ describe "Project", -> ignoredFile = null beforeEach -> - ignoredFile = fs.join(fs.resolveOnLoadPath('fixtures/dir'), 'ignored/ignored.txt') - fs.write(ignoredFile, "") + ignoredFile = fsUtils.join(fsUtils.resolveOnLoadPath('fixtures/dir'), 'ignored/ignored.txt') + fsUtils.write(ignoredFile, "") afterEach -> - fs.remove(ignoredFile) + fsUtils.remove(ignoredFile) it "ignores ignored folder", -> paths = null @@ -222,7 +222,7 @@ describe "Project", -> range: [[2, 6], [2, 11]] it "works on evil filenames", -> - project.setPath(fs.resolveOnLoadPath('fixtures/evil-files')) + project.setPath(fsUtils.resolveOnLoadPath('fixtures/evil-files')) paths = [] matches = [] waitsForPromise -> @@ -237,7 +237,7 @@ describe "Project", -> expect(paths[1]).toMatch /file with spaces.txt$/ expect(paths[2]).toMatch /goddam\nnewlines$/m expect(paths[3]).toMatch /quote".txt$/m - expect(fs.base(paths[4])).toBe "utfa\u0306.md" + expect(fsUtils.base(paths[4])).toBe "utfa\u0306.md" it "handles breaks in the search subprocess's output following the filename", -> spyOn(BufferedProcess.prototype, 'bufferStream') @@ -246,7 +246,7 @@ describe "Project", -> project.scan /a+/, iterator stdout = BufferedProcess.prototype.bufferStream.argsForCall[0][1] - stdout ":#{fs.resolveOnLoadPath('fixtures/dir/a')}\n" + stdout ":#{fsUtils.resolveOnLoadPath('fixtures/dir/a')}\n" stdout "1;0 3:aaa bbb\n2;3 2:cc aa cc\n" expect(iterator.argsForCall[0][0]).toEqual diff --git a/spec/app/root-view-spec.coffee b/spec/app/root-view-spec.coffee index 4935dbd1b..8c28d4297 100644 --- a/spec/app/root-view-spec.coffee +++ b/spec/app/root-view-spec.coffee @@ -1,5 +1,5 @@ $ = require 'jquery' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' Project = require 'project' RootView = require 'root-view' Buffer = require 'text-buffer' @@ -83,7 +83,7 @@ describe "RootView", -> expect(editor3.isFocused).toBeFalsy() expect(editor4.isFocused).toBeFalsy() - expect(rootView.title).toBe "#{fs.base(editor2.getPath())} - #{project.getPath()}" + expect(rootView.title).toBe "#{fsUtils.base(editor2.getPath())} - #{project.getPath()}" describe "where there are no open editors", -> it "constructs the view with no open editors", -> @@ -223,7 +223,7 @@ describe "RootView", -> it "creates an edit session for the given path as an item on a new pane, and focuses the pane", -> editSession = rootView.open('b') expect(rootView.getActivePane().activeItem).toBe editSession - expect(editSession.getPath()).toBe fs.resolveOnLoadPath('fixtures/dir/b') + expect(editSession.getPath()).toBe fsUtils.resolveOnLoadPath('fixtures/dir/b') expect(rootView.getActivePane().focus).toHaveBeenCalled() describe "when the changeFocus option is false", -> diff --git a/spec/app/syntax-spec.coffee b/spec/app/syntax-spec.coffee index ad12bee71..6a07cd289 100644 --- a/spec/app/syntax-spec.coffee +++ b/spec/app/syntax-spec.coffee @@ -1,4 +1,4 @@ -fs = require 'fs-utils' +fsUtils = require 'fs-utils' describe "the `syntax` global", -> beforeEach -> @@ -44,10 +44,10 @@ describe "the `syntax` global", -> it "doesn't read the file when the file contents are specified", -> filePath = require.resolve("fixtures/shebang") - filePathContents = fs.read(filePath) - spyOn(fs, 'read').andCallThrough() + filePathContents = fsUtils.read(filePath) + spyOn(fsUtils, 'read').andCallThrough() expect(syntax.selectGrammar(filePath, filePathContents).name).toBe "Ruby" - expect(fs.read).not.toHaveBeenCalled() + expect(fsUtils.read).not.toHaveBeenCalled() it "allows the default grammar to be overridden for a path", -> path = '/foo/bar/file.js' diff --git a/spec/app/text-buffer-spec.coffee b/spec/app/text-buffer-spec.coffee index dbf75502d..8181e2ab6 100644 --- a/spec/app/text-buffer-spec.coffee +++ b/spec/app/text-buffer-spec.coffee @@ -1,6 +1,6 @@ Project = require 'project' Buffer = require 'text-buffer' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' _ = require 'underscore' describe 'Buffer', -> @@ -8,7 +8,7 @@ describe 'Buffer', -> beforeEach -> filePath = require.resolve('fixtures/sample.js') - fileContents = fs.read(filePath) + fileContents = fsUtils.read(filePath) buffer = project.bufferForPath(filePath) afterEach -> @@ -24,7 +24,7 @@ describe 'Buffer', -> it "loads the contents of that file", -> filePath = require.resolve 'fixtures/sample.txt' buffer = project.bufferForPath(filePath) - expect(buffer.getText()).toBe fs.read(filePath) + expect(buffer.getText()).toBe fsUtils.read(filePath) it "is not modified and has no undo history", -> buffer = project.bufferForPath(filePath) @@ -34,7 +34,7 @@ describe 'Buffer', -> describe "when no file exists for the path", -> it "throws an exception", -> filePath = "does-not-exist.txt" - expect(fs.exists(filePath)).toBeFalsy() + expect(fsUtils.exists(filePath)).toBeFalsy() expect(-> project.bufferForPath(filePath)).toThrow() describe "when no path is given", -> @@ -46,25 +46,25 @@ describe 'Buffer', -> [path, newPath, bufferToChange, eventHandler] = [] beforeEach -> - path = fs.join(fs.resolveOnLoadPath("fixtures"), "atom-manipulate-me") + path = fsUtils.join(fsUtils.resolveOnLoadPath("fixtures"), "atom-manipulate-me") newPath = "#{path}-i-moved" - fs.write(path, "") + fsUtils.write(path, "") bufferToChange = project.bufferForPath(path) eventHandler = jasmine.createSpy('eventHandler') bufferToChange.on 'path-changed', eventHandler afterEach -> bufferToChange.destroy() - fs.remove(path) if fs.exists(path) - fs.remove(newPath) if fs.exists(newPath) + fsUtils.remove(path) if fsUtils.exists(path) + fsUtils.remove(newPath) if fsUtils.exists(newPath) it "triggers a `path-changed` event when path is changed", -> bufferToChange.saveAs(newPath) expect(eventHandler).toHaveBeenCalledWith(bufferToChange) it "triggers a `path-changed` event when the file is moved", -> - fs.remove(newPath) if fs.exists(newPath) - fs.move(path, newPath) + fsUtils.remove(newPath) if fsUtils.exists(newPath) + fsUtils.move(path, newPath) waitsFor "buffer path change", -> eventHandler.callCount > 0 @@ -76,14 +76,14 @@ describe 'Buffer', -> path = null beforeEach -> path = "/tmp/tmp.txt" - fs.write(path, "first") + fsUtils.write(path, "first") buffer.release() buffer = project.bufferForPath(path).retain() afterEach -> buffer.release() buffer = null - fs.remove(path) if fs.exists(path) + fsUtils.remove(path) if fsUtils.exists(path) it "does not trigger a change event when Atom modifies the file", -> buffer.insert([0,0], "HELLO!") @@ -99,7 +99,7 @@ describe 'Buffer', -> it "changes the memory contents of the buffer to match the new disk contents and triggers a 'changed' event", -> changeHandler = jasmine.createSpy('changeHandler') buffer.on 'changed', changeHandler - fs.write(path, "second") + fsUtils.write(path, "second") expect(changeHandler.callCount).toBe 0 waitsFor "file to trigger change event", -> @@ -119,7 +119,7 @@ describe 'Buffer', -> buffer.file.on 'contents-changed', fileChangeHandler buffer.insert([0, 0], "a change") - fs.write(path, "second") + fsUtils.write(path, "second") expect(fileChangeHandler.callCount).toBe 0 waitsFor "file to trigger 'contents-changed' event", -> @@ -134,7 +134,7 @@ describe 'Buffer', -> buffer.insert([0, 0], "a second change") handler = jasmine.createSpy('fileChange') - fs.write(path, "second") + fsUtils.write(path, "second") buffer.on 'contents-conflicted', handler expect(handler.callCount).toBe 0 @@ -149,7 +149,7 @@ describe 'Buffer', -> beforeEach -> path = "/tmp/atom-file-to-delete.txt" - fs.write(path, 'delete me') + fsUtils.write(path, 'delete me') bufferToDelete = project.bufferForPath(path) path = bufferToDelete.getPath() # symlinks may have been converted @@ -158,7 +158,7 @@ describe 'Buffer', -> removeHandler = jasmine.createSpy('removeHandler') bufferToDelete.file.on 'removed', removeHandler - fs.remove(path) + fsUtils.remove(path) waitsFor "file to be removed", -> removeHandler.callCount > 0 @@ -174,7 +174,7 @@ describe 'Buffer', -> expect(bufferToDelete.fileExists()).toBeTruthy() expect(bufferToDelete.isInConflict()).toBeFalsy() - fs.write(path, 'moo') + fsUtils.write(path, 'moo') changeHandler = jasmine.createSpy('changeHandler') bufferToDelete.on 'changed', changeHandler @@ -207,19 +207,19 @@ describe 'Buffer', -> it "reports the modified status changing to true after the underlying file is deleted", -> buffer.release() filePath = "/tmp/atom-tmp-file" - fs.write(filePath, 'delete me') + fsUtils.write(filePath, 'delete me') buffer = project.bufferForPath(filePath) modifiedHandler = jasmine.createSpy("modifiedHandler") buffer.on 'modified-status-changed', modifiedHandler - fs.remove(filePath) + fsUtils.remove(filePath) waitsFor "modified status to change", -> modifiedHandler.callCount runs -> expect(buffer.isModified()).toBe true it "reports the modified status changing to false after a modified buffer is saved", -> filePath = "/tmp/atom-tmp-file" - fs.write(filePath, '') + fsUtils.write(filePath, '') buffer.release() buffer = project.bufferForPath(filePath) modifiedHandler = jasmine.createSpy("modifiedHandler") @@ -243,7 +243,7 @@ describe 'Buffer', -> it "reports the modified status changing to false after a modified buffer is reloaded", -> filePath = "/tmp/atom-tmp-file" - fs.write(filePath, '') + fsUtils.write(filePath, '') buffer.release() buffer = project.bufferForPath(filePath) modifiedHandler = jasmine.createSpy("modifiedHandler") @@ -420,16 +420,16 @@ describe 'Buffer', -> beforeEach -> filePath = '/tmp/temp.txt' - fs.write(filePath, "") + fsUtils.write(filePath, "") saveBuffer = project.bufferForPath(filePath) saveBuffer.setText("blah") it "saves the contents of the buffer to the path", -> saveBuffer.setText 'Buffer contents!' saveBuffer.save() - expect(fs.read(filePath)).toEqual 'Buffer contents!' + expect(fsUtils.read(filePath)).toEqual 'Buffer contents!' - it "fires will-be-saved and saved events around the call to fs.write", -> + it "fires will-be-saved and saved events around the call to fsUtils.write", -> events = [] beforeSave1 = -> events.push('beforeSave1') beforeSave2 = -> events.push('beforeSave2') @@ -438,12 +438,12 @@ describe 'Buffer', -> saveBuffer.on 'will-be-saved', beforeSave1 saveBuffer.on 'will-be-saved', beforeSave2 - spyOn(fs, 'write').andCallFake -> events.push 'fs.write' + spyOn(fsUtils, 'write').andCallFake -> events.push 'fsUtils.write' saveBuffer.on 'saved', afterSave1 saveBuffer.on 'saved', afterSave2 saveBuffer.save() - expect(events).toEqual ['beforeSave1', 'beforeSave2', 'fs.write', 'afterSave1', 'afterSave2'] + expect(events).toEqual ['beforeSave1', 'beforeSave2', 'fsUtils.write', 'afterSave1', 'afterSave2'] it "fires will-reload and reloaded events when reloaded", -> events = [] @@ -477,7 +477,7 @@ describe 'Buffer', -> it "saves the contents of the buffer to the path", -> filePath = '/tmp/temp.txt' - fs.remove filePath if fs.exists(filePath) + fsUtils.remove filePath if fsUtils.exists(filePath) saveAsBuffer = project.bufferForPath(null).retain() eventHandler = jasmine.createSpy('eventHandler') @@ -485,14 +485,14 @@ describe 'Buffer', -> saveAsBuffer.setText 'Buffer contents!' saveAsBuffer.saveAs(filePath) - expect(fs.read(filePath)).toEqual 'Buffer contents!' + expect(fsUtils.read(filePath)).toEqual 'Buffer contents!' expect(eventHandler).toHaveBeenCalledWith(saveAsBuffer) it "stops listening to events on previous path and begins listening to events on new path", -> originalPath = "/tmp/original.txt" newPath = "/tmp/new.txt" - fs.write(originalPath, "") + fsUtils.write(originalPath, "") saveAsBuffer = project.bufferForPath(originalPath).retain() changeHandler = jasmine.createSpy('changeHandler') @@ -500,11 +500,11 @@ describe 'Buffer', -> saveAsBuffer.saveAs(newPath) expect(changeHandler).not.toHaveBeenCalled() - fs.write(originalPath, "should not trigger buffer event") + fsUtils.write(originalPath, "should not trigger buffer event") waits 20 runs -> expect(changeHandler).not.toHaveBeenCalled() - fs.write(newPath, "should trigger buffer event") + fsUtils.write(newPath, "should trigger buffer event") waitsFor -> changeHandler.callCount > 0 diff --git a/spec/app/text-mate-grammar-spec.coffee b/spec/app/text-mate-grammar-spec.coffee index c23bfd6b8..27c88fef2 100644 --- a/spec/app/text-mate-grammar-spec.coffee +++ b/spec/app/text-mate-grammar-spec.coffee @@ -1,7 +1,7 @@ TextMateGrammar = require 'text-mate-grammar' TextMatePackage = require 'text-mate-package' plist = require 'plist' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' _ = require 'underscore' describe "TextMateGrammar", -> @@ -16,13 +16,13 @@ describe "TextMateGrammar", -> describe "@loadSync(path)", -> it "loads grammars from plists", -> - grammar = TextMateGrammar.loadSync(fs.resolveOnLoadPath('packages/text.tmbundle/Syntaxes/Plain text.plist')) + grammar = TextMateGrammar.loadSync(fsUtils.resolveOnLoadPath('packages/text.tmbundle/Syntaxes/Plain text.plist')) expect(grammar.scopeName).toBe "text.plain" {tokens} = grammar.tokenizeLine("this text is so plain. i love it.") expect(tokens[0]).toEqual value: "this text is so plain. i love it.", scopes: ["text.plain", "meta.paragraph.text"] it "loads grammars from cson files", -> - grammar = TextMateGrammar.loadSync(fs.resolveOnLoadPath('package-with-grammars/grammars/alot.cson')) + grammar = TextMateGrammar.loadSync(fsUtils.resolveOnLoadPath('package-with-grammars/grammars/alot.cson')) expect(grammar.scopeName).toBe "source.alot" {tokens} = grammar.tokenizeLine("this is alot of code") expect(tokens[1]).toEqual value: "alot", scopes: ["source.alot", "keyword.alot"] diff --git a/spec/app/text-mate-theme-spec.coffee b/spec/app/text-mate-theme-spec.coffee index 16588606c..0292c714b 100644 --- a/spec/app/text-mate-theme-spec.coffee +++ b/spec/app/text-mate-theme-spec.coffee @@ -1,4 +1,4 @@ -fs = require 'fs-utils' +fsUtils = require 'fs-utils' plist = require 'plist' TextMateTheme = require 'text-mate-theme' Theme = require 'theme' @@ -7,7 +7,7 @@ describe "TextMateTheme", -> [theme, themePath] = [] beforeEach -> - themePath = fs.resolveOnLoadPath(fs.join('fixtures', 'test.tmTheme')) + themePath = fsUtils.resolveOnLoadPath(fsUtils.join('fixtures', 'test.tmTheme')) theme = Theme.load(themePath) afterEach -> diff --git a/spec/app/theme-spec.coffee b/spec/app/theme-spec.coffee index 86d703b9d..b447a8cf1 100644 --- a/spec/app/theme-spec.coffee +++ b/spec/app/theme-spec.coffee @@ -1,5 +1,5 @@ $ = require 'jquery' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' Theme = require 'theme' describe "@load(name)", -> @@ -15,7 +15,7 @@ describe "@load(name)", -> it "applies the theme's stylesheet to the current window", -> expect($(".editor").css("background-color")).not.toBe("rgb(20, 20, 20)") - themePath = fs.resolveOnLoadPath(fs.join('fixtures', 'test.tmTheme')) + themePath = fsUtils.resolveOnLoadPath(fsUtils.join('fixtures', 'test.tmTheme')) theme = Theme.load(themePath) expect($(".editor").css("background-color")).toBe("rgb(20, 20, 20)") diff --git a/spec/app/window-spec.coffee b/spec/app/window-spec.coffee index 52c095904..c27a7a31e 100644 --- a/spec/app/window-spec.coffee +++ b/spec/app/window-spec.coffee @@ -1,5 +1,5 @@ $ = require 'jquery' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' {less} = require 'less' describe "Window", -> @@ -70,7 +70,7 @@ describe "Window", -> element = $('head style[id*="css.css"]') expect(element.attr('id')).toBe cssPath - expect(element.text()).toBe fs.read(cssPath) + expect(element.text()).toBe fsUtils.read(cssPath) # doesn't append twice requireStylesheet(cssPath) @@ -112,7 +112,7 @@ describe "Window", -> describe ".removeStylesheet(path)", -> it "removes styling applied by given stylesheet path", -> - cssPath = require.resolve(fs.join("fixtures", "css.css")) + cssPath = require.resolve(fsUtils.join("fixtures", "css.css")) expect($(document.body).css('font-weight')).not.toBe("bold") requireStylesheet(cssPath) @@ -159,14 +159,14 @@ describe "Window", -> commandPath = '/tmp/installed-atom-command/atom' afterEach -> - fs.remove(commandPath) if fs.exists(commandPath) + fsUtils.remove(commandPath) if fsUtils.exists(commandPath) describe "when the command path doesn't exist", -> it "copies atom.sh to the specified path", -> - expect(fs.exists(commandPath)).toBeFalsy() + expect(fsUtils.exists(commandPath)).toBeFalsy() window.installAtomCommand(commandPath) - expect(fs.exists(commandPath)).toBeTruthy() - expect(fs.read(commandPath).length).toBeGreaterThan 1 + expect(fsUtils.exists(commandPath)).toBeTruthy() + expect(fsUtils.read(commandPath).length).toBeGreaterThan 1 describe ".deserialize(state)", -> class Foo diff --git a/spec/jasmine-helper.coffee b/spec/jasmine-helper.coffee index fb6175ae0..86a8bb625 100644 --- a/spec/jasmine-helper.coffee +++ b/spec/jasmine-helper.coffee @@ -1,8 +1,8 @@ window.nakedLoad = (file) -> - fs = require 'fs-utils' + fsUtils = require 'fs-utils' file = require.resolve(file) - code = fs.read(file) - if fs.extension(file) is '.coffee' + code = fsUtils.read(file) + if fsUtils.extension(file) is '.coffee' require('coffee-script').eval(code, filename: file) else window.eval("#{code}\n//@ sourceURL=#{file}") diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index 0475902a7..d4dcac2cb 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -12,11 +12,11 @@ Directory = require 'directory' File = require 'file' Editor = require 'editor' TokenizedBuffer = require 'tokenized-buffer' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' RootView = require 'root-view' Git = require 'git' requireStylesheet "jasmine" -fixturePackagesPath = fs.resolveOnLoadPath('fixtures/packages') +fixturePackagesPath = fsUtils.resolveOnLoadPath('fixtures/packages') config.packageDirPaths.unshift(fixturePackagesPath) keymap.loadBundledKeymaps() [bindingSetsToRestore, bindingSetsByFirstKeystrokeToRestore] = [] @@ -30,7 +30,7 @@ jasmine.getEnv().defaultTimeoutInterval = 5000 beforeEach -> jQuery.fx.off = true - window.project = new Project(fs.resolveOnLoadPath('fixtures')) + window.project = new Project(fsUtils.resolveOnLoadPath('fixtures')) window.git = Git.open(project.getPath()) window.project.on 'path-changed', -> window.git?.destroy() @@ -126,7 +126,7 @@ addCustomMatchers = (spec) -> toExistOnDisk: (expected) -> notText = this.isNot and " not" or "" @message = -> return "Expected path '" + @actual + "'" + notText + " to exist." - fs.exists(@actual) + fsUtils.exists(@actual) window.keyIdentifierForKey = (key) -> if key.length > 1 # named key @@ -241,5 +241,5 @@ $.fn.textInput = (data) -> event = jQuery.event.fix(event) $(this).trigger(event) -unless fs.md5ForPath(require.resolve('fixtures/sample.js')) == "dd38087d0d7e3e4802a6d3f9b9745f2b" +unless fsUtils.md5ForPath(require.resolve('fixtures/sample.js')) == "dd38087d0d7e3e4802a6d3f9b9745f2b" throw new Error("Sample.js is modified") diff --git a/spec/spec-suite.coffee b/spec/spec-suite.coffee index 0c7ebc37e..7fc7cdadd 100644 --- a/spec/spec-suite.coffee +++ b/spec/spec-suite.coffee @@ -1,15 +1,15 @@ require 'window' measure 'spec suite require time', -> - fs = require 'fs-utils' + fsUtils = require 'fs-utils' require 'spec-helper' # Run core specs - for path in fs.listTree(fs.resolveOnLoadPath("spec")) when /-spec\.coffee$/.test path + for path in fsUtils.listTree(fsUtils.resolveOnLoadPath("spec")) when /-spec\.coffee$/.test path require path # Run extension specs for packageDirPath in config.packageDirPaths - for packagePath in fs.list(packageDirPath) - for path in fs.listTree(fs.join(packagePath, "spec")) when /-spec\.coffee$/.test path + for packagePath in fsUtils.list(packageDirPath) + for path in fsUtils.listTree(fsUtils.join(packagePath, "spec")) when /-spec\.coffee$/.test path require path diff --git a/spec/stdlib/fs-utils-spec.coffee b/spec/stdlib/fs-utils-spec.coffee index 5317be474..8d2204a8b 100644 --- a/spec/stdlib/fs-utils-spec.coffee +++ b/spec/stdlib/fs-utils-spec.coffee @@ -1,95 +1,95 @@ -fs = require 'fs-utils' +fsUtils = require 'fs-utils' -describe "fs", -> +describe "fsUtils", -> describe ".read(path)", -> it "return contents of file", -> - expect(fs.read(require.resolve("fixtures/sample.txt"))).toBe "Some text.\n" + expect(fsUtils.read(require.resolve("fixtures/sample.txt"))).toBe "Some text.\n" it "does not through an exception when the path is a binary file", -> - expect(-> fs.read(require.resolve("fixtures/binary-file.png"))).not.toThrow() + expect(-> fsUtils.read(require.resolve("fixtures/binary-file.png"))).not.toThrow() describe ".isFile(path)", -> - fixturesDir = fs.resolveOnLoadPath('fixtures') + fixturesDir = fsUtils.resolveOnLoadPath('fixtures') it "returns true with a file path", -> - expect(fs.isFile(fs.join(fixturesDir, 'sample.js'))).toBe true + expect(fsUtils.isFile(fsUtils.join(fixturesDir, 'sample.js'))).toBe true it "returns false with a directory path", -> - expect(fs.isFile(fixturesDir)).toBe false + expect(fsUtils.isFile(fixturesDir)).toBe false it "returns false with a non-existent path", -> - expect(fs.isFile(fs.join(fixturesDir, 'non-existent'))).toBe false - expect(fs.isFile(null)).toBe false + expect(fsUtils.isFile(fsUtils.join(fixturesDir, 'non-existent'))).toBe false + expect(fsUtils.isFile(null)).toBe false describe ".directory(path)", -> describe "when called with a file path", -> it "returns the path to the directory", -> - expect(fs.directory(fs.resolveOnLoadPath('fixtures/dir/a'))).toBe fs.resolveOnLoadPath('fixtures/dir') + expect(fsUtils.directory(fsUtils.resolveOnLoadPath('fixtures/dir/a'))).toBe fsUtils.resolveOnLoadPath('fixtures/dir') describe "when called with a directory path", -> it "return the path it was given", -> - expect(fs.directory("/a/b/c")).toBe "/a/b" - expect(fs.directory("/a")).toBe "" - expect(fs.directory("a")).toBe "" - expect(fs.directory("/a/b/c++")).toBe "/a/b" + expect(fsUtils.directory("/a/b/c")).toBe "/a/b" + expect(fsUtils.directory("/a")).toBe "" + expect(fsUtils.directory("a")).toBe "" + expect(fsUtils.directory("/a/b/c++")).toBe "/a/b" describe ".base(path, ext)", -> describe "when called with an extension", -> it "return the base name without the extension when the path has the given extension", -> - expect(fs.base("/a/b/c.txt", '.txt')).toBe "c" - expect(fs.base("/a/b/c.txt", '.txt2')).toBe "c.txt" - expect(fs.base("/a/b/c.+", '.+')).toBe "c" + expect(fsUtils.base("/a/b/c.txt", '.txt')).toBe "c" + expect(fsUtils.base("/a/b/c.txt", '.txt2')).toBe "c.txt" + expect(fsUtils.base("/a/b/c.+", '.+')).toBe "c" describe ".exists(path)", -> it "returns true when path exsits", -> - expect(fs.exists(fs.resolveOnLoadPath('fixtures'))).toBe true + expect(fsUtils.exists(fsUtils.resolveOnLoadPath('fixtures'))).toBe true it "returns false when path doesn't exsit", -> - expect(fs.exists(fs.resolveOnLoadPath("fixtures") + "/-nope-does-not-exist")).toBe false - expect(fs.exists("")).toBe false - expect(fs.exists(null)).toBe false + expect(fsUtils.exists(fsUtils.resolveOnLoadPath("fixtures") + "/-nope-does-not-exist")).toBe false + expect(fsUtils.exists("")).toBe false + expect(fsUtils.exists(null)).toBe false describe ".join(paths...)", -> it "concatenates the given paths with the directory separator", -> - expect(fs.join('a')).toBe 'a' - expect(fs.join('a', 'b', 'c')).toBe 'a/b/c' - expect(fs.join('/a/b/', 'c', 'd')).toBe '/a/b/c/d' - expect(fs.join('a', 'b/c/', 'd/')).toBe 'a/b/c/d/' + expect(fsUtils.join('a')).toBe 'a' + expect(fsUtils.join('a', 'b', 'c')).toBe 'a/b/c' + expect(fsUtils.join('/a/b/', 'c', 'd')).toBe '/a/b/c/d' + expect(fsUtils.join('a', 'b/c/', 'd/')).toBe 'a/b/c/d/' describe ".split(path)", -> it "returns path components", -> - expect(fs.split("/a/b/c.txt")).toEqual ["", "a", "b", "c.txt"] - expect(fs.split("a/b/c.txt")).toEqual ["a", "b", "c.txt"] + expect(fsUtils.split("/a/b/c.txt")).toEqual ["", "a", "b", "c.txt"] + expect(fsUtils.split("a/b/c.txt")).toEqual ["a", "b", "c.txt"] describe ".extension(path)", -> it "returns the extension of a file", -> - expect(fs.extension("a/b/corey.txt")).toBe '.txt' - expect(fs.extension("a/b/corey.txt.coffee")).toBe '.coffee' + expect(fsUtils.extension("a/b/corey.txt")).toBe '.txt' + expect(fsUtils.extension("a/b/corey.txt.coffee")).toBe '.coffee' it "returns an empty string for paths without an extension", -> - expect(fs.extension("a/b.not-extension/a-dir")).toBe '' + expect(fsUtils.extension("a/b.not-extension/a-dir")).toBe '' describe ".makeTree(path)", -> beforeEach -> - fs.remove("/tmp/a") if fs.exists("/tmp/a") + fsUtils.remove("/tmp/a") if fsUtils.exists("/tmp/a") it "creates all directories in path including any missing parent directories", -> - fs.makeTree("/tmp/a/b/c") - expect(fs.exists("/tmp/a/b/c")).toBeTruthy() + fsUtils.makeTree("/tmp/a/b/c") + expect(fsUtils.exists("/tmp/a/b/c")).toBeTruthy() describe ".traverseTreeSync(path, onFile, onDirectory)", -> fixturesDir = null beforeEach -> - fixturesDir = fs.resolveOnLoadPath('fixtures') + fixturesDir = fsUtils.resolveOnLoadPath('fixtures') it "calls fn for every path in the tree at the given path", -> paths = [] onPath = (path) -> paths.push(path) true - fs.traverseTreeSync fixturesDir, onPath, onPath - expect(paths).toEqual fs.listTree(fixturesDir) + fsUtils.traverseTreeSync fixturesDir, onPath, onPath + expect(paths).toEqual fsUtils.listTree(fixturesDir) it "does not recurse into a directory if it is pruned", -> paths = [] @@ -99,43 +99,43 @@ describe "fs", -> else paths.push(path) true - fs.traverseTreeSync fixturesDir, onPath, onPath + fsUtils.traverseTreeSync fixturesDir, onPath, onPath expect(paths.length).toBeGreaterThan 0 for path in paths expect(path).not.toMatch /\/dir\// it "returns entries if path is a symlink", -> - symlinkPath = fs.join(fixturesDir, 'symlink-to-dir') + symlinkPath = fsUtils.join(fixturesDir, 'symlink-to-dir') symlinkPaths = [] onSymlinkPath = (path) -> symlinkPaths.push(path.substring(symlinkPath.length + 1)) - regularPath = fs.join(fixturesDir, 'dir') + regularPath = fsUtils.join(fixturesDir, 'dir') paths = [] onPath = (path) -> paths.push(path.substring(regularPath.length + 1)) - fs.traverseTreeSync(symlinkPath, onSymlinkPath, onSymlinkPath) - fs.traverseTreeSync(regularPath, onPath, onPath) + fsUtils.traverseTreeSync(symlinkPath, onSymlinkPath, onSymlinkPath) + fsUtils.traverseTreeSync(regularPath, onPath, onPath) expect(symlinkPaths).toEqual(paths) describe ".md5ForPath(path)", -> it "returns the MD5 hash of the file at the given path", -> - expect(fs.md5ForPath(require.resolve('fixtures/sample.js'))).toBe 'dd38087d0d7e3e4802a6d3f9b9745f2b' + expect(fsUtils.md5ForPath(require.resolve('fixtures/sample.js'))).toBe 'dd38087d0d7e3e4802a6d3f9b9745f2b' describe ".list(path, extensions)", -> it "returns the absolute paths of entries within the given directory", -> - paths = fs.list(project.getPath()) + paths = fsUtils.list(project.getPath()) expect(paths).toContain project.resolve('css.css') expect(paths).toContain project.resolve('coffee.coffee') expect(paths).toContain project.resolve('two-hundred.txt') it "returns an empty array for paths that aren't directories or don't exist", -> - expect(fs.list(project.resolve('sample.js'))).toEqual [] - expect(fs.list('/non/existent/directory')).toEqual [] + expect(fsUtils.list(project.resolve('sample.js'))).toEqual [] + expect(fsUtils.list('/non/existent/directory')).toEqual [] it "can filter the paths by an optional array of file extensions", -> - paths = fs.list(project.getPath(), ['.css', 'coffee']) + paths = fsUtils.list(project.getPath(), ['.css', 'coffee']) expect(paths).toContain project.resolve('css.css') expect(paths).toContain project.resolve('coffee.coffee') expect(path).toMatch /(css|coffee)$/ for path in paths @@ -145,7 +145,7 @@ describe "fs", -> it "calls the callback with the absolute paths of entries within the given directory", -> waitsFor (done) -> - fs.listAsync project.getPath(), (err, result) -> + fsUtils.listAsync project.getPath(), (err, result) -> paths = result done() runs -> @@ -155,7 +155,7 @@ describe "fs", -> it "can filter the paths by an optional array of file extensions", -> waitsFor (done) -> - fs.listAsync project.getPath(), ['css', '.coffee'], (err, result) -> + fsUtils.listAsync project.getPath(), ['css', '.coffee'], (err, result) -> paths = result done() runs -> diff --git a/src/app/atom-theme.coffee b/src/app/atom-theme.coffee index 3fab3f9e7..c679298a1 100644 --- a/src/app/atom-theme.coffee +++ b/src/app/atom-theme.coffee @@ -1,4 +1,4 @@ -fs = require 'fs-utils' +fsUtils = require 'fs-utils' Theme = require 'theme' CSON = require 'cson' @@ -9,17 +9,17 @@ class AtomTheme extends Theme @stylesheets[stylesheetPath] = window.loadStylesheet(stylesheetPath) load: -> - if fs.extension(@path) in ['.css', '.less'] + if fsUtils.extension(@path) in ['.css', '.less'] @loadStylesheet(@path) else - metadataPath = fs.resolveExtension(fs.join(@path, 'package'), ['cson', 'json']) - if fs.isFile(metadataPath) + metadataPath = fsUtils.resolveExtension(fsUtils.join(@path, 'package'), ['cson', 'json']) + if fsUtils.isFile(metadataPath) stylesheetNames = CSON.readObject(metadataPath)?.stylesheets if stylesheetNames for name in stylesheetNames - filename = fs.resolveExtension(fs.join(@path, name), ['.css', '.less', '']) + filename = fsUtils.resolveExtension(fsUtils.join(@path, name), ['.css', '.less', '']) @loadStylesheet(filename) else - @loadStylesheet(stylesheetPath) for stylesheetPath in fs.list(@path, ['.css', '.less']) + @loadStylesheet(stylesheetPath) for stylesheetPath in fsUtils.list(@path, ['.css', '.less']) super diff --git a/src/app/atom.coffee b/src/app/atom.coffee index 05eeea60d..cb8180113 100644 --- a/src/app/atom.coffee +++ b/src/app/atom.coffee @@ -1,4 +1,4 @@ -fs = require 'fs-utils' +fsUtils = require 'fs-utils' _ = require 'underscore' Package = require 'package' TextMatePackage = require 'text-mate-package' @@ -74,9 +74,9 @@ _.extend atom, throw new Error("Could not resolve '#{id}' to a package path") resolvePackagePath: _.memoize (id) -> - return id if fs.isDirectory(id) - path = fs.resolve(config.packageDirPaths..., id) - path if fs.isDirectory(path) + return id if fsUtils.isDirectory(id) + path = fsUtils.resolve(config.packageDirPaths..., id) + path if fsUtils.isDirectory(path) getLoadedPackage: (id) -> if path = @resolvePackagePath(id) @@ -90,13 +90,13 @@ _.extend atom, isPackageDisabled: (id) -> if path = @resolvePackagePath(id) - _.include(config.get('core.disabledPackages') ? [], fs.base(path)) + _.include(config.get('core.disabledPackages') ? [], fsUtils.base(path)) getPackagePaths: -> packagePaths = [] for packageDirPath in config.packageDirPaths - for packagePath in fs.list(packageDirPath) - packagePaths.push(packagePath) if fs.isDirectory(packagePath) + for packagePath in fsUtils.list(packageDirPath) + packagePaths.push(packagePath) if fsUtils.isDirectory(packagePath) _.uniq(packagePaths) loadThemes: -> @@ -109,8 +109,8 @@ _.extend atom, @loadedThemes.push Theme.load(name) loadUserStylesheet: -> - userStylesheetPath = fs.resolve(fs.join(config.configDirPath, 'user'), ['css', 'less']) - if fs.isFile(userStylesheetPath) + userStylesheetPath = fsUtils.resolve(fsUtils.join(config.configDirPath, 'user'), ['css', 'less']) + if fsUtils.isFile(userStylesheetPath) userStyleesheetContents = loadStylesheet(userStylesheetPath) applyStylesheet(userStylesheetPath, userStyleesheetContents, 'userTheme') @@ -247,9 +247,9 @@ _.extend atom, @sendMessageToBrowserProcess('getUpdateStatus', [], callback) requireUserInitScript: -> - userInitScriptPath = fs.join(config.configDirPath, "user.coffee") + userInitScriptPath = fsUtils.join(config.configDirPath, "user.coffee") try - require userInitScriptPath if fs.isFile(userInitScriptPath) + require userInitScriptPath if fsUtils.isFile(userInitScriptPath) catch error console.error "Failed to load `#{userInitScriptPath}`", error.stack, error diff --git a/src/app/binding-set.coffee b/src/app/binding-set.coffee index 3bf87dab8..e9017d1f9 100644 --- a/src/app/binding-set.coffee +++ b/src/app/binding-set.coffee @@ -1,6 +1,6 @@ $ = require 'jquery' _ = require 'underscore' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' Specificity = require 'specificity' PEG = require 'pegjs' @@ -17,7 +17,7 @@ class BindingSet name: null constructor: (@selector, commandsByKeystrokes, @index, @name) -> - BindingSet.parser ?= PEG.buildParser(fs.read(require.resolve 'keystroke-pattern.pegjs')) + BindingSet.parser ?= PEG.buildParser(fsUtils.read(require.resolve 'keystroke-pattern.pegjs')) @specificity = Specificity(@selector) @commandsByKeystrokes = @normalizeCommandsByKeystrokes(commandsByKeystrokes) diff --git a/src/app/config.coffee b/src/app/config.coffee index c8d36db6e..79cb4d35e 100644 --- a/src/app/config.coffee +++ b/src/app/config.coffee @@ -1,15 +1,15 @@ -fs = require 'fs-utils' +fsUtils = require 'fs-utils' _ = require 'underscore' EventEmitter = require 'event-emitter' CSON = require 'cson' -configDirPath = fs.absolute("~/.atom") -bundledPackagesDirPath = fs.join(resourcePath, "src/packages") -bundledThemesDirPath = fs.join(resourcePath, "themes") -vendoredPackagesDirPath = fs.join(resourcePath, "vendor/packages") -vendoredThemesDirPath = fs.join(resourcePath, "vendor/themes") -userThemesDirPath = fs.join(configDirPath, "themes") -userPackagesDirPath = fs.join(configDirPath, "packages") +configDirPath = fsUtils.absolute("~/.atom") +bundledPackagesDirPath = fsUtils.join(resourcePath, "src/packages") +bundledThemesDirPath = fsUtils.join(resourcePath, "themes") +vendoredPackagesDirPath = fsUtils.join(resourcePath, "vendor/packages") +vendoredThemesDirPath = fsUtils.join(resourcePath, "vendor/themes") +userThemesDirPath = fsUtils.join(configDirPath, "themes") +userPackagesDirPath = fsUtils.join(configDirPath, "packages") module.exports = class Config @@ -26,34 +26,34 @@ class Config core: _.clone(require('root-view').configDefaults) editor: _.clone(require('editor').configDefaults) @settings = {} - @configFilePath = fs.resolve(configDirPath, 'config', ['json', 'cson']) - @configFilePath ?= fs.join(configDirPath, 'config.cson') + @configFilePath = fsUtils.resolve(configDirPath, 'config', ['json', 'cson']) + @configFilePath ?= fsUtils.join(configDirPath, 'config.cson') initializeConfigDirectory: -> - return if fs.exists(@configDirPath) + return if fsUtils.exists(@configDirPath) - fs.makeDirectory(@configDirPath) + fsUtils.makeDirectory(@configDirPath) - templateConfigDirPath = fs.resolve(window.resourcePath, 'dot-atom') + templateConfigDirPath = fsUtils.resolve(window.resourcePath, 'dot-atom') onConfigDirFile = (path) => relativePath = path.substring(templateConfigDirPath.length + 1) - configPath = fs.join(@configDirPath, relativePath) - fs.write(configPath, fs.read(path)) - fs.traverseTreeSync(templateConfigDirPath, onConfigDirFile, (path) -> true) + configPath = fsUtils.join(@configDirPath, relativePath) + fsUtils.write(configPath, fsUtils.read(path)) + fsUtils.traverseTreeSync(templateConfigDirPath, onConfigDirFile, (path) -> true) - configThemeDirPath = fs.join(@configDirPath, 'themes') + configThemeDirPath = fsUtils.join(@configDirPath, 'themes') onThemeDirFile = (path) -> relativePath = path.substring(bundledThemesDirPath.length + 1) - configPath = fs.join(configThemeDirPath, relativePath) - fs.write(configPath, fs.read(path)) - fs.traverseTreeSync(bundledThemesDirPath, onThemeDirFile, (path) -> true) + configPath = fsUtils.join(configThemeDirPath, relativePath) + fsUtils.write(configPath, fsUtils.read(path)) + fsUtils.traverseTreeSync(bundledThemesDirPath, onThemeDirFile, (path) -> true) load: -> @initializeConfigDirectory() @loadUserConfig() loadUserConfig: -> - if fs.exists(@configFilePath) + if fsUtils.exists(@configFilePath) try userConfig = CSON.readObject(@configFilePath) _.extend(@settings, userConfig) diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index b420f7875..3c3951533 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -8,7 +8,7 @@ EventEmitter = require 'event-emitter' Subscriber = require 'subscriber' Range = require 'range' _ = require 'underscore' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' module.exports = class EditSession @@ -45,7 +45,7 @@ class EditSession @buffer.retain() @subscribe @buffer, "path-changed", => - @project.setPath(fs.directory(@getPath())) unless @project.getPath()? + @project.setPath(fsUtils.directory(@getPath())) unless @project.getPath()? @trigger "title-changed" @trigger "path-changed" @subscribe @buffer, "contents-conflicted", => @trigger "contents-conflicted" @@ -64,14 +64,14 @@ class EditSession getTitle: -> if path = @getPath() - fs.base(path) + fsUtils.base(path) else 'untitled' getLongTitle: -> if path = @getPath() - fileName = fs.base(path) - directory = fs.base(fs.directory(path)) + fileName = fsUtils.base(path) + directory = fsUtils.base(fsUtils.directory(path)) "#{fileName} - #{directory}" else 'untitled' diff --git a/src/app/editor.coffee b/src/app/editor.coffee index a3bed0f00..c3c147d0e 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -6,7 +6,7 @@ Range = require 'range' EditSession = require 'edit-session' CursorView = require 'cursor-view' SelectionView = require 'selection-view' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' $ = require 'jquery' _ = require 'underscore' @@ -1098,7 +1098,7 @@ class Editor extends View range.detach() leftPixels - pixelOffsetForScreenPosition: (position) -> + pixelOffsUtilsetForScreenPosition: (position) -> {top, left} = @pixelPositionForScreenPosition(position) offset = @renderedLines.offset() {top: top + offset.top, left: left + offset.left} @@ -1181,7 +1181,7 @@ class Editor extends View saveDebugSnapshot: -> atom.showSaveDialog (path) => - fs.write(path, @getDebugSnapshot()) if path + fsUtils.write(path, @getDebugSnapshot()) if path getDebugSnapshot: -> [ diff --git a/src/app/git.coffee b/src/app/git.coffee index 21128dfbd..9bbe63117 100644 --- a/src/app/git.coffee +++ b/src/app/git.coffee @@ -1,5 +1,5 @@ _ = require 'underscore' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' Subscriber = require 'subscriber' EventEmitter = require 'event-emitter' RepositoryStatusTask = require 'repository-status-task' @@ -48,7 +48,7 @@ class Git refreshIndex: -> @getRepo().refreshIndex() getPath: -> - @path ?= fs.absolute(@getRepo().getPath()) + @path ?= fsUtils.absolute(@getRepo().getPath()) destroy: -> if @statusTask? diff --git a/src/app/keymap.coffee b/src/app/keymap.coffee index 23e585ee7..0cc615726 100644 --- a/src/app/keymap.coffee +++ b/src/app/keymap.coffee @@ -1,6 +1,6 @@ $ = require 'jquery' _ = require 'underscore' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' CSON = require 'cson' BindingSet = require 'binding-set' @@ -32,13 +32,13 @@ class Keymap $(document).command 'open-dev', => atom.openDev() loadBundledKeymaps: -> - @loadDirectory(fs.resolveOnLoadPath('keymaps')) + @loadDirectory(fsUtils.resolveOnLoadPath('keymaps')) loadUserKeymaps: -> - @loadDirectory(fs.join(config.configDirPath, 'keymaps')) + @loadDirectory(fsUtils.join(config.configDirPath, 'keymaps')) loadDirectory: (directoryPath) -> - @load(filePath) for filePath in fs.list(directoryPath, ['.cson', '.json']) + @load(filePath) for filePath in fsUtils.list(directoryPath, ['.cson', '.json']) load: (path) -> @add(path, CSON.readObject(path)) diff --git a/src/app/package.coffee b/src/app/package.coffee index c03e082d5..b3f3333e0 100644 --- a/src/app/package.coffee +++ b/src/app/package.coffee @@ -1,4 +1,4 @@ -fs = require 'fs-utils' +fsUtils = require 'fs-utils' module.exports = class Package @@ -20,4 +20,4 @@ class Package path: null constructor: (@path) -> - @name = fs.base(@path) + @name = fsUtils.base(@path) diff --git a/src/app/project.coffee b/src/app/project.coffee index 81bf19c76..68b36a4c6 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -1,4 +1,4 @@ -fs = require 'fs-utils' +fsUtils = require 'fs-utils' _ = require 'underscore' $ = require 'jquery' Range = require 'range' @@ -41,7 +41,7 @@ class Project @rootDirectory?.off() if path? - directory = if fs.isDirectory(path) then path else fs.directory(path) + directory = if fsUtils.isDirectory(path) then path else fsUtils.directory(path) @rootDirectory = new Directory(directory) else @rootDirectory = null @@ -56,7 +56,7 @@ class Project paths = [] onFile = (path) => paths.push(path) unless @isPathIgnored(path) onDirectory = -> true - fs.traverseTreeSync(@getPath(), onFile, onDirectory) + fsUtils.traverseTreeSync(@getPath(), onFile, onDirectory) deferred.resolve(paths) deferred.promise() @@ -68,11 +68,11 @@ class Project @ignoreRepositoryPath(path) ignoreRepositoryPath: (path) -> - config.get("core.hideGitIgnoredFiles") and git?.isPathIgnored(fs.join(@getPath(), path)) + config.get("core.hideGitIgnoredFiles") and git?.isPathIgnored(fsUtils.join(@getPath(), path)) resolve: (filePath) -> - filePath = fs.join(@getPath(), filePath) unless filePath[0] == '/' - fs.absolute filePath + filePath = fsUtils.join(@getPath(), filePath) unless filePath[0] == '/' + fsUtils.absolute filePath relativize: (fullPath) -> return fullPath unless fullPath.lastIndexOf(@getPath()) is 0 diff --git a/src/app/repository-status-handler.coffee b/src/app/repository-status-handler.coffee index 7816cf383..05f866a96 100644 --- a/src/app/repository-status-handler.coffee +++ b/src/app/repository-status-handler.coffee @@ -1,5 +1,5 @@ Git = require 'git-utils' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' module.exports = loadStatuses: (path) -> @@ -8,7 +8,7 @@ module.exports = workingDirectoryPath = repo.getWorkingDirectory() statuses = {} for path, status of repo.getStatus() - statuses[fs.join(workingDirectoryPath, path)] = status + statuses[fsUtils.join(workingDirectoryPath, path)] = status upstream = repo.getAheadBehindCount() repo.release() else diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee index fc1fdb4e1..0a7d3a2e9 100644 --- a/src/app/root-view.coffee +++ b/src/app/root-view.coffee @@ -1,6 +1,6 @@ $ = require 'jquery' {$$} = require 'space-pen' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' _ = require 'underscore' {View} = require 'space-pen' @@ -174,4 +174,3 @@ class RootView extends View eachBuffer: (callback) -> project.eachBuffer(callback) - diff --git a/src/app/syntax.coffee b/src/app/syntax.coffee index 7ab711a14..75e954baf 100644 --- a/src/app/syntax.coffee +++ b/src/app/syntax.coffee @@ -2,7 +2,7 @@ _ = require 'underscore' jQuery = require 'jquery' Specificity = require 'specificity' {$$} = require 'space-pen' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' EventEmitter = require 'event-emitter' NullGrammar = require 'null-grammar' nodePath = require 'path' @@ -71,7 +71,7 @@ class Syntax grammarByFirstLineRegex: (filePath, fileContents) -> try - fileContents ?= fs.read(filePath) + fileContents ?= fsUtils.read(filePath) catch e return diff --git a/src/app/text-buffer.coffee b/src/app/text-buffer.coffee index 803e009bf..d403c44fd 100644 --- a/src/app/text-buffer.coffee +++ b/src/app/text-buffer.coffee @@ -1,5 +1,5 @@ _ = require 'underscore' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' File = require 'file' Point = require 'point' Range = require 'range' @@ -37,7 +37,7 @@ class Buffer @lineEndings = [] if path - throw "Path '#{path}' does not exist" unless fs.exists(path) + throw "Path '#{path}' does not exist" unless fsUtils.exists(path) @setPath(path) if initialText? @setText(initialText) diff --git a/src/app/text-mate-grammar.coffee b/src/app/text-mate-grammar.coffee index 03b35b6ca..774eac4f7 100644 --- a/src/app/text-mate-grammar.coffee +++ b/src/app/text-mate-grammar.coffee @@ -1,5 +1,5 @@ _ = require 'underscore' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' plist = require 'plist' Token = require 'token' CSON = require 'cson' @@ -8,17 +8,17 @@ CSON = require 'cson' module.exports = class TextMateGrammar @readFromPath: (path) -> - fs.readPlist(path) + fsUtils.readPlist(path) @load: (path, done) -> - fs.readObjectAsync path, (err, object) -> + fsUtils.readObjectAsync path, (err, object) -> if err done(err) else done(null, new TextMateGrammar(object)) @loadSync: (path) -> - new TextMateGrammar(fs.readObject(path)) + new TextMateGrammar(fsUtils.readObject(path)) name: null fileTypes: null diff --git a/src/app/text-mate-theme.coffee b/src/app/text-mate-theme.coffee index 2424bc2d3..3054b0633 100644 --- a/src/app/text-mate-theme.coffee +++ b/src/app/text-mate-theme.coffee @@ -1,5 +1,5 @@ _ = require 'underscore' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' plist = require 'plist' Theme = require 'theme' diff --git a/src/app/theme.coffee b/src/app/theme.coffee index 6ca385d66..da038e391 100644 --- a/src/app/theme.coffee +++ b/src/app/theme.coffee @@ -1,4 +1,4 @@ -fs = require 'fs-utils' +fsUtils = require 'fs-utils' module.exports = class Theme @@ -8,10 +8,10 @@ class Theme TextMateTheme = require 'text-mate-theme' AtomTheme = require 'atom-theme' - if fs.exists(name) + if fsUtils.exists(name) path = name else - path = fs.resolve(config.themeDirPaths..., name, ['', '.tmTheme', '.css', 'less']) + path = fsUtils.resolve(config.themeDirPaths..., name, ['', '.tmTheme', '.css', 'less']) throw new Error("No theme exists named '#{name}'") unless path diff --git a/src/app/window.coffee b/src/app/window.coffee index 78fc1bd42..f4950ed08 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -1,4 +1,4 @@ -fs = require 'fs-utils' +fsUtils = require 'fs-utils' $ = require 'jquery' _ = require 'underscore' {less} = require 'less' @@ -32,14 +32,14 @@ window.setUpEnvironment = -> requireStylesheet 'notification' requireStylesheet 'markdown' - if nativeStylesheetPath = fs.resolveOnLoadPath(process.platform, ['css', 'less']) + if nativeStylesheetPath = fsUtils.resolveOnLoadPath(process.platform, ['css', 'less']) requireStylesheet(nativeStylesheetPath) # This method is only called when opening a real application window window.startup = -> - directory = _.find ['/opt/boxen', '/opt/github', '/usr/local'], (dir) -> fs.isDirectory(dir) + directory = _.find ['/opt/boxen', '/opt/github', '/usr/local'], (dir) -> fsUtils.isDirectory(dir) if directory - installAtomCommand(fs.join(directory, 'bin/atom')) + installAtomCommand(fsUtils.join(directory, 'bin/atom')) else console.warn "Failed to install `atom` binary" @@ -73,11 +73,11 @@ window.shutdown = -> window.git = null window.installAtomCommand = (commandPath) -> - return if fs.exists(commandPath) + return if fsUtils.exists(commandPath) - bundledCommandPath = fs.resolve(window.resourcePath, 'atom.sh') + bundledCommandPath = fsUtils.resolve(window.resourcePath, 'atom.sh') if bundledCommandPath? - fs.write(commandPath, fs.read(bundledCommandPath)) + fsUtils.write(commandPath, fsUtils.read(bundledCommandPath)) spawn('chmod', ['u+x', commandPath]) window.handleWindowEvents = -> @@ -99,7 +99,7 @@ window.deserializeWindowState = -> window.project = deserialize(windowState.project) ? new Project(pathToOpen) window.rootView = deserialize(windowState.rootView) ? new RootView - if !windowState.rootView and (!pathToOpen or fs.isFile(pathToOpen)) + if !windowState.rootView and (!pathToOpen or fsUtils.isFile(pathToOpen)) rootView.open(pathToOpen) $(rootViewParentSelector).append(rootView) @@ -113,10 +113,10 @@ window.stylesheetElementForId = (id) -> $("head style[id='#{id}']") window.resolveStylesheet = (path) -> - if fs.extension(path).length > 0 - fs.resolveOnLoadPath(path) + if fsUtils.extension(path).length > 0 + fsUtils.resolveOnLoadPath(path) else - fs.resolveOnLoadPath(path, ['css', 'less']) + fsUtils.resolveOnLoadPath(path, ['css', 'less']) window.requireStylesheet = (path) -> if fullPath = window.resolveStylesheet(path) @@ -126,8 +126,8 @@ window.requireStylesheet = (path) -> throw new Error("Could not find a file at path '#{path}'") window.loadStylesheet = (path) -> - content = fs.read(path) - if fs.extension(path) == '.less' + content = fsUtils.read(path) + if fsUtils.extension(path) == '.less' (new less.Parser).parse content, (e, tree) -> throw new Error(e.message, path, e.line) if e content = tree.toCSS() diff --git a/src/packages/command-panel/lib/command-interpreter.coffee b/src/packages/command-panel/lib/command-interpreter.coffee index 07f53672e..8022fcb81 100644 --- a/src/packages/command-panel/lib/command-interpreter.coffee +++ b/src/packages/command-panel/lib/command-interpreter.coffee @@ -1,4 +1,4 @@ -fs = require 'fs-utils' +fsUtils = require 'fs-utils' PEG = require 'pegjs' module.exports = @@ -6,7 +6,7 @@ class CommandInterpreter constructor: (@project) -> eval: (string, activeEditSession) -> - @parser ?= PEG.buildParser(fs.read(require.resolve 'command-panel/lib/commands.pegjs')) + @parser ?= PEG.buildParser(fsUtils.read(require.resolve 'command-panel/lib/commands.pegjs')) compositeCommand = @parser.parse(string) @lastRelativeAddress = compositeCommand if compositeCommand.isRelativeAddress() compositeCommand.execute(@project, activeEditSession) diff --git a/src/packages/command-panel/lib/path-view.coffee b/src/packages/command-panel/lib/path-view.coffee index 9561d019b..814128c7e 100644 --- a/src/packages/command-panel/lib/path-view.coffee +++ b/src/packages/command-panel/lib/path-view.coffee @@ -1,5 +1,5 @@ {View} = require 'space-pen' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' OperationView = require './operation-view' $ = require 'jquery' @@ -7,7 +7,7 @@ module.exports = class PathView extends View @content: ({path, previewList} = {}) -> classes = ['path'] - classes.push('readme') if fs.isReadmePath(path) + classes.push('readme') if fsUtils.isReadmePath(path) @li class: classes.join(' '), => @div outlet: 'pathDetails', class: 'path-details', => @span class: 'path-name', path diff --git a/src/packages/command-panel/lib/preview-list.coffee b/src/packages/command-panel/lib/preview-list.coffee index 7f70db89d..6be25229f 100644 --- a/src/packages/command-panel/lib/preview-list.coffee +++ b/src/packages/command-panel/lib/preview-list.coffee @@ -1,7 +1,7 @@ $ = require 'jquery' ScrollView = require 'scroll-view' _ = require 'underscore' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' PathView = require './path-view' OperationView = require './operation-view' diff --git a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee index 1c11229db..8bdfc5521 100644 --- a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee +++ b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee @@ -2,7 +2,7 @@ SelectList = require 'select-list' _ = require 'underscore' $ = require 'jquery' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' LoadPathsTask = require './load-paths-task' module.exports = @@ -45,22 +45,22 @@ class FuzzyFinderView extends SelectList else if git.isStatusModified(status) @div class: 'status modified' - ext = fs.extension(path) - if fs.isReadmePath(path) + ext = fsUtils.extension(path) + if fsUtils.isReadmePath(path) typeClass = 'readme-name' - else if fs.isCompressedExtension(ext) + else if fsUtils.isCompressedExtension(ext) typeClass = 'compressed-name' - else if fs.isImageExtension(ext) + else if fsUtils.isImageExtension(ext) typeClass = 'image-name' - else if fs.isPdfExtension(ext) + else if fsUtils.isPdfExtension(ext) typeClass = 'pdf-name' - else if fs.isBinaryExtension(ext) + else if fsUtils.isBinaryExtension(ext) typeClass = 'binary-name' else typeClass = 'text-name' - @span fs.base(path), class: "file label #{typeClass}" - if folder = project.relativize(fs.directory(path)) + @span fsUtils.base(path), class: "file label #{typeClass}" + if folder = project.relativize(fsUtils.directory(path)) @span " - #{folder}/", class: 'directory' openPath: (path) -> @@ -76,7 +76,7 @@ class FuzzyFinderView extends SelectList confirmed : (path) -> return unless path.length - if fs.isFile(path) + if fsUtils.isFile(path) @cancel() @openPath(path) else @@ -134,7 +134,7 @@ class FuzzyFinderView extends SelectList populateGitStatusPaths: -> paths = [] - paths.push(path) for path, status of git.statuses when fs.isFile(path) + paths.push(path) for path, status of git.statuses when fsUtils.isFile(path) @setArray(paths) diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index 12ef322f9..391b39255 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -1,5 +1,5 @@ _ = require 'underscore' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' module.exports = class LoadPathsTask @@ -18,7 +18,7 @@ class LoadPathsTask path = path.substring(rootPath.length + 1) for segment in path.split('/') return true if _.contains(ignoredNames, segment) - ignoreGitIgnoredFiles and git?.isPathIgnored(fs.join(rootPath, path)) + ignoreGitIgnoredFiles and git?.isPathIgnored(fsUtils.join(rootPath, path)) onFile = (path) -> return if @aborted paths.push(path) unless isIgnored(path) @@ -27,7 +27,7 @@ class LoadPathsTask onDone = => @callback(paths) unless @aborted - fs.traverseTree(rootPath, onFile, onDirectory, onDone) + fsUtils.traverseTree(rootPath, onFile, onDirectory, onDone) abort: -> @aborted = true diff --git a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee index 3c9a5c6e4..0ea88f702 100644 --- a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee +++ b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee @@ -4,7 +4,7 @@ LoadPathsTask = require 'fuzzy-finder/lib/load-paths-task' _ = require 'underscore' $ = require 'jquery' {$$} = require 'space-pen' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' describe 'FuzzyFinder', -> [finderView] = [] @@ -57,7 +57,7 @@ describe 'FuzzyFinder', -> runs -> expect(finderView.list.children('li').length).toBe paths.length for path in paths - expect(finderView.list.find("li:contains(#{fs.base(path)})")).toExist() + expect(finderView.list.find("li:contains(#{fsUtils.base(path)})")).toExist() expect(finderView.list.children().first()).toHaveClass 'selected' expect(finderView.find(".loading")).not.toBeVisible() @@ -218,16 +218,16 @@ describe 'FuzzyFinder', -> editor = rootView.getActiveView() originalText = editor.getText() originalPath = editor.getPath() - fs.write(originalPath, 'making a change for the better') + fsUtils.write(originalPath, 'making a change for the better') git.getPathStatus(originalPath) newPath = project.resolve('newsample.js') - fs.write(newPath, '') + fsUtils.write(newPath, '') git.getPathStatus(newPath) afterEach -> - fs.write(originalPath, originalText) - fs.remove(newPath) if fs.exists(newPath) + fsUtils.write(originalPath, originalText) + fsUtils.remove(newPath) if fsUtils.exists(newPath) it "displays all new and modified paths", -> expect(rootView.find('.fuzzy-finder')).not.toExist() @@ -468,11 +468,11 @@ describe 'FuzzyFinder', -> originalText = editor.getText() originalPath = editor.getPath() newPath = project.resolve('newsample.js') - fs.write(newPath, '') + fsUtils.write(newPath, '') afterEach -> - fs.write(originalPath, originalText) - fs.remove(newPath) if fs.exists(newPath) + fsUtils.write(originalPath, originalText) + fsUtils.remove(newPath) if fsUtils.exists(newPath) describe "when a modified file is shown in the list", -> it "displays the modified icon", -> diff --git a/src/packages/markdown-preview/lib/markdown-preview-view.coffee b/src/packages/markdown-preview/lib/markdown-preview-view.coffee index 62a832e5c..5a8de69de 100644 --- a/src/packages/markdown-preview/lib/markdown-preview-view.coffee +++ b/src/packages/markdown-preview/lib/markdown-preview-view.coffee @@ -1,4 +1,3 @@ -fs = require 'fs-utils' $ = require 'jquery' ScrollView = require 'scroll-view' {$$$} = require 'space-pen' diff --git a/src/packages/package-generator/lib/package-generator-view.coffee b/src/packages/package-generator/lib/package-generator-view.coffee index 63f07890f..eebbf88c2 100644 --- a/src/packages/package-generator/lib/package-generator-view.coffee +++ b/src/packages/package-generator/lib/package-generator-view.coffee @@ -2,7 +2,7 @@ Editor = require 'editor' $ = require 'jquery' _ = require 'underscore' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' module.exports = class PackageGeneratorView extends View @@ -24,7 +24,7 @@ class PackageGeneratorView extends View @previouslyFocusedElement = $(':focus') @message.text("Enter package path") placeholderName = "package-name" - @miniEditor.setText(fs.join(config.userPackagesDirPath, placeholderName)); + @miniEditor.setText(fsUtils.join(config.userPackagesDirPath, placeholderName)); pathLength = @miniEditor.getText().length @miniEditor.setSelectedBufferRange([[0, pathLength - placeholderName.length], [0, pathLength]]) @@ -44,11 +44,11 @@ class PackageGeneratorView extends View getPackagePath: -> packagePath = @miniEditor.getText() - packageName = _.dasherize(fs.base(packagePath)) - fs.join(fs.directory(packagePath), packageName) + packageName = _.dasherize(fsUtils.base(packagePath)) + fsUtils.join(fsUtils.directory(packagePath), packageName) validPackagePath: -> - if fs.exists(@getPackagePath()) + if fsUtils.exists(@getPackagePath()) @error.text("Path already exists at '#{@getPackagePath()}'") @error.show() false @@ -56,22 +56,22 @@ class PackageGeneratorView extends View true createPackageFiles: -> - templatePath = fs.resolveOnLoadPath(fs.join("package-generator", "template")) - packageName = fs.base(@getPackagePath()) + templatePath = fsUtils.resolveOnLoadPath(fsUtils.join("package-generator", "template")) + packageName = fsUtils.base(@getPackagePath()) - for path in fs.listTree(templatePath) + for path in fsUtils.listTree(templatePath) relativePath = path.replace(templatePath, "") relativePath = relativePath.replace(/^\//, '') relativePath = relativePath.replace(/\.template$/, '') relativePath = @replacePackageNamePlaceholders(relativePath, packageName) - sourcePath = fs.join(@getPackagePath(), relativePath) - if fs.isDirectory(path) - fs.makeTree(sourcePath) - if fs.isFile(path) - fs.makeTree(fs.directory(sourcePath)) - content = @replacePackageNamePlaceholders(fs.read(path), packageName) - fs.write(sourcePath, content) + sourcePath = fsUtils.join(@getPackagePath(), relativePath) + if fsUtils.isDirectory(path) + fsUtils.makeTree(sourcePath) + if fsUtils.isFile(path) + fsUtils.makeTree(fsUtils.directory(sourcePath)) + content = @replacePackageNamePlaceholders(fsUtils.read(path), packageName) + fsUtils.write(sourcePath, content) replacePackageNamePlaceholders: (string, packageName) -> placeholderRegex = /__(?:(package-name)|([pP]ackageName)|(package_name))__/g diff --git a/src/packages/package-generator/spec/package-generator-spec.coffee b/src/packages/package-generator/spec/package-generator-spec.coffee index 87d57bbdd..b237dc285 100644 --- a/src/packages/package-generator/spec/package-generator-spec.coffee +++ b/src/packages/package-generator/spec/package-generator-spec.coffee @@ -1,5 +1,5 @@ RootView = require 'root-view' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' describe 'Package Generator', -> [packageGenerator] = [] @@ -35,21 +35,21 @@ describe 'Package Generator', -> packageName = "sweet-package-dude" packagePath = "/tmp/atom-packages/#{packageName}" - fs.remove(packagePath) if fs.exists(packagePath) + fsUtils.remove(packagePath) if fsUtils.exists(packagePath) afterEach -> - fs.remove(packagePath) if fs.exists(packagePath) + fsUtils.remove(packagePath) if fsUtils.exists(packagePath) it "forces the package's name to be lowercase with dashes", -> packageName = "CamelCaseIsForTheBirds" - packagePath = fs.join(fs.directory(packagePath), packageName) + packagePath = fsUtils.join(fsUtils.directory(packagePath), packageName) rootView.trigger("package-generator:generate") packageGeneratorView = rootView.find(".package-generator").view() packageGeneratorView.miniEditor.setText(packagePath) packageGeneratorView.trigger "core:confirm" expect(packagePath).not.toExistOnDisk() - expect(fs.join(fs.directory(packagePath), "camel-case-is-for-the-birds")).toExistOnDisk() + expect(fsUtils.join(fsUtils.directory(packagePath), "camel-case-is-for-the-birds")).toExistOnDisk() it "correctly lays out the package files and closes the package generator view", -> rootView.attachToDom() @@ -77,16 +77,16 @@ describe 'Package Generator', -> packageGeneratorView.miniEditor.setText(packagePath) packageGeneratorView.trigger "core:confirm" - lines = fs.read("#{packagePath}/package.cson").split("\n") + lines = fsUtils.read("#{packagePath}/package.cson").split("\n") expect(lines[0]).toBe "'main': 'lib\/#{packageName}'" - lines = fs.read("#{packagePath}/lib/#{packageName}.coffee").split("\n") + lines = fsUtils.read("#{packagePath}/lib/#{packageName}.coffee").split("\n") expect(lines[0]).toBe "SweetPackageDudeView = require 'sweet-package-dude/lib/sweet-package-dude-view'" expect(lines[3]).toBe " sweetPackageDudeView: null" it "displays an error when the package path already exists", -> rootView.attachToDom() - fs.makeTree(packagePath) + fsUtils.makeTree(packagePath) rootView.trigger("package-generator:generate") packageGeneratorView = rootView.find(".package-generator").view() diff --git a/src/packages/snippets/lib/snippet-body-parser.coffee b/src/packages/snippets/lib/snippet-body-parser.coffee index f4c3f99f7..15e6bb289 100644 --- a/src/packages/snippets/lib/snippet-body-parser.coffee +++ b/src/packages/snippets/lib/snippet-body-parser.coffee @@ -1,4 +1,4 @@ PEG = require 'pegjs' -fs = require 'fs-utils' -grammarSrc = fs.read(require.resolve('./snippet-body.pegjs')) +fsUtils = require 'fs-utils' +grammarSrc = fsUtils.read(require.resolve('./snippet-body.pegjs')) module.exports = PEG.buildParser(grammarSrc, trackLineAndColumn: true) diff --git a/src/packages/snippets/spec/snippets-spec.coffee b/src/packages/snippets/spec/snippets-spec.coffee index 2236757e7..de27a456a 100644 --- a/src/packages/snippets/spec/snippets-spec.coffee +++ b/src/packages/snippets/spec/snippets-spec.coffee @@ -3,7 +3,6 @@ RootView = require 'root-view' Buffer = require 'text-buffer' Editor = require 'editor' _ = require 'underscore' -fs = require 'fs-utils' Package = require 'package' describe "Snippets extension", -> diff --git a/src/packages/status-bar/spec/status-bar-spec.coffee b/src/packages/status-bar/spec/status-bar-spec.coffee index 497a4143d..d9bf04274 100644 --- a/src/packages/status-bar/spec/status-bar-spec.coffee +++ b/src/packages/status-bar/spec/status-bar-spec.coffee @@ -2,7 +2,7 @@ $ = require 'jquery' _ = require 'underscore' RootView = require 'root-view' StatusBar = require 'status-bar/lib/status-bar-view' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' describe "StatusBar", -> [editor, statusBar, buffer] = [] @@ -57,7 +57,7 @@ describe "StatusBar", -> describe "when the buffer content has changed from the content on disk", -> it "disables the buffer modified indicator on save", -> path = "/tmp/atom-whitespace.txt" - fs.write(path, "") + fsUtils.write(path, "") rootView.open(path) expect(statusBar.bufferModified.text()).toBe '' editor.insertText("\n") @@ -107,12 +107,12 @@ describe "StatusBar", -> describe "git branch label", -> beforeEach -> - fs.remove('/tmp/.git') if fs.isDirectory('/tmp/.git') + fsUtils.remove('/tmp/.git') if fsUtils.isDirectory('/tmp/.git') rootView.attachToDom() it "displays the current branch for files in repositories", -> path = require.resolve('fixtures/git/master.git/HEAD') - project.setPath(fs.resolveOnLoadPath('fixtures/git/master.git')) + project.setPath(fsUtils.resolveOnLoadPath('fixtures/git/master.git')) rootView.open(path) expect(statusBar.branchArea).toBeVisible() expect(statusBar.branchLabel.text()).toBe 'master' @@ -128,22 +128,22 @@ describe "StatusBar", -> beforeEach -> path = require.resolve('fixtures/git/working-dir/file.txt') - newPath = fs.join(fs.resolveOnLoadPath('fixtures/git/working-dir'), 'new.txt') - fs.write(newPath, "I'm new here") - ignoredPath = fs.join(fs.resolveOnLoadPath('fixtures/git/working-dir'), 'ignored.txt') - fs.write(ignoredPath, 'ignored.txt') + newPath = fsUtils.join(fsUtils.resolveOnLoadPath('fixtures/git/working-dir'), 'new.txt') + fsUtils.write(newPath, "I'm new here") + ignoredPath = fsUtils.join(fsUtils.resolveOnLoadPath('fixtures/git/working-dir'), 'ignored.txt') + fsUtils.write(ignoredPath, 'ignored.txt') git.getPathStatus(path) git.getPathStatus(newPath) - originalPathText = fs.read(path) + originalPathText = fsUtils.read(path) rootView.attachToDom() afterEach -> - fs.write(path, originalPathText) - fs.remove(newPath) if fs.exists(newPath) - fs.remove(ignoredPath) if fs.exists(ignoredPath) + fsUtils.write(path, originalPathText) + fsUtils.remove(newPath) if fsUtils.exists(newPath) + fsUtils.remove(ignoredPath) if fsUtils.exists(ignoredPath) it "displays the modified icon for a changed file", -> - fs.write(path, "i've changed for the worse") + fsUtils.write(path, "i've changed for the worse") git.getPathStatus(path) rootView.open(path) expect(statusBar.gitStatusIcon).toHaveClass('modified-status-icon') @@ -161,16 +161,16 @@ describe "StatusBar", -> expect(statusBar.gitStatusIcon).toHaveClass('ignored-status-icon') it "updates when a status-changed event occurs", -> - fs.write(path, "i've changed for the worse") + fsUtils.write(path, "i've changed for the worse") git.getPathStatus(path) rootView.open(path) expect(statusBar.gitStatusIcon).toHaveClass('modified-status-icon') - fs.write(path, originalPathText) + fsUtils.write(path, originalPathText) git.getPathStatus(path) expect(statusBar.gitStatusIcon).not.toHaveClass('modified-status-icon') it "displays the diff stat for modified files", -> - fs.write(path, "i've changed for the worse") + fsUtils.write(path, "i've changed for the worse") git.getPathStatus(path) rootView.open(path) expect(statusBar.gitStatusIcon).toHaveText('+1,-1') diff --git a/src/packages/symbols-view/lib/load-tags-handler.coffee b/src/packages/symbols-view/lib/load-tags-handler.coffee index 860d037f5..c29957822 100644 --- a/src/packages/symbols-view/lib/load-tags-handler.coffee +++ b/src/packages/symbols-view/lib/load-tags-handler.coffee @@ -1,13 +1,13 @@ ctags = require 'ctags' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' module.exports = getTagsFile: (path) -> - tagsFile = fs.join(path, "tags") - return tagsFile if fs.isFile(tagsFile) + tagsFile = fsUtils.join(path, "tags") + return tagsFile if fsUtils.isFile(tagsFile) - tagsFile = fs.join(path, "TAGS") - return tagsFile if fs.isFile(tagsFile) + tagsFile = fsUtils.join(path, "TAGS") + return tagsFile if fsUtils.isFile(tagsFile) loadTags: (path) -> tagsFile = @getTagsFile(path) diff --git a/src/packages/symbols-view/lib/symbols-view.coffee b/src/packages/symbols-view/lib/symbols-view.coffee index 816be9739..a6f617139 100644 --- a/src/packages/symbols-view/lib/symbols-view.coffee +++ b/src/packages/symbols-view/lib/symbols-view.coffee @@ -3,7 +3,7 @@ SelectList = require 'select-list' TagGenerator = require './tag-generator' TagReader = require './tag-reader' Point = require 'point' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' $ = require 'jquery' module.exports = @@ -30,7 +30,7 @@ class SymbolsView extends SelectList if position text = "Line #{position.row + 1}" else - text = fs.base(file) + text = fsUtils.base(file) @div text, class: 'right function-details' toggleFileSymbols: -> @@ -75,7 +75,7 @@ class SymbolsView extends SelectList setTimeout (=> @cancel()), 2000 confirmed : (tag) -> - if tag.file and not fs.isFile(project.resolve(tag.file)) + if tag.file and not fsUtils.isFile(project.resolve(tag.file)) @setError('Selected file does not exist') setTimeout((=> @setError()), 2000) else @@ -104,8 +104,8 @@ class SymbolsView extends SelectList pattern = $.trim(tag.pattern?.replace(/(^^\/\^)|(\$\/$)/g, '')) # Remove leading /^ and trailing $/ return unless pattern file = project.resolve(tag.file) - return unless fs.isFile(file) - for line, index in fs.read(file).split('\n') + return unless fsUtils.isFile(file) + for line, index in fsUtils.read(file).split('\n') return new Point(index, 0) if pattern is $.trim(line) goToDeclaration: -> @@ -123,7 +123,7 @@ class SymbolsView extends SelectList continue unless position tags.push file: match.file - name: fs.base(match.file) + name: fsUtils.base(match.file) position: position @miniEditor.show() @setArray(tags) diff --git a/src/packages/symbols-view/lib/tag-generator.coffee b/src/packages/symbols-view/lib/tag-generator.coffee index 5960ed6a2..8a847ea25 100644 --- a/src/packages/symbols-view/lib/tag-generator.coffee +++ b/src/packages/symbols-view/lib/tag-generator.coffee @@ -1,7 +1,7 @@ Point = require 'point' $ = require 'jquery' BufferedProcess = require 'buffered-process' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' module.exports = class TagGenerator @@ -18,7 +18,7 @@ class TagGenerator generate: -> deferred = $.Deferred() tags = [] - command = fs.resolveOnLoadPath('ctags') + command = fsUtils.resolveOnLoadPath('ctags') args = ['--fields=+KS', '-nf', '-', @path] stdout = (lines) => for line in lines.split('\n') diff --git a/src/packages/symbols-view/lib/tag-reader.coffee b/src/packages/symbols-view/lib/tag-reader.coffee index dd7f069a7..d243535e8 100644 --- a/src/packages/symbols-view/lib/tag-reader.coffee +++ b/src/packages/symbols-view/lib/tag-reader.coffee @@ -1,4 +1,4 @@ -fs = require 'fs-utils' +fsUtils = require 'fs-utils' $ = require 'jquery' LoadTagsTask = require './load-tags-task' ctags = require 'ctags' @@ -7,7 +7,7 @@ module.exports = getTagsFile: (project) -> tagsFile = project.resolve("tags") or project.resolve("TAGS") - return tagsFile if fs.isFile(tagsFile) + return tagsFile if fsUtils.isFile(tagsFile) find: (editor) -> word = editor.getTextInRange(editor.getCursor().getCurrentWordBufferRange()) diff --git a/src/packages/symbols-view/spec/symbols-view-spec.coffee b/src/packages/symbols-view/spec/symbols-view-spec.coffee index 7401a4448..4762ba421 100644 --- a/src/packages/symbols-view/spec/symbols-view-spec.coffee +++ b/src/packages/symbols-view/spec/symbols-view-spec.coffee @@ -1,7 +1,7 @@ RootView = require 'root-view' SymbolsView = require 'symbols-view/lib/symbols-view' TagGenerator = require 'symbols-view/lib/tag-generator' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' describe "SymbolsView", -> [symbolsView, setArraySpy] = [] @@ -162,11 +162,11 @@ describe "SymbolsView", -> beforeEach -> renamedPath = project.resolve("tagged-duplicate-renamed.js") - fs.remove(renamedPath) if fs.exists(renamedPath) - fs.move(project.resolve("tagged-duplicate.js"), renamedPath) + fsUtils.remove(renamedPath) if fsUtils.exists(renamedPath) + fsUtils.move(project.resolve("tagged-duplicate.js"), renamedPath) afterEach -> - fs.move(renamedPath, project.resolve("tagged-duplicate.js")) + fsUtils.move(renamedPath, project.resolve("tagged-duplicate.js")) it "doesn't display the tag", -> rootView.open("tagged.js") @@ -205,11 +205,11 @@ describe "SymbolsView", -> beforeEach -> renamedPath = project.resolve("tagged-renamed.js") - fs.remove(renamedPath) if fs.exists(renamedPath) - fs.move(project.resolve("tagged.js"), renamedPath) + fsUtils.remove(renamedPath) if fsUtils.exists(renamedPath) + fsUtils.move(project.resolve("tagged.js"), renamedPath) afterEach -> - fs.move(renamedPath, project.resolve("tagged.js")) + fsUtils.move(renamedPath, project.resolve("tagged.js")) it "doesn't open the editor", -> rootView.trigger "symbols-view:toggle-project-symbols" diff --git a/src/packages/tabs/lib/tab-view.coffee b/src/packages/tabs/lib/tab-view.coffee index 9e9594ef1..9c5faab90 100644 --- a/src/packages/tabs/lib/tab-view.coffee +++ b/src/packages/tabs/lib/tab-view.coffee @@ -1,6 +1,6 @@ $ = require 'jquery' {View} = require 'space-pen' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' module.exports = class TabView extends View @@ -46,7 +46,7 @@ class TabView extends View if fileNameText? duplicates = @editor.getEditSessions().filter (session) -> fileNameText is session.buffer.getBaseName() if duplicates.length > 1 - directory = fs.base(fs.directory(@editSession.getPath())) + directory = fsUtils.base(fsUtils.directory(@editSession.getPath())) fileNameText = "#{fileNameText} - #{directory}" if directory else fileNameText = 'untitled' diff --git a/src/packages/tabs/spec/tabs-spec.coffee b/src/packages/tabs/spec/tabs-spec.coffee index a366126d9..7eb59d232 100644 --- a/src/packages/tabs/spec/tabs-spec.coffee +++ b/src/packages/tabs/spec/tabs-spec.coffee @@ -4,7 +4,6 @@ RootView = require 'root-view' Pane = require 'pane' PaneContainer = require 'pane-container' TabBarView = require 'tabs/lib/tab-bar-view' -fs = require 'fs-utils' {View} = require 'space-pen' describe "Tabs package main", -> diff --git a/src/packages/tree-view/lib/dialog.coffee b/src/packages/tree-view/lib/dialog.coffee index 858db818a..d1d4787bd 100644 --- a/src/packages/tree-view/lib/dialog.coffee +++ b/src/packages/tree-view/lib/dialog.coffee @@ -1,6 +1,6 @@ {View} = require 'space-pen' Editor = require 'editor' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' $ = require 'jquery' module.exports = @@ -21,8 +21,8 @@ class Dialog extends View @miniEditor.setText(path) if select - extension = fs.extension(path) - baseName = fs.base(path) + extension = fsUtils.extension(path) + baseName = fsUtils.base(path) if baseName is extension selectionEnd = path.length else diff --git a/src/packages/tree-view/lib/file-view.coffee b/src/packages/tree-view/lib/file-view.coffee index 1ff73c910..1aea8286b 100644 --- a/src/packages/tree-view/lib/file-view.coffee +++ b/src/packages/tree-view/lib/file-view.coffee @@ -1,7 +1,7 @@ {View} = require 'space-pen' $ = require 'jquery' Git = require 'git' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' module.exports = class FileView extends View @@ -17,16 +17,16 @@ class FileView extends View if @file.symlink @fileName.addClass('symlink-icon') else - extension = fs.extension(@getPath()) - if fs.isReadmePath(@getPath()) + extension = fsUtils.extension(@getPath()) + if fsUtils.isReadmePath(@getPath()) @fileName.addClass('readme-icon') - else if fs.isCompressedExtension(extension) + else if fsUtils.isCompressedExtension(extension) @fileName.addClass('compressed-icon') - else if fs.isImageExtension(extension) + else if fsUtils.isImageExtension(extension) @fileName.addClass('image-icon') - else if fs.isPdfExtension(extension) + else if fsUtils.isPdfExtension(extension) @fileName.addClass('pdf-icon') - else if fs.isBinaryExtension(extension) + else if fsUtils.isBinaryExtension(extension) @fileName.addClass('binary-icon') else @fileName.addClass('text-icon') diff --git a/src/packages/tree-view/lib/tree-view.coffee b/src/packages/tree-view/lib/tree-view.coffee index c7e72221e..6e0aa25b2 100644 --- a/src/packages/tree-view/lib/tree-view.coffee +++ b/src/packages/tree-view/lib/tree-view.coffee @@ -4,7 +4,7 @@ Directory = require 'directory' DirectoryView = require './directory-view' FileView = require './file-view' Dialog = require './dialog' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' $ = require 'jquery' _ = require 'underscore' @@ -230,14 +230,14 @@ class TreeView extends ScrollView dialog.close() return - if fs.exists(newPath) + if fsUtils.exists(newPath) dialog.showError("Error: #{newPath} already exists. Try a different path.") return - directoryPath = fs.directory(newPath) + directoryPath = fsUtils.directory(newPath) try - fs.makeTree(directoryPath) unless fs.exists(directoryPath) - fs.move(oldPath, newPath) + fsUtils.makeTree(directoryPath) unless fsUtils.exists(directoryPath) + fsUtils.move(oldPath, newPath) dialog.close() catch e dialog.showError("Error: #{e.message} Try a different path.") @@ -254,13 +254,13 @@ class TreeView extends ScrollView "You are deleting #{entry.getPath()}", "Move to Trash", (=> $native.moveToTrash(entry.getPath())), "Cancel", null - "Delete", (=> fs.remove(entry.getPath())) + "Delete", (=> fsUtils.remove(entry.getPath())) ) add: -> selectedEntry = @selectedEntry() or @root selectedPath = selectedEntry.getPath() - directoryPath = if fs.isFile(selectedPath) then fs.directory(selectedPath) else selectedPath + directoryPath = if fsUtils.isFile(selectedPath) then fsUtils.directory(selectedPath) else selectedPath relativeDirectoryPath = project.relativize(directoryPath) relativeDirectoryPath += '/' if relativeDirectoryPath.length > 0 @@ -274,16 +274,16 @@ class TreeView extends ScrollView endsWithDirectorySeparator = /\/$/.test(relativePath) path = project.resolve(relativePath) try - if fs.exists(path) - pathType = if fs.isFile(path) then "file" else "directory" + if fsUtils.exists(path) + pathType = if fsUtils.isFile(path) then "file" else "directory" dialog.showError("Error: A #{pathType} already exists at path '#{path}'. Try a different path.") else if endsWithDirectorySeparator - fs.makeTree(path) + fsUtils.makeTree(path) dialog.cancel() @entryForPath(path).buildEntries() @selectEntryForPath(path) else - fs.write(path, "") + fsUtils.write(path, "") rootView.open(path) dialog.close() catch e diff --git a/src/packages/tree-view/spec/tree-view-spec.coffee b/src/packages/tree-view/spec/tree-view-spec.coffee index f5f01aace..d65c59fdb 100644 --- a/src/packages/tree-view/spec/tree-view-spec.coffee +++ b/src/packages/tree-view/spec/tree-view-spec.coffee @@ -4,7 +4,7 @@ _ = require 'underscore' TreeView = require 'tree-view/lib/tree-view' RootView = require 'root-view' Directory = require 'directory' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' describe "TreeView", -> [treeView, sampleJs, sampleTxt] = [] @@ -259,20 +259,20 @@ describe "TreeView", -> sampleJs.trigger clickEvent(originalEvent: { detail: 1 }) expect(sampleJs).toHaveClass 'selected' - expect(rootView.getActiveView().getPath()).toBe fs.resolveOnLoadPath('fixtures/tree-view/tree-view.js') + expect(rootView.getActiveView().getPath()).toBe fsUtils.resolveOnLoadPath('fixtures/tree-view/tree-view.js') expect(rootView.getActiveView().isFocused).toBeFalsy() sampleTxt.trigger clickEvent(originalEvent: { detail: 1 }) expect(sampleTxt).toHaveClass 'selected' expect(treeView.find('.selected').length).toBe 1 - expect(rootView.getActiveView().getPath()).toBe fs.resolveOnLoadPath('fixtures/tree-view/tree-view.txt') + expect(rootView.getActiveView().getPath()).toBe fsUtils.resolveOnLoadPath('fixtures/tree-view/tree-view.txt') expect(rootView.getActiveView().isFocused).toBeFalsy() describe "when a file is double-clicked", -> it "selects the file and opens it in the active editor on the first click, then changes focus to the active editor on the second", -> sampleJs.trigger clickEvent(originalEvent: { detail: 1 }) expect(sampleJs).toHaveClass 'selected' - expect(rootView.getActiveView().getPath()).toBe fs.resolveOnLoadPath('fixtures/tree-view/tree-view.js') + expect(rootView.getActiveView().getPath()).toBe fsUtils.resolveOnLoadPath('fixtures/tree-view/tree-view.js') expect(rootView.getActiveView().isFocused).toBeFalsy() sampleJs.trigger clickEvent(originalEvent: { detail: 2 }) @@ -568,7 +568,7 @@ describe "TreeView", -> it "opens the file in the editor and focuses it", -> treeView.root.find('.file:contains(tree-view.js)').click() treeView.root.trigger 'tree-view:open-selected-entry' - expect(rootView.getActiveView().getPath()).toBe fs.resolveOnLoadPath('fixtures/tree-view/tree-view.js') + expect(rootView.getActiveView().getPath()).toBe fsUtils.resolveOnLoadPath('fixtures/tree-view/tree-view.js') expect(rootView.getActiveView().isFocused).toBeTruthy() describe "when a directory is selected", -> @@ -593,14 +593,14 @@ describe "TreeView", -> beforeEach -> atom.deactivatePackage('tree-view') - rootDirPath = fs.join(fs.absolute("/tmp"), "atom-tests") - fs.remove(rootDirPath) if fs.exists(rootDirPath) + rootDirPath = fsUtils.join(fsUtils.absolute("/tmp"), "atom-tests") + fsUtils.remove(rootDirPath) if fsUtils.exists(rootDirPath) - dirPath = fs.join(rootDirPath, "test-dir") - filePath = fs.join(dirPath, "test-file.txt") - fs.makeDirectory(rootDirPath) - fs.makeDirectory(dirPath) - fs.write(filePath, "doesn't matter") + dirPath = fsUtils.join(rootDirPath, "test-dir") + filePath = fsUtils.join(dirPath, "test-file.txt") + fsUtils.makeDirectory(rootDirPath) + fsUtils.makeDirectory(dirPath) + fsUtils.write(filePath, "doesn't matter") project.setPath(rootDirPath) @@ -612,7 +612,7 @@ describe "TreeView", -> fileView = treeView.find('.file:contains(test-file.txt)').view() afterEach -> - fs.remove(rootDirPath) if fs.exists(rootDirPath) + fsUtils.remove(rootDirPath) if fsUtils.exists(rootDirPath) describe "tree-view:add", -> addDialog = null @@ -638,16 +638,16 @@ describe "TreeView", -> dirView.directory.trigger 'contents-changed' expect(directoryChangeHandler).toHaveBeenCalled() - expect(treeView.find('.selected').text()).toBe fs.base(filePath) + expect(treeView.find('.selected').text()).toBe fsUtils.base(filePath) describe "when the path without a trailing '/' is changed and confirmed", -> describe "when no file exists at that location", -> it "add a file, closes the dialog and selects the file in the tree-view", -> - newPath = fs.join(dirPath, "new-test-file.txt") - addDialog.miniEditor.insertText(fs.base(newPath)) + newPath = fsUtils.join(dirPath, "new-test-file.txt") + addDialog.miniEditor.insertText(fsUtils.base(newPath)) addDialog.trigger 'core:confirm' - expect(fs.exists(newPath)).toBeTruthy() - expect(fs.isFile(newPath)).toBeTruthy() + expect(fsUtils.exists(newPath)).toBeTruthy() + expect(fsUtils.isFile(newPath)).toBeTruthy() expect(addDialog.parent()).not.toExist() expect(rootView.getActiveView().getPath()).toBe newPath @@ -655,13 +655,13 @@ describe "TreeView", -> dirView.entries.find("> .file").length > 1 runs -> - expect(treeView.find('.selected').text()).toBe fs.base(newPath) + expect(treeView.find('.selected').text()).toBe fsUtils.base(newPath) describe "when a file already exists at that location", -> it "shows an error message and does not close the dialog", -> - newPath = fs.join(dirPath, "new-test-file.txt") - fs.write(newPath, '') - addDialog.miniEditor.insertText(fs.base(newPath)) + newPath = fsUtils.join(dirPath, "new-test-file.txt") + fsUtils.write(newPath, '') + addDialog.miniEditor.insertText(fsUtils.base(newPath)) addDialog.trigger 'core:confirm' expect(addDialog.prompt.text()).toContain 'Error' @@ -673,11 +673,11 @@ describe "TreeView", -> describe "when no file or directory exists at the given path", -> it "adds a directory and closes the dialog", -> treeView.attachToDom() - newPath = fs.join(dirPath, "new/dir") + newPath = fsUtils.join(dirPath, "new/dir") addDialog.miniEditor.insertText("new/dir/") addDialog.trigger 'core:confirm' - expect(fs.exists(newPath)).toBeTruthy() - expect(fs.isDirectory(newPath)).toBeTruthy() + expect(fsUtils.exists(newPath)).toBeTruthy() + expect(fsUtils.isDirectory(newPath)).toBeTruthy() expect(addDialog.parent()).not.toExist() expect(rootView.getActiveView().getPath()).not.toBe newPath expect(treeView.find(".tree-view")).toMatchSelector(':focus') @@ -686,11 +686,11 @@ describe "TreeView", -> it "selects the created directory", -> treeView.attachToDom() - newPath = fs.join(dirPath, "new2/") + newPath = fsUtils.join(dirPath, "new2/") addDialog.miniEditor.insertText("new2/") addDialog.trigger 'core:confirm' - expect(fs.exists(newPath)).toBeTruthy() - expect(fs.isDirectory(newPath)).toBeTruthy() + expect(fsUtils.exists(newPath)).toBeTruthy() + expect(fsUtils.isDirectory(newPath)).toBeTruthy() expect(addDialog.parent()).not.toExist() expect(rootView.getActiveView().getPath()).not.toBe newPath expect(treeView.find(".tree-view")).toMatchSelector(':focus') @@ -699,8 +699,8 @@ describe "TreeView", -> describe "when a file or directory already exists at the given path", -> it "shows an error message and does not close the dialog", -> - newPath = fs.join(dirPath, "new-dir") - fs.makeDirectory(newPath) + newPath = fsUtils.join(dirPath, "new-dir") + fsUtils.makeDirectory(newPath) addDialog.miniEditor.insertText("new-dir/") addDialog.trigger 'core:confirm' @@ -770,24 +770,24 @@ describe "TreeView", -> waits 50 # The move specs cause too many false positives because of their async nature, so wait a little bit before we cleanup it "opens a move dialog with the file's current path (excluding extension) populated", -> - extension = fs.extension(filePath) - fileNameWithoutExtension = fs.base(filePath, extension) + extension = fsUtils.extension(filePath) + fileNameWithoutExtension = fsUtils.base(filePath, extension) expect(moveDialog).toExist() expect(moveDialog.prompt.text()).toBe "Enter the new path for the file." expect(moveDialog.miniEditor.getText()).toBe(project.relativize(filePath)) - expect(moveDialog.miniEditor.getSelectedText()).toBe fs.base(fileNameWithoutExtension) + expect(moveDialog.miniEditor.getSelectedText()).toBe fsUtils.base(fileNameWithoutExtension) expect(moveDialog.miniEditor.isFocused).toBeTruthy() describe "when the path is changed and confirmed", -> describe "when all the directories along the new path exist", -> it "moves the file, updates the tree view, and closes the dialog", -> - newPath = fs.join(rootDirPath, 'renamed-test-file.txt') + newPath = fsUtils.join(rootDirPath, 'renamed-test-file.txt') moveDialog.miniEditor.setText(newPath) moveDialog.trigger 'core:confirm' - expect(fs.exists(newPath)).toBeTruthy() - expect(fs.exists(filePath)).toBeFalsy() + expect(fsUtils.exists(newPath)).toBeTruthy() + expect(fsUtils.exists(filePath)).toBeFalsy() expect(moveDialog.parent()).not.toExist() waitsFor "tree view to update", -> @@ -800,7 +800,7 @@ describe "TreeView", -> describe "when the directories along the new path don't exist", -> it "creates the target directory before moving the file", -> - newPath = fs.join(rootDirPath, 'new/directory', 'renamed-test-file.txt') + newPath = fsUtils.join(rootDirPath, 'new/directory', 'renamed-test-file.txt') moveDialog.miniEditor.setText(newPath) moveDialog.trigger 'core:confirm' @@ -809,14 +809,14 @@ describe "TreeView", -> treeView.root.find('> .entries > .directory:contains(new)').length > 0 runs -> - expect(fs.exists(newPath)).toBeTruthy() - expect(fs.exists(filePath)).toBeFalsy() + expect(fsUtils.exists(newPath)).toBeTruthy() + expect(fsUtils.exists(filePath)).toBeFalsy() describe "when a file or directory already exists at the target path", -> it "shows an error message and does not close the dialog", -> runs -> - fs.write(fs.join(rootDirPath, 'target.txt'), '') - newPath = fs.join(rootDirPath, 'target.txt') + fsUtils.write(fsUtils.join(rootDirPath, 'target.txt'), '') + newPath = fsUtils.join(rootDirPath, 'target.txt') moveDialog.miniEditor.setText(newPath) moveDialog.trigger 'core:confirm' @@ -844,8 +844,8 @@ describe "TreeView", -> [dotFilePath, dotFileView, moveDialog] = [] beforeEach -> - dotFilePath = fs.join(dirPath, ".dotfile") - fs.write(dotFilePath, "dot") + dotFilePath = fsUtils.join(dirPath, ".dotfile") + fsUtils.write(dotFilePath, "dot") dirView.collapse() dirView.expand() dotFileView = treeView.find('.file:contains(.dotfile)').view() @@ -875,22 +875,22 @@ describe "TreeView", -> temporaryFilePath = null beforeEach -> - temporaryFilePath = fs.join(fs.resolveOnLoadPath('fixtures/tree-view'), 'temporary') - if fs.exists(temporaryFilePath) - fs.remove(temporaryFilePath) + temporaryFilePath = fsUtils.join(fsUtils.resolveOnLoadPath('fixtures/tree-view'), 'temporary') + if fsUtils.exists(temporaryFilePath) + fsUtils.remove(temporaryFilePath) waits(20) afterEach -> - fs.remove(temporaryFilePath) if fs.exists(temporaryFilePath) + fsUtils.remove(temporaryFilePath) if fsUtils.exists(temporaryFilePath) describe "when a file is added or removed in an expanded directory", -> it "updates the directory view to display the directory's new contents", -> entriesCountBefore = null runs -> - expect(fs.exists(temporaryFilePath)).toBeFalsy() + expect(fsUtils.exists(temporaryFilePath)).toBeFalsy() entriesCountBefore = treeView.root.entries.find('.entry').length - fs.write temporaryFilePath, 'hi' + fsUtils.write temporaryFilePath, 'hi' waitsFor "directory view contens to refresh", -> treeView.root.entries.find('.entry').length == entriesCountBefore + 1 @@ -898,7 +898,7 @@ describe "TreeView", -> runs -> expect(treeView.root.entries.find('.entry').length).toBe entriesCountBefore + 1 expect(treeView.root.entries.find('.file:contains(temporary)')).toExist() - fs.remove(temporaryFilePath) + fsUtils.remove(temporaryFilePath) waitsFor "directory view contens to refresh", -> treeView.root.entries.find('.entry').length == entriesCountBefore @@ -907,12 +907,12 @@ describe "TreeView", -> [ignoreFile] = [] beforeEach -> - ignoreFile = fs.join(fs.resolveOnLoadPath('fixtures/tree-view'), '.gitignore') - fs.write(ignoreFile, 'tree-view.js') + ignoreFile = fsUtils.join(fsUtils.resolveOnLoadPath('fixtures/tree-view'), '.gitignore') + fsUtils.write(ignoreFile, 'tree-view.js') config.set "core.hideGitIgnoredFiles", false afterEach -> - fs.remove(ignoreFile) if fs.exists(ignoreFile) + fsUtils.remove(ignoreFile) if fsUtils.exists(ignoreFile) it "hides git-ignored files if the option is set, but otherwise shows them", -> expect(treeView.find('.file:contains(tree-view.js)').length).toBe 1 @@ -930,17 +930,17 @@ describe "TreeView", -> beforeEach -> config.set "core.hideGitIgnoredFiles", false - ignoreFile = fs.join(fs.resolveOnLoadPath('fixtures/tree-view'), '.gitignore') - fs.write(ignoreFile, 'tree-view.js') + ignoreFile = fsUtils.join(fsUtils.resolveOnLoadPath('fixtures/tree-view'), '.gitignore') + fsUtils.write(ignoreFile, 'tree-view.js') git.getPathStatus(ignoreFile) - newFile = fs.join(fs.resolveOnLoadPath('fixtures/tree-view/dir2'), 'new2') - fs.write(newFile, '') + newFile = fsUtils.join(fsUtils.resolveOnLoadPath('fixtures/tree-view/dir2'), 'new2') + fsUtils.write(newFile, '') git.getPathStatus(newFile) - modifiedFile = fs.join(fs.resolveOnLoadPath('fixtures/tree-view/dir1'), 'file1') - originalFileContent = fs.read(modifiedFile) - fs.write modifiedFile, 'ch ch changes' + modifiedFile = fsUtils.join(fsUtils.resolveOnLoadPath('fixtures/tree-view/dir1'), 'file1') + originalFileContent = fsUtils.read(modifiedFile) + fsUtils.write modifiedFile, 'ch ch changes' git.getPathStatus(modifiedFile) treeView.updateRoot() @@ -948,9 +948,9 @@ describe "TreeView", -> treeView.root.entries.find('.directory:contains(dir2)').view().expand() afterEach -> - fs.remove(ignoreFile) if fs.exists(ignoreFile) - fs.remove(newFile) if fs.exists(newFile) - fs.write modifiedFile, originalFileContent + fsUtils.remove(ignoreFile) if fsUtils.exists(ignoreFile) + fsUtils.remove(newFile) if fsUtils.exists(newFile) + fsUtils.write modifiedFile, originalFileContent describe "when a file is modified", -> it "adds a custom style", -> diff --git a/src/packages/whitespace/spec/whitespace-spec.coffee b/src/packages/whitespace/spec/whitespace-spec.coffee index 854cf3295..1a0e54f44 100644 --- a/src/packages/whitespace/spec/whitespace-spec.coffee +++ b/src/packages/whitespace/spec/whitespace-spec.coffee @@ -1,12 +1,12 @@ RootView = require 'root-view' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' describe "Whitespace", -> [editor, path] = [] beforeEach -> path = "/tmp/atom-whitespace.txt" - fs.write(path, "") + fsUtils.write(path, "") window.rootView = new RootView rootView.open(path) @@ -16,10 +16,10 @@ describe "Whitespace", -> editor = rootView.getActiveView() afterEach -> - fs.remove(path) if fs.exists(path) + fsUtils.remove(path) if fsUtils.exists(path) it "strips trailing whitespace before an editor saves a buffer", -> - spyOn(fs, 'write') + spyOn(fsUtils, 'write') config.set("whitespace.ensureSingleTrailingNewline", false) config.update() @@ -79,4 +79,3 @@ describe "Whitespace", -> editor.insertText "no trailing newline" editor.getBuffer().save() expect(editor.getText()).toBe "no trailing newline" - diff --git a/src/stdlib/task.coffee b/src/stdlib/task.coffee index 8216a4f15..091362d73 100644 --- a/src/stdlib/task.coffee +++ b/src/stdlib/task.coffee @@ -1,7 +1,7 @@ _ = require 'underscore' child_process = require 'child_process' EventEmitter = require 'event-emitter' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' module.exports = class Task From 930cd9551b37cfdf5bdc0aee0b0076844c3cea51 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Apr 2013 12:01:23 -0600 Subject: [PATCH 54/87] :lipstick: add eof newlines --- spec/fixtures/packages/package-with-main/main-module.coffee | 2 +- src/packages/grammar-selector/lib/grammar-selector.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/fixtures/packages/package-with-main/main-module.coffee b/spec/fixtures/packages/package-with-main/main-module.coffee index 46920ab78..825eb4055 100644 --- a/spec/fixtures/packages/package-with-main/main-module.coffee +++ b/spec/fixtures/packages/package-with-main/main-module.coffee @@ -1,2 +1,2 @@ module.exports = -activate: -> \ No newline at end of file +activate: -> diff --git a/src/packages/grammar-selector/lib/grammar-selector.coffee b/src/packages/grammar-selector/lib/grammar-selector.coffee index ca0aa828b..81f678a52 100644 --- a/src/packages/grammar-selector/lib/grammar-selector.coffee +++ b/src/packages/grammar-selector/lib/grammar-selector.coffee @@ -61,4 +61,4 @@ class GrammarSelector extends SelectList attach: -> super rootView.append(this) - @miniEditor.focus() \ No newline at end of file + @miniEditor.focus() From ee1585538321a96ba944a5e86778f301ede98961 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 3 Apr 2013 11:04:15 -0700 Subject: [PATCH 55/87] Bump nak --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9cae1eb37..f957af416 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "coffee-cache": "0.1.0", "pegjs": "0.7.0", "async": "0.2.6", - "nak": "0.2.10", + "nak": "0.2.11", "spellchecker": "0.2.0", "plist": "git://github.com/nathansobo/node-plist.git", "space-pen": "git://github.com/nathansobo/space-pen.git" From e1761418468b7361f4d163fd41e113ed262f664e Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 11:22:20 -0700 Subject: [PATCH 56/87] Follow symlinks loading paths --- src/packages/fuzzy-finder/lib/load-paths-task.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index cc10cb2a2..530ade9dc 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -16,6 +16,7 @@ class LoadPathsTask args = ['-l', rootPath] args.unshift('--addVCSIgnores') if config.get('nak.addVCSIgnores') args.unshift('-d', ignoredNames.join(',')) if ignoredNames.length > 0 + args.unshift('--follow') paths = [] deferred = $.Deferred() From 19cf333e52fa93e24bf57ecf3aefe07d425d9819 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 11:22:35 -0700 Subject: [PATCH 57/87] Use long opts for readability --- src/packages/fuzzy-finder/lib/load-paths-task.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index 530ade9dc..496693a3c 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -13,9 +13,9 @@ class LoadPathsTask ignoreGitIgnoredFiles = config.get('core.hideGitIgnoredFiles') command = require.resolve 'nak' - args = ['-l', rootPath] + args = ['--list', rootPath] args.unshift('--addVCSIgnores') if config.get('nak.addVCSIgnores') - args.unshift('-d', ignoredNames.join(',')) if ignoredNames.length > 0 + args.unshift('--ignore', ignoredNames.join(',')) if ignoredNames.length > 0 args.unshift('--follow') paths = [] From 5d22cff6ad7f076dc7be439feae7326dc81fd33b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Apr 2013 12:33:40 -0600 Subject: [PATCH 58/87] :speak_no_evil: --- .../packages/package-with-activation-events/index.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/fixtures/packages/package-with-activation-events/index.coffee b/spec/fixtures/packages/package-with-activation-events/index.coffee index 91f2230b1..44704fa79 100644 --- a/spec/fixtures/packages/package-with-activation-events/index.coffee +++ b/spec/fixtures/packages/package-with-activation-events/index.coffee @@ -10,5 +10,4 @@ module.exports = activate: -> @activateCallCount++ rootView.getActiveView()?.command 'activation-event', => - console.log "ACTIVATION EVENT" @activationEventCallCount++ From ace71acb3c2b387ca9142aa332314770d400f86b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 11:34:27 -0700 Subject: [PATCH 59/87] Invoke callback even when command fails --- src/packages/fuzzy-finder/lib/load-paths-task.coffee | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index 496693a3c..77d5b4c0b 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -1,6 +1,5 @@ _ = require 'underscore' BufferedProcess = require 'buffered-process' -$ = require 'jquery' module.exports = class LoadPathsTask @@ -19,18 +18,12 @@ class LoadPathsTask args.unshift('--follow') paths = [] - deferred = $.Deferred() - exit = (code) => - if code is -1 - deferred.reject({command, code}) - else - @callback(paths) - deferred.resolve() + exit = => + @callback(paths) stdout = (data) -> paths.push(_.compact(data.split('\n'))...) @process = new BufferedProcess({command, args, stdout, exit}) - deferred abort: -> if @process? From 40aa81e9c11063838b2ade2bf8f2ce676b560bc0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Apr 2013 12:34:56 -0600 Subject: [PATCH 60/87] Add required activate method to avoid logging during specs --- .../fixtures/packages/package-with-config-defaults/index.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/fixtures/packages/package-with-config-defaults/index.coffee b/spec/fixtures/packages/package-with-config-defaults/index.coffee index 5e0b1eed6..554c6c5eb 100644 --- a/spec/fixtures/packages/package-with-config-defaults/index.coffee +++ b/spec/fixtures/packages/package-with-config-defaults/index.coffee @@ -1,3 +1,5 @@ module.exports = configDefaults: numbers: { one: 1, two: 2 } + + activate: -> # no-op From 0b3a91b5feb6f38734c144cd48f8df5ce278cb6b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 11:35:24 -0700 Subject: [PATCH 61/87] Rename VCS ignore config setting to fuzzyFinder.hideVcsIgnoredPaths --- src/packages/fuzzy-finder/lib/load-paths-task.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index 77d5b4c0b..fab6e99cc 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -9,11 +9,10 @@ class LoadPathsTask rootPath = project.getPath() ignoredNames = config.get('fuzzyFinder.ignoredNames') ? [] ignoredNames = ignoredNames.concat(config.get('core.ignoredNames') ? []) - ignoreGitIgnoredFiles = config.get('core.hideGitIgnoredFiles') command = require.resolve 'nak' args = ['--list', rootPath] - args.unshift('--addVCSIgnores') if config.get('nak.addVCSIgnores') + args.unshift('--addVCSIgnores') if config.get('fuzzyFinder.hideVcsIgnoredPaths') args.unshift('--ignore', ignoredNames.join(',')) if ignoredNames.length > 0 args.unshift('--follow') From a5870cedd201cb786182ad8990fc92c0dd8f312a Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 11:45:55 -0700 Subject: [PATCH 62/87] Use more generic core.excludeVcsIgnoredPaths config key --- src/app/project.coffee | 2 +- src/packages/fuzzy-finder/lib/load-paths-task.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/project.coffee b/src/app/project.coffee index 3d50f6272..bc4c6187e 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -193,7 +193,7 @@ class Project command = require.resolve('nak') args = ['--ackmate', regex.source, @getPath()] - args.unshift("--addVCSIgnores") if config.get('nak.addVCSIgnores') + args.unshift("--addVCSIgnores") if config.get('core.excludeVcsIgnoredPaths') new BufferedProcess({command, args, stdout, exit}) deferred diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index fab6e99cc..fff7c0b5e 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -12,7 +12,7 @@ class LoadPathsTask command = require.resolve 'nak' args = ['--list', rootPath] - args.unshift('--addVCSIgnores') if config.get('fuzzyFinder.hideVcsIgnoredPaths') + args.unshift('--addVCSIgnores') if config.get('core.excludeVcsIgnoredPaths') args.unshift('--ignore', ignoredNames.join(',')) if ignoredNames.length > 0 args.unshift('--follow') From 52d47f555f8997d21f73020eeb49d0a65f7cb0a7 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 12:11:43 -0700 Subject: [PATCH 63/87] Don't run clean before test Clean should be optional --- Rakefile | 2 +- script/cibuild | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index f4fa7f69e..76c5a0c31 100644 --- a/Rakefile +++ b/Rakefile @@ -90,7 +90,7 @@ task :clean do end desc "Run the specs" -task :test => ["clean", "update-cef", "clone-default-bundles", "build"] do +task :test => ["update-cef", "clone-default-bundles", "build"] do `pkill Atom` if path = application_path() cmd = "#{path}/Contents/MacOS/Atom --test --resource-path=#{ATOM_SRC_PATH}" diff --git a/script/cibuild b/script/cibuild index b0d8fbb30..01a5298d3 100755 --- a/script/cibuild +++ b/script/cibuild @@ -2,4 +2,4 @@ set -ex rm -rf ~/.atom -CI_BUILD=true rake test +CI_BUILD=true rake clean test From a3ef9c204bb4d2d439cc006462a65339b348d2c0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 13:28:34 -0700 Subject: [PATCH 64/87] Use repository URL for nak dependency This is temporary until https://github.com/gjtorikian/nak/pull/17/files is merged and released. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f957af416..1d2e2541e 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "coffee-cache": "0.1.0", "pegjs": "0.7.0", "async": "0.2.6", - "nak": "0.2.11", + "nak": "git://github.com/kevinsawicki/nak.git", "spellchecker": "0.2.0", "plist": "git://github.com/nathansobo/node-plist.git", "space-pen": "git://github.com/nathansobo/space-pen.git" From d1fd3c8d7c69ef7d70d274a4f76c81f69a3e6481 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 13:54:35 -0700 Subject: [PATCH 65/87] Invoke callback with empty array on non-zero exit code --- src/packages/fuzzy-finder/lib/load-paths-task.coffee | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/packages/fuzzy-finder/lib/load-paths-task.coffee b/src/packages/fuzzy-finder/lib/load-paths-task.coffee index fff7c0b5e..bb358db8e 100644 --- a/src/packages/fuzzy-finder/lib/load-paths-task.coffee +++ b/src/packages/fuzzy-finder/lib/load-paths-task.coffee @@ -17,8 +17,11 @@ class LoadPathsTask args.unshift('--follow') paths = [] - exit = => - @callback(paths) + exit = (code) => + if code is 0 + @callback(paths) + else + @callback([]) stdout = (data) -> paths.push(_.compact(data.split('\n'))...) From 8447a22419be59c436df31a5f9e529d03b7d36f2 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 14:48:11 -0700 Subject: [PATCH 66/87] Support reloading the window from the event palette --- src/app/window.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/window.coffee b/src/app/window.coffee index f4950ed08..5b95f4061 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -85,6 +85,7 @@ window.handleWindowEvents = -> $(window).on 'focus', -> $("body").removeClass('is-blurred') $(window).on 'blur', -> $("body").addClass('is-blurred') $(window).command 'window:close', => confirmClose() + $(window).command 'window:reload', => reload() window.deserializeWindowState = -> RootView = require 'root-view' From 5d2a0a0223341c0ab09fe59f4d0266975add288a Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 15:01:23 -0700 Subject: [PATCH 67/87] Use fs.chmod() to make atom.sh executable --- src/app/window.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/window.coffee b/src/app/window.coffee index 5b95f4061..272f91029 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -1,8 +1,8 @@ +fs = require 'fs' fsUtils = require 'fs-utils' $ = require 'jquery' _ = require 'underscore' {less} = require 'less' -{spawn} = require 'child_process' require 'jquery-extensions' require 'underscore-extensions' require 'space-pen-extensions' @@ -78,7 +78,7 @@ window.installAtomCommand = (commandPath) -> bundledCommandPath = fsUtils.resolve(window.resourcePath, 'atom.sh') if bundledCommandPath? fsUtils.write(commandPath, fsUtils.read(bundledCommandPath)) - spawn('chmod', ['u+x', commandPath]) + fs.chmod(commandPath, 0o755, commandPath) window.handleWindowEvents = -> $(window).command 'window:toggle-full-screen', => atom.toggleFullScreen() From 262e2fe1ec53d9f633fe17baadb7983441fa929f Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Wed, 3 Apr 2013 15:07:11 -0700 Subject: [PATCH 68/87] Add Quincy crash reporter framework --- atom.gyp | 5 + native/atom_application.mm | 6 + native/frameworks/Quincy.framework/Headers | 1 + native/frameworks/Quincy.framework/Quincy | 1 + native/frameworks/Quincy.framework/Resources | 1 + .../Versions/A/Headers/BWQuincyManager.h | 169 ++++++++++++++++++ .../Quincy.framework/Versions/A/Quincy | Bin 0 -> 133096 bytes .../Versions/A/Resources/BWQuincyMain.nib | Bin 0 -> 14997 bytes .../Versions/A/Resources/Info.plist | 34 ++++ .../Quincy.framework/Versions/Current | 1 + 10 files changed, 218 insertions(+) create mode 120000 native/frameworks/Quincy.framework/Headers create mode 120000 native/frameworks/Quincy.framework/Quincy create mode 120000 native/frameworks/Quincy.framework/Resources create mode 100644 native/frameworks/Quincy.framework/Versions/A/Headers/BWQuincyManager.h create mode 100755 native/frameworks/Quincy.framework/Versions/A/Quincy create mode 100644 native/frameworks/Quincy.framework/Versions/A/Resources/BWQuincyMain.nib create mode 100644 native/frameworks/Quincy.framework/Versions/A/Resources/Info.plist create mode 120000 native/frameworks/Quincy.framework/Versions/Current diff --git a/atom.gyp b/atom.gyp index e3ceb2326..300582736 100644 --- a/atom.gyp +++ b/atom.gyp @@ -25,6 +25,9 @@ '-change', '@loader_path/../Frameworks/Sparkle.framework/Versions/A/Sparkle', '@rpath/Sparkle.framework/Versions/A/Sparkle', + '-change', + '@executable_path/../Frameworks/Quincy.framework/Versions/A/Quincy', + '@rpath/Quincy.framework/Versions/A/Quincy', '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}' ], }, @@ -138,6 +141,7 @@ '<(PRODUCT_DIR)/Atom Helper.app', '<(PRODUCT_DIR)/Atom.framework', 'native/frameworks/Sparkle.framework', + 'native/frameworks/Quincy.framework' ], }, { @@ -259,6 +263,7 @@ 'libraries': [ '$(SDKROOT)/System/Library/Frameworks/AppKit.framework', 'native/frameworks/Sparkle.framework', + 'native/frameworks/Quincy.framework', ], }, 'mac_bundle_resources': [ diff --git a/native/atom_application.mm b/native/atom_application.mm index 26c3c7778..ab26df02d 100644 --- a/native/atom_application.mm +++ b/native/atom_application.mm @@ -5,6 +5,7 @@ #import "native/atom_cef_app.h" #import #import +#import @implementation AtomApplication @@ -207,6 +208,11 @@ } - (void)applicationDidFinishLaunching:(NSNotification *)notification { + BWQuincyManager *manager = [BWQuincyManager sharedQuincyManager]; + [manager setCompanyName:@"GitHub"]; + [manager setSubmissionURL:@"https://speakeasy.githubapp.com/submit_crash_log"]; + [manager setAutoSubmitCrashReport:YES]; + if (!_filesOpened && [self shouldOpenFiles]) { NSString *path = [self.arguments objectForKey:@"path"]; NSNumber *pid = [self.arguments objectForKey:@"wait"] ? [self.arguments objectForKey:@"pid"] : nil; diff --git a/native/frameworks/Quincy.framework/Headers b/native/frameworks/Quincy.framework/Headers new file mode 120000 index 000000000..a177d2a6b --- /dev/null +++ b/native/frameworks/Quincy.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/native/frameworks/Quincy.framework/Quincy b/native/frameworks/Quincy.framework/Quincy new file mode 120000 index 000000000..9bbfc5b61 --- /dev/null +++ b/native/frameworks/Quincy.framework/Quincy @@ -0,0 +1 @@ +Versions/Current/Quincy \ No newline at end of file diff --git a/native/frameworks/Quincy.framework/Resources b/native/frameworks/Quincy.framework/Resources new file mode 120000 index 000000000..953ee36f3 --- /dev/null +++ b/native/frameworks/Quincy.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/native/frameworks/Quincy.framework/Versions/A/Headers/BWQuincyManager.h b/native/frameworks/Quincy.framework/Versions/A/Headers/BWQuincyManager.h new file mode 100644 index 000000000..bb6d91c01 --- /dev/null +++ b/native/frameworks/Quincy.framework/Versions/A/Headers/BWQuincyManager.h @@ -0,0 +1,169 @@ +/* + * Author: Andreas Linde + * Kent Sutherland + * + * Copyright (c) 2011 Andreas Linde & Kent Sutherland. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +typedef enum CrashAlertType { + CrashAlertTypeSend = 0, + CrashAlertTypeFeedback = 1, +} CrashAlertType; + +typedef enum CrashReportStatus { + // This app version is set to discontinued, no new crash reports accepted by the server + CrashReportStatusFailureVersionDiscontinued = -30, + + // XML: Sender ersion string contains not allowed characters, only alphanumberical including space and . are allowed + CrashReportStatusFailureXMLSenderVersionNotAllowed = -21, + + // XML: Version string contains not allowed characters, only alphanumberical including space and . are allowed + CrashReportStatusFailureXMLVersionNotAllowed = -20, + + // SQL for adding a symoblicate todo entry in the database failed + CrashReportStatusFailureSQLAddSymbolicateTodo = -18, + + // SQL for adding crash log in the database failed + CrashReportStatusFailureSQLAddCrashlog = -17, + + // SQL for adding a new version in the database failed + CrashReportStatusFailureSQLAddVersion = -16, + + // SQL for checking if the version is already added in the database failed + CrashReportStatusFailureSQLCheckVersionExists = -15, + + // SQL for creating a new pattern for this bug and set amount of occurrances to 1 in the database failed + CrashReportStatusFailureSQLAddPattern = -14, + + // SQL for checking the status of the bugfix version in the database failed + CrashReportStatusFailureSQLCheckBugfixStatus = -13, + + // SQL for updating the occurances of this pattern in the database failed + CrashReportStatusFailureSQLUpdatePatternOccurances = -12, + + // SQL for getting all the known bug patterns for the current app version in the database failed + CrashReportStatusFailureSQLFindKnownPatterns = -11, + + // SQL for finding the bundle identifier in the database failed + CrashReportStatusFailureSQLSearchAppName = -10, + + // the post request didn't contain valid data + CrashReportStatusFailureInvalidPostData = -3, + + // incoming data may not be added, because e.g. bundle identifier wasn't found + CrashReportStatusFailureInvalidIncomingData = -2, + + // database cannot be accessed, check hostname, username, password and database name settings in config.php + CrashReportStatusFailureDatabaseNotAvailable = -1, + + CrashReportStatusUnknown = 0, + + CrashReportStatusAssigned = 1, + + CrashReportStatusSubmitted = 2, + + CrashReportStatusAvailable = 3, +} CrashReportStatus; + + +@class BWQuincyUI; + +@protocol BWQuincyManagerDelegate + +@required + +// Invoked once the modal sheets are gone +- (void) showMainApplicationWindow; + +@optional + +// Return the description the crashreport should contain, empty by default. The string will automatically be wrapped into <[DATA[ ]]>, so make sure you don't do that in your string. +-(NSString *) crashReportDescription; + +// Return the userid the crashreport should contain, empty by default +-(NSString *) crashReportUserID; + +// Return the contact value (e.g. email) the crashreport should contain, empty by default +-(NSString *) crashReportContact; +@end + + +@interface BWQuincyManager : NSObject +#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) + +#endif +{ + CrashReportStatus _serverResult; + NSInteger _statusCode; + + NSMutableString *_contentOfProperty; + + id _delegate; + + NSString *_submissionURL; + NSString *_companyName; + NSString *_appIdentifier; + BOOL _autoSubmitCrashReport; + + NSString *_crashFile; + + BWQuincyUI *_quincyUI; +} + +- (NSString*) modelVersion; + ++ (BWQuincyManager *)sharedQuincyManager; + +// submission URL defines where to send the crash reports to (required) +@property (nonatomic, retain) NSString *submissionURL; + +// defines the company name to be shown in the crash reporting dialog +@property (nonatomic, retain) NSString *companyName; + +// delegate is required +@property (nonatomic, assign) id delegate; + +// if YES, the crash report will be submitted without asking the user +// if NO, the user will be asked if the crash report can be submitted (default) +@property (nonatomic, assign, getter=isAutoSubmitCrashReport) BOOL autoSubmitCrashReport; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// settings + +// If you want to use HockeyApp instead of your own server, this is required +@property (nonatomic, retain) NSString *appIdentifier; + + +- (void) cancelReport; +- (void) sendReportCrash:(NSString*)crashContent + description:(NSString*)description; + +- (NSString *) applicationName; +- (NSString *) applicationVersionString; +- (NSString *) applicationVersion; + +@end diff --git a/native/frameworks/Quincy.framework/Versions/A/Quincy b/native/frameworks/Quincy.framework/Versions/A/Quincy new file mode 100755 index 0000000000000000000000000000000000000000..5d86784926c2118bdf855f4ac5af5622f0f9d179 GIT binary patch literal 133096 zcmeFa3s_v`xi-8=AW{_})~Q$<9W`m=Apu)a#E=dN=%A3GNtCJ)CNK$90wXghQL{^$ zP??TXX=_?~a7(||mY(S$7Oe-=XsB&1_Mq(6R@!YVX|2T`l)A0+-Ouyh@32^bB>(>Q z^&d~Hm&RR zSNL`lcJ5^cg=bEy3+Zyxy1HnrV^cIO^7`8pHbMW38p5&1kv_8NPx35>@mJRrZi$53 zmCwv?=4MrM%IQS-JR>sS2c)l|tu;EGVQ>0^TZ~zdK7>mh%yrb&wQYKTL*3SJtf@7$ zHGDvQ^wQtoa*YSet-l#*g|M!!AsTCMZl&1){&wDEEdR&?vV3lTn!bjXP&6tD^yYW# zzZuIeI`)B%ll7(jZ-2OURZ)$FBtZQ!F+y*6Y`o8x;WBCO-u-y9d zVqIOhscv(-RD~X1fAu?!yiaz~a0owG{YpQZK!>TTD=%ALrY7dg_OC+9Jp3q##jo^R zIqK>fx3x5ybHiu%muFSr?_lYwtJ~5x-HH!PUmnx5l>UUf{YmpV*~SH|t7{3>MYnC; z)YeiLiM1b)-(vb3qm6L4KgqA3VFK(I+U{DKqcLy!<$csB=g~&E+aJU>NHyrMwWXmY zI-5T~{bkdi@Wb#IX>W_Q&E{_@{rTxnxW^wu3^0AoTSHsI(v-dV4SLci+}rov4sH3_ z!OH8ehW?B| z%o|3W{$_r)KE$IpeSt3I?Vyvr(xA{Y5)44bA4y z`HFFrHUd8ke~qD7i2i4e2YK{2$@B^L_+y9xrmy+Nkj#{65q@xg4V&E) zk+(iZ>8~FRLk{5w_m|G9>U;e~zNKQPlbu5N!Q0EG==3V%!2C{p$N013fiQRaGE8v# zTgG{r^XuxWD%V_AQC?}+RKrw>5XslS-*9Q(ZeLiUIraGDr{xlw;RM2+;f9V_Xj4nL zE)t406)ay~aIs8NH?_52A1%1Nqq((V+j5<_L{e~NxINm8&R&CeB}5cqLj}WIDZ+qQl+zh1>ba>ix4#Sn&UhrD#MO*C;ZCXAn~N0 zIRDedzS76Pb#q4SreA)raxr9i_&8ZdJ5Kh|dNZmPRCG<%y%lE}PQkiu(O7tEK~?jn z_E7sacV1VwHME6h&#DH&-rVG>2d4ezeGAohPR2p}UfkBvibjS~o6Y&^g6VMjFLwKP zUI0V7JEH9cEzO(cBP*xnWy>*fH*Z3q9&sWUEf27Bx-kXMwA<3q zP#0Z(uG{Ru@?G+Ibc}6oWkYY(s6F%wkbuKjdI9R>4hu4MMZ)|P| zM`!b1Rewqy&zXz>yL-QUXUx;7AD^DS;y;aHItOzf=Nc>#CBA_WeGUDqELY@^FXa1r*Sxavd$XO_m@ICnvK>XQY z{Hb8PVj|^x{X@d{1rr@0|5B2*5-x&?tP2Eo?)7hcY;*3ZyVNV=3j~uDIYtSU@VG$>DhI$2jpA+_w#v;Lq?Ne2W ztRKPCs(qP9rEpO62t=1cB$^c6d!Xp62a5VVBBXFE7=I?1*Z`;)4aTcSQofTV6$vC& zi)0jm5WdX{ckT_KFg9;qK1X@vXpVfZcEgFS5h<2L>Dh=pxGz)pY*5xB@*gPrvsBJZ zt=}CedR9b<^Hv7quSxpj)kyyaq`v}5y+xvy^e1xqR4p@`SxNcoY*-wXm&l4Fp^7$u zLL5Po#!|jDqM&(n=dbLlvMb9rl&xPE-0{)?_MOnA!lIbW5&f!U#b8x3=RJDrdxr$X zw<9qfBS_RR8eHgh5j|TKf2Jy)tVKOm#k*^yI^)TpLU#a=^8FSq49OWn%Fb64w8=f9 zpYlCpf(DQ=1jDOT&K|W+B*jZ$dh{>Y$3Z+{Cl|#0TBHN#ZR#np1t*~)0m|XOBJuPYwNNgXj zN`!lZ@dt~M#;Q2-yr)plUJb@~FV(Z(1>@a*oS``KRj@lx*6m?=w5W6B~MrOkE|C;v?l7mmpO&UJS43LXn|lbAIq)wd}1* zh~thfDW-&`38|hyyfS1YlA7v7QnQiRE%K`P35%-|cbP0+yEr?zuY9rYB_e0z2p@D( zzFnHEq)_#{$7K1@l<#JQ?7vRr$*kifU&)-;L^)~Fn=JYdX$;BIMum?vpZ79iZmB;rC ztKf<9_}3rMv*e({?g2nm{GNV=?mmTNufpygg}b@{!T4RB%JxJQ9&7@nd>`H+u}Osq z4<>etpF~okNcpyF!X!{^Ebs2bxv3@JdnB=mJu1N{p4csMpn~cZFqrK&XWg~xph_Gj zb_xVRiiG>G%kdg&^|d!?Cw>;BT~C>GrpSC zSGRGwTbX8bMEztKrFx^JprHfC-LNXXlrEh?4^4qAo2CQM=lz z**S9@`PE>IoRWbK2sVgWq8j?Xvuv`GS!+dl9~|rP*WA3Uii)(~<3E5?@hOwg6G#)r z-&4e3=ib7NkGaR+8T0#e`hTER=1KpLy5J$1(;?1HHvgAZbEW@E4R)n}x0spqw~Kxp z#hT2z+c+E25+r&lDZ0ul79ipGYrMXf)q0+~SZ(WMO(M{{?{%_7J& z5BpCMlFa(N7J1I?>etLay~cb5sv}Hj@AUdA>v9QB=9~pU`MpOWr$uXLM){qqE>Sg4 z9aw(yn8s}7_j{FRy{hvF%kL49+4B22$RWy4^^-Zj)YYYLr&_1WPv*t+@`UAApN?S6 zl-~zbaES7g`%c01lq=0*KO|DVw$Kh}e>+|p&VWWTYmG#Z%(=j%Z4!NL;)#kEVT*;b ztes>D+?Dd3XwrdHRX{Gq0pamRI2zoseM}-uuUbzsPUi{8($VG24Ak-6G6G`0-7XqQ z9S&tK<)DqR_Zft&fXC}3Bgw31MKPJvrTQ3y6GdT}(3rw*Gm!3<5i{kx#yB;@r#LmC zaQab%l;=J)T()D%q<6r0|FZG^F$Lc(3Pt4tiS3h^?2+XMYvH;IIrps0b_X=h6IEH) zZf5cpaWIZJmuU!GByzs3!@Hy?sG;4m9>99q*a|VWHF^fyg$Q|~BIWGAD+_6P7+-n> zA41*falf2QoU>92r|56D3X~pui$G2{+B`bNQ^L?9q~fX))mU*pSP1u3vcTK3RL?M8 zCH)#@cRnEH`?T7il6579c}zl6zVF-&S9ZB0xe#;B<2H#|iYbwEom9(?71@%!-0qv_ zgC(=xrzy*67Y9k-G1428MdwK@rhFw;d2nXRH(TB6iVRD@DV@}gN+-#Y&;-SPlO(Y< zMQ3bLbbnCqnxr2}v8R*tF=_WhFp%Q|xW-t#L(-A6M8Wrv*d>d;r|~^FiO9uqyn8~D z@l)u1-6(uf!S?|(JfxmR$mb!dC>)0Mr%d1~2~5xl7j}=7_h7-RqzqQ^ieYnYk+nn% zr|1+w3hi623vxmxuB!#&`&4}pmkdK#fJu6S7gh}B?s@wZD3T-&<46?4T7d1s|=L;5Fq8dL)@h=Cqx%5z^pTVE)m9M zT`Z+sJzy$8trKbumu7`>bEi1r+E5yfi3zn7fDb!XN$c@TC8VqB0;wujtDh&03WsU+ z=U;@-WYNDg!=-PNIFdPcn4FFYi&yls)%T-)4Iu3%$NhRHt-eq46YuT?q=20;9g{;b*H*t%%SM}6Qx}xd=|-lImaGO%S|eV0fY5+^LwxnqxLp-k<3|YAO4(zq46o=7CbB9ub_tSbas7); z9Xfnf=ayL=o}6|liAwn{(o&QwHgPHoc=1@PVFeOaiXy>awM?pQ$960qz&>=Uuw>Rp zw5%l$8+vslz*X_hkYSR{&_e!j#fv=^soaMuQdninq^i3g2d=05aQ3t~b8mXc`~`>i ziM&+f-G#(Uy!VPInRB;*tWxdLbRy+TsLb~U9s<&=$gb3FkjaZGdd;IE)X?Y9m`;e9KFi~caIn^ z#S)y%IZeU$Iv(FdSNObSAW`~V9o;ZP#jA&9aKrVV2FMkNTrLzHBds8D?73J*fR<*0 zRwXi5Dn)7%9Yg5qGA?2Ws_w@MuP^2Mq(qUZh7{vX$1sWz;}ilD6@8WQM-XnBCj9yC zl*vg}^JYt^Tjw&nC4_bItx(MXR)pZy$$?9+b+XSAm?jM?4OX1lDJ=-(!Ci4xa`X%$`hjcSwF6G4Kt7<93h@@uKV|t@B2AFOiod`e%zaywQY11gkYw>MdiGv{# z%Au~XB}!lZXA)Coj&M9oVX1pxu=#p4$C7CRk2Wg9@u&RDGg=k-aNU8R(<#WWfPkE(7DlV zQ8e!_VE=A~G;8;MWxRR_m!E_948-?U#h;i6O4JWe?MO-fb9c$M^S+8N2zmJ3@DogS z^iM3FO{ZUV-eYt=JK@Zx^I6eBUha`JCv!fc;JZnos1T2QV$RxzrieM~bO@2)vz7VE zl`YyXO**;gd=(a*s>0F<2&$Poj@xQwU40~Te1>LyCrev|Q-GvHwnT7LRvaGI(@5xut1#rsu zn>LA6`m~5szE;U$BIh>k>aun+k#);LH|5LIl#6yADAp_zYs&YQwAeT~(>N)Clf;HE zlqV|s%M;a~)v?iJBcT@n@!gr?CH`b0X-;%YRd%mNq*&5>Uncq8lRASgnk{HTgXYZ$ zlDwll$2I6nlETD>L5!Cen70pAVU39m(oWTRa11(C@#i6EQo){475pY>wotJ;9f2Si z|0Na?QV>-Mv-z9Q&EKko?!{Kc&%>VUgA!j={8sF_#N@0YsMa-LLtQ@ipf~>0w+N@IupG&h8%8G-5l3 zOX}_+2*mHr;y01=n6ZUx*-(^bgW?%fqvQYp*Q*+l5>r{Vs)U8!Z6-uKY6vEX#BO7Y z*=?JI?$|3m*|q1Fduo^TB^e*w{av0rM$6v-8}y+yx>$1Eoj`BL7&<7Hm|A5PMS{&F zf4W3t2FhJq%>-E>qGZm5qzynDaYFA)V126=Nr}N!kJe$rZ0jbHVvAV-i{^-EBs2|JX)KmD zaN|~qjrR_uU#F|~Yf@+l-uRjB@u>8lN(h!Z`#F<5s+u`FwRhvHkk#^4s-aT5Z-y7yg){|K`88#*DHJKGRY*JW4mTnKqfFUIu-(&hKcImZC zqpM55#M}&l>c^9#DtH6~uXN?A_=D2XP}9;G{+hc`yAWzzx)4U*eg(altBT*%3x>N6 zJu1|leypdpDkK@w#rs4fVFnm`Tc~-4hKVPS7q=T{gmoEfNm0sog$89kCVfILkj1~| zG@*Avq#d+guXWn5<&fCW?@okXbf$cNLM>rF`hvL8BubW2zD)6s#V#!I$=Rr}$QoGM zjERLK2XBDLNYp9mi+4u=68j{c4feLcdadsbq5AKG*wJz$?r&*aXNIqUXoOjnqgtR4m( z)k7|V>OJcQ6Uo&8EXJ@t!8K^I=pvPtE>>wy4q(PO`VPrfa#0v5t4ehA$sJZCCLgZN z-PJrjlLF4xv!9|pI2v(s>R+PNJ*jZl1ORC2Z5NPl2~+a zCP!Wz^D1J(2SSBzT-4TNd@0=Xy10UquhGOEHmEpqaq+ zWI{{L#4x4F*gdEqnUFKAMJ55*PtxWkOG&ICG(}R%Dc@&BZtl-jO5}3sxnsLVl)LML zGL6b|Baw5C1Y+ApQkHvr<8;7V8PE$+Zt^lS@qV!p+Svcn%J+2w9@3;IORvQ_I$MQ8 z&UMhkApb69i~M4hSoA*WG}t23j65IT^99LATxQlj6(xHWcK4_}*`=_%Q=vNoz`7Gwt$3)zr=1yLdw@G9ulrVNumwh=?#?R z*}xmUfs%!kuT8~z{cIYLWU!~ZO#?R}Df_k5cI=f^k!$UA;C@a3)s%IE}kkLw7e&{tp2%Kyy(5*H<4NN2vZGupO@FOes5XRK*qo6 z-Hi(+t{vOQ0Vv0J3QJ~vW!nAoIu;N@z-!2j79*T@}yA(?!R`Z8XC0R{4SOhNW1Sq&z9TRU)m%BY#*gJ zg03=Y{fGo4b6%7_p7iC3U9#v*@s%k3pho51Da%Vof^D<^#MERW89^k&cz(e~A~#=m z?yZr_{eaycko=uyBKx$7?BlRU{vJ>(yJ=9AGe`b*s~YoHn~s|K+ZU3w%#y!vNI){@ zT_(yxu}cIS(QU>p&?>lJ4$lr5n zmCoNXjr73!+oNjCU#D~pcm2IsuSq?y{+^P6WX={7WsTS+i<-q(qV!R11@8RSq$5H8 z&PYeX{C&_ya$x?_?N7Hv|EXOob7guzC*3|}TuR7C^`6$eo&Y`XcsA2td`^GH&yEAa z@qFbF8ZZjUH6Y4uUwy)&v_=i&AqCmZ#O3aW@v``E8TQ(~eHx5(<9WvO9M+AAFXMpt zW`-|BcE65B|lmfh>33bknP102^QZ7<{Ce_68F<>$O?xszV>(`(?@}e>ABLIlWofI8VH1 zbxA-nN5b#d;z|}(-n z)1rQg7EOyr;tyVnNf7s&$`g0V8P?eNF2_E~jJw(?-%tqYl*b2jt`0&8t7CkpeA<80 zA#%Z+@_j|L^#TBXx;J$$@(f9;`?g9Q+y`)4h+VxJOT}V9!dF9Ze3?vkXT=fpav92Zl zntQDzNzbJov6w1GIiRGm9F(*8p7WH;eb%Ko3z}Q*@!jtLN%@-U5v7*31}4noj*0AG z{Mg08L=aeo4Xi{E3yvz;#0uuiNq$1j6G`!%@~svp68Vn3(wNc7-TT3XXnvPN<30xm zM#CbUV>CPkVEYPt$=`Ig(PwAPtTusXim-HvS3j5Xy{x;@^6+fY4^=O-&4%{jh%F_iH97rdEvTnLI3N!t$|MmrYsMAA69 zMVx4Sn0@sjlLXzLhLHX8dq*tdh2UDm**H0%h&f~&yGYCVP(y}1V~9!efFUD@lt~fG zP=^c&oi1V(nj(H2_k)5--A>n0BbY4xn27F6mu)cjtVd9NIBQ++8SWn4s||v%Vxj`h+xgGd>Nn|6~L{q`LR}1#kqD z(x~=LOr9h)lvgo;FYN`sCTOFGv~NtH^#2=UeMOJhVS>h(Zk|%X_<&8mWY(XqPIrc! zTqG@M9(;HK;sb~GYV<`N+x6x#y5NJ-yUf6@{VcAxHg%xXNhH3zQR>A!$Di_j1YS*b z)tc&xC}baUWEU@qqdsfHVM;-*!?_h=n9Sh$qfR&pRLyw!gKYSjIcWyiK3UKc=ZpEd_P1V zx-_xkM2w%<0FhLUVJ03$s?c4kaUFV=CPXjqWn93+8@T?)4Q<(}HW$RfxIQEwLX0rO z<%%pYaxRt5DXBJF15u@TqRYTgL??h%ruWFJlG|~qwd4+X!L6gElE8R(p>!?mFG*!_os9aBi1-IU`1=>b z@{MM{tYzY63QT5wOxmQGEnUxJV0YUn|EGy$O7?~EyreW7>~$MYtB#E4@QV1+hhD28F8#xjXx^e^26fKJ_IOtRgk9#*@zQ4S9v3<(TK*ihsd&$$S>d49#kil<2HvqBeK>aKjlk=@k4h zooUA0qaOFB#eK@ZTP41D@sRb5#HXJ7HS|UkY9_x5sph>>#CA@xPl^wy@(*Ml;cfY3 z*7ahFTaU~ELy9@(;bv( z@bonQN>cYGWN{90@In>l?mA5zCbLRS>?Yy*X9qSBXCP$1Y=c1S0yFdZ6?~PFwPew} z%K(Ye)mjw#Oe=bAvBVMIU8oryzZ4o;N-9oJ{2yk#MJf62)f$q@;ueFnfPfl5CR)p| z1edZ3A{~QR96Tr|5IH^F1<=a9eYMHPC)A~RDu7QI=v6LKJ3|nQr030Uie5^ z?%l)X#?gn>-9ScecNSFkf6aL9l{S#f`IN4me5*7L3|ooPlr}f)0VMQ75p7A*h3me2 z@hpF*`X~~erzDxJ3CJ`lGz!vfOen^Nvm};81^niGN&VWfK{d=BHQdiuEfY@`B11Gv z_WxdDNoIXl#~$qgA@wqS*WptVR9f>Gb^$;13zpZk_Is>$!YA_+Dz;fbnxgy0C!X%l z`sNCW7SGh}s~8e@NcAu{=1Dt2%dI(Z^A{B(m}arg;e#FKK@dE*fSXCk>Is^H-Nlj< zyT^daXOw#;MGKRIxm}2AoPeMz{s&d_C* zR#!lDrm$M4ewdkSP2?X_>tyD^FkGcJdrP?FaaV$Z)#}K28@7kX2 z-B}&qHI=BtD-rd?`;L`}v#vJD_)fJ%>bv%QffB4n`f+tMss^||l1Emgb>AxeH{Y@_PKc^m zBI-KBE(49|A5u*-ML+e5^s&sW>ovi$I>qBry236K&t^Mo=6cDKvZvwjaQ3Ia3_abS zKH%@QX67`0nEpa@`jhJo>(h1b@`;LPQB>GHy(ni-1=8Gos_9LgT7|O6lxq(&hBrb6 z{R}+%{;Vkky>k|y;w`iTPIQr7Gjac09B0>X?5)9_rG@9geaETTDyhIQ_@{*sEmP64 z5WO9uiSI!fjk;dqPcF(3i2o+#o6{OZ6O3rEWXujpuS^^#r-{;qR)0kF zlfG6J7yYq3t@wM%U!rtU!o~63M)51^v2@7jKWg>=IkCs+7hA_|Mt{&Ker{UvcB6Qr zRlL|Je#$7;Tg6ut82+i7@Gt`WM6MIFWUVjy**u0yd_`S>s6mM?@ZfLa)B>LwtyKKg>nJ)Vq!KzcBv( z0MjOZj{x6Q7fyBfv5@(s6mxVM|^;mwCqX z-7*;`i(UfENZOx7J~L@+m^66)*D6t8@{MUf7a2e2x%?!Go-%5;rPYdlZX_L)OkX0& zWn^4-p*uzsm*nALD-43&!@0Z4FvL|QxA=qDPOCWg&*ga30U=fKam+S7C^01Rv7Hy4 z)KOlQyd_(n)kzjroPs{^>;nAw{cy)|7{f+_XRpr3o_5CdA1P@BreZ0XZ?xG5$BVskq-P( z@%8~kvQ{G5xxF8!x@)_0`yfs)bf@DSkaLIQ-7zhSdt{MVwsXbelXAN!B>`sHm?#Sb zlbKz?N5(UQ$?}XKN}wy4tPEm_6O51K?tEO+TG89p88|Ap`yMrv$94ltgO5zi*U4Hc zK>8#K9_5Izpg%ajx+jPYibSA1Uh%-W!`1PZ%i@nGDjq0HmS-XA>cK?j4&5IM#1y(> zWts#$%a?dZMsWVd{)%%`74bLVa1{N8(i6vRs21xBgrTrFReU~OO(T1ys zFO6ruQkHtW>Y}Y9yI+W% zgU4tmiZfv;gXSM5{xx4t9}9N>Dz;Fcu>B4C;lv^+R7%gAGTm-Q)agcFgQhWB8UIIR z9R5?WEJV_g>oGu!o%n%?Lt^g7X=VJ0igRE1(=q7U6Ca+A{;k^8U!AxfqN)ogI>syG z&xuLYm{e!HR+%VY><{i7N7)CmUv;`-?=Md@Wn$=>C>1x|!?E|3?R@;GvYo%5`f&cP zU&SuNuu*m9{@^3~=aV;wgt8S zT!h$h(ELcu&X@8l=`*oEU`ElwK9BQF*5Z6RT&G4Jxl=SfaR0)K967|6K;1oqO|L3ohBeoj6LI zNKY9u9SZB)zzk3Sp=!UBZfzRM$cN`+F2}>U)zGbk?r%{emD%Nq8#2pwK7sQ8!&KFL ziM(Sp_ugUTsq&FOXRb}2_wqn)?2^j(-pZXXQr^&g31ej z6Dvm4*N9^Lc|2ciPS6%6u(u|u>R%zgvpxktBQfnzW^c@)>E**?y{RRmXCW&~eyZO; z*Y9WadrZH7so&4(_iyxjT)%&(-!JI*AM|@dzyGM;FX{Ko`aP-Nf7S25>-VerJ*D5T z>-QV_{ic39XG?nK>31f+v1_;t0sH;m#RQpLbf%o2V9uAwdA2z}P0ru=j_NFu^GS1l zlAOO_&V6z|X3iJN`H#$bj+_sh^S8+Pcg=a0oDZ1u3_1U-Isez0IPW#*|CIAx=KLRW z-f7NXk@FUF{%1L_H|P81e7!mUy_^Tl`ETWXg*pGVoEMt&|B&;==KL3Oo^Q^dmh)rH z`H$t?G3P&!^S^&v%i(c3pTIdDljIGBFl!srddppFx`+G0ScGkQbdmJ_~l z$!~Ao^$WxcG4~Ru9U-A; z*l7$Ki^lNgP)AFwI@F36L$o`g_V&;=2d|)sgWGEmz7jvdKGXB&=9X}ETVwO)<_7(#^m63FX>4wgpH0W_v|rU6Yclaf%UT;R3vY|U zVPm5am1zD&1py_YXoHC#8XZU@Gp;$Dc7qaXYmbE+%Ov9)qDXFeINH$O9D#-Sjzn6R z5-C3?(jG=)kS6hPaa;S=5ORbx$HEAh_EBTiN}L-*Egkr|@OC6gOVGg^A8b)Iv_-a| zyh82i^2gg7HpA;WBvy*O1>s$EK~#QTYx24Odh_5)0wS<=Y!K zL`6yKG%A&Ur9@u>pHd`FbX#jfQ+r!$^UYy*!b(uW;sAAwR}Zzcg&I+L{Dps|RgkZ4 z4@H}_I$VWfm71d!H*|zr*0-(Gmjj_DtUxRzd8mMCr-)GMRi!h0OV&c|4NV$ZRoj+} z(Hu>)b@DRrvAzmM~i$%CizLF)2^C3zIvI+g&s5q1G+owVQ3CY}33V9BBzPpsv?8 zG<4u~8Ljxm__UT3xiyg1xK0YILHbZTlG4bYZmLGQkSW_juEKkQ+HOJ@i=wSJ zMo}2H8l9$4w8peX%^4DaR)<0=L(9~fG{tM>=*E%__R5k5DS9mmO$Yn3s~Sw-4n?nb zc`pygLd`8`1nE*)o$l7^3)x0TYu#cywfNRP8eJDgW>G-HmVKUalI@(*;w|R=mm7)#nFD|w*OrJYj+fdB5X+_-)-9clQlCeg0k+x{8 zwz{e$)~4M6Wm4IQVFu#|Ml+Lj3`LDP4u#t>BE%}94WURls(rn}76HzlWR+d5*E7%1D(#Z@MH)p+q#8&uG~L;B^32MI)x zp;Ls?Cac;SwM(})u4DIX>xf=q(okt>wmWMJF=Y8fvLPnZ}w$15E?-hbdY_ zZAKUwD3LE)Etrc!E$BCmGkYo2-JKSF2z{#2yj#T1K5^Y7zngyMS zW*W_?-C2$1F1SWkkA>#md^B)pN<~ZsK~>xwMAm1t42#hcQ7qh zyM3j{=}Zl4uT5k%8eFIWE!wu@c2i@OTUy)N<=r<|H8o>lBE2Q;R1Mje5OF2m&ez_d z>k(;P>qG5Z!m$$R?lUd0;&C@z6VGaSiBeh*6U6~t%q-ImXr)p~yP7 z0WZU>xOVeudC`?D3#!9gH-+2N0xUIU!Gp;X`Z2tD2SX{wJWQ;F)#%zWOuJ@LhFs!& zQ%8E0CISq{W$tmm#Ayg&N^jw8iUwn6XwE5Sm@<>PnM~EkY;$pmQ->ARjp25TzgQ=O zO^>^ETqm#HjI^U%V%wa$^!lJ~=DY>n^eWt`Ly}z6RGq9(4_pP*rKf$T?goAT(uT_P zSZ~%EYnnHi;Ri*4f&C^ny7WkY6Bhv`j=aMVYZLS4L1^o{ol3Cm!%7Hi?u z!NNY&!l^?CJSq0H;nvXg;ftHwqcJnpVY=1Aw+oE2^!gV|Qcf?XBC#5j^`9BvrW3oq zIVQFt&`XrpY?3!XA80vf}e zB~DAYbxTY-+9u;i46cv`s3}SHjmF*4s>Ogrn>v)M7BsQgn($31Q7F%#Ix?Z4b>$&d zBbQ&Ho201P+qR0QCfIA`lP&s z^~&b(O(l(O5*MyDur$=RA?b1T&h=2#v^v^)v4j_im^zp+!$LHIRUNSyx~`;=*SWPtyO8bG(ghspj2KSCtIZ-8To%oU-wNcihL;blvT!3hof%iOia5lD zOoh$}HC1gI+Kd1+A(~+rLo1vkkUrhf7=3#!ZCuOPCzmE+Emjs-qSS%7PDJZ}@Td!Z<)Yfo)cpFBa<*ngZ zf#d3->(F7t?aSrjRHf?fz;Kw_bvcSQT2S5`!qgeXh+vvh)KRg^5M3Yd-D;H)9n)^X zl0X4mhwOXToYfbb3p>|vcvkv2`exWdS1CCu%ck5WQAPS=%7EXFhjD!ZEMe0pyFbv)UE0Y9TQPMuW4#h9kOEqU#ZCaJb4u0C*0>37*nJ19LJgTd(+`Q<|0aLvVXk-A*0mzB0( z2C)K!4a*md?En9|DEZ$^2VB2p+`t^xv5hmpJ*p;T0nTaDEM5!5t z?Iw=1K5+V)bw=~_bKZH*nscu7EF#=%h*^}~!Rd7f^j568YQ{BIIRntoXw))Lr zBaK0ZxEkHk^qWqyDeT8)ryo~&k*L2l9BvFZ`eSW=%)VQ&!Re1Rh5ZrZ#aYwl$Eq!g zjl~wfT$##l9vYedrskFwKm1@Ah=IhKVAUM;V`xT&E_Vtd?Rbr8EL?zhnHHeqar9V@ zQ@pnP8uY89C9USg+fq8*daP+{YpLnLQhfH1wg_JCsV@(Az2j3h6?tOWUI*SM)wBM8QSow@#)rKcB$8_!_f*sYnW~@ zm$MC>LA>skx;5*r#^rbEc1omnk>e7yB5640R5H&*BfvBOr-2r950~kfPPdoQ(ij;o z9iV~bIOj(`_R1A zIdUIS4$(tAPW~s2UfT66dM#qmXY|J8Tn>-kIS}26&k*(YWQr0Ft5>u0sM{Cdmv}Sp zkTTNv_X2}=8Nc$Zf*w|%RMPnpKI7E;9OY@hF1-_w|E7O8{&pWkZw2(msrT;((QAZW z?cIm=cRTb(srT`N_|vDg`i;L$Xb^hLlHcdxulGwv@0$nlmx(r(KVbCa8IrmD$$Pf@ zzhd-`f$zEW)qIawKyWgbmV0vGG zUh(%2t@k77O;T^sLE`;8^d_jcd;}!pUc;Y?OIB4_tOSGxk?8R?6KK&0Jy1lhPFaKeqH&_1< ze=-jGsdqW*Ob<`GMehN8Y9BTJ^pRUpoJH>ih$kO2dh!M!J!a867Ga%_8@&%uZ8m@B zL2s0LuN_41TIdZtVf@M1s)voYa;J{v(!X~htbd=;>&W)#dBy|Dw~T|6KQa3EP<^&^ z$~f3LYV;1)o@5-1P*42pVbe(}av28?$DfRYhvQGiLF*5H6>1JoyoYL^A7i@husB;e zl*4oMF!ZE-u0IStX`e;ZyWCr^7n^#O@{;y>-P5N0^Kq_+Cx4>%0zPXFp(pJ`+UH8@ zm8mm)S$~)8TyT4Obzs&9GEc9izR#;qF7eAey@GnD9Yjy&=_2ZJ%{CpsE8o&S+YcjN zX`gk65wEn*TIwBtkn~CWTy+@nO8YE041dx-PdbF2)XNv3H=lYp<6IA0FVzRG+kOt6 z>Ytf*aT3+j^++y$-+TUAsT{_dR=*v7Ep}zh_o!HoP%%XoS^tb-pq$7s9(8KD_ zlJ47~|E*sb{e!iq??Uh2sdqHprqe+#?e00~bsdJk%uJ;7F!auW-qqAQ*!;d8dRHBW zzgwYKei;6~0KKxq@b@F=EvFv-xZ51IyxD)O*vT8NOJNeqCZ^?wILX{>6y!JaYQ3>TC;4BGFn zQGTvR{w>Nc^vEBWL;fh`Hh$}WALV5pKR=>;ok#v_%CGat|4Dg^NA7PoF>UwAmr~yC zkzX)}JV?2>d``aJ_`k>F=UVD}^ENn7jxBT&JtR7x{Ti>_L5pL6O z{n`4nX#Lsxu;p*d+m^2_Pg{N#dzmj=9@d}DujMxW`>1a*!(_{`?VD8FsjP|K$9DV= zwqqN9!yIz?>2f*p(+gX}<=4~YxZWerYnxqu73EKP^t^k)oI~!|@+F=_{GbP) zC%0(z7BYUzZFn2yUi60FNxc!`Jho4Zwtd=h*Wn>@+4z%>``Bv7+m9M)e|lrWtu=iv z{r41nKEZLq_LpIf*Tah{_F(HVK>7PUivd#^B~K_rfbJmG~5Kat`8@Py|v{4|Ci|3Tx&`d`ZM!a2h88D2U^c#z>$3_pebC6)|){0wiH z!#Lh3%+oyCw35b5c`Q=CjOlGN232$W51jzOv<;`h|}L&?L7NQ zBeLVy*r(DedgA|#;gUxg1LfmBK5hNDg$Fs8aZvKQ@p_}QoaGQ9(mVgMt+Y+9;i0*Z+qmEYm9u0C*H_fBOmtYM{11xPUg#| zv-fi9vmaV{9yf@dq8~eM4pV*y+llROdRZt<*29Jk8`Pq zF1E{@8$oca^R&xOhM&&xO`h<6hQDVH`yqy}V0gr1Kg{q;<_I5Q_!V=6_c6R~j__KB zw=n!xPy96uzlGu6aigB$bM@;chR-#wL>Mmb7?i{7zlY%;Ww>|T>t*=o=CB`NxNS$? zacPj@w%vHcB~8+FAC(i=Vf%IVXI(;^_TDv%g~a~!K@<<`&l=;z-(ex9cM%O1Ja zvvRv0%DCRdG)c7MPZj&IjmP@6>9^@!!gR~KIpvUdgvzmvD6bB6AH5uxtjw-Y=i+u< zV#DqF!g6~ZY`I<6T0Y{5?^zH3wTHjy;kixLlfk!pc!`JCdiYikPk8tzJltNt+gR`O zgg@!wc3o)oU-N_?%XOd)KhwiYJX~Jz;y$kOaC;qKb$UJF_jvfd9{#9@|HQ-Xdf)oD z*9(^0b*<&OT+doA@Ah#Y_PWB#0-kVtonpi7^^fISJ^Gyj=y5@#v3w__&Av%flCLwVn+6J$#9W7khZQhhOgDJ3Rb$ z5C61>-|OLH9{#$AJKTS=xj4$h^E~_%4?o|-JGrj2{_MKYa=Y%ce9)u+Jr94-!=Lr= z7d-rL9{zT&hi!azU2gfgo^X5JZo@C~gkR#}>pXnBhxdE&R7eL--q~KdWc&-*~+)?KhHa2JkX5 zeH~p)d5~C0^b_S8O%7RS$`R0Stiy>t#6Ds_ae!D$wDAs-4-vbF^~7P~Bg7_RgxE>6 zFmjYR+v!X}TQe z8AyNI@_UuxbCqAlEyiT7^kp-AuJ(U2!{BDBmBP@e)1gQCvGlD}zdL8@Tb4hE-1>7kU!TtOt{_@_i_1dh>~q&y$xPfryPd?36Z?r@ zCq7L4G4XlgYs3?np9_fB5D%5Ui4ijiSFi?tv?3=-w~LFBc>dZO$*iabJ;?JMD3#2#WVv5(kK z93T!7hls<(5#lIuj5to5AWjmei1M(Pq$8Us?_|yg&nNncOM$r7UrS}& zkFRWCEf9NK3ku;}_)?(oeDXYUiCn^`=9%ybpzv|>QSuS;A#(Y9VDZ;a-bda;-bEfE zZz8WHuOSbR7n3g~_mk(5XOmAcf0NALIQba)2>CGiAo&1!A9*i%7kMXn4a+0Q@|hx^ zBu)^=iDSf3;s|k=I7A#I4iNi^eZ*d353!5bNsJJii1ozmOw%qVQ5jNi6F{k#G4fIJ zVe%pJ0rGzGUh*FDPVxwOJ$Ws8kUT(MNWPRjpFEG;A)i8eak^jvsO^k=lzfDIh>_p&Bg7_RJ+YQpLktoF#A0G0 zaVgPH%qQj%vxyFI04V)`9Ed&71!KffAoj=?jK0P6{}G_%V;CsqJVf~*?823>Q(BvA4- zfiKC|IOSuMk5WED`7q@}ln+uqKzTpqeU$f7-a~m8<(-sAC~u;?p7L7CYbXy=9-zFK z@rC)mEnfs+0R@`tXxpov&dtR>bEgTw%_m{>?$N*n^pI59x%C-xD0 zfhg()W5k{u)4sZZQofx)Dc=a?O_bMDUQ2lm9;o#V)cU4;jPg;+M<^ere2DTv$_FU#r@W8yUdnqY@1neu@(ATkl-E;UOL-0D zLCOP^7gJtH`BKXLl;=~PM|lr!L`goofRfKnptOr7%Ihhwr94P^fbwF>ms0MhJfHGx z${ot5jx+uzfD-RGP~shq< z13;1Y0Hu9)0kyvXrTm7-2g&=%`^ZPhM~KcsV?V|43ChRG^BJB;^aCZHrNlyFF)=_4 z5^IRH#Cl>AF+%Jlb`g7seZ&Fc5OIV!Mw}o{5wlU4lFoeMQerVNNUS9`5j%-J#6IEx zafmoV93xH;r-<2aW&FgY#A0HQSW9dob`pDteZ&Fc5OIV!Mw}o{5wlNV{KTciVq%b3 zOKc)`5_^b!!~x?HOO`-lU?A>s&ej5tA@B4(e+_=!u2#l#@7me@q>B=!*d zhy%nS;s|k!I6<5uX5%7W^G{q#EG7ntwZtZ3C$WdvM;ssy5l4t)#0laQF&l%U#!p;I zEG7nkay=0w)&OC;V1Vf0!3p8nMEOBFbgcz>Ev7b0h94E>j(@Hx0!~n6L*hTCo4im?T4j%MU zdt!iCPwXQ06Nic8M8{8iVt`mr>>~CPhl%4v=QP?A1H^jb5K!u`kJv-(1j>AteYy#s zpnQ}#!ti{AOaAhR#lQ@3`Fmi&A<74d1H^t}AF-F%L+m1U5+lSWVm+~zSVJs6gYgrW z68*${VjeM@=n$viN76k>oFK~IF^hbRI7;k=os?fau?8sRHcmcB?5BM2yc3R%^v)a4 z7=Fue#$ZN2|4kk@m@$~uJFjr^sNv)LGe$Ea;~DbLU+DMO`2B_BS^XJ>;|qr8563RA2WxmtGzSEBP9e@1mnI~=f`n4BuH_y*-T9VgZE(*x2vm9SF=#O%og!9ENF5!#uiAW$0;cM{a=3+lg`vA_x z-g2C(eV&})5D9sf)d&_aE_TxY~gp2*S5FEnA-u@kX6z5`Zxr9slhq2Ei zhj6iXbFq({roqmqaW3|jH^wRe}d*t@xw-!fzWB<=0*2c+#GmqWPPySUif-xc^H z?Jc+V?)EPBZZ7ur_Xm!iZ_3YdYhS3bPsi`(VsC$^;4Io(ZtW)_mqWP3@8)7}f6w4* z+FNezWj{|2YwzY_Z-4h-JMAsE_Wf!-ZSUq{Z+{=*DcW0Z?MFTKZZ7tD0i$p{`nj|} z%dLIHWAEl--$naU+FNez$36CLF820!8R}_o`Qh5z-*32`_Ld*6z5ShsZ`0oLr@(hH z?E==HpEBWV@a5)`AN%_dXJZj5SO*dlN0RxBPJJ2Ui=13EEq3?Q2}|N&4Mf;#$mU= zi#)u+!;>EVT@U}Yhv#53#qGZg{GC|iPkhH@0$J7HV>HbC&2^(Cz#F0O<~zY>TkqK4 zhqwUanzV1rZw24w@+a{v#%J6$-b;DAxzxA)U5PWW&nx~cxAy(?DO~K`Tn1kY1&@%gLWy0xLt#~()mRozjT2I@%x!Bv^2g$_#wB*NfYhUQGcXLhuHAdkK+S}h1 zvG(rx)@U0ho#o-x9^UTZpYia$9{!4lpM?E!cYLcne2a(oc=*>n{AmyWyN92M{c(4E z87Q+x`Qotx;fIj$mcXO%#$7ugA z+FO3O_CKQi33(=d%MaJy{w~Nlw71;aUoIy&1SI`#F6n>gMh$jKX>YmcOa8Gpr-!w7 zbFp7Z`^#x>`Qh4MN&80HTW;;kJn_4^#Q#a!M`>@lwdZz4I{$7i_Ftp@-L$vd+Mni$ z-_6DT$F%CbX& z@2-DIkDH6V{rj`;(B5)uk7bh{!o}Xr#oqqi+D~Y2xwUu4H(UJn@7-Rcz2%2%Z~sni zKJL#+d$8QvFY%<`%_aTz@9UP(-g0YysPx;v%d4Wj<<{OEpXA@oCH_}9iEg94<<|aC z@!P)x{1WXgxAvUw(&Mq^k3rwhvJrR&{GF%+`+Eg{BVXEYlup3?M+uLRpD!Gfll?t| z8glzP2d%Z>}Ks6z-Qv97h@XFnI&H z-T%Cu-0o+7kKFE0K1*)*8($;0`-dmuex$@__X95>xBGkVC%5}`-zT^GZz=M;fF{>D z3w5daA+IL4`(4|}?f%u>9l70K z`7pWNFZmw1y}o~g++Lrbh;||V?e+Hg$yZ-zIxm{20C%5av zH^}XJ?-=a8Nc!yh?E-SU9=n*_uCKO}+x61d$?f{*esa5>d6nF*Pu{i2#AnwV4dizH z@F8-$9{3FTvXCi{hso`H{d;md|E9?8eEPOiOni1eTt;r^w@Pw5Uxmo+{Ii|h&L?-1 z+xg)May#Dtjogmkr{Vsul&>9+OND1n@84|^p5-|9cL#44jy*H``-Qg&$KHYcJ;G0e zpMq;1|3^)Ed|kqipB}HDB)8-BpU5NqMnA_7ee^*)UcZanj@K2!(MIfeeI2y$rvD z-2Tq!z2r3v{~7rR`4o94`DvI(B)whaYsiPmZy@g{{}}lY`5y8C@?VkkCOM0?-ZUfeSP0YZm-wBNp7#d_klwlJ-{5JhGUo7 z9M5|A^M=o};hFDLd+xS5&f5)7hnINx5!7rqlB-OJ27_sHN!b>3ivl*Kk)CsUf@4~O~6-ywZPYa^}s3MFz}ziap3E~N#K70 z$AE7Dhk*YA=41W!CU62g1?&Pk^PJ-%zzkp?a2{|FI3G9y#QU|5>j7o~n_#~HI0}9g zuoxVFn|54)DCkqXj)(|7aUg98elsHMu!+JsOi8aIsv6nbV93@T?^Zc|Y)(|7aUg98e zlsHMu!~CuB6KjZmly@%5HAsvQ`-pOVC*c#sr9k{`;c;>uDA-BtCyo#&iGvFr=XB^z z5({x%B)k{=3}8QTm^extC&~t$=;aap#6n_#SVOEQMu=U+USdCSkT^^nC5{s(iSqI$ zji2Zz77_!*8e%;$LhK^;68njR#9`tnahy0w#5;>k{6s&okQgA!{(z*jo){r^5qpXK z#6jXPag;btoFvMImgb-6Cl(R|#2TVp_ltgn*hTCm_7ew*!^BbIIB}Bb;JRGnC;Ewn z!~n5|SWk=)yNJESe&Qf;m^extCr%O_v{#Lv=qCn;d3HBC*kvUd``i~kIy^tc^5wNH_B(?b2L83;&U863-QUv=VW}|fzKj* z&cf$xd?pdc%lP~SpB(U0@i`5jCHVZm8Nc5t*boi3M++L-+QZulwlv3@IyMzFG=*Zz z8d{pet+9d|!>x^N?FE;2G`BWvyR11@&=}s@R!|*kC|JH+{!>i^j^`cd%eT(-Ue&y* zJ=DIfpgeqIxTP%;ZZD{9Xlo3Ew>RGyZj^W4GoEt9v8*B5bi=mx>uycGYq+sF6bnaPw#zqZaCvxB#}y`};7i^&I?E1FYi@0h%_eUQhgw?N z8fFWMhC}TQO{?2O(Wa`lEf+VpgiB^ui>>R}w6!@JZEkDba7EQzN@bBqWh1Jtd2@5P zeRic7YP%{FjcJ@H_t_(CX$v*Z(3&+>;tNq;iB^Pk%^IILnpT<3OH(LXgND}Jxt?S#WL(Q#l+0xt)iXqzBd`4rT_SivcYAf1a3zI#YeM6|VA>1-| zQldz(3Dq>0G=`%M?adKM&a9U(piYii#|0 z4Q&laBcX=yhAS#dZs-WLNYys3fwUwPi?ugz>Ok|KHO^^=6|Iej;cm8appVUM9j%S4 z(FsBg=ybD37n-gSN!@JTTq-R4b;v>Mmc!GXO$)Uc-hwu~apq|Ae>RqRdjImKS-N^O z94lwsvlf2T-CJi%c|+UQNT_w&Ttx-{tEV+)jRK`P-PdP}qB&aD5o^eAJ9tEuC#(&XhfteQM~BdzQP_vjZIIDihVnz{O;_+CG;K3Kgc?S@SVwd< z#lnmT+=RdactWm^h2|?qr;phmz&z)V4PkjY^eOdZ;603x4FiG4V8YG z@vp+diMcCDSAB`Af0ww%Jsr3)T9r+YmQk}5S`Mv()uv*iu60sPODMJ(E0(A%ywtoN%a#gTW6 zUALrlwZFN66M92m-yQDAa*nk`*P}IKf0o&Iz$@LV!|_mSWRz1&Yg2d(Cx8E((MCZc zS_<1a&uvC`g?2i$F8wx)R->(GZbVibuy2(GniEeyA=X+aym zRt4JUoZU;RmCx0)q#Uz)3RL9ahINtLtsWgS+bq+u<&cr8?qf@8yig<0;IbNsF{Md( zm?zY0QjW>!8;_EAL2EeDG$wNp-9aoG32WnX9zLC>T!>LR*7&?cJRWTuQ(9Xz9;R+% zGWc7K^D=2MIWNxzlwnhI0T$EbC{uY|G||x*o*!y#372CA7io>BaONGimB+-n+)*cX zbr;{_*GHofOv78-v}dReH-|9jDcgXWqAul$SVwuJbv45#YB<4lp~h%zLs@9L4LJG- zPB6OqBaJ~!o**&YWG2CCnP|I!b#1hj(`+Xg49;CPe=%Yi401;21Vc?t!Pa%5HQ``O zG`g0qqwMa36(SMGC^EItb|=^#iZ`|d zBcaDOVlpbZ8uRBzPJfA5OBN6EiqT%gY{eA$flx;)b7BmxM?+F{Y{r?qMsw4?(UB62 zb)`rGt3a`EW1=Hm9*oI!5FMGXpon6on2upR4{IxHbGS+TN7?(*W|W%=hBF~ey-^%K z-^*k<;(<^kksWxYChZbd1Vj-W+XP1MS+PZ6;3}BcWIO>X_~+$6zScNgNm#F0HQ1A;M1K&L=!8=&T0gzWHTsT-wOdYLunRXO<_lGGq9n zOUhf*#z+&&Qet&*H5w5tFa%q|5zKRtUgFf8Xlq0>7G0ZYzsO09Z`7|teHr@u$3fOU z$FR#fjW70-t~v{-UWpG zj!*0}(jrIe{a&BkzkEd8Zo@Aw8_WxPAGo{RaPhXsk^sJDagvR%0z2YF5j7U3%*+ z_qtP>H|O3-&Lzc^p9?M;`Z(X-PR<2ZEK_prSnC3jyV#w!i{HDu=sm29*u8r@ORHFw zNNvdGjx?pct(DTTw*W=dMJ}dZ>{|5s*0VF#E^`(=X!QSParq)vRxfHL_yVi5|9OkW zvNMzsT6(=?tOg_(2OE(KZfW=yxBlHrv-gF$#ENxvEUC6*p>Ijuyl_~sPF^mm4aHlq z&{^HuAzLl2;ngYPIFKa=!?Vqm6j0pFC#H70)57?2(J0>Z`spYq z))#GnM!ES{e8+O+OJrlZrIvoj;z+Nxj>TbKRvF6`@AWJTKaxOZjN(`-Yuux)Q>((G zoNBRJ%-5Snr(Ydjj;hwTjiOl5@-6q3BJ0P{5z38Xg&j6>+E6GkZXWH5_bqheVRSKh z5X)-Z(v^+92p86})WEAwwemiby7()|lDdra$aESlIVj7!X3-df)AUpI%x|J7GP2 zacE;Sfg+Z&tV>{JQ=Xh)b4M7WTEP}X$b8oz{j{{X8H*$iwup?z>%!~SMIQwN2yu`I zBNnY|+PDyp_2v0^O;C4=eCyt_qaZs2;-fJd;T|qlSp#j{3PAFT{kqtiI&8#eK8doo z2b)_rVCgl3FQ*;Uz?O-h8_#IC)GO3Zzrcb(xjBJ&&u3l;3oXF3NcJJxJEHL@v?Ytl z3|o(+{3EQ!bx3c*>5k0qWs|_e~gNs89Cdp zbewHY5q}@-9K%;ISRYtkj|cy!T_1q-e<_UpF>5f$HsF)B;cg@>vt>Nd;5Z9mPo$pqlYgm^L0vea!B(bjVUCt@6n;MeJ|ccX zlK!X+T42SAqo$NwwjQeE8Ts%_8AQJf<|B>*ln>sZ37t@=NK z-&JrRjx9r8KEtdqi19xr1X<;4H~dy1O^N51p^1U;vSpmyhO;i3Vk zqqy7DcjDMG%zV#wNZEQ+!!O$x;@C1w!)5Dhwv1Z%ErAT;*fOxqk%)xT?Kda>@T8x1 zl|dX^hIwyLyMR&Y(F?!%kVhO#2C~hW0}~7`UbN)?K=mTqulK^T3I^!C-}LF^wI3Xh z>a2jl&qB4orRxNz3hy2^nwJXsqFf7q-ROH`{zLME65reM`j$ehe6Ie)5K^cs{`6$H@_%_eG&OCorDCOB;|q0LY=6a85K=%&hyKF`IsMvaz|5~ zMCK|;O35rbFwt>lOgN9soWC|mrQdwtuMr@ZWq7t{+$49noc&pHmv9*!@OXV!`F&v*)qr@(j$jHkeO3XG?~cnXZCz<3Ibr@(j$ zjHkeO3XG?~e{KqF9nP=)kVl!z-R}2dV<@?}w7~8D3FhRW^8ax8%gw#&-1$vdzBSaF z@o7l@cE9kA)Gp_hB;q8NNn4 zH*x0hg1ooz5o&tMt~f>EcunOiRm$y;X(*dY^A1;+=F>h|Gn8yN>0QQXAvQIq+#T!5 zWuof_hLT|PUY{e*TL))799;Qw;SJS}gZLqOrK6&EHi;ywIeU2SWC@m(Ea{1zQIB>; zy_&P`qEfeacOybHsgBqu6GPsk65v_4YPzT-44MNE8K-2xa|5T`?oDAv0!g?BLfnR- zWU-{8R{^%IgQX86U#z}$4?eJEct6y#N*B2e1#Tk0XY)`pF_LT;mOT06)sWNsb9|>% zI+)zt=e>r-SZL=44!YfcDm-tbdAz?9*OOEU8QXBsefCYdLgzTL6b04|^dTt=yxqch z!VR2oYYw_QexqD0a9hObxEnYwPH*{~n#Jju7KmH0?g~A6x?iqyoSxqA@w@Lee80pr z?^!wI2E1fozZ*D~4D6%AofDbDfxT{EKU8u9d!b668`#IpO|IONtl6Dx*p*D|Om6P= zesd>Tk4ij8rG@uHxVkk*C5D$%s6U2#B!*uT7R2pJb!Xf@FH(=Vfs+!qPa$q@;+VVn zsJrrrn=B-!$vC7}z6*U&+moA0kwd#s8HaDP^K0H+Ls%x{?i@$>%nq zdU-#49~hWX*QF!lZleW!3 zkhWS8H!&{H(as;8CJ{Cu+44n7Y8Y|*ZdOSwV?A+7x!X#)Riw~PrMQSwJwjt4yiSB2 zasvZy%^}Ila!N|)rT4G1se8YRf5!d}bw=X4fi6kk?}0|7&_c!PBf@ev~q}dzQzWdw1l=^3F3y-F4%3U=h$-uBxAI;em7ctwh`{A_u+Yx{pK#u_X z5ubgW_BCgdbEhD3J>C6FsrOm8?;LrcF|!=ainmx4ly>gC{f}+y`frA$bSihhv=V}Q zgOqApU@y7(ly~NKhCl2!c+-u54U*l6-KGySChJp^liCYx^emB8zg zI=!z#tCT+by*3FD*?{`J-`(*VU5!d!oj9G`vU!Lh6?X9jB7`Styx)EHbrr-i8`$Sf zlUPB-PZ{mRK7Z4C1>7njVX;U+2L-lrvK{yVg=l39?DdAqVM*7Yi;%s3A%k!$eDTPZ z|6{c26j{(3H7Hw~J>H*ML2rnlJ$^xt(@o=Rw0P66#cuDz*7I+~^KQTAd*Jp;;0@>W zt9i(ZOQFQ`?4J(jm6AsDmM4#>8^mN_SF&cOH_-^}+3^*mKoSEJgq}Eo#*yKoM`h#K z(98bz)iO+_P=C2Kmp zJS*>V+W(~I`PY~jAI(obke9s7`w6zMCFidc*W`kNo-N-a!vZq6fllyz9Xv^l-8iyC z8IkDx#CZRrct0*hq0of8G@n6V?ro>9hNH>Aku95#ptt^5Vb|x7Mv^+rkDlH;m`2Et zj0EbDA;{A*a#-Y(ehsDjnid&bNNatQsE5%A3gw_^BB_c-A63LP^3b6qx5+r-UuS59 zC3&CojUy!WzYk}v;ptqx70GPM+$D&?*QBJS3m)xI08PQ$*3N-RPlVr3x8U3Rs0q*( z!T~9S1DS>J=_c}TTSfkQtDwJZY!e2y{O_~QMt^$omym1e>?@FKZIY)7ub_md zmow;pyj9aCBWPh@&|kC8CTM!`2mL8HOVVYhU)MBSOs|n*8pU@~3eZ(vv&bCgY>Cau z$Klko`B3t;3-wg4(uVG)ykF+s3`-f!p$?1|e#L0v$=BUyC@vW+ zNZOM6Biwu;CHp#42c%+r3zo@-gULXj3>8m`de>(bLT?|K?q+c)+*TU22F|*er0)u#et2~c9HIcNXBbs` ze+Oq@^N+fgQ_83PQnUf}lCa!rrm7kGqrcobyg0M7G(+PNxA(X8z8IYF0+7KztO(Ui zhLb1U9haz%94kyS?%O^uik)z~uY!NK_m;F2?-JDw6N_b~ZeqlJbh5j7#9ev9{nTS* zt1QJV44vumWX&;^xg+K818M^EWQi3ec!~pr~9%1+tgR^?IupS{qIyU?Bh5)su}QpeX~aD zDaKl}|HrVD@!&v`Q`nI$8;c+j3BF7OBQ8Y}mus022Y#(SIrw1d_#%A!N{1PDX85t; zY?g4aj({1#klS#Q4gZVp)&!!GY@+Kig{ueS>B$;6Q}I2@#N2*A9jV`#9Pa|h*1^S6 zbL{#Ei(9Rv*K$`@%-p`m=OQ|HriQmRGrE63HKAblt3sT5URr#H*poQP^_0p|4mNC? zPX`tS*h#Vhi^a|sChs}CcVlYcw%u^SoEa(N{V{AYAlb6{1i1^lHsQOULVIdX%0gIA zV#wWm0_DJl#ayVn+yyTZkr?m2euB@a*@pYCVXeE2j*OlbZgGJogab<3>iu0 zJq~}l*AqvLpNrjwA~%5wmj5@m;YVK>L-Yhr$cn>}p3XO_AanCbGBKQN@RBvBPU|rK+?Pac5wF89ETrLNLJMTL*BKyJRFBNr~eM| za10(u84vy5k1^^S6~ZxkaG`PcYbawqy)W~-?|c02{~Sx*t~ZvF0TbwhU~@ay)G{~F z%^XB-nAJ4szdu>CFWIm+nb?!uyxY4MA-Oe2+#T02LAC1MVlCgKzymK+jvdqQ@(Qf$ zVcHunZeX{Jaz<{-A?^p(QuIUh+;|?2Cc6O+sc7&NxRCV2y}X%COc)k#k@ct^ZfM`rjrb_u4V?cam#rF z7)CFLHCX4H=$z=ZJ+@2dhEWV{o0qOnJ-a@45A^a?pTmJ6 zme}M1Be0dl^bpI)irqn6bcOVbbkdBP_Fsi_dR^Sln4LrE)MG5&M)H| zm5D2Gou3mLYi*{i!{(cmeu1)Hhajm+!+`s&)kez2E^m%F4Y)ggKslyNqe0B40^ar3 z@o_l1iGG`MCZpWME;iIiJrt3>fMR#^9u$Jf-fJZc4?@2q{tl@>u;hD(t=p^Ow%_me zmvG~H09p;Ei`y*;&~VD_BeSgo>?OAtPU-m(QqPXMdy#er=JWgcJNW&{oACX7Ate2> z@Rq+NPjVt`P`Q}mo!1E6EA(s3zrc2(-w?VRs{=_^3?%St3_8%s0?)>z6R|)&VO_{K zb)g_>W<<}(I_%Rj|9MU(EiZ7=f(1e|4f)m$jMb1Nj0n{hB(+5BV&+_rqPW+`C`(3$ z5o?knNLC#4y^s31eo7s!I8lR1y+SyUj4U-AAoMXGH5pwqqq1X!S>ToVJoGyu1;wh! zf&wnr%)?k!@~qsyrrh42tI&G%NL(kAi7oaekpB{J;-ve$1dNuH)!d{I>m5taHcp1T z1Mnw4-blwJRSb7p$2Z~VJ}0v$Gy@1vN>qZ*Y;O>tlx~v{-b&c(Nd3Dg2I;aAyO(99 z5#MZ(KPf>%9@;+}WOAEK#h#XV%?q4*c!ilt(9U0UdB>dW#@JXNuu(?(xkeD9ylpXs zeVyFYH&=8&Zyil^>A@coZvj6203yWl+PxLA_?om9iQ{g+h(l9@WKKopxrG$)bTz~M zV5Vqs@ulRgaPA2l?WsAYA_sb2{9`85%HuBfBezvShSUk0WT*Dr6R%WjB3tLAMAU*g4Sg6jnu9o_c=!0?SD(F1MEzseEbmZyVb=}PL5ytKASx7YpOkv; z1y$JdYjDXbst9|DiX1qMZ#!t&Hl3+eFM^LqbT8KcZFJ{|K7$zK*$(16^beIR$F=lL zXJpWoX?2V&Hd zv9}BkLkpdpbWtPjH_Lpu(&G~*C?#&g6gPn>YO%W#Q$DP_VT5ta4dlxL^)c+bBv*3f z9oxy+;ZD|I7IBhZ3%f4EHzyd}%st-I{VVLPy6lg>6i6pB8b|LJzh~yu=rF;LnZmVT zaC_Sp7`^M!G=3W_lD!362P<>xJHzTb#cjYACc4uicO^8%Dl0Wjt{hHo_L7NHNo-x$ zU^P33jwgD$7g!yCTb-$6jp~T<2^}$|ERbpF*BKQpg0~nQ@0y!e_z4r0oNNev#KiA8xS5@z^Tn?P1;plGrIF4E#KiA^!Yw;~cx4HVkXwUQq;p9u zAqLK2RcPyAKHr#O`?Yl#v#%nvv|?UH!aF|l*3G%(Trd`(#QWzbx#SgR$QvErWS_gd zf0PRGMHG4Sf{59nMgu7WtTWhuVTsaipIjWUqAMr@$tC+@qG_ePpX0&5fqNl+r}*?4 z9iPG%t4f4H_6LzZPi3Z0ci_#Z0&il8q!6AMFF_j7Bou-T7SPuv|hvrsi0a(6_lPy+DUi5X6r zp?tpg>NU*!<6AZ#XTlVA{dj_Q*s|B=HXJ`ZZy{aWhND~)eW02%eiDn_hCv}}*#Xj~ z9^7tXxBFtO3Nle1b&v;%2x5D(rrTSK&}1wH#nzL{)Mn&MKA0TuexLi}X?O1q+A}sE zgXP_1#g-c8JcV61g6_e~Kk1nd-gse$R{$?thx1O~28Mq6c%DU|2Q$NBSzx0T!=-J~ z%y4=*G4X!!PF!wXSQO~PSIxmU3a@6@x*HFj<)|lb6fQpkyP6@goTBRqQh2;Na1s-s zzzNKL0(d>+>cAc@oz(QXHG7ja2YYJv$tKc^4ELqqN1D7*c*|i1vHy+26(Vv!2m8Au z+PXX~ufZgrZs*wSxd@fJ1@} zl6Y|l1G(NQ=@@a1>jjhzw`QliqXhQ&M=WIPIJ4AoqK#FuUMY2d0xv9e*oC@qsl!55 zO_#TYp3-Qz7mpa6kPfI5U_s6Xx7i z{)AzK(M>RQI**&wk$UGbIfm&vL<*0n@=TZ^Cd>%GcDXfpy8u;rx4$Z9C5&kp_Pf~$ zBh5-u78SgYK%>!V!*=KGq)jQLY#HoB-vhZ?RW!>rd%e%V z3)7XQ4li2<4gz~k&Y054q_4S%OhA9upTv4=&7ow?Q7xSVFKSaNrE}0WrRbSXy8RMe zTRQh78+Lm?#c*C?T+NiQL&@E5C++u9gueMXCoP2yo0Q9LxNjNkL@r3)r1zsPB7b(z z$;h9(t+sn$e{uP9Exf$V{F&g3&@L*FS%>~m3U6RWaXgzbAJopl?O%$8-qeO6-{S1O z{{6lzl9%EB6cYKWbiTY^c!RoQF6C_&7fceX<%Q0h=wy`kR>NuQpiB(Scv{A{w(^=6 z2H!s&{N@$=?$h@~&oBe9d&ZsPWSK@F-iCoYi4> z7$DmSvc_}j3P@mEXmo?G74qVe=f3zGc%+hS(e2Xfb6^+V6%vo@m4$Q9KQm5;t5z@_ zbug{($NBdAQwjQ8?DG5#S<$|Bc0gy}Y1&hI&jQ; zuY@@wFVla?r^{8e=S%8U2(0ph^r{C@s> z_~rzHX=dI}dRks!`eVz?7pZN7!F1=O@WRh#{j=8VfJD;wmsqn5ASL{0?7wRPvKTT`uZex*_GIJV9 zBD?Sd!nD*Ez_-QekaV<#T@6e+4$BWpA$^sMuczK0`z(1I_Tz2bEt~g2Hr{l+K|ElP za!5Qhs$}K{=cDcLae5nAStq+$bKTyXz=Iz?kW&k14>xqYYVeABKljH$_IgBg(FGw^d*#hER6X&hMaFFWd}AVf zH<7DnF?@XBZwvqY6fvYgZ>6P>6w+iOd#~qY&k4+^W>T)NhO;-1Jgka8oeBc4 z^Bi~kq^*ZqEQhl9<^2SOLsIv0ku-;?=#z99&dF`uAKNm>4og9vxlpeL#m-_Q&hE5@{SUJ3Red2Sn2L=haBHe;mRRoYmWB| zbFRm&+R6pX3$Mq4@51YqSlq&)Ct%ai?{)c(n2x1rcg6uFxV+?Dvz+DLLUm-mwm z9Ll{Ccoo?*bXWIPGm!*=6NTGulH@ui!xy|c_)glRb+#=FmV5C!>8lg)4(Y3Qr{k?@ zJg@StX~ptAS-z*pcZqzL%J+2no+00}w8o3{9b@ zry81~PhV?j25@?kp&88S-wujj26p?p{|32UgNxI zeQC43@nPfKIPZX{2zPYgrftU@lpbhHtP6L9aG9vnhEoNXnkx#-d7$$);@~ix-Hx+f z>5&RX+aR;T3B_>&?CL~34DpL_y?mzRW?tYBZE9`i)de_IT`quFQs=w!L9RdI0ecS4 z5fo=!bD($fKG+&>F>1u-wl(28-5A1a(hG|#kP=x}AK1I^z*!NBH5z^4C4E^1{Kyr- zxP}=ila^oM#Bhs3xJeuvVu+?a2|n6^K$p~|uV0|TCG!IJEm~6d;G+74CDW>$P+UFxdz^{I5K2&L|~uW(x8@%C6nc{xt#U0c?I8*#%Mp=nuLI9|?DhKrTgwc>F9 zXdCW*U>qC#7d-Gng(h5$kYPEWH`FW(w2w>x6x)2hm$MW}pn>*@|%KMf|mGlkc%C^>(DA$&JWnW~DCvIxf| zr>l`kY>B47%yop?)`XWf+xXkM4E-aaMila;jksl5uE{{{RW24L>w(o*E~AjEkU9{( z)GbiFUZ)1CBK>UHH#c>l!HS`AXo?}-ap5#>C`SvE^O9HO2k%3ca%c*XNm$3WmAEj+ zR%tEz6^$%`lG480nJRplZyL=|Y^^U3>~-)a{!AcdglQ5*7A^`wBh|LXHi);RK9eS) z`P9ZPRu{%CttcZ+^ETQP5`B!?w#DGx5i!&bV=HP@H0k{;aH~K9@h<23Xjbx}r{hC5jM z@0nIrS)OTAN&V%9t~5i2O{L$jr7KaY;%t~iV(OtXfp(>}Ni3DDWS10bjEkM6D}9M{ zPvwmd&F4Yd4^69Dx$>UM@(i~~bj>IRu`4%e<)qWknbuk+|Lk7E5!*DFL=DG`0xVOY z*#zQNoXl&)V8a}-xq+L~V{zPNQxT6!t2q;&1xuH*3-Hy_R5uJBdj-(6s!)4t`R&Xz zsr{mAICgKWY3<$9s@@9-YC_^s%L2OHCh2o=p9``y5Niyzhhx$|1#FRZ%u%$??Kqh9 zPLwUVa7YRfTXo}DUK46`*pknjy9RRD`ls6Ih4uBd3&SB~_kz}Nq^ZJL^H^)UGqyGm9^ zQ7qBOF=ATtX3DjOsdQpf$mTcX%$zx`>K&7W&Wa}z4rP#>6)7pzt!QgvtsE~@l=wnq z4Y_+NE30c4P&1WoZ+ah$3@kDGsN-spEZ&9)h zqe%~hSl1FwM4E8>4NjJgEkXQa?IDop=B%4(kWx+v2LJb{cno6N%6-~7%RICe(qjtKu3@H>r|KF6^V>z~S3#86jh6I0~ zmf}FnI^tS$HAw5()@RWV7n&hdl)dU`BZjgZoj!Wo?NzfXW>?KgQB!@1ICYMR7(E71 zv{K%TIE-RYZkI%n0VrS9VH~)P)*mEm7n8A~J+T^{-=gXY(|m$(9=(0G39$-6-*#u! z?Xyx=nL$_K(ZpsdIDHRy`X1ohuS-LlJV9Ax%*Kn;2zTj;(xNbQnUken)$OxWw*M{- z{p!xnPl^7aO09;G}`G zr9WpP%Scu$MTaRR8C52yryONPv$jc#txb+LTX`FnEvn!MmR)(18hK|b(^3!TiHr?U zYUTVE)Wt?LQ?_x^d{~4bdIv8Zd$6Syb5b^eDJ{gKHjvPC5w1V!NXT516}mpufos4j z*a>C&z+~4nCjOczgA=wdCJv+3X1k1vZIwpTIjUtf6Z~}3fwm%*AZR)q$s>wU`_X~q zZa-|8qHlmxd>!Ybb01q_Vf1N?Lf(qRKy#NR!qWeYpAs&E=p4K9ZKEzTk-q8L)8 zZlt@(*q944V&O=$Q)O%(!-9hI@Z-3#@WY_)-fV2Mr@9WCRkY|hjw;(}Ju=a(7F2*s z@}*)9*w4q9;m0Ji_+j$5#!yx^VEM#o(i*!T*VHU+o{uRn3Y|vura9oN39nln?npaD z(T{bk!vqKdcZ^geM+xMGbRt?kSQDG-|FhOsHU zI+0qbAV=yZMU{lUf3AOI3PnP={vx6iEmT=MdCTlyW2j!?CNp@ns}8;i7snJir*14@ zl0S`eaBrUfj^9>1r$;ipe25dUrOikWWMq7!L@r}oG1UpCW}8)0EA6CE)v-nI9sv82eg85K3bbfAXea%71g|6#L zOkLvagKL4Q;jXEu3(P{888M_ISKk_EG*J^HPHiL{iiJx|MPD5*!OCw5rswUklD2TT zDcn>NkCtF8y=G0gqa@xEE@?O6QtMhQ5mo7LC$} zwXIZX=5^Jn2Y>4cKDim%tiN|eBNd!uKF}IoU(pmLAJ$i~jwFSiDmMCh z{|aQ%_SJX}cCj*U*ZArQG>2)k)o~%{c0v)@&3E zIo0u9&Yf{ZxQVL01p5)W{l<0->84R_&?L*<BOigKwQwmRi9n62CyS9&6_02j5GYyHGpzKVwaTRx1n;9YnpU{1k|z?_50e*pJR z!IZ$vfSChR4O0t~{V7@v6<}PLnT^K$8<2)}Kf;DJk?55(S@YM6fKQwm|(Sq&`c;>4K z{P6i%JlkyMbIpex=UU?|?i=vzm$wto_<@~#$Um6ne)kG<=MNd^{tZ0e!Lp>!`45}> zqwMp0@cb@uHT*nO58mWM|2y!E4bIf_|Hd=-`S_vxFYwG45cuKq$xA#Ha(PW9L7^%JO##6U_1rJQ(!y=##3NC1;$fg zJOzG-Qh;~P|0B$=U_OQU>T599VV+t8a~tNLe-7hf9@z(T=poFHFsIuL^LfmrlB+O( zhIt6{oIUXW1(?5tc^>8)FyDoF9p+NVyb@*x%-t{xVOn6Gg!vlG379i5S0c>zV9GHs{41=31c4i1x?w&6^9;;hn3rJw9_EKI!!VUyC@_u;+c= z{7eST5AW5Efd1PyQ+{Yi{wA2o;9dN2+GKvX2Jt$Fcx*3LeH_Qf@Z@}p5jjx3$W%y_j`bJuT$&>eg)>7K3A+U z?u^5akp^5-x(0L`kn2)gV7Ru_1H<*HKUe%bkh}+AxK?!-hU-;7g5jFgm5{}Ct9dY7 zyZR6e*RMXJ_&AXASuVMj#r&c?)-$eYvF`Bs5NL*fMBR%XF?q!luHMRT)K#o$eHc9X zUFO+-GfuqY-hTLeChPeglqZH$d%cnCF9(-^f0laY9rgU!azL8A{&L{wr+fB?Yei2W zyk3|*(0`dMfA-z?6W#OYKWR zF92N&GaWQ#9Y>ix4a4WNz$>DroL>i|J>yye`c@dO8@*rYIiTk$T?u-r(p8{WfL;qz z4f>-%>belL)nhSet9LEvt;$mmdM9Yc8CmV@ReLl)P6u>hd{%+}n9?E8{JaN-?K1Q1 z81js5@>?xv&yf&p3t6{T{k~~WNspAk4q=e)yV10nq-&>|c9n9Rn@pQZ`#qr9=1Q4B zBC^e7`5gky_K{)L-fG%y(nV+}*jAFiv&^(lwC5cMY*ShHN|3N@LrJ&emo~O8m)w=E z&&NR9I=rXS^cR#_g5QKqgZ`rDJ*FQZJp`Il^g!m9!Je{vL02l> zejo0sSGs7C<2;~r-v>>*O~145N4}}OgTlgoiS|V`>AW4PGi8E0wxi&%zo3r2pjkd> zKLDD22{H$hSyIi z_A35~;%>#yD(+MKk|OUaq5rQb4k*5&_$|eMRQ$2xFBJbx@d}IwDCb>@H!99l{D5LW z@k5G5?U?7oAAjyjErP!&sNpXW>T(Mm-qS&I?q_|3Ph2nC>dc|7B z8JeDzii;IjC`J@FDQ;KXt+-$Dkm6CrlZwNN=M)P%OukJ~oS|5$xL9$8VnlJ1;&#Q| ziu)B0DIQfksW_~7PO%`S;VaHitW;dAxI!_axJhxl;%>$LiiZ@BDxOpvR&+W|xOs~C ziUo>Aip7eP6{jecD3&TtSDc|ZOL4a19K}k-D#dEWg^G(6YZdDimn*JNT&38g*rFIw zY*&mcZcyB$*s0j1*sZu-u~%`Y;x5JAihC6ID(+L3NU>OPvf>oQ62(%*>54NHXDQBB zoTFH&SfyC4xKMGiVy$Am;&R0mimMcx6k8M{itUPV#SMy^6gw5W6uT9-EA}exRNSSw zTXB!#Ud4Ti`xW~X4=VO69#R}o98^4_cvSJ2;&H_jiYFC^6i+F7io=Q{if0thDxOnx zFb8LUm8Y1mSfE&>Sgbf%af)JzVyWVE#TkmT6juQ!Bb?>HcLHmH*8&#<-vz7$Qja-` zTq`KXb0?5|n}FntsC$bd*AU2ea;x!wLh-obF~y^bM-&GY2NVw}_A4G#>{HyYxKDAf z;vU7_in|ndD)uUFSL{~oQtVXRq_{ybuGp>^QEX9cQe36DLUFlby<)B6V#S4uRf?60 zvlVA4&QP4LSgKf}I7M-?VzFY8Vu515VxFR-cuw>0tl}BP5yfFePw|xEkm5Zv0agSEVM7$X0SjpPtj34 zt9)k^hZQ}=A;pu5#}$t$9#I@rJfzsK*r&K(aj)VY#p9~SF~uW_gNla~`xW~X_bcvI z+@rWlai`*T#cst;#Z8KF#dgIO#U{lSipv#i6&EX3D^@AaQJk$fLvgxdiQ*K+g&#Nh zUJbky`BkM@srGa9dA8y#wV$ES(-lkAzC@p=C{9-UVtp=BEKvJ=ea=&K)c)L)MxV2a zXMil9BlR*} zU#QR3idAY~sn2s1XRG}zeV(B>UF}QtxkPb_+E3Q!V#Ol0FVN?F#XPll^!c2Y&$B@0 z`x$*6Q5;r#PoGaI7Iho`0^p_am#>(o_KrTE)ADv!%i9@!9#I@tdrzNFDGsUqNqs({ zcwFs|>GM&=BWgdW&jX5w)V^Pz4=VPl{eFGkr?^+`_vrI(#a(K@Q=fYkx2t`(K6fd0 zs{JN?-k=y)`*wYfD7L75lRmFfT%q>M^|@ZLO4GMeagO3_#aW6o6sIeeDwZftQCtkX z4*9qOSO|sChP&y^iCKl7??1UH*`tW zvCH}j`z9QlKqmf`%ql5qFDaSTdui{4s-a8wOx!cE6VAPt9D7GE9cP*I!kQfOSpu;1 zD$vanCpeLw6(2%bb;|I?{pjm}rApJj6s8k~^b&kon*7`gy$48s(g5Kf0+J@brOD6z z(g%R#Ck+t(J|DlO$J=TblgbbAHCk2W|NWeDW<#e(p;jQGU{vza4g@ zsgI?}&%J9;`AJ*;RUk-{-_qpg{&xOElb)o>gJ?KaFqYraY z^zmC-^ufOUUCPfre9J%NVc~Ql~%1`=2{M`S4O8H4&h@bBW^eI2-qoBJGEI-z7 ziPB48VQK2dcMARhjU?L&(g4Y8WTVjJw>0^Ss<7{`{G=^^iI3mXe1g70QDp>9xT(tiOu zJAZp2kfN91%hHlQ^U=QK8^3(7gFK}$y)Y?$rO97gjdv}SpS0z#^6^`m{Cp?mDdi_^ z`G(|os%XecqQJ1m`+VYQ;cYJ^5TIDBg`AZ zcr4?2mRS&A#SErP+`CleGu^Yo&{-@H+kDIvu;krA{O;zy}May)~UOdW*u3sG|T^brCGLjSbor7R+?q3{iKinGatR* z&=OYZBPf>>oPybk)r$3sO^R{FF2$XSdle5V4k{j3Jf(O>F|WgfSFBj7I9st=v0kxB zF|OF9xKnYj;z7kh#p8;n6wfH;#WZ}yQpMSd)r$3sO^R{FF2$XSdle5V4k{j3Jf-+< zWb}q zy1cO^6rb4`X~l^Z<&WYZm}p12IT-2w);P{3T^Hr)n&o9>{8u(wq)xHbLov$MWlGnB zf6J@GkA@>W1F3vD4xtU>87HrV<(x|mry60*Y>c%$vaw_BBacMaw5@GzTHW$k_|Zsg zeN$s|qNRLZqBYV~E(h1G3pcgm+?JTnZ`o>buI6D;YOPj!xb{q(nYJzzDQ~@F&Yk7y zBY@gMIC7>e>fHKJmR(7X{g&c&Z~jBYNL^m&AGyx>KgNa#Bp@%#dD7U4yRJCBU)BM5V`W*vTW z0hWy8*Dj7X7neOXY-MI!^B-%;vTA=>OOC3J^P235Y`LqM|FHR7nm48oYRlye`8YEv zbt^(H79@E3bhlg#W1N;|O2eZ6-+B~i9Q75QhDA7_tToQFH?@nr$kW|XxY4fLg9~G(-z-D+%lZ1~ zrg2t(s#C=gX>ulcjJ@>Iy5(g!gib7cJayIgTj$$mmCY%`S={Ayazwg4p1)kquv;JP zSQ{%}Ag5XLP_;5S;*PfE<^=6n`I5T(!qIi%c*n+!YJ#}>&l~d>IXGn>V3*~baX48H zH+wo+mXrVBvMeWmbDmM8p-j%h?y~%69DJ7Lmhde@Pmf#a1lQVt7lvEcwBT^HwJK1b$81<|xMkDE zNE43dOso#B#+h?CJTKT1j$laY1cTz#oM>wdMxxQRiS~AtmFZLI079CBDI>jvRB>tVnnbCWE=$eisL9XutoxBaZas7D9f5`=BmL@4}*+CH6QYbsLl(LqPhBgqInxrg3jTtjihj4@q_SoYii4U78y3)~XPm zv1&7qdm|YNM+Rg@sVEC&qde3Xm7!|nhxY+gkCve4(JHhWtwrn5dh`-{6}^VuLhqq{ z=s3EJuAr;v8oG{dpquD#bPL_a2xCkzgIUbsFf7C2n8!Mtj8kwrw&5Jy3b(@#;Q@Fs z9*Ui~99QD;xCVQ%4^PCC@D%(Qo`GlL*?2B~1~0(R;wAWbyd3`vZ@{nN?f66d3H}uC z!C&C7@d11gAHpZ`FZenkgdr@ElUO7paU_YP5HrajnZ!=ANgL9Zv?HBJXHrDElOCj) z^d)1-IN~AWNiFn$giIq(khx?5d4Vh=8_BEWHS#ujhrCa=k)320`HXx;_L6VOA>5Pv zM1CeG$!YQjIY%y%%j6m(W5O9Z6UAs59iwNGm}JJtn3xpC!elYoOfJ)k>BMwqdNBQ% z{>%VoC{xK)G1ZKhsb!`zPck!@XPCvzbIcOv1!e`anrUFxFl(80%m(J)%*)JX<_+de z=3V9kW;?T!`Hb1ke8uc#4loCqL(F031oJaTJ;R>^AE zBsP^zW7F9TwgsER=CXNgJGMRBfh}aau|;fmwhvp%4q`{JPWDN57W))CkA0SXgFVmL zI6Ifkwcv8NTrQ7m$+hBIb8Wb`Tsy8k*MaNEJ;Zh5I&%+mUAV4XK3BjMa^1Kht~=L* zE9QD~y|~_7AFeOgkL%A3;0AIfTq!q*E8_-p<=hZ%C^w87&W+$kauwVt&cTi5oLnX6 z;>L15&d*KcrgHV%|Giw3{bhYi270}L;qcO3=`VA5 z>RkS^RHw(`_Yalc3B6!Ufw!gxj0Wt-U!hbq%^{7bQ0n@Ybagsiept!vaR(+hcK7&jiGKWgrsevx#(xb~i62{f}1K#lxD;l7mr98ALVRkFB zAv_Ht503P0W2+X1=gSzC>L1<^(}!f(#V#m6>YOwsY9(%8`KuH zL+w!q)Db;|I-$dZFH^4-Ka}+MkY~m2^Dy(Mj}k z`UU-p?xp+b0s1Zdo*tq{>96#6`UgEvuh5(H);BfM!P&9 zvOEw3U6TSJwKBJBBCJv519pP)6u3Md5JE{^t;;t7nqZ*`MIJ|$q$I+a zfi8cox2DqNgC98ruD2Pdw9A91y!!eF zZ+8Nkh$h|HuAI84p9X*+k*Fi+F*FTP6hx1sC(v~CB$@$4&P21&Q)o7tgXW@nXg+!x zJ%bhi;}^pJ#pt;TrK)d9f0xhgt#mt!YXZO(hrl3#Pa(9&JA)kw_G{v=;pf{TppW7I@oLbVb73MkSPnY(oEm zm8Bu;&?fXU+KgU77T|)w9)Tx*$RNWgKkCJ-T0Az~c%u zxwAFsb@V2(0PCQw>1}f>dQCbPU$b?_S{bczy6x=yf(Kn^UrW zAfTYm=lA+(2VjF<$+kSPDic3&I7tvY3%Ry2TGhicdbZz-u+`F zISni=D!ji9z=Eh%_qQ~%u77bs>HUr1_H%4Tdu}Vi>$hsz)@Fpq`U6;wBTzJs#8EgJ zJ%?k^7OWHvs<&gZw=U4fQ3K9X(BRU7kPQi(7nB)-++cTQpql2;c2sZfGNr%h!VX{+ zjs;8Jj^nTzeTp?`57vUu6^4}rykKlDn67spaXQgc-(|agnoHY)*|pveWa(j+1e}QW zfVoIqr>$rn1yhX125iJ8m?_&Yii5W7FS;gd#;M{4LI^}`T?4PvLpS5Q^@SKm%@zEcd^bMzqHr#HPC#*?>DXj=1afA>0Xf#t-8z zz{0LL9~a<4+zl7u?jWXO+!ObL^llG22v7ac9#K5K-8JKyHYlOhLf05aohQ)W0n+q2 zYO6~ElRaSE0Hup-{4SpWs6-tVyh=%bpKFZE2i{nqqX;mz%P001K#~B2z+2yGsZjFZ zHyzG#RX%TBO=aUO5cb7QRJ)wx!1P3KEykY`@aImDMRWSVLi;#;L*_XI-iw_Y za5=IF!-pX&9*#%gQP7e-4lp|{YQQ6L1?@yTH(&=IO&_KO58BiL?1JHAVErmw4HS&U z>LsZz#ESqJga)p| zi}7<3CTjK;K#|Y^G~DTP_^W$+t3v&o0UP`RUM2#Y;dH=1(nbeI72E0o$PK%aoh+q9Ehb$fyK*8c#}@kaa--UKtgEM+8eI+E5D{ z2JYA1dxz|9QqUo^+Yn)ZNUS8sFY#ARPO!-B@>Gh57=MZP;(h-QM2z?2ZyI+lj#`KJ zhd?oCRHe%=#%XY90&m0~%|-ql-YAOP32c9G%7~BPAMsHT(6Nw!M$roDpb;YDC<=+G zLa714=YXP0iCJzg$X&?Hjw%)SSA0rHM)7YFLdK`j2>iQ{2I4>P8E{@_@i}}RJ{Mt~ zzi?0Z+Y?pcEBGqD1`#sfJ4x`ELQI3C!e0x>xF#UD8o+_@B-j#|Z@$+DLX}V&L?00` z0Z?%Z&Rk%SfS?4H!i4?CjPbhy{&4y@-2k7L1#L45+Dn6UBmMV(mlw`Q&){3YzQ()| zG$$7FsL{Yk$fJh$a96sFg)YGJ9{_``%zF|^qDVAy5=F?| zs^}P6Es&cMGB=Uy!0Nml(b`(I25ZX|jO}i#0h=Le!Di3|!Dfh_B#=Z=glkQ=Fp%yN z)!t3V(Xj%NO_?7t5|fnTrHJqdDV3#ty@5uL2eF5wk#z8NEQFcz8fXyotob&x@Jex; zkASZr|EMMZHP1;N%y&1>Yhh-A=OxwNiA_BB!bNMA&KuTUmIkOD*_7AnSo>nnzkTU-fN;~wJ{6HTdiV{!*MrIe9ED3ThA{g6fr zs27Svun?erm#aot2qYQm(`biHgTeZw!GiAv7Yi0QxKxBnP1K1sJ+Mk)UGZ6T*wQEw zT=jAI$I(aW6o5Lrz%oCT(@*GU^f4t-)lLF8J(bprk)}-!M4Hy^^90>*-8L7V+qJE) zA6XzoIpaW=P!?%cmuiIF#7kS23M&ttl9vmWq}-gg5KD!UU`yhsZC(PGw~S1HSho>U z_fcdDnF`ga33L>MhRvXABdJi|dX>Hj#VIIoNaKQll<_(G_ah?dxFrT(*fX;`Z!;A-4vksF*WHCM`cmsS+2);AulXNCHuwn3l z$Sb8td2L!so&Y$L2Px8FR8C&J<9o?+DeWgK$V#$`tVX-Z8evYdPMDKyAS)qP@r%+D zP4JxN`+asBG*j_^8IvD~WGAZLw{9499v@Ca22n9=9*gMEvPqtDYN{dk<*rw<%-AyiA%Jy&r9H^JkF2l@}( zq~~R`3P%sn4k>?$n^SQ7k#~N!4AM>iv~obZO)&-LMP1Do-M!cM(m%Ir-NngseO0ZO z^*mluyffe1Y2H_-_$+lyeTNkTHoVc^HhM_AU{}-E+MzEe$ku*pG$Is(5gg4SIHFU+ zAKdI&II=@#F zOY(bzkPSD>GRRrlu@G{A#wE^^3nA5LAm@=q97p~ne>GLp8ptIv%a>|vQu`Hh71k>7 zxN8NUwU%6w;QbHex`3(34e&rhi6095kU%wYI1LLrX{gUO|0Dm;`rd{wT1 zFkPKbJlUbEK;c({Cs|Ergb*DP3V7y+&kMXkNK}1$ODMjG%AX72vVBf{{qTxPZ^$8Z z5D8@>m`Ip8^l}{&5dxxz1yThkL~bw=vEq4zKrUH_o@10itV$qQAR9=I2UCJVwiDDC z=vzV8&>7~3&*tx{ih693MCx7iLB?T+>`15%<%lS#olqTWR}UcqtQOwiNksBmLn0zk zo4_PW0S)|{$XF(!F>iwMk^rdMwU&CK&5U7<)C5OejoD3G@${joV||&hPEP%|jUF&s zOjQcd9LCI~;&+*JCIfGS{QV{He*dAHXxSh!KHr737<&k5rAwS|rbtYVko+2RvfD{X zU}H|nDxQ<%0)JAl75W+k*2bhw*j5f~x^<4kD*;Bf zZvPKC8=1;lviE&?eoz~@H32_PuX{&aU0t*ozeD?zxg_kj<%phTFA;dj^zC*W!81?@_ zhRiU=jOH^VAS0bmrbx(;af)X3&ipR70cF1pkuMgogK*Tsf%g`wfXo=E{Yjc7SXryK z?S&wdD;S!f#19OrjGIht;^n&lMH+coUf>-+9?Zsn2#Lpk%p<7z0pJcmj}w>xs$eDv zdt`*Ou_|T?SZ%jTw|L+w2f4g2r`ur1JK+;Ww@Puy#ym#D);zODDvk85)@98(lK9jCm0NkLcco zlyMEbSdJ_Zu!V2Z94nbsp)P{=SjVgkdAp%^kf^A9qGJGw=Cy7EkGZV`p@xuWUVCs- zAneB4j`R;Y-SxfEVXbHWB~mS+=>z^n(#CW=S@&C)LBC8Qy}A=BAI37uSC z*?4Z$yvM76ZG|3R0-Bu+5`^=lfZO3Ia{K&u&UBj(fGP2^#wiChH8AfnTVbs!^lQ2g z)_Qe>ka>mFurV@x$n1axLPu&6S`%*@cztV_51Eht`MeEg{{(J5l)~)a!0hr7(perv zW`On`niH`Hx{ZQv2ccW^2r2P;K)0`96Dy(HchIe5xIk-TmVVb{-$Ad9(CY{2HFCI+ zzc=+_jxaw$$nDP@Wqx9gp)C+q#d56=)I&HjqDSaq`lA3D0Wy?&y`EBcZ4Z~H7EX98 zlx_QaTn@hr5U(HVGNWApc_$m83|i|q*0@}iaJB+o3kvjAaA+Yqz*-3+OFH)p0}uu3 z%f|(IVB*J`y0MvPBXtVe*m@O4Fcv2bCx;BoM$dD7X?u=mmtmpS^XV> z>I!9|D4u*!BuLqs<}Q=2R6)na7I^AcE8Y^J$CZR0qrcFffJpzHdlc% z>7~f(kXi_lO{_dKH<-U^(Q4)<{dqNWi=Gs-N)ecdrJgZxKIRhZ$6{%$uXy&E4?J}_ zBvWPy%Y-!RUeedFEXxUukreOV=6gIo`!;7^UXGD z$FOGl)U-!d#l|(?o5rA(N?0y9{_U?;HI^iO&b=C}ZJY^HxzDAOw4esC&+LI`&hL^;5I~-`mwgC>Xt?8xJY+HI+G>yW$)~ZmBOPeB+o?G9=SPhZ> zen9Yl1q0IjdG%c${$3CP+p)Q6un#fkP*1iqaHosN9XR!XV;ea9nc();`I`)aUWc1G z*MMtRMXuebP?{dVjl2$!!H%y5>;7ks3v59Ps@*&|IyLu)~`v+$ehc4jC1!qq)*HW}mE!F5Rp&uw$Tp!uEd+z=|PO4>=gm!lWl-p zwg4P@3qnu;bnrDWEeH6LQYc4OQ#o8A8U+P|nSl8lAPvw%@x>d&j6e%q4r6ChHi!w- zl$r&3Xk%xyb5Mn-W6}`|oaqIG8z4c<%0)-Y&S#&7@{v>;Ydi}SBrF6bsCBGipJ5jW zj^iJDK;m$xdx$lQMAm?9Lf{pc1J$~z@RMJ|NGl*aGme}Cffh~{kYJcFN%9+|78DSFR_=|E9_PF8hf3+!QN#5W^b{#ImBU( za16(C92dsPxNuI+MR1W^6c^3Ka0*Vz@tlf_<>ELsr{Ur`EvMu3TmqNKC2`4|firR@ zE`>95sazVD&Sh|!oQ2EctU(+e#PT4H2;#^fjtb)FAdU%QMGz~4m=9uA5XS~_To9{+ zSQEtYL97j8T@dSoI3b7=gE%ROlY`h0#Ks^t1#wCcn}awth|_{NJ%}@cI5UVXL7WxD zu$C=|?LnL!#4Un2Cx~-{I4_7>263w(ZXLvJg1BuEw+rI-LEIsTI|lJXLEI^bI|uQ@ zLEMGMzw-DLkALIwX&(R1<3D(OhR0`le2&NGd3=G#7kT_AkN@KFB_3bq@f99lAIFTovI6`Le1Os&Dz8$cc+npU<&Aw zATAssP8~Wv6CDQ^;OfN76mU1J*%VE|p@Ewq_zj&1r5@Cw`3@!vI}nyD5|=CMS}vfv zMt$a{L7DH)&O>jf3vZv~W(09@5clMXmM1!ivf11mKs3eq(tTIq;#dPWTObCi@xTwb zP0)B1ejhiVdwN81e&fa0O6W;3l;d`=IGUCT~n>)9D_>-v561NKAqBlctVQ}#3VbM_1ND|R2dpFO~S%YM%uVvn##*<_pgQ8I~zzI+<10LH3ZWtE{K2pKO4vL^en^ST;m9Og2JRA#=#8Ws_x8 zHdFSD>{;0&*>c$`*?(kj$=;XkkbNZkSoWptknCsK1=-bb93By_4mX6Shv$Vq9Nr_m zB797EUHFXfS>dz8=Z4P@|1^AG_=)iI;n(GyJVvgUC(4uMMtO=nRh}--lxNB9@)q)3 zc}sa~d0Tn4+$WzZpCNx*zCgZEzF59QzEr+UzFfXa-XLEqUoYPve^vgv{5$y%^272U z6_%)J?G(~ob z92i*>*$}xc^1H}OQPEK;QLUmnM-@j6h$@L16g4<%R8(Em6H)V`Rz)>Lt&LhAwIS-w zsBKZ(qjp5?jQS$#+omgbY=ATXm7MHIuKnSJvVw` z^s?x+(Qie+7yU)_!RS-bXQFS%*kT@z84}}(c`9Z>%!-)TVs^zGjrl9)a?I73>oGTD zZYdZAr;sV+ib#c4k*2UKS}1ZAtrVRU-4s0)gB2ADhhmJPS}|7PRXn2bD`qNQRBTkd zrP!v}uGpd2srW>(OYxQBjN-cDrZPflP@0rxWtuWW*-_a;*;Cm^S*CO&v&e&u&c@Q{22AIm567QPkVk?+e_@DAR|yZ9=8 zEn94DwnECRjnGU@~9@M=Bb`mEl@2~Empmx+OFE6+Nt_PwM+H2>X7P;>T)a-8xb2D zYlzK??HW5UwluaZwmf!d>@+xbemr)1?2Oo1u?u1s#x9Os68lQ*x!7xQvN&~IN?fbB zE^$5Mdd2mL>lZg5t|V?yTzTBkxZ!al<3`1ej$0D9G;Uek^0<|8tK-(ht&4jt?)|tg zFQqUQgxZSTs>4hTs=}fNgay)yvdt)o-fbR&P|KNx>H{*U;x@#o_&#{U(6IsQugwfGzHf5+d}vf40hxYn%A({|Jr zYKyeJv}M`~twTFW`>1xR_AxEhKCYdvouQqjovod#ov&S@U8;RiyF&Yc_CxJQ+K;un zw7a!?v|noXYQNVW(*C49uKigj*G1@}bTK-(N3Dy~X>?j$j?SSQtDB&ETsK|!lx~i0 zp6+Sg0^LI0V%-wmQr$A$R^2w;cHIu$PTeQEUAjN@;ra-Dls-nU)Fq$Q+olQEQbTR3#q{~USlW{VW%q7c`mC4rR{N!HA<;jj@PjX%I)a2)qmnXlQ zyft}S^7iB%$vcxjN#2uuB>8CavEXU&6mwr&DYI0&9_pcQj<~*sistOYFcW`)WX!F)E=olQ+uZlNgbQ&Nv%n( zP4%TtOMNDFY3i!fhSasG>r*$RzL$C_^;ViHEk4bf);p~%tuAd^+EZzB(&nW-owgut zVcO!fC2333UP*g9ZD-oPwEbxZ)4oePly)TTa(Z|=pKeLdN$-(9IDJz3qv=!AA4{j{ zkEc&hpOOAz`iAs3)89^iH+^gR2k9TC|Cs($`tkIi(|<`nm3}%ymLboG%!tlVWbhfW z8HS9sjI4}a8GSPPWemtD$rzL|IAcggFyr}*jTvueY{}S~u`T1njE^!7W)^0;GpA(+ zGoQ$OGIM6;Q<-xz=Vd;f`FiH=%!8RHGEZin$~>KUCi9#n$`WHyT2z)ei^ih0WLx@K z23SfggDitBLoCBABP?Sr9*fuVh$Uc|V3}qKT6S1IwtQ;&%<{SA3(HrQeU|-}1D0jDba>4Sa<&x!!<(lP&X6kbt8-TOtkGHStm#<`vR=q~G3!Xy(X3-xC$dgv{hIY#*6&$ovd(2)$oezuQr4BM zYgsq4{?58>#a713S!GtaHPRYwRakjztW|A|x9Y43)>Lb{b(wX!b(OWjy4Jehy1}~9 zy2<*o^%d)D);FwgS>Lg~Yu#$yX5DVxVclu{#JbD6+q%d4rFF0MYwI`GgVyh?KUj}g zk6MpePgqY{f3^N*{oQ)Tdd_;m`lt1h^@{bH^@jCt>unphF*eR7v&n6dwrHEernTv9 zPurfgEw(*xTV`8fTWwow`a>_zrsdvAL``#}33d%1m>eWcxCue4X$$J)o+Ywdpf1p8$B zRQojhkm7SiQpIwpd$ga#DlkLu)m`$@^ z%wC=Se)gByd$advAISbL`)Ky5?9 + + + + BuildMachineOSBuild + 12D78 + CFBundleDevelopmentRegion + English + CFBundleExecutable + Quincy + CFBundleIdentifier + de.buzzworks.Quincy + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + FMWK + CFBundleSignature + ???? + DTCompiler + + DTPlatformBuild + 4H512 + DTPlatformVersion + GM + DTSDKBuild + 12D75 + DTSDKName + macosx10.8 + DTXcode + 0461 + DTXcodeBuild + 4H512 + + diff --git a/native/frameworks/Quincy.framework/Versions/Current b/native/frameworks/Quincy.framework/Versions/Current new file mode 120000 index 000000000..8c7e5a667 --- /dev/null +++ b/native/frameworks/Quincy.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file From 101605e50f8e1005d051a7221b46dfdd0df15c6d Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Wed, 3 Apr 2013 15:07:47 -0700 Subject: [PATCH 69/87] Add atom.crashMainProcess and atom.crashRenderProcess --- native/atom_cef_client.cpp | 3 +++ native/v8_extensions/native.mm | 6 +++++- src/app/atom.coffee | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/native/atom_cef_client.cpp b/native/atom_cef_client.cpp index 4427a8901..6ddf00ce1 100644 --- a/native/atom_cef_client.cpp +++ b/native/atom_cef_client.cpp @@ -87,6 +87,9 @@ bool AtomCefClient::OnProcessMessageReceived(CefRefPtr browser, else if (name == "getVersion") { GetVersion(messageId, browser); } + else if (name == "crash") { + __builtin_trap(); + } else { return false; } diff --git a/native/v8_extensions/native.mm b/native/v8_extensions/native.mm index 03c92eda0..b1ff00f3f 100644 --- a/native/v8_extensions/native.mm +++ b/native/v8_extensions/native.mm @@ -24,7 +24,7 @@ namespace v8_extensions { const char* methodNames[] = { "writeToPasteboard", "readFromPasteboard", "quit", "watchPath", "unwatchPath", "getWatchedPaths", "unwatchAllPaths", "moveToTrash", - "reload", "setWindowState", "getWindowState", "beep" + "reload", "setWindowState", "getWindowState", "beep", "crash" }; CefRefPtr nativeObject = CefV8Value::CreateObject(NULL); @@ -166,6 +166,10 @@ namespace v8_extensions { NSBeep(); } + else if (name == "crash") { + __builtin_trap(); + } + return false; } }; diff --git a/src/app/atom.coffee b/src/app/atom.coffee index cb8180113..debc9b02f 100644 --- a/src/app/atom.coffee +++ b/src/app/atom.coffee @@ -246,6 +246,12 @@ _.extend atom, getUpdateStatus: (callback) -> @sendMessageToBrowserProcess('getUpdateStatus', [], callback) + crashMainProcess: -> + @sendMessageToBrowserProcess('crash') + + crashRenderProcess: -> + $native.crash() + requireUserInitScript: -> userInitScriptPath = fsUtils.join(config.configDirPath, "user.coffee") try From 081967dc955179f955ec34ed783247093a85890a Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Wed, 3 Apr 2013 15:24:17 -0700 Subject: [PATCH 70/87] Change development version number to the sha --- atom.gyp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/atom.gyp b/atom.gyp index 300582736..ea825f03c 100644 --- a/atom.gyp +++ b/atom.gyp @@ -1,6 +1,5 @@ { 'variables': { - 'version': '2.0. Date: Wed, 3 Apr 2013 15:28:43 -0700 Subject: [PATCH 71/87] Fix version spec --- spec/app/atom-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/atom-spec.coffee b/spec/app/atom-spec.coffee index 1da757d0f..82a3d7672 100644 --- a/spec/app/atom-spec.coffee +++ b/spec/app/atom-spec.coffee @@ -249,7 +249,7 @@ describe "the `atom` global", -> versionHandler.callCount > 0 runs -> - expect(versionHandler.argsForCall[0][0]).toMatch /^\d+\.\d+\.\w+$/ + expect(versionHandler.argsForCall[0][0]).toBeDefined() describe "modal native dialogs", -> beforeEach -> From 0b9f6b14ba59470f51d54d1a2fdb56f04d706bb6 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 15:14:42 -0700 Subject: [PATCH 72/87] Put 'atom' in window title when no panes are open Previously the title would revert to 'index.html' when focus was gained and no panes were open. --- spec/app/root-view-spec.coffee | 4 ++-- src/app/root-view.coffee | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/app/root-view-spec.coffee b/spec/app/root-view-spec.coffee index 8c28d4297..d8a3eb921 100644 --- a/spec/app/root-view-spec.coffee +++ b/spec/app/root-view-spec.coffee @@ -175,10 +175,10 @@ describe "RootView", -> expect(rootView.title).toBe "#{item.getTitle()} - #{project.getPath()}" describe "when the last pane item is removed", -> - it "sets the title to the project's path", -> + it "update the title to contain the project's path", -> rootView.getActivePane().remove() expect(rootView.getActivePaneItem()).toBeUndefined() - expect(rootView.title).toBe project.getPath() + expect(rootView.title).toBe "atom -#{project.getPath()}" describe "when an inactive pane's item changes", -> it "does not update the title", -> diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee index 0a7d3a2e9..a8a2ada13 100644 --- a/src/app/root-view.coffee +++ b/src/app/root-view.coffee @@ -82,7 +82,7 @@ class RootView extends View @getActivePane().focus() false else - @setTitle(null) + @updateTitle() focusableChild = this.find("[tabindex=-1]:visible:first") if focusableChild.length focusableChild.focus() @@ -115,7 +115,7 @@ class RootView extends View if item = @getActivePaneItem() @setTitle("#{item.getTitle?() ? 'untitled'} - #{projectPath}") else - @setTitle(projectPath) + @setTitle("atom - #{projectPath}") else @setTitle('untitled') From ec2b116674eea40b6e019b4080343606ef1189fb Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 15:42:04 -0700 Subject: [PATCH 73/87] :lipstick: --- src/packages/command-panel/lib/preview-list.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/command-panel/lib/preview-list.coffee b/src/packages/command-panel/lib/preview-list.coffee index 58cb0f799..18aedf8eb 100644 --- a/src/packages/command-panel/lib/preview-list.coffee +++ b/src/packages/command-panel/lib/preview-list.coffee @@ -20,7 +20,7 @@ class PreviewList extends ScrollView @on 'core:move-down', => @selectNextOperation(); false @on 'core:move-up', => @selectPreviousOperation(); false @on 'scroll', => - @renderOperations() if @scrollBottom() >= (@prop('scrollHeight')) + @renderOperations() if @scrollBottom() >= @prop('scrollHeight') @command 'command-panel:collapse-all', => @collapseAllPaths() @command 'command-panel:expand-all', => @expandAllPaths() From a762291de90ab082bd7073bacc1e9fb6b0314230 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 15:42:12 -0700 Subject: [PATCH 74/87] Render more operations when path is collapsed Previously the command panel would shrink when a path was collapsed since operations that hadn't been rendered yet weren't properly added to the DOM. This removes the collapse/expand animation since it becomes jittery when combined with rendering new path and operation views. --- .../command-panel/lib/path-view.coffee | 34 ++++++++----------- .../spec/preview-list-spec.coffee | 11 ++++++ 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/packages/command-panel/lib/path-view.coffee b/src/packages/command-panel/lib/path-view.coffee index 814128c7e..d4727da86 100644 --- a/src/packages/command-panel/lib/path-view.coffee +++ b/src/packages/command-panel/lib/path-view.coffee @@ -17,9 +17,11 @@ class PathView extends View initialize: ({@previewList}) -> @pathDetails.on 'mousedown', => @toggle(true) @subscribe @previewList, 'command-panel:collapse-result', => - @collapse(true) if @isSelected() + if @isSelected() + @collapse() + @previewList.renderOperations() @subscribe @previewList, 'command-panel:expand-result', => - @expand(true) if @isSelected() + @expand() if @isSelected() @subscribe @previewList, 'core:confirm', => if @hasClass('selected') @toggle(true) @@ -36,30 +38,22 @@ class PathView extends View @previewList.find('.selected').removeClass('selected') @addClass('selected') - toggle: (animate) -> + toggle: -> if @hasClass('is-collapsed') - @expand(animate) + @expand() else - @collapse(animate) + @collapse() - expand: (animate=false) -> - if animate - @matches.show 100, => @removeClass 'is-collapsed' - else - @matches.show() - @removeClass 'is-collapsed' + expand: -> + @matches.show() + @removeClass 'is-collapsed' scrollTo: -> top = @previewList.scrollTop() + @offset().top - @previewList.offset().top bottom = top + @pathDetails.outerHeight() @previewList.scrollTo(top, bottom) - collapse: (animate=false) -> - if animate - @matches.hide 100, => - @addClass 'is-collapsed' - @setSelected() if @isSelected() - else - @matches.hide() - @addClass 'is-collapsed' - @setSelected() if @isSelected() + collapse: -> + @matches.hide() + @addClass 'is-collapsed' + @setSelected() if @isSelected() diff --git a/src/packages/command-panel/spec/preview-list-spec.coffee b/src/packages/command-panel/spec/preview-list-spec.coffee index ce91de176..24444fb93 100644 --- a/src/packages/command-panel/spec/preview-list-spec.coffee +++ b/src/packages/command-panel/spec/preview-list-spec.coffee @@ -43,3 +43,14 @@ describe "Preview List", -> previousOperationCount = previewList.find("li").length previewList.collapseAllPaths() expect(previewList.find("li").length).toBeGreaterThan previousOperationCount + + it "renders more operations when a preview item is collapsed", -> + waitsForPromise -> + commandPanelView.execute('X x/so/') + + runs -> + expect(previewList.prop('scrollHeight')).toBeGreaterThan previewList.height() + previousScrollHeight = previewList.prop('scrollHeight') + previousOperationCount = previewList.find("li").length + previewList.trigger 'command-panel:collapse-result' + expect(previewList.find("li").length).toBeGreaterThan previousOperationCount From b50b2231d69f7cd1b5511b49e2243b1a21c458f5 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 15:52:47 -0700 Subject: [PATCH 75/87] Render all operations when moving to bottom --- src/packages/command-panel/lib/preview-list.coffee | 2 ++ .../command-panel/spec/preview-list-spec.coffee | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/packages/command-panel/lib/preview-list.coffee b/src/packages/command-panel/lib/preview-list.coffee index 18aedf8eb..6cbdd7bf2 100644 --- a/src/packages/command-panel/lib/preview-list.coffee +++ b/src/packages/command-panel/lib/preview-list.coffee @@ -112,6 +112,8 @@ class PreviewList extends ScrollView @scrollTop(top) if top < @scrollTop() scrollToBottom: -> + @renderOperations(renderAll: true) + super() @find('.selected').removeClass('selected') diff --git a/src/packages/command-panel/spec/preview-list-spec.coffee b/src/packages/command-panel/spec/preview-list-spec.coffee index 24444fb93..f1bfafa85 100644 --- a/src/packages/command-panel/spec/preview-list-spec.coffee +++ b/src/packages/command-panel/spec/preview-list-spec.coffee @@ -54,3 +54,14 @@ describe "Preview List", -> previousOperationCount = previewList.find("li").length previewList.trigger 'command-panel:collapse-result' expect(previewList.find("li").length).toBeGreaterThan previousOperationCount + + it "renders all operations when core:move-to-bottom is triggered", -> + waitsForPromise -> + commandPanelView.execute('X x/so/') + + runs -> + expect(previewList.prop('scrollHeight')).toBeGreaterThan previewList.height() + previousScrollHeight = previewList.prop('scrollHeight') + previewList.trigger 'core:move-to-bottom' + liCount = previewList.getPathCount() + previewList.getOperations().length + expect(previewList.find("li").length).toBe liCount From 4f8d51450adeba2dc46aa8dc8cc943093c5282d4 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 16:01:17 -0700 Subject: [PATCH 76/87] Add missing space in expected string --- spec/app/root-view-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/root-view-spec.coffee b/spec/app/root-view-spec.coffee index d8a3eb921..2651a45f6 100644 --- a/spec/app/root-view-spec.coffee +++ b/spec/app/root-view-spec.coffee @@ -178,7 +178,7 @@ describe "RootView", -> it "update the title to contain the project's path", -> rootView.getActivePane().remove() expect(rootView.getActivePaneItem()).toBeUndefined() - expect(rootView.title).toBe "atom -#{project.getPath()}" + expect(rootView.title).toBe "atom - #{project.getPath()}" describe "when an inactive pane's item changes", -> it "does not update the title", -> From b4eb7d072eb2cbcb8e16bceea12cf6b221f44c8d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 17:07:23 -0700 Subject: [PATCH 77/87] Only expand folder when needed for file asserts Closes #470 --- src/packages/tree-view/spec/tree-view-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/tree-view/spec/tree-view-spec.coffee b/src/packages/tree-view/spec/tree-view-spec.coffee index d65c59fdb..a79b4f116 100644 --- a/src/packages/tree-view/spec/tree-view-spec.coffee +++ b/src/packages/tree-view/spec/tree-view-spec.coffee @@ -944,7 +944,6 @@ describe "TreeView", -> git.getPathStatus(modifiedFile) treeView.updateRoot() - treeView.root.entries.find('.directory:contains(dir1)').view().expand() treeView.root.entries.find('.directory:contains(dir2)').view().expand() afterEach -> @@ -954,6 +953,7 @@ describe "TreeView", -> describe "when a file is modified", -> it "adds a custom style", -> + treeView.root.entries.find('.directory:contains(dir1)').view().expand() expect(treeView.find('.file:contains(file1)')).toHaveClass 'modified' describe "when a directory if modified", -> From cf357376b36a53e988acbcf827811540a5670859 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 17:30:24 -0700 Subject: [PATCH 78/87] Only add wrap guide to pane-based editors --- src/packages/wrap-guide/lib/wrap-guide-view.coffee | 3 ++- src/packages/wrap-guide/spec/wrap-guide-spec.coffee | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/packages/wrap-guide/lib/wrap-guide-view.coffee b/src/packages/wrap-guide/lib/wrap-guide-view.coffee index 491e19705..dcce9c44e 100644 --- a/src/packages/wrap-guide/lib/wrap-guide-view.coffee +++ b/src/packages/wrap-guide/lib/wrap-guide-view.coffee @@ -6,7 +6,8 @@ module.exports = class WrapGuideView extends View @activate: -> rootView.eachEditor (editor) -> - editor.underlayer.append(new WrapGuideView(editor)) if editor.attached + if editor.attached and editor.getPane() + editor.underlayer.append(new WrapGuideView(editor)) @content: -> @div class: 'wrap-guide' diff --git a/src/packages/wrap-guide/spec/wrap-guide-spec.coffee b/src/packages/wrap-guide/spec/wrap-guide-spec.coffee index cd0761881..f1aa1b22c 100644 --- a/src/packages/wrap-guide/spec/wrap-guide-spec.coffee +++ b/src/packages/wrap-guide/spec/wrap-guide-spec.coffee @@ -1,4 +1,5 @@ RootView = require 'root-view' +Editor = require 'editor' describe "WrapGuide", -> [editor, wrapGuide] = [] @@ -62,3 +63,8 @@ describe "WrapGuide", -> editor.width(10) wrapGuide.updateGuide() expect(wrapGuide).toBeHidden() + + it "only attaches to editors that are part of a pane", -> + editor2 = new Editor(mini: true) + editor.overlayer.append(editor2) + expect(editor2.find('.wrap-guide').length).toBe 0 From ed1c5d3417b283e449b93d4bafde22acce95aa24 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 3 Apr 2013 13:00:30 -0600 Subject: [PATCH 79/87] Localize grammar reloading / setting to LanguageMode Previously, logic associated with swapping grammars was a bit scattered. Now grammar reloading / assignment methods delegate to LanguageMode directly, and it emits a 'grammar-changed' event when the grammar changes. Now EditSession and TokenizedBuffer listen for this event and perform necessary actions for grammar change. --- spec/app/editor-spec.coffee | 2 +- src/app/edit-session.coffee | 9 +++------ src/app/editor.coffee | 9 +-------- src/app/language-mode.coffee | 19 +++++++++++++------ src/app/tokenized-buffer.coffee | 1 + 5 files changed, 19 insertions(+), 21 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 4424d9d73..f5c7612c4 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2113,7 +2113,7 @@ describe "Editor", -> editor.edit(project.buildEditSession(path)) expect(editor.getGrammar().name).toBe 'Plain Text' syntax.setGrammarOverrideForPath(path, 'source.js') - expect(editor.reloadGrammar()).toBeTruthy() + editor.reloadGrammar() expect(editor.getGrammar().name).toBe 'JavaScript' tokenizedBuffer = editor.activeEditSession.displayBuffer.tokenizedBuffer diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index 3c3951533..c1a039389 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -57,7 +57,7 @@ class EditSession @subscribe @displayBuffer, "changed", (e) => @trigger 'screen-lines-changed', e - @subscribe syntax, 'grammars-loaded', => @reloadGrammar() + @languageMode.on 'grammar-changed', => @handleGrammarChange() getViewClass: -> require 'editor' @@ -846,17 +846,14 @@ class EditSession getGrammar: -> @languageMode.grammar setGrammar: (grammar) -> - @languageMode.grammar = grammar - @handleGrammarChange() + @languageMode.setGrammar(grammar) reloadGrammar: -> - @handleGrammarChange() if @languageMode.reloadGrammar() + @languageMode.reloadGrammar() handleGrammarChange: -> @unfoldAll() - @displayBuffer.tokenizedBuffer.resetScreenLines() @trigger 'grammar-changed' - true getDebugSnapshot: -> [ diff --git a/src/app/editor.coffee b/src/app/editor.coffee index c3c147d0e..02ad2a73e 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -1147,16 +1147,9 @@ class Editor extends View setGrammar: (grammar) -> throw new Error("Only mini-editors can explicity set their grammar") unless @mini @activeEditSession.setGrammar(grammar) - @handleGrammarChange() reloadGrammar: -> - grammarChanged = @activeEditSession.reloadGrammar() - @handleGrammarChange() if grammarChanged - grammarChanged - - handleGrammarChange: -> - @clearRenderedLines() - @updateDisplay() + @activeEditSession.reloadGrammar() bindToKeyedEvent: (key, event, callback) -> binding = {} diff --git a/src/app/language-mode.coffee b/src/app/language-mode.coffee index dc13fae18..ff1dea641 100644 --- a/src/app/language-mode.coffee +++ b/src/app/language-mode.coffee @@ -2,6 +2,7 @@ Range = require 'range' _ = require 'underscore' require 'underscore-extensions' {OnigRegExp} = require 'oniguruma' +EventEmitter = require 'event-emitter' module.exports = class LanguageMode @@ -12,14 +13,18 @@ class LanguageMode constructor: (@editSession) -> @buffer = @editSession.buffer @reloadGrammar() + syntax.on 'grammars-loaded', => @reloadGrammar() + + setGrammar: (grammar) -> + return if grammar is @grammar + @grammar = grammar + @trigger 'grammar-changed', grammar reloadGrammar: -> - path = @buffer.getPath() - pathContents = @buffer.cachedDiskContents - previousGrammar = @grammar - @grammar = syntax.selectGrammar(path, pathContents) - throw new Error("No grammar found for path: #{path}") unless @grammar - previousGrammar isnt @grammar + if grammar = syntax.selectGrammar(@buffer.getPath(), @buffer.getText()) + @setGrammar(grammar) + else + throw new Error("No grammar found for path: #{path}") toggleLineCommentsForBufferRows: (start, end) -> scopes = @editSession.scopesForBufferPosition([start, 0]) @@ -158,3 +163,5 @@ class LanguageMode foldEndRegexForScopes: (scopes) -> if foldEndPattern = syntax.getProperty(scopes, 'editor.foldEndPattern') new OnigRegExp(foldEndPattern) + +_.extend LanguageMode.prototype, EventEmitter diff --git a/src/app/tokenized-buffer.coffee b/src/app/tokenized-buffer.coffee index 70e982e28..f35c94680 100644 --- a/src/app/tokenized-buffer.coffee +++ b/src/app/tokenized-buffer.coffee @@ -23,6 +23,7 @@ class TokenizedBuffer @id = @constructor.idCounter++ @resetScreenLines() @buffer.on "changed.tokenized-buffer#{@id}", (e) => @handleBufferChange(e) + @languageMode.on 'grammar-changed', => @resetScreenLines() resetScreenLines: -> @screenLines = @buildPlaceholderScreenLinesForRows(0, @buffer.getLastRow()) From 05d6adc6c7505b7e314cc4f56b7f10dfec75d91e Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Wed, 3 Apr 2013 17:49:14 -0600 Subject: [PATCH 80/87] Change syntax.selectGrammar to choose the highest-scoring grammar This sets us up to switch to a grammar when it is loaded if it is a better match for the current file. --- spec/app/syntax-spec.coffee | 2 +- src/app/null-grammar.coffee | 2 ++ src/app/syntax.coffee | 51 +++----------------------------- src/app/text-mate-grammar.coffee | 45 +++++++++++++++++++++++++++- 4 files changed, 51 insertions(+), 49 deletions(-) diff --git a/spec/app/syntax-spec.coffee b/spec/app/syntax-spec.coffee index 6a07cd289..943868fc4 100644 --- a/spec/app/syntax-spec.coffee +++ b/spec/app/syntax-spec.coffee @@ -13,7 +13,7 @@ describe "the `syntax` global", -> expect(syntax.selectGrammar(path).name).not.toBe 'Ruby' syntax.setGrammarOverrideForPath(path, 'source.ruby') syntax2 = deserialize(syntax.serialize()) - syntax2.addGrammar(grammar) for grammar in syntax.grammars + syntax2.addGrammar(grammar) for grammar in syntax.grammars when grammar isnt syntax.nullGrammar expect(syntax2.selectGrammar(path).name).toBe 'Ruby' describe ".selectGrammar(filePath)", -> diff --git a/src/app/null-grammar.coffee b/src/app/null-grammar.coffee index 21c7877a2..2f2a37108 100644 --- a/src/app/null-grammar.coffee +++ b/src/app/null-grammar.coffee @@ -5,5 +5,7 @@ class NullGrammar name: 'Null Grammar' scopeName: 'text.plain.null-grammar' + getScore: -> 0 + tokenizeLine: (line) -> { tokens: [new Token(value: line, scopes: ['null-grammar.text.plain'])] } diff --git a/src/app/syntax.coffee b/src/app/syntax.coffee index 75e954baf..53dc963c3 100644 --- a/src/app/syntax.coffee +++ b/src/app/syntax.coffee @@ -5,8 +5,6 @@ Specificity = require 'specificity' fsUtils = require 'fs-utils' EventEmitter = require 'event-emitter' NullGrammar = require 'null-grammar' -nodePath = require 'path' -pathSplitRegex = new RegExp("[#{nodePath.sep}.]") module.exports = class Syntax @@ -18,13 +16,13 @@ class Syntax syntax constructor: -> - @grammars = [] + @nullGrammar = new NullGrammar + @grammars = [@nullGrammar] @grammarsByFileType = {} @grammarsByScopeName = {} @grammarOverridesByPath = {} @scopedPropertiesIndex = 0 @scopedProperties = [] - @nullGrammar = new NullGrammar serialize: -> { deserializer: @constructor.name, @grammarOverridesByPath } @@ -50,51 +48,10 @@ class Syntax @grammarOverridesByPath = {} selectGrammar: (filePath, fileContents) -> - - return @grammarsByFileType["txt"] ? @nullGrammar unless filePath - - @grammarOverrideForPath(filePath) ? - @grammarByFirstLineRegex(filePath, fileContents) ? - @grammarByPath(filePath) ? - @grammarsByFileType["txt"] ? - @nullGrammar + _.max @grammars, (grammar) -> grammar.getScore(filePath, fileContents) grammarOverrideForPath: (path) -> - @grammarsByScopeName[@grammarOverridesByPath[path]] - - grammarByPath: (path) -> - pathComponents = path.split(pathSplitRegex) - for fileType, grammar of @grammarsByFileType - fileTypeComponents = fileType.split(pathSplitRegex) - pathSuffix = pathComponents[-fileTypeComponents.length..-1] - return grammar if _.isEqual(pathSuffix, fileTypeComponents) - - grammarByFirstLineRegex: (filePath, fileContents) -> - try - fileContents ?= fsUtils.read(filePath) - catch e - return - - return unless fileContents - - lines = fileContents.split('\n') - _.find @grammars, (grammar) -> - regex = grammar.firstLineRegex - return unless regex? - - escaped = false - numberOfNewlinesInRegex = 0 - for character in regex.source - switch character - when '\\' - escaped = !escaped - when 'n' - numberOfNewlinesInRegex++ if escaped - escaped = false - else - escaped = false - - regex.test(lines[0..numberOfNewlinesInRegex].join('\n')) + @grammarOverridesByPath[path] grammarForScopeName: (scopeName) -> @grammarsByScopeName[scopeName] diff --git a/src/app/text-mate-grammar.coffee b/src/app/text-mate-grammar.coffee index 774eac4f7..a9f5090b6 100644 --- a/src/app/text-mate-grammar.coffee +++ b/src/app/text-mate-grammar.coffee @@ -2,8 +2,9 @@ _ = require 'underscore' fsUtils = require 'fs-utils' plist = require 'plist' Token = require 'token' -CSON = require 'cson' {OnigRegExp, OnigScanner} = require 'oniguruma' +nodePath = require 'path' +pathSplitRegex = new RegExp("[#{nodePath.sep}.]") module.exports = class TextMateGrammar @@ -38,6 +39,48 @@ class TextMateGrammar data = {patterns: [data], tempName: name} if data.begin? or data.match? @repository[name] = new Rule(this, data) + getScore: (path, contents) -> + contents = fsUtils.read(path) if not contents? and fsUtils.isFile(path) + + if syntax.grammarOverrideForPath(path) is @scopeName + Infinity + else if @matchesContents(contents) + 3 + else if @matchesPath(path) + 2 + else if @isTextGrammar() + 1 + else + -1 + + matchesContents: (contents) -> + return false unless contents? and @firstLineRegex? + + escaped = false + numberOfNewlinesInRegex = 0 + for character in @firstLineRegex.source + switch character + when '\\' + escaped = !escaped + when 'n' + numberOfNewlinesInRegex++ if escaped + escaped = false + else + escaped = false + lines = contents.split('\n') + @firstLineRegex.test(lines[0..numberOfNewlinesInRegex].join('\n')) + + matchesPath: (path) -> + return false unless path? + pathComponents = path.split(pathSplitRegex) + _.find @fileTypes, (fileType) -> + fileTypeComponents = fileType.split(pathSplitRegex) + pathSuffix = pathComponents[-fileTypeComponents.length..-1] + _.isEqual(pathSuffix, fileTypeComponents) + + isTextGrammar: -> + @scopeName is 'text.plain' + tokenizeLine: (line, ruleStack=[@initialRule], firstLine=false) -> originalRuleStack = ruleStack ruleStack = new Array(ruleStack...) # clone ruleStack From 16b53d318384bdbae12d556107784d1bc0ce4ea0 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Wed, 3 Apr 2013 17:55:57 -0600 Subject: [PATCH 81/87] Eliminate syntax.grammarsByFileType hash --- spec/app/text-mate-grammar-spec.coffee | 8 ++++---- src/app/syntax.coffee | 8 ++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/spec/app/text-mate-grammar-spec.coffee b/spec/app/text-mate-grammar-spec.coffee index 27c88fef2..eb9033e14 100644 --- a/spec/app/text-mate-grammar-spec.coffee +++ b/spec/app/text-mate-grammar-spec.coffee @@ -138,7 +138,7 @@ describe "TextMateGrammar", -> describe "when the line matches a pattern with no `name` or `contentName`", -> it "creates tokens without adding a new scope", -> - grammar = syntax.grammarsByFileType["rb"] + grammar = syntax.selectGrammar('foo.rb') {tokens} = grammar.tokenizeLine('%w|oh \\look|') expect(tokens.length).toBe 5 expect(tokens[0]).toEqual value: '%w|', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby", "punctuation.definition.string.begin.ruby"] @@ -183,7 +183,7 @@ describe "TextMateGrammar", -> describe "when the end pattern contains a back reference", -> it "constructs the end rule based on its back-references to captures in the begin rule", -> - grammar = syntax.grammarsByFileType["rb"] + grammar = syntax.selectGrammar('foo.rb') {tokens} = grammar.tokenizeLine('%w|oh|,') expect(tokens.length).toBe 4 expect(tokens[0]).toEqual value: '%w|', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby", "punctuation.definition.string.begin.ruby"] @@ -192,7 +192,7 @@ describe "TextMateGrammar", -> expect(tokens[3]).toEqual value: ',', scopes: ["source.ruby", "punctuation.separator.object.ruby"] it "allows the rule containing that end pattern to be pushed to the stack multiple times", -> - grammar = syntax.grammarsByFileType["rb"] + grammar = syntax.selectGrammar('foo.rb') {tokens} = grammar.tokenizeLine('%Q+matz had some #{%Q-crazy ideas-} for ruby syntax+ # damn.') expect(tokens[0]).toEqual value: '%Q+', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","punctuation.definition.string.begin.ruby"] expect(tokens[1]).toEqual value: 'matz had some ', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby"] @@ -212,7 +212,7 @@ describe "TextMateGrammar", -> atom.activatePackage('html.tmbundle', sync: true) atom.activatePackage('ruby-on-rails-tmbundle', sync: true) - grammar = syntax.grammarsByFileType["html.erb"] + grammar = syntax.selectGrammar('foo.html.erb') {tokens} = grammar.tokenizeLine("
<%= User.find(2).full_name %>
") expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"] diff --git a/src/app/syntax.coffee b/src/app/syntax.coffee index 53dc963c3..9f00429fb 100644 --- a/src/app/syntax.coffee +++ b/src/app/syntax.coffee @@ -18,7 +18,6 @@ class Syntax constructor: -> @nullGrammar = new NullGrammar @grammars = [@nullGrammar] - @grammarsByFileType = {} @grammarsByScopeName = {} @grammarOverridesByPath = {} @scopedPropertiesIndex = 0 @@ -29,14 +28,11 @@ class Syntax addGrammar: (grammar) -> @grammars.push(grammar) - @grammarsByFileType[fileType] = grammar for fileType in grammar.fileTypes @grammarsByScopeName[grammar.scopeName] = grammar removeGrammar: (grammar) -> - if _.include(@grammars, grammar) - _.remove(@grammars, grammar) - delete @grammarsByFileType[fileType] for fileType in grammar.fileTypes - delete @grammarsByScopeName[grammar.scopeName] + _.remove(@grammars, grammar) + delete @grammarsByScopeName[grammar.scopeName] setGrammarOverrideForPath: (path, scopeName) -> @grammarOverridesByPath[path] = scopeName From 1b56cfb27063d9056d707472d298686e16e241c5 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Wed, 3 Apr 2013 18:12:26 -0600 Subject: [PATCH 82/87] LanguageMode switches to a better-matching grammar when it is added --- spec/app/edit-session-spec.coffee | 12 ++++++------ src/app/language-mode.coffee | 8 ++++++-- src/app/syntax.coffee | 1 + src/packages/status-bar/spec/status-bar-spec.coffee | 1 - 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/spec/app/edit-session-spec.coffee b/spec/app/edit-session-spec.coffee index 3337d526b..06a0a047c 100644 --- a/spec/app/edit-session-spec.coffee +++ b/spec/app/edit-session-spec.coffee @@ -2060,18 +2060,18 @@ describe "EditSession", -> editSession.buffer.reload() expect(editSession.getCursorScreenPosition()).toEqual [0,1] - describe "when the 'grammars-loaded' event is triggered on the syntax global", -> - it "reloads the edit session's grammar and re-tokenizes the buffer if it changes", -> + describe "when a better-matched grammar is added to syntax", -> + it "switches to the better-matched grammar and re-tokenizes the buffer", -> editSession.destroy() jsGrammar = syntax.selectGrammar('a.js') - grammarToReturn = syntax.nullGrammar - spyOn(syntax, 'selectGrammar').andCallFake -> grammarToReturn + syntax.removeGrammar(jsGrammar) editSession = project.buildEditSession('sample.js', autoIndent: false) + expect(editSession.getGrammar()).toBe syntax.nullGrammar expect(editSession.lineForScreenRow(0).tokens.length).toBe 1 - grammarToReturn = jsGrammar - syntax.trigger 'grammars-loaded' + syntax.addGrammar(jsGrammar) + expect(editSession.getGrammar()).toBe jsGrammar expect(editSession.lineForScreenRow(0).tokens.length).toBeGreaterThan 1 describe "auto-indent", -> diff --git a/src/app/language-mode.coffee b/src/app/language-mode.coffee index ff1dea641..a16a498d8 100644 --- a/src/app/language-mode.coffee +++ b/src/app/language-mode.coffee @@ -9,15 +9,19 @@ class LanguageMode buffer = null grammar = null editSession = null + currentGrammarScore: null constructor: (@editSession) -> @buffer = @editSession.buffer @reloadGrammar() - syntax.on 'grammars-loaded', => @reloadGrammar() + syntax.on 'grammar-added', (grammar) => + newScore = grammar.getScore(@buffer.getPath(), @buffer.getText()) + @setGrammar(grammar, newScore) if newScore > @currentGrammarScore - setGrammar: (grammar) -> + setGrammar: (grammar, score) -> return if grammar is @grammar @grammar = grammar + @currentGrammarScore = score ? grammar.getScore(@buffer.getPath(), @buffer.getText()) @trigger 'grammar-changed', grammar reloadGrammar: -> diff --git a/src/app/syntax.coffee b/src/app/syntax.coffee index 9f00429fb..3aebccbb8 100644 --- a/src/app/syntax.coffee +++ b/src/app/syntax.coffee @@ -29,6 +29,7 @@ class Syntax addGrammar: (grammar) -> @grammars.push(grammar) @grammarsByScopeName[grammar.scopeName] = grammar + @trigger 'grammar-added', grammar removeGrammar: (grammar) -> _.remove(@grammars, grammar) diff --git a/src/packages/status-bar/spec/status-bar-spec.coffee b/src/packages/status-bar/spec/status-bar-spec.coffee index d9bf04274..ead338eae 100644 --- a/src/packages/status-bar/spec/status-bar-spec.coffee +++ b/src/packages/status-bar/spec/status-bar-spec.coffee @@ -183,7 +183,6 @@ describe "StatusBar", -> beforeEach -> atom.activatePackage('text.tmbundle', sync: true) atom.activatePackage('javascript.tmbundle', sync: true) - syntax.trigger 'grammars-loaded' it "displays the name of the current grammar", -> expect(statusBar.find('.grammar-name').text()).toBe 'JavaScript' From 144984c72638995ff97ba16c4ce203a51ca0680a Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Wed, 3 Apr 2013 18:14:52 -0600 Subject: [PATCH 83/87] Make LanguageMode unsubscribe from syntax global when destroyed --- src/app/edit-session.coffee | 1 + src/app/language-mode.coffee | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index c1a039389..c72fc5302 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -83,6 +83,7 @@ class EditSession @buffer.release() selection.destroy() for selection in @getSelections() @displayBuffer.destroy() + @languageMode.destroy() @project?.removeEditSession(this) @trigger 'destroyed' @off() diff --git a/src/app/language-mode.coffee b/src/app/language-mode.coffee index a16a498d8..447cf576a 100644 --- a/src/app/language-mode.coffee +++ b/src/app/language-mode.coffee @@ -3,6 +3,7 @@ _ = require 'underscore' require 'underscore-extensions' {OnigRegExp} = require 'oniguruma' EventEmitter = require 'event-emitter' +Subscriber = require 'subscriber' module.exports = class LanguageMode @@ -14,10 +15,13 @@ class LanguageMode constructor: (@editSession) -> @buffer = @editSession.buffer @reloadGrammar() - syntax.on 'grammar-added', (grammar) => + @subscribe syntax, 'grammar-added', (grammar) => newScore = grammar.getScore(@buffer.getPath(), @buffer.getText()) @setGrammar(grammar, newScore) if newScore > @currentGrammarScore + destroy: -> + @unsubscribe() + setGrammar: (grammar, score) -> return if grammar is @grammar @grammar = grammar @@ -169,3 +173,4 @@ class LanguageMode new OnigRegExp(foldEndPattern) _.extend LanguageMode.prototype, EventEmitter +_.extend LanguageMode.prototype, Subscriber From 2973a812938f0568b5e3745e010c561efea258c3 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Wed, 3 Apr 2013 18:21:07 -0600 Subject: [PATCH 84/87] Don't assign text grammar by default. Just use the null grammar. --- spec/app/syntax-spec.coffee | 6 +++--- src/app/text-mate-grammar.coffee | 9 ++------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/spec/app/syntax-spec.coffee b/spec/app/syntax-spec.coffee index 943868fc4..6f5ece9df 100644 --- a/spec/app/syntax-spec.coffee +++ b/spec/app/syntax-spec.coffee @@ -23,8 +23,8 @@ describe "the `syntax` global", -> expect(syntax.selectGrammar("file.js").name).toBe "JavaScript" # based on extension (.js) expect(syntax.selectGrammar("/tmp/.git/config").name).toBe "Git Config" # based on end of the path (.git/config) expect(syntax.selectGrammar("Rakefile").name).toBe "Ruby" # based on the file's basename (Rakefile) - expect(syntax.selectGrammar("curb").name).toBe "Plain Text" - expect(syntax.selectGrammar("/hu.git/config").name).toBe "Plain Text" + expect(syntax.selectGrammar("curb").name).toBe "Null Grammar" + expect(syntax.selectGrammar("/hu.git/config").name).toBe "Null Grammar" it "uses the filePath's shebang line if the grammar cannot be determined by the extension or basename", -> filePath = require.resolve("fixtures/shebang") @@ -37,7 +37,7 @@ describe "the `syntax` global", -> expect(syntax.selectGrammar("dummy.coffee", fileContent).name).toBe "CoffeeScript" fileContent = '' - expect(syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Plain Text" + expect(syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Null Grammar" fileContent += '\n' expect(syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Property List (XML)" diff --git a/src/app/text-mate-grammar.coffee b/src/app/text-mate-grammar.coffee index a9f5090b6..95d52db1c 100644 --- a/src/app/text-mate-grammar.coffee +++ b/src/app/text-mate-grammar.coffee @@ -43,12 +43,10 @@ class TextMateGrammar contents = fsUtils.read(path) if not contents? and fsUtils.isFile(path) if syntax.grammarOverrideForPath(path) is @scopeName - Infinity - else if @matchesContents(contents) 3 - else if @matchesPath(path) + else if @matchesContents(contents) 2 - else if @isTextGrammar() + else if @matchesPath(path) 1 else -1 @@ -78,9 +76,6 @@ class TextMateGrammar pathSuffix = pathComponents[-fileTypeComponents.length..-1] _.isEqual(pathSuffix, fileTypeComponents) - isTextGrammar: -> - @scopeName is 'text.plain' - tokenizeLine: (line, ruleStack=[@initialRule], firstLine=false) -> originalRuleStack = ruleStack ruleStack = new Array(ruleStack...) # clone ruleStack From 5c9dd50e39124964d783fa9f6c676275d1889eed Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Wed, 3 Apr 2013 18:23:31 -0600 Subject: [PATCH 85/87] Start spell-checking immediately When the grammar changes, spell check will detect this and toggle itself accordingly. Before a grammar matches a buffer, it has the NullGrammar, which won't be spell-checked. --- src/packages/spell-check/lib/spell-check.coffee | 9 +++------ src/packages/spell-check/spec/spell-check-spec.coffee | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/packages/spell-check/lib/spell-check.coffee b/src/packages/spell-check/lib/spell-check.coffee index 9256e6c3c..da33850ec 100644 --- a/src/packages/spell-check/lib/spell-check.coffee +++ b/src/packages/spell-check/lib/spell-check.coffee @@ -9,12 +9,9 @@ module.exports = ] activate: -> - syntax.on 'grammars-loaded.spell-check', => @subscribeToEditors() - - deactivate: -> - syntax.off '.spell-check' - - subscribeToEditors: -> rootView.eachEditor (editor) -> if editor.attached and not editor.mini editor.underlayer.append(new SpellCheckView(editor)) + + deactivate: -> + syntax.off '.spell-check' diff --git a/src/packages/spell-check/spec/spell-check-spec.coffee b/src/packages/spell-check/spec/spell-check-spec.coffee index 5ac927d96..97fcc2bd9 100644 --- a/src/packages/spell-check/spec/spell-check-spec.coffee +++ b/src/packages/spell-check/spec/spell-check-spec.coffee @@ -10,7 +10,6 @@ describe "Spell check", -> rootView.open('sample.js') config.set('spell-check.grammars', []) atom.activatePackage('spell-check', immediate: true) - syntax.trigger 'grammars-loaded' rootView.attachToDom() editor = rootView.getActiveView() From 2f4bbd90a649c98e95ce03857c58e2b60de85eec Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Wed, 3 Apr 2013 18:24:02 -0600 Subject: [PATCH 86/87] Don't emit grammars-loaded event --- src/app/text-mate-package.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/text-mate-package.coffee b/src/app/text-mate-package.coffee index a6ef9b8fd..10156f4ba 100644 --- a/src/app/text-mate-package.coffee +++ b/src/app/text-mate-package.coffee @@ -13,7 +13,6 @@ class TextMatePackage extends Package @getLoadQueue: -> return @loadQueue if @loadQueue @loadQueue = async.queue (pack, done) -> pack.loadGrammars(done) - @loadQueue.drain = -> syntax.trigger 'grammars-loaded' @loadQueue constructor: -> From bac2209449c768bf45cc44872f47ce37c448afce Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Apr 2013 17:51:21 -0700 Subject: [PATCH 87/87] Remove unneeded syntax.off() call syntax.on() is no longer called anywhere in the package. --- src/packages/spell-check/lib/spell-check.coffee | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/packages/spell-check/lib/spell-check.coffee b/src/packages/spell-check/lib/spell-check.coffee index da33850ec..5a41afc41 100644 --- a/src/packages/spell-check/lib/spell-check.coffee +++ b/src/packages/spell-check/lib/spell-check.coffee @@ -12,6 +12,3 @@ module.exports = rootView.eachEditor (editor) -> if editor.attached and not editor.mini editor.underlayer.append(new SpellCheckView(editor)) - - deactivate: -> - syntax.off '.spell-check'